fast-xml-validator 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/fxv.cjs +1 -1
- package/lib/fxv.d.cts +57 -27
- package/lib/fxv.min.js +1 -1
- package/lib/fxv.min.js.map +1 -1
- package/package.json +4 -2
- package/src/SyntaxValidator.js +27 -4
- package/src/fxv.d.ts +46 -20
- package/src/validators/AttributeStringValidator.js +101 -4
- package/src/validators/DocTypeValidator.js +13 -1
- package/src/validators/XmlDeclarationValidator.js +97 -10
- package/src/DocTypeValidator.js +0 -280
- package/src/validator.js +0 -539
package/lib/fxv.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"./lib/fxv.min.js","mappings":"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","sources":["webpack://fxp/webpack/universalModuleDefinition","webpack://fxp/webpack/bootstrap","webpack://fxp/webpack/runtime/define property getters","webpack://fxp/webpack/runtime/hasOwnProperty shorthand","webpack://fxp/webpack/runtime/make namespace object","webpack://fxp/./src/ValidationError.js","webpack://fxp/./src/util.js","webpack://fxp/./node_modules/xml-naming/src/index.js","webpack://fxp/./src/validators/XmlNameValidator.js","webpack://fxp/./src/validators/XmlDeclarationValidator.js","webpack://fxp/./src/validators/ControlCharValidator.js","webpack://fxp/./src/validators/AttributeStringValidator.js","webpack://fxp/./src/validators/CommentCdataReader.js","webpack://fxp/./node_modules/path-expression-matcher/src/Expression.js","webpack://fxp/./node_modules/path-expression-matcher/src/Matcher.js","webpack://fxp/./src/validators/SkipTagMatcher.js","webpack://fxp/./src/validators/RawSubtreeReader.js","webpack://fxp/./src/validators/DocTypeValidator.js","webpack://fxp/./src/SyntaxValidator.js","webpack://fxp/./node_modules/path-expression-matcher/src/ExpressionSet.js","webpack://fxp/./node_modules/@nodable/compact-builder/src/ParserOptionsBuilder.js","webpack://fxp/./node_modules/@nodable/entities/src/entities.js","webpack://fxp/./node_modules/@nodable/entities/src/EntityDecoder.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/html.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/sql.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/nosql.js","webpack://fxp/./node_modules/is-unsafe/src/registry.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/xml.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/svg.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/sql-strict.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/shell.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/redos.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/log.js","webpack://fxp/./node_modules/is-unsafe/src/index.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/BaseValueParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/EntityParser.js","webpack://fxp/./node_modules/anynum/digitTable.js","webpack://fxp/./node_modules/anynum/anynum.js","webpack://fxp/./node_modules/strnum/strnum.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/number.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParserRegistry.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/trim.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/WSNormalizer.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/booleanParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/BaseOutputBuilder.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/BaseOutputBuilderFactory.js","webpack://fxp/./node_modules/@nodable/compact-builder/src/CompactBuilder.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/ParseError.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/util.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/OptionsBuilder.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/scanTagExpEnd.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/StringSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/ScanStrategy/ByteScanStrategy.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/BufferSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingDetector.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/TextDecoderAdapter.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingRegistry.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/AttributeProcessor.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XmlPartReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/StopNodeProcessor.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XmlSpecialTagsReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/DocTypeReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/AutoCloseHandler.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Xml2JsParser.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingProfile.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/ScanStrategy/CharScanStrategy.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/FeedableSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/StreamSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XMLParser.js","webpack://fxp/./node_modules/detailed-xml-validator/src/OrderTrackingBuilder.js","webpack://fxp/./node_modules/detailed-xml-validator/src/util.js","webpack://fxp/./node_modules/detailed-xml-validator/src/validations.js","webpack://fxp/./node_modules/detailed-xml-validator/src/Traverser.js","webpack://fxp/./node_modules/detailed-xml-validator/src/validator.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"fxp\"] = factory();\n\telse\n\t\troot[\"fxp\"] = factory();\n})(this, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export default class ValidationError extends Error {\n constructor(message, code, line, col) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.line = line;\n this.col = col;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}","'use strict';\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\nexport function isWhiteSpace(char) {\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\n}\n\n/**\n * True for character codes that are illegal as literal characters anywhere in\n * an XML document (element text, attribute values, comments): 0x00-0x08,\n * 0x0B, 0x0C, 0x0E-0x1F. Tab/LF/CR (0x09/0x0A/0x0D) are legal whitespace and\n * are excluded.\n */\nexport function isIllegalControlCode(code) {\n return code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31);\n}\n\nexport function getLineNumberForPosition(xmlData, index) {\n const lines = xmlData.substring(0, index).split(/\\r?\\n/);\n return {\n line: lines.length,\n col: lines[lines.length - 1].length + 1,\n };\n}\n\nexport function getPositionFromMatch(match) {\n return match.startIndex + match[1].length;\n}\n","/**\n * xml-naming\n * Validates XML Name productions as defined in the XML 1.0 and 1.1 specifications.\n * Covers: Name, NCName, QName, NMToken, NMTokens\n *\n * XML 1.0 spec: https://www.w3.org/TR/xml/#NT-Name\n * XML 1.1 spec: https://www.w3.org/TR/xml11/#NT-NameStartChar\n * XML NS spec: https://www.w3.org/TR/xml-names/#NT-NCName\n */\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.0\n//\n// NameStartChar ::= \":\" | [A-Z] | \"_\" | [a-z]\n// | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF]\n// | [#x370-#x37D] | [#x37F-#x1FFF] <- split to exclude #x0487\n// | [#x200C-#x200D]\n// | [#x2070-#x218F] | [#x2C00-#x2FEF]\n// | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD]\n//\n// NameChar ::= NameStartChar | \"-\" | \".\" | [0-9]\n// | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]\n//\n// Note: \\u0487 (Combining Cyrillic Millions Sign) was added in Unicode 4.0,\n// after XML 1.0 was defined against Unicode 2.0. It falls inside the range\n// \\u037F-\\u1FFF but must be excluded. We split that range into\n// \\u037F-\\u0486 and \\u0488-\\u1FFF to exclude it explicitly.\n// ---------------------------------------------------------------------------\n\nconst nameStartChar10 =\n ':A-Za-z_' +\n '\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF' +\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD';\n\nconst nameChar10 =\n nameStartChar10 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.1\n//\n// Differences from XML 1.0:\n//\n// NameStartChar:\n// 1.0 has split ranges: \\u00C0-\\u00D6, \\u00D8-\\u00F6, \\u00F8-\\u02FF\n// 1.1 merges them into: \\u00C0-\\u02FF\n// (\\u00D7 x and \\u00F7 / are division symbols, excluded in both versions)\n//\n// 1.0 tops out at \\uFFFD (BMP only)\n// 1.1 adds \\u{10000}-\\u{EFFFF} (supplementary planes)\n// These require the /u flag on the RegExp — see buildRegexes below.\n//\n// NameChar:\n// 1.1 adds \\u0487 (Combining Cyrillic Millions Sign, added in Unicode 4.0)\n// ---------------------------------------------------------------------------\n\nconst nameStartChar11 =\n ':A-Za-z_' +\n '\\u00C0-\\u02FF' + // merged — 1.0 had three split ranges here\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487 (combining mark, never a NameStartChar)\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD' +\n '\\u{10000}-\\u{EFFFF}'; // supplementary planes — REQUIRES /u flag on RegExp\n\nconst nameChar11 =\n nameStartChar11 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u0487' + // Combining Cyrillic Millions Sign — valid in 1.1, not 1.0\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Regex builders\n//\n// XML 1.0 regexes: no flags — BMP only, standard JS regex behaviour.\n// XML 1.1 regexes: /u flag — required for \\u{10000}-\\u{EFFFF} to match actual\n// supplementary code points rather than lone surrogates (which are illegal XML).\n// ---------------------------------------------------------------------------\n\nconst buildRegexes = (startChar, char, flags = '') => {\n const ncStart = startChar.replace(':', '');\n const ncChar = char.replace(':', '');\n const ncNamePat = `[${ncStart}][${ncChar}]*`;\n\n return {\n name: new RegExp(`^[${startChar}][${char}]*$`, flags),\n ncName: new RegExp(`^${ncNamePat}$`, flags),\n qName: new RegExp(`^${ncNamePat}(?::${ncNamePat})?$`, flags),\n nmToken: new RegExp(`^[${char}]+$`, flags),\n nmTokens: new RegExp(`^[${char}]+(?:\\\\s+[${char}]+)*$`, flags),\n };\n};\n\nconst regexes10 = buildRegexes(nameStartChar10, nameChar10); // no /u — BMP only\nconst regexes11 = buildRegexes(nameStartChar11, nameChar11, 'u'); // /u — enables \\u{10000}-\\u{EFFFF}\n\n// ---------------------------------------------------------------------------\n// ASCII-only fast path (opt-in, off by default)\n//\n// The XML 1.0 vs 1.1 NameStartChar/NameChar productions differ *only* in\n// their non-ASCII ranges (merged vs split Latin-1 ranges, \\u0487, and\n// supplementary planes). Restricted to ASCII, both versions collapse to the\n// same character classes, so a single regex pair covers both xmlVersion\n// values — no /u flag needed.\n//\n// Rationale: unicode-aware regexes (the /u flag, required for XML 1.1's\n// supplementary-plane range) are measurably slower in V8 than plain\n// non-unicode regexes on the same input, even when the input is pure ASCII.\n// For the common case — HTML/SVG ids, XML tags — names are ASCII, so callers\n// who know this can opt in to skip the unicode-aware matching path entirely.\n// This is a real but *conditional* win: mainly for XML 1.1 input (avoids /u),\n// or at scale where the larger unicode character classes add engine\n// overhead. It also changes behaviour (rejects legitimate non-ASCII XML\n// 1.0/1.1 names), so it must never be silently enabled — hence off by\n// default.\n// ---------------------------------------------------------------------------\n\nconst nameStartCharAscii = ':A-Za-z_';\nconst nameCharAscii = nameStartCharAscii + '\\\\-\\\\.\\\\d';\n\nconst regexesAscii = buildRegexes(nameStartCharAscii, nameCharAscii); // no /u — ASCII only\n\nconst getRegexes = (xmlVersion = '1.0', asciiOnly = false) => {\n if (asciiOnly) return regexesAscii;\n return xmlVersion === '1.1' ? regexes11 : regexes10;\n};\n\n// ---------------------------------------------------------------------------\n// Boolean validators\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if the string is a valid XML Name.\n * Colons are allowed anywhere (Name production).\n * Used for: DOCTYPE entity names, notation names, DTD element declarations.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const name = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).name.test(str);\n\n/**\n * Returns true if the string is a valid NCName (Non-Colonized Name).\n * Colons are not permitted.\n * Used for: namespace prefixes, local names, SVG id attributes.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const ncName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).ncName.test(str);\n\n/**\n * Returns true if the string is a valid QName (Qualified Name).\n * Allows exactly one colon as a prefix separator: prefix:localName.\n * Used for: element and attribute names in namespace-aware XML/SVG.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const qName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).qName.test(str);\n\n/**\n * Returns true if the string is a valid NMToken.\n * Like Name but no restriction on the first character.\n * Used for: DTD NMTOKEN attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmToken = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmToken.test(str);\n\n/**\n * Returns true if the string is a valid NMTokens value.\n * A whitespace-separated list of NMToken values.\n * Used for: DTD NMTOKENS attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmTokens = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmTokens.test(str);\n\n// ---------------------------------------------------------------------------\n// Memoized validator factory\n//\n// Real documents reuse a small vocabulary of tag/attribute names across many\n// siblings (e.g. `id`, `class`, `href` repeated across hundreds of elements).\n// The plain boolean validators above re-run the regex on every call\n// regardless of repeats. `createValidator` returns a closure with a private\n// string -> boolean cache, so repeated names after the first become O(1)\n// lookups instead of regex tests.\n//\n// - opts (xmlVersion, asciiOnly) are fixed at creation time, so the regex is\n// resolved once, not on every call.\n// - The cache is private to the returned closure — no shared/global state,\n// no cross-caller pollution.\n// - `maxCacheSize` bounds memory: once the cache reaches this many entries,\n// it stops accepting new ones (existing entries keep serving hits; new\n// misses just fall through to the regex, uncached). This avoids unbounded\n// growth against adversarial/high-cardinality input (e.g. validating\n// attacker-supplied names with no repeats) without the cost/complexity of\n// a full LRU, and without the perf cliff of reset-and-refill thrashing.\n// - Call `.reset()` on the returned function to clear the cache manually\n// (e.g. between unrelated parse calls).\n// ---------------------------------------------------------------------------\n\nconst PRODUCTIONS = ['name', 'ncName', 'qName', 'nmToken', 'nmTokens'];\n\n/**\n * Returns a memoized boolean validator function for a single production,\n * with opts fixed at creation time.\n *\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean, maxCacheSize?: number }} [opts]\n * maxCacheSize: max number of distinct strings to cache (default 2048).\n * Once reached, new strings are validated but not cached; existing cached\n * entries keep being served.\n * @returns {((str: string) => boolean) & { reset: () => void }}\n */\nexport const createValidator = (production, { xmlVersion = '1.0', asciiOnly = false, maxCacheSize = 2048 } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const regex = getRegexes(xmlVersion, asciiOnly)[production];\n let cache = new Map();\n\n const validator = (str) => {\n const cached = cache.get(str);\n if (cached !== undefined) return cached;\n\n const result = regex.test(str);\n if (cache.size < maxCacheSize) cache.set(str, result);\n return result;\n };\n\n validator.reset = () => { cache = new Map(); };\n\n return validator;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostic validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates a string against a named production and returns a detailed result.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {{ valid: boolean, production: string, input: string, reason?: string, position?: number }}\n */\nexport const validate = (str, production, { xmlVersion = '1.0', asciiOnly = false } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const validators = { name, ncName, qName, nmToken, nmTokens };\n const isValid = validators[production](str, { xmlVersion, asciiOnly });\n\n if (isValid) return { valid: true, production, input: str };\n\n let reason = 'Does not match the production rules';\n let position;\n\n // Diagnostic fallback char checks must mirror the same character set the\n // boolean validator above used, or the reported reason/position could\n // contradict the `valid: false` result (e.g. flagging a char as illegal\n // that the unicode-aware check would have accepted).\n const startCharPattern = asciiOnly ? /^[:A-Za-z_]/ : /^[:A-Za-z_\\u00C0-\\uFFFD]/;\n const namePattern = asciiOnly ? /[\\w\\-\\\\.:]/ : /[\\w\\-\\\\.:\\u00B7\\u00C0-\\uFFFD]/;\n\n if (str.length === 0) {\n reason = 'Input is empty';\n } else if (production === 'ncName' && str.includes(':')) {\n position = str.indexOf(':');\n reason = 'Colon is not allowed in NCName';\n } else if (production === 'qName' && str.startsWith(':')) {\n reason = 'QName cannot start with a colon';\n position = 0;\n } else if (production === 'qName' && str.endsWith(':')) {\n reason = 'QName cannot end with a colon';\n position = str.length - 1;\n } else if (production === 'qName' && (str.match(/:/g) || []).length > 1) {\n reason = 'QName can have at most one colon';\n position = str.lastIndexOf(':');\n } else if (\n ['name', 'ncName', 'qName'].includes(production) &&\n !startCharPattern.test(str[0])\n ) {\n reason = `First character \"${str[0]}\" is not a valid NameStartChar`;\n position = 0;\n } else {\n for (let i = 0; i < str.length; i++) {\n if (!namePattern.test(str[i])) {\n reason = `Character \"${str[i]}\" at position ${i} is not a valid NameChar`;\n position = i;\n break;\n }\n }\n }\n\n return { valid: false, production, input: str, reason, position };\n};\n\n// ---------------------------------------------------------------------------\n// Batch validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates an array of strings against a named production.\n *\n * @param {string[]} strings\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {Array<{ valid: boolean, production: string, input: string, reason?: string, position?: number }>}\n */\nexport const validateAll = (strings, production, opts = {}) =>\n strings.map(str => validate(str, production, opts));\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms an invalid string into the nearest valid XML name for the given production.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ replacement?: string, asciiOnly?: boolean }} [opts]\n * asciiOnly: also replace any non-ASCII character, not just XML-illegal\n * ones (default false).\n * @returns {string}\n */\nexport const sanitize = (str, production = 'name', { replacement = '_', asciiOnly = false } = {}) => {\n if (!str) return replacement;\n\n let result = str;\n\n // Strip colons for NCName\n if (production === 'ncName') {\n result = result.replace(/:/g, '');\n }\n\n // Replace illegal characters\n const allowedCharPattern = asciiOnly ? /[^\\w\\-\\.:]/g : /[^\\w\\-\\.:\\u00B7\\u00C0-\\uFFFD]/g;\n result = result.replace(allowedCharPattern, replacement);\n\n // Fix invalid start character for Name / NCName / QName\n if (production !== 'nmToken' && production !== 'nmTokens') {\n if (/^[\\-\\.\\d]/.test(result)) {\n result = replacement + result;\n }\n }\n\n return result || replacement;\n};","'use strict';\n\nimport { createValidator } from 'xml-naming';\n\n/**\n * XmlNameValidator — single responsibility: expose XML Name / QName\n * validators for a given xmlVersion, memoized per instance since element\n * and attribute names repeat heavily across a document.\n */\nexport default class XmlNameValidator {\n constructor(xmlVersion = '1.0') {\n this.xmlVersion = xmlVersion;\n this.qName = createValidator('qName', { xmlVersion });\n this.name = createValidator('name', { xmlVersion });\n }\n\n isValidQName(value) {\n return this.qName(value);\n }\n\n isValidName(value) {\n return this.name(value);\n }\n}\n","'use strict';\n\n/**\n * XmlDeclarationValidator — single responsibility: parse and validate the\n * `<?xml version=\"...\" encoding=\"...\" standalone=\"...\"?>` prolog.\n *\n * Behaviour is controlled by the `xmlDeclaraion` config block:\n *\n * xmlDeclaraion: {\n * optional: true, // when false, the document MUST start with a decl\n * argPosition: true, // when true, version/encoding/standalone must\n * // appear in that fixed order (spec-mandated);\n * // set false to allow any order.\n * }\n *\n * (`argPostion` — the spelling used in the original design note — is also\n * accepted as an alias for `argPosition`.)\n */\nexport default class XmlDeclarationValidator {\n constructor(options = {}, throwError) {\n this.optional = options.optional !== undefined ? options.optional : true;\n this.argPosition = options.argPosition !== undefined\n ? options.argPosition\n : (options.argPostion !== undefined ? options.argPostion : true);\n this._throwError = throwError;\n }\n\n /**\n * @param {string} xmlData - full document (BOM already stripped)\n * @returns {{ version: string, end: number }} version defaults to '1.0'\n * and end is 0 when there is no declaration present.\n */\n validate(xmlData) {\n const hasDecl = xmlData.startsWith('<?xml');\n\n if (!hasDecl) {\n if (!this.optional) {\n this._throwError('InvalidXml', 'XML declaration is required but missing.', { line: 1, col: 1 });\n }\n return { version: '1.0', end: 0 };\n }\n\n const piEnd = xmlData.indexOf('?>');\n if (piEnd === -1) {\n this._throwError('InvalidXml', 'XML declaration is not closed with \"?>\".', { line: 1, col: 1 });\n }\n\n const declBody = xmlData.substring(5, piEnd);\n const end = piEnd + 2;\n\n const ALLOWED = ['version', 'encoding', 'standalone'];\n const attrRe = /\\s+([\\w]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g;\n\n let version = '1.0';\n let lastAllowedIdx = -1;\n let match;\n\n while ((match = attrRe.exec(declBody)) !== null) {\n const attrName = match[1];\n const attrValue = match[2] !== undefined ? match[2] : match[3];\n const attrPos = match.index;\n\n const allowedIdx = ALLOWED.indexOf(attrName);\n if (allowedIdx === -1) {\n this._throwError('InvalidXml',\n 'XML declaration contains unknown attribute \"' + attrName + '\". Allowed: version, encoding, standalone.',\n { line: 1, col: 6 + attrPos });\n }\n\n if (this.argPosition && allowedIdx <= lastAllowedIdx) {\n this._throwError('InvalidXml',\n 'XML declaration attribute \"' + attrName + '\" is out of order. Required order: version, encoding, standalone.',\n { line: 1, col: 6 + attrPos });\n }\n lastAllowedIdx = allowedIdx;\n\n if (attrName === 'version') {\n if (attrValue !== '1.0' && attrValue !== '1.1') {\n this._throwError('InvalidXml',\n 'XML declaration version \"' + attrValue + '\" is not supported. Must be \"1.0\" or \"1.1\".',\n { line: 1, col: 6 + attrPos });\n }\n version = attrValue;\n } else if (attrName === 'standalone') {\n if (attrValue !== 'yes' && attrValue !== 'no') {\n this._throwError('InvalidXml',\n 'XML declaration standalone \"' + attrValue + '\" is invalid. Must be \"yes\" or \"no\".',\n { line: 1, col: 6 + attrPos });\n }\n }\n // encoding: value format validation is out of scope\n }\n\n const leftover = declBody.replace(/\\s+[\\w]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*')/g, '').trim();\n if (leftover.length > 0) {\n this._throwError('InvalidXml',\n 'XML declaration contains invalid content: \"' + leftover + '\".',\n { line: 1, col: 1 });\n }\n\n return { version, end };\n }\n}\n","'use strict';\n\nimport { isIllegalControlCode } from '../util.js';\n\n/**\n * ControlCharValidator — single responsibility: detect illegal control\n * characters (0x00-0x08, 0x0B, 0x0C, 0x0E-0x1F) anywhere they appear as\n * literal characters in the document (element text, attribute values,\n * comments, CDATA, PI content, etc.).\n *\n * This check is always on — it is not gated by any option — since illegal\n * control characters make a document non-well-formed per the XML spec.\n */\nexport default class ControlCharValidator {\n /**\n * Scan `str` for the first illegal control character.\n * @param {string} str\n * @returns {number} local index (within `str`) of the first offending\n * character, or -1 if none found.\n */\n static findIllegalIndex(str) {\n const len = str.length;\n for (let i = 0; i < len; i++) {\n if (isIllegalControlCode(str.charCodeAt(i))) return i;\n }\n return -1;\n }\n\n /**\n * @param {string} str\n * @returns {boolean}\n */\n static hasIllegalChar(str) {\n return this.findIllegalIndex(str) !== -1;\n }\n\n /**\n * Convenience formatter for error messages.\n * @param {number} code - char code\n */\n static describe(code) {\n return `0x${code.toString(16).padStart(2, '0')}`;\n }\n}\n","'use strict';\n\nimport { getAllMatches, getPositionFromMatch } from '../util.js';\nimport ControlCharValidator from './ControlCharValidator.js';\n\nconst validAttrStrRegxp = new RegExp('(\\\\s*)([^\\\\s=]+)(\\\\s*=)?(\\\\s*([\\'\"])(([\\\\s\\\\S])*?)\\\\5)?', 'g');\n\n/**\n * AttributeStringValidator — single responsibility: validate the raw\n * attribute-expression substring of a start tag (everything between the tag\n * name and the closing `>`/`/>`).\n *\n * Checks performed (always on):\n * - proper whitespace separation between attributes\n * - attribute names are valid QNames\n * - no duplicate attribute names\n * - `=` present without a value -> \"without value\"\n * - value present but not quoted -> UnquotedAttributeValue\n * - boolean attributes rejected unless `allowBooleanAttributes`\n * - illegal control characters inside the attribute value\n * - `xmlns:x=\"\"` only allowed in XML 1.1\n *\n * Checks performed (opt-in via `invalidCharSequence.attrLt`):\n * - literal `<` inside an attribute value\n */\nexport default class AttributeStringValidator {\n /**\n * @param {object} options - the full validation options object\n * @param {import('./XmlNameValidator.js').default} nameValidator\n * @param {string} xmlVersion\n */\n constructor(options, nameValidator, xmlVersion) {\n this.options = options;\n this.nameValidator = nameValidator;\n this.xmlVersion = xmlVersion;\n }\n\n /**\n * @param {string} attrStr\n * @returns {true | { err: { code: string, msg: string, line: number } }}\n */\n validate(attrStr) {\n const matches = getAllMatches(attrStr, validAttrStrRegxp);\n const attrNames = {};\n const checkAttrLt = this.options.invalidCharSequence?.attrLt === true;\n\n for (let i = 0; i < matches.length; i++) {\n const m = matches[i];\n\n if (m[1].length === 0) {\n return this._err('InvalidAttr', \"Attribute '\" + m[2] + \"' has no space in starting.\", m);\n }\n\n const hasEquals = m[3] !== undefined;\n const hasQuotedValue = m[4] !== undefined;\n\n if (hasEquals && !hasQuotedValue) {\n // Either `attr=` with nothing after it, or `attr=value` where value\n // isn't wrapped in quotes — XML requires every attribute value to be\n // quoted, so both shapes are rejected as UnquotedAttributeValue.\n return this._err(\n 'UnquotedAttributeValue',\n \"Attribute '\" + m[2] + \"' is without value.\",\n m\n );\n } else if (!hasEquals && !this.options.allowBooleanAttributes) {\n return this._err('InvalidAttr', \"boolean attribute '\" + m[2] + \"' is not allowed.\", m);\n }\n\n const attrName = m[2];\n\n if (!this.nameValidator.isValidQName(attrName)) {\n return this._err('InvalidAttr', \"Attribute '\" + attrName + \"' is an invalid name.\", m);\n }\n\n const attrValue = m[6] || '';\n\n if (hasQuotedValue) {\n const illegalIdx = ControlCharValidator.findIllegalIndex(attrValue);\n if (illegalIdx !== -1) {\n const code = attrValue.charCodeAt(illegalIdx);\n return this._err(\n 'IllegalCharacter',\n `Illegal control character ${ControlCharValidator.describe(code)} in attribute '${attrName}' value.`,\n m\n );\n }\n\n if (checkAttrLt && attrValue.indexOf('<') !== -1) {\n return this._err('InvalidAttr', `Attribute '${attrName}' value must not contain '<'.`, m);\n }\n }\n\n if (attrName.startsWith('xmlns:') && m[6] === '' && this.xmlVersion === '1.0') {\n return this._err(\n 'InvalidAttr',\n `Undeclaring the prefixed namespace ${attrName}=\"\" is only permitted in XML 1.1 documents.`,\n m\n );\n }\n\n if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {\n attrNames[attrName] = 1;\n } else {\n return this._err('InvalidAttr', \"Attribute '\" + attrName + \"' is repeated.\", m);\n }\n }\n\n return true;\n }\n\n _err(code, msg, match) {\n return { err: { code, msg, line: getPositionFromMatch(match) } };\n }\n}\n","'use strict';\n\nimport ControlCharValidator from './ControlCharValidator.js';\n\n/**\n * CommentCdataReader — single responsibility: recognize and consume\n * `<!-- comment -->`, `<![CDATA[ ... ]]>` and `<!DOCTYPE ...>` constructs\n * starting at a given index (which points at the `!`), returning the index\n * of the last character consumed (the closing `>`).\n *\n * DOCTYPE handling is fully delegated to a DocTypeValidator instance passed\n * to `read()` by the caller (SyntaxValidator), since DOCTYPE parsing is a\n * large, separate concern in its own right.\n *\n * Opt-in checks (via `invalidCharSequence`):\n * - `comment: true` -> literal `--` inside a comment's body is rejected\n * (besides the sequence immediately before the closing `-->`).\n */\nexport default class CommentCdataReader {\n constructor(options = {}) {\n this.checkCommentDashes = options.invalidCharSequence?.comment === true;\n }\n\n /**\n * @param {string} xmlData\n * @param {number} i - index of the '!' character\n * @param {import('./DocTypeValidator.js').default} docTypeValidator\n * @returns {number} index of the last character consumed\n */\n read(xmlData, i, docTypeValidator) {\n if (xmlData.length > i + 2 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {\n return this._readComment(xmlData, i);\n } else if (\n xmlData.length > i + 7 &&\n xmlData[i + 1] === 'D' &&\n xmlData[i + 2] === 'O' &&\n xmlData[i + 3] === 'C' &&\n xmlData[i + 4] === 'T' &&\n xmlData[i + 5] === 'Y' &&\n xmlData[i + 6] === 'P' &&\n xmlData[i + 7] === 'E'\n ) {\n return docTypeValidator.validateDocType(xmlData, i - 1);\n } else if (\n xmlData.length > i + 8 &&\n xmlData[i + 1] === '[' &&\n xmlData[i + 2] === 'C' &&\n xmlData[i + 3] === 'D' &&\n xmlData[i + 4] === 'A' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'A' &&\n xmlData[i + 7] === '['\n ) {\n return this._readCdata(xmlData, i);\n } else {\n throw new Error(\"Invalid construct starting with '<!'.\");\n }\n }\n\n _readComment(xmlData, i) {\n const bodyStart = i + 3;\n for (i = bodyStart; i < xmlData.length; i++) {\n if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n if (xmlData[i] !== '>') {\n throw new Error('Comment is not closed with \"-->\".');\n }\n\n const body = xmlData.substring(bodyStart, i - 2);\n const illegalIdx = ControlCharValidator.findIllegalIndex(body);\n if (illegalIdx !== -1) {\n const code = body.charCodeAt(illegalIdx);\n throw new Error(`Illegal control character ${ControlCharValidator.describe(code)} in comment.`);\n }\n if (this.checkCommentDashes && body.indexOf('--') !== -1) {\n throw new Error(`Comment must not contain '--'.`);\n }\n\n return i;\n }\n\n _readCdata(xmlData, i) {\n const bodyStart = i + 8;\n for (i = bodyStart; i < xmlData.length; i++) {\n if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n if (xmlData[i] !== '>') {\n throw new Error('CDATA section is not closed with \"]]>\".');\n }\n\n const body = xmlData.substring(bodyStart, i - 2);\n const illegalIdx = ControlCharValidator.findIllegalIndex(body);\n if (illegalIdx !== -1) {\n const code = body.charCodeAt(illegalIdx);\n throw new Error(`Illegal control character ${ControlCharValidator.describe(code)} in CDATA section.`);\n }\n\n return i;\n }\n}\n","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}, data) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n this.data = data;\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","import ExpressionSet from \"./ExpressionSet.js\";\n\n/**\n * MatcherView - A lightweight read-only view over a Matcher's internal state.\n *\n * Created once by Matcher and reused across all callbacks. Holds a direct\n * reference to the parent Matcher so it always reflects current parser state\n * with zero copying or freezing overhead.\n *\n * Users receive this via {@link Matcher#readOnly} or directly from parser\n * callbacks. It exposes all query and matching methods but has no mutation\n * methods — misuse is caught at the TypeScript level rather than at runtime.\n *\n * @example\n * const matcher = new Matcher();\n * const view = matcher.readOnly();\n *\n * matcher.push(\"root\", {});\n * view.getCurrentTag(); // \"root\"\n * view.getDepth(); // 1\n */\nexport class MatcherView {\n /**\n * @param {Matcher} matcher - The parent Matcher instance to read from.\n */\n constructor(matcher) {\n this._matcher = matcher;\n }\n\n /**\n * Get the path separator used by the parent matcher.\n * @returns {string}\n */\n get separator() {\n return this._matcher.separator;\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return undefined;\n return path[path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return false;\n const current = path[path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {*}\n */\n getAnyParentAttr(attrName) {\n return this._matcher.getAnyParentAttr(attrName);\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n return this._matcher.hasAnyParentAttr(attrName);\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this._matcher.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n return this._matcher.toString(separator, includeNamespace);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this._matcher.path.map(n => n.tag);\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n return this._matcher.matches(expression);\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this._matcher);\n }\n}\n\n/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions.\n *\n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n *\n * Use {@link Matcher#readOnly} to obtain a {@link MatcherView} safe to pass to\n * user callbacks — it always reflects current state with no Proxy overhead.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n *\n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\nexport default class Matcher {\n /**\n * Create a new Matcher.\n * @param {Object} [options={}]\n * @param {string} [options.separator='.'] - Default path separator\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag, values, position, counter, namespace? }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n this._pathStringCache = null;\n this._view = new MatcherView(this);\n\n // Kept-attribute stack: only populated when push() is called with options.keep.\n this._keptAttrs = [];\n }\n\n /**\n * Push a new tag onto the path.\n * @param {string} tagName\n * @param {Object|null} [attrValues=null]\n * @param {string|null} [namespace=null]\n * @param {Object|null} [options=null]\n * @param {string[]} [options.keep] - Names of attributes (from attrValues)\n */\n push(tagName, attrValues = null, namespace = null, options = null) {\n this._pathStringCache = null;\n\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n this.path[this.path.length - 1].values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n let level = this.siblingStacks[currentLevel];\n if (!level) {\n // `counts` tells same-name siblings apart (the \"counter\" — nth <item>\n // among other <item>s). `total` is every child seen at this level so\n // far, kept as a running number instead of re-added from `counts` on\n // every push — a parent with many differently-named children would\n // otherwise cost more per child the more distinct names it has.\n level = { counts: new Map(), total: 0 };\n this.siblingStacks[currentLevel] = level;\n }\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = level.counts.get(siblingKey) || 0;\n\n // Position = total children at this level seen before this one.\n const position = level.total;\n\n // Update sibling count for this tag, and the level's running total.\n level.counts.set(siblingKey, counter + 1);\n level.total++;\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n\n // Depth of the node we just pushed (1-based, matches this.path.length)\n const depth = this.path.length;\n\n // Copy only the requested attributes into the kept-attrs stack. This is\n // the one part of push() whose cost scales with input (O(keep.length))\n // rather than being O(1) — by design, since the caller is explicitly\n // opting in for specific attribute names. No options/keep => zero added\n // cost beyond the two property reads below.\n const keep = options !== null ? options.keep : null;\n if (keep !== null && keep !== undefined && keep.length > 0 && attrValues) {\n for (let i = 0; i < keep.length; i++) {\n const name = keep[i];\n if (attrValues[name] !== undefined) {\n this._keptAttrs.push({ depth, name, value: attrValues[name] });\n }\n }\n }\n }\n\n /**\n * Pop the last tag from the path.\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) return undefined;\n this._pathStringCache = null;\n\n const node = this.path.pop();\n\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n // Drop any kept attributes that belonged to the popped node (or deeper).\n // _keptAttrs is depth-ordered (push only ever appends increasing depths),\n // so this is a backward scan that stops at the first surviving entry —\n // typically O(1) since kept attrs are rare by design.\n const poppedDepth = this.path.length + 1;\n while (\n this._keptAttrs.length > 0 &&\n this._keptAttrs[this._keptAttrs.length - 1].depth >= poppedDepth\n ) {\n this._keptAttrs.pop();\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values.\n * Useful when attributes are parsed after push.\n * @param {Object} attrValues\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n }\n }\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n return this.path[this.path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * Unlike getAttrValue(), this works regardless of how deep the path has\n * gone since the attribute was pushed — but only for attribute names that\n * were explicitly marked with `keep` at push time. Cost is proportional to\n * the number of currently-kept attributes (typically 0-3), not path depth.\n * @param {string} attrName\n * @returns {*} the value, or undefined if no ancestor kept this attribute\n */\n getAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return kept[i].value;\n }\n return undefined;\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return true;\n }\n return false;\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty.\n */\n reset() {\n this._pathStringCache = null;\n this.path = [];\n this.siblingStacks = [];\n this._keptAttrs = [];\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n return this._matchSimple(segments);\n }\n\n /**\n * @private\n */\n _matchSimple(segments) {\n if (this.path.length !== segments.length) {\n return false;\n }\n\n for (let i = 0; i < segments.length; i++) {\n if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1;\n let segIdx = segments.length - 1;\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n segIdx--;\n\n if (segIdx < 0) {\n return true;\n }\n\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n return segIdx < 0;\n }\n\n /**\n * @private\n */\n _matchSegment(segment, node, isCurrentNode) {\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n if (segment.namespace !== undefined) {\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n if (segment.attrValue !== undefined) {\n if (String(node.values[segment.attrName]) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth' && counter !== segment.positionValue) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this);\n }\n\n /**\n * Create a snapshot of current state.\n * @returns {Object}\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level),\n keptAttrs: this._keptAttrs.map(entry => ({ ...entry }))\n };\n }\n\n /**\n * Restore state from snapshot.\n * @param {Object} snapshot\n */\n restore(snapshot) {\n this._pathStringCache = null;\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level);\n this._keptAttrs = (snapshot.keptAttrs || []).map(entry => ({ ...entry }));\n }\n\n /**\n * Return the read-only {@link MatcherView} for this matcher.\n *\n * The same instance is returned on every call — no allocation occurs.\n * It always reflects the current parser state and is safe to pass to\n * user callbacks without risk of accidental mutation.\n *\n * @returns {MatcherView}\n *\n * @example\n * const view = matcher.readOnly();\n * // pass view to callbacks — it stays in sync automatically\n * view.matches(expr); // ✓\n * view.getCurrentTag(); // ✓\n * // view.push(...) // ✗ method does not exist — caught by TypeScript\n */\n readOnly() {\n return this._view;\n }\n}\n","'use strict';\n\nimport { Expression, Matcher } from 'path-expression-matcher';\n\n/**\n * SkipTagMatcher — single responsibility: decide whether a tag currently\n * being opened should be excluded from validation, based on a list of path\n * expressions supplied via `options.skipTags` (same expression syntax as\n * `path-expression-matcher`, e.g. `\"..script\"`, `\"root.data.raw\"`,\n * `\"row[type=raw]\"`).\n *\n * Skip decisions are only ever made for tags reached through normal\n * (non-skipped) parsing — once a match is found, the caller\n * (`SyntaxValidator`) consumes the whole matched subtree as opaque raw text\n * (mirroring how CDATA is treated) and calls `pop()` once for that tag, so\n * this matcher's path stack never needs to represent the skipped subtree's\n * internal structure.\n */\nexport default class SkipTagMatcher {\n /**\n * @param {Array<string>} [skipTags]\n */\n constructor(skipTags = []) {\n this._expressions = skipTags.map((expr) => new Expression(expr));\n this._matcher = new Matcher();\n this._enabled = this._expressions.length > 0;\n }\n\n get enabled() {\n return this._enabled;\n }\n\n /**\n * Push the tag currently being opened onto the path and report whether it\n * (and its whole subtree) should be skipped.\n * @param {string} tagName\n * @param {Record<string,string>} [rawAttributes] - for `[attr=value]` conditions\n * @returns {boolean}\n */\n push(tagName, rawAttributes) {\n if (!this._enabled) return false;\n this._matcher.push(tagName, rawAttributes || null);\n for (let i = 0; i < this._expressions.length; i++) {\n if (this._matcher.matches(this._expressions[i])) return true;\n }\n return false;\n }\n\n /** Pop the most recently pushed tag off the path (tag fully closed). */\n pop() {\n if (!this._enabled) return;\n this._matcher.pop();\n }\n}\n","'use strict';\n\n/**\n * RawSubtreeReader — single responsibility: given the index right after a\n * skipped tag's opening `>`, consume raw characters until the literal\n * closing tag `</tagName>` (optional whitespace before `>`) is found.\n *\n * Deliberately does NOT try to parse anything in between: skipped content is\n * assumed to be non-XML or otherwise not meant to be validated (script/style\n * bodies, vendor blobs, etc.), so it may contain unmatched `<`, quotes, or\n * anything else. This mirrors how CDATA content is treated — opaque text,\n * found by looking for its one true terminator.\n *\n * Same-name tags never \"nest\" under this strategy — the first `</tagName>`\n * found ends the skip, matching the common script/style use case. Use a more\n * specific skip expression (e.g. targeting only the outermost occurrence) if\n * your skipped content can itself contain literal `</tagName>` text.\n */\nexport default class RawSubtreeReader {\n /**\n * @param {string} xmlData\n * @param {string} tagName\n * @param {number} i - index right after the opening tag's '>'\n * @returns {number} index of the matching closing tag's own '>'\n */\n static consume(xmlData, tagName, i) {\n const needle = '</' + tagName;\n let searchFrom = i;\n\n while (true) {\n const idx = xmlData.indexOf(needle, searchFrom);\n if (idx === -1) {\n throw new Error(`Unclosed skipped tag '${tagName}'.`);\n }\n\n let j = idx + needle.length;\n while (j < xmlData.length && /\\s/.test(xmlData[j])) j++;\n\n if (xmlData[j] === '>') {\n return j;\n }\n // Not actually a match (e.g. tagName is a prefix of a longer name) —\n // keep looking further in the string.\n searchFrom = idx + needle.length;\n }\n }\n}\n","import { name as isXmlName } from 'xml-naming';\n\nconst defaultOptions = {\n maxEntityCount: Infinity,\n maxEntitySize: Infinity,\n};\n\nexport default class DocTypeValidator {\n constructor(options, xmlVersion = '1.0') {\n this.options = Object.assign({}, defaultOptions, options);\n this.suppressValidationErr = !options;\n this.xmlVersion = xmlVersion;\n }\n\n validateDocType(xmlData, i) {\n if (\n xmlData[i + 3] !== 'O' ||\n xmlData[i + 4] !== 'C' ||\n xmlData[i + 5] !== 'T' ||\n xmlData[i + 6] !== 'Y' ||\n xmlData[i + 7] !== 'P' ||\n xmlData[i + 8] !== 'E'\n ) {\n throw new Error(`Invalid Tag instead of DOCTYPE`);\n }\n\n i += 9;\n let angleBracketsCount = 1;\n let hasBody = false;\n let comment = false;\n let entityCount = 0;\n\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === '<' && !comment) {\n if (hasBody && hasSeq(xmlData, '!ENTITY', i)) {\n i += 7;\n [entityCount, i] = this._validateEntityExp(xmlData, i + 1, entityCount);\n // continue;\n } else if (hasBody && hasSeq(xmlData, '!ELEMENT', i)) {\n i += 8;\n i = this._validateElementExp(xmlData, i + 1);\n // continue;\n } else if (hasBody && hasSeq(xmlData, '!ATTLIST', i)) {\n i += 8;\n i = this._validateAttlistExp(xmlData, i + 1);\n continue;\n } else if (hasBody && hasSeq(xmlData, '!NOTATION', i)) {\n i += 9;\n i = this._validateNotationExp(xmlData, i + 1);\n // continue;\n } else if (hasSeq(xmlData, '!--', i)) {\n comment = true;\n } else {\n throw new Error(`Invalid DOCTYPE`);\n }\n angleBracketsCount++;\n } else if (xmlData[i] === '>') {\n if (comment) {\n if (xmlData[i - 1] === '-' && xmlData[i - 2] === '-') {\n comment = false;\n angleBracketsCount--;\n }\n } else {\n angleBracketsCount--;\n }\n if (angleBracketsCount === 0) break;\n } else if (xmlData[i] === '[') {\n hasBody = true;\n }\n }\n\n if (angleBracketsCount !== 0) {\n throw new Error(`Unclosed DOCTYPE`);\n }\n\n return i;\n }\n\n _validateEntityExp(xmlData, i, entityCount) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate entity name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i]) && xmlData[i] !== '\"' && xmlData[i] !== \"'\") {\n i++;\n }\n const entityName = xmlData.substring(nameStart, i);\n validateName(entityName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Check for unsupported constructs\n if (!this.suppressValidationErr) {\n if (xmlData.substring(i, i + 6).toUpperCase() === 'SYSTEM') {\n throw new Error('External entities are not supported');\n } else if (xmlData[i] === '%') {\n throw new Error('Parameter entities are not supported');\n }\n }\n\n // Read and validate the entity value (validates quoting + size limit)\n let entityValue;\n [i, entityValue] = readQuotedValue(xmlData, i, 'entity');\n\n const { maxEntitySize, maxEntityCount } = this.options;\n\n if (entityValue.length > maxEntitySize) {\n throw new Error(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${maxEntitySize})`\n );\n }\n\n // Only count non-parameter entities (parameter entities contain '&')\n if (entityValue.indexOf('&') === -1) {\n entityCount++;\n if (entityCount > maxEntityCount) {\n throw new Error(\n `Entity count (${entityCount}) exceeds maximum allowed (${maxEntityCount})`\n );\n }\n }\n\n return [entityCount, i - 1];\n }\n\n _validateNotationExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate notation name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const notationName = xmlData.substring(nameStart, i);\n !this.suppressValidationErr && validateName(notationName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n const identifierType = xmlData.substring(i, i + 6).toUpperCase();\n if (!this.suppressValidationErr && identifierType !== 'SYSTEM' && identifierType !== 'PUBLIC') {\n throw new Error(`Expected SYSTEM or PUBLIC, found \"${identifierType}\"`);\n }\n i += identifierType.length;\n i = skipWhitespace(xmlData, i);\n\n if (identifierType === 'PUBLIC') {\n [i] = readQuotedValue(xmlData, i, 'publicIdentifier');\n i = skipWhitespace(xmlData, i);\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n [i] = readQuotedValue(xmlData, i, 'systemIdentifier');\n }\n } else if (identifierType === 'SYSTEM') {\n [i] = readQuotedValue(xmlData, i, 'systemIdentifier');\n }\n\n return --i;\n }\n\n _validateElementExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate element name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const elementName = xmlData.substring(nameStart, i);\n if (!this.suppressValidationErr && !isXmlName(elementName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid element name: \"${elementName}\"`);\n }\n\n i = skipWhitespace(xmlData, i);\n\n // Validate content model keyword or group\n if (xmlData[i] === 'E' && hasSeq(xmlData, 'MPTY', i)) {\n i += 4;\n } else if (xmlData[i] === 'A' && hasSeq(xmlData, 'NY', i)) {\n i += 2;\n } else if (xmlData[i] === '(') {\n i++;\n while (i < xmlData.length && xmlData[i] !== ')') i++;\n if (xmlData[i] !== ')') throw new Error('Unterminated content model');\n } else if (!this.suppressValidationErr) {\n throw new Error(`Invalid Element Expression, found \"${xmlData[i]}\"`);\n }\n\n return i;\n }\n\n _validateAttlistExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n const elemNameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const elementName = xmlData.substring(elemNameStart, i);\n validateName(elementName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Read attribute name\n const attrNameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const attributeName = xmlData.substring(attrNameStart, i);\n validateName(attributeName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Read attribute type\n if (xmlData.substring(i, i + 8).toUpperCase() === 'NOTATION') {\n i += 8;\n i = skipWhitespace(xmlData, i);\n if (xmlData[i] !== '(') throw new Error(`Expected '(', found \"${xmlData[i]}\"`);\n i++;\n while (i < xmlData.length && xmlData[i] !== ')') {\n const notationStart = i;\n while (i < xmlData.length && xmlData[i] !== '|' && xmlData[i] !== ')') i++;\n const notation = xmlData.substring(notationStart, i).trim();\n validateName(notation, this.xmlVersion);\n if (xmlData[i] === '|') {\n i++;\n i = skipWhitespace(xmlData, i);\n }\n }\n if (xmlData[i] !== ')') throw new Error('Unterminated list of notations');\n i++;\n } else {\n const typeStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const attributeType = xmlData.substring(typeStart, i);\n const validTypes = ['CDATA', 'ID', 'IDREF', 'IDREFS', 'ENTITY', 'ENTITIES', 'NMTOKEN', 'NMTOKENS'];\n if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {\n throw new Error(`Invalid attribute type: \"${attributeType}\"`);\n }\n }\n\n i = skipWhitespace(xmlData, i);\n\n // Read default value declaration\n if (xmlData.substring(i, i + 9).toUpperCase() === '#REQUIRED') {\n i += 9;\n } else if (xmlData.substring(i, i + 8).toUpperCase() === '#IMPLIED') {\n i += 8;\n } else {\n [i] = readQuotedValue(xmlData, i, 'ATTLIST default value');\n }\n\n return i;\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction skipWhitespace(data, index) {\n while (index < data.length && /\\s/.test(data[index])) index++;\n return index;\n}\n\nfunction hasSeq(data, seq, i) {\n for (let j = 0; j < seq.length; j++) {\n if (seq[j] !== data[i + j + 1]) return false;\n }\n return true;\n}\n\nfunction validateName(name, xmlVersion = '1.0') {\n if (!isXmlName(name, { xmlVersion })) throw new Error(`Invalid entity name \"${name}\"`);\n}\n\n/**\n * Read a single- or double-quoted string and return [nextIndex, value].\n */\nfunction readQuotedValue(xmlData, i, type) {\n const startChar = xmlData[i];\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new Error(`Expected quoted string for ${type}, found \"${startChar}\"`);\n }\n i++;\n const start = i;\n while (i < xmlData.length && xmlData[i] !== startChar) i++;\n if (xmlData[i] !== startChar) throw new Error(`Unterminated ${type} value`);\n const value = xmlData.substring(start, i);\n return [++i, value];\n}","'use strict';\n\nimport ValidationError from './ValidationError.js';\nimport { isWhiteSpace, getLineNumberForPosition, isIllegalControlCode } from './util.js';\nimport XmlNameValidator from './validators/XmlNameValidator.js';\nimport XmlDeclarationValidator from './validators/XmlDeclarationValidator.js';\nimport AttributeStringValidator from './validators/AttributeStringValidator.js';\nimport CommentCdataReader from './validators/CommentCdataReader.js';\nimport ControlCharValidator from './validators/ControlCharValidator.js';\nimport SkipTagMatcher from './validators/SkipTagMatcher.js';\nimport RawSubtreeReader from './validators/RawSubtreeReader.js';\nimport DocTypeValidator from './validators/DocTypeValidator.js';\n\nconst defaultOptions = {\n allowBooleanAttributes: false,\n unpairedTags: [],\n docType: {\n maxEntityCount: Infinity,\n maxEntitySize: Infinity,\n },\n invalidCharSequence: {\n comment: false, // '--' inside a comment body\n tagValue: false, // ']]>' inside element text content\n attrLt: false, // '<' inside an attribute value\n },\n xmlDeclaraion: {\n optional: true,\n argPosition: true,\n },\n multipleRoots: true,\n skipTags: [],\n};\n\n/**\n * SyntaxValidator — validates that an XML document is well-formed.\n *\n * This is an orchestrator: it owns the top-level character scan and the\n * open/close tag stack, and delegates every other concern to a focused,\n * single-responsibility collaborator:\n *\n * - XmlDeclarationValidator — the `<?xml ... ?>` prolog\n * - DocTypeValidator — `<!DOCTYPE ...>`\n * - CommentCdataReader — `<!-- -->` and `<![CDATA[ ]]>`\n * - AttributeStringValidator — a tag's attribute expression\n * - XmlNameValidator — element/attribute/PI name well-formedness\n * - ControlCharValidator — illegal control characters (always on)\n * - SkipTagMatcher / RawSubtreeReader — `skipTags` support\n *\n * Usage: `new SyntaxValidator(options).validate(xmlData)` or the static\n * convenience `SyntaxValidator.validate(xmlData, options)`.\n */\nexport default class SyntaxValidator {\n constructor(options) {\n this.options = mergeOptions(defaultOptions, options);\n }\n\n /** Static convenience wrapper — matches the historical fxv v1 API. */\n static validate(xmlData, options) {\n return new SyntaxValidator(options).validate(xmlData);\n }\n\n validate(xmlData) {\n const options = this.options;\n\n const tags = [];\n let tagFound = false;\n let reachedRoot = false;\n let docTypeSeen = false;\n\n if (xmlData[0] === '\\ufeff') {\n xmlData = xmlData.substr(1);\n }\n\n const throwError = (code, message, lineNumber) => {\n throw new ValidationError(message, code, lineNumber.line || lineNumber, lineNumber.col);\n };\n\n const declValidator = new XmlDeclarationValidator(options.xmlDeclaraion, throwError);\n const declResult = declValidator.validate(xmlData);\n const xmlVersion = declResult.version;\n xmlData = xmlData.substring(declResult.end);\n\n const docTypeValidator = new DocTypeValidator(options.docType, xmlVersion);\n const commentCdataReader = new CommentCdataReader(options);\n const nameValidator = new XmlNameValidator(xmlVersion);\n const attrValidator = new AttributeStringValidator(options, nameValidator, xmlVersion);\n const skipMatcher = new SkipTagMatcher(options.skipTags);\n\n const checkTagValueSeq = options.invalidCharSequence?.tagValue === true;\n\n const checkDocTypePlacement = (startBang) => {\n const isDoctype = xmlData.substring(startBang, startBang + 9).toUpperCase() === '<!DOCTYPE';\n if (!isDoctype) return;\n if (docTypeSeen) {\n throwError('InvalidXml', 'Multiple DOCTYPE declarations found.', getLineNumberForPosition(xmlData, startBang));\n }\n if (tagFound || tags.length > 0) {\n throwError('InvalidXml', 'DOCTYPE must appear before the root element.', getLineNumberForPosition(xmlData, startBang));\n }\n docTypeSeen = true;\n };\n\n for (let i = 0; i < xmlData.length; i++) {\n if (xmlData[i] === '<' && xmlData[i + 1] === '?') {\n i += 2;\n i = this._readPI(xmlData, i, nameValidator, throwError);\n } else if (xmlData[i] === '<') {\n let tagStartPos = i;\n i++;\n\n if (xmlData[i] === '!') {\n try {\n checkDocTypePlacement(tagStartPos);\n i = commentCdataReader.read(xmlData, i, docTypeValidator);\n } catch (err) {\n throwError('InvalidDocType', err.message, getLineNumberForPosition(xmlData, i - 1));\n }\n continue;\n } else {\n let closingTag = false;\n if (xmlData[i] === '/') {\n closingTag = true;\n i++;\n }\n\n let tagName = '';\n for (;\n i < xmlData.length &&\n xmlData[i] !== '>' &&\n xmlData[i] !== ' ' &&\n xmlData[i] !== '\\t' &&\n xmlData[i] !== '\\n' &&\n xmlData[i] !== '\\r';\n i++\n ) {\n tagName += xmlData[i];\n }\n tagName = tagName.trim();\n\n if (tagName[tagName.length - 1] === '/') {\n tagName = tagName.substring(0, tagName.length - 1);\n i--;\n }\n\n if (!nameValidator.isValidQName(tagName)) {\n let msg;\n if (tagName.trim().length === 0) {\n msg = \"Invalid space after '<'.\";\n } else {\n msg = \"Tag '\" + tagName + \"' is an invalid name.\";\n }\n throwError('InvalidTag', msg, getLineNumberForPosition(xmlData, tagStartPos));\n }\n\n const result = this._readAttributeStr(xmlData, i);\n if (result === false) {\n throwError('InvalidAttr', \"Attributes for '\" + tagName + \"' have open quote.\", getLineNumberForPosition(xmlData, i));\n }\n let attrStr = result.value;\n i = result.index;\n\n if (attrStr[attrStr.length - 1] === '/') {\n const attrStrStart = i - attrStr.length;\n attrStr = attrStr.substring(0, attrStr.length - 1);\n const isValid = attrValidator.validate(attrStr);\n if (isValid === true) {\n if (reachedRoot === true && !options.multipleRoots) {\n throwError('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, tagStartPos));\n }\n tagFound = true;\n if (tags.length === 0) {\n reachedRoot = true;\n }\n skipMatcher.push(tagName);\n skipMatcher.pop();\n } else {\n throwError(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));\n }\n } else if (closingTag) {\n if (!result.tagClosed) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' doesn't have proper closing.\", getLineNumberForPosition(xmlData, i));\n } else if (attrStr.trim().length > 0) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' can't have attributes or invalid starting.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else if (tags.length === 0) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' has not been opened.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else {\n const otg = tags.pop();\n if (tagName !== otg.tagName) {\n let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);\n throwError(\n 'InvalidTag',\n \"Expected closing tag '\" + otg.tagName + \"' (opened in line \" + openPos.line + \", col \" + openPos.col + \") instead of closing tag '\" + tagName + \"'.\",\n getLineNumberForPosition(xmlData, tagStartPos)\n );\n }\n skipMatcher.pop();\n if (tags.length === 0) {\n reachedRoot = true;\n }\n }\n } else {\n // Opening tag — decide whether this subtree should be skipped\n // from validation entirely before validating its attributes.\n const skip = skipMatcher.push(tagName);\n\n if (!skip) {\n const isValid = attrValidator.validate(attrStr);\n if (isValid !== true) {\n throwError(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));\n }\n }\n\n if (reachedRoot === true && !options.multipleRoots) {\n throwError('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));\n } else if (options.unpairedTags.indexOf(tagName) !== -1) {\n skipMatcher.pop();\n } else if (skip) {\n // Consume the whole skipped subtree now, as opaque raw text,\n // then continue scanning right after its closing tag.\n i = RawSubtreeReader.consume(xmlData, tagName, i + 1);\n skipMatcher.pop();\n if (tags.length === 0) reachedRoot = true;\n tagFound = true;\n continue;\n } else {\n tags.push({ tagName, tagStartPos });\n }\n tagFound = true;\n }\n\n //skip tag text value\n //It may include comments and CDATA value\n let prevTwo = '';\n for (i++; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n if (xmlData[i + 1] === '!') {\n i++;\n try {\n checkDocTypePlacement(i - 1);\n i = commentCdataReader.read(xmlData, i, docTypeValidator);\n } catch (err) {\n throwError('InvalidDocType', err.message, getLineNumberForPosition(xmlData, i - 1));\n }\n prevTwo = '';\n continue;\n } else if (xmlData[i + 1] === '?') {\n i += 2;\n i = this._readPI(xmlData, i, nameValidator, throwError);\n prevTwo = '';\n } else {\n break;\n }\n } else if (xmlData[i] === '&') {\n const afterAmp = this._validateAmpersand(xmlData, i);\n if (afterAmp === -1)\n throwError('InvalidChar', \"char '&' is not expected.\", getLineNumberForPosition(xmlData, i));\n i = afterAmp;\n prevTwo = '';\n } else {\n const code = xmlData.charCodeAt(i);\n if (isIllegalControlCode(code)) {\n throwError('IllegalCharacter', `Illegal control character ${ControlCharValidator.describe(code)} in document content.`, getLineNumberForPosition(xmlData, i));\n }\n if (reachedRoot === true && tags.length === 0 && !isWhiteSpace(xmlData[i])) {\n throwError('InvalidXml', 'Extra text at the end', getLineNumberForPosition(xmlData, i));\n }\n if (checkTagValueSeq) {\n prevTwo = (prevTwo + xmlData[i]).slice(-2);\n if (prevTwo === ']]' && xmlData[i + 1] === '>') {\n throwError('InvalidXml', \"Element text content must not contain ']]>'.\", getLineNumberForPosition(xmlData, i - 1));\n }\n }\n }\n } //end of reading tag text value\n if (xmlData[i] === '<') {\n i--;\n }\n }\n } else {\n if (isWhiteSpace(xmlData[i])) {\n continue;\n }\n if (reachedRoot) {\n throwError('InvalidXml', \"Extra text at the end: '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n throwError('InvalidChar', \"char '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n }\n\n if (!tagFound) {\n throwError('InvalidXml', 'Start tag expected.', { line: 1, col: 1 });\n } else if (tags.length === 1) {\n throwError('InvalidTag', \"Unclosed tag '\" + tags[0].tagName + \"'.\", getLineNumberForPosition(xmlData, tags[0].tagStartPos));\n } else if (tags.length > 0) {\n throwError('InvalidXml', \"Invalid '\" +\n JSON.stringify(tags.map(t => t.tagName)).replace(/\\r?\\n/g, '') +\n \"' found.\", { line: 1, col: 1 });\n }\n\n return true;\n }\n\n _readPI(xmlData, i, nameValidator, throwError) {\n const piStart = i;\n const nameStart = i;\n let nameValidated = false;\n\n for (; i < xmlData.length; i++) {\n const ch = xmlData[i];\n\n if (!nameValidated && (ch === ' ' || ch === '?')) {\n const piName = xmlData.substr(nameStart, i - nameStart);\n nameValidated = true;\n\n if (piName.toLowerCase() === 'xml') {\n throwError(\n 'InvalidXml',\n 'XML declaration allowed only at the start of the document.',\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n } else if (!nameValidator.isValidName(piName)) {\n throwError(\n 'InvalidXml',\n `Processing instruction target \"${piName}\" is not a valid XML Name.`,\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n }\n }\n\n if (ch === '?' && xmlData[i + 1] === '>') {\n i++;\n return i;\n }\n }\n\n throwError(\n 'InvalidXml',\n 'Processing instruction is not closed with \"?>\".',\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n }\n\n _readAttributeStr(xmlData, i) {\n let attrStr = '';\n let startChar = '';\n let tagClosed = false;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n if (startChar === '') {\n startChar = xmlData[i];\n } else if (startChar !== xmlData[i]) {\n // opposite quote inside value — allowed\n } else {\n startChar = '';\n }\n } else if (xmlData[i] === '>') {\n if (startChar === '') {\n tagClosed = true;\n break;\n }\n }\n attrStr += xmlData[i];\n }\n if (startChar !== '') {\n return false;\n }\n return { value: attrStr, index: i, tagClosed };\n }\n\n _validateNumberAmpersand(xmlData, i) {\n let re = /\\d/;\n if (xmlData[i] === 'x') {\n i++;\n re = /[\\da-fA-F]/;\n }\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === ';') return i;\n if (!xmlData[i].match(re)) break;\n }\n return -1;\n }\n\n _validateAmpersand(xmlData, i) {\n i++;\n if (xmlData[i] === ';') return -1;\n if (xmlData[i] === '#') {\n i++;\n return this._validateNumberAmpersand(xmlData, i);\n }\n let count = 0;\n for (; i < xmlData.length; i++, count++) {\n if (xmlData[i].match(/\\w/) && count < 20) continue;\n if (xmlData[i] === ';') break;\n return -1;\n }\n return i;\n }\n}\n\nfunction mergeOptions(defaults, options) {\n const merged = Object.assign({}, defaults, options);\n merged.docType = Object.assign({}, defaults.docType, options?.docType);\n merged.invalidCharSequence = Object.assign({}, defaults.invalidCharSequence, options?.invalidCharSequence);\n merged.xmlDeclaraion = Object.assign(\n {},\n defaults.xmlDeclaraion,\n options?.xmlDeclaraion,\n options?.xmlDeclaration // accept the correctly-spelled alias too\n );\n merged.skipTags = options?.skipTags || defaults.skipTags;\n return merged;\n}\n\nexport { SyntaxValidator };\n","/**\n * ExpressionSet - An indexed collection of Expressions for efficient bulk matching\n *\n * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes\n * them at insertion time by depth and terminal tag name. At match time, only\n * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)\n * lookup plus O(small bucket) matches.\n *\n * Three buckets are maintained:\n * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)\n * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)\n * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)\n *\n * @example\n * import { Expression, ExpressionSet } from 'fast-xml-tagger';\n *\n * // Build once at config time\n * const stopNodes = new ExpressionSet();\n * stopNodes.add(new Expression('root.users.user'));\n * stopNodes.add(new Expression('root.config.setting'));\n * stopNodes.add(new Expression('..script'));\n *\n * // Query on every tag — hot path\n * if (stopNodes.matchesAny(matcher)) { ... }\n */\nexport default class ExpressionSet {\n constructor() {\n /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */\n this._byDepthAndTag = new Map();\n\n /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */\n this._wildcardByDepth = new Map();\n\n /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */\n this._deepWildcards = [];\n\n /** @type {Map<string, import('./Expression.js').default[]>} terminalTag → deep wildcard expressions */\n this._deepByTerminalTag = new Map();\n\n /** @type {Set<string>} pattern strings already added — used for deduplication */\n this._patterns = new Set();\n\n /** @type {boolean} whether the set is sealed against further additions */\n this._sealed = false;\n }\n\n /**\n * Add an Expression to the set.\n * Duplicate patterns (same pattern string) are silently ignored.\n *\n * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance\n * @returns {this} for chaining\n * @throws {TypeError} if called after seal()\n *\n * @example\n * set.add(new Expression('root.users.user'));\n * set.add(new Expression('..script'));\n */\n add(expression) {\n if (this._sealed) {\n throw new TypeError(\n 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'\n );\n }\n\n // Deduplicate by pattern string\n if (this._patterns.has(expression.pattern)) return this;\n this._patterns.add(expression.pattern);\n\n if (expression.hasDeepWildcard()) {\n const lastSeg = expression.segments[expression.segments.length - 1];\n if (lastSeg && lastSeg.type !== 'deep-wildcard' && lastSeg.tag !== '*') {\n const tag = lastSeg.tag;\n if (!this._deepByTerminalTag.has(tag)) this._deepByTerminalTag.set(tag, []);\n this._deepByTerminalTag.get(tag).push(expression);\n } else {\n this._deepWildcards.push(expression);\n }\n return this;\n }\n\n const depth = expression.length;\n const lastSeg = expression.segments[expression.segments.length - 1];\n const tag = lastSeg?.tag;\n\n if (!tag || tag === '*') {\n // Can index by depth but not by tag\n if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);\n this._wildcardByDepth.get(depth).push(expression);\n } else {\n // Tightest bucket: depth + tag\n const key = `${depth}:${tag}`;\n if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);\n this._byDepthAndTag.get(key).push(expression);\n }\n\n return this;\n }\n\n /**\n * Add multiple expressions at once.\n *\n * @param {import('./Expression.js').default[]} expressions - Array of Expression instances\n * @returns {this} for chaining\n *\n * @example\n * set.addAll([\n * new Expression('root.users.user'),\n * new Expression('root.config.setting'),\n * ]);\n */\n addAll(expressions) {\n for (const expr of expressions) this.add(expr);\n return this;\n }\n\n /**\n * Check whether a pattern string is already present in the set.\n *\n * @param {import('./Expression.js').default} expression\n * @returns {boolean}\n */\n has(expression) {\n return this._patterns.has(expression.pattern);\n }\n\n /**\n * Number of expressions in the set.\n * @type {number}\n */\n get size() {\n return this._patterns.size;\n }\n\n /**\n * Seal the set against further modifications.\n * Useful to prevent accidental mutations after config is built.\n * Calling add() or addAll() on a sealed set throws a TypeError.\n *\n * @returns {this}\n */\n seal() {\n this._sealed = true;\n return this;\n }\n\n /**\n * Whether the set has been sealed.\n * @type {boolean}\n */\n get isSealed() {\n return this._sealed;\n }\n\n /**\n * Test whether the matcher's current path matches any expression in the set.\n *\n * Evaluation order (cheapest → most expensive):\n * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions\n * 2. Depth-only wildcard bucket — O(1) lookup, rare\n * 3. Deep-wildcard list — always checked, but usually small\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {boolean} true if any expression matches the current path\n *\n * @example\n * if (stopNodes.matchesAny(matcher)) {\n * // handle stop node\n * }\n */\n matchesAny(matcher) {\n return this.findMatch(matcher) !== null;\n }\n /**\n * Find and return the first Expression that matches the matcher's current path.\n *\n * Uses the same evaluation order as matchesAny (cheapest → most expensive):\n * 1. Exact depth + tag bucket\n * 2. Depth-only wildcard bucket\n * 3. Deep-wildcard list\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {import('./Expression.js').default | null} the first matching Expression, or null\n *\n * @example\n * const expr = stopNodes.findMatch(matcher);\n * if (expr) {\n * // access expr.config, expr.pattern, etc.\n * }\n */\n findMatch(matcher) {\n const depth = matcher.getDepth();\n const tag = matcher.getCurrentTag();\n\n // 1. Tightest bucket — most expressions live here\n const exactKey = `${depth}:${tag}`;\n const exactBucket = this._byDepthAndTag.get(exactKey);\n if (exactBucket) {\n for (let i = 0; i < exactBucket.length; i++) {\n if (matcher.matches(exactBucket[i])) return exactBucket[i];\n }\n }\n\n // 2. Depth-matched wildcard-tag expressions\n const wildcardBucket = this._wildcardByDepth.get(depth);\n if (wildcardBucket) {\n for (let i = 0; i < wildcardBucket.length; i++) {\n if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];\n }\n }\n\n // 3. Deep wildcards — indexed by terminal tag, then unindexed fallback\n const deepBucket = this._deepByTerminalTag.get(tag);\n if (deepBucket) {\n for (let i = 0; i < deepBucket.length; i++) {\n if (matcher.matches(deepBucket[i])) return deepBucket[i];\n }\n }\n for (let i = 0; i < this._deepWildcards.length; i++) {\n if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];\n }\n\n return null;\n }\n}\n","import { Expression, ExpressionSet } from 'path-expression-matcher';\n\nconst defaultOptions = {\n nameFor: {\n // text: \"#text\",\n // comment: \"\",\n // cdata: \"\",\n },\n skip: {\n // declaration: false,\n // pi: false,\n // attributes: true,\n // cdata: false,\n // comment: false,\n // nsPrefix: false,\n // tags: false,\n },\n tags: {\n valueParsers: [],\n // stopNodes: [],\n },\n attributes: {\n // prefix: \"@_\",\n // suffix: \"\",\n // groupBy: \"\",\n valueParsers: [],\n },\n textJoint: \"\",\n /**\n * Array of strings (tag names) or Expression objects.\n * Any match votes true; no match abstains (does not veto).\n * Combined with forceArray using equal-priority voting:\n * - Either explicit false → false (veto wins)\n * - Any true, none false → true\n * - All abstain → false (default)\n */\n alwaysArray: [],\n /**\n * Function to determine if a tag should be forced into an array.\n * Called with (matcher, isLeafNode) where:\n * - matcher: ReadOnlyMatcher - path matcher for current tag\n * - isLeafNode: boolean|null - null when not yet determinable\n * Returns: boolean - true to force array, false to veto, undefined to abstain\n */\n forceArray: null,\n\n /**\n * Boolean flag that forces creation of a text node for every tag.\n * When true, a text node is always created under nameFor.text even if\n * the tag has no other children or attributes.\n * Default: false (text node created only when tag has attributes or children)\n */\n forceTextNode: false,\n};\n\n// Default chains: replaceEntities first (expand references), then type coercion.\nconst defaultTagParsers = [\"ws\", \"entity\", \"boolean\", \"number\"];\nconst defaultAttrParsers = [\"entity\", \"number\", \"boolean\"];\n\n\nexport function buildOptions(options) {\n const finalOptions = deepClone(defaultOptions);\n\n if (!options || options.tags?.valueParsers === undefined) {\n finalOptions.tags.valueParsers = [...defaultTagParsers];\n }\n if (!options || options.attributes?.valueParsers === undefined) {\n finalOptions.attributes.valueParsers = [...defaultAttrParsers];\n }\n\n // Always build _alwaysArraySet so _resolveForceArray never crashes on a missing set.\n // Uses the user-supplied alwaysArray if provided, otherwise the default empty array.\n const alwaysArraySource = Array.isArray(options?.alwaysArray)\n ? options.alwaysArray\n : finalOptions.alwaysArray;\n const alwaysArraySet = new ExpressionSet();\n for (const entry of alwaysArraySource) {\n normalizeEntry(entry, 'alwaysArray', alwaysArraySet);\n }\n alwaysArraySet.seal();\n finalOptions._alwaysArraySet = alwaysArraySet;\n\n if (options) {\n copyProperties(finalOptions, options);\n }\n\n return finalOptions;\n}\n\nfunction deepClone(obj) {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(deepClone);\n const clone = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone(obj[key]);\n }\n return clone;\n}\n\nfunction copyProperties(target, source) {\n for (const key of Object.keys(source)) {\n // Guard against prototype pollution via option keys\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n if (typeof source[key] === 'function') {\n target[key] = source[key];\n } else if (Array.isArray(source[key])) {\n target[key] = source[key];\n } else if (typeof source[key] === 'object' && source[key] !== null) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n copyProperties(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n}\n\n\nfunction normalizeEntry(entry, optionName, set) {\n let pattern;\n\n if (typeof entry === 'string') {\n if (entry.length === 0) throw new Error(`${optionName} expression cannot be empty`);\n pattern = entry;\n } else if (typeof entry?.pattern === 'string' && entry.pattern.length > 0 && Array.isArray(entry?.segments)) {\n // Duck-type as Expression — avoids instanceof failure when the caller's copy of\n // path-expression-matcher resolves to a different module instance than ours\n // (e.g. parent project has its own node_modules/ copy, or CJS vs ESM mismatch).\n pattern = entry.toString();\n } else {\n // console.log(entry);\n // console.log(entry instanceof Expression);\n // console.log(typeof entry);\n throw new Error(\n `Invalid ${optionName} entry: expected a string, or Expression.`\n );\n }\n\n const expr = new Expression(pattern);\n set.add(expr);\n return expr;\n}","// ---------------------------------------------------------------------------\n// Complete HTML5 named entity reference\n// Organized by logical categories for easy maintenance and selective importing\n// ---------------------------------------------------------------------------\n\n/**\n * Basic Latin & Special Characters\n * @type {Record<string, string>}\n */\nexport const BASIC_LATIN = {\n amp: '&',\n AMP: '&',\n lt: '<',\n LT: '<',\n gt: '>',\n GT: '>',\n quot: '\"',\n QUOT: '\"',\n apos: \"'\",\n lsquo: '‘',\n rsquo: '’',\n ldquo: '“',\n rdquo: '”',\n lsquor: '‚',\n rsquor: '’',\n ldquor: '„',\n bdquo: '„',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n excl: '!',\n quest: '?',\n num: '#',\n dollar: '$',\n percent: '%',\n ast: '*',\n commat: '@',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n sol: '/',\n bsol: '\\\\',\n lbrace: '{',\n rbrace: '}',\n lbrack: '[',\n rbrack: ']',\n lpar: '(',\n rpar: ')',\n nbsp: '\\u00a0',\n iexcl: '¡',\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n COPY: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n REG: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n half: '½',\n frac34: '¾',\n iquest: '¿',\n times: '×',\n div: '÷',\n divide: '÷',\n};\n\n/**\n * Latin Extended & Accented Letters (A-Z)\n * @type {Record<string, string>}\n */\nexport const LATIN_ACCENTS = {\n Agrave: 'À',\n agrave: 'à',\n Aacute: 'Á',\n aacute: 'á',\n Acirc: 'Â',\n acirc: 'â',\n Atilde: 'Ã',\n atilde: 'ã',\n Auml: 'Ä',\n auml: 'ä',\n Aring: 'Å',\n aring: 'å',\n AElig: 'Æ',\n aelig: 'æ',\n Ccedil: 'Ç',\n ccedil: 'ç',\n Egrave: 'È',\n egrave: 'è',\n Eacute: 'É',\n eacute: 'é',\n Ecirc: 'Ê',\n ecirc: 'ê',\n Euml: 'Ë',\n euml: 'ë',\n Igrave: 'Ì',\n igrave: 'ì',\n Iacute: 'Í',\n iacute: 'í',\n Icirc: 'Î',\n icirc: 'î',\n Iuml: 'Ï',\n iuml: 'ï',\n ETH: 'Ð',\n eth: 'ð',\n Ntilde: 'Ñ',\n ntilde: 'ñ',\n Ograve: 'Ò',\n ograve: 'ò',\n Oacute: 'Ó',\n oacute: 'ó',\n Ocirc: 'Ô',\n ocirc: 'ô',\n Otilde: 'Õ',\n otilde: 'õ',\n Ouml: 'Ö',\n ouml: 'ö',\n Oslash: 'Ø',\n oslash: 'ø',\n Ugrave: 'Ù',\n ugrave: 'ù',\n Uacute: 'Ú',\n uacute: 'ú',\n Ucirc: 'Û',\n ucirc: 'û',\n Uuml: 'Ü',\n uuml: 'ü',\n Yacute: 'Ý',\n yacute: 'ý',\n THORN: 'Þ',\n thorn: 'þ',\n szlig: 'ß',\n yuml: 'ÿ',\n Yuml: 'Ÿ',\n};\n\n/**\n * Latin Extended (Letters with diacritics)\n * @type {Record<string, string>}\n */\nexport const LATIN_EXTENDED = {\n Amacr: 'Ā',\n amacr: 'ā',\n Abreve: 'Ă',\n abreve: 'ă',\n Aogon: 'Ą',\n aogon: 'ą',\n Cacute: 'Ć',\n cacute: 'ć',\n Ccirc: 'Ĉ',\n ccirc: 'ĉ',\n Cdot: 'Ċ',\n cdot: 'ċ',\n Ccaron: 'Č',\n ccaron: 'č',\n Dcaron: 'Ď',\n dcaron: 'ď',\n Dstrok: 'Đ',\n dstrok: 'đ',\n Emacr: 'Ē',\n emacr: 'ē',\n Ecaron: 'Ě',\n ecaron: 'ě',\n Edot: 'Ė',\n edot: 'ė',\n Eogon: 'Ę',\n eogon: 'ę',\n Gcirc: 'Ĝ',\n gcirc: 'ĝ',\n Gbreve: 'Ğ',\n gbreve: 'ğ',\n Gdot: 'Ġ',\n gdot: 'ġ',\n Gcedil: 'Ģ',\n Hcirc: 'Ĥ',\n hcirc: 'ĥ',\n Hstrok: 'Ħ',\n hstrok: 'ħ',\n Itilde: 'Ĩ',\n itilde: 'ĩ',\n Imacr: 'Ī',\n imacr: 'ī',\n Iogon: 'Į',\n iogon: 'į',\n Idot: 'İ',\n IJlig: 'IJ',\n ijlig: 'ij',\n Jcirc: 'Ĵ',\n jcirc: 'ĵ',\n Kcedil: 'Ķ',\n kcedil: 'ķ',\n kgreen: 'ĸ',\n Lacute: 'Ĺ',\n lacute: 'ĺ',\n Lcedil: 'Ļ',\n lcedil: 'ļ',\n Lcaron: 'Ľ',\n lcaron: 'ľ',\n Lmidot: 'Ŀ',\n lmidot: 'ŀ',\n Lstrok: 'Ł',\n lstrok: 'ł',\n Nacute: 'Ń',\n nacute: 'ń',\n Ncaron: 'Ň',\n ncaron: 'ň',\n Ncedil: 'Ņ',\n ncedil: 'ņ',\n ENG: 'Ŋ',\n eng: 'ŋ',\n Omacr: 'Ō',\n omacr: 'ō',\n Odblac: 'Ő',\n odblac: 'ő',\n OElig: 'Œ',\n oelig: 'œ',\n Racute: 'Ŕ',\n racute: 'ŕ',\n Rcaron: 'Ř',\n rcaron: 'ř',\n Rcedil: 'Ŗ',\n rcedil: 'ŗ',\n Sacute: 'Ś',\n sacute: 'ś',\n Scirc: 'Ŝ',\n scirc: 'ŝ',\n Scedil: 'Ş',\n scedil: 'ş',\n Scaron: 'Š',\n scaron: 'š',\n Tcedil: 'Ţ',\n tcedil: 'ţ',\n Tcaron: 'Ť',\n tcaron: 'ť',\n Tstrok: 'Ŧ',\n tstrok: 'ŧ',\n Utilde: 'Ũ',\n utilde: 'ũ',\n Umacr: 'Ū',\n umacr: 'ū',\n Ubreve: 'Ŭ',\n ubreve: 'ŭ',\n Uring: 'Ů',\n uring: 'ů',\n Udblac: 'Ű',\n udblac: 'ű',\n Uogon: 'Ų',\n uogon: 'ų',\n Wcirc: 'Ŵ',\n wcirc: 'ŵ',\n Ycirc: 'Ŷ',\n ycirc: 'ŷ',\n Zacute: 'Ź',\n zacute: 'ź',\n Zdot: 'Ż',\n zdot: 'ż',\n Zcaron: 'Ž',\n zcaron: 'ž',\n};\n\n/**\n * Greek Letters\n * @type {Record<string, string>}\n */\nexport const GREEK = {\n Alpha: 'Α',\n alpha: 'α',\n Beta: 'Β',\n beta: 'β',\n Gamma: 'Γ',\n gamma: 'γ',\n Delta: 'Δ',\n delta: 'δ',\n Epsilon: 'Ε',\n epsilon: 'ε',\n epsiv: 'ϵ',\n varepsilon: 'ϵ',\n Zeta: 'Ζ',\n zeta: 'ζ',\n Eta: 'Η',\n eta: 'η',\n Theta: 'Θ',\n theta: 'θ',\n thetasym: 'ϑ',\n vartheta: 'ϑ',\n Iota: 'Ι',\n iota: 'ι',\n Kappa: 'Κ',\n kappa: 'κ',\n kappav: 'ϰ',\n varkappa: 'ϰ',\n Lambda: 'Λ',\n lambda: 'λ',\n Mu: 'Μ',\n mu: 'μ',\n Nu: 'Ν',\n nu: 'ν',\n Xi: 'Ξ',\n xi: 'ξ',\n Omicron: 'Ο',\n omicron: 'ο',\n Pi: 'Π',\n pi: 'π',\n piv: 'ϖ',\n varpi: 'ϖ',\n Rho: 'Ρ',\n rho: 'ρ',\n rhov: 'ϱ',\n varrho: 'ϱ',\n Sigma: 'Σ',\n sigma: 'σ',\n sigmaf: 'ς',\n sigmav: 'ς',\n varsigma: 'ς',\n Tau: 'Τ',\n tau: 'τ',\n Upsilon: 'Υ',\n upsilon: 'υ',\n upsi: 'υ',\n Upsi: 'ϒ',\n upsih: 'ϒ',\n Phi: 'Φ',\n phi: 'φ',\n phiv: 'ϕ',\n varphi: 'ϕ',\n Chi: 'Χ',\n chi: 'χ',\n Psi: 'Ψ',\n psi: 'ψ',\n Omega: 'Ω',\n omega: 'ω',\n ohm: 'Ω',\n Gammad: 'Ϝ',\n gammad: 'ϝ',\n digamma: 'ϝ',\n};\n\n/**\n * Cyrillic Letters\n * @type {Record<string, string>}\n */\nexport const CYRILLIC = {\n Afr: '𝔄',\n afr: '𝔞',\n Acy: 'А',\n acy: 'а',\n Bcy: 'Б',\n bcy: 'б',\n Vcy: 'В',\n vcy: 'в',\n Gcy: 'Г',\n gcy: 'г',\n Dcy: 'Д',\n dcy: 'д',\n IEcy: 'Е',\n iecy: 'е',\n IOcy: 'Ё',\n iocy: 'ё',\n ZHcy: 'Ж',\n zhcy: 'ж',\n Zcy: 'З',\n zcy: 'з',\n Icy: 'И',\n icy: 'и',\n Jcy: 'Й',\n jcy: 'й',\n Kcy: 'К',\n kcy: 'к',\n Lcy: 'Л',\n lcy: 'л',\n Mcy: 'М',\n mcy: 'м',\n Ncy: 'Н',\n ncy: 'н',\n Ocy: 'О',\n ocy: 'о',\n Pcy: 'П',\n pcy: 'п',\n Rcy: 'Р',\n rcy: 'р',\n Scy: 'С',\n scy: 'с',\n Tcy: 'Т',\n tcy: 'т',\n Ucy: 'У',\n ucy: 'у',\n Fcy: 'Ф',\n fcy: 'ф',\n KHcy: 'Х',\n khcy: 'х',\n TScy: 'Ц',\n tscy: 'ц',\n CHcy: 'Ч',\n chcy: 'ч',\n SHcy: 'Ш',\n shcy: 'ш',\n SHCHcy: 'Щ',\n shchcy: 'щ',\n HARDcy: 'Ъ',\n hardcy: 'ъ',\n Ycy: 'Ы',\n ycy: 'ы',\n SOFTcy: 'Ь',\n softcy: 'ь',\n Ecy: 'Э',\n ecy: 'э',\n YUcy: 'Ю',\n yucy: 'ю',\n YAcy: 'Я',\n yacy: 'я',\n DJcy: 'Ђ',\n djcy: 'ђ',\n GJcy: 'Ѓ',\n gjcy: 'ѓ',\n Jukcy: 'Є',\n jukcy: 'є',\n DScy: 'Ѕ',\n dscy: 'ѕ',\n Iukcy: 'І',\n iukcy: 'і',\n YIcy: 'Ї',\n yicy: 'ї',\n Jsercy: 'Ј',\n jsercy: 'ј',\n LJcy: 'Љ',\n ljcy: 'љ',\n NJcy: 'Њ',\n njcy: 'њ',\n TSHcy: 'Ћ',\n tshcy: 'ћ',\n KJcy: 'Ќ',\n kjcy: 'ќ',\n Ubrcy: 'Ў',\n ubrcy: 'ў',\n DZcy: 'Џ',\n dzcy: 'џ',\n};\n\n/**\n * Mathematical Operators & Relations\n * @type {Record<string, string>}\n */\nexport const MATH = {\n plus: '+',\n pm: '±',\n times: '×',\n div: '÷',\n divide: '÷',\n sdot: '⋅',\n star: '☆',\n starf: '★',\n bigstar: '★',\n lowast: '∗',\n ast: '*',\n midast: '*',\n compfn: '∘',\n smallcircle: '∘',\n bullet: '•',\n bull: '•',\n nbsp: '\\u00a0',\n hellip: '…',\n mldr: '…',\n prime: '′',\n Prime: '″',\n tprime: '‴',\n bprime: '‵',\n backprime: '‵',\n minus: '−',\n minusd: '∸',\n dotminus: '∸',\n plusdo: '∔',\n dotplus: '∔',\n plusmn: '±',\n minusplus: '∓',\n mnplus: '∓',\n mp: '∓',\n setminus: '∖',\n smallsetminus: '∖',\n Backslash: '∖',\n setmn: '∖',\n ssetmn: '∖',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n VerticalLine: '|',\n colon: ':',\n Colon: '∷',\n Proportion: '∷',\n ratio: '∶',\n equals: '=',\n ne: '≠',\n nequiv: '≢',\n equiv: '≡',\n Congruent: '≡',\n sim: '∼',\n thicksim: '∼',\n thksim: '∼',\n sime: '≃',\n simeq: '≃',\n TildeEqual: '≃',\n asymp: '≈',\n approx: '≈',\n thickapprox: '≈',\n thkap: '≈',\n TildeTilde: '≈',\n ncong: '≇',\n cong: '≅',\n TildeFullEqual: '≅',\n asympeq: '≍',\n CupCap: '≍',\n bump: '≎',\n Bumpeq: '≎',\n HumpDownHump: '≎',\n bumpe: '≏',\n bumpeq: '≏',\n HumpEqual: '≏',\n le: '≤',\n LessEqual: '≤',\n ge: '≥',\n GreaterEqual: '≥',\n lesseqgtr: '⋚',\n lesseqqgtr: '⪋',\n greater: '>',\n less: '<',\n};\n\n/**\n * Mathematical Operators (Advanced)\n * @type {Record<string, string>}\n */\nexport const MATH_ADVANCED = {\n alefsym: 'ℵ',\n aleph: 'ℵ',\n beth: 'ℶ',\n gimel: 'ℷ',\n daleth: 'ℸ',\n forall: '∀',\n ForAll: '∀',\n part: '∂',\n PartialD: '∂',\n exist: '∃',\n Exists: '∃',\n nexist: '∄',\n nexists: '∄',\n empty: '∅',\n emptyset: '∅',\n emptyv: '∅',\n varnothing: '∅',\n nabla: '∇',\n Del: '∇',\n isin: '∈',\n isinv: '∈',\n in: '∈',\n Element: '∈',\n notin: '∉',\n notinva: '∉',\n ni: '∋',\n niv: '∋',\n SuchThat: '∋',\n ReverseElement: '∋',\n notni: '∌',\n notniva: '∌',\n prod: '∏',\n Product: '∏',\n coprod: '∐',\n Coproduct: '∐',\n sum: '∑',\n Sum: '∑',\n minus: '−',\n mp: '∓',\n plusdo: '∔',\n dotplus: '∔',\n setminus: '∖',\n lowast: '∗',\n radic: '√',\n Sqrt: '√',\n prop: '∝',\n propto: '∝',\n Proportional: '∝',\n varpropto: '∝',\n infin: '∞',\n infintie: '⧝',\n ang: '∠',\n angle: '∠',\n angmsd: '∡',\n measuredangle: '∡',\n angsph: '∢',\n mid: '∣',\n VerticalBar: '∣',\n nmid: '∤',\n nsmid: '∤',\n npar: '∦',\n parallel: '∥',\n spar: '∥',\n nparallel: '∦',\n nspar: '∦',\n and: '∧',\n wedge: '∧',\n or: '∨',\n vee: '∨',\n cap: '∩',\n cup: '∪',\n int: '∫',\n Integral: '∫',\n conint: '∮',\n ContourIntegral: '∮',\n Conint: '∯',\n DoubleContourIntegral: '∯',\n Cconint: '∰',\n there4: '∴',\n therefore: '∴',\n Therefore: '∴',\n becaus: '∵',\n because: '∵',\n Because: '∵',\n ratio: '∶',\n Proportion: '∷',\n minusd: '∸',\n dotminus: '∸',\n mDDot: '∺',\n homtht: '∻',\n sim: '∼',\n bsimg: '∽',\n backsim: '∽',\n ac: '∾',\n mstpos: '∾',\n acd: '∿',\n VerticalTilde: '≀',\n wr: '≀',\n wreath: '≀',\n nsime: '≄',\n nsimeq: '≄',\n ncong: '≇',\n simne: '≆',\n ncongdot: '⩭̸',\n ngsim: '≵',\n nsim: '≁',\n napprox: '≉',\n nap: '≉',\n ngeq: '≱',\n nge: '≱',\n nleq: '≰',\n nle: '≰',\n ngtr: '≯',\n ngt: '≯',\n nless: '≮',\n nlt: '≮',\n nprec: '⊀',\n npr: '⊀',\n nsucc: '⊁',\n nsc: '⊁',\n};\n\n/**\n * Arrows\n * @type {Record<string, string>}\n */\nexport const ARROWS = {\n larr: '←',\n leftarrow: '←',\n LeftArrow: '←',\n uarr: '↑',\n uparrow: '↑',\n UpArrow: '↑',\n rarr: '→',\n rightarrow: '→',\n RightArrow: '→',\n darr: '↓',\n downarrow: '↓',\n DownArrow: '↓',\n harr: '↔',\n leftrightarrow: '↔',\n LeftRightArrow: '↔',\n varr: '↕',\n updownarrow: '↕',\n UpDownArrow: '↕',\n nwarr: '↖',\n nwarrow: '↖',\n UpperLeftArrow: '↖',\n nearr: '↗',\n nearrow: '↗',\n UpperRightArrow: '↗',\n searr: '↘',\n searrow: '↘',\n LowerRightArrow: '↘',\n swarr: '↙',\n swarrow: '↙',\n LowerLeftArrow: '↙',\n lArr: '⇐',\n Leftarrow: '⇐',\n uArr: '⇑',\n Uparrow: '⇑',\n rArr: '⇒',\n Rightarrow: '⇒',\n dArr: '⇓',\n Downarrow: '⇓',\n hArr: '⇔',\n Leftrightarrow: '⇔',\n iff: '⇔',\n vArr: '⇕',\n Updownarrow: '⇕',\n lAarr: '⇚',\n Lleftarrow: '⇚',\n rAarr: '⇛',\n Rrightarrow: '⇛',\n lrarr: '⇆',\n leftrightarrows: '⇆',\n rlarr: '⇄',\n rightleftarrows: '⇄',\n lrhar: '⇋',\n leftrightharpoons: '⇋',\n ReverseEquilibrium: '⇋',\n rlhar: '⇌',\n rightleftharpoons: '⇌',\n Equilibrium: '⇌',\n udarr: '⇅',\n UpArrowDownArrow: '⇅',\n duarr: '⇵',\n DownArrowUpArrow: '⇵',\n llarr: '⇇',\n leftleftarrows: '⇇',\n rrarr: '⇉',\n rightrightarrows: '⇉',\n ddarr: '⇊',\n downdownarrows: '⇊',\n har: '↽',\n lhard: '↽',\n leftharpoondown: '↽',\n lharu: '↼',\n leftharpoonup: '↼',\n rhard: '⇁',\n rightharpoondown: '⇁',\n rharu: '⇀',\n rightharpoonup: '⇀',\n lsh: '↰',\n Lsh: '↰',\n rsh: '↱',\n Rsh: '↱',\n ldsh: '↲',\n rdsh: '↳',\n hookleftarrow: '↩',\n hookrightarrow: '↪',\n mapstoleft: '↤',\n mapstoup: '↥',\n map: '↦',\n mapsto: '↦',\n mapstodown: '↧',\n crarr: '↵',\n nleftarrow: '↚',\n nleftrightarrow: '↮',\n nrightarrow: '↛',\n nrarr: '↛',\n larrtl: '↢',\n rarrtl: '↣',\n leftarrowtail: '↢',\n rightarrowtail: '↣',\n twoheadleftarrow: '↞',\n twoheadrightarrow: '↠',\n Larr: '↞',\n Rarr: '↠',\n larrhk: '↩',\n rarrhk: '↪',\n larrlp: '↫',\n looparrowleft: '↫',\n rarrlp: '↬',\n looparrowright: '↬',\n harrw: '↭',\n leftrightsquigarrow: '↭',\n nrarrw: '↝̸',\n rarrw: '↝',\n rightsquigarrow: '↝',\n larrbfs: '⤟',\n rarrbfs: '⤠',\n nvHarr: '⤄',\n nvlArr: '⤂',\n nvrArr: '⤃',\n larrfs: '⤝',\n rarrfs: '⤞',\n Map: '⤅',\n larrsim: '⥳',\n rarrsim: '⥴',\n harrcir: '⥈',\n Uarrocir: '⥉',\n lurdshar: '⥊',\n ldrdhar: '⥧',\n ldrushar: '⥋',\n rdldhar: '⥩',\n lrhard: '⥭',\n uharr: '↾',\n uharl: '↿',\n dharr: '⇂',\n dharl: '⇃',\n Uarr: '↟',\n Darr: '↡',\n zigrarr: '⇝',\n nwArr: '⇖',\n neArr: '⇗',\n seArr: '⇘',\n swArr: '⇙',\n nharr: '↮',\n nhArr: '⇎',\n nlarr: '↚',\n nlArr: '⇍',\n nrArr: '⇏',\n larrb: '⇤',\n LeftArrowBar: '⇤',\n rarrb: '⇥',\n RightArrowBar: '⇥',\n};\n\n/**\n * Geometric Shapes\n * @type {Record<string, string>}\n */\nexport const SHAPES = {\n square: '□',\n Square: '□',\n squ: '□',\n squf: '▪',\n squarf: '▪',\n blacksquar: '▪',\n blacksquare: '▪',\n FilledVerySmallSquare: '▪',\n blk34: '▓',\n blk12: '▒',\n blk14: '░',\n block: '█',\n srect: '▭',\n rect: '▭',\n sdot: '⋅',\n sdotb: '⊡',\n dotsquare: '⊡',\n triangle: '▵',\n tri: '▵',\n trine: '▵',\n utri: '▵',\n triangledown: '▿',\n dtri: '▿',\n tridown: '▿',\n triangleleft: '◃',\n ltri: '◃',\n triangleright: '▹',\n rtri: '▹',\n blacktriangle: '▴',\n utrif: '▴',\n blacktriangledown: '▾',\n dtrif: '▾',\n blacktriangleleft: '◂',\n ltrif: '◂',\n blacktriangleright: '▸',\n rtrif: '▸',\n loz: '◊',\n lozenge: '◊',\n blacklozenge: '⧫',\n lozf: '⧫',\n bigcirc: '◯',\n xcirc: '◯',\n circ: 'ˆ',\n Circle: '○',\n cir: '○',\n o: '○',\n bullet: '•',\n bull: '•',\n hellip: '…',\n mldr: '…',\n nldr: '‥',\n boxh: '─',\n HorizontalLine: '─',\n boxv: '│',\n boxdr: '┌',\n boxdl: '┐',\n boxur: '└',\n boxul: '┘',\n boxvr: '├',\n boxvl: '┤',\n boxhd: '┬',\n boxhu: '┴',\n boxvh: '┼',\n boxH: '═',\n boxV: '║',\n boxdR: '╒',\n boxDr: '╓',\n boxDR: '╔',\n boxDl: '╕',\n boxdL: '╖',\n boxDL: '╗',\n boxuR: '╘',\n boxUr: '╙',\n boxUR: '╚',\n boxUl: '╜',\n boxuL: '╛',\n boxUL: '╝',\n boxvR: '╞',\n boxVr: '╟',\n boxVR: '╠',\n boxVl: '╢',\n boxvL: '╡',\n boxVL: '╣',\n boxHd: '╤',\n boxhD: '╥',\n boxHD: '╦',\n boxHu: '╧',\n boxhU: '╨',\n boxHU: '╩',\n boxvH: '╪',\n boxVh: '╫',\n boxVH: '╬',\n};\n\n/**\n * Punctuation & Diacritics\n * @type {Record<string, string>}\n */\nexport const PUNCTUATION = {\n excl: '!',\n iexcl: '¡',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n frac34: '¾',\n iquest: '¿',\n nbsp: '\\u00a0',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n vert: '|',\n Verbar: '‖',\n verbar: '|',\n dblac: '˝',\n circ: 'ˆ',\n caron: 'ˇ',\n breve: '˘',\n dot: '˙',\n ring: '˚',\n ogon: '˛',\n tilde: '˜',\n DiacriticalGrave: '`',\n DiacriticalAcute: '´',\n DiacriticalTilde: '˜',\n DiacriticalDot: '˙',\n DiacriticalDoubleAcute: '˝',\n grave: '`',\n};\n\n/**\n * Currency Symbols\n * @type {Record<string, string>}\n */\nexport const CURRENCY = {\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n euro: '€',\n dollar: '$',\n fnof: 'ƒ',\n inr: '₹',\n af: '؋',\n birr: 'ብር',\n peso: '₱',\n rub: '₽',\n won: '₩',\n yuan: '¥',\n cedil: '¸',\n};\n\n/**\n * Fractions\n * @type {Record<string, string>}\n */\nexport const FRACTIONS = {\n frac12: '½',\n half: '½',\n frac13: '⅓',\n frac14: '¼',\n frac15: '⅕',\n frac16: '⅙',\n frac18: '⅛',\n frac23: '⅔',\n frac25: '⅖',\n frac34: '¾',\n frac35: '⅗',\n frac38: '⅜',\n frac45: '⅘',\n frac56: '⅚',\n frac58: '⅝',\n frac78: '⅞',\n frasl: '⁄',\n};\n\n/**\n * Miscellaneous Symbols\n * @type {Record<string, string>}\n */\nexport const MISC_SYMBOLS = {\n trade: '™',\n TRADE: '™',\n telrec: '⌕',\n target: '⌖',\n ulcorn: '⌜',\n ulcorner: '⌜',\n urcorn: '⌝',\n urcorner: '⌝',\n dlcorn: '⌞',\n llcorner: '⌞',\n drcorn: '⌟',\n lrcorner: '⌟',\n intercal: '⊺',\n intcal: '⊺',\n oplus: '⊕',\n CirclePlus: '⊕',\n ominus: '⊖',\n CircleMinus: '⊖',\n otimes: '⊗',\n CircleTimes: '⊗',\n osol: '⊘',\n odot: '⊙',\n CircleDot: '⊙',\n oast: '⊛',\n circledast: '⊛',\n odash: '⊝',\n circleddash: '⊝',\n ocirc: '⊚',\n circledcirc: '⊚',\n boxplus: '⊞',\n plusb: '⊞',\n boxminus: '⊟',\n minusb: '⊟',\n boxtimes: '⊠',\n timesb: '⊠',\n boxdot: '⊡',\n sdotb: '⊡',\n veebar: '⊻',\n vee: '∨',\n barvee: '⊽',\n and: '∧',\n wedge: '∧',\n Cap: '⋒',\n Cup: '⋓',\n Fork: '⋔',\n pitchfork: '⋔',\n epar: '⋕',\n ltlarr: '⥶',\n nvap: '≍⃒',\n nvsim: '∼⃒',\n nvge: '≥⃒',\n nvle: '≤⃒',\n nvlt: '<⃒',\n nvgt: '>⃒',\n nvltrie: '⊴⃒',\n nvrtrie: '⊵⃒',\n Vdash: '⊩',\n dashv: '⊣',\n vDash: '⊨',\n Vvdash: '⊪',\n nvdash: '⊬',\n nvDash: '⊭',\n nVdash: '⊮',\n nVDash: '⊯',\n};\n\n/**\n * All entities combined (if you need everything)\n * @type {Record<string, string>}\n */\nexport const ALL_ENTITIES = {\n ...BASIC_LATIN,\n ...LATIN_ACCENTS,\n ...LATIN_EXTENDED,\n ...GREEK,\n ...CYRILLIC,\n ...MATH,\n ...MATH_ADVANCED,\n ...ARROWS,\n ...SHAPES,\n ...PUNCTUATION,\n ...CURRENCY,\n ...FRACTIONS,\n ...MISC_SYMBOLS,\n};\n\nexport const XML = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n quot: \"\\\"\"\n}\nexport const COMMON_HTML = {\n nbsp: '\\u00a0',\n copy: '\\u00a9',\n reg: '\\u00ae',\n trade: '\\u2122',\n mdash: '\\u2014',\n ndash: '\\u2013',\n hellip: '\\u2026',\n laquo: '\\u00ab',\n raquo: '\\u00bb',\n lsquo: '\\u2018',\n rsquo: '\\u2019',\n ldquo: '\\u201c',\n rdquo: '\\u201d',\n bull: '\\u2022',\n para: '\\u00b6',\n sect: '\\u00a7',\n deg: '\\u00b0',\n frac12: '\\u00bd',\n frac14: '\\u00bc',\n frac34: '\\u00be',\n}\n// ---------------------------------------------------------------------------\n// Note: NUMERIC_ENTITIES (&#NNN; / &#xHH;) are handled by the scanner directly\n// via String.fromCodePoint() without any map lookup.\n// ---------------------------------------------------------------------------","// ---------------------------------------------------------------------------\n// Built-in named entity map (name → replacement string)\n// No regex, no {regex,val} objects — just flat key/value pairs.\n// ---------------------------------------------------------------------------\n\nimport { XML as DEFAULT_XML_ENTITIES } from \"./entities.js\"\n\n// ---------------------------------------------------------------------------\n// Entity hook action constants\n// ---------------------------------------------------------------------------\n\n/**\n * Action constants for `onExternalEntity` and `onInputEntity` hooks.\n *\n * Use these instead of raw strings to avoid typos:\n *\n * @example\n * import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';\n * const dec = new EntityDecoder({\n * onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,\n * });\n */\nexport const ENTITY_ACTION = Object.freeze({\n /** Resolve and expand the entity normally. */\n ALLOW: 'allow',\n /** Silently skip this entity — it will not be registered. */\n BLOCK: 'block',\n /** Throw an error, aborting entity registration entirely. */\n THROW: 'throw',\n});\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst SPECIAL_CHARS = new Set('!?\\\\\\\\/[]$%{}^&*()<>|+');\n\n/**\n * Validate that an entity name contains no dangerous characters.\n * @param {string} name\n * @returns {string} the name, unchanged\n * @throws {Error} on invalid characters\n */\nfunction validateEntityName(name) {\n if (name[0] === '#') {\n throw new Error(`[EntityReplacer] Invalid character '#' in entity name: \"${name}\"`);\n }\n for (const ch of name) {\n if (SPECIAL_CHARS.has(ch)) {\n throw new Error(`[EntityReplacer] Invalid character '${ch}' in entity name: \"${name}\"`);\n }\n }\n return name;\n}\n\n/**\n * Merge one or more entity maps into a flat name→string map.\n * Accepts either:\n * - plain string values: { amp: '&' }\n * - legacy {regex,val} / {regx,val}: { lt: { regex: /.../, val: '<' } }\n *\n * Values containing '&' are skipped (recursive expansion risk).\n *\n * @param {...object} maps\n * @returns {Record<string, string>}\n */\nfunction mergeEntityMaps(...maps) {\n const out = Object.create(null);\n for (const map of maps) {\n if (!map) continue;\n for (const key of Object.keys(map)) {\n const raw = map[key];\n if (typeof raw === 'string') {\n out[key] = raw;\n } else if (raw && typeof raw === 'object' && raw.val !== undefined) {\n // Legacy {regex,val} or {regx,val} — extract the string val only\n const val = raw.val;\n if (typeof val === 'string') {\n out[key] = val;\n }\n // function vals are not supported in the scanner — skip\n }\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// applyLimitsTo helpers\n// ---------------------------------------------------------------------------\n\nconst LIMIT_TIER_EXTERNAL = 'external'; // input/runtime + persistent external maps\nconst LIMIT_TIER_BASE = 'base'; // DEFAULT_XML_ENTITIES + namedEntities (system) maps\nconst LIMIT_TIER_ALL = 'all'; // every entity regardless of tier\n\n/**\n * Resolve `applyLimitsTo` option into a normalised Set of tier strings.\n * Accepted values: 'external' | 'base' | 'all' | string[]\n * Default: 'external' (only untrusted injected entities are counted).\n * @param {string|string[]|undefined} raw\n * @returns {Set<string>}\n */\nfunction parseLimitTiers(raw) {\n if (!raw || raw === LIMIT_TIER_EXTERNAL) return new Set([LIMIT_TIER_EXTERNAL]);\n if (raw === LIMIT_TIER_ALL) return new Set([LIMIT_TIER_ALL]);\n if (raw === LIMIT_TIER_BASE) return new Set([LIMIT_TIER_BASE]);\n if (Array.isArray(raw)) return new Set(raw);\n return new Set([LIMIT_TIER_EXTERNAL]); // safe default for unrecognised values\n}\n\n// ---------------------------------------------------------------------------\n// NCR (Numeric Character Reference) classification\n// ---------------------------------------------------------------------------\n\n// Severity order — higher number = stricter action.\n// Used to enforce minimum action levels for specific codepoint ranges.\nconst NCR_LEVEL = Object.freeze({ allow: 0, leave: 1, remove: 2, throw: 3 });\n\n// XML 1.0 §2.2: allowed chars are #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n// Restricted C0: U+0001–U+001F excluding U+0009, U+000A, U+000D\nconst XML10_ALLOWED_C0 = new Set([0x09, 0x0A, 0x0D]);\n\n/**\n * Parse the `ncr` constructor option into flat, hot-path-friendly fields.\n * @param {object|undefined} ncr\n * @returns {{ xmlVersion: number, onLevel: number, nullLevel: number }}\n */\nfunction parseNCRConfig(ncr) {\n if (!ncr) {\n return { xmlVersion: 1.0, onLevel: NCR_LEVEL.allow, nullLevel: NCR_LEVEL.remove };\n }\n const xmlVersion = ncr.xmlVersion === 1.1 ? 1.1 : 1.0;\n const onLevel = NCR_LEVEL[ncr.onNCR] ?? NCR_LEVEL.allow;\n const nullLevel = NCR_LEVEL[ncr.nullNCR] ?? NCR_LEVEL.remove;\n // 'allow' is not meaningful for null — clamp to at least 'remove'\n const clampedNull = Math.max(nullLevel, NCR_LEVEL.remove);\n return { xmlVersion, onLevel, nullLevel: clampedNull };\n}\n\n// ---------------------------------------------------------------------------\n// EntityReplacer\n// ---------------------------------------------------------------------------\n\n/**\n * Single-pass, zero-regex entity replacer for XML/HTML content.\n *\n * Algorithm: scan the string once for '&', read to ';', resolve via map\n * or direct codepoint conversion, build output chunks, join once at the end.\n *\n * Entity lookup priority (highest → lowest):\n * 1. input / runtime (DOCTYPE entities for current document)\n * 2. persistent external (survive across documents)\n * 3. base named map (DEFAULT_XML_ENTITIES + user-supplied namedEntities)\n *\n * Both input and external resolve as the 'external' tier for limit purposes.\n * Base map entities resolve as the 'base' tier.\n *\n * Numeric / hex references (&#NNN; / &#xHH;) are resolved directly via\n * String.fromCodePoint() — no map needed. They count as 'base' tier.\n *\n * @example\n * const replacer = new EntityReplacer({ namedEntities: COMMON_HTML });\n * replacer.setExternalEntities({ brand: 'Acme' });\n *\n * const instance = replacer.reset();\n * instance.addInputEntities({ version: '1.0' });\n * instance.encode('&brand; v&version; <'); // 'Acme v1.0 <'\n */\nexport default class EntityDecoder {\n /**\n * @param {object} [options]\n * @param {object|null} [options.namedEntities] — extra named entities merged into base map\n * @param {object} [options.limit] — security limits\n * @param {number} [options.limit.maxTotalExpansions=0] — 0 = unlimited\n * @param {number} [options.limit.maxExpandedLength=0] — 0 = unlimited\n * @param {'external'|'base'|'all'|string[]} [options.limit.applyLimitsTo='external']\n * Which entity tiers count against the security limits:\n * - 'external' (default) — only input/runtime + persistent external entities\n * - 'base' — only DEFAULT_XML_ENTITIES + namedEntities\n * - 'all' — every entity regardless of tier\n * - string[] — explicit combination, e.g. ['external', 'base']\n * @param {((resolved: string, original: string) => string)|null} [options.postCheck=null]\n * @param {string[]} [options.remove=[]] — entity names (e.g. ['nbsp', '#13']) to delete (replace with empty string)\n * @param {string[]} [options.leave=[]] — entity names to keep as literal (unchanged in output)\n * @param {object} [options.ncr] — Numeric Character Reference controls\n * @param {1.0|1.1} [options.ncr.xmlVersion=1.0]\n * XML version governing which codepoint ranges are restricted:\n * - 1.0 — C0 controls U+0001–U+001F (except U+0009/000A/000D) are prohibited\n * - 1.1 — C0 controls are allowed when written as NCRs; C1 (U+007F–U+009F) decoded as-is\n * @param {'allow'|'leave'|'remove'|'throw'} [options.ncr.onNCR='allow']\n * Base action for numeric references. Severity order: allow < leave < remove < throw.\n * For codepoint ranges that carry a minimum level (surrogates → remove, XML 1.0 C0 → remove),\n * the effective action is max(onNCR, rangeMinimum).\n * @param {'remove'|'throw'} [options.ncr.nullNCR='remove']\n * Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]\n * Hook called when an external entity is registered via `setExternalEntities()` or\n * `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,\n * `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]\n * Hook called when an input entity is registered via `addInputEntities()`. Return\n * `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or\n * `ENTITY_ACTION.THROW` to abort with an error.\n */\n constructor(options = {}) {\n this._limit = options.limit || {};\n this._maxTotalExpansions = this._limit.maxTotalExpansions || 0;\n this._maxExpandedLength = this._limit.maxExpandedLength || 0;\n this._postCheck = typeof options.postCheck === 'function' ? options.postCheck : r => r;\n this._limitTiers = parseLimitTiers(this._limit.applyLimitsTo ?? LIMIT_TIER_EXTERNAL);\n this._numericAllowed = options.numericAllowed ?? true;\n // Base map: DEFAULT_XML_ENTITIES + user-supplied extras. Immutable after construction.\n this._baseMap = mergeEntityMaps(DEFAULT_XML_ENTITIES, options.namedEntities || null);\n\n // Persistent external entities — survive across documents.\n // Stored as a separate map so reset() never touches them.\n /** @type {Record<string, string>} */\n this._externalMap = Object.create(null);\n\n // Input / runtime entities — current document only, wiped on reset().\n /** @type {Record<string, string>} */\n this._inputMap = Object.create(null);\n\n // Per-document counters\n this._totalExpansions = 0;\n this._expandedLength = 0;\n\n // --- New: remove / leave sets ---\n /** @type {Set<string>} */\n this._removeSet = new Set(options.remove && Array.isArray(options.remove) ? options.remove : []);\n /** @type {Set<string>} */\n this._leaveSet = new Set(options.leave && Array.isArray(options.leave) ? options.leave : []);\n\n // --- NCR config (parsed into flat fields for hot-path speed) ---\n const ncrCfg = parseNCRConfig(options.ncr);\n this._ncrXmlVersion = ncrCfg.xmlVersion;\n this._ncrOnLevel = ncrCfg.onLevel;\n this._ncrNullLevel = ncrCfg.nullLevel;\n\n // --- Registration hooks ---\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onExternalEntity = typeof options.onExternalEntity === 'function'\n ? options.onExternalEntity\n : null;\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onInputEntity = typeof options.onInputEntity === 'function'\n ? options.onInputEntity\n : null;\n }\n\n // -------------------------------------------------------------------------\n // Private: registration hook dispatch\n // -------------------------------------------------------------------------\n\n /**\n * Invoke a registration hook for a single entity name/value pair.\n * Returns true when the entity should be accepted, false when it should be\n * silently skipped (BLOCK), and throws when the hook returns THROW.\n *\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook\n * @param {string} name\n * @param {string} value\n * @param {string} context — used in error messages ('external' | 'input')\n * @returns {boolean} true = accept, false = skip\n */\n _applyRegistrationHook(hook, name, value, context) {\n if (!hook) return true; // no hook → always accept\n const action = hook(name, value);\n if (action === ENTITY_ACTION.BLOCK) return false;\n if (action === ENTITY_ACTION.THROW) {\n throw new Error(\n `[EntityDecoder] Registration of ${context} entity \"&${name};\" was rejected by hook`\n );\n }\n return true; // ALLOW or any unknown return value → accept\n }\n\n // -------------------------------------------------------------------------\n // Persistent external entity registration\n // -------------------------------------------------------------------------\n\n /**\n * Replace the full set of persistent external entities.\n * All keys are validated — throws on invalid characters.\n * If `onExternalEntity` is set, it is called once per entry; entries that\n * return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`\n * aborts the whole call.\n * @param {Record<string, string | { regex?: RegExp, val: string }>} map\n */\n setExternalEntities(map) {\n if (map) {\n for (const key of Object.keys(map)) {\n validateEntityName(key);\n }\n }\n if (!this._onExternalEntity) {\n this._externalMap = mergeEntityMaps(map);\n return;\n }\n // Hook present — resolve values first, then filter\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {\n filtered[name] = value;\n }\n }\n this._externalMap = filtered;\n }\n\n /**\n * Add a single persistent external entity.\n * If `onExternalEntity` is set it is called before the entity is stored;\n * `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.\n * @param {string} key\n * @param {string} value\n */\n addExternalEntity(key, value) {\n validateEntityName(key);\n if (typeof value === 'string' && value.indexOf('&') === -1) {\n if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {\n this._externalMap[key] = value;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Input / runtime entity registration (per document)\n // -------------------------------------------------------------------------\n\n /**\n * Inject DOCTYPE entities for the current document.\n * Also resets per-document expansion counters.\n * If `onInputEntity` is set it is called once per entry; entries returning\n * `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.\n * @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map\n */\n addInputEntities(map) {\n this._totalExpansions = 0;\n this._expandedLength = 0;\n if (!this._onInputEntity) {\n this._inputMap = mergeEntityMaps(map);\n return;\n }\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {\n filtered[name] = value;\n }\n }\n this._inputMap = filtered;\n }\n\n // -------------------------------------------------------------------------\n // Per-document reset\n // -------------------------------------------------------------------------\n\n /**\n * Wipe input/runtime entities and reset counters.\n * Call this before processing each new document.\n * @returns {this}\n */\n reset() {\n this._inputMap = Object.create(null);\n this._totalExpansions = 0;\n this._expandedLength = 0;\n return this;\n }\n\n // -------------------------------------------------------------------------\n // XML version (can be set after construction, e.g. once parser reads <?xml?>)\n // -------------------------------------------------------------------------\n\n /**\n * Update the XML version used for NCR classification.\n * Call this as soon as the document's `<?xml version=\"...\">` declaration is parsed.\n * @param {1.0|1.1|number} version\n */\n setXmlVersion(version) {\n this._ncrXmlVersion = version === 1.1 ? 1.1 : 1.0;\n }\n\n // -------------------------------------------------------------------------\n // Primary API\n // -------------------------------------------------------------------------\n\n /**\n * Replace all entity references in `str` in a single pass.\n *\n * @param {string} str\n * @returns {string}\n */\n decode(str) {\n if (typeof str !== 'string' || str.length === 0) return str;\n //TODO: check if needed\n if (str.indexOf('&') === -1) return str; // fast path — no entities at all\n\n const original = str;\n const chunks = [];\n const len = str.length;\n let last = 0; // start of next unprocessed literal chunk\n let i = 0;\n\n const limitExpansions = this._maxTotalExpansions > 0;\n const limitLength = this._maxExpandedLength > 0;\n const checkLimits = limitExpansions || limitLength;\n\n while (i < len) {\n // Scan forward to next '&'\n if (str.charCodeAt(i) !== 38 /* '&' */) { i++; continue; }\n\n // --- Found '&' at position i ---\n\n // Scan forward to ';'\n let j = i + 1;\n while (j < len && str.charCodeAt(j) !== 59 /* ';' */ && (j - i) <= 32) j++;\n\n if (j >= len || str.charCodeAt(j) !== 59) {\n // No closing ';' within window — treat '&' as literal\n i++;\n continue;\n }\n\n // Raw token between '&' and ';' (exclusive)\n const token = str.slice(i + 1, j);\n if (token.length === 0) { i++; continue; }\n\n let replacement;\n let tier; // which limit tier this entity belongs to\n\n if (this._removeSet.has(token)) {\n // Remove entity: replace with empty string\n replacement = '';\n // If entity was unknown (replacement undefined), we still need a tier for limits.\n // Treat as external tier because it's user-directed removal of an unknown reference.\n if (tier === undefined) {\n tier = LIMIT_TIER_EXTERNAL;\n }\n } else if (this._leaveSet.has(token)) {\n // Do not replace — keep original &token; as literal\n i++;\n continue;\n } else if (token.charCodeAt(0) === 35 /* '#' */) {\n // ---- Numeric / NCR reference ----\n // NCR classification always runs first — prohibited codepoints must be\n // caught regardless of numericAllowed.\n const ncrResult = this._resolveNCR(token);\n if (ncrResult === undefined) {\n // 'leave' action — keep original &token; as-is\n i++;\n continue;\n }\n replacement = ncrResult; // '' for remove, char string for allow\n tier = LIMIT_TIER_BASE;\n } else {\n // ---- Named reference ----\n const resolved = this._resolveName(token);\n replacement = resolved?.value;\n tier = resolved?.tier;\n }\n\n if (replacement === undefined) {\n // Unknown entity — leave as-is, advance past '&' only\n i++;\n continue;\n }\n\n // Flush literal chunk before this entity\n if (i > last) chunks.push(str.slice(last, i));\n chunks.push(replacement);\n last = j + 1; // skip past ';'\n i = last;\n\n // Apply expansion limits only if this tier is being tracked\n if (checkLimits && this._tierCounts(tier)) {\n if (limitExpansions) {\n this._totalExpansions++;\n if (this._totalExpansions > this._maxTotalExpansions) {\n throw new Error(\n `[EntityReplacer] Entity expansion count limit exceeded: ` +\n `${this._totalExpansions} > ${this._maxTotalExpansions}`\n );\n }\n }\n if (limitLength) {\n // delta: replacement.length minus the raw &token; length (token.length + 2 for '&' and ';')\n const delta = replacement.length - (token.length + 2);\n if (delta > 0) {\n this._expandedLength += delta;\n if (this._expandedLength > this._maxExpandedLength) {\n throw new Error(\n `[EntityReplacer] Expanded content length limit exceeded: ` +\n `${this._expandedLength} > ${this._maxExpandedLength}`\n );\n }\n }\n }\n }\n }\n\n // Flush trailing literal\n if (last < len) chunks.push(str.slice(last));\n\n // If nothing was replaced, chunks is empty — return original\n const result = chunks.length === 0 ? str : chunks.join('');\n\n return this._postCheck(result, original);\n }\n\n // -------------------------------------------------------------------------\n // Private: limit tier check\n // -------------------------------------------------------------------------\n\n /**\n * Returns true if a resolved entity of the given tier should count\n * against the expansion/length limits.\n * @param {string} tier — LIMIT_TIER_EXTERNAL | LIMIT_TIER_BASE\n * @returns {boolean}\n */\n _tierCounts(tier) {\n if (this._limitTiers.has(LIMIT_TIER_ALL)) return true;\n return this._limitTiers.has(tier);\n }\n\n // -------------------------------------------------------------------------\n // Private: entity resolution\n // -------------------------------------------------------------------------\n\n /**\n * Resolve a named entity token (without & and ;).\n * Priority: inputMap > externalMap > baseMap\n * Returns the resolved value tagged with its limit tier.\n *\n * @param {string} name\n * @returns {{ value: string, tier: string }|undefined}\n */\n _resolveName(name) {\n // input and external both count as 'external' tier for limit purposes —\n // they are injected at runtime and are the untrusted surface.\n if (name in this._inputMap) return { value: this._inputMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._externalMap) return { value: this._externalMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._baseMap) return { value: this._baseMap[name], tier: LIMIT_TIER_BASE };\n return undefined;\n }\n\n /**\n * Classify a codepoint and return the minimum action level that must be applied.\n * Returns -1 when no minimum is imposed (normal allow path).\n *\n * Ranges checked (in priority order):\n * 1. U+0000 — null, governed by nullNCR (always ≥ remove)\n * 2. U+D800–U+DFFF — surrogates, always prohibited (min: remove)\n * 3. U+0001–U+001F \\ {0x09,0x0A,0x0D} — XML 1.0 restricted C0 (min: remove)\n * (skipped in XML 1.1 — C0 controls are allowed when written as NCRs)\n *\n * @param {number} cp — codepoint\n * @returns {number} — minimum NCR_LEVEL value, or -1 for no restriction\n */\n _classifyNCR(cp) {\n // 1. Null\n if (cp === 0) return this._ncrNullLevel;\n\n // 2. Surrogates — always prohibited, minimum 'remove'\n if (cp >= 0xD800 && cp <= 0xDFFF) return NCR_LEVEL.remove;\n\n // 3. XML 1.0 restricted C0 controls\n if (this._ncrXmlVersion === 1.0) {\n if (cp >= 0x01 && cp <= 0x1F && !XML10_ALLOWED_C0.has(cp)) return NCR_LEVEL.remove;\n }\n\n return -1; // no restriction\n }\n\n /**\n * Execute a resolved NCR action.\n *\n * @param {number} action — NCR_LEVEL value\n * @param {string} token — raw token (e.g. '#38') for error messages\n * @param {number} cp — codepoint, used only for error messages\n * @returns {string|undefined}\n * - decoded character string → 'allow'\n * - '' → 'remove'\n * - undefined → 'leave' (caller must skip past '&' only)\n * - throws Error → 'throw'\n */\n _applyNCRAction(action, token, cp) {\n switch (action) {\n case NCR_LEVEL.allow: return String.fromCodePoint(cp);\n case NCR_LEVEL.remove: return '';\n case NCR_LEVEL.leave: return undefined; // signal: keep literal\n case NCR_LEVEL.throw:\n throw new Error(\n `[EntityDecoder] Prohibited numeric character reference ` +\n `&${token}; (U+${cp.toString(16).toUpperCase().padStart(4, '0')})`\n );\n default: return String.fromCodePoint(cp);\n }\n }\n\n /**\n * Full NCR resolution pipeline for a numeric token.\n *\n * Steps:\n * 1. Parse the codepoint (decimal or hex).\n * 2. Validate the raw codepoint range (NaN, <0, >0x10FFFF).\n * 3. If numericAllowed is false and no minimum restriction applies → leave as-is.\n * 4. Classify the codepoint to find the minimum required action level.\n * 5. Resolve effective action = max(onNCR, minimum).\n * 6. Apply and return.\n *\n * @param {string} token — e.g. '#38', '#x26', '#X26'\n * @returns {string|undefined}\n * - string (incl. '') — replacement ('' = remove)\n * - undefined — leave original &token; as-is\n */\n _resolveNCR(token) {\n // Step 1: parse codepoint\n const second = token.charCodeAt(1);\n let cp;\n if (second === 120 /* x */ || second === 88 /* X */) {\n cp = parseInt(token.slice(2), 16);\n } else {\n cp = parseInt(token.slice(1), 10);\n }\n\n // Step 2: out-of-range → leave as-is unconditionally\n if (Number.isNaN(cp) || cp < 0 || cp > 0x10FFFF) return undefined;\n\n // Step 3: classify to get minimum action level\n const minimum = this._classifyNCR(cp);\n\n // Step 4: if numericAllowed is false and no hard minimum → leave\n if (!this._numericAllowed && minimum < NCR_LEVEL.remove) return undefined;\n\n // Step 5: effective action = max(configured onNCR, range minimum)\n const effective = minimum === -1\n ? this._ncrOnLevel\n : Math.max(this._ncrOnLevel, minimum);\n\n // Step 6: apply\n return this._applyNCRAction(effective, token, cp);\n }\n}","/**\n * HTML context patterns.\n *\n * Detects XSS vectors that are dangerous when a string ends up rendered as HTML.\n * All patterns use bounded quantifiers to ensure linear-time matching (ReDoS-safe).\n *\n * Each entry is { pattern: RegExp, id: string, description: string }\n * so callers can inspect which rule fired if they need to.\n */\n\nconst HTML_PATTERNS = [\n {\n id: 'html-script-open',\n description: '<script opening tag',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'html-script-close',\n description: '</script closing tag',\n pattern: /<\\/script[\\s>]/i,\n },\n {\n id: 'html-javascript-protocol',\n description: 'javascript: URI scheme (with optional whitespace/encoding)',\n // Handles javascript:, j\\u0061vascript:, and whitespace variants\n pattern: /j[\\t\\n\\r ]*a[\\t\\n\\r ]*v[\\t\\n\\r ]*a[\\t\\n\\r ]*s[\\t\\n\\r ]*c[\\t\\n\\r ]*r[\\t\\n\\r ]*i[\\t\\n\\r ]*p[\\t\\n\\r ]*t[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-vbscript-protocol',\n description: 'vbscript: URI scheme',\n pattern: /vbscript[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-data-html',\n description: 'data:text/html URI — can execute scripts in browsers',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*text\\/html/i,\n },\n {\n id: 'html-data-xhtml',\n description: 'data:application/xhtml+xml URI',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*application\\/xhtml/i,\n },\n {\n id: 'html-data-svg',\n description: 'data:image/svg+xml URI — can execute scripts',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*image\\/svg\\+xml/i,\n },\n {\n id: 'html-inline-event-handler',\n description: 'Inline event handler attributes: onclick=, onerror=, onload=, etc.',\n // \\bon ensures we match a word boundary so \"phonetic=\" is not caught\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'html-entity-obfuscated-script',\n description: 'HTML-entity-encoded <script (e.g. <script or <script)',\n // Entities include optional trailing semicolon: < or < (both valid in HTML5)\n pattern: /(?:�*3[Cc];?|�*60;?|<)\\s*script/i,\n },\n {\n id: 'html-entity-obfuscated-javascript',\n description: 'HTML-entity-encoded javascript: (partial — catches common j or j for \"j\")',\n pattern: /(?:�*6[Aa];?|�*106;?)\\s*(?:�*61;?|a)[\\s\\S]{0,80}script\\s*:/i,\n },\n {\n id: 'html-style-expression',\n description: 'CSS expression() — IE-era code execution in style attributes',\n pattern: /style[\\s\\S]{0,20}expression\\s*\\(/i,\n },\n {\n id: 'html-object-embed',\n description: '<object or <embed tags that can load active content',\n pattern: /<(?:object|embed)[\\s>/]/i,\n },\n {\n id: 'html-base-tag',\n description: '<base href= — can hijack all relative URLs on a page',\n pattern: /<base[\\s>]/i,\n },\n {\n id: 'html-meta-refresh',\n description: '<meta http-equiv=\"refresh\" — can redirect users',\n pattern: /<meta[\\s\\S]{0,40}http-equiv[\\s\\S]{0,20}refresh/i,\n },\n {\n id: 'html-srcdoc',\n description: 'srcdoc= attribute on iframes — embeds HTML that can run scripts',\n pattern: /srcdoc\\s*=/i,\n },\n {\n id: 'html-iframe',\n description: '<iframe tag',\n pattern: /<iframe[\\s>/]/i,\n },\n {\n id: 'html-form',\n description: '<form tag — can be used for phishing / credential harvesting injection',\n pattern: /<form[\\s>/]/i,\n },\n];\n\nexport default HTML_PATTERNS;\n","/**\n * SQL context patterns — high-precision rules only.\n *\n * These rules have very low false-positive risk and are safe to apply to\n * general user text (names, descriptions, search queries, etc.).\n * All patterns are ReDoS-safe — unlike the `sql-injection` npm package\n * which has an active CVE on its own detection regexes.\n *\n * For exhaustive coverage including noisier heuristics (comment sequences,\n * hex literals, stacked queries with semicolons), use 'SQL-STRICT' instead.\n * Apply 'SQL-STRICT' only to strings that are specifically SQL fragments,\n * not to general free-text fields.\n */\n\nconst SQL_PATTERNS = [\n {\n id: 'sql-block-comment-open',\n description: 'SQL block comment open: /* ... */ — unusual in legitimate user text',\n pattern: /\\/\\*/,\n },\n {\n id: 'sql-union-select',\n description: 'UNION SELECT — most common SQL injection aggregation attack',\n pattern: /\\bUNION\\s{1,20}(?:ALL\\s{1,20})?SELECT\\b/i,\n },\n {\n id: 'sql-drop-table',\n description: 'DROP TABLE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}TABLE\\b/i,\n },\n {\n id: 'sql-drop-database',\n description: 'DROP DATABASE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}DATABASE\\b/i,\n },\n {\n id: 'sql-insert-into',\n description: 'INSERT INTO — data injection',\n pattern: /\\bINSERT\\s{1,20}INTO\\b/i,\n },\n {\n id: 'sql-delete-from',\n description: 'DELETE FROM — data deletion injection',\n pattern: /\\bDELETE\\s{1,20}FROM\\b/i,\n },\n {\n id: 'sql-update-set',\n description: 'UPDATE ... SET — data modification injection',\n // Allows arbitrary content between UPDATE and SET (table name, alias, etc.)\n pattern: /\\bUPDATE\\b[\\s\\S]{1,60}\\bSET\\b/i,\n },\n {\n id: 'sql-exec-xp',\n description: 'EXEC xp_ — MSSQL extended stored procedure execution',\n pattern: /\\bEXEC(?:UTE)?\\s{1,20}xp_/i,\n },\n {\n id: 'sql-tautology-string',\n description: \"Classic string tautology: ' OR '1'='1 or \\\" OR \\\"1\\\"=\\\"1\\\"\",\n // Last quote is optional — injection may truncate it: ' OR '1'='1--\n pattern: /'\\s{0,10}OR\\s{0,10}'[^']{0,20}'\\s*=\\s*'[^']{0,20}/i,\n },\n {\n id: 'sql-tautology-numeric',\n description: 'Numeric tautology: OR 1=1',\n pattern: /\\bOR\\s{1,10}1\\s*=\\s*1\\b/i,\n },\n {\n id: 'sql-always-true-zero',\n description: 'Numeric tautology: OR 0=0',\n pattern: /\\bOR\\s{1,10}0\\s*=\\s*0\\b/i,\n },\n {\n id: 'sql-sleep-benchmark',\n description: 'Time-based blind injection: SLEEP() or BENCHMARK()',\n pattern: /\\b(?:SLEEP|BENCHMARK)\\s*\\(/i,\n },\n {\n id: 'sql-waitfor-delay',\n description: 'MSSQL time-based blind injection: WAITFOR DELAY',\n pattern: /\\bWAITFOR\\s{1,20}DELAY\\b/i,\n },\n {\n id: 'sql-char-function',\n description: 'CHAR() function — used to obfuscate injected strings',\n pattern: /\\bCHAR\\s*\\(\\s*\\d{1,3}/i,\n },\n {\n id: 'sql-information-schema',\n description: 'INFORMATION_SCHEMA — reconnaissance query for table/column enumeration',\n pattern: /\\bINFORMATION_SCHEMA\\b/i,\n },\n];\n\nexport default SQL_PATTERNS;\n","/**\n * NOSQL context patterns.\n *\n * Detects injection vectors specific to NoSQL databases (primarily MongoDB)\n * and JavaScript-evaluated queries.\n *\n * Attack categories:\n * 1. MongoDB query operator injection: $where, $ne, $gt, $regex, $or, $and, etc.\n * These operators, when injected into a JSON query object, can bypass\n * authentication or exfiltrate data without knowing passwords.\n *\n * 2. JavaScript execution: $where clauses execute arbitrary JS server-side.\n *\n * 3. Prototype pollution: __proto__, constructor.prototype — can corrupt\n * the prototype chain of all objects in the Node.js process.\n *\n * Pattern note: MongoDB operators appear as JSON keys. In JSON, keys are\n * quoted: {\"$where\": ...} so the pattern must allow an optional closing\n * quote between the operator name and the colon: /\\$where[\"'\\s]*:/\n */\n\n// Shared suffix: optional closing quote/whitespace before the colon\n// Handles: $op: (bare), \"$op\": (JSON), '$op': (single-quoted)\nconst SEP = /[\"'\\s]*:/;\nconst sep = '[\"\\'\\\\s]*:';\n\nconst NOSQL_PATTERNS = [\n // ─── MongoDB $ operator injection ────────────────────────────────────────\n {\n id: 'nosql-where-operator',\n description: '$where — executes arbitrary JavaScript server-side in MongoDB',\n pattern: new RegExp(`\\\\$where${sep}`, 'i'),\n },\n {\n id: 'nosql-ne-operator',\n description: '$ne — \"not equal\" operator used to bypass equality checks',\n pattern: new RegExp(`\\\\$ne${sep}`, 'i'),\n },\n {\n id: 'nosql-gt-operator',\n description: '$gt — \"greater than\" used to bypass password/value checks',\n pattern: new RegExp(`\\\\$gte?${sep}`, 'i'),\n },\n {\n id: 'nosql-lt-operator',\n description: '$lt / $lte — \"less than\" bypass variants',\n pattern: new RegExp(`\\\\$lte?${sep}`, 'i'),\n },\n {\n id: 'nosql-regex-operator',\n description: '$regex — can be used to extract data character by character (blind injection)',\n pattern: new RegExp(`\\\\$regex${sep}`, 'i'),\n },\n {\n id: 'nosql-or-operator',\n description: '$or — logical OR; used to create always-true conditions',\n pattern: new RegExp(`\\\\$or${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-and-operator',\n description: '$and — logical AND operator injection',\n pattern: new RegExp(`\\\\$and${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-nor-operator',\n description: '$nor — logical NOR operator injection',\n pattern: new RegExp(`\\\\$nor${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-exists-operator',\n description: '$exists — can enumerate fields to determine schema',\n pattern: new RegExp(`\\\\$exists${sep}`, 'i'),\n },\n {\n id: 'nosql-in-operator',\n description: '$in — matches any value in a list; can enumerate values',\n pattern: new RegExp(`\\\\$in${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-expr-operator',\n description: '$expr — allows aggregation expressions in queries (MongoDB 3.6+)',\n pattern: new RegExp(`\\\\$expr${sep}`, 'i'),\n },\n {\n id: 'nosql-function-operator',\n description: '$function — executes arbitrary JavaScript in MongoDB 4.4+',\n pattern: new RegExp(`\\\\$function${sep}`, 'i'),\n },\n {\n id: 'nosql-accumulator-operator',\n description: '$accumulator — custom aggregation with arbitrary JS execution',\n pattern: new RegExp(`\\\\$accumulator${sep}`, 'i'),\n },\n // ─── Prototype pollution ─────────────────────────────────────────────────\n {\n id: 'nosql-proto-pollution',\n description: '__proto__ — prototype pollution via object key injection',\n pattern: /__proto__/,\n },\n {\n id: 'nosql-constructor-prototype',\n description: 'constructor.prototype — alternative prototype pollution vector (dot notation or JSON key)',\n // Matches dot-notation (obj.constructor.prototype) and JSON key adjacency\n // (\"constructor\": {\"prototype\": ...})\n pattern: /constructor[\\s\"':.,{\\[]*prototype/i,\n },\n {\n id: 'nosql-proto-bracket',\n description: '[\"__proto__\"] — bracket-notation prototype pollution',\n pattern: /\\[[\"']__proto__[\"']\\]/,\n },\n];\n\nexport default NOSQL_PATTERNS;\n","/**\n * Context registry — maps context name strings to their pattern arrays.\n *\n * Adding a new context: create a file in ./contexts/, export a default array\n * of pattern objects, and register it here.\n *\n * Context name guide:\n * SQL — high-precision rules; safe for general text fields\n * SQL-STRICT — SQL + three noisier rules (line comments, stacked queries, hex);\n * use only for SQL-specific inputs\n * REDOS — detects ReDoS-prone patterns when string will be compiled as RegExp\n */\n\nimport HTML_PATTERNS from './contexts/html.js';\nimport XML_PATTERNS from './contexts/xml.js';\nimport SVG_PATTERNS from './contexts/svg.js';\nimport SQL_PATTERNS from './contexts/sql.js';\nimport SQL_STRICT_PATTERNS from './contexts/sql-strict.js';\nimport SHELL_PATTERNS from './contexts/shell.js';\nimport REDOS_PATTERNS from './contexts/redos.js';\nimport NOSQL_PATTERNS from './contexts/nosql.js';\nimport LOG_PATTERNS from './contexts/log.js';\n\n/** @type {Record<string, Array<{id: string, description: string, pattern: RegExp}>>} */\nconst CONTEXT_REGISTRY = {\n HTML: HTML_PATTERNS,\n XML: XML_PATTERNS,\n SVG: SVG_PATTERNS,\n SQL: SQL_PATTERNS,\n 'SQL-STRICT': SQL_STRICT_PATTERNS,\n SHELL: SHELL_PATTERNS,\n REDOS: REDOS_PATTERNS,\n NOSQL: NOSQL_PATTERNS,\n LOG: LOG_PATTERNS,\n};\n\nexport default CONTEXT_REGISTRY;\n\n/**\n * Enum of valid context names — e.g. `VALID_CONTEXTS.HTML === 'HTML'`.\n * @type {Record<string, string>}\n */\nexport const VALID_CONTEXTS = Object.freeze(\n Object.fromEntries(Object.keys(CONTEXT_REGISTRY).map((k) => [k, k]))\n);","/**\n * XML context patterns.\n *\n * Detects injection vectors that are specifically dangerous when a string\n * is inserted into an XML document (not HTML rendering context).\n *\n * Key distinction from HTML: these patterns target parser-level attacks —\n * things that can confuse or subvert an XML parser, trigger external entity\n * resolution, or inject DTD content. HTML rendering concerns (XSS) belong\n * in the HTML context.\n */\n\nconst XML_PATTERNS = [\n {\n id: 'xml-cdata-injection',\n description: 'CDATA section injection: <![CDATA[ breaks out of text node context',\n pattern: /<!\\[CDATA\\[/i,\n },\n {\n id: 'xml-cdata-close',\n description: 'CDATA close sequence: ]]> can terminate an enclosing CDATA section',\n pattern: /\\]\\]>/,\n },\n {\n id: 'xml-processing-instruction',\n description: 'XML processing instruction: <?xml-stylesheet or <?php etc.',\n pattern: /<\\?(?:xml[\\- ]|php|asp)/i,\n },\n {\n id: 'xml-doctype-injection',\n description: 'DOCTYPE declaration embedded in content — can define entities',\n // Match <!DOCTYPE followed by end-of-string, whitespace, or [ (internal subset)\n pattern: /<!DOCTYPE(?:[\\s[]|$)/i,\n },\n {\n id: 'xml-entity-system',\n description: 'SYSTEM keyword — used in external entity declarations (XXE)',\n pattern: /\\bSYSTEM\\s+[\"']/i,\n },\n {\n id: 'xml-entity-public',\n description: 'PUBLIC keyword — used in external entity declarations (XXE)',\n pattern: /\\bPUBLIC\\s+[\"']/i,\n },\n {\n id: 'xml-entity-declaration',\n description: '<!ENTITY declaration — defines entities, potential XXE or entity expansion',\n pattern: /<!ENTITY[\\s%]/i,\n },\n {\n id: 'xml-billion-laughs',\n description: 'Entity reference chaining / billion laughs: repeated &eX; style references',\n // Heuristic: 3+ consecutive entity refs suggests expansion attack\n pattern: /(?:&\\w{1,20};){3,}/,\n },\n {\n id: 'xml-namespace-confusion',\n description: 'xmlns: attribute injection — can redefine namespaces to confuse parsers',\n pattern: /\\bxmlns\\s*(?::\\w{1,40})?\\s*=/i,\n },\n {\n id: 'xml-comment-injection',\n description: '<!-- comment injection — can hide content from some parsers',\n pattern: /<!--/,\n },\n {\n id: 'xml-comment-close',\n description: '--> closes an enclosing XML comment',\n pattern: /-->/,\n },\n {\n id: 'xml-pi-close',\n description: '?> closes an enclosing processing instruction',\n pattern: /\\?>/,\n },\n];\n\nexport default XML_PATTERNS;\n","/**\n * SVG context patterns.\n *\n * SVG is XML-based but renders in browsers, giving it a unique attack surface\n * that combines XML parser behaviour with browser rendering and JavaScript execution.\n *\n * Many of these vectors bypass HTML sanitizers that don't understand SVG semantics\n * (DOMPurify has documented bypass vulnerabilities specifically in SVG/XML context).\n */\n\nconst SVG_PATTERNS = [\n {\n id: 'svg-script-element',\n description: '<script element inside SVG executes JavaScript',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'svg-xlink-href-javascript',\n description: 'xlink:href with javascript: — classic SVG XSS via <a> or <use>',\n pattern: /xlink\\s*:\\s*href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-href-javascript',\n description: 'href= with javascript: in SVG context (<a>, <animate>, etc.)',\n pattern: /href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-foreignobject',\n description: '<foreignObject embeds HTML inside SVG — can execute scripts',\n pattern: /<foreignObject[\\s>/]/i,\n },\n {\n id: 'svg-use-external',\n description: '<use xlink:href or href pointing to external resource (non-fragment URL)',\n // Match <use with href= where the value starts with a non-# character (external URL)\n // [\\\"'][^#] catches quoted values not starting with #; [^\\\"'#\\s>] catches unquoted\n pattern: /<use[\\s\\S]{0,60}(?:xlink\\s*:\\s*)?href\\s*=\\s*(?:[\"'][^#]|[^\"'#\\s>])/i,\n },\n {\n id: 'svg-animate-href',\n description: '<animate attributeName=\"href\" — can dynamically change href to javascript:',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*href[\"']/i,\n },\n {\n id: 'svg-animate-xlinkhref',\n description: '<animate attributeName=\"xlink:href\"',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*xlink\\s*:\\s*href[\"']/i,\n },\n {\n id: 'svg-set-javascript',\n description: '<set to=\"javascript:...\" — sets an attribute to a javascript: URI',\n pattern: /<set[\\s\\S]{0,80}to\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-event-handler',\n description: 'SVG-specific event handler attributes: onload=, onerror=, onactivate=, etc.',\n pattern: /\\bon(?:load|error|activate|begin|end|repeat|focus|blur|click|mouse\\w{1,20}|key\\w{1,20})\\s*=/i,\n },\n {\n id: 'svg-handler-generic',\n description: 'Generic on* handler catch-all for SVG attributes',\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'svg-filter-feimage',\n description: '<feImage href= — filter primitive that can load external resources',\n pattern: /<feImage[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=/i,\n },\n {\n id: 'svg-image-external',\n description: '<image xlink:href with http/https or javascript protocol',\n pattern: /<image[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=\\s*[\"']?\\s*(?:https?|javascript)\\s*:/i,\n },\n {\n id: 'svg-style-javascript',\n description: 'style= attribute containing javascript: (e.g. background:url(javascript:...))',\n pattern: /style\\s*=[\\s\\S]{0,60}javascript\\s*:/i,\n },\n];\n\nexport default SVG_PATTERNS;\n","/**\n * SQL-STRICT context patterns.\n *\n * Extends the base 'SQL' context with three additional rules that are\n * effective at detecting real injections but carry a higher false-positive\n * risk on general free-text input.\n *\n * Use 'SQL-STRICT' when:\n * - The string is specifically a SQL fragment or database identifier\n * - You control the input domain (e.g. a dedicated SQL search field)\n * - You can tolerate occasional false positives in exchange for broader coverage\n *\n * Use 'SQL' (not STRICT) when:\n * - The field is general user text (names, descriptions, comments)\n * - False positives would block legitimate content (e.g. \"see note -- above\")\n *\n * Rules moved here from 'SQL' due to false-positive risk:\n *\n * sql-line-comment — \"--\" fires on \"see note -- above\", \"value--\", CSS var(--primary)\n * sql-stacked-query — \"; SELECT\" fires on legitimate prose with semicolons + SQL words\n * sql-hex-encoding — \"0xDEAD\" fires on hex values in technical docs and log output\n */\n\nimport SQL_PATTERNS from './sql.js';\n\nconst SQL_STRICT_EXTRA = [\n {\n id: 'sql-line-comment',\n description: 'SQL line comment: -- followed by whitespace or end of string',\n pattern: /--(?:\\s|$)/,\n },\n {\n id: 'sql-stacked-query',\n description: 'Stacked queries: semicolon immediately followed by a SQL keyword',\n pattern: /;\\s{0,10}(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC)\\b/i,\n },\n {\n id: 'sql-hex-encoding',\n description: 'Hex-encoded string injection: 0x41414141 style (MySQL)',\n pattern: /\\b0x[0-9a-f]{4,}/i,\n },\n];\n\n// SQL-STRICT = all base SQL rules + the three noisy extras\nconst SQL_STRICT_PATTERNS = [...SQL_PATTERNS, ...SQL_STRICT_EXTRA];\n\nexport default SQL_STRICT_PATTERNS;\n","/**\n * SHELL context patterns.\n *\n * Detects shell injection vectors and path traversal patterns.\n * Designed for use when a string will be passed to a shell command,\n * used as a file path, or interpolated into OS-level operations.\n */\n\nconst SHELL_PATTERNS = [\n {\n id: 'shell-path-traversal-unix',\n description: 'Unix path traversal: ../ — climbing the directory tree',\n pattern: /\\.\\.\\//,\n },\n {\n id: 'shell-path-traversal-windows',\n description: 'Windows path traversal: ..\\\\ — climbing the directory tree',\n pattern: /\\.\\.\\\\/,\n },\n {\n id: 'shell-path-traversal-encoded',\n description: 'URL-encoded path traversal: %2e%2e or %2f variants',\n pattern: /%2e%2e|%2f\\.\\.|\\.\\.%2f/i,\n },\n {\n id: 'shell-null-byte',\n description: 'Null byte injection: \\\\x00 or %00 — truncates strings in C-backed functions',\n pattern: /\\x00|%00/,\n },\n {\n id: 'shell-semicolon',\n description: 'Semicolon command separator: cmd1; cmd2',\n pattern: /;/,\n },\n {\n id: 'shell-pipe',\n description: 'Pipe operator: cmd1 | cmd2',\n pattern: /\\|/,\n },\n {\n id: 'shell-and-operator',\n description: 'AND operator: cmd1 && cmd2',\n pattern: /&&/,\n },\n {\n id: 'shell-or-operator',\n description: 'OR operator: cmd1 || cmd2',\n pattern: /\\|\\|/,\n },\n {\n id: 'shell-backtick',\n description: 'Backtick command substitution: `cmd`',\n pattern: /`/,\n },\n {\n id: 'shell-dollar-paren',\n description: 'Dollar-paren command substitution: $(cmd)',\n pattern: /\\$\\(/,\n },\n {\n id: 'shell-dollar-brace',\n description: 'Dollar-brace variable expansion: ${var} — can be abused for injection',\n pattern: /\\$\\{/,\n },\n {\n id: 'shell-redirect-out',\n description: 'Output redirection: cmd > file or cmd >> file',\n pattern: />{1,2}/,\n },\n {\n id: 'shell-redirect-in',\n description: 'Input redirection: cmd < file',\n pattern: /</,\n },\n {\n id: 'shell-newline-injection',\n description: 'Newline injection: \\\\n or \\\\r — can inject new shell commands',\n pattern: /[\\n\\r]/,\n },\n {\n id: 'shell-glob-star',\n description: 'Glob expansion: * or ? — can expand to unintended files',\n // Only flag when combined with path separators to reduce false positives\n pattern: /[/\\\\][*?]/,\n },\n {\n id: 'shell-absolute-root',\n description: 'Absolute root path injection: string starting with / or \\\\ (Windows UNC)',\n pattern: /^(?:\\/|\\\\\\\\)/,\n },\n {\n id: 'shell-windows-drive',\n description: 'Windows drive letter path injection: C:\\\\ or D:/',\n pattern: /^[a-zA-Z]:[/\\\\]/,\n },\n {\n id: 'shell-curl-wget',\n description: 'curl/wget with URL or flags — can exfiltrate data or download payloads',\n // Require a URL scheme (http/https/ftp) or a flag (-) to reduce false positives\n // \"curl is a tool\" won't match; \"curl http://...\" or \"curl -s ...\" will\n pattern: /\\b(?:curl|wget)\\s+(?:https?:\\/\\/|ftp:\\/\\/|-)/i,\n },\n];\n\nexport default SHELL_PATTERNS;\n","/**\n * REDOS context patterns.\n *\n * Detects strings that, if used as regular expressions, could cause\n * catastrophic backtracking (ReDoS — Regular Expression Denial of Service).\n *\n * These patterns detect the structural forms that lead to exponential or\n * polynomial backtracking in NFA-based regex engines (V8, PCRE, Java, etc.).\n *\n * Use this context when user-supplied strings will be compiled into RegExp objects.\n */\n\nconst REDOS_PATTERNS = [\n {\n id: 'redos-nested-quantifier-plus',\n description: 'Nested + quantifier inside a group with outer quantifier: (a+)+, (.+b)*, etc.',\n // Matches any group containing a + quantifier, with an outer * or + — catches (a+)+, (.+b)*, etc.\n pattern: /\\([^)]*\\+[^)]*\\)[+*]/,\n },\n {\n id: 'redos-nested-quantifier-star',\n description: 'Nested * quantifier: (a*)* or (a*)+ — catastrophic backtracking',\n pattern: /\\([^)]*\\*[^)]*\\)[*+]/,\n },\n {\n id: 'redos-nested-groups',\n description: 'Doubly nested quantified groups: ((a+)+) — guaranteed catastrophic',\n pattern: /\\(\\([^)]{0,40}\\)[+*]\\)[+*]/,\n },\n {\n id: 'redos-alternation-overlap',\n description: 'Overlapping alternation under quantifier: (a|a)+ — ambiguous NFA paths',\n // Detect repeated identical alternatives under a quantifier\n pattern: /\\(([^|()]{1,20})\\|(?:\\1)(?:\\|[^|()]{1,20}){0,5}\\)[+*?]{1,2}/,\n },\n {\n id: 'redos-star-plus-concat',\n description: '(x*x)+ pattern — triggers super-linear backtracking',\n pattern: /\\([^)]{0,10}\\*[^)]{0,10}\\)[+*]/,\n },\n {\n id: 'redos-dot-star-greedy',\n description: '(.*){n,} or (.+){n,} — repeated greedy dot quantifiers',\n pattern: /\\(\\.[*+]\\)\\{?\\d/,\n },\n {\n id: 'redos-large-repetition',\n description: 'Very large fixed or range repetition count {1000,} or {1000,n} — denial of service via backtracking',\n // Matches { followed by 4+ digits (≥1000), then optional ,digits }\n pattern: /\\{\\d{4,}(?:,\\d*)?\\}/,\n },\n {\n id: 'redos-catastrophic-alternation',\n description: 'Long alternation with many similar branches — polynomial backtracking risk',\n // Heuristic: 10+ pipe-separated alternatives in a single group\n pattern: /\\([^)]{0,200}(?:\\|[^|)]{0,50}){9,}\\)/,\n },\n];\n\nexport default REDOS_PATTERNS;\n","/**\n * LOG context patterns.\n *\n * Detects injection vectors that are dangerous when a string is written\n * to a log file, passed to a logging framework, or interpolated into\n * a log message that will be parsed or displayed.\n *\n * Attack categories:\n * 1. CRLF injection — injects fake log lines by embedding newlines\n * 2. Log4Shell (CVE-2021-44228) — ${jndi:...} triggers JNDI lookup in Log4j\n * 3. SSTI in log templates — {{...}}, #{...} trigger template evaluation\n * if the log message is passed through a template engine\n * 4. Null byte injection — truncates log entries in some implementations\n * 5. ANSI escape injection — manipulates terminal output when logs are\n * tailed in a terminal (colour codes, cursor movement, etc.)\n *\n * Note: Newline characters (\\n, \\r) will produce false positives for\n * multi-line legitimate values. Use this context only for single-line\n * log field values (usernames, IDs, request parameters, etc.).\n */\n\nconst LOG_PATTERNS = [\n // ─── CRLF / newline injection ─────────────────────────────────────────────\n {\n id: 'log-crlf-injection',\n description: 'CRLF injection: literal \\\\r or \\\\n embeds fake log lines',\n pattern: /[\\r\\n]/,\n },\n {\n id: 'log-url-encoded-crlf',\n description: 'URL-encoded CRLF: %0d, %0a, %0D, %0A — decoded by some log parsers',\n pattern: /%0[dDaA]/,\n },\n {\n id: 'log-unicode-newline',\n description: 'Unicode newline variants: U+2028 (line separator), U+2029 (paragraph separator)',\n pattern: /[\\u2028\\u2029]/,\n },\n\n // ─── Log4Shell / JNDI injection (CVE-2021-44228) ─────────────────────────\n {\n id: 'log-log4shell-jndi',\n description: 'Log4Shell: ${jndi:...} triggers remote code execution in Apache Log4j',\n pattern: /\\$\\{jndi\\s*:/i,\n },\n {\n id: 'log-log4shell-obfuscated',\n description: 'Obfuscated Log4Shell: ${::-j}... lookup-bypass prefix used to evade WAF detection',\n // ${::- is the Log4j lookup-bypass escape sequence; presence alone is suspicious\n pattern: /\\$\\{::-/,\n },\n {\n id: 'log-log4j-lookup',\n description: 'Log4j lookup syntax: ${env:...}, ${sys:...}, ${ctx:...} — data exfiltration',\n pattern: /\\$\\{(?:env|sys|ctx|main|map|sd|web|docker|k8s|spring)\\s*:/i,\n },\n\n // ─── Server-Side Template Injection (SSTI) in log messages ───────────────\n {\n id: 'log-ssti-double-brace',\n description: 'SSTI double-brace: {{expression}} — Jinja2, Twig, Handlebars, etc.',\n pattern: /\\{\\{[\\s\\S]{0,80}\\}\\}/,\n },\n {\n id: 'log-ssti-hash-brace',\n description: 'SSTI hash-brace: #{expression} — Thymeleaf, Velocity, Ruby ERB',\n pattern: /#\\{[\\s\\S]{0,80}\\}/,\n },\n {\n id: 'log-ssti-dollar-brace',\n description: 'SSTI/EL injection: ${expression with operators or method calls} — JSP EL, Freemarker, SpEL',\n // Require that the ${...} content looks like an expression, not a plain variable name.\n // Flags if the content contains: . ( * + operators, or known SSTI keywords.\n // This avoids flagging ${PATH}, ${HOME} etc. (plain shell variables).\n pattern: /\\$\\{[^}]*(?:\\.|\\(|\\*|\\+|\\bclass\\b|\\bruntime\\b|\\bprocess\\b|\\bexec\\b)[^}]{0,80}\\}/i,\n },\n {\n id: 'log-ssti-percent-tag',\n description: 'SSTI ERB/ASP tag: <%= expression %> — Ruby ERB, ASP',\n pattern: /<%=[\\s\\S]{0,80}%>/,\n },\n\n // ─── Null byte ────────────────────────────────────────────────────────────\n {\n id: 'log-null-byte',\n description: 'Null byte: \\\\x00 or %00 — can truncate log entries in C-backed loggers',\n pattern: /\\x00|%00/,\n },\n\n // ─── ANSI escape injection ────────────────────────────────────────────────\n {\n id: 'log-ansi-escape',\n description: 'ANSI escape sequence: ESC[ — can manipulate terminal output when logs are tailed',\n pattern: /\\x1b\\[/,\n },\n];\n\nexport default LOG_PATTERNS;\n","/**\n * is-unsafe\n *\n * Zero-dependency, DOM-free, pure predicate for detecting unsafe strings\n * across HTML, XML, SVG, SQL, SQL-STRICT, SHELL, REDOS, NOSQL, and LOG contexts.\n *\n * @module is-unsafe\n */\n\nimport CONTEXT_REGISTRY, { VALID_CONTEXTS } from './registry.js';\n\n/**\n * @typedef {'HTML'|'XML'|'SVG'|'SQL'|'SQL-STRICT'|'SHELL'|'REDOS'|'NOSQL'|'LOG'} ContextName\n */\n\n/**\n * @typedef {Object} MatchResult\n * @property {string} context - The context in which the match was found\n * @property {string} id - Rule identifier\n * @property {string} description - Human-readable description of what was matched\n * @property {RegExp} pattern - The pattern that matched\n */\n\n// ─── Validation helpers ────────────────────────────────────────────────────\n\n/**\n * Validate that `value` is a string. Throws TypeError if not.\n * @param {unknown} value\n */\nfunction assertString(value) {\n if (typeof value !== 'string') {\n throw new TypeError(\n `is-unsafe: first argument must be a string, got ${typeof value}`\n );\n }\n}\n\n/**\n * Validate that `context` is a recognised context name, an array of them,\n * or a RegExp instance. Throws TypeError if not.\n * @param {ContextName|ContextName[]|RegExp} context\n */\nfunction assertContext(context) {\n if (context instanceof RegExp) return;\n\n if (typeof context === 'string') {\n if (!CONTEXT_REGISTRY[context]) {\n throw new TypeError(\n `is-unsafe: unknown context \"${context}\". Valid contexts: ${Object.keys(VALID_CONTEXTS).join(', ')}`\n );\n }\n return;\n }\n\n if (Array.isArray(context)) {\n if (context.length === 0) {\n throw new TypeError('is-unsafe: context array must not be empty');\n }\n for (const c of context) {\n if (typeof c !== 'string' || !CONTEXT_REGISTRY[c]) {\n throw new TypeError(\n `is-unsafe: unknown context \"${c}\" in array. Valid contexts: ${Object.keys(VALID_CONTEXTS).join(', ')}`\n );\n }\n }\n return;\n }\n\n throw new TypeError(\n `is-unsafe: second argument must be a context string, array of context strings, or RegExp. Got: ${typeof context}`\n );\n}\n\n// ─── Core matching logic ───────────────────────────────────────────────────\n\n/**\n * Test a single value against one named context's patterns.\n * Returns the first matching MatchResult, or null if nothing matched.\n *\n * @param {string} value\n * @param {string} contextName\n * @returns {MatchResult|null}\n */\nfunction matchContext(value, contextName) {\n const patterns = CONTEXT_REGISTRY[contextName];\n for (const rule of patterns) {\n if (rule.pattern.test(value)) {\n return { context: contextName, id: rule.id, description: rule.description, pattern: rule.pattern };\n }\n }\n return null;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if `value` is unsafe in the given context(s), `false` otherwise.\n *\n * @param {string} value - The string to test\n * @param {ContextName|ContextName[]|RegExp} context\n * - A named context ('HTML', 'XML', 'SVG', 'SQL', 'SQL-STRICT', 'SHELL', 'REDOS', 'NOSQL', 'LOG')\n * - An array of named contexts — returns true if unsafe in **any** of them\n * - A custom RegExp — returns true if the pattern matches\n * @returns {boolean}\n *\n * @example\n * isUnsafe('<script>alert(1)</script>', 'HTML') // true\n * isUnsafe('hello world', 'HTML') // false\n * isUnsafe('value', ['HTML', 'SQL']) // false\n * isUnsafe('value', /my-pattern/i) // false\n */\nfunction isUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n // Custom RegExp — caller-supplied pattern\n if (context instanceof RegExp) {\n return context.test(value);\n }\n\n // Single named context\n if (typeof context === 'string') {\n return matchContext(value, context) !== null;\n }\n\n // Array of named contexts — unsafe if ANY context matches\n for (const c of context) {\n if (matchContext(value, c) !== null) return true;\n }\n return false;\n}\n\n/**\n * Like `isUnsafe`, but instead of a boolean returns the first `MatchResult`\n * describing **why** the value was flagged, or `null` if it is safe.\n *\n * Useful for logging, error messages, or policy reporting.\n *\n * @param {string} value\n * @param {ContextName|ContextName[]|RegExp} context\n * @returns {MatchResult|null}\n *\n * @example\n * whyUnsafe('<script>alert(1)</script>', 'HTML')\n * // { context: 'HTML', id: 'html-script-open', description: '...', pattern: /.../ }\n */\nfunction whyUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n if (context instanceof RegExp) {\n return context.test(value)\n ? { context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: context }\n : null;\n }\n\n if (typeof context === 'string') {\n return matchContext(value, context);\n }\n\n for (const c of context) {\n const result = matchContext(value, c);\n if (result !== null) return result;\n }\n return null;\n}\n\n/**\n * Returns all matching rules across the given context(s), or an empty array\n * if the value is safe. Useful for comprehensive auditing.\n *\n * @param {string} value\n * @param {ContextName|ContextName[]|RegExp} context\n * @returns {MatchResult[]}\n */\nfunction allUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const results = [];\n\n if (context instanceof RegExp) {\n if (context.test(value)) {\n results.push({ context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: context });\n }\n return results;\n }\n\n const contexts = typeof context === 'string' ? [context] : context;\n\n for (const c of contexts) {\n const patterns = CONTEXT_REGISTRY[c];\n for (const rule of patterns) {\n if (rule.pattern.test(value)) {\n results.push({ context: c, id: rule.id, description: rule.description, pattern: rule.pattern });\n }\n }\n }\n\n return results;\n}\n\nexport { isUnsafe, whyUnsafe, allUnsafe, VALID_CONTEXTS };\nexport default isUnsafe;","export default class BaseValueParser {\n constructor(IS_FINAL = false) {\n this.IS_FINAL = IS_FINAL;\n }\n /**\n * Called once per parse by `ValueParserPipeline` after construction.\n * Subclasses that need shared XML metadata (version, entities) read it\n * from `ctx` lazily inside `parse()` — it is fully populated by the time\n * the first value is parsed.\n * @param {import('../ValueParser.js').SharedContext} ctx\n */\n init(ctx) { // By ValueParserPipeline\n this.ctx = ctx;\n }\n\n /**\n * Reset internal state between document parses.\n * Called by `BaseOutputBuilderFactory.resetValueParsers()` before each parse.\n * Stateful subclasses (e.g. `EntityParser`) must override this.\n */\n reset() {\n //Do nothing\n }\n\n /**\n * Transform `val` and return the result.\n * Return a `FinalValue` instance to stop the pipeline early (unless\n * `this.IS_FINAL` is false, in which case the pipeline continues).\n *\n * @param {*} val\n * @param {import('../ValueParser.js').Context} [runTimeContext]\n * @returns {*}\n */\n parse(val, runTimeContext) {\n throw new Error(\"You must implement parse() in a value parser.\");\n }\n\n}\n","import { EntityDecoder, XML, COMMON_HTML, ENTITY_ACTION } from '@nodable/entities';\nimport { isUnsafe, VALID_CONTEXTS } from \"is-unsafe\"\n\nimport BaseValueParser from \"./BaseValueParser.js\"\n\nconst defaultOptions = {\n namedEntities: { ...XML },\n numericAllowed: true,\n //limit: {},\n onInputEntity: (name, value) =>\n isUnsafe(value, [VALID_CONTEXTS.XML])\n ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW,\n}\n\nexport default class EntityParser extends BaseValueParser {\n #seen = false;\n #decoder = null;\n #options = null;\n /**\n * @param {object} options \n * @param {boolean} isFinal true if it should skip rest of the value parsers in the chain\n */\n constructor(options, isFinal = false) { // By user options (optional)\n super(isFinal);\n this.#options = {\n ...defaultOptions,\n ...(options || {}),\n // Additive merge: user-supplied namedEntities extend, not replace, the defaults.\n namedEntities: { ...defaultOptions.namedEntities, ...(options?.namedEntities || {}) },\n };\n }\n\n #ensureDecoder() {\n if (!this.#decoder) {\n this.#decoder = new EntityDecoder(this.#options);\n }\n if (!this.#seen) {\n const version = this.ctx?.get('xmlVersion');\n const entities = this.ctx?.get('inputEntities');\n if (version) this.#decoder.setXmlVersion(version);\n if (entities) this.#decoder.addInputEntities(entities);\n this.#seen = true;\n }\n }\n\n reset() {\n this.#seen = false;\n if (this.#decoder) {\n this.#decoder.reset();\n }\n }\n\n parse(val) {\n if (typeof val !== 'string') return val;\n this.#ensureDecoder();\n return this.#decoder.decode(val);\n }\n\n}\n","/**\n * Flat lookup table: maps Unicode code point → ASCII digit (0-9).\n * Only decimal digit characters (Unicode category Nd) are included.\n *\n * Strategy: Int32Array of size (maxCodePoint - minCodePoint + 1).\n * Value 0xFF means \"not a digit\". Value 0-9 is the ASCII digit value.\n * This gives O(1) lookup with no branching, no bisect, no loop.\n *\n * Memory: range is 0x0660 to 0x1FBF0 → ~129,936 entries × 1 byte = ~127 KB.\n * Acceptable for a one-time init; lookup is a single array index.\n */\n\n// All known Unicode Nd (decimal digit) script zero code points.\n// Each script has exactly 10 consecutive digits: zero+0 .. zero+9.\nconst SCRIPT_ZEROS = [\n // Basic Latin (ASCII) — included for completeness / pass-through\n 0x0030, // 0-9\n\n // Arabic scripts\n 0x0660, // Arabic-Indic ٠١٢٣٤٥٦٧٨٩\n 0x06F0, // Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳\n\n // Indic scripts\n 0x0966, // Devanagari ०१२३४५६७८९\n 0x09E6, // Bengali ০১২৩৪৫৬৭৮৯\n 0x0A66, // Gurmukhi ੦੧੨੩੪੫੬੭੮੯\n 0x0AE6, // Gujarati ૦૧૨૩૪૫૬૭૮૯\n 0x0B66, // Odia ୦୧୨୩୪୫୬୭୮୯\n 0x0BE6, // Tamil ௦௧௨௩௪௫௬௭௮௯\n 0x0C66, // Telugu ౦౧౨౩౪౫౬౭౮౯\n 0x0CE6, // Kannada ೦೧೨೩೪೫೬೭೮೯\n 0x0D66, // Malayalam ൦൧൨൩൪൫൬൭൮൯\n 0x0DE6, // Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯\n\n // Southeast Asian scripts\n 0x0E50, // Thai ๐๑๒๓๔๕๖๗๘๙\n 0x0ED0, // Lao ໐໑໒໓໔໕໖໗໘໙\n 0x0F20, // Tibetan ༠༡༢༣༤༥༦༧༨༩\n 0x1040, // Myanmar ၀၁၂၃၄၅၆၇၈၉\n 0x1090, // Myanmar Shan ႐႑႒႓႔႕႖႗႘႙\n 0x17E0, // Khmer ០១២៣៤៥៦៧៨៩\n 0x1810, // Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙\n 0x1946, // Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏\n 0x19D0, // New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙\n 0x1A80, // Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉\n 0x1A90, // Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙\n 0x1B50, // Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙\n 0x1BB0, // Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹\n 0x1C40, // Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉\n 0x1C50, // Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙\n\n // Fullwidth (CJK context)\n 0xFF10, // Fullwidth 0123456789\n\n // Mathematical digit variants (Unicode math block)\n 0x1D7CE, // Mathematical Bold\n 0x1D7D8, // Mathematical Double-Struck\n 0x1D7E2, // Mathematical Sans-Serif\n 0x1D7EC, // Mathematical Sans-Serif Bold\n 0x1D7F6, // Mathematical Monospace\n\n // Other scripts\n 0x104A0, // Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩\n 0x10D30, // Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹\n 0x11066, // Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯\n 0x110F0, // Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹\n 0x11136, // Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿\n 0x111D0, // Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙\n 0x112F0, // Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹\n 0x11450, // Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙\n 0x114D0, // Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙\n 0x11650, // Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙\n 0x116C0, // Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉\n 0x11730, // Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹\n 0x118E0, // Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩\n 0x11950, // Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙\n 0x11BF0, // Khitan Small Script \n 0x11C50, // Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙\n 0x11D50, // Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙\n 0x11DA0, // Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩\n 0x11F50, // Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙\n 0x16A60, // Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩\n 0x16AC0, // Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉\n 0x16B50, // Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙\n 0x1E140, // Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉\n 0x1E2F0, // Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹\n 0x1E4F0, // Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹\n 0x1E950, // Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙\n 0x1FBF0, // Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹\n];\n\n// Build a sparse Map for scripts above 0xFFFF (surrogate-pair range).\n// These can't go into a flat Uint8Array indexed by code point efficiently.\nconst NOT_DIGIT = 0xFF;\nconst HIGH_MAP = new Map(); // codePoint → digit value (0-9)\n\nconst LOW_MAX = 0xFFFF;\nconst LOW_MIN = 0x0660; // first non-ASCII digit script\n\n// Flat Uint8Array covering 0x0660 .. 0xFFFF\nconst TABLE_OFFSET = LOW_MIN;\nconst TABLE_SIZE = LOW_MAX - LOW_MIN + 1;\nconst TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);\n\nfor (const zero of SCRIPT_ZEROS) {\n for (let d = 0; d < 10; d++) {\n const cp = zero + d;\n if (cp <= LOW_MAX) {\n TABLE[cp - TABLE_OFFSET] = d;\n } else {\n HIGH_MAP.set(cp, d);\n }\n }\n}\n\nexport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT };\n","'use strict';\n\nimport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT } from './digitTable.js';\n\nconst CHAR_0 = 48; // '0'.charCodeAt(0)\nconst CHAR_9 = 57; // '9'.charCodeAt(0)\nconst CHAR_MINUS = 45; // '-'.charCodeAt(0)\n\n// Unicode minus/hyphen variants worth normalizing to ASCII '-' in numeric context:\n// U+2212 MINUS SIGN − (mathematically correct minus)\n// U+FF0D FULLWIDTH HYPHEN-MINUS - (Japanese fullwidth context)\n// U+FE63 SMALL HYPHEN-MINUS ﹣ (small form variant)\n//\n// NOT normalized (deliberate):\n// U+2013 EN DASH – (punctuation, not a numeric sign)\n// U+2014 EM DASH — (punctuation)\n// U+2010 HYPHEN ‐ (typographic hyphen)\n//\n// Rationale: only characters a human or locale formatter would plausibly use\n// as a numeric minus sign are normalized. Dashes used for punctuation are left\n// alone to avoid mangling non-numeric strings.\nconst MINUS_SET = new Set([0x2212, 0xFF0D, 0xFE63]);\n\n/**\n * Normalize all Unicode decimal digit characters in a string to ASCII (0-9),\n * and normalize Unicode minus variants to ASCII '-' (U+002D).\n *\n * Non-digit, non-minus characters are passed through unchanged.\n *\n * Performance design:\n * - Fast path: if the string has no convertible characters, return it unchanged\n * (zero allocation).\n * - BMP digits (0x0660..0xFFFF excl. surrogates): flat Uint8Array lookup (O(1)).\n * - Supplementary plane digits (> 0xFFFF, encoded as surrogate pairs): Map lookup.\n * - Minus variants: checked inline with a small fixed Set.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction anynum(str) {\n if (typeof str !== 'string') return str;\n\n const len = str.length;\n if (len === 0) return str;\n\n // Scan for first character needing conversion.\n // If none found, return original string (zero allocation).\n let firstHit = -1;\n\n for (let i = 0; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — already normalized, skip fast\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) continue;\n\n // Below first unicode digit script — check minus variants only\n if (cc < TABLE_OFFSET) {\n if (MINUS_SET.has(cc)) { firstHit = i; break; }\n continue;\n }\n\n // Surrogate pairs live in BMP range 0xD800-0xDFFF — check before TABLE\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n if (HIGH_MAP.has(cp)) { firstHit = i; break; }\n }\n }\n continue;\n }\n\n // BMP non-surrogate: flat table lookup; also check minus variants in this range\n if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {\n firstHit = i;\n break;\n }\n }\n\n // Nothing to replace — return original, zero allocation\n if (firstHit === -1) return str;\n\n // Build result: copy unchanged prefix, then convert from firstHit onward\n const chars = [];\n\n if (firstHit > 0) chars.push(str.slice(0, firstHit));\n\n for (let i = firstHit; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — pass through\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) {\n chars.push(str[i]);\n continue;\n }\n\n // Below TABLE_OFFSET — check minus variants, else pass through\n if (cc < TABLE_OFFSET) {\n chars.push(MINUS_SET.has(cc) ? '-' : str[i]);\n continue;\n }\n\n // Surrogate pairs\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n const d = HIGH_MAP.get(cp);\n if (d !== undefined) {\n chars.push(String.fromCharCode(d + 48));\n i++; // consume low surrogate\n continue;\n }\n }\n }\n chars.push(str[i]);\n continue;\n }\n\n // BMP non-surrogate: flat table lookup + minus variants\n if (MINUS_SET.has(cc)) {\n chars.push('-');\n continue;\n }\n const d = TABLE[cc - TABLE_OFFSET];\n chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str[i]);\n }\n\n return chars.join('');\n}\n\nexport { anynum };\nexport default anynum;","const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;\nconst binRegex = /^0b[01]+$/;\nconst octRegex = /^0o[0-7]+$/;\nconst numRegex = /^([\\-\\+])?(0*)([0-9]*(\\.[0-9]*)?)$/;\n\nimport anynum from \"anynum\";\n\nconst consider = {\n hex: true,\n binary: false,\n octal: false,\n leadingZeros: true,\n decimalPoint: \"\\.\",\n eNotation: true,\n //skipLike: /regex/,\n infinity: \"original\", // \"null\", \"infinity\" (Infinity type), \"string\" (\"Infinity\" (the string literal))\n unicode: false,\n};\n\nexport default function toNumber(str, options = {}) {\n options = Object.assign({}, consider, options);\n if (!str || typeof str !== \"string\") return str;\n\n let trimmedStr = str.trim();\n\n if (trimmedStr.length === 0) return str;\n else if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;\n else if (trimmedStr === \"0\") return 0;\n\n if (options.unicode) {\n trimmedStr = anynum(trimmedStr);\n if (trimmedStr === \"0\") return 0; // re-check after normalization\n }\n if (options.hex && hexRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 16);\n } else if (options.binary && binRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 2);\n } else if (options.octal && octRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 8);\n } else if (!isFinite(trimmedStr)) { //Infinity\n return handleInfinity(str, Number(trimmedStr), options);\n } else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation\n return resolveEnotation(str, trimmedStr, options);\n } else {\n //separate negative sign, leading zeros, and rest number\n const match = numRegex.exec(trimmedStr);\n // +00.123 => [ , '+', '00', '.123', ..\n if (match) {\n const sign = match[1] || \"\";\n const leadingZeros = match[2];\n let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros\n const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000.\n str[leadingZeros.length + 1] === \".\"\n : str[leadingZeros.length] === \".\";\n\n //trim ending zeros for floating number\n if (!options.leadingZeros //leading zeros are not allowed\n && (leadingZeros.length > 1\n || (leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros))) {\n // 00, 00.3, +03.24, 03, 03.24\n return str;\n }\n else {//no leading zeros or leading zeros are allowed\n const num = Number(trimmedStr);\n const parsedStr = String(num);\n\n if (num === 0) return num;\n if (parsedStr.search(/[eE]/) !== -1) { //given number is long and parsed to eNotation\n if (options.eNotation) return num;\n else return str;\n } else if (trimmedStr.indexOf(\".\") !== -1) { //floating number\n if (parsedStr === \"0\") return num; //0.0\n else if (parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000\n else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;\n else return str;\n }\n\n let n = leadingZeros ? numTrimmedByZeros : trimmedStr;\n if (leadingZeros) {\n // -009 => -9\n return (n === parsedStr) || (sign + n === parsedStr) ? num : str\n } else {\n // +9\n return (n === parsedStr) || (n === sign + parsedStr) ? num : str\n }\n }\n } else { //non-numeric string\n return str;\n }\n }\n}\n\nconst eNotationRegx = /^([-+])?(0*)(\\d*(\\.\\d*)?[eE][-\\+]?\\d+)$/;\nfunction resolveEnotation(str, trimmedStr, options) {\n if (!options.eNotation) return str;\n const notation = trimmedStr.match(eNotationRegx);\n if (notation) {\n let sign = notation[1] || \"\";\n const eChar = notation[3].indexOf(\"e\") === -1 ? \"E\" : \"e\";\n const leadingZeros = notation[2];\n const eAdjacentToLeadingZeros = sign ? // 0E.\n str[leadingZeros.length + 1] === eChar\n : str[leadingZeros.length] === eChar;\n\n if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str;\n else if (leadingZeros.length === 1\n && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {\n return Number(trimmedStr);\n } else if (leadingZeros.length > 0) {\n // Has leading zeros — only accept if leadingZeros option allows it\n if (options.leadingZeros && !eAdjacentToLeadingZeros) {\n trimmedStr = (notation[1] || \"\") + notation[3];\n return Number(trimmedStr);\n } else return str;\n } else {\n // No leading zeros — always valid e-notation, parse it\n return Number(trimmedStr);\n }\n } else {\n return str;\n }\n}\n\n/**\n * \n * @param {string} numStr without leading zeros\n * @returns \n */\nfunction trimZeros(numStr) {\n if (numStr && numStr.indexOf(\".\") !== -1) {//float\n numStr = numStr.replace(/0+$/, \"\"); //remove ending zeros\n if (numStr === \".\") numStr = \"0\";\n else if (numStr[0] === \".\") numStr = \"0\" + numStr;\n else if (numStr[numStr.length - 1] === \".\") numStr = numStr.substring(0, numStr.length - 1);\n return numStr;\n }\n return numStr;\n}\n\nfunction parse_int(numStr, base) {\n const str = numStr.trim();\n if (base === 2 || base === 8) numStr = str.substring(2);\n\n if (parseInt) return parseInt(numStr, base);\n else if (Number.parseInt) return Number.parseInt(numStr, base);\n else if (window && window.parseInt) return window.parseInt(numStr, base);\n else throw new Error(\"parseInt, Number.parseInt, window.parseInt are not supported\");\n}\n\n/**\n * Handle infinite values based on user option\n * @param {string} str - original input string\n * @param {number} num - parsed number (Infinity or -Infinity)\n * @param {object} options - user options\n * @returns {string|number|null} based on infinity option\n */\nfunction handleInfinity(str, num, options) {\n const isPositive = num === Infinity;\n\n switch (options.infinity.toLowerCase()) {\n case \"null\":\n return null;\n case \"infinity\":\n return num; // Return Infinity or -Infinity\n case \"string\":\n return isPositive ? \"Infinity\" : \"-Infinity\";\n case \"original\":\n default:\n return str; // Return original string like \"1e1000\"\n }\n}","import toNumber from 'strnum';\nimport BaseValueParser from \"./BaseValueParser.js\"\nimport { FinalValue } from \"./../ValueParser.js\"\n\n/**\n * Number parser class that wraps the strnum toNumber function\n * Provides consistent API for value parsing in flexible-xml-parser\n */\nexport default class numParser extends BaseValueParser {\n constructor(options, isFinal = false) {\n super(isFinal);\n this.options = options || {};\n }\n\n /**\n * Parse a value, converting strings to numbers based on options\n * @param {*} val - Value to parse\n * @returns {*} Parsed value (number if successfully parsed, otherwise original value)\n */\n parse(val) {\n if (typeof val === 'string') {\n const newval = toNumber(val, this.options);\n if (typeof newval !== val) {\n return this.IS_FINAL ? new FinalValue(newval) : newval;\n }\n }\n return val;\n }\n}\n","\nimport EntityParser from './ValueParsers/EntityParser.js';\nimport WSNormalizer from './ValueParsers/WSNormalizer.js';\nimport trimParser from './ValueParsers/trim.js';\nimport booleanParser from './ValueParsers/booleanParser.js';\nimport numberParser from './ValueParsers/number.js';\n// import currencyParser from './ValueParsers/currency.js';\nimport BaseValueParser from './ValueParsers/BaseValueParser.js';\n\n\nconst defaultValParsers = {\n \"entity\": new EntityParser(),\n \"trim\": new trimParser(),\n \"ws\": new WSNormalizer(),\n \"boolean\": new booleanParser(),\n \"number\": new numberParser({ hex: true, leadingZeros: true, eNotation: true }),\n // \"currency\": new currencyParser(),\n};\n\nexport default class ValueParserRegistry {\n constructor() {\n this.registered = { ...defaultValParsers };\n }\n\n /**\n * @param {string} name\n * @param {BaseValueParser} parser\n */\n register(name, parser) {\n if (!name || typeof name !== 'string') {\n throw new Error('name must be a string');\n }\n if (!parser) {\n throw new Error('parser is required');\n }\n if (!parser.reset || typeof parser.reset !== 'function') {\n throw new Error('parser must implement reset()');\n }\n if (!parser.parse || typeof parser.parse !== 'function') {\n throw new Error('parser must implement parse()');\n }\n this.registered[name] = parser;\n }\n\n reset(name) {\n this.registered[name]?.reset();\n }\n\n resetAll() {\n for (const vp in this.registered) {\n this.registered[vp]?.reset();\n }\n }\n\n /**\n * \n * @param {string} name \n * @returns {BaseValueParser}\n */\n get(name) {\n const ret = this.registered[name];\n if (!ret) {\n throw new Error('parser not found: ' + name);\n }\n return ret;\n }\n}","import BaseValueParser from \"./BaseValueParser.js\"\n// import { FinalValue } from \"./../ValueParser.js\"\n\nexport default class trimmer extends BaseValueParser {\n parse(val) {\n if (typeof val === \"string\") return val.trim();\n else return val;\n }\n}\n","import { Expression, ExpressionSet } from 'path-expression-matcher';\nimport BaseValueParser from \"./BaseValueParser.js\"\n// import { FinalValue } from \"./../ValueParser.js\"\n\n/**\n * Whitespace-normalizing value parser.\n *\n * Collapses runs of whitespace (spaces, tabs, newlines) to a single space\n * and trims leading/trailing whitespace from string tag values. Replaces\n * the `'trim'` parser in the default chain.\n *\n * Normalization is skipped when any of these conditions hold:\n * 1. The value is not a string.\n * 2. The element is an attribute.\n * 3. Any ancestor element has `xml:space=\"preserve\"` (requires the XML\n * parser to be configured with `{ keep: ['space'] }` on that tag).\n * 4. The current tag path matches a user-supplied exclusion expression\n * (e.g. `[\"..pre\", \"..code\", \"..script\"]`).\n *\n * @example\n * import { WSNormalizer } from '@nodable/base-output-builder';\n * import { CompactBuilderFactory } from '@nodable/compact-builder';\n *\n * const ws = new WSNormalizer({ tags: ['..pre', '..code'] });\n * const builder = new CompactBuilderFactory({\n * tags: { valueParsers: ['entity', ws, 'boolean', 'number'] },\n * });\n */\nexport default class WSNormalizer extends BaseValueParser {\n /**\n * @param {{ exclude?: Array<string|import('path-expression-matcher').Expression> }} [options]\n * `exclude` — paths whose whitespace should be left untouched (e.g. `[\"..pre\", \"..code\"]`).\n * Accepts plain expression strings or pre-built `Expression` objects.\n */\n constructor(options, isFinal = false) {\n super(isFinal);\n const exclude = options?.exclude ?? [];\n\n const set = new ExpressionSet();\n for (const entry of exclude) {\n set.add(typeof entry === 'string' ? new Expression(entry) : entry);\n }\n set.seal();\n\n /** @type {ExpressionSet} */\n this._excludeSet = set;\n }\n\n /**\n * Normalize whitespace in `val` unless an exclusion condition applies.\n *\n * @param {*} val\n * @param {import('../ValueParser.js').Context} [ctx]\n * @returns {*}\n */\n parse(val, ctx) {\n if (typeof val !== 'string') return val;\n\n if (ctx) {\n // Only normalize element text, not attribute values\n if (ctx.isAttribute) return val;\n\n if (ctx.matcher) {\n // Respect xml:space=\"preserve\" on any ancestor\n if (ctx.matcher.getAnyParentAttr('xml:space') === 'preserve') return val;\n\n // Respect user-configured exclusion paths\n if (this._excludeSet.size > 0 && this._excludeSet.matchesAny(ctx.matcher)) return val;\n }\n }\n\n return normalizeSpaces(val);\n }\n}\n\n/**\n * Collapse all whitespace runs to a single space and trim both ends.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction normalizeSpaces(str) {\n return str.replace(/[ \\t\\r\\n]+/g, ' ').trim();\n}\n","import BaseValueParser from \"./BaseValueParser.js\";\nimport { FinalValue } from \"../ValueParser.js\";\n\nexport default class boolParser extends BaseValueParser {\n constructor(trueList, falseList, isFinal = false) {\n super(isFinal)\n if (trueList)\n this.trueList = trueList;\n else\n this.trueList = [\"true\"];\n\n if (falseList)\n this.falseList = falseList;\n else\n this.falseList = [\"false\"];\n }\n parse(val) {\n if (typeof val === 'string') {\n //TODO: performance: don't convert\n const temp = val.toLowerCase();\n if (this.trueList.indexOf(temp) !== -1) return this.IS_FINAL ? new FinalValue(true) : true;\n else if (this.falseList.indexOf(temp) !== -1) return this.IS_FINAL ? new FinalValue(false) : false;\n }\n return val;\n }\n}\n","import ValueParserRegistry from './ValueParserRegistry.js';\nimport BaseValueParser from './ValueParsers/BaseValueParser.js';\n\n/**\n * Context object passed to every value parser's `parse(val, context)` call.\n */\nexport class Context {\n /**\n * @param {string} elementName Tag or attribute name\n * @param {import('path-expression-matcher').MatcherView} matcher Read-only path inspector\n * @param {boolean|null} isLeafNode True when element has no child elements\n * @param {boolean} isAttribute True when attribute type\n */\n constructor(elementName, matcher, isLeafNode, isAttribute = false) {\n this.elementName = elementName;\n this.matcher = matcher;\n this.isLeafNode = isLeafNode;\n this.isAttribute = isAttribute;\n }\n}\n\n/**\n * Sentinel returned by a value parser to stop the pipeline immediately.\n *\n * When `ValueParserPipeline.run()` sees a `FinalValue` result it unwraps\n * `.value` and returns it without running any subsequent parsers.\n *\n * Whether to emit `FinalValue` is the parser's own decision, controlled by\n * its `IS_FINAL` constructor option (not injected by the pipeline).\n *\n * @example\n * import { FinalValue } from '@nodable/base-output-builder/ValueParser.js';\n *\n * class StopOnNull extends BaseValueParser {\n * constructor(options, IS_FINAL) {\n * super(IS_FINAL);\n * this.options = options || {};\n * }\n * parse(val) {\n * if (val === 'null') {\n * return this.IS_FINAL ? new FinalValue(null) : null;\n * }\n * return val;\n * }\n * }\n */\nexport class FinalValue {\n /** @param {*} value The resolved value to return from the pipeline */\n constructor(value) {\n this.value = value;\n }\n}\n\n/**\n * Mutable key-value store owned by `BaseOutputBuilder` and shared with every\n * value parser that opts in via `init(ctx)`.\n *\n * A fresh `SharedContext` is created for each document parse by\n * `BaseOutputBuilderFactory.getInstance()`, so there is no risk of stale\n * data from a previous document leaking into the next.\n *\n * ## Well-known keys (set by `BaseOutputBuilder`)\n * - `'xmlVersion'` — numeric XML version from `<?xml version=\"…\"?>`\n * - `'inputEntities'` — entity map from the DOCTYPE block\n *\n * ## Extension\n * Parsers may also write to the context to communicate with later parsers in\n * the same pipeline (e.g. a locale detector writing `'detectedLocale'` for a\n * downstream date formatter).\n *\n * @example\n * const ctx = new SharedContext();\n * ctx.set('xmlVersion', 1.1);\n * ctx.get('xmlVersion'); // → 1.1\n */\nexport class SharedContext {\n #data = {};\n\n /** @param {string} key @param {*} value */\n set(key, value) { this.#data[key] = value; }\n\n /** @param {string} key @returns {*} */\n get(key) { return this.#data[key]; }\n\n /**\n * Remove all entries.\n * Not needed when the factory creates a fresh SharedContext per parse\n * (the current approach), but available for alternative setups.\n */\n clear() { this.#data = {}; }\n}\n\n/**\n * Owns a value-parser registry and a configured parser chain, and executes\n * them in order on demand.\n *\n * Builders receive two pre-built pipelines from their factory:\n * - `tagsPipeline` — for tag text content\n * - `attrsPipeline` — for attribute values\n *\n * Both pipelines share the same `SharedContext` instance and the same parser\n * instances from the registry. `init(ctx)` is called on every parser that\n * implements it when the pipeline is constructed, and again on any parser\n * added later via `register()`.\n *\n * Because a fresh pipeline is built for every document parse, parsers receive\n * an up-to-date `ctx` reference each time — no stale state.\n *\n * `IS_FINAL` is a parser-level decision set in each parser's own constructor,\n * not injected by the pipeline.\n *\n * @example\n * const ctx = new SharedContext();\n * const pipeline = new ValueParserPipeline(\n * ['entity', 'ws', 'boolean', 'number'],\n * registry,\n * ctx,\n * );\n * const result = pipeline.run(' true ', context); // → true\n */\nexport class ValueParserPipeline {\n /**\n * @param {Array<BaseValueParser>} valParsers Ordered parser names or instances\n * @param {ValueParserRegistry} registry Map of name → parser instance\n * @param {SharedContext} [sharedContext]\n */\n constructor(valParsers = [], registry, sharedContext = null) {\n this.valParsers = valParsers;\n this.registry = registry;\n this.sharedContext = sharedContext || new SharedContext();\n\n this._initAll(valParsers);\n }\n\n /**\n * Execute the parser chain on `val`.\n *\n * Each parser receives `(currentValue, context)`. If a parser returns a\n * {@link FinalValue} the chain stops and the wrapped value is returned.\n *\n * @param {*} val\n * @param {Context} [runtimContext]\n * @returns {*}\n */\n run(val, runtimContext) {\n for (let i = 0; i < this.valParsers.length; i++) {\n let parser = this.valParsers[i];\n if (typeof parser === 'string') parser = this.registry.get(parser);\n if (parser) {\n const result = parser.parse(val, runtimContext);\n if (result instanceof FinalValue) return result.value;\n val = result;\n }\n }\n return val;\n }\n\n resetAll() {\n for (let i = 0; i < this.valParsers.length; i++) {\n let parser = this.valParsers[i];\n if (typeof parser === 'string') parser = this.registry.get(parser);\n if (parser) if (parser.reset && typeof parser.reset === 'function') parser.reset();\n }\n }\n\n /**\n * Add or replace a named parser in the registry.\n * If the instance implements `init()` and the pipeline has a `SharedContext`,\n * `init` is called immediately so the parser is ready before the next `run()`.\n *\n * @param {string} name\n * @param {object} instance Must implement `parse(val, context?)`\n */\n register(name, instance) {\n this.registry.register(name, instance);\n if (instance.init) instance.init(this.sharedContext);\n }\n\n // ── private ──────────────────────────────────────────────────────────────\n\n /**\n * Call `init(sharedContext)` on each parser in `instances` that implements it.\n * Used only during construction to wire up the initial registry.\n *\n * A local `Set` deduplicates in case the same instance is registered under\n * multiple names (e.g. an alias). A module-level WeakSet is intentionally\n * NOT used: pipelines are rebuilt on every `getInstance()` call, so parsers\n * must receive `init()` fresh each parse with the new `SharedContext` ref.\n *\n * @param {object[]} instances\n */\n _initAll(instances) {\n if (!this.sharedContext) return;\n const seen = new Set();\n for (const p of instances) {\n let parserInstance = p;\n if (typeof p === 'string') parserInstance = this.registry.get(p);\n if (parserInstance && typeof parserInstance.init === 'function' && !seen.has(parserInstance)) {\n seen.add(parserInstance);\n parserInstance.init(this.sharedContext);\n }\n }\n }\n}","import { MatcherView } from 'path-expression-matcher';\nimport { Context, SharedContext, ValueParserPipeline } from './ValueParser.js';\nimport ValueParserRegistry from './ValueParserRegistry.js';\n\n// Default parser chains.\nconst DEFAULT_TAG_PARSERS = ['ws', 'entity', 'boolean', 'number'];\nconst DEFAULT_ATTR_PARSERS = ['entity', 'boolean', 'number'];\n\nexport default class BaseOutputBuilder {\n\n /**\n * @param {object} parserOptions\n * @param {object} builderOptions\n * @param {MatcherView} matcherView\n * @param {ValueParserRegistry} registry\n * @param {boolean} resetPipelines\n */\n constructor(parserOptions, builderOptions, matcherView, registry, resetPipelines = true) {\n this.matcher = matcherView;\n this._rootName = \"^\";\n this.parserOptions = parserOptions;\n this.builderOptions = builderOptions;\n this.registry = registry;\n\n const tagChain = builderOptions?.tags?.valueParsers ?? DEFAULT_TAG_PARSERS;\n const attrChain = builderOptions?.attributes?.valueParsers ?? DEFAULT_ATTR_PARSERS;\n\n /**\n * Shared mutable context distributed to all value parsers.\n * `BaseOutputBuilder` is the sole writer; parsers are readers (or\n * co-writers for cross-parser communication).\n * @type {SharedContext|null}\n */\n this.sharedContext = new SharedContext();\n /**\n * Pipeline for tag text values.\n * Call `this.tagsPipeline.run(val, context)` from subclass\n * `closeElement()` implementations.\n * @type {ValueParserPipeline}\n */\n this.tagsPipeline = new ValueParserPipeline(tagChain, this.registry, this.sharedContext);\n /**\n * Pipeline for attribute values.\n * Call `this.attrsPipeline.run(val, context)` from subclass\n * `addAttribute()` implementations.\n * @type {ValueParserPipeline}\n */\n this.attrsPipeline = new ValueParserPipeline(attrChain, this.registry, this.sharedContext);\n\n if (resetPipelines) {\n this.tagsPipeline.resetAll();\n this.attrsPipeline.resetAll();\n }\n\n this._pendingStopNode = false;\n }\n\n /**\n * Add a parsed attribute to the current element.\n * Only called when skip.attributes is false.\n *\n * @param {string} name Processed attribute name (prefix stripped, sanitised)\n * @param {*} value Raw attribute value\n * @param {object} matcher Read-only Matcher proxy from the parser\n */\n addAttribute(name, value, matcher) {\n // Capture XML version from the declaration tag and make it available to\n // value parsers (e.g. EntityParser) via SharedContext.\n if (name === \"version\" && this.tagName === this._rootName) {\n this.sharedContext?.set('xmlVersion', +value);\n }\n const prefixed = `${this.parserOptions.attributes.prefix}${name}${this.parserOptions.attributes.suffix}`;\n const context = new Context(name, matcher, true, true);// attributes are always leaf values\n this.attributes[prefixed] = this.attrsPipeline.run(value, context);\n\n }\n\n /** Hook for subclasses to append a named child node. */\n _addChild(key, val) { }\n\n /**\n * Add a comment node.\n * - Dropped entirely when skip.comment is true.\n * - Stored under nameFor.comment when set; '' = omit from output.\n */\n addComment(text) {\n if (this.parserOptions.skip.comment) return;\n if (this.parserOptions.nameFor.comment) {\n this._addChild(this.parserOptions.nameFor.comment, text);\n }\n }\n\n /**\n * Add a CDATA section.\n * - Dropped entirely when skip.cdata is true.\n * - Stored under nameFor.cdata when set; '' = merge into element text value.\n */\n addLiteral(text) {\n if (this.parserOptions.skip.cdata) return;\n if (this.parserOptions.nameFor.cdata) {\n this._addChild(this.parserOptions.nameFor.cdata, text);\n } else {\n this.addRawValue(text || \"\");\n }\n }\n\n /**\n * Add raw text directly to the current element's text value, bypassing the\n * value-parser chain. Used by addLiteral() when CDATA merges into element text.\n */\n addRawValue(text) {\n this.addValue(text);\n }\n\n /**\n * Receive DOCTYPE entities from the XML parser and store them in\n * SharedContext so that value parsers can access them on their first\n * `parse()` call.\n *\n * @param {object} entities — raw entity map from DocTypeReader\n */\n addInputEntities(entities) {\n this.sharedContext?.set('inputEntities', entities);\n }\n\n /**\n * Handle XML declaration (<?xml ... ?>).\n * Dropped when skip.declaration is true.\n */\n addDeclaration(name) {\n this.addInstruction(name);\n }\n\n /**\n * Handle a processing instruction.\n * Subclasses override; base is a no-op.\n */\n addInstruction(name) {\n }\n\n /**\n * Called when a stop node is fully collected, before `addValue`.\n *\n * @param {TagDetail} tagDetail - name, line, col, index of the stop node\n * @param {string} rawContent - raw unparsed content between the tags\n */\n onStopNode(tagDetail, rawContent) {\n this._pendingStopNode = true;\n if (typeof this.parserOptions.onStopNode === 'function') {\n this.parserOptions.onStopNode(tagDetail, rawContent, this.matcher);\n }\n }\n\n /**\n * Called by the parser when `exitIf` returns true for the current tag.\n * Receives a snapshot of the parser state at the moment of exit, after\n * all open tags have been cleanly closed by the parser.\n *\n * Override in subclasses to record the exit position or annotate output.\n *\n * @param {object} exitInfo\n * @param {object} exitInfo.tagDetail `{ name, line, col, index }` of the tag that triggered the exit\n * @param {object} exitInfo.matcher Read-only matcher at the moment exitIf fired\n * @param {number} exitInfo.depth Nesting depth at exit (0 = root children)\n */\n onExit(exitInfo) { }\n}\n","import BaseOutputBuilder from './BaseOutputBuilder.js';\nimport ValueParserRegistry from './ValueParserRegistry.js';\nimport { MatcherView } from 'path-expression-matcher';\n\n\n\nexport default class BaseOutputBuilderFactory {\n constructor(builderOptions = {}) {\n this.builderOptions = builderOptions;\n this.registry = new ValueParserRegistry();\n }\n\n /**\n * Add or replace a named value parser in the shared registry.\n * Takes effect for all builder instances created after this call.\n *\n * @param {string} name\n * @param {object} parserInstance Must implement `parse(val, context?)`\n */\n registerValueParser(name, parserInstance) {\n this.registry.register(name, parserInstance);\n }\n\n /**\n * Called by XMLParser before each document parse to obtain a fresh builder.\n * This resets all value parsers, builds separate pipeline to process values \n * of tags and attributes and builds a fresh shared context per document.\n *\n * @param {object} options XML Parser options\n * @param {MatcherView} matcherView\n * @returns {BaseOutputBuilder}\n */\n getInstance(parserOptions, matcherView) {\n // return new BaseOutputBuilder(\n // parserOptions,\n // this.builderOptions,\n // matcherView,\n // this.registry, true\n // );\n throw new Error(\"getInstance is not implemented\");\n }\n}","import { buildOptions } from './ParserOptionsBuilder.js';\nimport { BaseOutputBuilder, BaseOutputBuilderFactory, Context } from '@nodable/base-output-builder';\nimport { MatcherView } from 'path-expression-matcher';\n\n\nexport default class CompactBuilderFactory extends BaseOutputBuilderFactory {\n constructor(builderOptions = {}) {\n super();\n this.builderOptions = buildOptions(builderOptions);\n }\n\n getInstance(parserOptions, readonlyMatcher) {\n return new CompactBuilder(\n parserOptions,\n this.builderOptions,\n readonlyMatcher,\n this.registry\n );\n }\n}\n\nexport class CompactBuilder extends BaseOutputBuilder {\n\n /**\n * @param {object} parserOptions\n * @param {object} builderOptions\n * @param {MatcherView} readonlyMatcher\n * @param {object} registry\n */\n constructor(parserOptions, builderOptions, readonlyMatcher, registry) {\n super(parserOptions, builderOptions, readonlyMatcher, registry);\n this.parserOptions = parserOptions;\n this.builderOptions = builderOptions;\n this.tagsStack = [];\n this.root = {};\n this.parent = this.root;\n this.tagName = this._rootName;\n this.value = {};\n this.textValue = \"\";\n this.attributes = {};\n this.hasAttributes = false;\n }\n\n /**\n * Builds the initial value object from current attributes.\n * Returns \"\" when there are no attributes.\n */\n _buildAttributeValue() {\n if (isEmpty(this.attributes)) {\n this.hasAttributes = false;\n return \"\";\n }\n this.hasAttributes = true;\n if (this.parserOptions.attributes.groupBy) {\n return { [this.parserOptions.attributes.groupBy]: this.attributes };\n }\n return this.attributes;\n }\n\n addElement(tag) {\n const value = this._buildAttributeValue();\n this.tagsStack.push([this.tagName, this.textValue, this.value, this.hasAttributes]);\n this.tagName = tag.name;\n this.value = value;\n this.textValue = \"\";\n this.attributes = {};\n }\n\n /**\n * Combines votes from `alwaysArray` and `forceArray` with equal priority.\n *\n * Voting rules:\n * - `alwaysArray`: true if any entry matches, undefined (abstain) if none match.\n * It never votes false — no match is not a veto.\n * - `forceArray`: returns true / false / undefined directly.\n *\n * Resolution:\n * - Any voter returns explicit false → false (veto wins)\n * - Any voter returns true, none false → true\n * - All abstain (undefined) → false (default)\n *\n * @param {boolean|null} isLeafNode\n * @returns {boolean}\n */\n _resolveForceArray(isLeafNode) {\n // --- alwaysArray vote ---\n let alwaysVote; // undefined = abstain\n const matched = this.builderOptions._alwaysArraySet.matchesAny(this.matcher);\n if (matched) alwaysVote = true;\n // no match → alwaysVote stays undefined (abstain, not false)\n\n // --- forceArray vote ---\n let forceVote; // undefined = abstain\n if (typeof this.builderOptions.forceArray === 'function') {\n const result = this.builderOptions.forceArray(this.matcher, isLeafNode);\n if (result === true) forceVote = true;\n else if (result === false) forceVote = false;\n // anything else (undefined, null, …) → abstain\n }\n\n // --- resolution ---\n if (alwaysVote === false || forceVote === false) return false; // veto\n else if (alwaysVote === true || forceVote === true) return true; // at least one yes\n return false; // all abstain → default\n }\n\n closeElement() {\n const tagName = this.tagName;\n let value = this.value; // contains attributes if not skipped\n const textValue = this.textValue;\n const hasAttributes = this.hasAttributes;\n\n // A tag is a leaf node if it has no child elements.\n // It can have attributes and still be a leaf node.\n // hasAttributes is tracked explicitly by _buildAttributeValue() so we never\n // need to reverse-engineer this from key names, prefixes, or groupBy keys.\n const isLeafNode = typeof value !== \"object\" // plain string value (no attrs, no children)\n || Array.isArray(value) // unexpected array → treat as leaf\n || isEmpty(value) // no attributes, no children\n || hasAttributes; // only attributes present, no child elements yet\n\n const context = new Context(\n tagName,\n this.matcher,\n isLeafNode,\n false,\n );\n\n if (isLeafNode) {\n const parsedText = this._pendingStopNode ? textValue : this.tagsPipeline.run(textValue, context);\n\n if (hasAttributes) {\n // Attributes are present — value is already an object.\n // Only write the text node when there is actual text content; an empty\n // parsedText alongside attributes would produce a spurious #text:\"\" key.\n // forceTextNode overrides this and writes the node even when empty.\n if (parsedText !== \"\" && parsedText !== null && parsedText !== undefined) {\n value[this.parserOptions.nameFor.text] = parsedText;\n } else if (this.builderOptions.forceTextNode) {\n value[this.parserOptions.nameFor.text] = parsedText;\n }\n } else if (this.builderOptions.forceTextNode) {\n // No attributes — wrap in an object so the shape is always consistent\n value = { [this.parserOptions.nameFor.text]: parsedText };\n } else {\n // No attributes, no forceTextNode — use the plain parsed value\n value = parsedText;\n }\n } else if (textValue.length > 0 || this.builderOptions.forceTextNode) {\n // Non-leaf node with actual text content sitting between child elements\n // mixed content: element has both child tags and text\n const parsedText = this._pendingStopNode ? textValue : this.tagsPipeline.run(textValue, context);\n value[this.parserOptions.nameFor.text] = parsedText;\n }\n\n let resultTag = { tagName, value };\n\n // if (this.parserOptions.onTagClose !== undefined) {\n // resultTag = this.parserOptions.onTagClose(tagName, value, textValue, this.matcher);\n // if (!resultTag) return;\n // }\n\n const arr = this.tagsStack.pop();\n let parentTag = arr[2];\n\n // Check if this tag should be forced into an array\n const shouldForceArray = this._resolveForceArray(isLeafNode);\n\n parentTag = this._addChildTo(resultTag.tagName, resultTag.value, parentTag, shouldForceArray);\n\n this.tagName = arr[0];\n this.textValue = arr[1];\n this.value = parentTag;\n this.hasAttributes = arr[3]; // restore parent tag's flag\n this._pendingStopNode = false;\n }\n\n _addChild(key, val) {\n if (typeof this.value === \"string\") {\n this.value = { [this.parserOptions.nameFor.text]: this.value };\n }\n this._addChildTo(key, val, this.value, false);\n this.attributes = {};\n }\n\n _addChildTo(key, val, node, forceArray) {\n if (typeof node === 'string') node = {};\n\n if (!Object.prototype.hasOwnProperty.call(node, key)) {\n node[key] = forceArray ? [val] : val;\n } else {\n if (!Array.isArray(node[key])) node[key] = [node[key]];\n node[key].push(val);\n }\n return node;\n }\n\n addValue(text) {\n if (this.textValue.length > 0) this.textValue += `${this.builderOptions.textJoint}${text}`;\n else this.textValue = text;\n }\n\n addInstruction(name) {\n const value = this._buildAttributeValue();\n this._addChild(name, value);\n this.attributes = {};\n }\n\n onExit(exitInfo) { }\n\n getOutput() {\n return this.value;\n }\n}\n\nfunction isEmpty(obj) {\n return Object.keys(obj).length === 0;\n}\n\n","/**\n * ParseError — structured error class for flexible-xml-parser.\n *\n * All errors thrown by the parser are instances of ParseError so callers can\n * distinguish library errors from generic runtime errors and reliably inspect\n * position information.\n *\n * @property {string} code - Machine-readable error code (e.g. 'UNEXPECTED_CLOSE_TAG')\n * @property {number|undefined} index - 0-based character offset from document start (when available)\n */\nexport class ParseError extends Error {\n /**\n * @param {string} message - Human-readable error message\n * @param {string} code - Machine-readable error code\n * @param {object} [position] - Optional position info\n * @param {number} [position.index]\n */\n constructor(message, code, position = {}) {\n super(message);\n this.name = 'ParseError';\n this.code = code;\n\n this.index = position.index ?? undefined;\n }\n\n toString() {\n const pos = this._posStr();\n return pos ? `${this.name} [${this.code}] at ${pos}: ${this.message}` : `${this.name} [${this.code}]: ${this.message}`;\n }\n\n _posStr() {\n if (this.index !== undefined) {\n return `index ${this.index}`;\n }\n return null;\n }\n}\n\n// ─── Error codes ─────────────────────────────────────────────────────────────\n\nexport const ErrorCode = Object.freeze({\n // Input type errors\n INVALID_INPUT: 'INVALID_INPUT',\n INVALID_STREAM: 'INVALID_STREAM',\n\n // Streaming / feed API\n ALREADY_STREAMING: 'ALREADY_STREAMING',\n NOT_STREAMING: 'NOT_STREAMING',\n DATA_MUST_BE_STRING: 'DATA_MUST_BE_STRING',\n\n // Tag structure\n UNEXPECTED_END: 'UNEXPECTED_END',\n UNEXPECTED_CLOSE_TAG: 'UNEXPECTED_CLOSE_TAG',\n MISMATCHED_CLOSE_TAG: 'MISMATCHED_CLOSE_TAG',\n UNEXPECTED_TRAILING_DATA: 'UNEXPECTED_TRAILING_DATA',\n INVALID_TAG: 'INVALID_TAG',\n UNCLOSED_QUOTE: 'UNCLOSED_QUOTE',\n INVALID_ATTRIBUTE_NAME: 'INVALID_ATTRIBUTE_NAME',\n\n // Namespace\n MULTIPLE_NAMESPACES: 'MULTIPLE_NAMESPACES',\n\n // Conformance (illegal chars, attribute value rules)\n ILLEGAL_CHARACTER: 'ILLEGAL_CHARACTER',\n DUPLICATE_ATTRIBUTE: 'DUPLICATE_ATTRIBUTE',\n UNQUOTED_ATTRIBUTE_VALUE: 'UNQUOTED_ATTRIBUTE_VALUE',\n BOOLEAN_ATTRIBUTE_REJECTED: 'BOOLEAN_ATTRIBUTE_REJECTED',\n\n // Security\n SECURITY_PROTOTYPE_POLLUTION: 'SECURITY_PROTOTYPE_POLLUTION',\n SECURITY_RESERVED_OPTION: 'SECURITY_RESERVED_OPTION',\n SECURITY_RESTRICTED_NAME: 'SECURITY_RESTRICTED_NAME',\n\n // Limits (DoS prevention)\n LIMIT_MAX_NESTED_TAGS: 'LIMIT_MAX_NESTED_TAGS',\n LIMIT_MAX_ATTRIBUTES: 'LIMIT_MAX_ATTRIBUTES',\n\n // Entity limits\n ENTITY_MAX_COUNT: 'ENTITY_MAX_COUNT',\n ENTITY_MAX_SIZE: 'ENTITY_MAX_SIZE',\n ENTITY_MAX_EXPANSIONS: 'ENTITY_MAX_EXPANSIONS',\n ENTITY_MAX_EXPANDED_LENGTH: 'ENTITY_MAX_EXPANDED_LENGTH',\n\n // Entity registration\n ENTITY_INVALID_KEY: 'ENTITY_INVALID_KEY',\n ENTITY_INVALID_VALUE: 'ENTITY_INVALID_VALUE',\n\n // Encoding\n UNSUPPORTED_ENCODING: 'UNSUPPORTED_ENCODING',\n INVALID_DECODER: 'INVALID_DECODER',\n ENCODING_MISMATCH: 'ENCODING_MISMATCH',\n});\n\nexport default ParseError;\n","import { ParseError, ErrorCode } from './ParseError.js';\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\n\n\nexport function isSpace(char) {\n return char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\" || char === \"\\f\";\n}\n\n\nexport function isSpaceCode(code) {\n return code === 32 || code === 9 || code === 10 || code === 13 || code === 12; // space \\t \\n \\r \\f\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\nexport const DANGEROUS_PROPERTY_NAMES = [\n 'hasOwnProperty',\n 'toString',\n 'valueOf',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__',\n \"toLocaleString\",\n \"isPrototypeOf\",\n \"propertyIsEnumerable\"\n];\n\nexport const criticalProperties = [\"__proto__\", \"constructor\", \"prototype\"];\n\n// Capacity (in numbers, i.e. QUOTE_PAIRS_CAPACITY/2 quoted-attribute-values)\n// of the reusable typed array each InputSource uses to record quote\n// positions found by scanTagExpEnd(), reused by\n// AttributeProcessor.parseAttributes(). A plain fixed cap, not a growable\n// array — see scanTagExpEnd()'s doc for why: comfortably covers real-world\n// tags, and a tag with more quoted attributes than this just falls back to\n// per-character quote scanning for the overflow, so there's no correctness\n// cost to keeping this small and allocation-free.\nexport const QUOTE_PAIRS_CAPACITY = 128; // 64 quoted attribute values per tag\n\n/**\n * True document-start-relative offset for a source's current read position.\n *\n * `source.startIndex` is only an offset into the source's *live buffer* —\n * every flush() trims already-consumed characters off the front of that\n * buffer and rebases startIndex back down, so startIndex alone drifts from\n * the true document offset the moment a flush has happened. Each source\n * tracks how much it has trimmed away so far in `_baseOffset` (bumped by the\n * trimmed amount inside flush()); the real position is always the sum of\n * the two. This is the one place that sum is computed — every caller that\n * needs an absolute, document-start-relative position (errors, tag index/\n * openEnd/closeEnd, attribute offsets, stop-node end) must go through this\n * function rather than reading `startIndex` directly.\n */\nexport function absolutePosition(source) {\n return source.startIndex + (source._baseOffset || 0);\n}\n\n/**\n * Uniform error-position accessor across all InputSource types.\n *\n * Position reporting is index-only (absolute offset from document start) —\n * no line/column.\n */\nexport function errorPositionOf(source) {\n return { index: absolutePosition(source) };\n}\n\n/**\n * True for character codes that are illegal as literal characters anywhere in\n * an XML document (element text, attribute values, CDATA, comments): 0x00-0x08,\n * 0x0B, 0x0C, 0x0E-0x1F. Tab/LF/CR (0x09/0x0A/0x0D) are legal whitespace and\n * excluded. A numeric character reference like `�` is just ASCII text at\n * this stage (entity expansion happens later, in the output builder), so it\n * never reaches this check as a raw byte.\n */\nfunction isIllegalControlCode(c) {\n return c <= 8 || c === 11 || c === 12 || (c >= 14 && c <= 31);\n}\n\n/**\n * Normalize a complete piece of document content (element text, CDATA\n * content, or comment content) per XML §2.11: a real `\\r\\n` pair or a lone\n * `\\r` becomes a single `\\n`; a bare `\\n` is left alone. Also rejects illegal\n * literal control characters (see isIllegalControlCode) — this check always\n * runs, regardless of autoClose/lenient settings.\n *\n * Meant to be called exactly once per finished token (a whole text run, a\n * whole CDATA block, a whole comment) — never mid-scan — so no chunk-boundary\n * carry logic is needed: by the time a reader has a complete string, any\n * `\\r\\n` that happened to straddle a feed() chunk boundary has already been\n * concatenated back together by the caller.\n *\n * @param {string} str\n * @param {object} [source] - for error position only; omit if unavailable.\n * @returns {string}\n */\nexport function sanitizeContent(str, source) {\n const len = str.length;\n let hasCR = false;\n for (let i = 0; i < len; i++) {\n const c = str.charCodeAt(i);\n if (isIllegalControlCode(c)) {\n throw new ParseError(\n `Illegal control character 0x${c.toString(16).padStart(2, '0')} in document content`,\n ErrorCode.ILLEGAL_CHARACTER,\n source ? errorPositionOf(source) : {}\n );\n }\n if (c === 13) hasCR = true;\n }\n if (!hasCR) return str; // fast path — nothing to fold, no reallocation\n\n let out = '';\n let segStart = 0;\n for (let i = 0; i < len; i++) {\n if (str.charCodeAt(i) === 13) {\n out += str.substring(segStart, i) + '\\n';\n if (str.charCodeAt(i + 1) === 10) i++; // \\r\\n pair → one \\n, not two\n segStart = i + 1;\n }\n }\n out += str.substring(segStart);\n return out;\n}\n\n/**\n * Assert that the upcoming characters in the source match the expected string.\n * If not enough data → throws UNEXPECTED_END.\n * If mismatch → throws INVALID_TAG with the given errorMsg.\n * On success, consumes the matched characters (advances startIndex).\n *\n * @param {object} source - input source (must have canRead, matchAhead, updateBufferBoundary)\n * @param {string} expected - string to match\n * @param {string} errorMsg - description of what is being read (used in error messages)\n * @param {boolean} [caseInsensitive=false]\n */\nexport function expectMatch(source, expected, errorMsg, caseInsensitive = false) {\n const len = expected.length;\n if (!source.canRead(len)) {\n throw new ParseError(\n `Unexpected end of source reading ${errorMsg}`,\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(source)\n );\n }\n const matched = source.matchAhead(expected, caseInsensitive);\n if (matched !== true) {\n throw new ParseError(\n `Invalid ${errorMsg}`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n source.updateBufferBoundary(len);\n}\n\n/**\n * Assert that the source has at least `n` characters available from the current position.\n * Throws UNEXPECTED_END if not enough data.\n * Does NOT consume any characters.\n *\n * @param {object} source - input source (must have canRead)\n * @param {number} n - number of characters needed\n * @param {string} errorMsg - description of what is being read (used in error message)\n */\nexport function ensureCanRead(source, n, errorMsg) {\n if (!source.canRead(n)) {\n throw new ParseError(\n `Unexpected end of source reading ${errorMsg}`,\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(source)\n );\n }\n}","import { CompactBuilderFactory } from '@nodable/compact-builder';\nimport { Expression, ExpressionSet } from 'path-expression-matcher';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { DANGEROUS_PROPERTY_NAMES, criticalProperties } from './util.js';\n\nconst defaultOnDangerousProperty = (name) => {\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return \"__\" + name;\n }\n return name;\n};\n\nexport const defaultOptions = {\n // --- skip group ---\n // Controls which node types are excluded from output\n skip: {\n declaration: false, // Skip <?xml ... ?> declaration\n pi: false, // Skip processing instructions (other than declaration)\n attributes: true, // Skip all attributes\n cdata: false, // Exclude CDATA sections from output entirely\n comment: false, // Exclude comments from output entirely\n nsPrefix: false, // Strip namespace prefixes (e.g. ns:tag → tag)\n tags: [], // Tag paths to skip entirely — content is silently dropped from output\n whitespaceText: true // addValue() of a builder would not be called if text is only whitespaces\n },\n\n // --- nameFor group ---\n // Property names used when including special nodes in output.\n nameFor: {\n text: '#text', // Property for mixed text content\n cdata: '', // '' = merge CDATA into text value\n comment: '', // '' = omit comments from output\n },\n\n // --- attributes group ---\n attributes: {\n // 'allow' (default) keeps a valueless attribute (e.g. <e flag/>) as `true`.\n // 'ignore' drops it silently; 'throw' rejects the document as soon as one\n // is found. Replaces a previous same-named boolean option that had no\n // effect on parsing (it was never read anywhere in the parsing code).\n booleanType: 'allow',\n // 'overwrite' (default) — last occurrence of a repeated attribute name wins.\n // 'ignore' — first occurrence wins, later ones are dropped entirely.\n // 'throw' — reject the document as soon as a repeated name is found on one tag.\n duplicate: 'overwrite',\n groupBy: '', // Group all attributes under this key; '' = inline with tag\n prefix: '@_', // Prepended to attribute names in output\n suffix: '', // Appended to attribute names in output\n },\n\n // --- tags group ---\n tags: {\n unpaired: [], // Tags that never have a closing tag (e.g. br, img, hr)\n stopNodes: [], // Tag paths whose content is captured raw without parsing\n },\n\n // --- security ---\n strictReservedNames: false,\n onDangerousProperty: defaultOnDangerousProperty,\n // sanitizeNames: false skips the dangerous-property rename step only\n // (hasOwnProperty, toString, valueOf, ... pass through unprefixed,\n // onDangerousProperty is never called for them). It does NOT disable the\n // critical-property check (__proto__, constructor, prototype) — that\n // guards against actual prototype pollution and always runs. Only for\n // trusted input — internal XML-to-XML pipelines, generated documents —\n // where the source is known not to contain hostile names. Does NOT affect\n // strictReservedNames, which is a separate, independent check.\n sanitizeNames: true,\n\n // --- filtering (path-expression-matcher) ---\n only: [], // for future\n\n // --- DOCTYPE parsing ---\n // Controls whether DOCTYPE entities are collected and read-time security limits.\n //\n // enabled — false (default) → DOCTYPE is read (to consume it) but entities\n // are discarded and never forwarded to output builders\n // true → collect DOCTYPE entities and forward them to the output builder\n // Note: the output builder must have an EntitiesValueParser registered\n // under 'entity' and 'entity' must be in its\n // valueParsers chain for replacement to actually happen.\n //\n // Read-time security limits (enforced by DocTypeReader at declaration time):\n // maxEntityCount — max entities declared in a DOCTYPE (default: 100)\n // maxEntitySize — max bytes per entity definition value (default: 10000)\n //\n // Replacement-time limits (maxTotalExpansions, maxExpandedLength) are configured\n // on EntitiesValueParser directly — they are not part of doctypeOptions.\n doctypeOptions: {\n enabled: false,\n maxEntityCount: 100,\n maxEntitySize: 10000,\n },\n\n // --- autoClose ---\n // Controls parser behaviour when tags are unclosed or mismatched.\n //\n // onEof — what to do when EOF is reached with open tags still on the stack\n // 'throw' (default) → throw an error\n // 'closeAll' → silently close all remaining open tags\n //\n // onMismatch — what to do when a closing tag doesn't match the current open tag\n // 'throw' (default) → throw an error\n // 'recover' → pop the stack toward the nearest matching opener;\n // if no match is found the tag is discarded\n // 'discard' → silently ignore the bad closing tag\n //\n // collectErrors — when true, errors are recorded in result.__parseErrors instead\n // of being silently dropped. Each entry has the shape:\n // { type, tag, expected, index }\n //\n // Shorthand: autoClose: 'html' sets onEof:'closeAll', onMismatch:'discard',\n // collectErrors:true, and adds the standard HTML void elements to tags.unpaired.\n autoClose: null, // null = feature disabled; throws on any malformed input\n\n // --- limits (DoS prevention) ---\n // Group structural limits that guard against resource exhaustion.\n //\n // maxNestedTags — maximum tag nesting depth; throws when exceeded.\n // Prevents stack-overflow attacks via deeply nested XML.\n // Default: null (no limit)\n //\n // maxAttributesPerTag — maximum number of attributes on a single tag.\n // Throws when a tag exceeds this count.\n // Default: null (no limit)\n //\n limits: {\n maxNestedTags: null,\n maxAttributesPerTag: null,\n },\n\n // --- feedable (feed/end and parseStream input options) ---\n // Controls buffer behaviour for the FeedableSource and StreamSource.\n //\n // maxBufferSize — maximum number of characters allowed in the buffer at\n // any one time. Prevents memory exhaustion when a caller\n // feeds data faster than it is consumed.\n // Default: 10 MB (10 * 1024 * 1024 characters)\n //\n // autoFlush — when true (default), already-processed characters are\n // automatically discarded from the front of the buffer\n // whenever the processed portion exceeds flushThreshold.\n // Keeps memory usage flat for large documents.\n //\n // flushThreshold — number of processed characters that triggers an auto-\n // flush. Lower values free memory sooner but incur more\n // string-slice operations. Default: 1024 characters (1 KB)\n //\n feedable: {\n maxBufferSize: 10 * 1024 * 1024,\n autoFlush: true,\n flushThreshold: 1024,\n bufferSize: 256\n },\n\n // --- exitIf ---\n // Stops parsing as soon as the predicate returns true for the current tag.\n //\n // The callback receives a read-only matcher positioned at the just-opened tag:\n // exitIf(matcher) → boolean\n //\n // When exitIf returns true the parser immediately:\n // 1. Closes all currently open tags (innermost first) by calling addTextNode()\n // and popTag() for each, so the output builder can finalise its tree.\n // 2. Calls outputBuilder.onExit({ tagDetail, matcher, tagsStack }) so the\n // builder can record that the parse was intentionally truncated.\n // 3. Breaks the parse loop — no further source characters are read.\n //\n // The parse call returns the partial-but-consistent output as normal.\n // No error is thrown.\n //\n // Default: null (feature disabled)\n exitIf: () => false,\n\n //onStopNode(tagDetail, rawContent, matcher)\n // --- output ---\n OutputBuilder: null, //TODO: accept lower case\n\n // --- decoding ---\n // Controls how raw bytes (Buffer/Uint8Array input to parse()/parseBytesArr(),\n // or chunks fed to feed()/parseStream()) are turned into text.\n //\n // encoding — 'auto' (default) sniffs BOM + a leading <?xml ... encoding=\"...\">\n // declaration per XML 1.0 Appendix F, falling back to utf8 if\n // neither is present. Set explicitly (e.g. 'utf8', 'utf16le',\n // 'latin1', 'ascii', or a custom-registered name) to skip\n // detection entirely.\n // NOTE: streaming inputs (feed()/parseStream()) do not yet\n // support 'auto' detection — see docs/16-encoding.md — and\n // fall back to utf8 the same way they always have.\n //\n // customDecoders — { name: descriptor } map merged into the encoding\n // registry before resolution, for encodings FXP doesn't\n // ship natively (e.g. Shift_JIS via iconv-lite). See\n // docs/16-encoding.md for the descriptor shape.\n //\n decoding: {\n encoding: 'auto',\n customDecoders: null,\n },\n};\n\n// All names that should never appear as property keys\nconst ALL_RESERVED = new Set([...criticalProperties, ...DANGEROUS_PROPERTY_NAMES]);\nexport { ALL_RESERVED as RESERVED_JS_NAMES };\n\nfunction validatePropertyName(value, optionName) {\n if (typeof value !== 'string' || value === '') return;\n if (ALL_RESERVED.has(value)) {\n throw new ParseError(\n `SECURITY: '${value}' is a reserved JavaScript keyword and cannot be used as ${optionName}`,\n ErrorCode.SECURITY_RESERVED_OPTION\n );\n }\n}\n\nexport const buildOptions = function (options) {\n // Validate security-sensitive option values BEFORE merging\n if (options) {\n if (options.nameFor?.text) validatePropertyName(options.nameFor.text, 'nameFor.text');\n if (options.nameFor?.cdata) validatePropertyName(options.nameFor.cdata, 'nameFor.cdata');\n if (options.nameFor?.comment) validatePropertyName(options.nameFor.comment, 'nameFor.comment');\n if (options.attributes?.prefix) validatePropertyName(options.attributes.prefix, 'attributes.prefix');\n if (options.attributes?.groupBy) validatePropertyName(options.attributes.groupBy, 'attributes.groupBy');\n\n // Validate limits option\n if (options.limits !== undefined && options.limits !== null) {\n if (typeof options.limits !== 'object') {\n throw new ParseError(`'limits' must be an object, got ${typeof options.limits}`, ErrorCode.INVALID_INPUT);\n }\n const { maxNestedTags, maxAttributesPerTag } = options.limits;\n if (maxNestedTags !== undefined && maxNestedTags !== null &&\n (typeof maxNestedTags !== 'number' || !Number.isInteger(maxNestedTags) || maxNestedTags < 1)) {\n throw new ParseError(`'limits.maxNestedTags' must be a positive integer, got ${maxNestedTags}`, ErrorCode.INVALID_INPUT);\n }\n if (maxAttributesPerTag !== undefined && maxAttributesPerTag !== null &&\n (typeof maxAttributesPerTag !== 'number' || !Number.isInteger(maxAttributesPerTag) || maxAttributesPerTag < 0)) {\n throw new ParseError(`'limits.maxAttributesPerTag' must be a non-negative integer, got ${maxAttributesPerTag}`, ErrorCode.INVALID_INPUT);\n }\n }\n }\n\n const finalOptions = deepClone(defaultOptions);\n\n if (options) {\n copyProperties(finalOptions, options);\n }\n\n if (!finalOptions.OutputBuilder) {\n finalOptions.OutputBuilder = new CompactBuilderFactory();\n }\n\n // Normalize stopNodes and skip.tags entries into Expression objects with config embedded\n // in Expression.data as { nested, skipEnclosures }. Build a sealed ExpressionSet for\n // O(1) hot-path matching in the parser.\n //\n // Accepted entry forms (identical for both stopNodes and skip.tags):\n // \"..script\"\n // → Expression(\"..script\", {}, { nested: false, skipEnclosures: [] })\n //\n // Expression instance\n // → re-wrapped with { nested: false, skipEnclosures: [] } in data\n //\n // { expression: \"..script\", nested?: boolean, skipEnclosures?: [] }\n // { expression: Expression, nested?: boolean, skipEnclosures?: [] }\n // → Expression with the given config embedded in .data\n //\n // `nested` defaults to false; `skipEnclosures` defaults to [].\n // The two flags are fully independent — any combination is valid.\n if (Array.isArray(finalOptions.tags?.stopNodes)) {\n const stopSet = new ExpressionSet();\n finalOptions.tags.stopNodes = finalOptions.tags.stopNodes.map(\n (entry) => normalizeTagEntry(entry, 'stopNodes', stopSet)\n );\n stopSet.seal();\n finalOptions.tags.stopNodesSet = stopSet;\n }\n\n if (Array.isArray(finalOptions.skip?.tags)) {\n const skipSet = new ExpressionSet();\n finalOptions.skip.tags = finalOptions.skip.tags.map(\n (entry) => normalizeTagEntry(entry, 'skip.tags', skipSet)\n );\n skipSet.seal();\n finalOptions.skip.tagsSet = skipSet;\n }\n\n if (finalOptions.onDangerousProperty === null) {\n finalOptions.onDangerousProperty = defaultOnDangerousProperty;\n }\n\n // Validate exitIf\n if (finalOptions.exitIf !== null && finalOptions.exitIf !== undefined) {\n if (typeof finalOptions.exitIf !== 'function') {\n throw new ParseError(\n `'exitIf' must be a function, got ${typeof finalOptions.exitIf}`,\n ErrorCode.INVALID_INPUT,\n );\n }\n }\n\n // Resolve autoClose: expand the 'html' preset and normalise to an object\n finalOptions.autoClose = resolveAutoClose(finalOptions.autoClose, finalOptions);\n\n return finalOptions;\n};\n\n/** Standard HTML void elements — never have a closing tag. */\nconst HTML_VOID_ELEMENTS = [\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n];\n\n/**\n * Normalise the raw `autoClose` option value into either null (disabled)\n * or a fully-resolved options object.\n *\n * @param {null|string|object} raw - Value supplied by the user\n * @param {object} opts - The already-merged final options (mutated for html preset)\n * @returns {null|object}\n */\nfunction resolveAutoClose(raw, opts) {\n if (!raw) return null;\n\n if (raw === 'html') {\n // Apply HTML-specific tag defaults\n const existingUnpaired = opts.tags.unpaired || [];\n const merged = [...new Set([...existingUnpaired, ...HTML_VOID_ELEMENTS])];\n opts.tags = { ...opts.tags, unpaired: merged };\n\n return {\n onEof: 'closeAll',\n onMismatch: 'discard',\n collectErrors: true,\n };\n }\n\n if (typeof raw === 'string') {\n // e.g. autoClose: 'closeAll' — treat as shorthand for onEof\n return {\n onEof: raw,\n onMismatch: 'throw',\n collectErrors: false,\n };\n }\n\n if (typeof raw === 'object') {\n return {\n onEof: raw.onEof || 'throw',\n onMismatch: raw.onMismatch || 'throw',\n collectErrors: raw.collectErrors || false,\n };\n }\n\n return null;\n}\n\n/**\n * Normalize one entry from `tags.stopNodes` or `skip.tags` into an Expression\n * whose `.data` carries `{ nested, skipEnclosures }`, and register it in `set`.\n *\n * Accepted forms:\n * string → bare pattern, defaults applied\n * Expression instance → re-wrapped with defaults\n * { expression: string|Expression, nested?, skipEnclosures? }\n *\n * @param {string|Expression|object} entry\n * @param {string} optionName - Used in error messages (\"stopNodes\" or \"skip.tags\")\n * @param {ExpressionSet} set - The set to register the resulting Expression into\n * @returns {Expression}\n */\nfunction normalizeTagEntry(entry, optionName, set) {\n let pattern, nested, skipEnclosures;\n\n if (typeof entry === 'string') {\n if (entry.length === 0) throw new ParseError(`${optionName} expression cannot be empty`, ErrorCode.INVALID_INPUT);\n pattern = entry;\n nested = false;\n skipEnclosures = [];\n } else if (entry instanceof Expression) {\n // Bare Expression — keep its pattern, apply defaults for missing data fields\n pattern = entry.toString();\n nested = entry.data?.nested ?? false;\n skipEnclosures = entry.data?.skipEnclosures ?? [];\n } else if (entry && typeof entry === 'object' && entry.expression !== undefined) {\n const raw = entry.expression;\n if (typeof raw === 'string') {\n if (raw.length === 0) throw new ParseError(`${optionName} expression cannot be empty`, ErrorCode.INVALID_INPUT);\n pattern = raw;\n } else if (raw instanceof Expression) {\n pattern = raw.toString();\n } else {\n throw new ParseError(`${optionName} expression must be a string or Expression instance`, ErrorCode.INVALID_INPUT);\n }\n nested = entry.nested === true;\n skipEnclosures = Array.isArray(entry.skipEnclosures) ? entry.skipEnclosures : [];\n } else {\n throw new ParseError(\n `Invalid ${optionName} entry: expected a string, Expression, or { expression, nested?, skipEnclosures? } object.`,\n ErrorCode.INVALID_INPUT,\n );\n }\n\n const expr = new Expression(pattern, {}, { nested, skipEnclosures });\n set.add(expr);\n return expr;\n}\n\nfunction deepClone(obj) {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(deepClone);\n if (obj instanceof RegExp) return obj; // ← guard\n if (obj instanceof Expression) return obj; // ← guard — Expression instances are immutable\n const clone = {};\n for (const key of Object.keys(obj)) {\n clone[key] = typeof obj[key] === 'function' ? obj[key] : deepClone(obj[key]);\n }\n return clone;\n}\n\nfunction copyProperties(target, source) {\n for (const key of Object.keys(source)) {\n // Guard against prototype pollution via option keys\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n if (key === 'OutputBuilder') {\n target[key] = source[key];\n } else if (typeof source[key] === 'function') {\n target[key] = source[key];\n } else if (source[key] instanceof RegExp) { // ← guard, before the generic object check\n target[key] = source[key];\n } else if (Array.isArray(source[key])) {\n target[key] = source[key];\n } else if (typeof source[key] === 'object' && source[key] !== null) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n copyProperties(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n}","/**\n * Shared scanTagExpEnd implementations for sources whose buffer is a JS string\n * (StringSource, FeedableSource, CharScanStrategy).\n *\n * Two separate functions instead of one with a flag — the flag was evaluated\n * inside the loop on every character, which adds overhead to the hottest path\n * in the parser. The caller resolves which variant to use once, before the\n * scan begins.\n *\n * Neither function is exported as a method — each source assigns both onto\n * itself so `this` binding works correctly with zero indirection.\n */\n\n/**\n * Scan for the unquoted '>' that ends a tag expression, and record each\n * quote boundary in `this._quotePairs` as it goes. Used when attributes\n * will be parsed (skip.attributes is false).\n *\n * @returns {number} relative offset of the unquoted '>' from startIndex,\n * or -1 if the buffer is exhausted (chunk boundary for FeedableSource,\n * malformed input for StringSource/CharScanStrategy).\n */\nexport function scanTagExpEnd() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n const pairs = this._quotePairs;\n const capacity = pairs.length;\n let pairsLen = 0;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === \"'\") {\n if (!inDouble) {\n inSingle = !inSingle;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === '\"') {\n if (!inSingle) {\n inDouble = !inDouble;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === '>' && !inSingle && !inDouble) {\n this._quotePairsLen = pairsLen;\n return i - start;\n }\n }\n this._quotePairsLen = pairsLen;\n return -1;\n}\n\n/**\n * Scan for the unquoted '>' that ends a tag expression, without recording\n * quote positions. Used when attributes are being skipped entirely\n * (skip.attributes is true) — nobody will read _quotePairs, so don't pay\n * to populate it.\n *\n * @returns {number} relative offset of the unquoted '>' from startIndex,\n * or -1 if the buffer is exhausted.\n */\nexport function scanTagExpEndFast() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === \"'\") {\n if (!inDouble) inSingle = !inSingle;\n } else if (c === '\"') {\n if (!inSingle) inDouble = !inDouble;\n } else if (c === '>' && !inSingle && !inDouble) {\n return i - start;\n }\n }\n return -1;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { isSpace, QUOTE_PAIRS_CAPACITY } from '../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from './scanTagExpEnd.js';\n\n/**\n * StringSource — input source backed by an in-memory string.\n *\n * ### Memory reclamation\n *\n * Unlike FeedableSource, the full document is available from the start, so\n * there is no chunk-boundary risk and rewindToMark() is a safe no-op.\n * However, the parsed prefix of the string is still held in memory until the\n * parse finishes. flush() reclaims that prefix by slicing the buffer and\n * resetting startIndex to 0.\n *\n * The same mark/flush protocol used by FeedableSource is implemented here so\n * all reader functions (readTagExp, readClosingTagName, readCdata, etc.) work\n * without any source-type conditionals:\n *\n * markTokenStart() — save the current read position at the start of a token\n * rewindToMark() — no-op for StringSource (full doc always present)\n * flush() — drop the already-parsed prefix to free memory\n *\n * Auto-flush fires inside updateBufferBoundary() whenever the processed\n * portion exceeds flushThreshold and no token checkpoint is active.\n *\n * Position reporting is index-only (absolute character offset from document\n * start) — no line/column tracking. That bookkeeping used to run on every\n * character and every bulk-read span for a field most callers never read;\n * dropping it is a straight speed win. A caller that wants line/column can\n * derive it from `index` plus the original document text.\n */\nexport default class StringSource {\n /**\n * @param {string} str — the full XML document string\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed chars\n */\n constructor(str, options = {}) {\n this.buffer = str;\n // Boundary pointer: data before this index has been consumed and may be freed.\n this.startIndex = 0;\n // Running total of characters trimmed off the front by flush() so far.\n // startIndex alone is only an offset into the live (post-trim) buffer;\n // the true document-start-relative position is startIndex + _baseOffset.\n // See util.js#absolutePosition — the single place that sum is computed.\n this._baseOffset = 0;\n\n this.autoFlush = options.autoFlush !== false;\n this.flushThreshold = options.flushThreshold ?? 1024;\n\n // Two-level mark stack matching FeedableSource's API.\n // _marks[0] = outer mark (parseXml loop), _marks[1] = inner mark (readers).\n // -1 means \"not set\" for that level.\n this._marks = [-1, -1];\n\n // Reused across every scanTagExpEnd() call, never reallocated. Holds\n // flat [openIdx, closeIdx, ...] pairs of quoted-attribute-value\n // positions found while scanning for the tag's closing '>'. Fixed-size\n // typed array + a manually-tracked length (`_quotePairsLen`), NOT\n // Array.push() — push() was measured to cost more than the per-character\n // quote re-scan it was meant to replace (bounds/growth checks on every\n // call add up across thousands of tags). Plain indexed writes with a\n // local counter avoid that entirely. See scanTagExpEnd() doc for the\n // capacity rationale and overflow behaviour.\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n }\n\n // ─── Token-start checkpoint ───────────────────────────────────────────────\n\n /**\n * Save the current read position into the two-level mark stack.\n *\n * Mirrors FeedableSource's two-level API so all reader functions work\n * identically regardless of source type:\n *\n * level 0 (default) — outer mark, set by parseXml()'s main loop.\n * level 1 — inner mark, set by individual reader functions.\n *\n * For StringSource the distinction only matters for flush() boundary\n * calculations — rewindToMark() is always a no-op here.\n *\n * @param {0|1} [level=0]\n */\n markTokenStart(level = 0) {\n this._marks[level] = this.startIndex;\n }\n\n /**\n * Restore startIndex to the last markTokenStart() position.\n *\n * StringSource always has the full document available, so a mid-token end\n * of input cannot occur and this method is a safe no-op. It exists solely\n * so caller code (XMLParser.feed / parseXml) can call rewindToMark()\n * unconditionally without branching on source type.\n */\n rewindToMark() {\n // No-op: the complete document is in memory; no rewind is ever needed.\n }\n\n /** Clear both mark slots (mirrors FeedableSource.clearMark). */\n clearMark() {\n this._marks[0] = -1;\n this._marks[1] = -1;\n }\n\n /**\n * Discard the already-processed prefix of the buffer to free memory.\n *\n * The flush origin is the minimum of all active mark positions so that any\n * in-progress token (at either mark level) is preserved in the buffer.\n * If no marks are active, the origin is startIndex itself.\n */\n flush() {\n let origin = this.startIndex;\n for (const m of this._marks) {\n if (m >= 0 && m < origin) origin = m;\n }\n if (origin > 0) {\n this.buffer = this.buffer.substring(origin);\n const marksLen = this._marks.length;\n for (let i = 0; i < marksLen; i++) {\n if (this._marks[i] >= 0) this._marks[i] -= origin;\n }\n this.startIndex -= origin;\n this._baseOffset += origin;\n }\n }\n\n // ─── Core read interface ──────────────────────────────────────────────────\n\n readCh() {\n return this.buffer[this.startIndex++];\n }\n\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n }\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n }\n\n /**\n * Check whether the upcoming characters equal `expected`, without\n * consuming or allocating anything — no substring is built even for a\n * full match. Contrast with the `readStr(n).toUpperCase() === \"X\"` style,\n * which allocates two throwaway strings on every call, matching or not.\n *\n * Caller decides what to do with a match — this never consumes. Follow a\n * `true` result with `updateBufferBoundary(expected.length)` (or more, if\n * something like trailing whitespace should also be consumed).\n *\n * @param {string} expected - literal to match against, already in the\n * case matchAhead should compare against when caseInsensitive is true\n * (e.g. pass \"system\", not \"SYSTEM\", together with caseInsensitive=true)\n * @param {boolean} [caseInsensitive=false]\n * @returns {boolean|null} true on full match, false on a definite\n * mismatch, null if the buffer runs out before enough characters are\n * available to decide either way (treat like any other\n * not-enough-data-yet case — same handling as scanTagExpEnd's -1)\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n }\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd = scanTagExpEnd;\n scanTagExpEndFast = scanTagExpEndFast;\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Single-character variant of readUpto — faster because there is no inner\n * match loop. Reads until `stopChar` is found, consumes it, and returns\n * the text before it.\n *\n * @param {string} stopChar Exactly one character.\n * @returns {string}\n */\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n }\n\n readUptoCloseTag(stopStr) { // stopStr: \"</tagname\"\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n // 0: scanning, 1: tag-name matched (scanning for '>'), 2: full match\n let state = 0;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; } // false match e.g. </scriptX>\n } else {\n // Try to match stopStr at position i\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) {\n state = 1;\n tagMatchStart = i;\n i += stopLength - 1; // skip past matched string\n }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n readFromBuffer(n, updateIndex) {\n const ch = n === 1\n ? this.buffer[this.startIndex]\n : this.buffer.substring(this.startIndex, this.startIndex + n);\n if (updateIndex) this.updateBufferBoundary(n);\n return ch;\n }\n\n /**\n * Advance the read cursor by n characters.\n *\n * Triggers an automatic flush of already-processed data when autoFlush is\n * enabled, the processed portion has grown past flushThreshold, and no\n * token checkpoint is currently active (a flush while a checkpoint is live\n * would invalidate the saved position).\n *\n * @param {number} [n=1]\n */\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n // See FeedableSource.updateBufferBoundary() for why there is no \"any mark\n // active\" gate here — flush()'s own min-origin computation already\n // protects any in-progress token; a separate gate was redundant and, since\n // marks are effectively always set in normal operation, made flush()\n // permanently unreachable. See specs/flushArchitecture_spec.js.\n if (this.autoFlush && this.startIndex >= this.flushThreshold) {\n this.flush();\n }\n }\n\n /**\n * Returns true when there is at least one character available at or after\n * the given offset (relative to startIndex). Mirrors FeedableSource's\n * formula so all three sources answer the same question the same way.\n * @param {number} [n=0]\n */\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n }\n}\n","import { ParseError, ErrorCode } from '../../ParseError.js';\nimport { isSpaceCode } from '../../util.js';\n\n/**\n * ByteScanStrategy — for self-synchronizing encodings only (utf8/ascii/latin1\n * by default; any custom encoding that explicitly asserts\n * `selfSynchronizing: true`). All positions/indices here (`n`, `index`, loop\n * counters passed in from XmlPartReader/DocTypeReader/StopNodeProcessor) are\n * BYTE offsets, matching BufferSource's original convention throughout —\n * unchanged by the encoding feature. Delimiters (<,>,\",') are matched at the\n * byte level for speed, safe here precisely because self-synchronizing means\n * an ASCII delimiter byte can never occur as a continuation byte of a\n * different character.\n *\n * `decodeCharAt(buf, i)` -> `{ char, width }` is the one piece that varies\n * per encoding (width in BYTES for the character starting at byte offset i).\n * ascii/latin1 are always width 1. utf8 needs the real multi-byte decode so\n * `startIndex` advances by the correct byte count per character — that part\n * is still required for correct scanning, independent of any position\n * reporting: the parser only ever exposes the absolute byte/char `index`,\n * not line/col, so no per-character column bookkeeping is needed here.\n *\n * Functions here are plain (non-arrow) so they can be assigned directly onto\n * a BufferSource instance and keep correct `this` binding with zero extra\n * indirection — resolved once at construction, no per-call branching.\n */\nexport function createByteScanStrategy(decodeCharAt, nodeEncoding = 'utf8', isContinuationByte = () => false) {\n return {\n readCh() {\n const { char, width } = decodeCharAt(this.buffer, this.startIndex);\n this.startIndex += width;\n return char;\n },\n\n /** index is a BYTE offset ahead of startIndex, same as original BufferSource. */\n readChAt(index) {\n return decodeCharAt(this.buffer, this.startIndex + index).char;\n },\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.slice(from, from + n).toString(nodeEncoding);\n },\n\n /**\n * See StringSource.js for the full doc — same contract. Compares raw\n * byte codes directly rather than going through decodeCharAt/readChAt:\n * safe here because every literal this is ever called with (DOCTYPE\n * keywords, tag names for closing-tag matching) is plain ASCII, and\n * ASCII is always exactly one byte per character in every encoding this\n * strategy handles (UTF-8, Latin-1, ASCII — see the module doc above\n * for why that guarantee is what lets this whole strategy exist).\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n const b = this.buffer[this.startIndex + i];\n if (b === undefined) return null;\n let code = b;\n if (caseInsensitive && code >= 65 && code <= 90) code += 32; // 'A'-'Z' -> 'a'-'z'\n if (code !== expected.charCodeAt(i)) return false;\n }\n return true;\n },\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Compares raw byte codes (39=', 34=\", 62=>)\n // rather than characters — safe here because self-synchronizing encodings\n // guarantee these byte values never appear as continuation bytes.\n scanTagExpEnd() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n const pairs = this._quotePairs;\n const capacity = pairs.length;\n let pairsLen = 0;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === 39) {\n if (!inDouble) {\n inSingle = !inSingle;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === 34) {\n if (!inSingle) {\n inDouble = !inDouble;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === 62 && !inSingle && !inDouble) {\n this._quotePairsLen = pairsLen;\n return i - start;\n }\n }\n this._quotePairsLen = pairsLen;\n return -1;\n },\n\n scanTagExpEndFast() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === 39) {\n if (!inDouble) inSingle = !inSingle;\n } else if (c === 34) {\n if (!inSingle) inDouble = !inDouble;\n } else if (c === 62 && !inSingle && !inDouble) {\n return i - start;\n }\n }\n return -1;\n },\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n const stopBuffer = Buffer.from(stopStr);\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopBuffer[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.slice(this.startIndex, i).toString(nodeEncoding);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoChar(stopChar) {\n const stopCode = stopChar.charCodeAt(0);\n const buf = this.buffer;\n const len = buf.length;\n for (let i = this.startIndex; i < len; i++) {\n if (buf[i] === stopCode) {\n const result = buf.slice(this.startIndex, i).toString(nodeEncoding);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n const stopBuffer = Buffer.from(stopStr);\n const GT = 62;\n let tagMatchStart = -1;\n let state = 0;\n for (let i = this.startIndex; i < inputLength; i++) {\n const b = this.buffer[i];\n if (state === 1) {\n if (isSpaceCode(b)) continue;\n if (b === GT) { state = 2; }\n else { state = 0; tagMatchStart = -1; }\n } else {\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopBuffer[j]) { matched = false; break; }\n }\n if (matched) { state = 1; tagMatchStart = i; i += stopLength - 1; }\n }\n if (state === 2) {\n const result = this.buffer.slice(this.startIndex, tagMatchStart).toString(nodeEncoding);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readFromBuffer(n, shouldUpdate) {\n if (n === 1) {\n const { char, width } = decodeCharAt(this.buffer, this.startIndex);\n if (shouldUpdate) this.updateBufferBoundary(width);\n return char;\n }\n const ch = this.buffer.slice(this.startIndex, this.startIndex + n).toString(nodeEncoding);\n if (shouldUpdate) this.updateBufferBoundary(n);\n return ch;\n },\n\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n if (this.autoFlush && this.startIndex >= this.flushThreshold && this._tokenStart < 0) {\n this.flush();\n }\n },\n\n // Relative to current position, matching FeedableSource's formula.\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n },\n };\n}\n\n// ─── decodeCharAt implementations ───────────────────────────────────────────\n\nexport function decodeCharAtFixedWidth1(buf, i) {\n return { char: String.fromCharCode(buf[i]), width: 1 };\n}\n\nexport function decodeCharAtUtf8(buf, i) {\n const b0 = buf[i];\n let width = 1;\n if ((b0 & 0x80) === 0x00) width = 1;\n else if ((b0 & 0xe0) === 0xc0) width = 2;\n else if ((b0 & 0xf0) === 0xe0) width = 3;\n else if ((b0 & 0xf8) === 0xf0) width = 4;\n // Invalid/truncated lead byte falls through with width 1; toString()\n // below then surfaces U+FFFD, consistent with Node's own UTF-8 handling\n // rather than us inventing a different fallback.\n if (i + width > buf.length) width = buf.length - i;\n const char = buf.toString('utf8', i, i + width);\n return { char, width };\n}\n\n/** UTF-8 continuation bytes (10xxxxxx) are not separate characters. Kept as\n * an export for callers that still classify byte kinds; no longer consumed\n * by this strategy's own position bookkeeping since only the absolute index\n * is tracked now. */\nexport function isUtf8ContinuationByte(b) {\n return (b & 0xc0) === 0x80;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { createByteScanStrategy, decodeCharAtUtf8 } from '../Encoding/ScanStrategy/ByteScanStrategy.js';\nimport { QUOTE_PAIRS_CAPACITY } from '../util.js';\n\nconst Constants = {\n space: 32,\n tab: 9,\n};\n\n// Zero-config default when no profile is supplied (e.g. tests/callers that\n// construct BufferSource directly rather than through XMLParser): UTF-8\n// byte-scan. Identical output/cost to the pre-encoding-feature code for pure\n// ASCII content (the common case) and correctly decodes multi-byte UTF-8,\n// which the old raw fromCharCode() path did not.\nconst DEFAULT_SCAN_STRATEGY = createByteScanStrategy(decodeCharAtUtf8, 'utf8');\n\n/**\n * BufferSource — input source backed by a Node.js Buffer (byte array).\n *\n * ### Memory reclamation\n *\n * The full document is available from the start, so there is no chunk-boundary\n * risk and rewindToMark() is a safe no-op. However, the parsed prefix of the\n * Buffer is held in memory until the parse finishes. flush() reclaims it by\n * slicing the Buffer and resetting startIndex to 0.\n *\n * The same mark/flush protocol used by FeedableSource is implemented here so\n * all reader functions work without source-type conditionals:\n *\n * markTokenStart() — save current read position at the start of a token\n * rewindToMark() — no-op for BufferSource (full doc always present)\n * flush() — drop the already-parsed prefix to free memory\n *\n * Auto-flush fires inside updateBufferBoundary() whenever the processed\n * portion exceeds flushThreshold and no token checkpoint is active.\n */\nexport default class BufferSource {\n /**\n * @param {Buffer} bytesArr — the full XML document as a Node.js Buffer\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed bytes\n */\n /**\n * @param {Buffer} bytesArr — the full XML document as a Node.js Buffer\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed bytes\n * @param {object} [profile] — resolved encoding profile from\n * Encoding/EncodingProfile.js#buildProfileForBuffer. Omit for the\n * zero-config UTF-8 default (used directly by tests/callers that don't\n * go through XMLParser).\n */\n constructor(bytesArr, options = {}, profile = null) {\n // BOM bytes (if any) are detection artifacts, not content — strip them\n // and exclude from the index.\n this.buffer = profile?.bomLength ? bytesArr.subarray(profile.bomLength) : bytesArr;\n if (profile?.decodeFirst) {\n // Not self-synchronizing (UTF-16, or a custom encoding that didn't\n // assert selfSynchronizing) — byte-level delimiter scanning is unsafe,\n // so decode the whole buffer once up front and hand off to\n // CharScanStrategy, which then behaves exactly like StringSource.\n const decoder = profile.descriptor.createDecoder();\n this.buffer = decoder.write(this.buffer) + decoder.end();\n }\n this.startIndex = 0;\n // Running total of bytes/chars trimmed off the front by flush() so far.\n // See StringSource.js's copy of this field / util.js#absolutePosition —\n // startIndex alone drifts from the true document offset after any flush.\n this._baseOffset = 0;\n\n this.autoFlush = options.autoFlush !== false;\n this.flushThreshold = options.flushThreshold ?? 1024;\n\n // Token-start checkpoint for mark/rewind (mirrors FeedableSource API).\n this._tokenStart = -1;\n\n // Reused across every scanTagExpEnd() call, never reallocated. See\n // StringSource.js's copy of this field for the full doc. Populated by\n // whichever ScanStrategy (byte or char) is assigned below.\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n\n // Resolve once, dispatch polymorphically from here on — no encoding\n // branching anywhere else in this class. See EncodingProfile.js.\n const strategy = profile?.scanStrategy ?? DEFAULT_SCAN_STRATEGY;\n Object.assign(this, strategy);\n this.encodingName = profile?.descriptor?.name ?? 'utf8';\n // See EncodingProfile.js's buildProfileForBuffer() doc — false only for\n // ByteScanStrategy+utf8, where a raw byte offset can land mid-character\n // once decoded. DEFAULT_SCAN_STRATEGY (used when no profile is supplied)\n // is itself utf8 byte-scan, so the no-profile default must also be false.\n this._quotePairsUsable = profile ? profile.quotePairsUsable !== false : false;\n }\n\n // ─── Token-start checkpoint ───────────────────────────────────────────────\n\n /**\n * Save the current read position as the start of a new logical token.\n *\n * For BufferSource this primarily guards flush() from reclaiming data that\n * is still being read, mirroring the same safety invariant as FeedableSource.\n */\n markTokenStart() {\n this._tokenStart = this.startIndex;\n }\n\n /**\n * Restore startIndex to the last markTokenStart() position.\n *\n * BufferSource always has the full document available, so a mid-token end\n * of input cannot occur and this method is a safe no-op. It exists solely\n * so caller code can call rewindToMark() unconditionally without branching\n * on source type.\n */\n rewindToMark() {\n // No-op: the complete document is in memory; no rewind is ever needed.\n }\n\n /**\n * Discard the already-processed prefix of the buffer to free memory.\n *\n * Uses Buffer.subarray() (zero-copy view) rather than Buffer.slice() for\n * clarity, then copies to a fresh Buffer so the original allocation can be\n * GC'd. If a token checkpoint is active, the flush origin is moved back to\n * the checkpoint so the in-progress token is preserved.\n */\n flush() {\n const origin = this._tokenStart >= 0 ? this._tokenStart : this.startIndex;\n if (origin > 0) {\n // Buffer.from(subarray) copies the bytes so the original large Buffer\n // can be released by the GC once no other references remain.\n this.buffer = Buffer.from(this.buffer.subarray(origin));\n if (this._tokenStart >= 0) {\n this.startIndex -= origin;\n this._tokenStart = 0;\n } else {\n this.startIndex = 0;\n }\n this._baseOffset += origin;\n }\n }\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\n\nconst DECL_PEEK_BYTES = 200; // more than enough for a <?xml ... ?> declaration\n\n/**\n * sniff(bytes, registry) -> { encoding, bomLength, declaredEncoding }\n *\n * Pure function — no state, easy to unit test standalone. Only needs the\n * leading bytes of the document (caller decides how many it has available).\n *\n * Algorithm (XML 1.0 Appendix F, practical subset):\n * 1. Check for a known BOM signature.\n * 2. ASCII-sniff far enough to find `encoding=\"...\"` inside a leading\n * `<?xml ... ?>` declaration (the declaration's own bytes are ASCII-\n * stable across UTF-8/ASCII/Latin-1/most single-byte sets, which is\n * exactly why this step doesn't need to already know the encoding).\n * 3. BOM present + declared encoding present + they disagree -> hard error.\n * 4. Neither found -> default to utf8 (spec default).\n */\nexport function sniff(bytes, registry) {\n const bomMatch = matchBom(bytes, registry);\n const declaredEncoding = sniffDeclaration(bytes, bomMatch ? bomMatch.bomLength : 0);\n\n if (bomMatch && declaredEncoding && !sameEncoding(bomMatch.descriptor.name, declaredEncoding, registry)) {\n throw new ParseError(\n `Byte-order mark indicates \"${bomMatch.descriptor.name}\" but the XML declaration says encoding=\"${declaredEncoding}\"`,\n ErrorCode.ENCODING_MISMATCH\n );\n }\n\n if (bomMatch) {\n return { encoding: bomMatch.descriptor.name, bomLength: bomMatch.bomLength, declaredEncoding };\n }\n if (declaredEncoding) {\n return { encoding: declaredEncoding, bomLength: 0, declaredEncoding };\n }\n return { encoding: 'utf8', bomLength: 0, declaredEncoding: null };\n}\n\nfunction matchBom(bytes, registry) {\n for (const descriptor of registry.bomCandidates()) {\n const sig = descriptor.bomBytes;\n if (bytes.length >= sig.length && sig.equals(bytes.subarray(0, sig.length))) {\n return { descriptor, bomLength: sig.length };\n }\n }\n return null;\n}\n\nfunction sniffDeclaration(bytes, offset) {\n // ASCII-decode a bounded prefix; non-ASCII-safe encodings (UTF-16 etc.) are\n // already handled via BOM before we'd ever reach here without one.\n const prefix = bytes.subarray(offset, Math.min(bytes.length, offset + DECL_PEEK_BYTES)).toString('latin1');\n const declMatch = prefix.match(/^\\s*<\\?xml\\s+[^?]*\\?>/);\n if (!declMatch) return null;\n const encMatch = declMatch[0].match(/encoding\\s*=\\s*[\"']([^\"']+)[\"']/i);\n return encMatch ? encMatch[1].toLowerCase() : null;\n}\n\nfunction sameEncoding(a, b, registry) {\n try {\n return registry.resolve(a).name === registry.resolve(b).name;\n } catch {\n return false;\n }\n}\n","/**\n * TextDecoderAdapter — gives a { write(buf): string, end(): string } shaped\n * stateful decoder, same contract every EncodingRegistry descriptor already\n * promises, but built on the global TextDecoder instead of Node's\n * node:string_decoder.\n *\n * Why this exists: node:string_decoder is Node-only. Bundling it for a\n * browser target fails outright (no browser polyfill is registered by\n * default). TextDecoder is available natively in every modern browser *and*\n * in Node (global since Node 11), and it already supports incremental,\n * chunk-safe decoding via { stream: true } — a multi-byte character split\n * across two write() calls is buffered internally and stitched together on\n * the next call, same safety guarantee StringDecoder gave.\n *\n * Scope: only replaces the five *built-in* encodings (utf8, ascii, latin1,\n * utf16le, utf16be). Fully custom Node-only decoders (e.g. iconv-lite based\n * Shift_JIS) registered via `decoding.customDecoders` are untouched — they\n * keep supplying their own createDecoder() and are expected to only run\n * under Node, same as before.\n */\n\n/**\n * @param {string} label - a valid TextDecoder label ('utf-8', 'utf-16le', 'windows-1252', ...)\n * @returns {{ write(buf: Uint8Array): string, end(): string }}\n */\nexport function createTextDecoderAdapter(label) {\n const decoder = new TextDecoder(label, { fatal: false, ignoreBOM: true });\n return {\n write(buf) {\n // stream:true holds back a trailing partial multi-byte sequence\n // instead of emitting U+FFFD for it, and prepends it on the next call.\n return decoder.decode(buf, { stream: true });\n },\n end() {\n // Final call, no more bytes coming — flush anything held back.\n // A non-empty result here means genuinely truncated input; TextDecoder\n // substitutes U+FFFD for it, matching StringDecoder's prior behavior.\n return decoder.decode();\n },\n };\n}\n\n/**\n * utf16be has no native TextDecoder label. Same approach as the previous\n * Node-based implementation: byte-swap to little-endian, then decode as\n * utf-16le. Kept here (rather than EncodingRegistry) since it's just another\n * flavor of \"adapter around a decoder\".\n */\nexport function createUtf16BeAdapter() {\n const inner = createTextDecoderAdapter('utf-16le');\n let pending = null; // holds a single odd leftover byte across writes\n\n return {\n write(buf) {\n let work = pending ? concatBytes(pending, buf) : buf;\n pending = null;\n if (work.length % 2 === 1) {\n pending = work.subarray(work.length - 1);\n work = work.subarray(0, work.length - 1);\n }\n const swapped = swapBytePairs(work);\n return inner.write(swapped);\n },\n end() {\n return inner.end();\n },\n };\n}\n\nfunction concatBytes(a, b) {\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n}\n\nfunction swapBytePairs(bytes) {\n const swapped = new Uint8Array(bytes);\n for (let i = 0; i + 1 < swapped.length; i += 2) {\n const tmp = swapped[i];\n swapped[i] = swapped[i + 1];\n swapped[i + 1] = tmp;\n }\n return swapped;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { createTextDecoderAdapter, createUtf16BeAdapter } from './TextDecoderAdapter.js';\n\n/**\n * EncodingRegistry — owns the set of known EncodingDescriptors.\n *\n * A descriptor is a pure data + factory bundle:\n * {\n * name, aliases,\n * bomBytes // Buffer | null — signature to detect this encoding via BOM\n * selfSynchronizing // boolean — true if an ASCII delimiter byte (<,>,\",')\n * // can never appear as part of a multi-byte sequence.\n * // Only self-synchronizing encodings are eligible for\n * // BufferSource's byte-level scan fast path.\n * variableWidth // boolean — true if bytes-per-character varies.\n * // Informational only for now (position reporting\n * // is index-only, so nothing currently branches\n * // on this) — kept on the descriptor shape so\n * // existing custom-encoding registrations don't\n * // need to change.\n * createDecoder() // -> { write(buf): string, end(): string } stateful decoder\n * }\n *\n * SRP: this class only stores/validates/resolves descriptors. It knows nothing\n * about scanning strategy — see ScanStrategy/, composed together in\n * EncodingProfile.js.\n */\nexport default class EncodingRegistry {\n constructor() {\n this._byName = new Map();\n this._seedDefaults();\n }\n\n _seedDefaults() {\n this.register({\n name: 'utf8',\n aliases: ['utf-8'],\n bomBytes: Buffer.from([0xef, 0xbb, 0xbf]),\n selfSynchronizing: true,\n variableWidth: true,\n createDecoder: () => createTextDecoderAdapter('utf-8'),\n });\n this.register({\n name: 'ascii',\n aliases: [],\n bomBytes: null,\n selfSynchronizing: true,\n variableWidth: false,\n // TextDecoder has no dedicated 'ascii' label. windows-1252 is a strict\n // superset of ASCII and decodes any valid ASCII byte identically —\n // only bytes 0x80-0x9F (never legal ASCII) would differ, so behavior\n // for real ASCII input is unchanged.\n createDecoder: () => createTextDecoderAdapter('windows-1252'),\n });\n this.register({\n name: 'latin1',\n aliases: ['iso-8859-1', 'binary'],\n bomBytes: null,\n selfSynchronizing: true,\n variableWidth: false,\n createDecoder: () => createTextDecoderAdapter('iso-8859-1'),\n });\n this.register({\n name: 'utf16le',\n aliases: ['utf-16le', 'ucs2', 'ucs-2'],\n bomBytes: Buffer.from([0xff, 0xfe]),\n selfSynchronizing: false,\n variableWidth: true,\n createDecoder: () => createTextDecoderAdapter('utf-16le'),\n });\n this.register({\n name: 'utf16be',\n aliases: ['utf-16be'],\n // No native TextDecoder label for utf16be either; byte-swap then\n // decode as utf16le, same trick as before.\n bomBytes: Buffer.from([0xfe, 0xff]),\n selfSynchronizing: false,\n variableWidth: true,\n createDecoder: () => createUtf16BeAdapter(),\n });\n }\n\n /**\n * Register a descriptor. Validates shape immediately (fail-fast), same\n * spirit as ValueParserRegistry.register() in base-output-builder: a\n * broken custom encoding should throw at registration time, not silently\n * corrupt data three parses later.\n */\n register(descriptor) {\n if (!descriptor || typeof descriptor.name !== 'string' || !descriptor.name) {\n throw new ParseError('Encoding descriptor requires a non-empty \"name\"', ErrorCode.INVALID_DECODER);\n }\n if (typeof descriptor.createDecoder !== 'function') {\n throw new ParseError(`Encoding \"${descriptor.name}\" is missing createDecoder()`, ErrorCode.INVALID_DECODER);\n }\n const probe = descriptor.createDecoder();\n if (!probe || typeof probe.write !== 'function' || typeof probe.end !== 'function') {\n throw new ParseError(\n `Encoding \"${descriptor.name}\"'s createDecoder() must return an object with write()/end()`,\n ErrorCode.INVALID_DECODER\n );\n }\n const resolved = {\n selfSynchronizing: false, // safe default per savepoint §3 — opt-in speed, not opt-in correctness\n variableWidth: true,\n aliases: [],\n bomBytes: null,\n ...descriptor,\n };\n this._byName.set(resolved.name.toLowerCase(), resolved);\n for (const alias of resolved.aliases) this._byName.set(alias.toLowerCase(), resolved);\n }\n\n resolve(name) {\n const descriptor = this._byName.get(String(name).toLowerCase());\n if (!descriptor) {\n throw new ParseError(`Unsupported encoding \"${name}\"`, ErrorCode.UNSUPPORTED_ENCODING);\n }\n return descriptor;\n }\n\n /** All descriptors that carry a BOM signature, for detection (longest first). */\n bomCandidates() {\n const seen = new Set();\n const out = [];\n for (const d of this._byName.values()) {\n if (d.bomBytes && !seen.has(d.name)) {\n seen.add(d.name);\n out.push(d);\n }\n }\n return out.sort((a, b) => b.bomBytes.length - a.bomBytes.length);\n }\n}\n\nexport const defaultEncodingRegistry = new EncodingRegistry();\n","'use strict';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { isSpaceCode, errorPositionOf } from \"./util.js\"\n\n/**\n * AttributeProcessor — owns all attribute parsing logic.\n *\n * Two-pass attribute processing:\n *\n * Pass 1 — collectRawAttributes()\n * Populates the rawAttributes map from the raw attribute expression string.\n * Called inside buildTagExpObj() (via XmlPartReader) so rawAttributes is\n * ready before readOpeningTag() calls matcher.updateCurrent(rawAttributes).\n * The matcher must reflect all raw attribute values before any value-parser\n * runs so that attribute-based path expressions (e.g. \"div[class=code]\")\n * resolve correctly during pass 2.\n *\n * Pass 2 — flushAttributes()\n * Calls outputBuilder.addAttribute() for each attribute, running the full\n * value-parser chain. Called from readOpeningTag() AFTER\n * matcher.updateCurrent(), so the read-only matcher already carries the\n * complete attribute context when value parsers execute.\n */\n\n// Module-level regex kept for reference only — no longer called from this\n// module. parseAttributes() below replaces it with an O(n) linear scanner\n// that is immune to catastrophic backtracking and stack overflow.\n// const attrsRegx = new RegExp('([^\\\\s=]+)\\\\s*(=\\\\s*([\\'\"])([\\\\s\\\\S]*?)\\\\3)?', 'gm');\n\n/**\n * Parse an attribute expression string into an array of match tuples.\n *\n * Each element is `{ name, value, startIndex }` — `value` is `undefined` for\n * a boolean attribute (no `=`). Earlier versions of this function also built\n * a full-match string and an `'=value'` string per attribute (matching an\n * old regex-based getAllMatches() return shape) — neither was ever read by\n * collectRawAttributes()/flushAttributes() (only `name`, `value`, and\n * `.startIndex` are), so building them was pure wasted string concatenation\n * on every attribute, on every tag. Dropped.\n *\n * The implementation is a single O(n) pass over char codes with no regex and\n * no recursion, making it safe for arbitrarily long attribute strings.\n *\n * State machine:\n * SEEK_NAME — skipping whitespace looking for the start of an attr name\n * IN_NAME — accumulating a name token until whitespace or '='\n * SEEK_VALUE — saw name + optional whitespace, now expecting '=' or next name\n * IN_VALUE — inside a quoted value, accumulating until the closing quote\n *\n * @param {string} attrStr\n * @param {Array<number>} [quotePairs] - flat [openIdx, closeIdx, ...] list from\n * scanTagExpEnd(), offsets relative to the *tag expression* (not attrStr).\n * When a value's opening quote lines up with the next expected pair, the\n * closing quote's position is taken directly instead of re-scanning for\n * it — the per-character `!== quote` comparison is skipped entirely for\n * that value. Falls back to the old per-character scan (undefined, or a\n * mismatch — belt-and-braces, should never trigger given how the pairs\n * are produced, but costs nothing to check once per value) so correctness\n * never depends on the fast path succeeding.\n * @param {number} [attrsOffset] - offset of attrStr's first character within\n * the coordinate system quotePairs is expressed in — required whenever\n * quotePairs is passed.\n * @param {number} [quotePairsLen=0] - how many entries in `quotePairs` are\n * valid — it's a reused fixed-capacity typed array, not sized to this tag,\n * so `quotePairs.length` itself is not the right bound to loop against.\n * @returns {Array<{name: string, value: string|undefined, startIndex: number}>}\n */\n/**\n * True for character codes illegal as literal content anywhere in the\n * document (mirrors util.js's isIllegalControlCode — not imported directly\n * to keep this hot loop free of a cross-module call for a one-line check).\n */\nfunction isIllegalAttrCode(c) {\n return c <= 8 || c === 11 || c === 12 || (c >= 14 && c <= 31);\n}\n\n/**\n * Scan one quoted attribute value's characters from `i` up to `end`\n * (exclusive), folding whitespace per XML §3.3.3 and rejecting illegal\n * control characters. Shared by both the fast path (closing quote position\n * already known from scanTagExpEnd's quote pairs) and the slow path (closing\n * quote found by scanning for `quote`) below — same rules either way.\n *\n * - real `\\r\\n` pair → exactly one space (not two)\n * - lone `\\r` or lone `\\n` → one space\n * - literal tab (0x09) → one space (a `	` reference is left\n * untouched — that's resolved later, by\n * the entity value-parser, not here)\n * - other illegal control code → throws ILLEGAL_CHARACTER\n *\n * @returns {string} the folded value (does not include the closing quote)\n */\nfunction scanAttrValue(attrStr, i, end, parser) {\n let value = '';\n let segStart = i;\n for (; i < end; i++) {\n const c = attrStr.charCodeAt(i);\n if (c === 13) { // \\r — possibly paired with a following \\n\n value += attrStr.substring(segStart, i) + ' ';\n if (attrStr.charCodeAt(i + 1) === 10) i++;\n segStart = i + 1;\n } else if (c === 10 || c === 9) { // lone \\n, or literal tab\n value += attrStr.substring(segStart, i) + ' ';\n segStart = i + 1;\n } else if (isIllegalAttrCode(c)) {\n throw new ParseError(\n `Illegal control character 0x${c.toString(16).padStart(2, '0')} in attribute value`,\n ErrorCode.ILLEGAL_CHARACTER,\n parser ? errorPositionOf(parser.source) : {}\n );\n }\n }\n value += attrStr.substring(segStart, i);\n return value;\n}\n\nfunction parseAttributes(attrStr, quotePairs, attrsOffset, quotePairsLen = 0, parser) {\n const results = [];\n const len = attrStr.length;\n let i = 0;\n const usePairs = quotePairs !== undefined && quotePairsLen > 0;\n let pairIdx = 0;\n\n while (i < len) {\n // Skip whitespace between attributes\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n if (i >= len) break;\n\n // Read name\n const nameStart = i;\n while (i < len && attrStr.charCodeAt(i) !== 61 && !isSpaceCode(attrStr.charCodeAt(i))) i++;\n const name = attrStr.substring(nameStart, i);\n\n // Skip whitespace before '='\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n\n if (i >= len || attrStr.charCodeAt(i) !== 61) {\n // Boolean attribute — no '='\n results.push({ name, value: undefined, startIndex: nameStart });\n continue;\n }\n\n i++; // skip '='\n\n // Skip whitespace after '='\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n\n // The character right after '=' (mod whitespace) MUST be a quote — this\n // is never relaxed, in any mode. Reject before consuming anything, so an\n // unquoted value never partially reaches the output builder.\n const quote = attrStr.charCodeAt(i); // NaN when i >= len — also fails both checks below\n if (quote !== 34 && quote !== 39) { // not \" or '\n throw new ParseError(\n `Attribute '${name}' has an unquoted value — attribute values must be wrapped in '\"' or \"'\"`,\n ErrorCode.UNQUOTED_ATTRIBUTE_VALUE,\n parser ? errorPositionOf(parser.source) : {}\n );\n }\n\n // Fast path: the tag-end scanner already found this exact quote pair\n // while looking for '>' — reuse its closing-quote position instead of\n // re-scanning character-by-character for it.\n let closeLocal = -1;\n if (usePairs && pairIdx + 1 < quotePairsLen && quotePairs[pairIdx] === i + attrsOffset) {\n closeLocal = quotePairs[pairIdx + 1] - attrsOffset;\n pairIdx += 2;\n }\n\n i++; // skip opening quote\n let end;\n if (closeLocal >= 0) {\n end = closeLocal;\n } else {\n end = i;\n while (end < len && attrStr.charCodeAt(end) !== quote) end++;\n }\n const value = scanAttrValue(attrStr, i, end, parser);\n i = end + 1; // skip closing quote\n results.push({ name, value, startIndex: nameStart });\n }\n\n return results;\n}\n\n/**\n * Pass 1: extract raw (unparsed) attribute values into rawAttributes, AND\n * build tagExp._parsedAttrs — the processed-name/value list pass 2 will\n * consume directly.\n *\n * Previously, pass 2 (flushAttributes) re-ran parseAttributes() from scratch\n * on the same attrStr, and re-ran parser.processAttrName() (ns-prefix\n * resolution + name validation + sanitizeName + reserved-name check) on\n * every attribute a second time — full re-tokenization plus full re-validation\n * of work already done here. processAttrName() is a pure function of\n * (rawName, options) — nothing between pass 1 and pass 2 (matcher.push,\n * stop/skip resolution) can change its result — so it's safe to compute once\n * and cache. The matcher still gets the *raw* (pre-resolveNsPrefix/sanitize)\n * name as its rawAttributes key, unchanged, since PEM's attribute-condition\n * matching (`div[class=code]`) matches against attribute names as written.\n *\n * @param {string} attrStr - raw attribute expression substring\n * @param {object} parser - Xml2JsParser instance (for processAttrName)\n * @param {object} tagExp - tagExp object to populate rawAttributes\n * @param {Array<number>} [quotePairs] - see parseAttributes() doc.\n * @param {number} [attrsOffset] - see parseAttributes() doc.\n * @param {number} [quotePairsLen] - see parseAttributes() doc.\n */\nexport function collectRawAttributes(attrStr, parser, tagExp, quotePairs, attrsOffset, quotePairsLen) {\n if (!attrStr || attrStr.length === 0) return;\n\n const matches = parseAttributes(attrStr, quotePairs, attrsOffset, quotePairsLen, parser);\n const len = matches.length;\n tagExp._rawAttrMatchCount = len; // total parsed attrs, incl. dropped (xmlns:) ones — for maxAttributesPerTag parity with old behavior\n const parsedAttrs = [];\n let count = 0;\n\n // attributes.duplicate: 'overwrite' (default) needs no bookkeeping — last\n // occurrence naturally wins via the rawAttributes object-assign + the\n // builder's own last-write-wins addAttribute() calls below, exactly as\n // before this option existed. 'ignore'/'throw' need to track names seen\n // so far on *this* tag only — a fresh Set per call, never shared across tags.\n const dupMode = parser.options.attributes?.duplicate || 'overwrite';\n const seen = dupMode !== 'overwrite' ? new Set() : null;\n const boolMode = parser.options.attributes?.booleanType || 'allow';\n\n for (let i = 0; i < len; i++) {\n const m = matches[i];\n\n if (seen) {\n if (seen.has(m.name)) {\n if (dupMode === 'throw') {\n throw new ParseError(\n `Duplicate attribute '${m.name}'`,\n ErrorCode.DUPLICATE_ATTRIBUTE,\n errorPositionOf(parser.source)\n );\n }\n continue; // 'ignore' — first occurrence wins, later ones dropped entirely\n }\n seen.add(m.name);\n }\n\n const rawVal = m.value;\n if (rawVal === undefined) {\n if (boolMode === 'throw') {\n throw new ParseError(\n `Valueless attribute '${m.name}' is not allowed`,\n ErrorCode.BOOLEAN_ATTRIBUTE_REJECTED,\n errorPositionOf(parser.source)\n );\n }\n if (boolMode === 'ignore') continue; // drop silently, rest of tag unaffected\n }\n\n const attrName = parser.processAttrName(m.name);\n if (attrName === false) continue;\n count++;\n const attrVal = rawVal !== undefined ? rawVal : true;\n tagExp.rawAttributes[m.name] = attrVal;\n parsedAttrs.push({ name: attrName, value: attrVal, index: m.startIndex });\n }\n tagExp.rawAttributesLen = count;\n tagExp._parsedAttrs = parsedAttrs;\n}\n\n/**\n * Pass 2: push each attribute (already parsed + name-processed by pass 1,\n * see tagExp._parsedAttrs) to the output builder. No re-parsing, no\n * re-running processAttrName — this is now a plain loop over cached data.\n *\n * @param {Array<{name: string, value: *, index: number}>} parsedAttrs - tagExp._parsedAttrs from collectRawAttributes\n * @param {object} parser - Xml2JsParser instance\n * @param {number} [attrsExpStart] - absolute document offset where the\n * attribute expression began (tagExp._attrsExpStart). When provided, each\n * attribute's absolute document index is computed and passed to\n * addAttribute() as a 4th argument: { index }. Line/col are intentionally\n * NOT computed here — doing so would require re-scanning attrStr for\n * newlines on every call, for a field most builders won't use; callers\n * that need it can derive line/col from `index` plus the document text.\n * @param {number} rawAttrMatchCount - tagExp._rawAttrMatchCount, used for the\n * maxAttributesPerTag limit check (counts all parsed attrs, including any\n * dropped by processAttrName, matching the limit's pre-existing semantics).\n */\nexport function flushAttributes(parsedAttrs, parser, attrsExpStart, rawAttrMatchCount) {\n if (!parsedAttrs || parsedAttrs.length === 0) return;\n\n const maxAttrs = parser.options.limits?.maxAttributesPerTag;\n if (maxAttrs !== undefined && maxAttrs !== null && rawAttrMatchCount > maxAttrs) {\n const tagName = parser.currentTagDetail?.name ?? '(unknown)';\n throw new ParseError(\n `Tag '${tagName}' has ${rawAttrMatchCount} attributes, exceeding limit of ${maxAttrs}`,\n ErrorCode.LIMIT_MAX_ATTRIBUTES,\n errorPositionOf(parser.source)\n );\n }\n\n const len = parsedAttrs.length;\n for (let i = 0; i < len; i++) {\n const a = parsedAttrs[i];\n const attrMeta = attrsExpStart !== undefined\n ? { index: attrsExpStart + a.index }\n : undefined;\n parser.outputBuilder.addAttribute(a.name, a.value, parser.readonlyMatcher, attrMeta);\n }\n}","'use strict';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { collectRawAttributes } from './AttributeProcessor.js';\nimport { isSpace, absolutePosition } from \"./util.js\"\n// Re-export flushAttributes so Xml2JsParser and XmlSpecialTagsReader can\n// continue to import it from here without changing their import lines.\nexport { flushAttributes } from './AttributeProcessor.js';\n\nexport class TagExp {\n constructor() {\n this.tagName = \"\";\n this.selfClosing = false;\n // rawAttributes= Object.create(null),\n this.rawAttributes = {};\n this._attrsExp = \"\" // stored for two-pass attribute flushing in readOpeningTag\n this._attrsExpStart = undefined; // absolute document offset of _attrsExp's first char\n this._rawAttrMatchCount = 0;\n this._parsedAttrs = [];\n }\n}\n\n/**\n * Try to match an upcoming closing tag against the name we already expect\n * (the tag sitting on top of the stack) without reading it into a string\n * first. Peeks character-by-character (no consumption) — a mismatch, or\n * running out of buffered data, costs nothing to undo since nothing was\n * consumed. Caller falls back to the normal read+validate+compare path in\n * either case, so this never needs its own error handling or chunk-boundary\n * logic.\n *\n * On success, only whitespace is allowed between the name and '>' — matches\n * XML's own grammar for ETag (`</tag ... >`, no attributes permitted).\n *\n * @param {Source} source\n * @param {string} expectedRawName - the raw (pre namespace-stripped) name\n * the currently-open tag was written with\n * @returns {number} characters to consume (name + whitespace + '>'), or -1\n * if this isn't a match (or not enough data yet to tell)\n */\nexport function tryMatchClosingTagName(source, expectedRawName) {\n // false (mismatch) and null (not enough buffered data yet) both fall back\n // to the same slow path below, so both collapse to -1 here.\n if (source.matchAhead(expectedRawName) !== true) return -1;\n let i = expectedRawName.length;\n let c = source.readChAt(i);\n if (c === '>') return i + 1;\n while (isSpace(c)) {\n i++;\n c = source.readChAt(i);\n if (c === '>') return i + 1;\n }\n if (c !== '>') return -1;\n return i + 1;\n}\n\n/**\n * Read closing tag name.\n *\n * Uses level-1 (inner) mark so flush() knows the safe trim boundary while\n * this reader is in progress. Does NOT overwrite the level-0 outer mark set\n * by parseXml()'s loop, which rewindToMark() always restores to.\n *\n * @param {Source} source\n * @returns {string} tag name\n */\nexport function readClosingTagName(source) {\n source.markTokenStart(1);\n // Closing tags never carry attributes, so unlike an opening tag's\n // expression there is no quoting to worry about — the very first '>' is\n // always the real end. That means the whole name can be found with one\n // direct scan of whatever is already buffered (readUptoChar), instead of\n // asking \"is there more data yet?\" before every single character.\n const start = source.startIndex;\n try {\n const str = source.readUptoChar(\">\");\n return str.trimEnd();\n } catch (err) {\n // Buffer ran out before '>' showed up — the retryable chunk-boundary\n // case (readUptoChar didn't consume anything on failure). Re-throw with\n // whatever was buffered so far in the message so autoClose's truncation\n // recovery (which reads it back out of the message) can still report a\n // useful partial tag name.\n const partial = source.readStr(Number.MAX_SAFE_INTEGER, start);\n throw new ParseError(`Unexpected end of source reading closing tag '</${partial}'`, ErrorCode.UNEXPECTED_END);\n }\n}\n\n/**\n * Read an XML opening tag expression and return a tag descriptor.\n *\n * Handles normal tags — not comments, CDATA, or DOCTYPE.\n * Example input (from source, after '<'): `tag attr='some\"' attr2=\">\" bool>`\n *\n * Uses level-1 (inner) mark — see readClosingTagName for rationale.\n *\n * @param {object} parser - Xml2JsParser instance\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp }}\n */\nexport function readTagExp(parser) {\n parser.source.markTokenStart(1);\n // Absolute document offset where `exp` (tag name onward, right after '<')\n // begins — captured before any reads so buildTagExpObj can compute each\n // attribute's absolute document position from its offset within attrsExp.\n const expStart = absolutePosition(parser.source);\n\n // Pick the scan variant once — scanTagExpEnd records quote positions for\n // AttributeProcessor to reuse; scanTagExpEndFast skips that bookkeeping\n // entirely when attributes are being skipped (nobody reads _quotePairs).\n // No flag is passed into the loop; the decision is made here, once.\n const collectQuotes = !parser.options.skip.attributes;\n const relEnd = collectQuotes\n ? parser.source.scanTagExpEnd()\n : parser.source.scanTagExpEndFast();\n\n if (relEnd === -1) {\n // Buffer exhausted before an unquoted '>' was found — chunk boundary\n // mid-tag. Throw UNEXPECTED_END so feed()/parseStream() rewinds to the\n // level-0 outer mark and retries. (Note: scanTagExpEnd() only returns a\n // non-negative index once both quote flags are already balanced-closed —\n // by construction, not by a separate post-scan check — so there is no\n // longer a distinct \"unclosed quote but '>' was found\" case to detect;\n // the old UNCLOSED_QUOTE branch here was checking the same two flags\n // immediately after the only code path that requires them both false,\n // making it permanently unreachable.)\n throw new ParseError(\"Unexpected closing of source waiting for '>'\", ErrorCode.UNEXPECTED_END);\n }\n\n const exp = parser.source.readStr(relEnd);\n parser.source.updateBufferBoundary(relEnd + 1);\n\n // Quote positions the scan above already found while looking for '>' —\n // handed to AttributeProcessor so it doesn't re-scan for quotes itself.\n // Only usable when this source's offsets are guaranteed to line up 1:1\n // with characters in the decoded `exp` string — false for BufferSource\n // reading UTF-8, where a byte offset can land mid-character (see\n // BufferSource._quotePairsUsable doc). Consumed synchronously by\n // buildTagExpObj below, before any other tag scan can touch the array\n // again, so no staleness risk. `_quotePairs` is a fixed-capacity reusable\n // typed array — `_quotePairsLen` says how many of its slots are valid for\n // this tag (not `_quotePairs.length`, which is always the full capacity).\n const usableQuotes = collectQuotes && parser.source._quotePairsUsable !== false;\n const quotePairs = usableQuotes ? parser.source._quotePairs : undefined;\n const quotePairsLen = usableQuotes ? parser.source._quotePairsLen : 0;\n\n return buildTagExpObj(exp, parser, expStart, false, quotePairs, quotePairsLen);\n}\n\n/**\n * Read a processing-instruction tag expression (<?name attrs?>).\n *\n * Uses level-1 (inner) mark — see readClosingTagName for rationale.\n *\n * @param {object} parser\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp }}\n */\nexport function readPiExp(parser) {\n parser.source.markTokenStart(1);\n const expStart = absolutePosition(parser.source);\n let inSingleQuotes = false;\n let inDoubleQuotes = false;\n let i;\n let EOE = false;\n\n for (i = 0; parser.source.canRead(i); i++) {\n const currentChar = parser.source.readChAt(i);\n const nextChar = parser.source.readChAt(i + 1);\n\n if (currentChar === \"'\" && !inDoubleQuotes) {\n inSingleQuotes = !inSingleQuotes;\n } else if (currentChar === '\"' && !inSingleQuotes) {\n inDoubleQuotes = !inDoubleQuotes;\n }\n\n if (!inSingleQuotes && !inDoubleQuotes) {\n if (currentChar === '?' && nextChar === '>') {\n EOE = true;\n break;\n }\n }\n }\n\n if (!EOE) {\n // Buffer exhausted before '?>' — chunk boundary mid-PI-tag.\n throw new ParseError(\"Unexpected closing of source waiting for '?>'\", ErrorCode.UNEXPECTED_END);\n } else if (inSingleQuotes || inDoubleQuotes) {\n // '?>' found but a quote was never closed — real syntax error.\n throw new ParseError(\"Invalid attribute expression. Quote is not properly closed in PI tag expression\", ErrorCode.UNCLOSED_QUOTE);\n }\n\n // if (!parser.options.skip.attributes) {\n // //TODO: use regex to verify attributes if not set to ignore\n // }\n\n const exp = parser.source.readStr(i);\n parser.source.updateBufferBoundary(i + 2);\n return buildTagExpObj(exp, parser, expStart, true);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Parse a raw tag expression string into a structured tag descriptor.\n *\n * @param {string} exp - everything between '<' and '>' (exclusive)\n * @param {object} parser\n * @param {number} [expStart] - absolute document offset where `exp` begins\n * (i.e. right after '<' or '<?'). Used to compute each attribute's absolute\n * document position (tagExp._attrsExpStart) for addAttribute()'s meta arg.\n * Optional so callers that don't have/need it can omit it — attribute\n * position metadata is simply unavailable in that case, not an error.\n * @param {Array<number>} [quotePairs] - flat [openIdx, closeIdx, ...] list\n * from scanTagExpEnd(), offsets relative to `exp`'s start. Passed through\n * to collectRawAttributes so parseAttributes() can skip re-scanning for\n * quotes. Undefined when unavailable/unsafe for this source or call site\n * (readPiExp never supplies one) — collectRawAttributes falls back to its\n * own scan in that case.\n * @param {number} [quotePairsLen=0] - how many entries in `quotePairs` are\n * valid (it's a reused fixed-capacity array, not sized to this tag).\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp, _attrsExpStart }}\n */\nfunction buildTagExpObj(exp, parser, expStart, forceToReadAttrs = false, quotePairs = undefined, quotePairsLen = 0) {\n const tagExp = new TagExp();\n\n const expLen = exp.length;\n\n if (exp[expLen - 1] === \"/\") {\n tagExp.selfClosing = true;\n exp = exp.slice(0, -1); // Remove the trailing slash\n }\n\n // Separate tag name from attribute expression\n let attrsExp = \"\";\n let i = 0;\n let attrsLocalOffset; // exp-relative offset where attrsExp begins — rebases quotePairs\n\n for (; i < exp.length; i++) {\n const c = exp[i];\n if (isSpace(c)) {\n tagExp.tagName = exp.substring(0, i);\n attrsExp = exp.substring(i + 1);\n attrsLocalOffset = i + 1;\n if (expStart !== undefined) tagExp._attrsExpStart = expStart + i + 1;\n break;\n }\n }\n //only tag\n if (tagExp.tagName.length === 0 && i === exp.length) tagExp.tagName = exp;\n tagExp.tagName = tagExp.tagName.trimEnd();\n tagExp._attrsExp = attrsExp;\n\n if (!parser.isValidQName(tagExp.tagName)) {\n throw new ParseError(\"Invalid tag name\", ErrorCode.INVALID_TAG_NAME);\n }\n\n // Pass 1: collect raw attribute values for matcher.updateCurrent().\n // Pass 2 (flushAttributes) runs later in readOpeningTag, after updateCurrent()\n // — and is still gated by skip.attributes there, so output/matcher visibility\n // of attribute values is unaffected by the change below.\n //\n // This pass itself, however, always runs whenever an attribute expression is\n // present — even when skip.attributes is true. Malformed attribute syntax\n // (unquoted values, illegal duplicates, illegal control characters) is a\n // document-validity problem, not an output-shaping one, so skip.attributes\n // must not be able to silently let broken markup through.\n if (forceToReadAttrs || attrsExp.length > 0) {\n collectRawAttributes(attrsExp, parser, tagExp, quotePairs, attrsLocalOffset, quotePairsLen);\n }\n // console.log(tagExp)\n return tagExp;\n}\n\n","import { ParseError, ErrorCode } from './ParseError.js';\nimport { isSpace, ensureCanRead, absolutePosition } from './util.js';\n\n/**\n * Well-known enclosure presets.\n *\n * Import these in your parser config to compose skipEnclosures arrays:\n *\n * import { xmlEnclosures, quoteEnclosures } from '@nodable/flexible-xml-parser';\n *\n * stopNodes: [\n * \"..script\", // plain — no enclosures (default)\n * { expression: \"body..pre\", skipEnclosures: [...xmlEnclosures] },\n * { expression: \"head..style\", skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] },\n * { expression: \"root.stopNode\", nested: true, skipEnclosures: [{ open: '<!--', close: '-->' }] },\n * ]\n */\n\n/** XML structural delimiters — comments, CDATA, processing instructions. */\nexport const xmlEnclosures = [\n { open: '<!--', close: '-->' }, // comment\n { open: '<![CDATA[', close: ']]>' }, // CDATA section\n { open: '<?', close: '?>' }, // processing instruction\n];\n\n/** String literal delimiters — useful for JS / CSS stop-node content. */\nexport const quoteEnclosures = [\n { open: \"'\", close: \"'\" },\n { open: '\"', close: '\"' },\n { open: '`', close: '`' }, // template literal\n];\n\n/**\n * StopNodeProcessor — self-contained processor for stop nodes.\n *\n * A stop node is a \"sealed envelope\": the parser goes blind the moment it\n * enters one, collecting raw characters until the matching closing tag is\n * found. The content is returned as a raw string and never parsed by the\n * XML engine.\n *\n * ### Modes\n *\n * The behaviour is controlled by two independent flags:\n *\n * **`nested`** (boolean, default false):\n * When true, the processor tracks the depth of nested same-name opening\n * tags. The stop node ends only when the depth returns to zero — i.e. the\n * closing tag that matches the original opening tag. When false (default),\n * the very first `</tagName>` ends the stop node regardless of nesting.\n *\n * **`skipEnclosures`** (array, default []):\n * A list of `{ open: string, close: string }` pairs. When the processor\n * encounters an open marker it consumes everything up to the close marker\n * wholesale, suppressing all closing-tag (and depth) logic for that span.\n * Enclosures are checked in array order; the first match wins. When the\n * array is empty, no enclosure skipping is performed.\n *\n * The two flags compose freely:\n *\n * | nested | skipEnclosures | Behaviour |\n * |--------|----------------|------------------------------------------------------------------------|\n * | false | [] | Plain: stop at first `</tagName>`. |\n * | true | [] | Depth-only: track nested open tags, no enclosures. |\n * | false | [...] | Enclosure-only: skip interiors, stop at first close tag outside them. |\n * | true | [...] | Full: depth tracking + enclosure skipping. |\n *\n * ### Chunk-boundary survival (feedable / stream sources)\n *\n * When input runs out mid-collection, `collect()` throws `UNEXPECTED_END`.\n * The caller (`feed()` in XMLParser) catches it and rewinds the source to the\n * outer mark (the `<` of the stop node's opening tag). On the next `feed()`\n * call `readOpeningTag()` sees the reader is already active (`isActive()`) and\n * calls `resumeAfterOpenTag()` to re-consume the opening tag before calling\n * `collect()` again. All accumulated content and state are preserved in\n * instance fields between attempts.\n */\nexport class StopNodeProcessor {\n /**\n * @param {string} tagName\n * The stop-node tag name to watch for.\n * @param {object} [opts]\n * @param {boolean} [opts.nested=false]\n * When true, nested same-name open tags increment a depth counter; the\n * stop node ends only when depth returns to zero.\n * @param {Array<{open:string,close:string}>} [opts.skipEnclosures=[]]\n * Enclosure pairs whose interiors suppress closing-tag detection.\n */\n constructor(tagName, { nested = false, skipEnclosures = [] } = {}) {\n this._tagName = tagName;\n this._nested = nested;\n this._enclosures = skipEnclosures;\n\n // Runtime state — reset in activate() / resumeAfterOpenTag()\n this._content = '';\n this._depth = 1; // already inside the opening tag\n this._active = false;\n }\n\n /** True once activated; cleared when `collect()` returns successfully. */\n isActive() {\n return this._active;\n }\n\n /**\n * Activate this processor. Called by `readOpeningTag` the first time it\n * encounters the stop node (after `readTagExp` has consumed the opening tag).\n */\n activate() {\n this._active = true;\n this._content = '';\n this._depth = 1;\n }\n\n /**\n * Called on resume (chunk boundary): the source was rewound to the `<` of\n * the stop node's opening tag, so the caller must re-consume the opening tag\n * via `readTagExp` before calling `collect()`.\n *\n * Because the rewind replays the entire opening tag, any content accumulated\n * during the failed attempt is invalid. Reset to a clean post-activation\n * state so the next `collect()` starts fresh from right after the opening tag.\n */\n resumeAfterOpenTag() {\n this._content = '';\n this._depth = 1;\n }\n\n /**\n * Collect raw content from `source` until the matching closing tag is found.\n *\n * Dispatches to one of four internal strategies based on the `nested` flag\n * and whether `skipEnclosures` is non-empty:\n *\n * - Plain (`nested:false`, no enclosures): fastest path — scan for\n * the literal `</tagName>` string and stop immediately.\n * - Depth-only (`nested:true`, no enclosures): track open/close tags\n * for depth, no enclosure skipping.\n * - Enclosure-only (`nested:false`, enclosures): skip enclosure interiors,\n * stop at the first closing tag found outside them.\n * - Full (`nested:true`, enclosures): depth tracking AND\n * enclosure skipping.\n *\n * Progress (`_content`, `_depth`) is stored in instance fields so a\n * chunk-boundary `UNEXPECTED_END` can be retried seamlessly.\n *\n * @param {object} source Any source object with the standard read interface.\n * @returns {{content: string, end: {index: number}}}\n * `content` is the raw text between the opening and closing tags.\n * `end` is the position immediately after the matched closing tag's '>' —\n * mirrors `TagDetail.openEnd` / closeMeta.closeEnd for the opening-tag side,\n * letting a caller recover the exact span of `<tag>...</tag>` including\n * both delimiters, not just the inner content.\n */\n collect(source) {\n source.markTokenStart(1);\n\n const enclosuresLen = this._enclosures.length;//dont inline\n\n if (!this._nested && enclosuresLen === 0) {\n return this._collectPlain(source);\n }\n if (this._nested && enclosuresLen === 0) {\n return this._collectDepthOnly(source);\n }\n if (!this._nested && enclosuresLen > 0) {\n return this._collectEnclosureOnly(source);\n }\n // nested && enclosures.length > 0\n return this._collectFull(source);\n }\n\n // ── Strategy 1: Plain ──────────────────────────────────────────────────────\n\n /**\n * Fastest path. No depth tracking, no enclosure skipping.\n * Scans for the literal `</tagName>` followed by optional whitespace then `>`.\n */\n _collectPlain(source) {\n while (source.canRead()) {\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n continue;\n }\n\n if (this._tryConsumeCloseTag(source)) return this._finish(source);\n this._content += source.readCh();\n }\n\n throw this._unclosedError();\n }\n\n // ── Strategy 2: Depth-only ─────────────────────────────────────────────────\n\n /**\n * Depth tracking without enclosure skipping.\n * Properly handles nested same-name open tags. No enclosure awareness.\n */\n _collectDepthOnly(source) {\n while (this._depth > 0) {\n ensureCanRead(source, 0, `stop node <${this._tagName}> content`);\n if (this._stepDepthTracking(source)) return this._finish(source);\n }\n\n /* istanbul ignore next */\n throw this._unclosedError();\n }\n\n // ── Strategy 3: Enclosure-only ─────────────────────────────────────────────\n\n /**\n * Enclosure skipping without depth tracking.\n * Skips enclosure interiors; stops at the first `</tagName>` found outside them.\n */\n _collectEnclosureOnly(source) {\n while (source.canRead()) {\n // Enclosure openers take priority over everything else\n if (this._trySkipEnclosure(source)) continue;\n\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n continue;\n }\n\n // At '<' outside any enclosure — check for our closing tag\n if (this._tryConsumeCloseTag(source)) return this._finish(source);\n this._content += source.readCh();\n }\n\n throw this._unclosedError();\n }\n\n // ── Strategy 4: Full (nested + enclosures) ─────────────────────────────────\n\n /**\n * Full mode: enclosure skipping AND depth tracking.\n * Enclosure interiors suppress all closing-tag and depth logic for their span.\n * Depth tracks nested same-name open tags; the stop node ends at depth zero.\n */\n _collectFull(source) {\n while (this._depth > 0) {\n ensureCanRead(source, 0, `stop node <${this._tagName}> content`);\n\n // Enclosure openers take priority over tag scanning\n if (this._trySkipEnclosure(source)) continue;\n\n if (this._stepDepthTracking(source)) return this._finish(source);\n }\n\n /* istanbul ignore next */\n throw this._unclosedError();\n }\n\n // ── Shared finish helper ───────────────────────────────────────────────────\n\n /**\n * Reset runtime state and return the accumulated content plus the end\n * position (immediately after the matched closing tag's '>').\n * Called by every strategy when the closing tag is confirmed — always\n * right after that '>' has just been consumed from `source`.\n * @param {object} source\n * @returns {{content: string, end: {index: number}}}\n */\n _finish(source) {\n const result = this._content;\n const end = { index: absolutePosition(source) };\n this._active = false;\n this._content = '';\n this._depth = 1;\n return { content: result, end };\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n /**\n * If an enclosure opens at the current position, consume it and its whole\n * interior (added to `_content` verbatim) and return true. Returns false,\n * consuming nothing, if no enclosure opens here. Shared by the two\n * enclosure-aware strategies (`_collectEnclosureOnly`, `_collectFull`).\n */\n _trySkipEnclosure(source) {\n const encIdx = this._matchEnclosureOpen(source);\n if (encIdx === -1) return false;\n const enc = this._enclosures[encIdx];\n this._skipChars(source, enc.open.length);\n this._content += enc.open;\n const interior = this._readUpto(source, enc.close);\n this._content += interior + enc.close;\n return true;\n }\n\n /**\n * At a '<' that isn't inside an enclosure: check whether it starts our own\n * closing tag (`</tagName` + optional whitespace + `>`). If so, consume it\n * and return true — the caller then calls `_finish()`. If not a match,\n * returns false without consuming anything, so the caller falls back to\n * treating the `<` as ordinary content. Shared by the two depth-unaware\n * strategies (`_collectPlain`, `_collectEnclosureOnly`).\n */\n _tryConsumeCloseTag(source) {\n const needed = '</' + this._tagName;\n if (!this._peekMatch(source, needed)) return false;\n\n let offset = needed.length;\n let validClose = false;\n while (true) {\n const c = source.readChAt(offset);\n if (c === '>') { validClose = true; break; }\n if (isSpace(c)) { offset++; continue; }\n break;\n }\n if (!validClose) return false;\n\n this._skipChars(source, needed.length);\n while (source.canRead()) {\n const c = source.readCh();\n if (c === '>') break;\n }\n return true;\n }\n\n /**\n * Process one tag encountered while depth-tracking is active: plain content\n * is appended as-is, a matching closing tag decrements depth (returning\n * true once depth reaches zero, telling the caller to finish), and any\n * other tag — including a nested opener of our own tag name — is appended\n * verbatim and, for a same-name non-self-closing opener, increments depth.\n * Shared by the two depth-tracking strategies (`_collectDepthOnly`,\n * `_collectFull`), which differ only in whether they check for enclosures\n * before calling this.\n */\n _stepDepthTracking(source) {\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n return false;\n }\n\n // Consume '<'\n source.readCh();\n ensureCanRead(source, 0, `stop node <${this._tagName}> tag after '<'`);\n\n const c0 = source.readChAt(0);\n\n if (c0 === '/') {\n // Closing tag\n source.readCh(); // consume '/'\n const closeName = this._readTagName(source);\n const closeSuffix = this._readToAngleClose(source);\n\n if (closeName === this._tagName) {\n this._depth--;\n if (this._depth === 0) return true;\n }\n this._content += '</' + closeName + closeSuffix;\n return false;\n }\n\n // Opening tag (including self-closing)\n const openName = this._readTagName(source);\n this._content += '<' + openName;\n\n const { selfClosing, attrText } = this._readTagTail(source);\n this._content += attrText;\n\n if (!selfClosing && openName === this._tagName) {\n this._depth++;\n }\n return false;\n }\n\n /**\n * The \"ran out of input before the stop node closed\" error, identical\n * across all four collection strategies.\n */\n _unclosedError() {\n return new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end of input`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * Check whether any enclosure's `open` marker starts at the current source\n * position (without consuming). Returns the index of the first matching\n * enclosure, or -1 if none match.\n */\n _matchEnclosureOpen(source) {\n const enclosuresLen = this._enclosures.length;\n for (let i = 0; i < enclosuresLen; i++) {\n if (this._peekMatch(source, this._enclosures[i].open)) return i;\n }\n return -1;\n }\n\n /**\n * Read until `stopStr` is found, consuming `stopStr` itself.\n * Returns the text before `stopStr`. Throws UNEXPECTED_END if input runs out.\n */\n _readUpto(source, stopStr) {\n const s0 = stopStr[0];\n const sLen = stopStr.length;\n const start = source.startIndex;\n let len = 0;\n\n while (source.canRead()) {\n if (source.readChAt(0) === s0 && this._peekMatch(source, stopStr)) {\n const text = source.readStr(len, start);\n this._skipChars(source, sLen);\n return text;\n }\n source.readCh();\n len++;\n }\n\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end looking for '${stopStr}'`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * Check whether the source (starting at current position) starts with `str`.\n * Does NOT consume.\n */\n _peekMatch(source, str) {\n const strLen = str.length;\n for (let i = 0; i < strLen; i++) {\n if (source.readChAt(i) !== str[i]) return false;\n }\n return true;\n }\n\n /**\n * Consume exactly `n` characters from source (discarding them — the caller\n * is responsible for appending to `_content` if needed).\n */\n _skipChars(source, n) {\n for (let i = 0; i < n; i++) source.readCh();\n }\n\n /**\n * Read an XML name (tag name) from the current source position.\n * Stops at `>`, `/`, or any whitespace. Does NOT consume the delimiter.\n */\n _readTagName(source) {\n let name = '';\n while (source.canRead()) {\n const ch = source.readChAt(0);\n if (ch === '>' || ch === '/' || isSpace(ch)) break;\n name += source.readCh();\n }\n return name;\n }\n\n /**\n * Read from after the tag name up to and including the closing `>`,\n * detecting self-closing `/>` and respecting quoted attribute values so\n * a `>` inside a value does not prematurely end the tag.\n *\n * Returns `{ selfClosing: boolean, attrText: string }` where `attrText`\n * includes everything from the first attribute character up to and\n * including the closing `>` (or `/>`).\n */\n _readTagTail(source) {\n const start = source.startIndex;\n let len = 0;\n let inSingle = false;\n let inDouble = false;\n\n while (source.canRead()) {\n const ch = source.readCh();\n len++;\n\n if (ch === \"'\" && !inDouble) {\n inSingle = !inSingle;\n } else if (ch === '\"' && !inSingle) {\n inDouble = !inDouble;\n } else if (!inSingle && !inDouble) {\n if (ch === '>') {\n return { selfClosing: false, attrText: source.readStr(len, start) };\n }\n if (ch === '/' && source.canRead() && source.readChAt(0) === '>') {\n source.readCh(); // consume '>'\n len++;\n return { selfClosing: true, attrText: source.readStr(len, start) };\n }\n }\n }\n\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end inside tag`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * After reading a closing tag name, read optional whitespace and the `>`\n * returning them as a raw string (e.g. `' >'` or `'>'`).\n * Preserves original spacing when reconstructing inner closing tags.\n */\n _readToAngleClose(source) {\n const start = source.startIndex;\n let len = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n len++;\n if (ch === '>') return source.readStr(len, start);\n if (!isSpace(ch)) {\n throw new ParseError(\n `Malformed closing tag for </${this._tagName}>`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n }\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end looking for '>'`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n}\n","import { readPiExp, flushAttributes } from './XmlPartReader.js';\nimport { expectMatch, errorPositionOf, sanitizeContent } from './util.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\n\nexport function readCdata(parser) {\n // Level-1 inner mark: records where this reader began, used only by flush()\n // as a safe trim boundary. Does NOT overwrite the level-0 outer mark set by\n // parseXml()'s loop before it consumed '<![', which rewindToMark() restores to.\n parser.source.markTokenStart(1);\n\n //<![ already consumed up to this point\n expectMatch(parser.source, \"CDATA[\", \"CDATA preamble\");\n\n let text = parser.source.readUpto(\"]]>\");\n // Normalization is unconditional — applies even under xml:space=\"preserve\".\n text = sanitizeContent(text, parser.source);\n parser.outputBuilder.addLiteral(text);\n}\n\nexport function readPiTag(parser) {\n const skipOptions = parser.options.skip;\n parser.source.markTokenStart(1);\n //<? already consumed\n let tagExp = readPiExp(parser, \"?>\");\n if (!tagExp) {\n throw new ParseError(\n \"Invalid Pi Tag expression.\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n )\n } else if (tagExp.tagName === \"xml\") {\n // Read version from the declaration and store it on the parser for validators.\n const version = tagExp.rawAttributes?.version;\n if (version === '1.1') {\n parser.xmlDec.version = 1.1;\n }\n parser.xmlDec.encoding = tagExp.rawAttributes?.encoding;\n parser.xmlDec.standalone = tagExp.rawAttributes?.standalone;\n\n // BUG FIX: getNameValidator('qName') was already called (and memoized)\n // above the moment this PI tag's own name (\"xml\") got validated — before\n // xmlDec.version was known, so it was always cached with the '1.0'\n // default. Every subsequent tag/attribute name in the document —\n // including the root element — would silently be checked against XML\n // 1.0 rules even for a document declaring version=\"1.1\". Reset the\n // cache now that the real version is known; this runs at most once per\n // document (a <?xml?> declaration can only appear once), so the cost is\n // negligible.\n // parser._nameValidators = Object.create(null);\n parser._nameValidators = {};\n }\n\n // Flush attributes into the output builder's this.attributes accumulator\n // so addDeclaration() / addInstruction() pick them up, mirroring what readOpeningTag\n // does for regular tags. PI tags are not pushed onto the matcher, so no\n // updateCurrent() call is needed here.\n if (!skipOptions.attributes) {\n flushAttributes(tagExp._parsedAttrs, parser, tagExp._attrsExpStart, tagExp._rawAttrMatchCount);\n }\n\n if (tagExp.tagName === \"xml\") {//TODO: move it to above if condition\n //TODO: verify it is very first tag else error\n if (!skipOptions.declaration) { //TODO: unnecessary. builder can ommit it from response if not needed\n parser.outputBuilder.addDeclaration(\"?xml\", parser.xmlDec);\n }\n } else if (!skipOptions.pi) { //TODO: unnecessary. builder can ommit it from response if not needed\n parser.outputBuilder.addInstruction(\"?\" + tagExp.tagName); // TODO: send without '?'\n }\n}\n\nexport function readComment(parser) {\n parser.source.markTokenStart(1);\n //<!- already consumed\n expectMatch(parser.source, \"-\", \"comment second dash\");\n let text = parser.source.readUpto(\"-->\");\n text = sanitizeContent(text, parser.source);\n parser.outputBuilder.addComment(text);\n}","import { ParseError, ErrorCode } from './ParseError.js';\nimport { expectMatch, ensureCanRead, errorPositionOf, isSpace } from './util.js';\n\nexport function readDocType(parser) {\n parser.source.markTokenStart(1);\n\n // <!D are already consumed by the caller up to this point\n expectMatch(parser.source, \"OCTYPE\", \"DOCTYPE preamble\");\n\n // const entities = Object.create(null);\n const entities = {};\n let entityCount = 0;\n let hasBody = false;\n let bodyDone = false;\n\n while (parser.source.canRead()) {\n // Save a local snapshot of startIndex BEFORE consuming this character.\n // If the sub-tag dispatch below throws UNEXPECTED_END we restore here\n // and re-throw so that feed()'s catch calls rewindToMark(), which\n // restores all the way back to the '<' that began the DOCTYPE tag\n // (the level-0 mark set by parseXml's loop). We must NOT call\n // markTokenStart(0) here because that would overwrite parseXml's\n // level-0 mark and cause rewindToMark() to land at the wrong position.\n const subTagStart = parser.source.startIndex;\n\n let ch = parser.source.readCh();\n\n if (ch === '<' && hasBody && !bodyDone) {\n // ── \"<!…\" sub-tag inside [...] body ───────────────────────────────\n // If any read below hits a chunk boundary we restore to subTagStart\n // (the '<') and re-throw UNEXPECTED_END so the outer rewind via\n // rewindToMark() lands at parseXml's level-0 mark (the DOCTYPE '<').\n try {\n ensureCanRead(parser.source, 0, \"DOCTYPE sub-tag\");\n let bang = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n if (bang !== \"!\") throw new ParseError(\n `Invalid DOCTYPE body tag starting with \"<${bang}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n\n ensureCanRead(parser.source, 0, \"DOCTYPE sub-tag type\");\n let typeChar = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n\n if (typeChar === \"-\") {\n // <!-- comment -->\n ensureCanRead(parser.source, 0, \"DOCTYPE comment\");\n let dash2 = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n if (dash2 !== \"-\") throw new ParseError(\n \"Invalid comment in DOCTYPE\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n parser.source.readUpto(\"-->\");\n\n } else if (typeChar === \"E\") {\n // ENTITY or ELEMENT — one more char to distinguish\n ensureCanRead(parser.source, 0, \"DOCTYPE E-type sub-tag\");\n let typeChar2 = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n\n if (typeChar2 === \"N\") {\n // <!ENTITY — need 4 more chars for \"TITY\"\n expectMatch(parser.source, \"TITY\", \"DOCTYPE ENTITY keyword\");\n\n const [entityName, entityValue] = readEntityExp(parser);\n\n if (entityValue.indexOf(\"&\") === -1) {\n const ep = parser.options?.doctypeOptions;\n if (ep?.maxEntityCount && entityCount >= ep.maxEntityCount) {\n throw new ParseError(\n `Entity count (${entityCount + 1}) exceeds maximum allowed (${ep.maxEntityCount})`,\n ErrorCode.ENTITY_MAX_COUNT,\n errorPositionOf(parser.source)\n );\n }\n const escaped = entityName.replace(/[.\\-+*:]/g, '\\\\$&');\n entities[entityName] = {\n regx: RegExp(`&${escaped};`, \"g\"),\n val: entityValue\n };\n entityCount++;\n }\n\n } else if (typeChar2 === \"L\") {\n // <!ELEMENT — need 5 more chars for \"EMENT\"\n expectMatch(parser.source, \"EMENT\", \"DOCTYPE ELEMENT keyword\");\n readElementExp(parser);\n\n } else {\n throw new ParseError(\n `Invalid DOCTYPE sub-tag \"<!E${typeChar2}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n }\n\n } else if (typeChar === \"A\") {\n // <!ATTLIST — need 6 more chars for \"TTLIST\"\n expectMatch(parser.source, \"TTLIST\", \"DOCTYPE ATTLIST keyword\");\n readAttlistExp(parser);\n\n } else if (typeChar === \"N\") {\n // <!NOTATION — need 7 more chars for \"OTATION\"\n expectMatch(parser.source, \"OTATION\", \"DOCTYPE NOTATION keyword\");\n readNotationExp(parser);\n\n } else {\n throw new ParseError(\n `Invalid DOCTYPE sub-tag \"<!${typeChar}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n }\n\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n // Restore cursor to the '<' that started this sub-tag so\n // that when feed() calls rewindToMark() (which goes all the\n // way back to the DOCTYPE '<' via parseXml's level-0 mark)\n // the full DOCTYPE — including this sub-tag — is replayed.\n parser.source.startIndex = subTagStart;\n }\n // Always re-throw: UNEXPECTED_END bubbles up to feed() for rewind;\n // INVALID_TAG and others bubble up as real parse failures.\n throw err;\n }\n\n } else if (ch === '[') {\n hasBody = true;\n\n } else if (ch === ']') {\n bodyDone = true;\n\n } else if (ch === '>') {\n if (!hasBody || bodyDone) {\n return entities;\n }\n // '>' before '[' is part of the external identifier — skip it\n }\n // whitespace, external identifier text, public id text — all skipped\n }\n\n throw new ParseError(\n \"Unclosed DOCTYPE\",\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(parser.source)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-expression readers\n// ---------------------------------------------------------------------------\n\n/**\n * Read an ENTITY declaration body.\n * \"<!ENTITY\" has already been consumed by the caller.\n *\n * All canRead() guards throw UNEXPECTED_END on chunk boundaries. The caller's\n * try/catch restores startIndex to the '<' of this sub-tag, then re-throws\n * so feed() → rewindToMark() resets all the way to the DOCTYPE opening '<'.\n *\n * @returns {[string, string]} [entityName, entityValue]\n */\nfunction readEntityExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"entity name\");\n\n const entityNameStart = source.startIndex;\n let entityNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch) || ch === '\"' || ch === \"'\") break;\n entityNameLen++;\n }\n const entityName = source.readStr(entityNameLen, entityNameStart);\n\n // Ran out mid-name without hitting a terminator — wait for more data\n ensureCanRead(source, 1, `entity name \"${entityName}\"`);\n\n validateEntityName(entityName, parser);\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 0, `after entity name \"${entityName}\"`);\n\n // SYSTEM check requires 6 chars; only peek when they are available\n if (source.canRead(5)) {\n if (source.matchAhead(\"system\", true) === true) {\n throw new ParseError(\"External entities are not supported\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n }\n\n if (source.readStr(1) === \"%\") {\n throw new ParseError(\"Parameter entities are not supported\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n\n // Need at least the opening quote char\n ensureCanRead(source, 0, `entity value for \"${entityName}\"`);\n\n const [entityValue] = readIdentifierVal(source, \"entity\");\n\n const ep = parser.options?.doctypeOptions;\n if (ep?.maxEntitySize && entityValue.length > ep.maxEntitySize) {\n throw new ParseError(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${ep.maxEntitySize})`,\n ErrorCode.ENTITY_MAX_SIZE,\n errorPositionOf(source)\n );\n }\n\n // readUpto throws UNEXPECTED_END automatically if \">\" is not in the buffer yet\n source.readUptoChar(\">\");\n\n return [entityName, entityValue];\n}\n\n/**\n * Read an ELEMENT declaration body.\n * \"<!ELEMENT\" has already been consumed by the caller.\n */\nfunction readElementExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n const elementNameStart = source.startIndex;\n let elementNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch)) break;\n elementNameLen++;\n }\n const elementName = source.readStr(elementNameLen, elementNameStart);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n if (!parser.getNameValidator('name')(elementName)) {\n throw new ParseError(`Invalid element name: \"${elementName}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n let peek1 = source.readStr(1);\n if (peek1 === \"E\") {\n // Use expectMatch for \"EMPTY\"\n try {\n expectMatch(source, \"EMPTY\", \"ELEMENT content model keyword EMPTY\");\n } catch (e) {\n // If not EMPTY, it might be something else – we fall back to skipping until '>'\n source.readUptoChar(\">\");\n return { elementName, contentModel: \"\" };\n }\n } else if (peek1 === \"A\") {\n try {\n expectMatch(source, \"ANY\", \"ELEMENT content model keyword ANY\");\n } catch (e) {\n source.readUptoChar(\">\");\n return { elementName, contentModel: \"\" };\n }\n } else if (peek1 === \"(\") {\n source.updateBufferBoundary(1);\n source.readUptoChar(\")\");\n }\n\n source.readUptoChar(\">\");\n return { elementName };\n}\n\n/**\n * Read an ATTLIST declaration body.\n * \"<!ATTLIST\" has already been consumed by the caller.\n */\nfunction readAttlistExp(parser) {\n parser.source.readUptoChar(\">\");\n}\n\n/**\n * Read a NOTATION declaration body.\n * \"<!NOTATION\" has already been consumed by the caller.\n */\nfunction readNotationExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"NOTATION name\");\n\n const notationNameStart = source.startIndex;\n let notationNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch)) break;\n notationNameLen++;\n }\n const notationName = source.readStr(notationNameLen, notationNameStart);\n\n ensureCanRead(source, 1, `after NOTATION name \"${notationName}\"`);\n\n validateEntityName(notationName, parser);\n skipSourceWhitespace(source);\n\n // Need all 6 chars of \"SYSTEM\" / \"PUBLIC\" before we can classify\n ensureCanRead(source, 6, \"NOTATION identifier type\");\n\n if (source.matchAhead(\"system\", true) === true) {\n source.updateBufferBoundary(6);\n skipSourceWhitespace(source);\n readIdentifierVal(source, \"systemIdentifier\");\n } else if (source.matchAhead(\"public\", true) === true) {\n source.updateBufferBoundary(6);\n skipSourceWhitespace(source);\n readIdentifierVal(source, \"publicIdentifier\");\n skipSourceWhitespace(source);\n ensureCanRead(source, 1, \"after NOTATION PUBLIC identifier\");\n let next = source.readStr(1);\n if (next === '\"' || next === \"'\") {\n readIdentifierVal(source, \"systemIdentifier\");\n }\n } else {\n throw new ParseError(\n `Expected SYSTEM or PUBLIC in NOTATION, found \"${source.readStr(6)}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n\n source.readUptoChar(\">\");\n}\n\n/**\n * Read a quoted identifier value from the source.\n * Consumes the opening quote, the content, and the closing quote.\n * @returns {[string]} [value]\n */\nfunction readIdentifierVal(source, type) {\n ensureCanRead(source, 1, type + \" opening quote\")\n let startChar = source.readStr(1);\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new ParseError(\n `Expected quoted string for ${type}, found \"${startChar}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n source.updateBufferBoundary(1);\n // readUpto throws UNEXPECTED_END automatically when the closing quote is absent\n let value = source.readUptoChar(startChar);\n return [value];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction skipSourceWhitespace(source) {\n while (source.canRead()) {\n const ch = source.readChAt(0);\n if (!isSpace(ch)) break;\n source.updateBufferBoundary(1)\n }\n}\n\nfunction validateEntityName(name, parser) {\n if (parser.getNameValidator('name')(name)) return name;\n throw new ParseError(\n `Invalid entity name \"${name}\"`,\n ErrorCode.ENTITY_INVALID_KEY,\n {}\n );\n}\n","import { ParseError, ErrorCode } from './ParseError.js';\nimport { absolutePosition } from './util.js';\n\n/**\n * AutoCloseHandler\n *\n * Handles two distinct failure modes that arise when XML is malformed\n * or a data stream is interrupted:\n *\n * 1. EOF with open tags — `onEof` option\n * 2. Mismatched close tag — `onMismatch` option\n *\n * The handler is stateless; it receives the parser's live state on each\n * call and mutates it directly (matching how the parser normally works).\n */\n\n/**\n * Error types returned by getParseErrors() when `collectErrors` is true.\n * @enum {string}\n */\nexport const AutoCloseErrorType = Object.freeze({\n /** A tag was still open when the document ended. */\n UNCLOSED_EOF: 'unclosed-eof',\n\n /**\n * A closing tag didn't match the current open tag.\n * The handler popped up the stack to find the nearest match.\n */\n MISMATCHED_CLOSE: 'mismatched-close',\n\n /**\n * A closing tag appeared whose opener doesn't exist anywhere in the stack.\n * The tag is discarded.\n */\n PHANTOM_CLOSE: 'phantom-close',\n\n /**\n * The source ended mid-way through a tag — e.g. `<div><p` or `</di`.\n * The partial tag is discarded; any already-open tags are closed by handleEof.\n */\n PARTIAL_TAG: 'partial-tag',\n});\n\nexport default class AutoCloseHandler {\n /**\n * @param {object} autoCloseOptions - Resolved autoClose options\n * @param {string} autoCloseOptions.onEof - 'throw' | 'closeAll'\n * @param {string} autoCloseOptions.onMismatch - 'throw' | 'recover' | 'discard'\n * @param {boolean} autoCloseOptions.collectErrors\n */\n constructor(autoCloseOptions) {\n this.onEof = autoCloseOptions.onEof || 'throw';\n this.onMismatch = autoCloseOptions.onMismatch || 'throw';\n this.collectErrors = autoCloseOptions.collectErrors || false;\n this.errors = [];\n }\n\n /**\n * Called at end-of-document when `tagsStack` is non-empty.\n *\n * @param {object} parserState\n * @param {Array} parserState.tagsStack - Parser's open-tag stack\n * @param {object} parserState.currentTagDetail - The currently open TagDetail\n * @param {object} parserState.outputBuilder - Live OutputBuilder instance\n * @param {object} parserState.readonlyMatcher - Read-only Matcher proxy\n * @param {object} parserState.source - Current InputSource (for position)\n * @param {Function} parserState.addTextNode - Bound addTextNode on the parser\n */\n handleEof(parserState) {\n if (this.onEof === 'throw') {\n throw new ParseError('Unexpected data in the end of document', ErrorCode.UNEXPECTED_TRAILING_DATA);\n }\n\n // onEof === 'closeAll'\n // Close from innermost outward using the parser's canonical popTag(),\n // which keeps the parser stack and output builder in sync automatically.\n\n const { addTextNode, popTag } = parserState;\n\n let current = parserState.currentTagDetail;\n\n while (current && !current.root) {\n this._recordError(AutoCloseErrorType.UNCLOSED_EOF, {\n tag: current.name,\n expected: null,\n index: current.index,\n });\n\n addTextNode();\n popTag();\n\n // popTag() already updated currentTagDetail via tagsStack.pop()\n current = parserState.currentTagDetail;\n }\n }\n\n /**\n * Called when a closing tag name doesn't match `currentTagDetail.name`.\n *\n * Returns an object describing what the caller should do:\n * { action: 'close-matched' } — handler already closed intermediates;\n * caller should now close the matched tag normally\n * { action: 'discard' } — caller should skip this closing tag entirely\n *\n * @param {string} closingTagName - The mismatched closing tag we just read\n * @param {object} parserState - Same shape as handleEof\n * @returns {{ action: string }}\n */\n handleMismatch(closingTagName, parserState) {\n const { tagsStack, currentTagDetail, source, addTextNode } = parserState;\n\n if (this.onMismatch === 'throw') {\n throw new ParseError(\n `Unexpected closing tag '${closingTagName}' expecting '${currentTagDetail.name}'`,\n ErrorCode.MISMATCHED_CLOSE_TAG,\n { index: source ? absolutePosition(source) : undefined }\n );\n }\n\n if (this.onMismatch === 'discard') {\n this._recordError(AutoCloseErrorType.MISMATCHED_CLOSE, {\n tag: closingTagName,\n expected: currentTagDetail.name,\n index: source ? absolutePosition(source) : null,\n });\n return { action: 'discard' };\n }\n\n // onMismatch === 'recover'\n // Scan the stack (top → bottom) for the closest matching opener.\n // tagsStack holds ancestors with index 0 = root, last = parent of current.\n // currentTagDetail is the open tag at the top that didn't match.\n\n // Build a unified view: [root...ancestors, current] — we check current first\n // (it's the top), then walk down toward the root.\n const stackSnapshot = [...tagsStack, currentTagDetail];\n\n let matchIndex = -1;\n const stackSnapshotLength = stackSnapshot.length;\n for (let i = stackSnapshotLength - 1; i >= 0; i--) {\n if (stackSnapshot[i].name === closingTagName) {\n matchIndex = i;\n break;\n }\n }\n\n if (matchIndex === -1) {\n // No match anywhere — phantom closing tag\n this._recordError(AutoCloseErrorType.PHANTOM_CLOSE, {\n tag: closingTagName,\n expected: currentTagDetail.name,\n index: source ? absolutePosition(source) : null,\n });\n return { action: 'discard' };\n }\n\n // Close everything above the match (innermost first), then signal the\n // caller to close the matched tag itself in the normal path.\n const levelsToClose = stackSnapshotLength - 1 - matchIndex;\n\n for (let i = 0; i < levelsToClose; i++) {\n const tag = stackSnapshot[stackSnapshotLength - 1 - i];\n\n this._recordError(AutoCloseErrorType.MISMATCHED_CLOSE, {\n tag: tag.name,\n expected: closingTagName,\n index: tag.index,\n });\n\n addTextNode();\n parserState.popTag();\n }\n\n // Update currentTagDetail to the matched one so the normal close path works.\n // popTag() has already walked the stack up by levelsToClose steps; the next\n // currentTagDetail is the one we want to match against.\n parserState.currentTagDetail = stackSnapshot[matchIndex];\n\n return { action: 'close-matched' };\n }\n\n /**\n * Called when the source ended mid-way through a tag token.\n * Records the partial-tag error and delegates remaining open tags to handleEof.\n *\n * @param {Error} originalError - The error thrown by the read function\n * @param {object} parserState - Same shape as handleEof\n */\n handlePartialTag(originalError, parserState) {\n this._recordError(AutoCloseErrorType.PARTIAL_TAG, {\n tag: _extractPartialTagName(originalError),\n expected: null,\n index: parserState.source ? absolutePosition(parserState.source) : null,\n });\n\n // Discard any partially-accumulated text from the broken tag\n parserState.tagTextData = '';\n\n // Close whatever was legitimately open before this truncation\n this.handleEof(parserState);\n }\n\n /**\n * Return a copy of the collected error list.\n * Empty array when collectErrors is false or no errors occurred.\n * @returns {Array}\n */\n getErrors() {\n return this.errors.slice();\n }\n\n /**\n * Reset error log (useful if the same handler instance is reused).\n */\n reset() {\n this.errors = [];\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n _recordError(type, detail) {\n if (!this.collectErrors) return;\n this.errors.push({ type, ...detail });\n }\n}\n\n/**\n * Best-effort extraction of a partial tag name from a source-exhausted error.\n * Accepts the full error object so it can inspect both message and code.\n *\n * ParseError from readClosingTagName (new format):\n * message: \"Unexpected end of source reading closing tag '</di'\"\n *\n * Legacy plain Error (old format, kept for safety):\n * message: \"Unexpected end of source. Reading closing tag '</di'\"\n *\n * ParseError from readTagExp / readPiExp — opening tag truncated before '>':\n * No tag name is embedded; returns null.\n */\nfunction _extractPartialTagName(err) {\n if (!err) return null;\n const message = typeof err.message === 'string' ? err.message : String(err);\n // Match both \"reading closing tag\" (new, lowercase) and\n // \"Reading closing tag\" (old, capitalised, period-separated)\n const closeMatch = message.match(/[Rr]eading closing tag '<\\/([^']*)/);\n if (closeMatch) return closeMatch[1] || null;\n return null;\n}","import StringSource from './InputSource/StringSource.js';\nimport BufferSource from './InputSource/BufferSource.js';\nimport { buildProfileForBuffer } from './Encoding/EncodingProfile.js';\nimport { errorPositionOf, absolutePosition, sanitizeContent } from './util.js';\nimport { readTagExp, readClosingTagName, flushAttributes, tryMatchClosingTagName } from './XmlPartReader.js';\nimport { StopNodeProcessor } from './StopNodeProcessor.js';\nimport { readComment, readCdata, readPiTag } from './XmlSpecialTagsReader.js';\nimport { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';\nimport { readDocType } from './DocTypeReader.js';\nimport { DANGEROUS_PROPERTY_NAMES, criticalProperties } from './util.js';\nimport AutoCloseHandler from './AutoCloseHandler.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { createValidator } from 'xml-naming';\n\n// Cap on the tag-name and attribute-name caches (each capped independently —\n// see SAVEPOINT_name_cache.md for why: real documents have a small closed\n// vocabulary of names, so this is a not-unbounded-growth guard for a\n// pathological one-off document, not a security control (that's what\n// limits.maxAttributesPerTag/maxNestedTags are for). Not user-configurable —\n// an internal implementation detail, not a knob most users could tune\n// meaningfully. On overflow the whole map is cleared rather than evicting\n// individual entries (LRU) — simpler, and this ceiling is rare in practice.\nconst NAME_CACHE_LIMIT = 2000;\nconst keepSpace = { keep: [\"xml:space\"] }\n/**\n * Returns the cached result of computeFn(rawName) if present, otherwise\n * calls computeFn(), caches the result, and returns it.\n *\n * If computeFn() throws (invalid name, restricted name, prototype-pollution\n * attempt, etc.) nothing is cached — the next occurrence of the same bad\n * name re-runs the full check and throws again from scratch. These are rare\n * error paths where correctness matters, not speed.\n *\n * A plain Map is used (not a plain object) so a legitimately falsy cached\n * value (resolveNsPrefix returns `false` for a dropped xmlns declaration)\n * is never confused with \"not cached\" — Map.get() only returns undefined\n * when the key is genuinely absent.\n */\nfunction getCachedName(cache, rawName, computeFn) {\n const cached = cache.get(rawName);\n if (cached !== undefined || cache.has(rawName)) return cached;\n const result = computeFn();\n if (cache.size >= NAME_CACHE_LIMIT) cache.clear();\n cache.set(rawName, result);\n return result;\n}\n\nclass TagDetail {\n /**\n * @param {string} name - Tag name\n * @param {number} index - Character offset of '<' from document start\n * @param {number} [openEnd] - Character offset immediately after the opening\n * tag's closing '>' (i.e. end of `<tag attr=\"x\">`). Undefined until the\n * opening tag expression has been fully read; set in readOpeningTag().\n * For self-closing tags this is the offset after '/>'.\n * @param {string} [rawName] - the tag name exactly as written (before\n * namespace-prefix stripping) — what a matching closing tag will\n * literally contain. Used by readClosingTag()'s fast path; undefined for\n * the synthetic root node.\n */\n constructor(name, index = 0, openEnd = undefined, rawName = undefined) {\n this.name = name;\n this.index = index;\n this.openEnd = openEnd;\n this.rawName = rawName;\n }\n}\n\nexport default class Xml2JsParser {\n constructor(options) {\n this.options = options;\n\n this.currentTagDetail = null;\n this.tagTextData = \"\";\n this.tagsStack = [];\n this.doctypeFound = false;\n this.matcher = new Matcher();\n\n //create once and reuse\n this.readonlyMatcher = this.matcher.readOnly();\n\n // AutoClose handler — created once per parser instance, reset on each parse\n this.autoCloseHandler = options.autoClose\n ? new AutoCloseHandler(options.autoClose)\n : null;\n\n this._unpairedSet = new Set(this.options.tags.unpaired);\n\n // Reuse the sealed ExpressionSets built by OptionsBuilder.\n // Each Expression carries its config ({ nested, skipEnclosures }) in .data.\n // findMatch() returns the matched Expression directly — O(1) indexed lookup.\n this.stopNodeExpressionsSet = this.options.tags.stopNodesSet ?? new ExpressionSet();\n if (this.stopNodeExpressionsSet.size === 0) this.isStopNode = () => false;\n else this.isStopNode = isStopNode;\n this.skipTagExpressionsSet = this.options.skip.tagsSet ?? new ExpressionSet();\n if (this.skipTagExpressionsSet.size === 0) this.isSkipTag = () => false;\n else this.isSkipTag = isSkipTag;\n\n // exitIf: optional predicate called after each opening tag is pushed.\n // Stored directly — it's a plain function, not an ExpressionSet.\n this._exitIf = typeof options.exitIf === 'function' ? options.exitIf : () => false;\n\n // Tag/attribute name cache — read from `options`, not created fresh here.\n // `XMLParser` creates a brand-new Xml2JsParser on every parse() call but\n // passes the *same* options object each time, so a cache stored on\n // `options` survives across those calls. Deliberately NOT reset in\n // initializeParser() (unlike _nameValidators) — it depends only on the\n // raw name string plus options that are fixed for this instance's\n // lifetime, never on anything document-specific like xmlVersion.\n // Guarded fallback here in case Xml2JsParser is ever constructed\n // directly (e.g. in tests) without going through XMLParser.\n this._nameCache = options._nameCache || (options._nameCache = { tags: new Map(), attrs: new Map() });\n }\n\n initializeParser() {\n this.tagTextData = \"\";\n this.tagsStack = [];\n this.doctypeFound = false;\n this._stopNodeProcessor = null;\n this._exitIfTriggered = false;\n // Lazily-built, memoized xml-naming validators (v0.3.0 createValidator).\n // Lazy because xmlDec.version isn't final until the optional <?xml?>\n // declaration (if any) has been read — which happens after this method\n // runs but before any tag name is ever validated. Reset here (once per\n // document/session, see XMLParser._createParser / feed() call sites) so\n // a reused Xml2JsParser instance never validates against a stale\n // xmlVersion or leaks one document's name cache into the next.\n // this._nameValidators = Object.create(null);\n this._nameValidators = {};\n // Validity (unlike sanitize/ns-resolution in this._nameCache) depends on\n // xmlVersion, which is itself document-scoped — reset alongside\n // _nameValidators for the same reason, not on `options` with the rest of\n // _nameCache. See isValidQName().\n this._validQNames = new Set();\n this.xmlDec = {\n version: 1.0,\n lang: null,\n encoding: null,\n standalone: \"yes\"\n }\n\n if (!this.matcher) {\n this.matcher = new Matcher();\n this.readonlyMatcher = this.matcher.readOnly();\n }\n\n this.outputBuilder = this._createOutputBuilder();\n\n this.root = { root: true, name: \"\" };\n this.currentTagDetail = this.root;\n }\n\n /**\n * Create an OutputBuilder instance for this parse run.\n * The output builder owns all value parser registration, including\n * EntitiesValueParser — no injection needed from the parser side.\n */\n _createOutputBuilder() {\n return this.options.OutputBuilder.getInstance(this.options, this.readonlyMatcher);\n }\n\n /**\n * Returns true if the last parse call was terminated early by exitIf.\n * Useful when the caller needs to know whether parsing completed or stopped.\n */\n wasExited() {\n return this._exitIfTriggered === true;\n }\n\n parse(strData) {\n this.source = new StringSource(strData);\n this.initializeParser();\n this._parseAndFinalize();\n return this.outputBuilder.getOutput();\n }\n\n parseBytesArr(data) {\n const registry = this.options.decoding?._registry;\n const profile = buildProfileForBuffer(data, this.options.decoding, registry);\n this.source = new BufferSource(data, {}, profile);\n this.initializeParser();\n this._parseAndFinalize();\n return this.outputBuilder.getOutput();\n }\n\n /**\n * Advance the parser state machine as far as the source buffer allows.\n * Stops naturally when canRead() returns false — no EOF handling here.\n * Call finalizeXml() once all input is consumed to validate end-of-document.\n *\n * parseStream() and feed()/end() call this per chunk; _parseAndFinalize()\n * (used by parse() / parseBytesArr()) calls it then finalizeXml() immediately.\n */\n parseXml() {\n while (this.source.canRead()) {\n // exitIf triggered in this iteration — stop consuming input immediately.\n if (this._exitIfTriggered) break;\n\n // Level-0 outer mark: set before consuming any character so that if a\n // '<' dispatch throws UNEXPECTED_END (chunk boundary mid-tag), feed()\n // rewinds to here and the full token — including '<', '![', '</' etc. —\n // is re-read on the next chunk. Inner reader functions use level-1 marks\n // which never overwrite this position.\n this.source.markTokenStart(0);\n\n // Position of the next character, captured before it's read. When that\n // character turns out to be '<', this is exactly the position of '<'\n // itself — used below as the authoritative tag-start position for both\n // TagDetail (open tags) and closeMeta (close tags), instead of deriving\n // it after the fact from source.startIndex once the tag name/attrs have\n // already been consumed (which points past the tag, not at its start).\n const preReadPos = errorPositionOf(this.source);\n\n const ch = this.source.readCh();\n if (ch === undefined || ch === '') break;\n\n if (ch === '<') {\n const tagStart = preReadPos;\n\n const nextChar = this.source.readChAt(0);\n if (nextChar === '') throw new ParseError(\n \"Unexpected end of source after '<'\", ErrorCode.UNEXPECTED_END, errorPositionOf(this.source)\n );\n\n //sorted frequency wise\n if (nextChar === '/') {\n this.source.updateBufferBoundary();\n this.readClosingTag(tagStart);\n } else if (nextChar === '!') {\n this.source.updateBufferBoundary();\n this.addTextNode();\n this.readSpecialTag(nextChar);\n } else if (nextChar === '?') {\n this.source.updateBufferBoundary();\n this.addTextNode();\n readPiTag(this);\n } else {\n this.readOpeningTag(tagStart);\n }\n } else {\n // ch is already consumed. Peek ahead for more non-'<' chars and grab\n // the whole run in one readStr call rather than concatenating one char\n // at a time through every loop iteration.\n let runLen = 0;\n while (true) {\n const c = this.source.readChAt(runLen);\n if (c === '<' || c === undefined || c === '') break;\n runLen++;\n }\n if (runLen > 0) {\n this.tagTextData += ch + this.source.readStr(runLen, this.source.startIndex);\n this.source.updateBufferBoundary(runLen);\n } else {\n this.tagTextData += ch;\n }\n\n //TODO: why does below code doesn't work\n // const text = this.source.readUptoChar(\"<\");\n // this.tagTextData += text;\n }\n }\n }\n\n /**\n * Validate end-of-document state and apply autoClose recovery if configured.\n * Must be called exactly once after all input has been consumed.\n */\n finalizeXml() {\n // When exitIf fired, the parser already closed all open tags and notified\n // the builder — treat the partial parse as complete and skip EOF checks.\n if (this._exitIfTriggered) return;\n\n const hasOpenTags = this.tagsStack.length > 0 ||\n (this.currentTagDetail && !this.currentTagDetail.root);\n\n const hasTrailingText =\n !hasOpenTags &&\n this.tagTextData !== undefined &&\n this.tagTextData.trimEnd().length > 0;\n\n if (hasOpenTags || hasTrailingText) {\n if (this.autoCloseHandler && hasOpenTags && !hasTrailingText) {\n this.autoCloseHandler.handleEof(this._parserState());\n } else {\n throw new ParseError('Unexpected data in the end of document', ErrorCode.UNEXPECTED_TRAILING_DATA, errorPositionOf(this.source));\n }\n }\n }\n\n /**\n * One-shot helper used by parse() and parseBytesArr().\n * Runs parseXml() with autoClose partial-tag recovery, then finalizeXml().\n * @private\n */\n _parseAndFinalize() {\n let partialTagError = null;\n if (this.autoCloseHandler) this.autoCloseHandler.reset();\n\n try {\n this.parseXml();\n } catch (err) {\n if (this.autoCloseHandler && isSourceExhaustedError(err)) {\n partialTagError = err;\n } else {\n throw err;\n }\n }\n\n if (partialTagError) {\n this.autoCloseHandler.handlePartialTag(partialTagError, this._parserState());\n return;\n }\n\n this.finalizeXml();\n }\n\n readClosingTag(tagStart) {\n // ── Fast path ────────────────────────────────────────────────────────────\n // The overwhelming majority of closing tags match the tag already sitting\n // on top of the stack. Try that directly, character-by-character, before\n // reading the name into a fresh string and re-validating/re-sanitizing a\n // name we've already validated once (see\n // SAVEPOINT_closing_tag_and_double_scan.md). Peek-only — a mismatch or a\n // buffer that runs out mid-check costs nothing to abandon, so any failure\n // here falls straight through to the exact original slow path below.\n //\n // Skipping isUnpaired()/isStopNode() on this path is safe by construction,\n // not just by observation: only pushTag() ever sets currentTagDetail, and\n // pushTag() is never reached for an unpaired tag or a stop-node/skip-tag\n // match (both close themselves inline in readOpeningTag) — so whenever\n // currentTagDetail is a real open tag, both checks are guaranteed false.\n const current = this.currentTagDetail;\n if (current && !current.root && current.rawName !== undefined) {\n const consumed = tryMatchClosingTagName(this.source, current.rawName);\n if (consumed !== -1) {\n this.source.updateBufferBoundary(consumed);\n const closeMeta = {\n name: current.name,\n index: tagStart.index,\n closeEnd: absolutePosition(this.source),\n };\n this.addTextNode();\n this.popTag(closeMeta);\n return;\n }\n }\n\n // ── Slow path — unchanged ────────────────────────────────────────────────\n const tagName = this.processTagName(readClosingTagName(this.source));\n // closeMeta: position of this closing tag's '</' (tagStart, passed in from\n // parseXml's dispatch) plus the offset right after its '>' (closeEnd) —\n // mirrors tagDetail.index / tagDetail.openEnd for the opening-tag side.\n const closeMeta = {\n name: tagName,\n index: tagStart.index,\n closeEnd: absolutePosition(this.source),\n };\n\n if (this.isUnpaired(tagName) || this.isStopNode()) {\n throw new ParseError(`Unexpected closing tag '${tagName}'`, ErrorCode.UNEXPECTED_CLOSE_TAG, errorPositionOf(this.source));\n }\n\n if (tagName !== this.currentTagDetail.name) {\n if (!this.autoCloseHandler) {\n throw new ParseError(\n `Unexpected closing tag '${tagName}' expecting '${this.currentTagDetail.name}'`,\n ErrorCode.MISMATCHED_CLOSE_TAG,\n errorPositionOf(this.source)\n );\n }\n\n const decision = this.autoCloseHandler.handleMismatch(tagName, this._parserState());\n\n if (decision.action === 'discard') return;\n // 'close-matched': handler updated currentTagDetail; fall through to normal close\n }\n\n if (!this.currentTagDetail.root) this.addTextNode();\n this.popTag(closeMeta);\n }\n\n readOpeningTag(tagStart) {\n const options = this.options;\n this.addTextNode();\n\n // ── Stop-node resume ─────────────────────────────────────────────────────\n // When a chunk boundary fell inside StopNodeProcessor.collect(), feed() caught\n // UNEXPECTED_END and rewound the source to the '<' of the stop node's\n // opening tag. On the next feed() we re-enter here with the processor active.\n // Re-consume the opening tag (source was rewound to its '<'), then resume\n // collection — the processor remembers all accumulated content and depth.\n if (this._stopNodeProcessor && this._stopNodeProcessor.isActive()) {\n const { tagDetail, isSkip } = this._stopNodeProcessorMeta;\n this._stopNodeProcessor.resumeAfterOpenTag();\n readTagExp(this); // re-consume the opening tag from the rewound source\n // openEnd reflects the offset right after this opening tag's '>' — stable\n // across retries since the opening tag is fully re-read every time.\n tagDetail.openEnd = absolutePosition(this.source);\n const { content, end: stopEnd } = this._stopNodeProcessor.collect(this.source);\n if (!isSkip) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.outputBuilder.onStopNode?.(tagDetail, content, this.readonlyMatcher, stopEnd);\n this.outputBuilder.addValue(content, this.readonlyMatcher);\n this.outputBuilder.closeElement(this.readonlyMatcher, { name: tagDetail.name, closeEnd: stopEnd.index });\n }\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n return;\n }\n\n let tagExp = readTagExp(this);\n const processedTagName = this.processTagName(tagExp.tagName);\n const tagDetail = new TagDetail(\n processedTagName,\n tagStart.index,\n absolutePosition(this.source), // openEnd: offset right after this opening tag's '>'\n tagExp.tagName, // rawName: exactly as written, for readClosingTag()'s fast path\n );\n\n // Extract namespace prefix and local name from raw tag name (e.g. \"ns:tag\" → \"ns\", \"tag\").\n // Always done from the raw name (tagExp.tagName), before processTagName strips the prefix,\n // so these values are stable regardless of skip.nsPrefix.\n const colonIdx = tagExp.tagName.indexOf(':');\n const tagNamespace = colonIdx !== -1 ? tagExp.tagName.slice(0, colonIdx) : undefined;\n // Local name for the matcher: prefix-free always (e.g. \"code\" from \"ns:code\").\n // The matcher library tracks namespace separately via the 3rd push() argument —\n // passing the full \"ns:code\" as the tag name would break ns::code expression matching.\n const matcherTagName = tagNamespace !== undefined\n ? tagExp.tagName.slice(colonIdx + 1)\n : processedTagName;\n\n // ── Limit: maxNestedTags ─────────────────────────────────────────────────\n const maxNested = options.limits?.maxNestedTags;\n if (maxNested !== undefined && maxNested !== null) {\n const depth = this.tagsStack.length + 1;\n if (depth > maxNested) {\n throw new ParseError(\n `Nesting depth ${depth} exceeds limit of ${maxNested} (tag: '${processedTagName}')`,\n ErrorCode.LIMIT_MAX_NESTED_TAGS,\n { index: tagDetail.index }\n );\n }\n }\n\n // ── Two-pass attribute handling ──────────────────────────────────────────\n let rawAttributes = {};\n let raeAttrLen = 0;\n if (tagExp.rawAttributes) {\n rawAttributes = tagExp.rawAttributes;\n raeAttrLen = tagExp.rawAttributesLen;\n }\n\n if (raeAttrLen > 0) {\n this.matcher.push(matcherTagName, rawAttributes, tagNamespace, keepSpace);\n // this.matcher.updateCurrent(rawAttributes);\n } else {\n this.matcher.push(matcherTagName, {}, tagNamespace);\n\n }\n\n // Resolve skip/stop BEFORE touching the output builder\n const stopNodeConfig = this.isStopNode();\n const skipTagConfig = stopNodeConfig ? null : this.isSkipTag();\n\n if (!options.skip.attributes && !skipTagConfig) {\n flushAttributes(tagExp._parsedAttrs, this, tagExp._attrsExpStart, tagExp._rawAttrMatchCount);\n }\n\n // Stop-node and skip-tag checks AFTER attributes are set so attribute conditions work.\n // const stopNodeConfig = this.isStopNode();\n // Skip tag is only checked when this tag is not already a stop node — they are mutually exclusive.\n // const skipTagConfig = stopNodeConfig ? null : this.isSkipTag();\n\n if (this.isUnpaired(processedTagName)) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n // Unpaired tags (e.g. <br>, <img>) have no separate closing tag — the\n // close position is the same as the open tag's end.\n this.outputBuilder.closeElement(this.readonlyMatcher, this._closeMetaFor(tagDetail));\n this.matcher.pop();\n } else if (tagExp.selfClosing) {\n if (!skipTagConfig) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n // Self-closing tags (<tag/>) likewise have no distinct closing tag.\n this.outputBuilder.closeElement(this.readonlyMatcher, this._closeMetaFor(tagDetail));\n }\n this.matcher.pop();\n } else if (stopNodeConfig) {\n // Create a fresh processor with the matching nested + skipEnclosures config.\n // Raw tag name (tagExp.tagName) is used — the processor scans the source\n // character-by-character and must match the prefix-as-written (e.g. \"ns:code\"),\n // independent of what skip.nsPrefix does to the processed output name.\n this._stopNodeProcessor = new StopNodeProcessor(tagExp.tagName, {\n nested: stopNodeConfig.nested,\n skipEnclosures: stopNodeConfig.skipEnclosures,\n });\n this._stopNodeProcessorMeta = { tagDetail, isSkip: false };\n this._stopNodeProcessor.activate();\n const { content, end: stopEnd } = this._stopNodeProcessor.collect(this.source);\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.outputBuilder.onStopNode?.(tagDetail, content, this.readonlyMatcher, stopEnd);\n this.outputBuilder.addValue(content, this.readonlyMatcher);\n // closeMeta for a stop node carries only `closeEnd` (offset right after\n // the matched </tagname> was consumed) — StopNodeProcessor scans the\n // closing tag opaquely and doesn't track where '</tagname' itself starts,\n // so unlike the normal close path we don't have a real index\n // for the close tag's own start, only its end.\n this.outputBuilder.closeElement(this.readonlyMatcher, { name: tagDetail.name, closeEnd: stopEnd.index });\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n } else if (skipTagConfig) {\n // Skip tag: collect raw content (to advance the source past the closing tag)\n // but call no output builder methods — the tag is silently dropped.\n // Raw tag name used for the same reason as the stop-node branch above.\n this._stopNodeProcessor = new StopNodeProcessor(tagExp.tagName, {\n nested: skipTagConfig.nested,\n skipEnclosures: skipTagConfig.skipEnclosures,\n });\n this._stopNodeProcessorMeta = { tagDetail, isSkip: true };\n this._stopNodeProcessor.activate();\n this._stopNodeProcessor.collect(this.source); // advance source; content discarded\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n } else if (this._exitIf(this.readonlyMatcher)) {\n // ── exitIf ───────────────────────────────────────────────────────────────\n // Checked BEFORE addElement so the triggering tag is never added to the\n // output builder. The matcher is already positioned (push + updateCurrent\n // above), so attribute-based predicates work correctly.\n //\n // We pop the matcher entry for this tag (it was never added to the builder),\n // then close all already-open ancestors so the builder can finalise its tree.\n\n const exitDepth = this.tagsStack.length; // number of ancestors open before this tag\n this.matcher.pop(); // undo the push for the triggering tag\n\n while (this.currentTagDetail && !this.currentTagDetail.root) {\n this.addTextNode();\n this.popTag();\n }\n\n // Notify the output builder that parsing was intentionally truncated.\n if (typeof this.outputBuilder.onExit === 'function') {\n this.outputBuilder.onExit({\n tagDetail,\n matcher: this.readonlyMatcher,\n depth: exitDepth,\n });\n }\n\n this._exitIfTriggered = true;\n } else {\n this.pushTag(tagDetail);\n }\n }\n\n /**\n * Push a tag onto the parser stack and notify the output builder.\n * This is the single point of entry for opening a non-self-closing tag —\n * both the parser-side stack (currentTagDetail / tagsStack) and the\n * output builder are updated together, keeping them in sync.\n *\n * Custom OutputBuilder implementations that maintain their own tag stack\n * should override addElement() rather than calling pushTag() directly.\n *\n * @param {TagDetail} tagDetail\n */\n pushTag(tagDetail) {\n this.tagsStack.push(this.currentTagDetail);\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.currentTagDetail = tagDetail;\n }\n\n /**\n * Pop the current tag from the parser stack and notify the output builder.\n * This is the single point of exit for closing a tag — both stacks are\n * updated together.\n *\n * @param {object} [closeMeta] - Position info for the closing tag:\n * { name, index, closeEnd }. Omitted when there is no real\n * closing tag to report a position for — e.g. AutoCloseHandler synthesizing\n * a close at EOF, or exitIf closing already-open ancestors. In that case a\n * minimal `{ name }` is passed to the builder instead of nothing, so\n * closeElement() never has to special-case \"no second argument at all\".\n */\n popTag(closeMeta) {\n this.outputBuilder.closeElement(this.readonlyMatcher, closeMeta ?? { name: this.currentTagDetail?.name });\n this.matcher.pop();\n this.currentTagDetail = this.tagsStack.pop();\n }\n\n /**\n * Build a closeMeta object for tags with no distinct closing token\n * (unpaired tags like <br>, and self-closing tags like <tag/>) — the close\n * position is just the opening tag's own end.\n * @param {TagDetail} tagDetail\n */\n _closeMetaFor(tagDetail) {\n return {\n name: tagDetail.name,\n index: tagDetail.index,\n closeEnd: tagDetail.openEnd,\n };\n }\n\n readSpecialTag(startCh) {\n if (startCh === \"!\") {\n let nextChar = this.source.readCh();\n if (nextChar === null || nextChar === undefined) throw new ParseError(\"Unexpected end of source after '<!'\", ErrorCode.UNEXPECTED_END, errorPositionOf(this.source));\n\n if (nextChar === \"-\") {\n readComment(this);\n } else if (nextChar === \"[\") {\n readCdata(this);\n } else if (nextChar === \"D\") {\n // DOCTYPE is always read to consume its content and advance the cursor.\n // Entities are forwarded to the output builder only when doctypeOptions.enabled is true.\n const docTypeEntities = readDocType(this);\n if (this.doctypeFound) {\n throw new ParseError(\"Multiple DOCTYPE declarations found.\", ErrorCode.INVALID_INPUT, errorPositionOf(this.source));\n }\n this.doctypeFound = true;\n if (this.options.doctypeOptions.enabled &&\n docTypeEntities &&\n Object.keys(docTypeEntities).length > 0) {\n this.outputBuilder.addInputEntities(docTypeEntities);\n }\n }\n } else {\n throw new ParseError(`Invalid tag '<${startCh}'`, ErrorCode.INVALID_TAG, errorPositionOf(this.source));\n }\n }\n\n addTextNode() {\n if (this.tagTextData !== undefined && this.tagTextData !== \"\") {\n // Line-ending normalization + illegal-control-character rejection,\n // applied once per complete text run (never mid-chunk — see util.js).\n this.tagTextData = sanitizeContent(this.tagTextData, this.source);\n // Pass raw text — entity expansion is handled by 'entities' ValueParser in the chain\n if (!this.options.skip.whitespaceText || this.tagTextData.trim().length > 0) {\n this.outputBuilder.addValue(this.tagTextData, this.readonlyMatcher);\n }\n this.tagTextData = \"\";\n }\n }\n\n /**\n * Cached wrapper around getNameValidator('qName') — shape-validating a tag\n * name is a pure function of the name string alone (for a fixed xmlVersion,\n * itself fixed for the whole document), so a name seen once and found valid\n * never needs the regex re-run for its later occurrences. Only opening tag\n * names go through this (see buildTagExpObj in XmlPartReader.js); closing\n * tags are checked by exact-match against the already-validated opening\n * name instead (readClosingTag), so they never need shape validation of\n * their own.\n *\n * Only valid names are cached, matching processTagName/processAttrName's\n * existing convention — an invalid name throws every time it's seen, never\n * silently let through after a first failure.\n */\n isValidQName(name) {\n const cache = this._validQNames;\n if (cache.has(name)) return true;\n const ok = this.getNameValidator('qName')(name);\n if (ok) {\n if (cache.size >= NAME_CACHE_LIMIT) cache.clear();\n cache.add(name);\n }\n return ok;\n }\n\n /**\n * Returns a memoized xml-naming validator for the given production\n * ('qName' for tag/attribute names, 'name' for DOCTYPE entity/element/\n * notation names), built lazily on first use and cached per parser\n * instance for the rest of the document/session.\n *\n * xmlDec.version is stored as a number (1.0 / 1.1) but xml-naming's\n * xmlVersion option is the string '1.0'/'1.1' — normalized here rather\n * than changing xmlDec's public shape (it's forwarded as-is to\n * outputBuilder.addDeclaration(), so its type is part of the builder\n * contract, not just an internal detail).\n *\n * @param {'name'|'qName'} production\n */\n getNameValidator(production) {\n let validator = this._nameValidators[production];\n if (!validator) {\n const xmlVersion = this.xmlDec.version === 1.1 || this.xmlDec.version === '1.1' ? '1.1' : '1.0';\n validator = createValidator(production, { xmlVersion });\n this._nameValidators[production] = validator;\n }\n return validator;\n }\n\n processAttrName(rawAttrName) {\n return getCachedName(this._nameCache.attrs, rawAttrName, () => {\n const options = this.options;\n let attrName = resolveNsPrefix(rawAttrName, options.skip.nsPrefix);\n if (!this.getNameValidator('qName')(attrName)) { //TODO: make it optional\n throw new ParseError(`Invalid attribute name: ${attrName}`, ErrorCode.INVALID_ATTRIBUTE_NAME, errorPositionOf(this.source));\n }\n attrName = sanitizeName(attrName, options.onDangerousProperty, options.sanitizeNames, this.source);\n if (options.strictReservedNames && attrName === options.attributes.groupBy) {\n throw new ParseError(`Restricted attribute name: ${attrName}`, ErrorCode.SECURITY_RESTRICTED_NAME, errorPositionOf(this.source));\n }\n return attrName;\n });\n }\n\n processTagName(rawTagName) {\n return getCachedName(this._nameCache.tags, rawTagName, () => {\n const options = this.options;\n const nameFor = options.nameFor;\n let tagName = resolveNsPrefix(rawTagName, options.skip.nsPrefix);\n tagName = sanitizeName(tagName, options.onDangerousProperty, options.sanitizeNames, this.source);\n if (options.strictReservedNames && (\n tagName === nameFor.comment ||\n tagName === nameFor.cdata ||\n tagName === nameFor.text\n )) {\n throw new ParseError(`Restricted tag name: ${tagName}`, ErrorCode.SECURITY_RESTRICTED_NAME, errorPositionOf(this.source));\n }\n return tagName;\n });\n }\n\n isUnpaired(tagName) {\n return this._unpairedSet.has(tagName);\n }\n\n /**\n * Snapshot of mutable parser state passed to AutoCloseHandler.\n * Returns a live object — properties read from it reflect current state.\n */\n _parserState() {\n const self = this;\n return {\n get tagsStack() { return self.tagsStack; },\n get currentTagDetail() { return self.currentTagDetail; },\n set currentTagDetail(v) { self.currentTagDetail = v; },\n get outputBuilder() { return self.outputBuilder; },\n get readonlyMatcher() { return self.readonlyMatcher; },\n get matcher() { return self.matcher; },\n get source() { return self.source; },\n get tagTextData() { return self.tagTextData; },\n set tagTextData(v) { self.tagTextData = v; },\n addTextNode: self.addTextNode.bind(self),\n popTag: self.popTag.bind(self),\n };\n }\n}\n\nfunction resolveNsPrefix(name, skipNsPrefix) {\n if (skipNsPrefix) {\n const parts = name.split(':');\n if (parts.length === 2) {\n if (parts[0] === 'xmlns') return false; // drop xmlns declarations\n return parts[1];\n } else if (parts.length > 2) {\n throw new ParseError(`Multiple namespaces in name: ${name}`, ErrorCode.MULTIPLE_NAMESPACES, errorPositionOf(this.source));\n }\n }\n return name;\n}\n\nfunction sanitizeName(name, onDangerousProperty, sanitizeNames, source) {\n // criticalProperties (__proto__, constructor, prototype) guard against an\n // actual prototype-pollution vulnerability in the output object, not just\n // a naming collision — this check always runs, `sanitizeNames` cannot\n // turn it off. Only the DANGEROUS_PROPERTY_NAMES rename step (a milder,\n // cosmetic shadowing concern) is skippable for trusted input.\n if (criticalProperties.includes(name)) {\n throw new ParseError(`[SECURITY] Invalid name: \"${name}\" is a reserved JavaScript keyword that could cause prototype pollution`, ErrorCode.SECURITY_PROTOTYPE_POLLUTION, errorPositionOf(source));\n }\n if (sanitizeNames === false) return name;\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return onDangerousProperty(name);\n }\n return name;\n}\n\n/**\n * Returns true for errors thrown by read functions when the source ran out\n * mid-token — i.e. the document was truncated inside a tag.\n * These are the only errors we intercept for autoClose recovery.\n * Syntax errors (unclosed quotes) are NOT intercepted — they rethrow.\n */\nfunction isSourceExhaustedError(err) {\n // Accept both ParseError (with codes) and plain Error from lower-level readers\n if (err instanceof ParseError) {\n return err.code === ErrorCode.UNEXPECTED_END;\n }\n return (\n err.message.startsWith('Unexpected end of source') ||\n err.message.startsWith('Unexpected closing of source')\n );\n}\n\n/**\n * Returns the matched stop-node config `{ nested, skipEnclosures }` (from Expression.data)\n * if the current matcher position matches any stop-node expression, or `null` if not.\n * Uses ExpressionSet.findMatch() for O(1) indexed lookup.\n */\nfunction isStopNode() {\n if (this.stopNodeExpressionsSet.size === 0) return null;\n const matched = this.stopNodeExpressionsSet.findMatch(this.matcher);\n return matched ? matched.data : null;\n}\n\n/**\n * Returns the matched skip-tag config `{ nested, skipEnclosures }` (from Expression.data)\n * if the current matcher position matches any skip.tags expression, or `null` if not.\n * Uses ExpressionSet.findMatch() for O(1) indexed lookup.\n */\nfunction isSkipTag() {\n if (this.skipTagExpressionsSet.size === 0) return null;\n const matched = this.skipTagExpressionsSet.findMatch(this.matcher);\n return matched ? matched.data : null;\n}","import { createByteScanStrategy, decodeCharAtFixedWidth1, decodeCharAtUtf8 } from './ScanStrategy/ByteScanStrategy.js';\nimport { createCharScanStrategy } from './ScanStrategy/CharScanStrategy.js';\nimport { sniff } from './EncodingDetector.js';\nimport { defaultEncodingRegistry } from './EncodingRegistry.js';\n\n/**\n * buildProfileForBuffer(bytes, decodingOptions, registry) -> { descriptor, bomLength, scanStrategy }\n *\n * The ONE place encoding decisions are made for BufferSource. Called once\n * per parseBytesArr() call, never per-token — every field on the returned\n * object is a concrete, already-resolved strategy, so BufferSource itself\n * never branches on an encoding name again (Dependency Inversion: BufferSource\n * depends on the ScanStrategy interface, not on \"which encoding is this\").\n */\nexport function buildProfileForBuffer(bytes, decodingOptions = {}, registry = defaultEncodingRegistry) {\n const requested = decodingOptions.encoding || 'auto';\n let name, bomLength;\n if (requested === 'auto') {\n const detected = sniff(bytes, registry);\n name = detected.encoding;\n bomLength = detected.bomLength;\n } else {\n name = requested;\n bomLength = 0;\n }\n const descriptor = registry.resolve(name);\n const scanStrategy = descriptor.selfSynchronizing\n ? createByteScanStrategy(\n descriptor.name === 'utf8' ? decodeCharAtUtf8 : decodeCharAtFixedWidth1,\n descriptor.name // 'utf8'/'ascii'/'latin1' — all valid Buffer#toString() encodings\n )\n : createCharScanStrategy();\n\n // scanTagExpEnd() records quote positions as offsets into the *raw\n // buffer* it's scanning. AttributeProcessor.parseAttributes() wants to\n // reuse those offsets directly as indices into the *decoded* attrStr\n // string it receives from readStr(). Those two only line up when one\n // buffer unit == one decoded character:\n // - CharScanStrategy: always safe — it scans the already-decoded string,\n // same string readStr() hands back.\n // - ByteScanStrategy + fixed-width decode (ascii/latin1, and any custom\n // self-synchronizing single-byte encoding): safe — 1 byte == 1 char.\n // - ByteScanStrategy + utf8: NOT safe — non-ASCII characters are 2-4\n // bytes each, so a byte offset recorded mid-scan can land in the\n // middle of a character once decoded. AttributeProcessor falls back to\n // its own quote scan in this case (see `quotePairsUsable`).\n const quotePairsUsable = !descriptor.selfSynchronizing || descriptor.name !== 'utf8';\n\n return { descriptor, bomLength, scanStrategy, decodeFirst: !descriptor.selfSynchronizing, quotePairsUsable };\n}\n\n/**\n * buildDecoderForStream(decodingOptions, registry) -> descriptor's stateful\n * decoder, for FeedableSource/StreamSource. These two are already decode-\n * first architecturally (see CharScanStrategy's doc comment) so they only\n * ever need the decoder half of a profile, never a scan strategy.\n *\n * Streaming auto-detection (peeking enough of the first feed() chunk before\n * a decoder can even be constructed) is NOT implemented in this pass — see\n * the companion doc's \"Known follow-ups\" section. Until then, 'auto' on a\n * streaming source falls back to utf8, same as today's hardcoded behavior.\n */\nexport function buildDecoderForStream(decodingOptions = {}, registry = defaultEncodingRegistry) {\n const requested = decodingOptions.encoding && decodingOptions.encoding !== 'auto'\n ? decodingOptions.encoding\n : 'utf8';\n return registry.resolve(requested).createDecoder();\n}\n","import { ParseError, ErrorCode } from '../../ParseError.js';\nimport { isSpace } from '../../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from '../../InputSource/scanTagExpEnd.js';\n\n/**\n * CharScanStrategy — for encodings that are NOT self-synchronizing (UTF-16\n * LE/BE by default, or any custom multi-byte encoding that doesn't assert\n * `selfSynchronizing: true`). Byte-level delimiter scanning is unsafe for\n * these (an ASCII delimiter byte value can legitimately occur as part of a\n * different character), so the only correct option is to decode fully up\n * front and scan on the resulting JS string — exactly what StringSource\n * already does. This is that same algorithm, extracted so BufferSource can\n * reuse it verbatim instead of re-deriving a second copy.\n *\n * Cost model: one eager decode of the whole buffer at construction (not\n * per-token, not per-chunk). Only paid by documents that actually use one of\n * these encodings; the default UTF-8/ASCII/Latin-1 path never touches this\n * file.\n */\nexport function createCharScanStrategy() {\n return {\n readCh() {\n return this.buffer[this.startIndex++];\n },\n\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n },\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n },\n\n /** See StringSource.js for the full doc — identical contract, same\n * plain-string buffer shape. */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n },\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Always character-indexed (scans an already-\n // decoded string), so quote offsets are always safe for AttributeProcessor\n // to reuse. See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd,\n scanTagExpEndFast,\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n },\n\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n let state = 0;\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; }\n } else {\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) { state = 1; tagMatchStart = i; i += stopLength - 1; }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readFromBuffer(n, shouldUpdate) {\n const ch = n === 1\n ? this.buffer[this.startIndex]\n : this.buffer.substring(this.startIndex, this.startIndex + n);\n if (shouldUpdate) this.updateBufferBoundary(n);\n return ch;\n },\n\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n if (this.autoFlush && this.startIndex >= this.flushThreshold && this._tokenStart < 0) {\n this.flush();\n }\n },\n\n // Relative to current position, matching FeedableSource's formula.\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n },\n };\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { isSpace, QUOTE_PAIRS_CAPACITY } from '../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from './scanTagExpEnd.js';\nimport { createTextDecoderAdapter } from '../Encoding/TextDecoderAdapter.js';\nimport { sniff } from '../Encoding/EncodingDetector.js';\n\n// Matches EncodingDetector's own declaration-peek window — bounds how much\n// raw (undecoded) data 'auto' mode ever holds before giving up and resolving\n// on whatever it has (a document with no BOM and no <?xml?> declaration is\n// legitimately using the utf8 default, not something to keep waiting on).\nconst SNIFF_CAP = 200;\n\n/**\n * FeedableSource — input source for the feed()/end() API.\n *\n * Accepts incremental string/Buffer chunks via feed(), accumulates them in a\n * single string buffer, and exposes the same read interface as StringSource so\n * Xml2JsParser can use it without modification.\n *\n * ### Incremental parsing\n *\n * The parser calls parseXml() after every feed() call, consuming as much of\n * the buffer as possible. When a chunk boundary falls mid-token (e.g. a CDATA\n * section split across two feeds), every reader function marks its start\n * position with markTokenStart() before it begins. If the reader throws\n * UNEXPECTED_END, the caller (XMLParser.feed) catches it and calls\n * rewindToMark() to restore startIndex to the beginning of the incomplete\n * token. The incomplete bytes stay in the buffer and are re-parsed on the\n * next feed() once the rest of the token has arrived.\n *\n * ### Two-level mark stack\n *\n * There are two mark levels:\n *\n * Level 0 — outer mark, set by parseXml()'s main loop BEFORE it reads the\n * '<' character that begins a tag dispatch. This is the position\n * that rewindToMark() always restores to, so the full tag (including\n * its '<![', '</', etc. prefix) is replayed correctly on the next\n * feed().\n *\n * Level 1 — inner mark, set by individual reader functions (readCdata,\n * readClosingTagName, readTagExp, …) at the point where *they*\n * begin. This does NOT affect rewindToMark(); it is used only by\n * flush() to determine the safe trim boundary while a reader is\n * in progress.\n *\n * Using two levels instead of a single slot prevents inner markTokenStart()\n * calls from overwriting the outer mark that feed() needs to rewind to.\n *\n * ### Memory\n *\n * Parsed data is reclaimed from the buffer automatically (autoFlush) once the\n * processed portion exceeds flushThreshold bytes. Because parseXml() runs per\n * chunk and completed tokens are consumed before the next chunk arrives, only\n * incomplete tokens at the current chunk boundary are retained — not the whole\n * document.\n *\n * maxBufferSize is checked against the live (unprocessed) portion of the\n * buffer plus the incoming chunk, not the raw buffer.length, so post-flush\n * sizing stays accurate.\n */\nexport default class FeedableSource {\n constructor(options = {}) {\n this.buffer = '';\n this.startIndex = 0;\n this.isComplete = false;\n // Running total of characters trimmed off the front by flush() so far.\n // See StringSource.js's copy of this field / util.js#absolutePosition —\n // startIndex alone drifts from the true document offset after any flush.\n this._baseOffset = 0;\n\n this.maxBufferSize = options.maxBufferSize || 10 * 1024 * 1024; // 10 MB\n this.autoFlush = options.autoFlush !== false; // true by default\n this.flushThreshold = options.flushThreshold || 1024; // 1 KB\n\n // Encoding resolution. Three modes:\n // - explicit name (options.decoding.encoding, e.g. 'utf16le'): resolve\n // a decoder immediately via the registry.\n // - 'auto': can't build a decoder yet — not enough bytes seen. Buffer\n // raw (undecoded) bytes in `_sniffBuffer` until either a BOM+enough\n // bytes, a complete `<?xml ... ?>` declaration, or SNIFF_CAP bytes\n // have accumulated, then resolve once via _resolveDetection() and\n // replay the held bytes through the real decoder. See feed() below.\n // - neither supplied (direct FeedableSource construction, bypassing\n // XMLParser): falls back to a caller-supplied `options.createDecoder`\n // if given, else plain utf8 — identical to this class's behavior\n // before this feature existed.\n this._decodingOptions = options.decoding || null;\n const requestedEncoding = this._decodingOptions?.encoding;\n this._detecting = requestedEncoding === 'auto';\n this._sniffBuffer = this._detecting ? Buffer.alloc(0) : null;\n if (!this._detecting && requestedEncoding && this._decodingOptions.registry) {\n const registry = this._decodingOptions.registry;\n this._createDecoder = () => registry.resolve(requestedEncoding).createDecoder();\n } else {\n this._createDecoder = typeof options.createDecoder === 'function' ? options.createDecoder : null;\n }\n\n /**\n * Two-level mark stack.\n *\n * _marks[0] — outer mark: set by parseXml()'s loop before consuming '<'.\n * rewindToMark() always restores startIndex here.\n * _marks[1] — inner mark: set by individual reader functions.\n * Used only by flush() as the safe trim boundary.\n *\n * `null` means \"not set\" for that level. Each entry is a plain startIndex\n * number — no line/col to carry alongside it, since position reporting\n * is index-only.\n */\n this._marks = [null, null];\n\n /**\n * Lazily-created, persistent across the whole feed() session. Buffer\n * chunks must go through this rather than Buffer#toString() per chunk —\n * toString() decodes each chunk in isolation, so a multi-byte UTF-8\n * character whose bytes straddle a chunk boundary gets corrupted (each\n * half independently replaced with U+FFFD). The decoder holds back an\n * incomplete trailing sequence internally and prepends it to the next\n * write(), so a split character decodes correctly once the rest of its\n * bytes arrive. Only created if Buffer input is ever fed — string-only\n * callers never pay for it.\n */\n this._decoder = null;\n\n // Reused across every scanTagExpEnd() call, never reallocated. See\n // StringSource.js's copy of this field for the full doc (fixed-capacity\n // typed array + manual length, not push()). Safe across a\n // chunk-boundary rewind: the failed scan's contents are irrelevant the\n // moment the tag is re-scanned from scratch on the next feed().\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n }\n\n /**\n * Append a data chunk to the buffer.\n *\n * maxBufferSize is checked against the live unprocessed portion\n * (buffer.length - startIndex) plus the incoming data length. Data that has\n * already been parsed and is waiting to be flushed does not count against\n * the limit.\n *\n * @param {string|Buffer} data\n * @returns {number} number of characters appended to the buffer (after\n * decoding) — callers that track fed-byte totals (e.g. XMLParser.feed's\n * batch threshold) should use this rather than the raw input length,\n * since a Buffer chunk ending mid-character may decode to fewer chars\n * than its byte length until the next chunk completes the sequence.\n */\n feed(data) {\n if (this._detecting) {\n if (typeof data === 'string') {\n // Already decoded upstream (e.g. stream.setEncoding() was called by\n // the caller) — detection is moot, nothing left to sniff.\n this._detecting = false;\n } else {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);\n this._sniffBuffer = this._sniffBuffer.length ? Buffer.concat([this._sniffBuffer, chunk]) : chunk;\n const declarationComplete = this._sniffBuffer.includes(Buffer.from('?>'));\n if (this._sniffBuffer.length < SNIFF_CAP && !declarationComplete) {\n // Not enough to decide yet — hold everything, decode nothing.\n return 0;\n }\n data = this._resolveDetection();\n }\n }\n\n let newData = this._decodeNow(data);\n\n const liveBytes = this.buffer.length - this.startIndex;\n\n if (liveBytes + newData.length > this.maxBufferSize) {\n throw new ParseError(\n `Buffer size limit exceeded (${liveBytes + newData.length} > ${this.maxBufferSize}). ` +\n `Increase feedable.maxBufferSize or reduce chunk size.`,\n ErrorCode.INVALID_INPUT\n );\n }\n\n this.buffer += newData;\n return newData.length;\n }\n\n /** @private */\n _decodeNow(data) {\n if (typeof data === 'string') return data;\n if (Buffer.isBuffer(data)) {\n // Stateful decode: bytes of a multi-byte char split across two feed()\n // calls are buffered internally by the decoder and correctly stitched\n // together, instead of each chunk being decoded in isolation.\n if (!this._decoder) this._decoder = this._createDecoder ? this._createDecoder() : createTextDecoderAdapter('utf-8');\n return this._decoder.write(data);\n }\n if (data?.toString) return data.toString();\n throw new ParseError('feed() data must be a string or Buffer.', ErrorCode.DATA_MUST_BE_STRING);\n }\n\n /**\n * Resolve 'auto' encoding from `_sniffBuffer` (BOM + `<?xml encoding=\"...\">`\n * sniffing, XML 1.0 Appendix F — see Encoding/EncodingDetector.js), build\n * the real decoder, strip any BOM, and return the held bytes ready to be\n * decoded normally by the caller in feed(). Runs exactly once per session.\n * @private\n * @returns {Buffer}\n */\n _resolveDetection() {\n const registry = this._decodingOptions.registry;\n const { encoding, bomLength } = sniff(this._sniffBuffer, registry);\n const descriptor = registry.resolve(encoding);\n this._createDecoder = () => descriptor.createDecoder();\n this._detecting = false;\n const held = bomLength ? this._sniffBuffer.subarray(bomLength) : this._sniffBuffer;\n this._sniffBuffer = null;\n return held;\n }\n\n /** Signal that no more data will be fed. */\n end() {\n if (this._detecting) {\n // Whole document arrived without ever reaching SNIFF_CAP or a\n // complete declaration (a short, unadorned document like <root/>) —\n // resolve now, on whatever bytes we have.\n const held = this._resolveDetection();\n this.buffer += this._decodeNow(held);\n }\n if (this._decoder) {\n // Flush any final incomplete byte sequence held by the decoder. For\n // well-formed UTF-8 input this is normally '' (nothing pending); a\n // non-empty result here means the input was genuinely truncated\n // mid-character, and the decoder's own U+FFFD substitution is the\n // correct, standard behavior for that case.\n const tail = this._decoder.end();\n if (tail) this.buffer += tail;\n }\n this.isComplete = true;\n }\n\n /**\n * Returns true when there is at least one character available at or after\n * the given offset (relative to startIndex).\n * @param {number} [n=0]\n */\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n }\n\n // ─── Two-level mark API ───────────────────────────────────────────────────\n\n /**\n * Save the current read position into the mark stack.\n *\n * The `level` parameter selects which mark slot to write:\n *\n * level 0 (default) — outer mark, written by parseXml()'s main loop\n * before it reads the '<' that begins a dispatch.\n * level 1 — inner mark, written by reader functions\n * (readCdata, readClosingTagName, readTagExp, …)\n * at the start of their own logic.\n *\n * The two levels are independent. An inner markTokenStart(1) never\n * overwrites the outer mark[0] that rewindToMark() relies on.\n *\n * @param {0|1} [level=0]\n */\n markTokenStart(level = 0) {\n this._marks[level] = this.startIndex;\n }\n\n /**\n * Restore startIndex to the OUTER mark (level 0) and clear both marks.\n *\n * Always rewinds to the outermost saved position so the full tag —\n * including any prefix characters consumed by parseXml() before the\n * dispatch (e.g. '<', '!', '[') — is replayed on the next feed().\n *\n * Called by XMLParser.feed() when a reader throws UNEXPECTED_END.\n */\n rewindToMark() {\n if (this._marks[0] !== null) {\n this.startIndex = this._marks[0];\n }\n this._marks[0] = null;\n this._marks[1] = null;\n }\n\n /**\n * Clear both mark slots after a token completes successfully.\n *\n * Should be called (or marks allowed to be overwritten) once a dispatch\n * fully succeeds so stale positions don't block flush().\n *\n * In practice the outer mark is overwritten at the top of every\n * parseXml() loop iteration, so explicit clearing is only needed when\n * the loop does NOT continue (e.g. after a non-'<' character is consumed\n * as plain text). The flush guard uses the minimum of set marks, so a\n * stale mark only delays flushing — it does not cause correctness issues.\n */\n clearMark() {\n this._marks[0] = null;\n this._marks[1] = null;\n }\n\n /**\n * Read next character and advance position.\n * @returns {string}\n */\n readCh() {\n return this.buffer[this.startIndex++];\n }\n\n /**\n * Read character at offset without advancing.\n * @param {number} index - Offset from current position\n * @returns {string}\n */\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n }\n\n /**\n * Read n characters as string.\n * @param {number} n - Number of characters to read\n * @param {number} from - Start position (default: current position)\n * @returns {string}\n */\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n }\n\n /**\n * See StringSource.js's copy of this method for the full doc — identical\n * contract here. `null` (not enough buffered data yet) is the routine\n * case for this source in particular, since a chunk boundary can land\n * mid-check; callers already have to handle that the same way they\n * handle scanTagExpEnd's -1.\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n }\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Bracket access (not charCodeAt) is load-bearing\n // here: this buffer is a V8 ConsString/rope built via repeated +=, and\n // charCodeAt would force a full flatten on every access (confirmed O(n^2)\n // memory regression). The shared implementations use bracket access throughout.\n // See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd = scanTagExpEnd;\n scanTagExpEndFast = scanTagExpEndFast;\n\n /**\n * Read until stop string is found.\n * @param {string} stopStr\n * @returns {string} content before the stop string (stop string is consumed)\n * @throws {ParseError} UNEXPECTED_END when stop string is not found\n */\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Single-character variant of readUpto — faster because there is no inner\n * match loop. Reads until `stopChar` is found, consumes it, and returns\n * the text before it.\n *\n * @param {string} stopChar Exactly one character.\n * @returns {string}\n */\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n }\n\n /**\n * Read until a closing tag is found (used for stop nodes).\n * @param {string} stopStr e.g. `\"</tagname\"`\n * @returns {string} raw content between the current position and the closing tag\n * @throws {ParseError} UNEXPECTED_END when the closing tag is not found\n */\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n let state = 0; // 0=scanning, 1=tag-name matched (scanning for '>'), 2=full match\n\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; } // false match e.g. </scriptX>\n } else {\n // Try to match stopStr at position i\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) {\n state = 1;\n tagMatchStart = i;\n i += stopLength - 1; // skip past matched string\n }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Advance the read cursor by n characters.\n *\n * Triggers an automatic flush of already-processed data when autoFlush is\n * enabled, the processed portion has grown past flushThreshold, and no\n * mark is currently active. Any active mark (either level) blocks the\n * flush to prevent the saved position from becoming invalid.\n *\n * @param {number} [n=1]\n */\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n // No \"any mark active\" gate here — flush()'s own min(startIndex, marks...)\n // origin computation already guarantees any in-progress token (at either\n // mark level) survives the trim. A separate boolean gate on top of that\n // was redundant, and since _marks[0] is set on every parseXml() loop\n // iteration and never nulled outside of rewindToMark() (an error path),\n // that gate was effectively permanent — flush() never ran in normal\n // operation. See specs/flushArchitecture_spec.js for the regression test.\n if (this.autoFlush && this.startIndex >= this.flushThreshold) {\n this.flush();\n }\n }\n\n /**\n * Discard already-processed data from the front of the buffer to free memory.\n * startIndex is reset to 0 after the trim.\n *\n * The flush origin is the minimum of all active mark positions, so that any\n * in-progress token (at either mark level) is preserved in the buffer and\n * can be re-read after the flush. This is the sole safety mechanism for\n * flush() — callers do not need to additionally check \"is a mark active\"\n * before calling this; an active mark simply caps how much origin can\n * advance, rather than blocking the call outright.\n *\n * If no marks are active, the origin is startIndex itself — everything\n * before the current read position is discarded.\n */\n flush() {\n // Determine the earliest position that must be kept.\n let origin = this.startIndex;\n for (const m of this._marks) {\n if (m !== null && m < origin) origin = m;\n }\n\n if (origin > 0) {\n this.buffer = this.buffer.substring(origin);\n\n // Adjust all mark offsets by the amount trimmed.\n const marksLen = this._marks.length;\n for (let i = 0; i < marksLen; i++) {\n if (this._marks[i] !== null) this._marks[i] -= origin;\n }\n\n this.startIndex -= origin;\n this._baseOffset += origin;\n }\n }\n}\n","import FeedableSource from './FeedableSource.js';\n\n/**\n * StreamSource — input source that reads from a Node.js Readable stream.\n *\n * Extends FeedableSource so it shares the same buffer management and read\n * interface. attachStream() wires Node.js stream events. On each 'data'\n * event the chunk is appended to the buffer and onChunk is called so the\n * caller can run parseXml() incrementally. Parsing is therefore driven\n * chunk-by-chunk rather than once over the full accumulated document.\n */\nexport default class StreamSource extends FeedableSource {\n /**\n * Wire a Readable stream to this source.\n *\n * @param {NodeJS.ReadableStream} readable\n * @param {function(Error|null):void} onChunk\n * Called after each successful feed() with null, or immediately with the\n * feed error if the buffer limit is exceeded. The caller runs parseXml()\n * inside this callback and handles UNEXPECTED_END (chunk boundary mid-token)\n * by calling rewindToMark().\n * @param {function():void} onEnd\n * Called when the stream ends cleanly. The caller should finalise the parse\n * (finalizeXml) here.\n * @param {function(Error):void} onError\n * Called with any stream-level error (e.g. 'error' event from the readable).\n */\n attachStream(readable, onChunk, onEnd, onError) {\n readable.on('data', chunk => {\n try {\n // Pass the raw chunk (Buffer or string) straight through — feed()\n // decodes Buffers via a persistent stateful decoder so a multi-byte\n // UTF-8 character split across two chunks decodes correctly instead\n // of each half being independently mangled by a per-chunk toString().\n this.feed(chunk);\n onChunk(null); // chunk appended successfully — caller runs parseXml()\n } catch (err) {\n onChunk(err); // buffer overflow or coercion failure\n }\n });\n\n readable.on('error', onError);\n\n readable.on('end', () => {\n try {\n this.end();\n onEnd();\n } catch (err) {\n onError(err);\n }\n });\n }\n}","import { buildOptions } from './OptionsBuilder.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { absolutePosition } from './util.js';\nimport Xml2JsParser from './Xml2JsParser.js';\nimport FeedableSource from './InputSource/FeedableSource.js';\nimport StreamSource from './InputSource/StreamSource.js';\nimport EncodingRegistry, { defaultEncodingRegistry } from './Encoding/EncodingRegistry.js';\n\nexport default class XMLParser {\n\n constructor(options) {\n this.options = buildOptions(options);\n\n // feed()/end() session state\n this._feedParser = null;\n this._feedSource = null;\n this._isFeeding = false;\n\n // ── Batching state ──────────────────────────────────\n this._pendingBytes = 0;\n this._batchThreshold = this.options.feedable?.bufferSize;\n\n // Per-instance encoding registry only when custom decoders are supplied\n // — avoids mutating the shared default registry (which would leak a\n // customDecoder registered on one XMLParser instance into every other\n // instance in the process). The common case (no customDecoders) reuses\n // the shared default registry, seeded once at module load.\n if (this.options.decoding?.customDecoders) {\n const registry = new EncodingRegistry();\n for (const [name, descriptor] of Object.entries(this.options.decoding.customDecoders)) {\n registry.register({ name, ...descriptor });\n }\n this.options.decoding._registry = registry;\n } else {\n this.options.decoding = this.options.decoding || {};\n this.options.decoding._registry = defaultEncodingRegistry;\n }\n\n // Shared tag/attribute name cache — lives on `options`, not on any one\n // Xml2JsParser instance, because `.parse()` creates a fresh Xml2JsParser\n // every call while `this.options` is passed by reference to all of them.\n // This lets repeated names skip re-validation/re-sanitization across\n // separate parse() calls on the same XMLParser instance, not just within\n // one document. See Xml2JsParser.js for what's cached and why.\n this.options._nameCache = { tags: new Map(), attrs: new Map() };\n }\n\n // ─── One-shot parse methods ───────────────────────────────────────────────\n\n /**\n * Parse an XML string or Buffer and return a JS object.\n * @param {string|Buffer} xmlData\n */\n parse(xmlData) {\n if (xmlData instanceof Buffer || ArrayBuffer.isView(xmlData)) {\n // Route through the encoding-aware path (auto-detect / configured\n // `decoding.encoding`) instead of an unconditional utf8 toString() —\n // otherwise a non-utf8 `decoding.encoding` option would silently be\n // ignored for Buffer input given directly to parse().\n return this.parseBytesArr(xmlData);\n } else if (typeof xmlData !== 'string') {\n if (xmlData && typeof xmlData.toString === 'function') {\n xmlData = xmlData.toString();\n } else {\n throw new ParseError('XML data must be a string or Buffer.', ErrorCode.INVALID_INPUT);\n }\n }\n\n const parser = this._createParser();\n const result = parser.parse(xmlData);\n this.wasExited = parser.wasExited();\n this._lastParseErrors = parser.autoCloseHandler?.getErrors() ?? [];\n return result;\n }\n\n /**\n * Parse a Uint8Array / byte array and return a JS object.\n * @param {Uint8Array|ArrayBufferView} xmlData\n */\n parseBytesArr(xmlData) {\n if (xmlData instanceof Uint8Array || ArrayBuffer.isView(xmlData)) {\n xmlData = Buffer.from(xmlData);\n } else {\n throw new ParseError('XML data must be a Uint8Array or ArrayBufferView.', ErrorCode.INVALID_INPUT);\n }\n\n const parser = this._createParser();\n const result = parser.parseBytesArr(xmlData);\n this.wasExited = parser.wasExited();\n this._lastParseErrors = parser.autoCloseHandler?.getErrors() ?? [];\n return result;\n }\n\n // ─── Stream input ─────────────────────────────────────────────────────────\n\n /**\n * Parse an XML Node.js Readable stream and return a Promise that resolves\n * with the parsed JS object.\n *\n * Chunks are processed incrementally as they arrive — parseXml() runs after\n * each 'data' event and already-consumed input is freed before the next\n * chunk arrives, so memory stays proportional to the largest incomplete token\n * at any chunk boundary rather than the total document size.\n *\n * @param {NodeJS.ReadableStream} readable\n * @returns {Promise<any>}\n */\n parseStream(readable) {\n if (!isReadableStream(readable)) {\n throw new ParseError('parseStream() requires a Node.js Readable stream.', ErrorCode.INVALID_STREAM);\n }\n\n const source = new StreamSource({\n ...this.options.feedable,\n decoding: { encoding: this.options.decoding.encoding, registry: this.options.decoding._registry },\n });\n const streamParser = this._createParser();\n streamParser.source = source;\n streamParser.initializeParser();\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const fail = (err) => {\n if (!settled) {\n settled = true;\n readable.destroy(); // stop further data/end events and free the handle\n reject(err);\n }\n };\n\n source.attachStream(\n readable,\n // onChunk — run the parser incrementally after each chunk arrives.\n // Mirrors what feed() does: advance as far as possible, rewind on\n // UNEXPECTED_END (chunk boundary mid-token), re-throw real errors.\n (err) => {\n if (err) { fail(err); return; }\n try {\n streamParser.parseXml();\n } catch (parseErr) {\n if (parseErr.code === ErrorCode.UNEXPECTED_END) {\n source.rewindToMark();\n } else {\n fail(parseErr);\n }\n }\n },\n // onEnd — stream finished cleanly; finalise the document.\n () => {\n if (settled) return;\n try {\n // source.end() (called by attachStream just before this) can\n // release content that was held back pending encoding detection\n // (see FeedableSource's 'auto' mode) — run parseXml() once more\n // to consume it before finalizing. No-op if there's nothing new.\n streamParser.parseXml();\n streamParser.finalizeXml();\n this._lastParseErrors = streamParser.autoCloseHandler?.getErrors() ?? [];\n settled = true;\n resolve(streamParser.outputBuilder.getOutput());\n } catch (err) { fail(err); }\n },\n // onError — stream-level error (e.g. file not found, network drop)\n fail,\n );\n });\n }\n\n // ─── Incremental feed()/end() API ────────────────────────────────────────\n\n _runParse() {\n if (!this._feedParser) return;\n\n const beforePos = absolutePosition(this._feedSource); // bytes consumed so far, flush-proof\n\n try {\n this._feedParser.parseXml();\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n this._feedSource.rewindToMark();\n } else {\n throw err;\n }\n }\n\n const afterPos = absolutePosition(this._feedSource);\n const didAdvance = afterPos > beforePos;\n\n if (didAdvance) {\n // Real progress made — reset threshold normally\n this._pendingBytes = 0;\n this._batchThreshold = this.options.feedable.bufferSize;\n } else {\n // Parser is stuck mid-token — grow the threshold to avoid\n // hammering parseXml() until significantly more data arrives\n this._batchThreshold = Math.min(\n this._batchThreshold * 2,\n this.options.feedable.maxBufferSize\n );\n }\n }\n\n /**\n * Feed an XML data chunk for incremental parsing.\n *\n * After appending the chunk, parseXml() is run immediately so the parser\n * advances as far as possible. If a chunk boundary falls mid-token, the\n * reader throws UNEXPECTED_END; this is caught here and the source is\n * rewound to the start of the incomplete token so it will be re-parsed on\n * the next feed() call once more data has arrived.\n *\n * Any other ParseError (unclosed quote, mismatched tag, etc.) is a real\n * parse failure and is re-thrown after cleaning up the session.\n *\n * @param {string|Buffer} data\n * @returns {XMLParser} for chaining\n */\n feed(data) {\n if (!this._isFeeding) {\n this._initFeedSession();\n }\n\n // Pass raw data straight through — do NOT pre-convert Buffers to string\n // here. FeedableSource.feed() decodes Buffers via a persistent stateful\n // decoder so a multi-byte UTF-8 character split across two feed()\n // calls decodes correctly; converting each chunk with .toString() first\n // (as this used to do) decodes each chunk in isolation and corrupts a\n // split character. feed() itself validates the type and throws\n // DATA_MUST_BE_STRING for anything unsupported.\n const appendedLength = this._feedSource.feed(data);\n this._pendingBytes += appendedLength;\n\n if (this._pendingBytes >= this._batchThreshold) {\n this._runParse();\n }\n // Otherwise, delay parsing until next feed() or end()\n\n return this;\n }\n\n /**\n * Signal end of input, validate end-of-document state, and return the\n * parsed result. Throws if called before any feed() call.\n *\n * parseXml() is called one final time after marking the source complete.\n * This replays any bytes that were rewound during the last feed() call\n * (e.g. a tag that was split across the final chunk boundary). Now that\n * isComplete is true, any UNEXPECTED_END thrown by a reader means the\n * document is genuinely truncated — not a chunk boundary — so it is\n * treated as a real parse error rather than silently swallowed.\n *\n * autoClose partial-tag recovery works the same way it does in\n * _parseAndFinalize(): if autoCloseHandler is configured and parseXml()\n * throws UNEXPECTED_END, the handler is given a chance to recover before\n * finalizeXml() runs.\n *\n * @returns {any}\n */\n end() {\n if (!this._isFeeding) {\n throw new ParseError('No data fed. Call feed() before end().', ErrorCode.NOT_STREAMING);\n }\n\n // Force a final parse (any pending bytes are now processed)\n this._runParse();\n\n try {\n // Mark the source as complete so readers know there is no more data.\n this._feedSource.end();\n\n // Replay any bytes rewound during the last feed() call (e.g. an\n // incomplete tag at the very end of the input stream). Any\n // UNEXPECTED_END thrown here is a genuine truncation error.\n let partialTagError = null;\n const autoClose = this._feedParser.autoCloseHandler;\n if (autoClose) autoClose.reset();\n\n try {\n this._feedParser.parseXml();\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n if (autoClose) {\n // autoClose recovery: treat the truncated tag the same way\n // _parseAndFinalize() does for the one-shot parse path.\n partialTagError = err;\n } else {\n // No recovery configured — truncated document is a hard error.\n throw err;\n }\n } else {\n throw err;\n }\n }\n\n if (partialTagError) {\n autoClose.handlePartialTag(partialTagError, this._feedParser._parserState());\n } else {\n this._feedParser.finalizeXml();\n }\n\n this._lastParseErrors = autoClose?.getErrors() ?? [];\n this.wasExited = this._feedParser.wasExited();\n return this._feedParser.outputBuilder.getOutput();\n } finally {\n this._cleanupFeedSession();\n }\n }\n\n // ─── Error reporting ──────────────────────────────────────────────────────\n\n /**\n * Return structural errors collected during the last parse call.\n * Only populated when autoClose.collectErrors is true.\n * Each entry: { type, tag, expected, index }\n *\n * @returns {Array}\n */\n getParseErrors() {\n return this._lastParseErrors ?? [];\n }\n\n // ─── Private helpers ──────────────────────────────────────────────────────\n\n /** @private */\n _createParser() {\n return new Xml2JsParser(this.options);\n }\n\n /** @private */\n _initFeedSession() {\n this._feedSource = new FeedableSource({\n ...this.options.feedable,\n decoding: { encoding: this.options.decoding.encoding, registry: this.options.decoding._registry },\n });\n this._feedParser = this._createParser();\n this._feedParser.source = this._feedSource;\n this._feedParser.initializeParser();\n this._isFeeding = true;\n }\n\n /** @private */\n _cleanupFeedSession() {\n this._feedParser = null;\n this._feedSource = null;\n this._isFeeding = false;\n }\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction isReadableStream(value) {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof value.read === 'function' &&\n typeof value.on === 'function' &&\n typeof value.readableEnded === 'boolean'\n );\n}","import { CompactBuilderFactory, CompactBuilder } from \"@nodable/compact-builder\";\n\n/**\n * Same shape as CompactBuilder's output, plus a side-channel recording the\n * insertion order of child tags per parent path (Map<parentPath, string[]>,\n * dot-notation paths matching Traverser's own path format).\n *\n * Needed because plain object key order isn't a reliable stand-in for\n * document order once repeated sibling tags collapse into one key backed by\n * an array — this fires once per tag in document order regardless.\n */\nclass OrderTrackingBuilder extends CompactBuilder {\n constructor(parserOptions, builderOptions, readonlyMatcher, registry) {\n super(parserOptions, builderOptions, readonlyMatcher, registry);\n this.siblingOrder = new Map();\n }\n\n addElement(tag) {\n // this.matcher already reflects the tag being opened (Xml2JsParser\n // pushes onto the matcher before addElement is called), so its path\n // string is \"root.parent.child\" — the parent path is everything\n // before the last segment.\n const jPath = this.matcher.toString();\n const lastDot = jPath.lastIndexOf(\".\");\n const parentPath = lastDot === -1 ? \"\" : jPath.substring(0, lastDot);\n if (!this.siblingOrder.has(parentPath)) this.siblingOrder.set(parentPath, []);\n const siblings = this.siblingOrder.get(parentPath);\n // Only push if not already recorded (repeated tags share one entry)\n if (siblings[siblings.length - 1] !== tag.name) siblings.push(tag.name);\n\n super.addElement(tag);\n }\n\n getOutput() {\n return { data: super.getOutput(), siblingOrder: this.siblingOrder };\n }\n}\n\nexport class OrderTrackingBuilderFactory extends CompactBuilderFactory {\n getInstance(parserOptions, readonlyMatcher) {\n return new OrderTrackingBuilder(parserOptions, this.builderOptions, readonlyMatcher, this.registry);\n }\n}\n","import { NumberValueParser } from \"@nodable/base-output-builder\";\n\n// dxv's own numeric type-checking (Traverser.js, validations.js, and\n// parseRange below) used plain Number()/parseFloat()/isNaN(), none of which\n// understand full-width (unicode) numerals such as \"1000\". Centralizing\n// coercion here, backed by BOB's own number value parser configured with\n// unicode:true, gives every numeric check in dxv that support in one place.\n// FXP's tag/attribute value-parser pipeline stays disabled for dxv data\n// (see RAW_VALUE_PARSERS in validator.js) — this is a separate, dxv-local\n// use of the same parser class, not a re-enabling of that pipeline.\nconst numberParser = new NumberValueParser({ hex: true, leadingZeros: true, eNotation: true, unicode: true });\n\n/**\n * Coerce a raw XML string value to a number, accepting unicode (full-width)\n * numerals in addition to plain ASCII digits/hex/e-notation.\n * @param {string} val\n * @returns {number} the parsed number, or NaN if val isn't numeric\n */\nexport function toNumber(val) {\n const parsed = numberParser.parse(val);\n return typeof parsed === \"number\" ? parsed : NaN;\n}\n\n/**\n * @param {string[]} data\n * @param {string[]} rules\n * @returns {{ data: string[], rules: string[], common: string[] }}\n */\nexport function breakInSets(data, rules) {\n const extra = {\n data: [],\n rules: [],\n common: [],\n };\n\n for (let i = 0; i < data.length; i++) {\n if (rules.indexOf(data[i]) === -1) extra.data.push(data[i]);\n else extra.common.push(data[i]);\n }\n\n for (let i = 0; i < rules.length; i++) {\n if (data.indexOf(rules[i]) === -1 && rules[i] !== \"@\") extra.rules.push(rules[i]);\n }\n return extra;\n}\n\n/**\n * Parse a range shorthand \"min..max\" string into { min, max } numbers.\n * Returns null if the string is not a valid range.\n * @param {string} range\n * @returns {{ min: number, max: number } | null}\n */\nexport function parseRange(range) {\n if (typeof range !== \"string\") return null;\n const parts = range.split(\"..\");\n if (parts.length !== 2) return null;\n const min = toNumber(parts[0]);\n const max = toNumber(parts[1]);\n if (isNaN(min) || isNaN(max)) return null;\n return { min, max };\n}\n\n/**\n * Parse a date range shorthand \"minDate..maxDate\" string into { min, max } date strings.\n * Returns null if either part is not a parseable date.\n * @param {string} range\n * @returns {{ min: string, max: string } | null}\n */\nexport function parseDateRange(range) {\n if (typeof range !== \"string\") return null;\n const parts = range.split(\"..\");\n if (parts.length !== 2) return null;\n const [min, max] = parts;\n if (isNaN(Date.parse(min)) || isNaN(Date.parse(max))) return null;\n return { min, max };\n}\n","import { toNumber } from \"./util.js\";\n\nexport const string = {\n minLength(expected, actual) {\n return actual.length >= expected;\n },\n maxLength(expected, actual) {\n return actual.length <= expected;\n },\n length(expected, actual) {\n return actual.length === expected;\n },\n pattern(expected, actual, modifier = \"\") {\n const regxp = new RegExp(expected, modifier);\n return regxp.test(actual);\n },\n fixed(expected, actual) {\n return expected === actual;\n },\n in(expected, actual) {\n return expected.split(\",\").indexOf(actual) > -1;\n },\n};\n\nexport const num = {\n min(expected, actual) {\n return toNumber(actual) >= expected;\n },\n max(expected, actual) {\n return toNumber(actual) <= expected;\n },\n};\n\nexport const list = {\n minOccurs(expected, actual) {\n return actual >= expected;\n },\n maxOccurs(expected, actual) {\n return actual <= expected;\n },\n};\n\nexport const date = {\n /**\n * Compare two date strings.\n * @param {string} a\n * @param {string} b\n * @returns {number}\n */\n compare(a, b) {\n return Date.parse(a) - Date.parse(b);\n },\n\n isBefore(actual, reference) {\n return Date.parse(actual) < Date.parse(reference);\n },\n\n isAfter(actual, reference) {\n return Date.parse(actual) > Date.parse(reference);\n },\n\n min(expected, actual) {\n return Date.parse(actual) >= Date.parse(expected);\n },\n\n max(expected, actual) {\n return Date.parse(actual) <= Date.parse(expected);\n },\n};\n","import * as validations from \"./validations.js\";\nimport { breakInSets, parseRange, parseDateRange, toNumber } from \"./util.js\";\n\nconst numericTypes = [\"positiveInteger\", \"integer\", \"positiveDecimal\", \"decimal\", \"number\"];\n\nexport class Traverser {\n /**\n * @param {object} options\n * @param {object} validators\n */\n constructor(options, validators, siblingOrder) {\n this.options = options;\n this.failures = [];\n this.validators = validators;\n\n // uniqueness tracking: field path -> array of seen values (sibling-scoped)\n // global uniqueness tracking: field name -> array of seen values (document-scoped)\n this._globalUnique = new Map(); // Map<fieldName, Set<string>>\n\n // Side-channel sibling order map built during XML parsing.\n // Map<parentPath, string[]> where parentPath matches Traverser's dot-notation path.\n this._siblingOrder = siblingOrder || new Map();\n }\n\n /**\n * @param {object|string} ele\n * @param {string} key\n * @param {object|string} rules\n * @param {string} path\n */\n traverse(ele, key, rules, path) {\n if (Array.isArray(ele)) {\n if (rules !== undefined && rules[\"@rules\"] !== undefined) {\n if (rules[\"@rules\"].repeatable !== undefined) {\n this.checkOccurences(rules[\"@rules\"], ele.length, path);\n ele.forEach((val, index) => {\n const arrayPath = path + \"[\" + index + \"]\";\n if (Object.keys(rules).length > 1) {\n this.callForCommonProperties(ele[index], rules, arrayPath);\n } else {\n this.traverse(ele[index], key, rules, arrayPath);\n }\n });\n return;\n }\n }\n this.failures.push({\n code: \"unexpected sequence\",\n path: path.substr(1),\n });\n } else if (typeof ele === \"object\") {\n this.callForCommonProperties(ele, rules, path);\n } else {\n if (typeof rules === \"object\" && rules[\"@rules\"]) {\n this.checkDataAndType(ele, key, rules, path);\n } else if (this.isMapType(rules)) {\n this.validateMandatoryFields(rules, path);\n }\n this.applyCustomValidators(ele, rules, path);\n }\n }\n\n applyCustomValidators(ele, rules, path) {\n if (\n rules[\"@rules\"] &&\n rules[\"@rules\"].checkBy &&\n typeof this.validators[rules[\"@rules\"].checkBy] === \"function\"\n ) {\n let res = this.validators[rules[\"@rules\"].checkBy](ele, path.substr(1));\n if (typeof res === \"object\") {\n this.failures.push(res);\n }\n }\n }\n\n /**\n * @param {object} ele\n * @param {object} rules\n * @param {string} path\n */\n callForCommonProperties(ele, rules, path) {\n const tags = Object.keys(ele);\n const rulesTags = Object.keys(rules);\n\n const sets = breakInSets(tags, rulesTags);\n this.checkUnknownSiblings(sets, path);\n this.checkMissingSiblings(sets, rules, path);\n\n this.applyCustomValidators(ele, rules, path);\n\n // Collect sibling values for uniqueness checks before traversing\n // Build a map of fieldName -> array of values for this sibling scope\n const siblingValues = this._buildSiblingValues(ele, rules, sets.common);\n\n sets.common.forEach((key) => {\n const newpath = path + \".\" + key;\n this.traverse(ele[key], key, rules[key], newpath);\n });\n\n // After traversing, run ordering, uniqueness, and relational checks for this sibling scope\n this._checkOrderingConstraints(ele, rules, sets.common, path);\n this._checkUniquenessConstraints(ele, rules, sets.common, path, siblingValues);\n this._checkRelationalConstraints(ele, rules, sets.common, path);\n }\n\n // ─── Ordering Constraints (before / after) ────────────────────────────────\n\n /**\n * For every field that has `before` or `after` in its rules, validate that\n * it appears positionally before or after the referenced sibling tag in the\n * original XML document order.\n *\n * Order is sourced from the side-channel `_siblingOrder` map built during\n * parsing — not from JS object key order, which is unreliable for integer keys.\n *\n * Works for any tag type, not just dates.\n *\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path - current traversal path (e.g. \".event\" or \".order[0]\")\n */\n _checkOrderingConstraints(ele, rules, commonKeys, path) {\n // The siblingOrder map is keyed by the parent path without the leading dot.\n const orderKey = path.substr(1);\n const order = this._siblingOrder.get(orderKey);\n // If we have no order info for this path, skip — can happen for empty elements.\n if (!order) return;\n\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const r = fieldRules[\"@rules\"];\n\n const keyIdx = order.indexOf(key);\n if (keyIdx === -1) return; // tag not recorded — skip\n\n if (r.after) {\n const refKey = r.after;\n // Skip silently if referenced sibling is absent from data\n if (ele[refKey] === undefined) return;\n const refIdx = order.indexOf(refKey);\n if (refIdx === -1) return;\n if (keyIdx <= refIdx) {\n this.failures.push({\n code: \"after\",\n path: (path + \".\" + key).substr(1),\n actual: key,\n expected: refKey,\n });\n }\n }\n\n if (r.before) {\n const refKey = r.before;\n // Skip silently if referenced sibling is absent from data\n if (ele[refKey] === undefined) return;\n const refIdx = order.indexOf(refKey);\n if (refIdx === -1) return;\n if (keyIdx >= refIdx) {\n this.failures.push({\n code: \"before\",\n path: (path + \".\" + key).substr(1),\n actual: key,\n expected: refKey,\n });\n }\n }\n });\n }\n\n // ─── Relational Constraints (sameAs / notSameAs / lessThan / moreThan) ──────\n\n /**\n * Compare this field's value against another sibling field's value.\n *\n * Supported rule attributes:\n * sameAs=\"refField\" — values must be equal\n * notSameAs=\"refField\" — values must differ\n * lessThan=\"refField\" — this value must be strictly less than ref value\n * moreThan=\"refField\" — this value must be strictly greater than ref value\n *\n * Comparison is type-aware based on the field's declared `type`:\n * date types → Date.parse() comparison\n * numeric types → Number() comparison\n * all others → lexicographic string comparison\n *\n * Skips silently when:\n * - The referenced sibling is absent from data\n * - Either value is an object/map (not a primitive)\n * - The ref value cannot be coerced to the expected type\n *\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path\n */\n _checkRelationalConstraints(ele, rules, commonKeys, path) {\n const RELATIONAL_RULES = [\"sameAs\", \"notSameAs\", \"lessThan\", \"moreThan\"];\n\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const r = fieldRules[\"@rules\"];\n\n // Check if any relational rule is present\n if (!RELATIONAL_RULES.some((attr) => r[attr] !== undefined)) return;\n\n const actualRaw = ele[key];\n\n // Skip maps and arrays — only primitives are comparable\n if (actualRaw === null || actualRaw === undefined) return;\n if (typeof actualRaw === \"object\") return;\n\n const fieldType = r.type || \"string\";\n\n RELATIONAL_RULES.forEach((attr) => {\n if (r[attr] === undefined) return;\n\n const refKey = r[attr];\n const refRaw = ele[refKey];\n\n // Skip silently if ref sibling is absent or is a map/array\n if (refRaw === undefined || refRaw === null) return;\n if (typeof refRaw === \"object\") return;\n\n // Coerce both values and compare\n const cmp = this._relationalCompare(String(actualRaw), String(refRaw), fieldType);\n\n // cmp is null when coercion fails — skip silently\n if (cmp === null) return;\n\n let violated = false;\n if (attr === \"sameAs\" && cmp !== 0) violated = true;\n if (attr === \"notSameAs\" && cmp === 0) violated = true;\n if (attr === \"lessThan\" && cmp >= 0) violated = true;\n if (attr === \"moreThan\" && cmp <= 0) violated = true;\n\n if (violated) {\n this.failures.push({\n code: attr,\n path: (path + \".\" + key).substr(1),\n actual: String(actualRaw),\n expected: refKey,\n });\n }\n });\n });\n }\n\n /**\n * Compare two string-encoded values using the appropriate strategy for the field type.\n * Returns a negative number, zero, or positive number (like Array.sort comparator).\n * Returns null if coercion fails (caller should skip silently).\n * @param {string} a\n * @param {string} b\n * @param {string} fieldType\n * @returns {number|null}\n */\n _relationalCompare(a, b, fieldType) {\n if (fieldType === \"date\") {\n const da = Date.parse(a);\n const db = Date.parse(b);\n if (isNaN(da) || isNaN(db)) return null;\n return da - db;\n }\n if (numericTypes.indexOf(fieldType) !== -1) {\n const na = toNumber(a);\n const nb = toNumber(b);\n if (isNaN(na) || isNaN(nb)) return null;\n return na - nb;\n }\n // string / no type — lexicographic\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n }\n\n // ─── Uniqueness Constraints ───────────────────────────────────────────────\n\n /**\n * Build a map of fieldName -> array of values found in this sibling scope.\n * Works for both scalar values and array (repeatable) values.\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @returns {Map<string, string[]>}\n */\n _buildSiblingValues(ele, rules, commonKeys) {\n const map = new Map();\n commonKeys.forEach((key) => {\n const val = ele[key];\n if (Array.isArray(val)) {\n map.set(key, val.map(String));\n } else if (val !== undefined && val !== null && typeof val !== \"object\") {\n map.set(key, [String(val)]);\n }\n });\n return map;\n }\n\n /**\n * Check uniqueness constraints for fields in this sibling scope.\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path\n * @param {Map<string, string[]>} siblingValues\n */\n _checkUniquenessConstraints(ele, rules, commonKeys, path, siblingValues) {\n // For repeatable items (array context), ele is a single item; sibling-scope\n // uniqueness is managed by the parent array traversal which collects all items.\n // We handle uniqueness at the collection level in `_checkUniqueInCollection`.\n // This method handles per-field unique=\"global\".\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const unique = fieldRules[\"@rules\"].unique;\n if (!unique) return;\n\n const vals = siblingValues.get(key) || [];\n if (unique === \"global\") {\n vals.forEach((v, idx) => {\n const fieldPath = Array.isArray(ele[key])\n ? (path + \".\" + key + \"[\" + idx + \"]\").substr(1)\n : (path + \".\" + key).substr(1);\n this._assertGlobalUnique(key, v, fieldPath);\n });\n }\n // unique=\"true\" (sibling scope) is handled at the repeatable-collection level\n });\n }\n\n /**\n * Check uniqueness within a collection of repeatable items.\n * Called from _traverseRepeatableCollection when sibling-scoped uniqueness is needed.\n * @param {Array<object>} items\n * @param {string} fieldKey - field to check\n * @param {string} basePath - path prefix for error messages\n */\n _checkUniqueInCollection(items, fieldKey, basePath) {\n const seen = new Map(); // value -> first index\n items.forEach((item, idx) => {\n const val = item[fieldKey];\n if (val === undefined || val === null || typeof val === \"object\") return;\n const strVal = String(val);\n if (seen.has(strVal)) {\n this.failures.push({\n code: \"unique\",\n path: (basePath + \"[\" + idx + \"].\" + fieldKey).substr(1),\n value: strVal,\n });\n } else {\n seen.set(strVal, idx);\n }\n });\n }\n\n /**\n * Assert a value is unique globally (across the whole document).\n * @param {string} fieldName\n * @param {string} value\n * @param {string} fieldPath\n */\n _assertGlobalUnique(fieldName, value, fieldPath) {\n if (!this._globalUnique.has(fieldName)) {\n this._globalUnique.set(fieldName, new Set());\n }\n const seen = this._globalUnique.get(fieldName);\n if (seen.has(value)) {\n this.failures.push({\n code: \"unique\",\n path: fieldPath,\n value,\n });\n } else {\n seen.add(value);\n }\n }\n\n // ─── Data & Type ──────────────────────────────────────────────────────────\n\n /**\n * Check if the leaf node has correct type and passes validations.\n * @param {string} val\n * @param {string} key\n * @param {object} rules\n * @param {string} path\n */\n checkDataAndType(val, key, rules, path) {\n if (rules[\"@rules\"].repeatable === true) {\n this.checkOccurences(rules[\"@rules\"], 1, path);\n }\n const eleType = rules[\"@rules\"].type;\n\n // Expand range shorthand before numeric validation\n const expandedRules = this._expandRange(rules);\n\n if (eleType === \"map\" || (!eleType && this.isMapType(rules))) {\n if (typeof val === \"string\" && val.length > 0) {\n this.failures.push({\n code: \"unexpected value in a map\",\n path: path.substr(1),\n value: val,\n });\n }\n this.validateMandatoryFields(rules, path);\n } else if (eleType === \"date\") {\n this.validateDate(val, eleType, path);\n this._checkDateBounds(rules, val, path);\n } else if (eleType === \"boolean\") {\n this.validateBoolean(val, eleType, path);\n } else if (numericTypes.indexOf(eleType) !== -1) {\n if (!this.isValidNum(eleType, val)) {\n this.setInvalidDataType(eleType, path, val);\n } else {\n this.assertValue(expandedRules[\"@rules\"], \"num\", toNumber(val), path);\n }\n } else if (eleType === \"string\" || !eleType) {\n this.assertValue(expandedRules[\"@rules\"], \"string\", val, path);\n } else {\n throw new Error(\"Unsupported data type in Rules:\" + eleType);\n }\n }\n\n /**\n * Expand `range=\"min..max\"` shorthand into `min` and `max` on a cloned rules object.\n * @param {object} rules\n * @returns {object}\n */\n _expandRange(rules) {\n if (!rules[\"@rules\"] || !rules[\"@rules\"].range) return rules;\n const parsed = parseRange(rules[\"@rules\"].range);\n if (!parsed) return rules;\n // Clone to avoid mutating the shared rules object\n return {\n ...rules,\n \"@rules\": {\n ...rules[\"@rules\"],\n min: rules[\"@rules\"].min !== undefined ? rules[\"@rules\"].min : String(parsed.min),\n max: rules[\"@rules\"].max !== undefined ? rules[\"@rules\"].max : String(parsed.max),\n },\n };\n }\n\n /**\n * Check `min`, `max`, and `range` bounds for a date field.\n * Only runs after the value has already passed `validateDate` (i.e. it is a valid date).\n * Explicit `min`/`max` take precedence over `range` shorthand.\n * @param {object} rules\n * @param {string} val\n * @param {string} path\n */\n _checkDateBounds(rules, val, path) {\n if (isNaN(Date.parse(val))) return; // invalid date — type check already reported it\n const r = rules[\"@rules\"];\n\n let min = r.min;\n let max = r.max;\n\n // Expand range shorthand only for the parts not already set explicitly\n if (r.range && (!min || !max)) {\n const parsed = parseDateRange(r.range);\n if (parsed) {\n if (!min) min = parsed.min;\n if (!max) max = parsed.max;\n }\n }\n\n if (min !== undefined && !validations.date.min(min, val)) {\n this.setInvalidValueError(\"min\", path, val, min);\n }\n if (max !== undefined && !validations.date.max(max, val)) {\n this.setInvalidValueError(\"max\", path, val, max);\n }\n }\n\n /**\n * @param {object} rules\n * @param {string} newpath\n */\n validateMandatoryFields(rules, newpath) {\n const keys = Object.keys(rules);\n for (let i = 0; i < keys.length; i++) {\n if (keys[i] === \"@rules\") continue;\n const child = rules[keys[i]];\n const rulesForChild = child[\"@rules\"];\n if (rulesForChild) {\n if (rulesForChild.minOccurs && rulesForChild.minOccurs > 0) {\n this.failures.push({\n code: \"missing\",\n path: (newpath + \".\" + keys[i]).substr(1),\n });\n } else if (rulesForChild.nillable !== undefined && rulesForChild.nillable === \"false\") {\n this.failures.push({\n code: \"missing\",\n path: (newpath + \".\" + keys[i]).substr(1),\n });\n }\n }\n }\n }\n\n /**\n * @param {any} tag\n * @returns {boolean}\n */\n isMapType(tag) {\n if (typeof tag === \"string\") return false;\n const keys = Object.keys(tag);\n if (tag[\"@rules\"]) {\n if (keys.length === 1) return false;\n } else if (keys.length === 0) {\n return false;\n } else {\n return true;\n }\n }\n\n setInvalidValueError(rule, newpath, actual, expected) {\n this.failures.push({\n code: rule,\n path: newpath.substr(1),\n actual,\n expected,\n });\n }\n\n checkOccurences(rules, actual, newpath) {\n [\"minOccurs\", \"maxOccurs\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.list[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n }\n\n checkNumeric(rules, actual, newpath) {\n [\"min\", \"max\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.num[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n }\n\n checkString(rules, actual, newpath) {\n [\"minLength\", \"maxLength\", \"length\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.string[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n [\"fixed\", \"in\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = rules[rule];\n if (!validations.string[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n [\"pattern\", \"pattern_i\", \"pattern_im\", \"pattern_mi\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n let modifier = \"\";\n if (rule.length > 8) modifier = rule.substring(8);\n const expected = rules[rule];\n if (!validations.string[\"pattern\"](expected, actual, modifier)) {\n this.setInvalidValueError(\"pattern\", newpath, actual, expected);\n }\n }\n });\n }\n\n assertValue(rules, eleType, actual, newpath) {\n if (eleType === \"string\") this.checkString(rules, actual, newpath);\n else if (eleType === \"num\") this.checkNumeric(rules, actual, newpath);\n }\n\n validateBoolean(actual, eleType, newpath) {\n if (this.options.boolean.indexOf(actual) === -1) {\n this.setInvalidDataType(eleType, newpath, actual);\n }\n }\n\n isValidNum(eleType, val) {\n const num = toNumber(val);\n if (!isNaN(num)) {\n if (\n (eleType === \"positiveInteger\" && num < 0) ||\n (eleType === \"positiveDecimal\" && num < 0) ||\n (eleType === \"integer\" && !isInt(num))\n ) {\n return false;\n }\n } else {\n return false;\n }\n return true;\n }\n\n validateDate(val, eleType, newpath) {\n const date = Date.parse(val);\n if (isNaN(date)) this.setInvalidDataType(eleType, newpath, val);\n }\n\n setInvalidDataType(eleType, newpath, val) {\n this.failures.push({\n code: \"not a \" + eleType,\n path: newpath.substr(1),\n value: val,\n });\n }\n\n /**\n * @param {object} sets\n * @param {object} rules\n * @param {string} path\n */\n checkMissingSiblings(sets, rules, path) {\n if (sets.rules) {\n sets.rules.forEach((tagRule) => {\n const rulesNode = rules[tagRule][\"@rules\"];\n if (rulesNode && (rulesNode.nillable === \"false\" || rulesNode.minOccurs > 0)) {\n this.failures.push({\n code: \"missing\",\n path: (path + \".\" + tagRule).substr(1),\n });\n }\n });\n }\n }\n\n /**\n * @param {object} sets\n * @param {string} path\n */\n checkUnknownSiblings(sets, path) {\n if (sets.data && !this.options.unknownAllow) {\n sets.data.forEach((tag) => {\n this.failures.push({\n code: \"unknown\",\n path: (path + \".\" + tag).substr(1),\n });\n });\n }\n }\n}\n\n// ─── Traverser subclass that adds repeatable-collection uniqueness ─────────────\n// We override the array-handling branch of traverse() to also run sibling uniqueness.\n\nconst _originalTraverse = Traverser.prototype.traverse;\n\nTraverser.prototype.traverse = function traverse(ele, key, rules, path) {\n if (\n Array.isArray(ele) &&\n rules !== undefined &&\n rules[\"@rules\"] !== undefined &&\n rules[\"@rules\"].repeatable !== undefined\n ) {\n // Run the original logic (occurrence checks + per-item traversal)\n this.checkOccurences(rules[\"@rules\"], ele.length, path);\n ele.forEach((val, index) => {\n const arrayPath = path + \"[\" + index + \"]\";\n if (Object.keys(rules).length > 1) {\n this.callForCommonProperties(ele[index], rules, arrayPath);\n } else {\n // Simple scalar repeatable\n const scalarRules = rules;\n _originalTraverse.call(this, ele[index], key, scalarRules, arrayPath);\n }\n });\n\n // After all items processed, check sibling-scoped uniqueness\n if (ele.length > 0 && typeof ele[0] === \"object\") {\n this._runSiblingUniqueness(ele, rules, path);\n }\n return;\n }\n\n // For everything else, delegate to the original implementation\n _originalTraverse.call(this, ele, key, rules, path);\n};\n\n/**\n * Scan child field rules of a repeatable element for `unique=\"true\"` and\n * validate all occurrences within the array scope.\n * @param {Array<object>} items\n * @param {object} rules - rules for the repeatable element\n * @param {string} path\n */\nTraverser.prototype._runSiblingUniqueness = function (items, rules, path) {\n const fieldKeys = Object.keys(rules).filter((k) => k !== \"@rules\");\n fieldKeys.forEach((fieldKey) => {\n const fieldRules = rules[fieldKey];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n if (fieldRules[\"@rules\"].unique !== \"true\") return;\n this._checkUniqueInCollection(items, fieldKey, path);\n });\n};\n\nfunction isInt(num) {\n return (num | 0) === num;\n}\n","import XMLParser, { ParseError } from \"@nodable/flexible-xml-parser\";\nimport { CompactBuilderFactory } from \"@nodable/compact-builder\";\nimport { OrderTrackingBuilderFactory } from \"./OrderTrackingBuilder.js\";\nimport { Traverser } from \"./Traverser.js\";\n\nconst defaultOptions = {\n unknownAllow: true,\n boolean: [\"true\", \"false\"],\n};\n\n// Keep every tag/attribute value as a raw, untouched string — Traverser and\n// validations.js do their own number/date/boolean coercion, so no parser\n// pipeline should get there first.\nconst RAW_VALUE_PARSERS = { tags: { valueParsers: [] }, attributes: { valueParsers: [] } };\n\n// Parsers are stateless w.r.t. any single Validator instance and safe to\n// reuse across parses (each .parse() call gets a fresh internal tokenizer\n// and a fresh builder instance from the factory) — building them once here\n// avoids rebuilding the value-parser pipelines on every Validator/validate() call.\nconst ruleParser = new XMLParser({\n skip: { attributes: false },\n attributes: { prefix: \"\", groupBy: \"@rules\", booleanType: true },\n OutputBuilder: new CompactBuilderFactory(RAW_VALUE_PARSERS),\n});\n\nconst dataParser = new XMLParser({\n skip: { attributes: false },\n attributes: { prefix: \"\", groupBy: \":a\" },\n OutputBuilder: new OrderTrackingBuilderFactory(RAW_VALUE_PARSERS),\n});\n\nexport class Validator {\n /**\n * @param {string} rules - XML string containing rule definitions\n * @param {object} [options]\n */\n constructor(rules, options) {\n assertXmlString(rules);\n this.rules = restoreAttrsBucketKeys(parseOrThrow(ruleParser, sanitizeAttrsBucketTag(rules)));\n this.options = Object.assign({}, defaultOptions, options);\n this.validators = {};\n /** @type {object|null} Parsed XML data after the last validate() call */\n this.data = null;\n }\n\n /**\n * Register a custom validator function.\n * @param {string} name\n * @param {(value: string, path: string) => object | undefined} fn\n */\n register(name, fn) {\n this.validators[name] = fn;\n }\n\n /**\n * Validate XML data against the rules.\n * @param {string} xmldata\n * @returns {Array<object>} Array of failure objects (empty if valid)\n */\n validate(xmldata) {\n assertXmlString(xmldata);\n\n const { data, siblingOrder } = parseOrThrow(dataParser, xmldata);\n this.data = data;\n\n const traverser = new Traverser(this.options, this.validators, siblingOrder);\n traverser.traverse(data, \"\", this.rules, \"\");\n return traverser.failures;\n }\n}\n\n// Rules files may define a tag literally named \":a\" — a stand-in element\n// holding validation rules for a tag's attributes, mirroring the \":a\" key\n// data gets grouped under. A leading colon is not a legal tag name under\n// namespace-aware naming rules, which the new parser enforces and the old\n// one didn't. Rather than force every existing rules file to change, swap\n// it for a legal placeholder before parsing and rename the resulting object\n// key straight back — invisible to anyone authoring or consuming rules.\nconst ATTRS_BUCKET_TAG = \":a\";\nconst ATTRS_BUCKET_PLACEHOLDER = \"__nodableAttrsBucket__\";\nconst ATTRS_BUCKET_TAG_RE = /<(\\/?):a(\\s|>|\\/)/g;\n\nfunction sanitizeAttrsBucketTag(rules) {\n return rules.replace(ATTRS_BUCKET_TAG_RE, `<$1${ATTRS_BUCKET_PLACEHOLDER}$2`);\n}\n\nfunction restoreAttrsBucketKeys(value) {\n if (Array.isArray(value)) {\n for (const item of value) restoreAttrsBucketKeys(item);\n } else if (value !== null && typeof value === \"object\") {\n if (Object.prototype.hasOwnProperty.call(value, ATTRS_BUCKET_PLACEHOLDER)) {\n value[ATTRS_BUCKET_TAG] = value[ATTRS_BUCKET_PLACEHOLDER];\n delete value[ATTRS_BUCKET_PLACEHOLDER];\n }\n for (const key of Object.keys(value)) restoreAttrsBucketKeys(value[key]);\n }\n return value;\n}\n\nfunction assertXmlString(xmldata) {\n if (!xmldata) throw new Error(\"Empty data\");\n if (typeof xmldata !== \"string\") throw new Error(\"Not a valid string\");\n}\n\n/**\n * Runs a shared parser instance and turns a syntax-level ParseError into the\n * same plain \"message:line\" Error shape dxv has always thrown. Line is not\n * provided by the parser (position is index-only) so it's derived by\n * counting newlines up to the error's character offset.\n *\n * One error code gets its wording translated back to dxv's pre-migration\n * text, so existing callers matching on that exact message keep working\n * without forcing that wording onto every other FXP consumer.\n */\nfunction parseOrThrow(parser, xmldata) {\n try {\n return parser.parse(xmldata);\n } catch (err) {\n if (err instanceof ParseError) {\n let message = err.message;\n if (err.code === \"INVALID_ATTRIBUTE_NAME\") {\n const name = message.slice(message.indexOf(\": \") + 2);\n message = `Attribute '${name}' is an invalid name.`;\n }\n // Not every ParseError carries a position (position is index-only,\n // and not every code attaches one) — don't fabricate a line number\n // when it's missing.\n if (err.index === undefined) throw new Error(message);\n const line = xmldata.substring(0, err.index).split(\"\\n\").length;\n throw new Error(message + \":\" + line);\n }\n throw err;\n }\n}\n\nexport default Validator;\n"],"names":["root","factory","exports","module","define","amd","this","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","ValidationError","_Error","message","code","line","col","_this","name","constructor","Error","captureStackTrace","_wrapNativeSuper","isWhiteSpace","char","isIllegalControlCode","getLineNumberForPosition","xmlData","index","lines","substring","split","length","getPositionFromMatch","match","startIndex","nameStartChar10","nameStartChar11","nameChar11","buildRegexes","startChar","flags","ncNamePat","replace","RegExp","ncName","qName","nmToken","nmTokens","regexes10","regexes11","nameStartCharAscii","regexesAscii","getRegexes","xmlVersion","asciiOnly","str","test","PRODUCTIONS","createValidator","production","maxCacheSize","includes","TypeError","join","regex","cache","Map","validator","cached","undefined","result","size","set","reset","XmlNameValidator","_proto","isValidQName","isValidName","XmlDeclarationValidator","options","throwError","optional","argPosition","argPostion","_throwError","validate","startsWith","version","end","piEnd","indexOf","declBody","ALLOWED","attrRe","lastAllowedIdx","exec","attrName","attrValue","attrPos","allowedIdx","leftover","trim","ControlCharValidator","findIllegalIndex","len","i","charCodeAt","hasIllegalChar","describe","toString","padStart","validAttrStrRegxp","AttributeStringValidator","nameValidator","attrStr","_this$options$invalid","matches","string","allmatches","lastIndex","push","getAllMatches","attrNames","checkAttrLt","invalidCharSequence","attrLt","m","_err","hasEquals","hasQuotedValue","allowBooleanAttributes","illegalIdx","msg","err","CommentCdataReader","_options$invalidCharS","checkCommentDashes","comment","read","docTypeValidator","_readComment","validateDocType","_readCdata","bodyStart","body","Expression","pattern","data","separator","segments","_parse","_hasDeepWildcard","some","seg","type","_hasAttributeCondition","_hasPositionSelector","position","currentPart","_parseSegment","part","segment","bracketContent","withoutBrackets","bracketMatch","content","slice","namespace","tag","tagAndPosition","nsIndex","positionMatch","colonIndex","lastIndexOf","tagPart","posPart","eqIndex","nthMatch","positionValue","parseInt","hasDeepWildcard","hasAttributeCondition","hasPositionSelector","MatcherView","matcher","_matcher","getCurrentTag","path","getCurrentNamespace","getAttrValue","values","hasAttr","current","getAnyParentAttr","hasAnyParentAttr","getPosition","getCounter","counter","getIndex","getDepth","includeNamespace","toArray","map","n","expression","matchesAny","exprSet","Matcher","siblingStacks","_pathStringCache","_view","_keptAttrs","tagName","attrValues","currentLevel","level","counts","total","siblingKey","node","depth","keep","pop","poppedDepth","updateCurrent","kept","sep","_matchWithDeepWildcard","_matchSimple","_matchSegment","pathIdx","segIdx","nextSeg","found","isCurrentNode","String","snapshot","keptAttrs","entry","restore","readOnly","_defineProperties","e","r","t","configurable","writable","_toPropertyKey","toPrimitive","_toPrimitive","SkipTagMatcher","skipTags","_expressions","expr","_enabled","rawAttributes","RawSubtreeReader","consume","needle","searchFrom","idx","j","defaultOptions","maxEntityCount","Infinity","maxEntitySize","DocTypeValidator","assign","suppressValidationErr","angleBracketsCount","hasBody","entityCount","hasSeq","_this$_validateEntity","_validateEntityExp","_validateElementExp","_validateAttlistExp","_validateNotationExp","nameStart","skipWhitespace","entityValue","entityName","validateName","toUpperCase","_readQuotedValue","readQuotedValue","_this$options","notationName","identifierType","elementName","isXmlName","elemNameStart","attrNameStart","notationStart","typeStart","attributeType","seq","start","unpairedTags","docType","tagValue","xmlDeclaraion","multipleRoots","SyntaxValidator","defaults","merged","xmlDeclaration","mergeOptions","tags","tagFound","reachedRoot","docTypeSeen","substr","lineNumber","declResult","commentCdataReader","attrValidator","skipMatcher","checkTagValueSeq","checkDocTypePlacement","startBang","_readPI","tagStartPos","closingTag","_readAttributeStr","attrStrStart","isValid","tagClosed","otg","openPos","skip","prevTwo","afterAmp","_validateAmpersand","JSON","stringify","piStart","nameValidated","ch","piName","toLowerCase","_validateNumberAmpersand","re","count","ExpressionSet","_byDepthAndTag","_wildcardByDepth","_deepWildcards","_deepByTerminalTag","_patterns","Set","_sealed","add","has","lastSeg","addAll","expressions","seal","isSealed","findMatch","exactKey","exactBucket","wildcardBucket","deepBucket","nameFor","valueParsers","attributes","textJoint","alwaysArray","forceArray","forceTextNode","defaultTagParsers","defaultAttrParsers","buildOptions","finalOptions","deepClone","alwaysArraySource","Array","isArray","alwaysArraySet","normalizeEntry","_alwaysArraySet","copyProperties","clone","keys","target","source","optionName","XML","amp","apos","gt","lt","quot","ENTITY_ACTION","freeze","ALLOW","BLOCK","THROW","SPECIAL_CHARS","validateEntityName","mergeEntityMaps","maps","out","create","raw","val","LIMIT_TIER_EXTERNAL","LIMIT_TIER_BASE","LIMIT_TIER_ALL","NCR_LEVEL","allow","leave","remove","throw","XML10_ALLOWED_C0","EntityDecoder","_limit","limit","_maxTotalExpansions","maxTotalExpansions","_maxExpandedLength","maxExpandedLength","_postCheck","postCheck","_limitTiers","applyLimitsTo","_numericAllowed","numericAllowed","_baseMap","DEFAULT_XML_ENTITIES","namedEntities","_externalMap","_inputMap","_totalExpansions","_expandedLength","_removeSet","_leaveSet","ncrCfg","ncr","onLevel","nullLevel","onNCR","nullNCR","Math","max","parseNCRConfig","_ncrXmlVersion","_ncrOnLevel","_ncrNullLevel","_onExternalEntity","onExternalEntity","_onInputEntity","onInputEntity","_applyRegistrationHook","hook","context","action","setExternalEntities","flat","filtered","entries","addExternalEntity","addInputEntities","setXmlVersion","decode","original","chunks","last","limitExpansions","limitLength","checkLimits","token","replacement","tier","ncrResult","_resolveNCR","resolved","_resolveName","_tierCounts","delta","_classifyNCR","cp","_applyNCRAction","fromCodePoint","second","Number","isNaN","minimum","effective","id","description","CONTEXT_REGISTRY","HTML","SVG","SQL","SQL_PATTERNS","SHELL","REDOS","NOSQL","LOG","VALID_CONTEXTS","fromEntries","k","matchContext","contextName","patterns","rule","isUnsafe","assertString","c","assertContext","BaseValueParser","IS_FINAL","init","ctx","parse","runTimeContext","SCRIPT_ZEROS","HIGH_MAP","TABLE_OFFSET","TABLE","Uint8Array","LOW_MAX","fill","zero","d","MINUS_SET","hexRegex","binRegex","octRegex","numRegex","consider","hex","binary","octal","leadingZeros","decimalPoint","eNotation","infinity","unicode","toNumber","trimmedStr","skipLike","firstHit","cc","low","chars","fromCharCode","anynum","parse_int","isFinite","notation","eNotationRegx","sign","eChar","eAdjacentToLeadingZeros","resolveEnotation","numTrimmedByZeros","numStr","decimalAdjacentToLeadingZeros","num","parsedStr","search","isPositive","handleInfinity","base","window","numParser","isFinal","super","newval","FinalValue","defaultValParsers","entities","exclude","_excludeSet","isAttribute","trueList","falseList","temp","numberParser","ValueParserRegistry","registered","register","parser","resetAll","vp","ret","Context","isLeafNode","SharedContext","clear","ValueParserPipeline","valParsers","registry","sharedContext","_initAll","run","runtimContext","instance","instances","seen","p","parserInstance","DEFAULT_TAG_PARSERS","DEFAULT_ATTR_PARSERS","BaseOutputBuilder","parserOptions","builderOptions","matcherView","resetPipelines","_rootName","tagChain","attrChain","tagsPipeline","attrsPipeline","_pendingStopNode","addAttribute","prefixed","prefix","suffix","_addChild","addComment","text","addLiteral","cdata","addRawValue","addValue","addDeclaration","addInstruction","onStopNode","tagDetail","rawContent","onExit","exitInfo","BaseOutputBuilderFactory","registerValueParser","getInstance","CompactBuilderFactory","readonlyMatcher","CompactBuilder","tagsStack","parent","textValue","hasAttributes","_buildAttributeValue","isEmpty","groupBy","addElement","_resolveForceArray","alwaysVote","forceVote","closeElement","parsedText","resultTag","arr","parentTag","shouldForceArray","_addChildTo","getOutput","ParseError","pos","_posStr","ErrorCode","INVALID_INPUT","INVALID_STREAM","ALREADY_STREAMING","NOT_STREAMING","DATA_MUST_BE_STRING","UNEXPECTED_END","UNEXPECTED_CLOSE_TAG","MISMATCHED_CLOSE_TAG","UNEXPECTED_TRAILING_DATA","INVALID_TAG","UNCLOSED_QUOTE","INVALID_ATTRIBUTE_NAME","MULTIPLE_NAMESPACES","ILLEGAL_CHARACTER","DUPLICATE_ATTRIBUTE","UNQUOTED_ATTRIBUTE_VALUE","BOOLEAN_ATTRIBUTE_REJECTED","SECURITY_PROTOTYPE_POLLUTION","SECURITY_RESERVED_OPTION","SECURITY_RESTRICTED_NAME","LIMIT_MAX_NESTED_TAGS","LIMIT_MAX_ATTRIBUTES","ENTITY_MAX_COUNT","ENTITY_MAX_SIZE","ENTITY_MAX_EXPANSIONS","ENTITY_MAX_EXPANDED_LENGTH","ENTITY_INVALID_KEY","ENTITY_INVALID_VALUE","UNSUPPORTED_ENCODING","INVALID_DECODER","ENCODING_MISMATCH","isSpace","isSpaceCode","DANGEROUS_PROPERTY_NAMES","criticalProperties","absolutePosition","_baseOffset","errorPositionOf","sanitizeContent","hasCR","segStart","expectMatch","expected","errorMsg","caseInsensitive","canRead","matchAhead","updateBufferBoundary","ensureCanRead","defaultOnDangerousProperty","declaration","pi","nsPrefix","whitespaceText","booleanType","duplicate","unpaired","stopNodes","strictReservedNames","onDangerousProperty","sanitizeNames","only","doctypeOptions","enabled","autoClose","limits","maxNestedTags","maxAttributesPerTag","feedable","maxBufferSize","autoFlush","flushThreshold","bufferSize","exitIf","OutputBuilder","decoding","encoding","customDecoders","ALL_RESERVED","validatePropertyName","isInteger","stopSet","normalizeTagEntry","stopNodesSet","skipSet","tagsSet","opts","existingUnpaired","HTML_VOID_ELEMENTS","onEof","onMismatch","collectErrors","resolveAutoClose","nested","skipEnclosures","scanTagExpEnd","buf","buffer","pairs","_quotePairs","capacity","pairsLen","inSingle","inDouble","_quotePairsLen","scanTagExpEndFast","StringSource","_marks","Int32Array","markTokenStart","rewindToMark","clearMark","flush","origin","marksLen","readCh","readChAt","readStr","from","readUpto","stopStr","inputLength","stopLength","readUptoChar","stopChar","readUptoCloseTag","tagMatchStart","state","matched","readFromBuffer","updateIndex","createByteScanStrategy","decodeCharAt","nodeEncoding","isContinuationByte","width","b","stopBuffer","Buffer","stopCode","shouldUpdate","_tokenStart","decodeCharAtFixedWidth1","decodeCharAtUtf8","b0","DEFAULT_SCAN_STRATEGY","BufferSource","bytesArr","profile","bomLength","subarray","decodeFirst","decoder","descriptor","createDecoder","write","strategy","scanStrategy","encodingName","_quotePairsUsable","quotePairsUsable","sniff","bytes","bomMatch","bomCandidates","sig","bomBytes","equals","matchBom","declaredEncoding","offset","declMatch","min","encMatch","sniffDeclaration","a","resolve","sameEncoding","createTextDecoderAdapter","label","TextDecoder","fatal","ignoreBOM","stream","EncodingRegistry","_byName","_seedDefaults","aliases","selfSynchronizing","variableWidth","inner","pending","work","concatBytes","swapped","tmp","swapBytePairs","createUtf16BeAdapter","probe","alias","sort","defaultEncodingRegistry","isIllegalAttrCode","scanAttrValue","flushAttributes","parsedAttrs","attrsExpStart","rawAttrMatchCount","maxAttrs","currentTagDetail","attrMeta","outputBuilder","TagExp","selfClosing","_attrsExp","_attrsExpStart","_rawAttrMatchCount","_parsedAttrs","readTagExp","expStart","collectQuotes","relEnd","exp","usableQuotes","buildTagExpObj","forceToReadAttrs","quotePairs","quotePairsLen","tagExp","attrsLocalOffset","attrsExp","trimEnd","INVALID_TAG_NAME","attrsOffset","results","usePairs","pairIdx","quote","closeLocal","parseAttributes","dupMode","boolMode","rawVal","processAttrName","attrVal","rawAttributesLen","collectRawAttributes","StopNodeProcessor","_tagName","_nested","_enclosures","_content","_depth","_active","isActive","activate","resumeAfterOpenTag","collect","enclosuresLen","_collectDepthOnly","_collectEnclosureOnly","_collectFull","_collectPlain","_tryConsumeCloseTag","_finish","_unclosedError","_stepDepthTracking","_trySkipEnclosure","encIdx","_matchEnclosureOpen","enc","_skipChars","open","interior","_readUpto","close","needed","_peekMatch","validClose","closeName","_readTagName","closeSuffix","_readToAngleClose","openName","attrText","_readTagTail","s0","sLen","strLen","readPiTag","skipOptions","inSingleQuotes","inDoubleQuotes","EOE","currentChar","nextChar","readPiExp","xmlDec","standalone","_nameValidators","readEntityExp","skipSourceWhitespace","entityNameStart","entityNameLen","readIdentifierVal","ep","readElementExp","elementNameStart","elementNameLen","getNameValidator","peek1","contentModel","readAttlistExp","readNotationExp","notationNameStart","notationNameLen","next","AutoCloseErrorType","UNCLOSED_EOF","MISMATCHED_CLOSE","PHANTOM_CLOSE","PARTIAL_TAG","AutoCloseHandler","autoCloseOptions","errors","handleEof","parserState","addTextNode","popTag","_recordError","handleMismatch","closingTagName","stackSnapshot","matchIndex","stackSnapshotLength","levelsToClose","handlePartialTag","originalError","_extractPartialTagName","tagTextData","getErrors","detail","closeMatch","keepSpace","getCachedName","rawName","computeFn","TagDetail","openEnd","Xml2JsParser","doctypeFound","autoCloseHandler","_unpairedSet","stopNodeExpressionsSet","isStopNode","skipTagExpressionsSet","isSkipTag","_exitIf","_nameCache","attrs","initializeParser","_stopNodeProcessor","_exitIfTriggered","_validQNames","lang","_createOutputBuilder","wasExited","strData","_parseAndFinalize","parseBytesArr","_registry","decodingOptions","requested","detected","buildProfileForBuffer","parseXml","preReadPos","tagStart","readClosingTag","readSpecialTag","readOpeningTag","runLen","finalizeXml","hasOpenTags","hasTrailingText","_parserState","partialTagError","isSourceExhaustedError","consumed","expectedRawName","tryMatchClosingTagName","closeMeta","closeEnd","processTagName","partial","MAX_SAFE_INTEGER","readClosingTagName","isUnpaired","isSkip","_stopNodeProcessorMeta","stopEnd","processedTagName","colonIdx","tagNamespace","matcherTagName","maxNested","raeAttrLen","stopNodeConfig","skipTagConfig","_closeMetaFor","exitDepth","pushTag","startCh","readComment","readCdata","docTypeEntities","bodyDone","subTagStart","bang","typeChar","dash2","typeChar2","escaped","regx","readDocType","ok","rawAttrName","resolveNsPrefix","sanitizeName","rawTagName","self","v","bind","skipNsPrefix","parts","FeedableSource","isComplete","_decodingOptions","requestedEncoding","_detecting","_sniffBuffer","alloc","_createDecoder","_decoder","feed","chunk","isBuffer","concat","declarationComplete","_resolveDetection","newData","_decodeNow","liveBytes","held","tail","StreamSource","attachStream","readable","onChunk","onEnd","onError","on","XMLParser","_feedParser","_feedSource","_isFeeding","_pendingBytes","_batchThreshold","ArrayBuffer","isView","_createParser","_lastParseErrors","parseStream","readableEnded","streamParser","Promise","reject","settled","fail","destroy","parseErr","_runParse","beforePos","_initFeedSession","appendedLength","_cleanupFeedSession","getParseErrors","OrderTrackingBuilder","siblingOrder","jPath","lastDot","parentPath","siblings","NumberValueParser","parsed","NaN","minLength","actual","maxLength","modifier","fixed","in","list","minOccurs","maxOccurs","date","Date","numericTypes","Traverser","validators","failures","_globalUnique","_siblingOrder","traverse","ele","rules","repeatable","checkOccurences","forEach","arrayPath","callForCommonProperties","checkDataAndType","isMapType","validateMandatoryFields","applyCustomValidators","checkBy","res","sets","extra","common","breakInSets","checkUnknownSiblings","checkMissingSiblings","siblingValues","_buildSiblingValues","newpath","_checkOrderingConstraints","_checkUniquenessConstraints","_checkRelationalConstraints","commonKeys","orderKey","order","fieldRules","keyIdx","after","refKey","refIdx","before","RELATIONAL_RULES","attr","actualRaw","fieldType","refRaw","cmp","_relationalCompare","violated","da","db","na","nb","unique","vals","fieldPath","_assertGlobalUnique","_checkUniqueInCollection","items","fieldKey","basePath","item","strVal","fieldName","eleType","expandedRules","_expandRange","validateDate","_checkDateBounds","validateBoolean","isValidNum","assertValue","setInvalidDataType","range","parseRange","parseDateRange","validations","setInvalidValueError","rulesForChild","nillable","checkNumeric","checkString","boolean","isInt","tagRule","rulesNode","unknownAllow","_originalTraverse","scalarRules","_runSiblingUniqueness","filter","RAW_VALUE_PARSERS","ruleParser","dataParser","ATTRS_BUCKET_PLACEHOLDER","ATTRS_BUCKET_TAG_RE","restoreAttrsBucketKeys","assertXmlString","xmldata","parseOrThrow","sanitizeAttrsBucketTag","fn","traverser"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"./lib/fxv.min.js","mappings":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAa,IAAID,IAEjBD,EAAU,IAAIC,GACf,CATD,CASGK,KAAM,I,mBCRT,IAAIC,EAAsB,CCA1BA,EAAwB,CAACL,EAASM,KACjC,IAAI,IAAIC,KAAOD,EACXD,EAAoBG,EAAEF,EAAYC,KAASF,EAAoBG,EAAER,EAASO,IAC5EE,OAAOC,eAAeV,EAASO,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3EF,EAAwB,CAACQ,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFT,EAAyBL,IACH,oBAAXkB,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeV,EAASkB,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeV,EAAS,aAAc,CAAEoB,OAAO,M,qqCCLlCC,EAAe,SAAAC,GAClC,SAAAD,EAAYE,EAASC,EAAMC,EAAMC,GAAM,IAADC,EAMc,OALlDA,EAAAL,EAAAL,KAAA,KAAMM,IAAQ,MACTK,KAAOD,EAAKE,YAAYD,KAC7BD,EAAKH,KAAOA,EACZG,EAAKF,KAAOA,EACZE,EAAKD,IAAMA,EACY,MAAvBI,MAAMC,mBAAND,MAAMC,kBAAiBJ,EAASA,EAAKE,aAAaF,CACpD,C,QAAC,O,EAAAL,G,EAAAD,G,sEAAAA,CAAA,CARiC,CAQjCW,EAR0CF,QCkCtC,SAASG,EAAaC,GAC3B,MAAgB,MAATA,GAAyB,OAATA,GAA0B,OAATA,GAA0B,OAATA,CAC3D,CAQO,SAASC,EAAqBX,GACnC,OAAOA,GAAQ,GAAc,KAATA,GAAwB,KAATA,GAAgBA,GAAQ,IAAMA,GAAQ,EAC3E,CAEO,SAASY,EAAyBC,EAASC,GAChD,IAAMC,EAAQF,EAAQG,UAAU,EAAGF,GAAOG,MAAM,SAChD,MAAO,CACLhB,KAAMc,EAAMG,OACZhB,IAAKa,EAAMA,EAAMG,OAAS,GAAGA,OAAS,EAE1C,CAEO,SAASC,EAAqBC,GACnC,OAAOA,EAAMC,WAAaD,EAAM,GAAGF,MACrC,CC7BA,MAAMI,EACJ,+CAoCIC,EACJ,8CAYIC,EACJD,sBAeIE,EAAeA,CAACC,EAAWhB,EAAMiB,EAAQ,MAC7C,MAEMC,EAAY,IAFFF,EAAUG,QAAQ,IAAK,QACxBnB,EAAKmB,QAAQ,IAAK,QAGjC,MAAO,CACLzB,KAAM,IAAI0B,OAAO,KAAKJ,MAAchB,OAAWiB,GAC/CI,OAAQ,IAAID,OAAO,IAAIF,KAAcD,GACrCK,MAAO,IAAIF,OAAO,IAAIF,QAAgBA,OAAgBD,GACtDM,QAAS,IAAIH,OAAO,KAAKpB,OAAWiB,GACpCO,SAAU,IAAIJ,OAAO,KAAKpB,cAAiBA,SAAaiB,KAItDQ,EAAYV,EAAaH,EAnE7BA,sBAoEIc,EAAYX,EAAaF,EAAiBC,EAAY,KAuBtDa,EAAqB,WAGrBC,EAAeb,EAAaY,EAFZA,EAAqB,aAIrCE,EAAaA,CAACC,EAAa,MAAOC,GAAY,IAC9CA,EAAkBH,EACA,QAAfE,EAAuBJ,EAAYD,EAe/B/B,EAAOA,CAACsC,GAAOF,aAAa,MAAOC,aAAY,GAAU,CAAC,IACrEF,EAAWC,EAAYC,GAAWrC,KAAKuC,KAAKD,GAsExCE,EAAc,CAAC,OAAQ,SAAU,QAAS,UAAW,YAa9CC,EAAkBA,CAACC,GAAcN,aAAa,MAAOC,aAAY,EAAOM,eAAe,MAAS,CAAC,KAC5G,IAAKH,EAAYI,SAASF,GACxB,MAAM,IAAIG,UACR,uBAAuBH,uBAAgCF,EAAYM,KAAK,SAI5E,MAAMC,EAAQZ,EAAWC,EAAYC,GAAWK,GAChD,IAAIM,EAAQ,IAAIC,IAEhB,MAAMC,EAAaZ,IACjB,MAAMa,EAASH,EAAMhE,IAAIsD,GACzB,QAAec,IAAXD,EAAsB,OAAOA,EAEjC,MAAME,EAASN,EAAMR,KAAKD,GAE1B,OADIU,EAAMM,KAAOX,GAAcK,EAAMO,IAAIjB,EAAKe,GACvCA,GAKT,OAFAH,EAAUM,MAAQ,KAAQR,EAAQ,IAAIC,KAE/BC,GChQT,IAKqBO,EAAgB,WACnC,SAAAA,EAAYrB,QAAU,IAAVA,IAAAA,EAAa,OACvB5D,KAAK4D,WAAaA,EAClB5D,KAAKoD,MAAQa,EAAgB,QAAS,CAAEL,WAAAA,IACxC5D,KAAKwB,KAAOyC,EAAgB,OAAQ,CAAEL,WAAAA,GACxC,CAAC,IAAAsB,EAAAD,EAAAtE,UAQA,OARA,EAEDwE,aAAA,SAAanE,GACX,OAAOhB,KAAKoD,MAAMpC,EACpB,EAAC,EAEDoE,YAAA,SAAYpE,GACV,OAAOhB,KAAKwB,KAAKR,EACnB,EAACiE,CAAA,CAbkC,GCLrC,SAASI,EAAWC,GAClB,OAAQA,GAAM,KAAOA,GAAM,KAASA,GAAM,KAAOA,GAAM,KAASA,GAAM,KAAOA,GAAM,KAAe,MAAPA,CAC7F,CAoFA,IAgBqBC,EAAuB,WAC1C,SAAAA,EAAYC,EAAcC,QAAP,IAAPD,IAAAA,EAAU,CAAC,GACrBxF,KAAK0F,cAAgCd,IAArBY,EAAQE,UAAyBF,EAAQE,SACzD1F,KAAK2F,iBAAsCf,IAAxBY,EAAQG,YACvBH,EAAQG,iBACgBf,IAAvBY,EAAQI,YAA2BJ,EAAQI,WAChD5F,KAAK6F,YAAcJ,CACrB,CA2EC,OAzEDF,EAAA5E,UAKAmF,SAAA,SAAS7D,GAGP,IAFgBA,EAAQ8D,WAAW,SAMjC,OAHK/F,KAAK0F,UACR1F,KAAK6F,YAAY,aAAc,2CAA4C,CAAExE,KAAM,EAAGC,IAAK,IAEtF,CAAE0E,QAAS,MAAOC,IAAK,GAGhC,IAAMC,EAAQjE,EAAQkE,QAAQ,OACf,IAAXD,GACFlG,KAAK6F,YAAY,aAAc,2CAA4C,CAAExE,KAAM,EAAGC,IAAK,IAY7F,IATA,IACM2E,EAAMC,EAAQ,EAEdE,EAAU,CAAC,UAAW,WAAY,cACxCC,EA5GJ,SAA4BC,GAM1B,IALA,IAAMC,EAAU,GACZC,EAAW,GACTC,EAAMH,EAAShE,OACjBoE,EAAI,EAEDA,EAAID,GACT,GAAK5E,EAAayE,EAASI,IAA3B,CAQA,IAFA,IAAMC,EAAaD,EACfE,EAAIF,EACDE,EAAIH,GAAO5E,EAAayE,EAASM,KAAKA,IAG7C,IADA,IAAMC,EAAYD,EACXA,EAAIH,GAAOpB,EAAWiB,EAASM,KAAKA,IAC3C,GAAIA,IAAMC,EAAV,CAUA,IAHA,IAAMrF,EAAO8E,EAASQ,MAAMD,EAAWD,GAEnCG,EAAIH,EACDG,EAAIN,GAAO5E,EAAayE,EAASS,KAAKA,IAC7C,GAAoB,MAAhBT,EAASS,GAAb,CAMA,IADAA,IACOA,EAAIN,GAAO5E,EAAayE,EAASS,KAAKA,IAE7C,IAAMC,EAAQV,EAASS,GACvB,GAAc,MAAVC,GAA2B,MAAVA,EAArB,CAKA,IAAMC,EAAaF,EAAI,EACjBG,EAAWZ,EAASH,QAAQa,EAAOC,IACvB,IAAdC,GAMJX,EAAQY,KAAK,CAAE3F,KAAAA,EAAMR,MAAOsF,EAASQ,MAAMG,EAAYC,GAAWhF,MAAOyE,IACzED,EAAIQ,EAAW,IANbV,GAAYF,EAASQ,MAAMH,EAAYC,GACvCF,EAAIE,EALN,MAHEJ,GAAYF,EAASQ,MAAMH,EAAYC,GACvCF,EAAIE,CAPN,MAHEJ,GAAYF,EAASQ,MAAMH,EAAYC,GACvCF,EAAIE,CAPN,MAHEJ,GAAYF,EAASQ,MAAMH,EAAYC,EAAI,GAAKH,EAAMG,EAAIH,GAC1DC,EAAIE,EAAID,EAAaC,EAAID,EAAa,CAZxC,MAHEH,GAAYF,EAASI,GACrBA,IA+CJ,MAAO,CAAEH,QAAAA,EAASC,SAAAA,EACpB,CAmD6CY,CAJxBnF,EAAQG,UAAU,EAAG8D,IAIrBmB,EAAShB,EAAlBE,QAAoBC,EAAQH,EAARG,SAExBR,EAAU,MACVsB,GAAkB,EAEbC,EAAI,EAAGA,EAAIF,EAAU/E,OAAQiF,IAAK,CACzC,IAAMC,EAAWH,EAAUE,GAAG/F,KACxBiG,EAAYJ,EAAUE,GAAGvG,MACzB0G,EAAUL,EAAUE,GAAGrF,MAEvByF,EAAavB,EAAQD,QAAQqB,IACf,IAAhBG,GACF3H,KAAK6F,YAAY,aACf,+CAAiD2B,EAAW,6CAC5D,CAAEnG,KAAM,EAAGC,IAAK,EAAIoG,IAGpB1H,KAAK2F,aAAegC,GAAcL,GACpCtH,KAAK6F,YAAY,aACf,8BAAgC2B,EAAW,oEAC3C,CAAEnG,KAAM,EAAGC,IAAK,EAAIoG,IAExBJ,EAAiBK,EAEA,YAAbH,GACgB,QAAdC,GAAqC,QAAdA,GACzBzH,KAAK6F,YAAY,aACf,4BAA8B4B,EAAY,8CAC1C,CAAEpG,KAAM,EAAGC,IAAK,EAAIoG,IAExB1B,EAAUyB,GACY,eAAbD,GACS,QAAdC,GAAqC,OAAdA,GACzBzH,KAAK6F,YAAY,aACf,+BAAiC4B,EAAY,uCAC7C,CAAEpG,KAAM,EAAGC,IAAK,EAAIoG,GAI5B,CAEA,IAAME,EAAkBpB,EAASqB,OAOjC,OANID,EAAgBtF,OAAS,GAC3BtC,KAAK6F,YAAY,aACf,8CAAgD+B,EAAkB,KAClE,CAAEvG,KAAM,EAAGC,IAAK,IAGb,CAAE0E,QAAAA,EAASC,IAAAA,EACpB,EAACV,CAAA,CAlFyC,GC7FvBuC,EAAoB,oBAAAA,IAAA,CA6BtC,OA5BDA,EAMOC,iBAAP,SAAwBjE,GAEtB,IADA,IAAM2C,EAAM3C,EAAIxB,OACPoE,EAAI,EAAGA,EAAID,EAAKC,IACvB,GAAI3E,EAAqB+B,EAAIkE,WAAWtB,IAAK,OAAOA,EAEtD,OAAQ,CACV,EAEA,EAIOuB,eAAP,SAAsBnE,GACpB,OAAuC,IAAhC9D,KAAK+H,iBAAiBjE,EAC/B,EAEA,EAIOoE,SAAP,SAAgB9G,GACd,MAAM,KAAMA,EAAK+G,SAAS,IAAIC,SAAS,EAAG,IAC5C,EAACN,CAAA,CA7BsC,GC6GpBO,EAAwB,WAM3C,SAAAA,EAAY7C,EAAS8C,EAAe1E,GAClC5D,KAAKwF,QAAUA,EACfxF,KAAKsI,cAAgBA,EACrBtI,KAAK4D,WAAaA,CACpB,CAEA,IAAAsB,EAAAmD,EAAA1H,UA4EC,OA5ED,EAIAmF,SAAA,SAASyC,GAKP,IALiB,IAADC,EACVjC,EArGV,SAA6BgC,GAK3B,IAJA,IAAME,EAAS,GACThC,EAAM8B,EAAQjG,OAChBoE,EAAI,EAEDA,EAAID,GAAK,CAId,IAHA,IAAMiC,EAAahC,EAGZA,EAAID,GAAO5E,EAAa0G,EAAQ7B,KAAKA,IAC5C,GAAIA,GAAKD,EAAK,MAEd,GAAmB,MAAf8B,EAAQ7B,GAAZ,CAWA,IAJA,IAAMiC,EAAYJ,EAAQzB,MAAM4B,EAAYhC,GAGtCG,EAAYH,EACXA,EAAID,IAAQ5E,EAAa0G,EAAQ7B,KAAsB,MAAf6B,EAAQ7B,IAAYA,IAMnE,IALA,IAAMlF,EAAO+G,EAAQzB,MAAMD,EAAWH,GAGlCkC,OAAW,EACXC,EAAInC,EACDmC,EAAIpC,GAAO5E,EAAa0G,EAAQM,KAAKA,IACxCA,EAAIpC,GAAsB,MAAf8B,EAAQM,KACrBD,EAAcL,EAAQzB,MAAMJ,EAAGmC,EAAI,GACnCnC,EAAImC,EAAI,GAOV,IAHA,IAAIC,OAAS,EACT9H,OAAK,EACL+H,EAAIrC,EACDqC,EAAItC,GAAO5E,EAAa0G,EAAQQ,KAAKA,IAC5C,GAAIA,EAAItC,IAAuB,MAAf8B,EAAQQ,IAA6B,MAAfR,EAAQQ,IAAa,CACzD,IAAM9B,EAAa8B,EAAI,EACjB7B,EAAWqB,EAAQpC,QAAQoC,EAAQQ,GAAI9B,IAC3B,IAAdC,IACF4B,EAAYP,EAAQQ,GACpB/H,EAAQuH,EAAQzB,MAAMG,EAAYC,GAClCR,EAAIQ,EAAW,EAKnB,CAEA,IAAM8B,EAAQ,CAAEvG,WAAYiG,GAC5BM,EAAM,GAAKL,EACXK,EAAM,GAAKxH,EACXwH,EAAM,GAAKJ,EACXI,EAAM,QAAmBpE,IAAdkE,QAAiClE,EAC5CoE,EAAM,GAAKF,EACXE,EAAM,GAAKhI,EACXyH,EAAOtB,KAAK6B,EA3CZ,MAFEtC,EAAIgC,EAAa,CA8CrB,CAEA,OAAOD,CACT,CAqCoBQ,CAAoBV,GAC9BW,EAAY,CAAC,EACbC,GAA2D,KAAb,OAAhCX,EAAAxI,KAAKwF,QAAQ4D,0BAAmB,EAAhCZ,EAAkCa,QAE7C3C,EAAI,EAAGA,EAAIH,EAAQjE,OAAQoE,IAAK,CACvC,IAAMa,EAAIhB,EAAQG,GAElB,GAAoB,IAAhBa,EAAE,GAAGjF,OACP,OAAOtC,KAAKsJ,KAAK,cAAe,cAAgB/B,EAAE,GAAK,8BAA+BA,GAGxF,IAAMgC,OAAqB3E,IAAT2C,EAAE,GACdiC,OAA0B5E,IAAT2C,EAAE,GAEzB,GAAIgC,IAAcC,EAIhB,OAAOxJ,KAAKsJ,KACV,yBACA,cAAgB/B,EAAE,GAAK,sBACvBA,GAEG,IAAKgC,IAAcvJ,KAAKwF,QAAQiE,uBACrC,OAAOzJ,KAAKsJ,KAAK,cAAe,sBAAwB/B,EAAE,GAAK,oBAAqBA,GAGtF,IAAMC,EAAWD,EAAE,GAEnB,IAAKvH,KAAKsI,cAAcnD,aAAaqC,GACnC,OAAOxH,KAAKsJ,KAAK,cAAe,cAAgB9B,EAAW,wBAAyBD,GAGtF,IAAME,EAAYF,EAAE,IAAM,GAE1B,GAAIiC,EAAgB,CAClB,IAAME,EAAa5B,EAAqBC,iBAAiBN,GACzD,IAAoB,IAAhBiC,EAAmB,CACrB,IAAMtI,EAAOqG,EAAUO,WAAW0B,GAClC,OAAO1J,KAAKsJ,KACV,mBAAmB,6BACUxB,EAAqBI,SAAS9G,GAAK,kBAAkBoG,EAAQ,WAC1FD,EAEJ,CAEA,GAAI4B,IAA2C,IAA5B1B,EAAUtB,QAAQ,KACnC,OAAOnG,KAAKsJ,KAAK,cAAc,cAAe9B,EAAQ,gCAAiCD,EAE3F,CAEA,GAAIC,EAASzB,WAAW,WAAsB,KAATwB,EAAE,IAAiC,QAApBvH,KAAK4D,WACvD,OAAO5D,KAAKsJ,KACV,cAAc,sCACwB9B,EAAQ,8CAC9CD,GAIJ,GAAKlH,OAAOM,UAAUC,eAAeC,KAAKqI,EAAW1B,GAGnD,OAAOxH,KAAKsJ,KAAK,cAAe,cAAgB9B,EAAW,iBAAkBD,GAF7E2B,EAAU1B,GAAY,CAI1B,CAEA,OAAO,CACT,EAAC,EAED8B,KAAA,SAAKlI,EAAMuI,EAAKnH,GACd,MAAO,CAAEoH,IAAK,CAAExI,KAAAA,EAAMuI,IAAAA,EAAKtI,KAAMkB,EAAqBC,IACxD,EAAC6F,CAAA,CAxF0C,GCxGxBwB,EAAkB,WACrC,SAAAA,EAAYrE,GAAe,IAADsE,OAAP,IAAPtE,IAAAA,EAAU,CAAC,GACrBxF,KAAK+J,oBAA8D,KAAd,OAA3BD,EAAAtE,EAAQ4D,0BAAmB,EAA3BU,EAA6BE,QACzD,CAEA,IAAA9E,EAAA2E,EAAAlJ,UAiFC,OAjFD,EAMAsJ,KAAA,SAAKhI,EAASyE,EAAGwD,GACf,GAAIjI,EAAQK,OAASoE,EAAI,GAAwB,MAAnBzE,EAAQyE,EAAI,IAAiC,MAAnBzE,EAAQyE,EAAI,GAClE,OAAO1G,KAAKmK,aAAalI,EAASyE,GAC7B,GACLzE,EAAQK,OAASoE,EAAI,GACF,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,GAEZ,OAAOwD,EAAiBE,gBAAgBnI,EAASyE,EAAI,GAChD,GACLzE,EAAQK,OAASoE,EAAI,GACF,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,GAEZ,OAAO1G,KAAKqK,WAAWpI,EAASyE,GAEhC,MAAM,IAAIhF,MAAM,wCAEpB,EAAC,EAEDyI,aAAA,SAAalI,EAASyE,GACpB,IAAM4D,EAAY5D,EAAI,EACtB,IAAKA,EAAI4D,EAAW5D,EAAIzE,EAAQK,OAAQoE,IACtC,GAAmB,MAAfzE,EAAQyE,IAAiC,MAAnBzE,EAAQyE,EAAI,IAAiC,MAAnBzE,EAAQyE,EAAI,GAAY,CAC1EA,GAAK,EACL,KACF,CAEF,GAAmB,MAAfzE,EAAQyE,GACV,MAAM,IAAIhF,MAAM,wCAGlB,IAAM6I,EAAOtI,EAAQG,UAAUkI,EAAW5D,EAAI,GACxCgD,EAAa5B,EAAqBC,iBAAiBwC,GACzD,IAAoB,IAAhBb,EAAmB,CACrB,IAAMtI,EAAOmJ,EAAKvC,WAAW0B,GAC7B,MAAM,IAAIhI,MAAM,6BAA6BoG,EAAqBI,SAAS9G,GAAK,eAClF,CACA,GAAIpB,KAAK+J,qBAA8C,IAAxBQ,EAAKpE,QAAQ,MAC1C,MAAM,IAAIzE,MAAM,kCAGlB,OAAOgF,CACT,EAAC,EAED2D,WAAA,SAAWpI,EAASyE,GAClB,IAAM4D,EAAY5D,EAAI,EACtB,IAAKA,EAAI4D,EAAW5D,EAAIzE,EAAQK,OAAQoE,IACtC,GAAmB,MAAfzE,EAAQyE,IAAiC,MAAnBzE,EAAQyE,EAAI,IAAiC,MAAnBzE,EAAQyE,EAAI,GAAY,CAC1EA,GAAK,EACL,KACF,CAEF,GAAmB,MAAfzE,EAAQyE,GACV,MAAM,IAAIhF,MAAM,2CAGlB,IAAM6I,EAAOtI,EAAQG,UAAUkI,EAAW5D,EAAI,GACxCgD,EAAa5B,EAAqBC,iBAAiBwC,GACzD,IAAoB,IAAhBb,EAAmB,CACrB,IAAMtI,EAAOmJ,EAAKvC,WAAW0B,GAC7B,MAAM,IAAIhI,MAAM,6BAA6BoG,EAAqBI,SAAS9G,GAAK,qBAClF,CAEA,OAAOsF,CACT,EAACmD,CAAA,CAtFoC,GCRxB,MAAMW,EAOnB/I,WAAAA,CAAYgJ,EAASjF,EAAU,CAAC,EAAGkF,GACjC1K,KAAKyK,QAAUA,EACfzK,KAAK2K,UAAYnF,EAAQmF,WAAa,IACtC3K,KAAK4K,SAAW5K,KAAK6K,OAAOJ,GAC5BzK,KAAK0K,KAAOA,EAEZ1K,KAAK8K,iBAAmB9K,KAAK4K,SAASG,KAAKC,GAAoB,kBAAbA,EAAIC,MACtDjL,KAAKkL,uBAAyBlL,KAAK4K,SAASG,KAAKC,QAAwBpG,IAAjBoG,EAAIxD,UAC5DxH,KAAKmL,qBAAuBnL,KAAK4K,SAASG,KAAKC,QAAwBpG,IAAjBoG,EAAII,SAC5D,CAQAP,MAAAA,CAAOJ,GACL,MAAMG,EAAW,GAGjB,IAAIlE,EAAI,EACJ2E,EAAc,GAElB,KAAO3E,EAAI+D,EAAQnI,QACbmI,EAAQ/D,KAAO1G,KAAK2K,UAElBjE,EAAI,EAAI+D,EAAQnI,QAAUmI,EAAQ/D,EAAI,KAAO1G,KAAK2K,WAEhDU,EAAYxD,SACd+C,EAASzD,KAAKnH,KAAKsL,cAAcD,EAAYxD,SAC7CwD,EAAc,IAGhBT,EAASzD,KAAK,CAAE8D,KAAM,kBACtBvE,GAAK,IAGD2E,EAAYxD,QACd+C,EAASzD,KAAKnH,KAAKsL,cAAcD,EAAYxD,SAE/CwD,EAAc,GACd3E,MAGF2E,GAAeZ,EAAQ/D,GACvBA,KASJ,OAJI2E,EAAYxD,QACd+C,EAASzD,KAAKnH,KAAKsL,cAAcD,EAAYxD,SAGxC+C,CACT,CAQAU,aAAAA,CAAcC,GACZ,MAAMC,EAAU,CAAEP,KAAM,OAwBxB,IAAIQ,EAAiB,KACjBC,EAAkBH,EAEtB,MAAMI,EAAeJ,EAAK/I,MAAM,8BAChC,GAAImJ,IACFD,EAAkBC,EAAa,GAAKA,EAAa,GAC7CA,EAAa,IAAI,CACnB,MAAMC,EAAUD,EAAa,GAAG7E,MAAM,GAAI,GACtC8E,IACFH,EAAiBG,EAErB,CAIF,IAAIC,EAcAC,EAbAC,EAAiBL,EAErB,GAAIA,EAAgBtH,SAAS,MAAO,CAClC,MAAM4H,EAAUN,EAAgBvF,QAAQ,MAIxC,GAHA0F,EAAYH,EAAgBtJ,UAAU,EAAG4J,GAASnE,OAClDkE,EAAiBL,EAAgBtJ,UAAU4J,EAAU,GAAGnE,QAEnDgE,EACH,MAAM,IAAInK,MAAM,iCAAiC6J,IAErD,CAIA,IAAIU,EAAgB,KAEpB,GAAIF,EAAe3H,SAAS,KAAM,CAChC,MAAM8H,EAAaH,EAAeI,YAAY,KACxCC,EAAUL,EAAe3J,UAAU,EAAG8J,GAAYrE,OAClDwE,EAAUN,EAAe3J,UAAU8J,EAAa,GAAGrE,OAG/B,CAAC,QAAS,OAAQ,MAAO,QAAQzD,SAASiI,IAClE,eAAetI,KAAKsI,IAGpBP,EAAMM,EACNH,EAAgBI,GAGhBP,EAAMC,CAEV,MACED,EAAMC,EAGR,IAAKD,EACH,MAAM,IAAIpK,MAAM,4BAA4B6J,KAS9C,GANAC,EAAQM,IAAMA,EACVD,IACFL,EAAQK,UAAYA,GAIlBJ,EACF,GAAIA,EAAerH,SAAS,KAAM,CAChC,MAAMkI,EAAUb,EAAetF,QAAQ,KACvCqF,EAAQhE,SAAWiE,EAAerJ,UAAU,EAAGkK,GAASzE,OACxD2D,EAAQ/D,UAAYgE,EAAerJ,UAAUkK,EAAU,GAAGzE,MAC5D,MACE2D,EAAQhE,SAAWiE,EAAe5D,OAKtC,GAAIoE,EAAe,CACjB,MAAMM,EAAWN,EAAczJ,MAAM,kBACjC+J,GACFf,EAAQJ,SAAW,MACnBI,EAAQgB,cAAgBC,SAASF,EAAS,GAAI,KAE9Cf,EAAQJ,SAAWa,CAEvB,CAEA,OAAOT,CACT,CAMA,UAAIlJ,GACF,OAAOtC,KAAK4K,SAAStI,MACvB,CAMAoK,eAAAA,GACE,OAAO1M,KAAK8K,gBACd,CAMA6B,qBAAAA,GACE,OAAO3M,KAAKkL,sBACd,CAMA0B,mBAAAA,GACE,OAAO5M,KAAKmL,oBACd,CAMAhD,QAAAA,GACE,OAAOnI,KAAKyK,OACd,ECjNK,MAAMoC,EAIXpL,WAAAA,CAAYqL,GACV9M,KAAK+M,SAAWD,CAClB,CAMA,aAAInC,GACF,OAAO3K,KAAK+M,SAASpC,SACvB,CAMAqC,aAAAA,GACE,MAAMC,EAAOjN,KAAK+M,SAASE,KAC3B,OAAOA,EAAK3K,OAAS,EAAI2K,EAAKA,EAAK3K,OAAS,GAAGwJ,SAAMlH,CACvD,CAMAsI,mBAAAA,GACE,MAAMD,EAAOjN,KAAK+M,SAASE,KAC3B,OAAOA,EAAK3K,OAAS,EAAI2K,EAAKA,EAAK3K,OAAS,GAAGuJ,eAAYjH,CAC7D,CAOAuI,YAAAA,CAAa3F,GACX,MAAMyF,EAAOjN,KAAK+M,SAASE,KAC3B,GAAoB,IAAhBA,EAAK3K,OACT,OAAO2K,EAAKA,EAAK3K,OAAS,GAAG8K,SAAS5F,EACxC,CAOA6F,OAAAA,CAAQ7F,GACN,MAAMyF,EAAOjN,KAAK+M,SAASE,KAC3B,GAAoB,IAAhBA,EAAK3K,OAAc,OAAO,EAC9B,MAAMgL,EAAUL,EAAKA,EAAK3K,OAAS,GACnC,YAA0BsC,IAAnB0I,EAAQF,QAAwB5F,KAAY8F,EAAQF,MAC7D,CAQAG,gBAAAA,CAAiB/F,GACf,OAAOxH,KAAK+M,SAASQ,iBAAiB/F,EACxC,CAQAgG,gBAAAA,CAAiBhG,GACf,OAAOxH,KAAK+M,SAASS,iBAAiBhG,EACxC,CAMAiG,WAAAA,GACE,MAAMR,EAAOjN,KAAK+M,SAASE,KAC3B,OAAoB,IAAhBA,EAAK3K,QAAsB,EACxB2K,EAAKA,EAAK3K,OAAS,GAAG8I,UAAY,CAC3C,CAMAsC,UAAAA,GACE,MAAMT,EAAOjN,KAAK+M,SAASE,KAC3B,OAAoB,IAAhBA,EAAK3K,QAAsB,EACxB2K,EAAKA,EAAK3K,OAAS,GAAGqL,SAAW,CAC1C,CAOAC,QAAAA,GACE,OAAO5N,KAAKyN,aACd,CAMAI,QAAAA,GACE,OAAO7N,KAAK+M,SAASE,KAAK3K,MAC5B,CAQA6F,QAAAA,CAASwC,EAAWmD,GAAmB,GACrC,OAAO9N,KAAK+M,SAAS5E,SAASwC,EAAWmD,EAC3C,CAMAC,OAAAA,GACE,OAAO/N,KAAK+M,SAASE,KAAKe,IAAIC,GAAKA,EAAEnC,IACvC,CAOAvF,OAAAA,CAAQ2H,GACN,OAAOlO,KAAK+M,SAASxG,QAAQ2H,EAC/B,CAOAC,UAAAA,CAAWC,GACT,OAAOA,EAAQD,WAAWnO,KAAK+M,SACjC,EAsBa,MAAMsB,EAMnB5M,WAAAA,CAAY+D,EAAU,CAAC,GACrBxF,KAAK2K,UAAYnF,EAAQmF,WAAa,IACtC3K,KAAKiN,KAAO,GACZjN,KAAKsO,cAAgB,GAIrBtO,KAAKuO,iBAAmB,KACxBvO,KAAKwO,MAAQ,IAAI3B,EAAY7M,MAG7BA,KAAKyO,WAAa,EACpB,CAUAtH,IAAAA,CAAKuH,EAASC,EAAa,KAAM9C,EAAY,KAAMrG,EAAU,MAC3DxF,KAAKuO,iBAAmB,KAGpBvO,KAAKiN,KAAK3K,OAAS,IACrBtC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAG8K,YAASxI,GAI3C,MAAMgK,EAAe5O,KAAKiN,KAAK3K,OAC/B,IAAIuM,EAAQ7O,KAAKsO,cAAcM,GAC1BC,IAMHA,EAAQ,CAAEC,OAAQ,IAAIrK,IAAOsK,MAAO,GACpC/O,KAAKsO,cAAcM,GAAgBC,GAIrC,MAAMG,EAAanD,EAAY,GAAGA,KAAa6C,IAAYA,EAGrDf,EAAUkB,EAAMC,OAAOtO,IAAIwO,IAAe,EAG1C5D,EAAWyD,EAAME,MAGvBF,EAAMC,OAAO/J,IAAIiK,EAAYrB,EAAU,GACvCkB,EAAME,QAGN,MAAME,EAAO,CACXnD,IAAK4C,EACLtD,SAAUA,EACVuC,QAASA,GAGP9B,UACFoD,EAAKpD,UAAYA,GAGf8C,UACFM,EAAK7B,OAASuB,GAGhB3O,KAAKiN,KAAK9F,KAAK8H,GAGf,MAAMC,EAAQlP,KAAKiN,KAAK3K,OAOlB6M,EAAmB,OAAZ3J,EAAmBA,EAAQ2J,KAAO,KAC/C,GAAIA,SAAuCA,EAAK7M,OAAS,GAAKqM,EAC5D,IAAK,IAAIjI,EAAI,EAAGA,EAAIyI,EAAK7M,OAAQoE,IAAK,CACpC,MAAMlF,EAAO2N,EAAKzI,QACO9B,IAArB+J,EAAWnN,IACbxB,KAAKyO,WAAWtH,KAAK,CAAE+H,QAAO1N,OAAMR,MAAO2N,EAAWnN,IAE1D,CAEJ,CAMA4N,GAAAA,GACE,GAAyB,IAArBpP,KAAKiN,KAAK3K,OAAc,OAC5BtC,KAAKuO,iBAAmB,KAExB,MAAMU,EAAOjP,KAAKiN,KAAKmC,MAEnBpP,KAAKsO,cAAchM,OAAStC,KAAKiN,KAAK3K,OAAS,IACjDtC,KAAKsO,cAAchM,OAAStC,KAAKiN,KAAK3K,OAAS,GAOjD,MAAM+M,EAAcrP,KAAKiN,KAAK3K,OAAS,EACvC,KACEtC,KAAKyO,WAAWnM,OAAS,GACzBtC,KAAKyO,WAAWzO,KAAKyO,WAAWnM,OAAS,GAAG4M,OAASG,GAErDrP,KAAKyO,WAAWW,MAGlB,OAAOH,CACT,CAOAK,aAAAA,CAAcX,GACZ,GAAI3O,KAAKiN,KAAK3K,OAAS,EAAG,CACxB,MAAMgL,EAAUtN,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GACzCqM,UACFrB,EAAQF,OAASuB,EAErB,CACF,CAMA3B,aAAAA,GACE,OAAOhN,KAAKiN,KAAK3K,OAAS,EAAItC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAGwJ,SAAMlH,CACtE,CAMAsI,mBAAAA,GACE,OAAOlN,KAAKiN,KAAK3K,OAAS,EAAItC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAGuJ,eAAYjH,CAC5E,CAOAuI,YAAAA,CAAa3F,GACX,GAAyB,IAArBxH,KAAKiN,KAAK3K,OACd,OAAOtC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAG8K,SAAS5F,EAClD,CAOA6F,OAAAA,CAAQ7F,GACN,GAAyB,IAArBxH,KAAKiN,KAAK3K,OAAc,OAAO,EACnC,MAAMgL,EAAUtN,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAC7C,YAA0BsC,IAAnB0I,EAAQF,QAAwB5F,KAAY8F,EAAQF,MAC7D,CAYAG,gBAAAA,CAAiB/F,GACf,MAAM+H,EAAOvP,KAAKyO,WAClB,IAAK,IAAI/H,EAAI6I,EAAKjN,OAAS,EAAGoE,GAAK,EAAGA,IACpC,GAAI6I,EAAK7I,GAAGlF,OAASgG,EAAU,OAAO+H,EAAK7I,GAAG1F,KAGlD,CAQAwM,gBAAAA,CAAiBhG,GACf,MAAM+H,EAAOvP,KAAKyO,WAClB,IAAK,IAAI/H,EAAI6I,EAAKjN,OAAS,EAAGoE,GAAK,EAAGA,IACpC,GAAI6I,EAAK7I,GAAGlF,OAASgG,EAAU,OAAO,EAExC,OAAO,CACT,CAMAiG,WAAAA,GACE,OAAyB,IAArBzN,KAAKiN,KAAK3K,QAAsB,EAC7BtC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAG8I,UAAY,CACrD,CAMAsC,UAAAA,GACE,OAAyB,IAArB1N,KAAKiN,KAAK3K,QAAsB,EAC7BtC,KAAKiN,KAAKjN,KAAKiN,KAAK3K,OAAS,GAAGqL,SAAW,CACpD,CAOAC,QAAAA,GACE,OAAO5N,KAAKyN,aACd,CAMAI,QAAAA,GACE,OAAO7N,KAAKiN,KAAK3K,MACnB,CAQA6F,QAAAA,CAASwC,EAAWmD,GAAmB,GACrC,MAAM0B,EAAM7E,GAAa3K,KAAK2K,UAG9B,GAFmB6E,IAAQxP,KAAK2K,YAAkC,IAArBmD,EAE9B,CACb,GAA8B,OAA1B9N,KAAKuO,iBACP,OAAOvO,KAAKuO,iBAEd,MAAM1J,EAAS7E,KAAKiN,KAAKe,IAAIC,GAC1BA,EAAEpC,UAAa,GAAGoC,EAAEpC,aAAaoC,EAAEnC,MAAQmC,EAAEnC,KAC9CxH,KAAKkL,GAEP,OADAxP,KAAKuO,iBAAmB1J,EACjBA,CACT,CAEA,OAAO7E,KAAKiN,KAAKe,IAAIC,GAClBH,GAAoBG,EAAEpC,UAAa,GAAGoC,EAAEpC,aAAaoC,EAAEnC,MAAQmC,EAAEnC,KAClExH,KAAKkL,EACT,CAMAzB,OAAAA,GACE,OAAO/N,KAAKiN,KAAKe,IAAIC,GAAKA,EAAEnC,IAC9B,CAKA9G,KAAAA,GACEhF,KAAKuO,iBAAmB,KACxBvO,KAAKiN,KAAO,GACZjN,KAAKsO,cAAgB,GACrBtO,KAAKyO,WAAa,EACpB,CAOAlI,OAAAA,CAAQ2H,GACN,MAAMtD,EAAWsD,EAAWtD,SAE5B,OAAwB,IAApBA,EAAStI,SAIT4L,EAAWxB,kBACN1M,KAAKyP,uBAAuB7E,GAG9B5K,KAAK0P,aAAa9E,GAC3B,CAKA8E,YAAAA,CAAa9E,GACX,GAAI5K,KAAKiN,KAAK3K,SAAWsI,EAAStI,OAChC,OAAO,EAGT,IAAK,IAAIoE,EAAI,EAAGA,EAAIkE,EAAStI,OAAQoE,IACnC,IAAK1G,KAAK2P,cAAc/E,EAASlE,GAAI1G,KAAKiN,KAAKvG,GAAIA,IAAM1G,KAAKiN,KAAK3K,OAAS,GAC1E,OAAO,EAIX,OAAO,CACT,CAKAmN,sBAAAA,CAAuB7E,GACrB,IAAIgF,EAAU5P,KAAKiN,KAAK3K,OAAS,EAC7BuN,EAASjF,EAAStI,OAAS,EAE/B,KAAOuN,GAAU,GAAKD,GAAW,GAAG,CAClC,MAAMpE,EAAUZ,EAASiF,GAEzB,GAAqB,kBAAjBrE,EAAQP,KAA0B,CAGpC,GAFA4E,IAEIA,EAAS,EACX,OAAO,EAGT,MAAMC,EAAUlF,EAASiF,GACzB,IAAIE,GAAQ,EAEZ,IAAK,IAAIrJ,EAAIkJ,EAASlJ,GAAK,EAAGA,IAC5B,GAAI1G,KAAK2P,cAAcG,EAAS9P,KAAKiN,KAAKvG,GAAIA,IAAM1G,KAAKiN,KAAK3K,OAAS,GAAI,CACzEsN,EAAUlJ,EAAI,EACdmJ,IACAE,GAAQ,EACR,KACF,CAGF,IAAKA,EACH,OAAO,CAEX,KAAO,CACL,IAAK/P,KAAK2P,cAAcnE,EAASxL,KAAKiN,KAAK2C,GAAUA,IAAY5P,KAAKiN,KAAK3K,OAAS,GAClF,OAAO,EAETsN,IACAC,GACF,CACF,CAEA,OAAOA,EAAS,CAClB,CAKAF,aAAAA,CAAcnE,EAASyD,EAAMe,GAC3B,GAAoB,MAAhBxE,EAAQM,KAAeN,EAAQM,MAAQmD,EAAKnD,IAC9C,OAAO,EAGT,QAA0BlH,IAAtB4G,EAAQK,WACgB,MAAtBL,EAAQK,WAAqBL,EAAQK,YAAcoD,EAAKpD,UAC1D,OAAO,EAIX,QAAyBjH,IAArB4G,EAAQhE,SAAwB,CAClC,IAAKwI,EACH,OAAO,EAGT,IAAKf,EAAK7B,UAAY5B,EAAQhE,YAAYyH,EAAK7B,QAC7C,OAAO,EAGT,QAA0BxI,IAAtB4G,EAAQ/D,WACNwI,OAAOhB,EAAK7B,OAAO5B,EAAQhE,aAAeyI,OAAOzE,EAAQ/D,WAC3D,OAAO,CAGb,CAEA,QAAyB7C,IAArB4G,EAAQJ,SAAwB,CAClC,IAAK4E,EACH,OAAO,EAGT,MAAMrC,EAAUsB,EAAKtB,SAAW,EAEhC,GAAyB,UAArBnC,EAAQJ,UAAoC,IAAZuC,EAClC,OAAO,EACF,GAAyB,QAArBnC,EAAQJ,UAAsBuC,EAAU,GAAM,EACvD,OAAO,EACF,GAAyB,SAArBnC,EAAQJ,UAAuBuC,EAAU,GAAM,EACxD,OAAO,EACF,GAAyB,QAArBnC,EAAQJ,UAAsBuC,IAAYnC,EAAQgB,cAC3D,OAAO,CAEX,CAEA,OAAO,CACT,CAOA2B,UAAAA,CAAWC,GACT,OAAOA,EAAQD,WAAWnO,KAC5B,CAMAkQ,QAAAA,GACE,MAAO,CACLjD,KAAMjN,KAAKiN,KAAKe,IAAIiB,IAAQ,IAAMA,KAClCX,cAAetO,KAAKsO,cAAcN,IAAIa,GAASA,EAAQ,CAAEC,OAAQ,IAAIrK,IAAIoK,EAAMC,QAASC,MAAOF,EAAME,OAAUF,GAC/GsB,UAAWnQ,KAAKyO,WAAWT,IAAIoC,IAAS,IAAMA,KAElD,CAMAC,OAAAA,CAAQH,GACNlQ,KAAKuO,iBAAmB,KACxBvO,KAAKiN,KAAOiD,EAASjD,KAAKe,IAAIiB,IAAQ,IAAMA,KAC5CjP,KAAKsO,cAAgB4B,EAAS5B,cAAcN,IAAIa,GAASA,EAAQ,CAAEC,OAAQ,IAAIrK,IAAIoK,EAAMC,QAASC,MAAOF,EAAME,OAAUF,GACzH7O,KAAKyO,YAAcyB,EAASC,WAAa,IAAInC,IAAIoC,IAAS,IAAMA,IAClE,CAkBAE,QAAAA,GACE,OAAOtQ,KAAKwO,KACd,ECrpBW,SAAA+B,EAAAC,EAAAC,GAAA,QAAAC,EAAA,EAAAA,EAAAD,EAAAnO,OAAAoO,IAAA,KAAAtQ,EAAAqQ,EAAAC,GAAA,EAAAnQ,WAAAH,EAAAG,aAAA,EAAAH,EAAAuQ,cAAA,YAAAvQ,IAAAA,EAAAwQ,UAAA,GAAAvQ,OAAAC,eAAAkQ,EAAAK,EAAAzQ,EAAAD,KAAAC,EAAA,WAAAyQ,EAAAH,GAAA,IAAAhK,EAAA,SAAAgK,GAAA,oBAAAA,IAAAA,EAAA,OAAAA,EAAA,IAAAF,EAAAE,EAAA5P,OAAAgQ,aAAA,YAAAN,EAAA,KAAA9J,EAAA8J,EAAA3P,KAAA6P,EAAAD,UAAA,oBAAA/J,EAAA,OAAAA,EAAA,UAAArC,UAAA,uDAAA4L,OAAAS,EAAA,CAAAK,CAAAL,GAAA,uBAAAhK,EAAAA,EAAAA,EAAA,GAIb,IAcqBsK,EAAc,WAIjC,SAAAA,EAAYC,QAAQ,IAARA,IAAAA,EAAW,IACrBjR,KAAKkR,aAAeD,EAASjD,IAAI,SAACmD,GAAI,OAAK,IAAI3G,EAAW2G,EAAK,GAC/DnR,KAAK+M,SAAW,IAAIsB,EACpBrO,KAAKoR,SAAWpR,KAAKkR,aAAa5O,OAAS,CAC7C,CAAC,IA1BUkO,EAAAC,EA0BVvL,EAAA8L,EAAArQ,UA0BA,OApBDuE,EAOAiC,KAAA,SAAKuH,EAAS2C,GACZ,IAAKrR,KAAKoR,SAAU,OAAO,EAC3BpR,KAAK+M,SAAS5F,KAAKuH,EAAS2C,GAAiB,MAC7C,IAAK,IAAI3K,EAAI,EAAGA,EAAI1G,KAAKkR,aAAa5O,OAAQoE,IAC5C,GAAI1G,KAAK+M,SAASxG,QAAQvG,KAAKkR,aAAaxK,IAAK,OAAO,EAE1D,OAAO,CACT,EAEA,EACA0I,IAAA,WACOpP,KAAKoR,UACVpR,KAAK+M,SAASqC,KAChB,EApDWoB,EAoDVQ,GApDUP,EAoDV,EAAAtQ,IAAA,UAAAK,IAxBD,WACE,OAAOR,KAAKoR,QACd,MA9BWb,EAAAC,EAAA7P,UAAA8P,GAAApQ,OAAAC,eAAAkQ,EAAA,aAAAI,UAAA,IAAAJ,CA8BV,CAZgC,GCAdc,EAAgB,oBAAAA,IAAA,CA2BlC,OA1BDA,EAMOC,QAAP,SAAetP,EAASyM,EAAShI,GAI/B,IAHA,IAAM8K,EAAS,KAAO9C,EAClB+C,EAAa/K,IAEJ,CACX,IAAMgL,EAAMzP,EAAQkE,QAAQqL,EAAQC,GACpC,IAAa,IAATC,EACF,MAAM,IAAIhQ,MAAM,yBAAyBgN,EAAO,MAIlD,IADA,IAAI7F,EAAI6I,EAAMF,EAAOlP,OACduG,EAAI5G,EAAQK,QAAU,KAAKyB,KAAK9B,EAAQ4G,KAAKA,IAEpD,GAAmB,MAAf5G,EAAQ4G,GACV,OAAOA,EAIT4I,EAAaC,EAAMF,EAAOlP,MAC5B,CACF,EAACgP,CAAA,CA3BkC,GChB/BK,EAAiB,CACnBC,eAAgBC,IAChBC,cAAeD,KAGEE,EAAgB,WACjC,SAAAA,EAAYvM,EAAS5B,QAAU,IAAVA,IAAAA,EAAa,OAC9B5D,KAAKwF,QAAUnF,OAAO2R,OAAO,CAAC,EAAGL,EAAgBnM,GACjDxF,KAAKiS,uBAAyBzM,EAC9BxF,KAAK4D,WAAaA,CACtB,CAAC,IAAAsB,EAAA6M,EAAApR,UAoPA,OApPA,EAEDyJ,gBAAA,SAAgBnI,EAASyE,GACrB,GACuB,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,IACO,MAAnBzE,EAAQyE,EAAI,GAEZ,MAAM,IAAIhF,MAAM,kCAGpBgF,GAAK,EAOL,IANA,IAAIwL,EAAqB,EACrBC,GAAU,EACVnI,GAAU,EACVlB,EAAY,KACZsJ,EAAc,EAEX1L,EAAIzE,EAAQK,OAAQoE,IAGvB,GAAkB,OAAdoC,EAIJ,GAAKqJ,GAAYnI,GAA2B,MAAf/H,EAAQyE,IAA6B,MAAfzE,EAAQyE,GAK3D,GAAmB,MAAfzE,EAAQyE,IAAesD,EAuBpB,GAAmB,MAAf/H,EAAQyE,IASf,GARIsD,EACuB,MAAnB/H,EAAQyE,EAAI,IAAiC,MAAnBzE,EAAQyE,EAAI,KACtCsD,GAAU,EACVkI,KAGJA,IAEuB,IAAvBA,EAA0B,UACR,MAAfjQ,EAAQyE,KACfyL,GAAU,OAlCsB,CAChC,GAAIA,GAAWE,EAAOpQ,EAAS,UAAWyE,GAAI,CAC1CA,GAAK,EAEL,IAAA4L,EADmBtS,KAAKuS,mBAAmBtQ,EAASyE,EAAI,EAAG0L,GAA1DA,EAAWE,EAAA,GAAE5L,EAAC4L,EAAA,EAEnB,MAAO,GAAIH,GAAWE,EAAOpQ,EAAS,WAAYyE,GAC9CA,GAAK,EACLA,EAAI1G,KAAKwS,oBAAoBvQ,EAASyE,EAAI,OAEvC,IAAIyL,GAAWE,EAAOpQ,EAAS,WAAYyE,GAAI,CAClDA,GAAK,EACLA,EAAI1G,KAAKyS,oBAAoBxQ,EAASyE,EAAI,GAC1C,QACJ,CAAO,GAAIyL,GAAWE,EAAOpQ,EAAS,YAAayE,GAC/CA,GAAK,EACLA,EAAI1G,KAAK0S,qBAAqBzQ,EAASyE,EAAI,OAExC,KAAI2L,EAAOpQ,EAAS,MAAOyE,GAG9B,MAAM,IAAIhF,MAAM,mBAFhBsI,GAAU,CAGd,EACAkI,GACJ,MA3BIpJ,EAAY7G,EAAQyE,QAJhBzE,EAAQyE,KAAOoC,IAAWA,EAAY,MA8ClD,GAAkB,OAAdA,GAA6C,IAAvBoJ,EACtB,MAAM,IAAIxQ,MAAM,oBAGpB,OAAOgF,CACX,EAAC,EAED6L,mBAAA,SAAmBtQ,EAASyE,EAAG0L,GAK3B,IADA,IAAMvL,EAHNH,EAAIiM,EAAe1Q,EAASyE,GAIrBA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAsB,MAAfzE,EAAQyE,IAA6B,MAAfzE,EAAQyE,IACjFA,IAEJ,IAeIkM,EAfEC,EAAa5Q,EAAQG,UAAUyE,EAAWH,GAMhD,GALAoM,EAAaD,EAAY7S,KAAK4D,YAE9B8C,EAAIiM,EAAe1Q,EAASyE,IAGvB1G,KAAKiS,sBAAuB,CAC7B,GAAkD,WAA9ChQ,EAAQG,UAAUsE,EAAGA,EAAI,GAAGqM,cAC5B,MAAM,IAAIrR,MAAM,uCACb,GAAmB,MAAfO,EAAQyE,GACf,MAAM,IAAIhF,MAAM,uCAExB,CAGgB,IAAAsR,EACGC,EAAgBhR,EAASyE,EAAG,UAA9CA,EAACsM,EAAA,GAAEJ,EAAWI,EAAA,GAEf,IAAAE,EAA0ClT,KAAKwF,QAAvCsM,EAAaoB,EAAbpB,cAAeF,EAAcsB,EAAdtB,eAEvB,GAAIgB,EAAYtQ,OAASwP,EACrB,MAAM,IAAIpQ,MAAM,WACDmR,EAAU,WAAWD,EAAYtQ,OAAM,mCAAmCwP,EAAa,KAK1G,IAAkC,IAA9Bc,EAAYzM,QAAQ,QACpBiM,EACkBR,EACd,MAAM,IAAIlQ,MAAM,iBACK0Q,EAAW,8BAA8BR,EAAc,KAKpF,MAAO,CAACQ,EAAa1L,EAAI,EAC7B,EAAC,EAEDgM,qBAAA,SAAqBzQ,EAASyE,GAK1B,IADA,IAAMG,EAHNH,EAAIiM,EAAe1Q,EAASyE,GAIrBA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAKA,IACrD,IAAMyM,EAAelR,EAAQG,UAAUyE,EAAWH,IACjD1G,KAAKiS,uBAAyBa,EAAaK,EAAcnT,KAAK4D,YAE/D8C,EAAIiM,EAAe1Q,EAASyE,GAE5B,IAAM0M,EAAiBnR,EAAQG,UAAUsE,EAAGA,EAAI,GAAGqM,cACnD,IAAK/S,KAAKiS,uBAA4C,WAAnBmB,GAAkD,WAAnBA,EAC9D,MAAM,IAAI1R,MAAM,qCAAqC0R,EAAc,KAevE,OAZA1M,EAAIiM,EAAe1Q,EADnByE,GAAK0M,EAAe9Q,QAGG,WAAnB8Q,EAGmB,MAAfnR,EADJyE,EAAIiM,EAAe1Q,EADlByE,EAAKuM,EAAgBhR,EAASyE,EAAG,oBAAhC,MAEuC,MAAfzE,EAAQyE,KAC7BA,EAAKuM,EAAgBhR,EAASyE,EAAG,oBAAhC,IAEoB,WAAnB0M,IACN1M,EAAKuM,EAAgBhR,EAASyE,EAAG,oBAAhC,MAGGA,CACb,EAAC,EAED8L,oBAAA,SAAoBvQ,EAASyE,GAKzB,IADA,IAAMG,EAHNH,EAAIiM,EAAe1Q,EAASyE,GAIrBA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAKA,IACrD,IAAM2M,EAAcpR,EAAQG,UAAUyE,EAAWH,GACjD,IAAK1G,KAAKiS,wBAA0BqB,EAAUD,EAAa,CAAEzP,WAAY5D,KAAK4D,aAC1E,MAAM,IAAIlC,MAAM,0BAA0B2R,EAAW,KAMzD,GAAmB,MAAfpR,EAHJyE,EAAIiM,EAAe1Q,EAASyE,KAGF2L,EAAOpQ,EAAS,OAAQyE,GAC9CA,GAAK,OACF,GAAmB,MAAfzE,EAAQyE,IAAc2L,EAAOpQ,EAAS,KAAMyE,GACnDA,GAAK,OACF,GAAmB,MAAfzE,EAAQyE,GAAY,CAE3B,IADAA,IACOA,EAAIzE,EAAQK,QAAyB,MAAfL,EAAQyE,IAAYA,IACjD,GAAmB,MAAfzE,EAAQyE,GAAY,MAAM,IAAIhF,MAAM,6BAC5C,MAAO,IAAK1B,KAAKiS,sBACb,MAAM,IAAIvQ,MAAM,sCAAsCO,EAAQyE,GAAE,KAGpE,OAAOA,CACX,EAAC,EAED+L,oBAAA,SAAoBxQ,EAASyE,GAKzB,IADA,IAAM6M,EAHN7M,EAAIiM,EAAe1Q,EAASyE,GAIrBA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAKA,IAErDoM,EADoB7Q,EAAQG,UAAUmR,EAAe7M,GAC3B1G,KAAK4D,YAM/B,IADA,IAAM4P,EAHN9M,EAAIiM,EAAe1Q,EAASyE,GAIrBA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAKA,IAOrD,GALAoM,EADsB7Q,EAAQG,UAAUoR,EAAe9M,GAC3B1G,KAAK4D,YAEjC8C,EAAIiM,EAAe1Q,EAASyE,GAGsB,aAA9CzE,EAAQG,UAAUsE,EAAGA,EAAI,GAAGqM,cAA8B,CAG1D,GAAmB,MAAf9Q,EADJyE,EAAIiM,EAAe1Q,EADnByE,GAAK,IAEmB,MAAM,IAAIhF,MAAM,yBAAwBO,EAAQyE,GAAE,KAE1E,IADAA,IACOA,EAAIzE,EAAQK,QAAyB,MAAfL,EAAQyE,IAAY,CAE7C,IADA,IAAM+M,EAAgB/M,EACfA,EAAIzE,EAAQK,QAAyB,MAAfL,EAAQyE,IAA6B,MAAfzE,EAAQyE,IAAYA,IAEvEoM,EADiB7Q,EAAQG,UAAUqR,EAAe/M,GAAGmB,OAC9B7H,KAAK4D,YACT,MAAf3B,EAAQyE,KAERA,EAAIiM,EAAe1Q,IADnByE,GAGR,CACA,GAAmB,MAAfzE,EAAQyE,GAAY,MAAM,IAAIhF,MAAM,kCACxCgF,GACJ,KAAO,CAEH,IADA,IAAMgN,EAAYhN,EACXA,EAAIzE,EAAQK,SAAW,KAAKyB,KAAK9B,EAAQyE,KAAKA,IACrD,IAAMiN,EAAgB1R,EAAQG,UAAUsR,EAAWhN,GAEnD,IAAK1G,KAAKiS,wBADS,CAAC,QAAS,KAAM,QAAS,SAAU,SAAU,WAAY,UAAW,YACxC7N,SAASuP,EAAcZ,eAClE,MAAM,IAAIrR,MAAM,4BAA4BiS,EAAa,IAEjE,CAaA,OAXAjN,EAAIiM,EAAe1Q,EAASyE,GAGsB,cAA9CzE,EAAQG,UAAUsE,EAAGA,EAAI,GAAGqM,cAC5BrM,GAAK,EACgD,aAA9CzE,EAAQG,UAAUsE,EAAGA,EAAI,GAAGqM,cACnCrM,GAAK,EAEJA,EAAKuM,EAAgBhR,EAASyE,EAAG,yBAAhC,GAGCA,CACX,EAACqL,CAAA,CAzPgC,GA8PrC,SAASY,EAAejI,EAAMxI,GAC1B,KAAOA,EAAQwI,EAAKpI,QAAU,KAAKyB,KAAK2G,EAAKxI,KAASA,IACtD,OAAOA,CACX,CAEA,SAASmQ,EAAO3H,EAAMkJ,EAAKlN,GACvB,IAAK,IAAImC,EAAI,EAAGA,EAAI+K,EAAItR,OAAQuG,IAC5B,GAAI+K,EAAI/K,KAAO6B,EAAKhE,EAAImC,EAAI,GAAI,OAAO,EAE3C,OAAO,CACX,CAEA,SAASiK,EAAatR,EAAMoC,GACxB,QADkC,IAAVA,IAAAA,EAAa,QAChC0P,EAAU9R,EAAM,CAAEoC,WAAAA,IAAe,MAAM,IAAIlC,MAAM,wBAAwBF,EAAI,IACtF,CAKA,SAASyR,EAAgBhR,EAASyE,EAAGuE,GACjC,IAAMnI,EAAYb,EAAQyE,GAC1B,GAAkB,MAAd5D,GAAmC,MAAdA,EACrB,MAAM,IAAIpB,MAAM,8BAA8BuJ,EAAI,YAAYnI,EAAS,KAI3E,IADA,IAAM+Q,IADNnN,EAEOA,EAAIzE,EAAQK,QAAUL,EAAQyE,KAAO5D,GAAW4D,IACvD,GAAIzE,EAAQyE,KAAO5D,EAAW,MAAM,IAAIpB,MAAM,gBAAgBuJ,EAAI,UAClE,IAAMjK,EAAQiB,EAAQG,UAAUyR,EAAOnN,GACvC,MAAO,GAAGA,EAAG1F,EACjB,CCtRA,IAAM2Q,EAAiB,CACrBlI,wBAAwB,EACxBqK,aAAc,GACdC,QAAS,CACPnC,eAAgBC,IAChBC,cAAeD,KAEjBzI,oBAAqB,CACnBY,SAAS,EACTgK,UAAU,EACV3K,QAAQ,GAEV4K,cAAe,CACbvO,UAAU,EACVC,aAAa,GAEfuO,eAAe,EACfjD,SAAU,IAqBSkD,EAAe,WAClC,SAAAA,EAAY3O,GACVxF,KAAKwF,QAiXT,SAAsB4O,EAAU5O,GAC9B,IAAM6O,EAAShU,OAAO2R,OAAO,CAAC,EAAGoC,EAAU5O,GAU3C,OATA6O,EAAON,QAAU1T,OAAO2R,OAAO,CAAC,EAAGoC,EAASL,QAAgB,MAAPvO,OAAO,EAAPA,EAASuO,SAC9DM,EAAOjL,oBAAsB/I,OAAO2R,OAAO,CAAC,EAAGoC,EAAShL,oBAA4B,MAAP5D,OAAO,EAAPA,EAAS4D,qBACtFiL,EAAOJ,cAAgB5T,OAAO2R,OAC5B,CAAC,EACDoC,EAASH,cACF,MAAPzO,OAAO,EAAPA,EAASyO,cACF,MAAPzO,OAAO,EAAPA,EAAS8O,gBAEXD,EAAOpD,UAAkB,MAAPzL,OAAO,EAAPA,EAASyL,WAAYmD,EAASnD,SACzCoD,CACT,CA7XmBE,CAAa5C,EAAgBnM,EAC9C,CAEA2O,EACOrO,SAAP,SAAgB7D,EAASuD,GACvB,OAAO,IAAI2O,EAAgB3O,GAASM,SAAS7D,EAC/C,EAAC,IAAAiD,EAAAiP,EAAAxT,UAwWA,OAxWA,EAEDmF,SAAA,SAAS7D,GAAU,IAAD6H,EACVtE,EAAUxF,KAAKwF,QAEfgP,EAAO,GACTC,GAAW,EACXC,GAAc,EACdC,GAAc,EAEC,WAAf1S,EAAQ,KACVA,EAAUA,EAAQ2S,OAAO,IAG3B,IAAMnP,EAAa,SAACrE,EAAMD,EAAS0T,GACjC,MAAM,IAAI5T,EAAgBE,EAASC,EAAMyT,EAAWxT,MAAQwT,EAAYA,EAAWvT,IACrF,EAGMwT,EADgB,IAAIvP,EAAwBC,EAAQyO,cAAexO,GACxCK,SAAS7D,GACpC2B,EAAakR,EAAW9O,QAC9B/D,EAAUA,EAAQG,UAAU0S,EAAW7O,KAwCvC,IAtCA,IAAMiE,EAAmB,IAAI6H,EAAiBvM,EAAQuO,QAASnQ,GACzDmR,EAAqB,IAAIlL,EAAmBrE,GAC5C8C,EAAgB,IAAIrD,EAAiBrB,GACrCoR,EAAgB,IAAI3M,EAAyB7C,EAAS8C,EAAe1E,GACrEqR,EAAc,IAAIjE,EAAexL,EAAQyL,UAEzCiE,GAA6D,KAAf,OAA3BpL,EAAAtE,EAAQ4D,0BAAmB,EAA3BU,EAA6BkK,UAEhDmB,EAAwB,SAACC,GACmD,cAA9DnT,EAAQG,UAAUgT,EAAWA,EAAY,GAAGrC,gBAE1D4B,GACFlP,EAAW,aAAc,uCAAwCzD,EAAyBC,EAASmT,KAEjGX,GAAYD,EAAKlS,OAAS,IAC5BmD,EAAW,aAAc,+CAAgDzD,EAAyBC,EAASmT,IAE7GT,GAAc,EAChB,EAIMU,EAAoB,SAACC,GACL,IAAhBd,EAAKlS,QACPmD,EAAW,aAAc,wBAAyBzD,EAAyBC,EAASqT,GAExF,EAEMC,EAAsB,SAACH,GACP,IAAhBZ,EAAKlS,QAAgBL,EAAQ8D,WAAW,YAAaqP,IACvD3P,EACE,aACA,yDACAzD,EAAyBC,EAASmT,GAGxC,EAES1O,EAAI,EAAGA,EAAIzE,EAAQK,OAAQoE,IAClC,GAAmB,MAAfzE,EAAQyE,IAAiC,MAAnBzE,EAAQyE,EAAI,GACpCA,GAAK,EACLA,EAAI1G,KAAKwV,QAAQvT,EAASyE,EAAG4B,EAAe7C,QACvC,GAAmB,MAAfxD,EAAQyE,GAAY,CAC7B,IAAI+O,EAAc/O,EAGlB,GAFAA,IAEmB,MAAfzE,EAAQyE,GAAY,CACtB6O,EAAoBE,GACpB,IACEN,EAAsBM,GACtB/O,EAAIqO,EAAmB9K,KAAKhI,EAASyE,EAAGwD,EAC1C,CAAE,MAAON,GACPnE,EAAW,iBAAkBmE,EAAIzI,QAASa,EAAyBC,EAASyE,EAAI,GAClF,CACA,QACF,CACE,IAAIgP,GAAa,EACE,MAAfzT,EAAQyE,KACVgP,GAAa,EACbhP,KAIF,IADA,IAAIgI,EAAU,GAEZhI,EAAIzE,EAAQK,QACG,MAAfL,EAAQyE,IACO,MAAfzE,EAAQyE,IACO,OAAfzE,EAAQyE,IACO,OAAfzE,EAAQyE,IACO,OAAfzE,EAAQyE,GACRA,IAEAgI,GAAWzM,EAAQyE,GASrB,GALoC,OAFpCgI,EAAUA,EAAQ7G,QAEN6G,EAAQpM,OAAS,KAC3BoM,EAAUA,EAAQtM,UAAU,EAAGsM,EAAQpM,OAAS,GAChDoE,MAGG4B,EAAcnD,aAAauJ,GAAU,CACxC,IAAI/E,EAEFA,EAD4B,IAA1B+E,EAAQ7G,OAAOvF,OACX,2BAEA,QAAUoM,EAAU,wBAE5BjJ,EAAW,aAAckE,EAAK3H,EAAyBC,EAASwT,GAClE,CAEA,IAAM5Q,EAAS7E,KAAK2V,kBAAkB1T,EAASyE,IAChC,IAAX7B,GACFY,EAAW,cAAe,mBAAqBiJ,EAAU,qBAAsB1M,EAAyBC,EAASyE,IAEnH,IAAI6B,EAAU1D,EAAO7D,MAGrB,GAFA0F,EAAI7B,EAAO3C,MAEyB,MAAhCqG,EAAQA,EAAQjG,OAAS,GAAY,CACvC,IAAMsT,EAAelP,EAAI6B,EAAQjG,OACjCiG,EAAUA,EAAQnG,UAAU,EAAGmG,EAAQjG,OAAS,GAChD,IAAMuT,EAAUb,EAAclP,SAASyC,IACvB,IAAZsN,IACkB,IAAhBnB,GAAyBlP,EAAQ0O,eACnCzO,EAAW,aAAc,sCAAuCzD,EAAyBC,EAASwT,IAEpGhB,GAAW,EACS,IAAhBD,EAAKlS,SACPoS,GAAc,GAEhBO,EAAY9N,KAAKuH,GACjBuG,EAAY7F,OAEZ3J,EAAWoQ,EAAQjM,IAAIxI,KAAMyU,EAAQjM,IAAID,IAAK3H,EAAyBC,EAAS2T,EAAeC,EAAQjM,IAAIvI,MAE/G,MAAO,GAAIqU,EACT,GAAK7Q,EAAOiR,UAEL,GAAIvN,EAAQV,OAAOvF,OAAS,EACjCmD,EAAW,aAAc,gBAAkBiJ,EAAU,+CAAgD1M,EAAyBC,EAASwT,SAClI,GAAoB,IAAhBjB,EAAKlS,OACdmD,EAAW,aAAc,gBAAkBiJ,EAAU,yBAA0B1M,EAAyBC,EAASwT,QAC5G,CACL,IAAMM,EAAMvB,EAAKpF,MACjB,GAAIV,IAAYqH,EAAIrH,QAAS,CAC3B,IAAIsH,EAAUhU,EAAyBC,EAAS8T,EAAIN,aACpDhQ,EACE,aACA,yBAA2BsQ,EAAIrH,QAAU,qBAAuBsH,EAAQ3U,KAAO,SAAW2U,EAAQ1U,IAAM,6BAA+BoN,EAAU,KACjJ1M,EAAyBC,EAASwT,GAEtC,CACAR,EAAY7F,MACQ,IAAhBoF,EAAKlS,SACPoS,GAAc,EAElB,MAnBEjP,EAAW,aAAc,gBAAkBiJ,EAAU,iCAAkC1M,EAAyBC,EAASyE,QAoBtH,CAGL,IAAMuP,EAAOhB,EAAY9N,KAAKuH,GAE9B,IAAKuH,EAAM,CACT,IAAMJ,EAAUb,EAAclP,SAASyC,IACvB,IAAZsN,GACFpQ,EAAWoQ,EAAQjM,IAAIxI,KAAMyU,EAAQjM,IAAID,IAAK3H,EAAyBC,EAASyE,EAAI6B,EAAQjG,OAASuT,EAAQjM,IAAIvI,MAErH,EAEoB,IAAhBqT,GAAyBlP,EAAQ0O,eAEiB,IAA3C1O,EAAQsO,aAAa3N,QAAQuI,IACtCuG,EAAY7F,MAEQ,IAAhBoF,EAAKlS,SAAcoS,GAAc,IAC5BuB,GAKTvP,EAAI4K,EAAiBC,QAAQtP,EAASyM,EAAShI,EAAI,GACnDuO,EAAY7F,MACQ,IAAhBoF,EAAKlS,SAAcoS,GAAc,IAErCF,EAAKrN,KAAK,CAAEuH,QAAAA,EAAS+G,YAAAA,IAdrBhQ,EAAW,aAAc,sCAAuCzD,EAAyBC,EAASyE,IAgBpG+N,GAAW,CACb,CAIA,IAAIyB,EAAU,GACd,IAAKxP,IAAKA,EAAIzE,EAAQK,OAAQoE,IAC5B,GAAmB,MAAfzE,EAAQyE,GAAY,CACtB,GAAuB,MAAnBzE,EAAQyE,EAAI,GAAY,CAE1B6O,IADA7O,EACwB,GACxB,IACEyO,EAAsBzO,EAAI,GAC1BA,EAAIqO,EAAmB9K,KAAKhI,EAASyE,EAAGwD,EAC1C,CAAE,MAAON,GACPnE,EAAW,iBAAkBmE,EAAIzI,QAASa,EAAyBC,EAASyE,EAAI,GAClF,CACAwP,EAAU,GACV,QACF,CAAO,GAAuB,MAAnBjU,EAAQyE,EAAI,GAKrB,MAJAA,GAAK,EACLA,EAAI1G,KAAKwV,QAAQvT,EAASyE,EAAG4B,EAAe7C,GAC5CyQ,EAAU,EAId,MAAO,GAAmB,MAAfjU,EAAQyE,GAAY,CAC7B2O,EAAkB3O,GAClB,IAAMyP,EAAWnW,KAAKoW,mBAAmBnU,EAASyE,IAChC,IAAdyP,GACF1Q,EAAW,cAAe,4BAA6BzD,EAAyBC,EAASyE,IAC3FA,EAAIyP,EACJD,EAAU,EACZ,KAAO,CACL,IAAM9U,EAAOa,EAAQ+F,WAAWtB,GAC5B3E,EAAqBX,IACvBqE,EAAW,mBAAmB,6BAA8BqC,EAAqBI,SAAS9G,GAAK,wBAAyBY,EAAyBC,EAASyE,IAExI,IAAhB8N,EAAKlS,QAAiBT,EAAaI,EAAQyE,KAC7CjB,EAAW,aAAc,wBAAyBzD,EAAyBC,EAASyE,IAElFwO,GAEc,QADhBgB,GAAWA,EAAUjU,EAAQyE,IAAII,OAAO,KACG,MAAnB7E,EAAQyE,EAAI,IAClCjB,EAAW,aAAc,+CAAgDzD,EAAyBC,EAASyE,EAAI,GAGrH,CAEiB,MAAfzE,EAAQyE,IACVA,GAGN,KAAO,CACL,GAAI7E,EAAaI,EAAQyE,IACvB,SAEEgO,GACFjP,EAAW,aAAc,2BAA6BxD,EAAQyE,GAAK,qBAAsB1E,EAAyBC,EAASyE,IAE7HjB,EAAW,cAAe,SAAWxD,EAAQyE,GAAK,qBAAsB1E,EAAyBC,EAASyE,GAC5G,CAaF,OAVK+N,EAEsB,IAAhBD,EAAKlS,OACdmD,EAAW,aAAc,iBAAmB+O,EAAK,GAAG9F,QAAU,KAAM1M,EAAyBC,EAASuS,EAAK,GAAGiB,cACrGjB,EAAKlS,OAAS,GACvBmD,EAAW,aAAc,YACvB4Q,KAAKC,UAAU9B,EAAKxG,IAAI,SAAA0C,GAAC,OAAIA,EAAEhC,OAAO,IAAGzL,QAAQ,SAAU,IAC3D,WAAY,CAAE5B,KAAM,EAAGC,IAAK,IAN9BmE,EAAW,aAAc,sBAAuB,CAAEpE,KAAM,EAAGC,IAAK,KAS3D,CACT,EAAC,EAEDkU,QAAA,SAAQvT,EAASyE,EAAG4B,EAAe7C,GAKjC,IAJA,IAAM8Q,EAAU7P,EACVG,EAAYH,EACd8P,GAAgB,EAEb9P,EAAIzE,EAAQK,OAAQoE,IAAK,CAC9B,IAAMpB,EAAKrD,EAAQyE,GAEnB,IAAK8P,IAAyB,MAAPlR,GAAqB,MAAPA,GAAa,CAChD,IAAMmR,EAASxU,EAAQ2S,OAAO/N,EAAWH,EAAIG,GAC7C2P,GAAgB,EAEa,QAAzBC,EAAOC,cACTjR,EACE,aACA,6DACAzD,EAAyBC,EAASsU,EAAU,IAEpCjO,EAAclD,YAAYqR,IACpChR,EACE,aAAa,kCACqBgR,EAAM,6BACxCzU,EAAyBC,EAASsU,EAAU,GAGlD,CAEA,GAAW,MAAPjR,GAAiC,MAAnBrD,EAAQyE,EAAI,GAE5B,QADAA,CAGJ,CAEAjB,EACE,aACA,kDACAzD,EAAyBC,EAASsU,EAAU,GAEhD,EAAC,EAEDZ,kBAAA,SAAkB1T,EAASyE,GAIzB,IAHA,IAAI6B,EAAU,GACVzF,EAAY,GACZgT,GAAY,EACTpP,EAAIzE,EAAQK,OAAQoE,IAAK,CAC9B,GAAmB,MAAfzE,EAAQyE,IAA6B,MAAfzE,EAAQyE,GACd,KAAd5D,EACFA,EAAYb,EAAQyE,GACX5D,IAAcb,EAAQyE,KAG/B5D,EAAY,SAET,GAAmB,MAAfb,EAAQyE,IACC,KAAd5D,EAAkB,CACpBgT,GAAY,EACZ,KACF,CAEFvN,GAAWtG,EAAQyE,EACrB,CACA,MAAkB,KAAd5D,GAGG,CAAE9B,MAAOuH,EAASrG,MAAOwE,EAAGoP,UAAAA,EACrC,EAAC,EAEDa,yBAAA,SAAyB1U,EAASyE,GAChC,IAAIkQ,EAAK,KAKT,IAJmB,MAAf3U,EAAQyE,KACVA,IACAkQ,EAAK,cAEAlQ,EAAIzE,EAAQK,OAAQoE,IAAK,CAC9B,GAAmB,MAAfzE,EAAQyE,GAAY,OAAOA,EAC/B,IAAKzE,EAAQyE,GAAGlE,MAAMoU,GAAK,KAC7B,CACA,OAAQ,CACV,EAAC,EAEDR,mBAAA,SAAmBnU,EAASyE,GAE1B,GAAmB,MAAfzE,IADJyE,GACwB,OAAQ,EAChC,GAAmB,MAAfzE,EAAQyE,GAEV,OADAA,IACO1G,KAAK2W,yBAAyB1U,EAASyE,GAGhD,IADA,IAAImQ,EAAQ,EACLnQ,EAAIzE,EAAQK,OAAQoE,IAAKmQ,IAC9B,KAAI5U,EAAQyE,GAAGlE,MAAM,OAASqU,EAAQ,IAAtC,CACA,GAAmB,MAAf5U,EAAQyE,GAAY,MACxB,OAAQ,CAF0C,CAIpD,OAAOA,CACT,EAACyN,CAAA,CAhXiC,GC1BrB,MAAM2C,EACnBrV,WAAAA,GAEEzB,KAAK+W,eAAiB,IAAItS,IAG1BzE,KAAKgX,iBAAmB,IAAIvS,IAG5BzE,KAAKiX,eAAiB,GAGtBjX,KAAKkX,mBAAqB,IAAIzS,IAG9BzE,KAAKmX,UAAY,IAAIC,IAGrBpX,KAAKqX,SAAU,CACjB,CAcAC,GAAAA,CAAIpJ,GACF,GAAIlO,KAAKqX,QACP,MAAM,IAAIhT,UACR,gFAKJ,GAAIrE,KAAKmX,UAAUI,IAAIrJ,EAAWzD,SAAU,OAAOzK,KAGnD,GAFAA,KAAKmX,UAAUG,IAAIpJ,EAAWzD,SAE1ByD,EAAWxB,kBAAmB,CAChC,MAAM8K,EAAUtJ,EAAWtD,SAASsD,EAAWtD,SAAStI,OAAS,GACjE,GAAIkV,GAA4B,kBAAjBA,EAAQvM,MAA4C,MAAhBuM,EAAQ1L,IAAa,CACtE,MAAMA,EAAM0L,EAAQ1L,IACf9L,KAAKkX,mBAAmBK,IAAIzL,IAAM9L,KAAKkX,mBAAmBnS,IAAI+G,EAAK,IACxE9L,KAAKkX,mBAAmB1W,IAAIsL,GAAK3E,KAAK+G,EACxC,MACElO,KAAKiX,eAAe9P,KAAK+G,GAE3B,OAAOlO,IACT,CAEA,MAAMkP,EAAQhB,EAAW5L,OACnBkV,EAAUtJ,EAAWtD,SAASsD,EAAWtD,SAAStI,OAAS,GAC3DwJ,EAAM0L,GAAS1L,IAErB,GAAKA,GAAe,MAARA,EAIL,CAEL,MAAM3L,EAAM,GAAG+O,KAASpD,IACnB9L,KAAK+W,eAAeQ,IAAIpX,IAAMH,KAAK+W,eAAehS,IAAI5E,EAAK,IAChEH,KAAK+W,eAAevW,IAAIL,GAAKgH,KAAK+G,EACpC,MAPOlO,KAAKgX,iBAAiBO,IAAIrI,IAAQlP,KAAKgX,iBAAiBjS,IAAImK,EAAO,IACxElP,KAAKgX,iBAAiBxW,IAAI0O,GAAO/H,KAAK+G,GAQxC,OAAOlO,IACT,CAcAyX,MAAAA,CAAOC,GACL,IAAK,MAAMvG,KAAQuG,EAAa1X,KAAKsX,IAAInG,GACzC,OAAOnR,IACT,CAQAuX,GAAAA,CAAIrJ,GACF,OAAOlO,KAAKmX,UAAUI,IAAIrJ,EAAWzD,QACvC,CAMA,QAAI3F,GACF,OAAO9E,KAAKmX,UAAUrS,IACxB,CASA6S,IAAAA,GAEE,OADA3X,KAAKqX,SAAU,EACRrX,IACT,CAMA,YAAI4X,GACF,OAAO5X,KAAKqX,OACd,CAkBAlJ,UAAAA,CAAWrB,GACT,OAAmC,OAA5B9M,KAAK6X,UAAU/K,EACxB,CAkBA+K,SAAAA,CAAU/K,GACR,MAAMoC,EAAQpC,EAAQe,WAChB/B,EAAMgB,EAAQE,gBAGd8K,EAAW,GAAG5I,KAASpD,IACvBiM,EAAc/X,KAAK+W,eAAevW,IAAIsX,GAC5C,GAAIC,EACF,IAAK,IAAIrR,EAAI,EAAGA,EAAIqR,EAAYzV,OAAQoE,IACtC,GAAIoG,EAAQvG,QAAQwR,EAAYrR,IAAK,OAAOqR,EAAYrR,GAK5D,MAAMsR,EAAiBhY,KAAKgX,iBAAiBxW,IAAI0O,GACjD,GAAI8I,EACF,IAAK,IAAItR,EAAI,EAAGA,EAAIsR,EAAe1V,OAAQoE,IACzC,GAAIoG,EAAQvG,QAAQyR,EAAetR,IAAK,OAAOsR,EAAetR,GAKlE,MAAMuR,EAAajY,KAAKkX,mBAAmB1W,IAAIsL,GAC/C,GAAImM,EACF,IAAK,IAAIvR,EAAI,EAAGA,EAAIuR,EAAW3V,OAAQoE,IACrC,GAAIoG,EAAQvG,QAAQ0R,EAAWvR,IAAK,OAAOuR,EAAWvR,GAG1D,IAAK,IAAIA,EAAI,EAAGA,EAAI1G,KAAKiX,eAAe3U,OAAQoE,IAC9C,GAAIoG,EAAQvG,QAAQvG,KAAKiX,eAAevQ,IAAK,OAAO1G,KAAKiX,eAAevQ,GAG1E,OAAO,IACT,EC7NF,MAAMiL,EAAiB,CACrBuG,QAAS,CAGP,EAEFjC,KAAM,CAOJ,EAEFzB,KAAM,CACJ2D,aAAc,IAGhBC,WAAY,CAIVD,aAAc,IAEhBE,UAAW,GASXC,YAAa,GAQbC,WAAY,KAQZC,eAAe,GAIXC,EAAoB,CAAC,KAAM,SAAU,UAAW,UAChDC,EAAqB,CAAC,SAAU,SAAU,WAGzC,SAASC,EAAanT,GAC3B,MAAMoT,EAAeC,EAAUlH,GAE1BnM,QAA0CZ,IAA/BY,EAAQgP,MAAM2D,eAC5BS,EAAapE,KAAK2D,aAAe,IAAIM,IAElCjT,QAAgDZ,IAArCY,EAAQ4S,YAAYD,eAClCS,EAAaR,WAAWD,aAAe,IAAIO,IAK7C,MAAMI,EAAoBC,MAAMC,QAAQxT,GAAS8S,aAC7C9S,EAAQ8S,YACRM,EAAaN,YACXW,EAAiB,IAAInC,EAC3B,IAAK,MAAM1G,KAAS0I,EAClBI,EAAe9I,EAAO,cAAe6I,GASvC,OAPAA,EAAetB,OACfiB,EAAaO,gBAAkBF,EAE3BzT,GACF4T,EAAeR,EAAcpT,GAGxBoT,CACT,CAEA,SAASC,EAAUpY,GACjB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpD,GAAIsY,MAAMC,QAAQvY,GAAM,OAAOA,EAAIuN,IAAI6K,GACvC,MAAMQ,EAAQ,CAAC,EACf,IAAK,MAAMlZ,KAAOE,OAAOiZ,KAAK7Y,GAC5B4Y,EAAMlZ,GAAO0Y,EAAUpY,EAAIN,IAE7B,OAAOkZ,CACT,CAEA,SAASD,EAAeG,EAAQC,GAC9B,IAAK,MAAMrZ,KAAOE,OAAOiZ,KAAKE,GAEhB,cAARrZ,GAA+B,gBAARA,GAAiC,cAARA,IAEzB,mBAAhBqZ,EAAOrZ,IAEP4Y,MAAMC,QAAQQ,EAAOrZ,IAD9BoZ,EAAOpZ,GAAOqZ,EAAOrZ,GAGW,iBAAhBqZ,EAAOrZ,IAAqC,OAAhBqZ,EAAOrZ,IACxB,iBAAhBoZ,EAAOpZ,IAAqC,OAAhBoZ,EAAOpZ,KAC5CoZ,EAAOpZ,GAAO,CAAC,GAEjBiZ,EAAeG,EAAOpZ,GAAMqZ,EAAOrZ,KAEnCoZ,EAAOpZ,GAAOqZ,EAAOrZ,GAG3B,CAGA,SAAS+Y,EAAe9I,EAAOqJ,EAAY1U,GACzC,IAAI0F,EAEJ,GAAqB,iBAAV2F,EAAoB,CAC7B,GAAqB,IAAjBA,EAAM9N,OAAc,MAAM,IAAIZ,MAAM,GAAG+X,gCAC3ChP,EAAU2F,CACZ,KAAO,MAA8B,iBAAnBA,GAAO3F,SAAwB2F,EAAM3F,QAAQnI,OAAS,GAAKyW,MAAMC,QAAQ5I,GAAOxF,WAShG,MAAM,IAAIlJ,MACR,WAAW+X,8CANbhP,EAAU2F,EAAMjI,UAQlB,CAEA,MAAMgJ,EAAO,IAAI3G,EAAWC,GAE5B,OADA1F,EAAIuS,IAAInG,GACDA,CACT,CCtIO,MA6lCMuI,EAAM,CACjBC,IAAK,IACLC,KAAM,IACNC,GAAI,IACJC,GAAI,IACJC,KAAM,KCrlCKC,EAAgB3Z,OAAO4Z,OAAO,CAEzCC,MAAO,QAEPC,MAAO,QAEPC,MAAO,UAOHC,GAAgB,IAAIjD,IAAI,0BAQ9B,SAASkD,GAAmB9Y,GAC1B,GAAgB,MAAZA,EAAK,GACP,MAAM,IAAIE,MAAM,2DAA2DF,MAE7E,IAAK,MAAM8D,KAAM9D,EACf,GAAI6Y,GAAc9C,IAAIjS,GACpB,MAAM,IAAI5D,MAAM,uCAAuC4D,uBAAwB9D,MAGnF,OAAOA,CACT,CAaA,SAAS+Y,MAAmBC,GAC1B,MAAMC,EAAMpa,OAAOqa,OAAO,MAC1B,IAAK,MAAM1M,KAAOwM,EAChB,GAAKxM,EACL,IAAK,MAAM7N,KAAOE,OAAOiZ,KAAKtL,GAAM,CAClC,MAAM2M,EAAM3M,EAAI7N,GAChB,GAAmB,iBAARwa,EACTF,EAAIta,GAAOwa,OACN,GAAIA,GAAsB,iBAARA,QAAgC/V,IAAZ+V,EAAIC,IAAmB,CAElE,MAAMA,EAAMD,EAAIC,IACG,iBAARA,IACTH,EAAIta,GAAOya,EAGf,CACF,CAEF,OAAOH,CACT,CAMA,MAAMI,GAAsB,WACtBC,GAAkB,OAClBC,GAAiB,MAuBjBC,GAAY3a,OAAO4Z,OAAO,CAAEgB,MAAO,EAAGC,MAAO,EAAGC,OAAQ,EAAGC,MAAO,IAIlEC,GAAmB,IAAIjE,IAAI,CAAC,EAAM,GAAM,KAgD/B,MAAMkE,GAoCnB7Z,WAAAA,CAAY+D,EAAU,CAAC,GAtGzB,IAAyBmV,EAuGrB3a,KAAKub,OAAS/V,EAAQgW,OAAS,CAAC,EAChCxb,KAAKyb,oBAAsBzb,KAAKub,OAAOG,oBAAsB,EAC7D1b,KAAK2b,mBAAqB3b,KAAKub,OAAOK,mBAAqB,EAC3D5b,KAAK6b,WAA0C,mBAAtBrW,EAAQsW,UAA2BtW,EAAQsW,UAAYrL,GAAKA,EACrFzQ,KAAK+b,aA3GgBpB,EA2Gc3a,KAAKub,OAAOS,eAAiBnB,KA1GtDF,IAAQE,GAChBF,IAAQI,GAAuB,IAAI3D,IAAI,CAAC2D,KACxCJ,IAAQG,GAAwB,IAAI1D,IAAI,CAAC0D,KACzC/B,MAAMC,QAAQ2B,GAAa,IAAIvD,IAAIuD,GAChC,IAAIvD,IAAI,CAACyD,KAJgC,IAAIzD,IAAI,CAACyD,KA2GvD7a,KAAKic,gBAAkBzW,EAAQ0W,iBAAkB,EAEjDlc,KAAKmc,SAAW5B,GAAgB6B,EAAsB5W,EAAQ6W,eAAiB,MAK/Erc,KAAKsc,aAAejc,OAAOqa,OAAO,MAIlC1a,KAAKuc,UAAYlc,OAAOqa,OAAO,MAG/B1a,KAAKwc,iBAAmB,EACxBxc,KAAKyc,gBAAkB,EAIvBzc,KAAK0c,WAAa,IAAItF,IAAI5R,EAAQ2V,QAAUpC,MAAMC,QAAQxT,EAAQ2V,QAAU3V,EAAQ2V,OAAS,IAE7Fnb,KAAK2c,UAAY,IAAIvF,IAAI5R,EAAQ0V,OAASnC,MAAMC,QAAQxT,EAAQ0V,OAAS1V,EAAQ0V,MAAQ,IAGzF,MAAM0B,EA3GV,SAAwBC,GACtB,IAAKA,EACH,MAAO,CAAEjZ,WAAY,EAAKkZ,QAAS9B,GAAUC,MAAO8B,UAAW/B,GAAUG,QAE3E,MAAMvX,EAAgC,MAAnBiZ,EAAIjZ,WAAqB,IAAM,EAC5CkZ,EAAU9B,GAAU6B,EAAIG,QAAUhC,GAAUC,MAC5C8B,EAAY/B,GAAU6B,EAAII,UAAYjC,GAAUG,OAGtD,MAAO,CAAEvX,aAAYkZ,UAASC,UADVG,KAAKC,IAAIJ,EAAW/B,GAAUG,QAEpD,CAiGmBiC,CAAe5X,EAAQqX,KACtC7c,KAAKqd,eAAiBT,EAAOhZ,WAC7B5D,KAAKsd,YAAcV,EAAOE,QAC1B9c,KAAKud,cAAgBX,EAAOG,UAI5B/c,KAAKwd,kBAAwD,mBAA7BhY,EAAQiY,iBACpCjY,EAAQiY,iBACR,KAEJzd,KAAK0d,eAAkD,mBAA1BlY,EAAQmY,cACjCnY,EAAQmY,cACR,IACN,CAiBAC,sBAAAA,CAAuBC,EAAMrc,EAAMR,EAAO8c,GACxC,IAAKD,EAAM,OAAO,EAClB,MAAME,EAASF,EAAKrc,EAAMR,GAC1B,GAAI+c,IAAW/D,EAAcG,MAAO,OAAO,EAC3C,GAAI4D,IAAW/D,EAAcI,MAC3B,MAAM,IAAI1Y,MACR,mCAAmCoc,cAAoBtc,4BAG3D,OAAO,CACT,CAcAwc,mBAAAA,CAAoBhQ,GAClB,GAAIA,EACF,IAAK,MAAM7N,KAAOE,OAAOiZ,KAAKtL,GAC5BsM,GAAmBna,GAGvB,IAAKH,KAAKwd,kBAER,YADAxd,KAAKsc,aAAe/B,GAAgBvM,IAItC,MAAMiQ,EAAO1D,GAAgBvM,GACvBkQ,EAAW7d,OAAOqa,OAAO,MAC/B,IAAK,MAAOlZ,EAAMR,KAAUX,OAAO8d,QAAQF,GACrCje,KAAK4d,uBAAuB5d,KAAKwd,kBAAmBhc,EAAMR,EAAO,cACnEkd,EAAS1c,GAAQR,GAGrBhB,KAAKsc,aAAe4B,CACtB,CASAE,iBAAAA,CAAkBje,EAAKa,GACrBsZ,GAAmBna,GACE,iBAAVa,IAA8C,IAAxBA,EAAMmF,QAAQ,MACzCnG,KAAK4d,uBAAuB5d,KAAKwd,kBAAmBrd,EAAKa,EAAO,cAClEhB,KAAKsc,aAAanc,GAAOa,EAG/B,CAaAqd,gBAAAA,CAAiBrQ,GAGf,GAFAhO,KAAKwc,iBAAmB,EACxBxc,KAAKyc,gBAAkB,GAClBzc,KAAK0d,eAER,YADA1d,KAAKuc,UAAYhC,GAAgBvM,IAGnC,MAAMiQ,EAAO1D,GAAgBvM,GACvBkQ,EAAW7d,OAAOqa,OAAO,MAC/B,IAAK,MAAOlZ,EAAMR,KAAUX,OAAO8d,QAAQF,GACrCje,KAAK4d,uBAAuB5d,KAAK0d,eAAgBlc,EAAMR,EAAO,WAChEkd,EAAS1c,GAAQR,GAGrBhB,KAAKuc,UAAY2B,CACnB,CAWAlZ,KAAAA,GAIE,OAHAhF,KAAKuc,UAAYlc,OAAOqa,OAAO,MAC/B1a,KAAKwc,iBAAmB,EACxBxc,KAAKyc,gBAAkB,EAChBzc,IACT,CAWAse,aAAAA,CAActY,GACZhG,KAAKqd,eAA6B,MAAZrX,EAAkB,IAAM,CAChD,CAYAuY,MAAAA,CAAOza,GACL,GAAmB,iBAARA,GAAmC,IAAfA,EAAIxB,OAAc,OAAOwB,EAExD,IAA0B,IAAtBA,EAAIqC,QAAQ,KAAa,OAAOrC,EAEpC,MAAM0a,EAAW1a,EACX2a,EAAS,GACThY,EAAM3C,EAAIxB,OAChB,IAAIoc,EAAO,EACPhY,EAAI,EAER,MAAMiY,EAAkB3e,KAAKyb,oBAAsB,EAC7CmD,EAAc5e,KAAK2b,mBAAqB,EACxCkD,EAAcF,GAAmBC,EAEvC,KAAOlY,EAAID,GAAK,CAEd,GAA0B,KAAtB3C,EAAIkE,WAAWtB,GAAqB,CAAEA,IAAK,QAAU,CAKzD,IAAImC,EAAInC,EAAI,EACZ,KAAOmC,EAAIpC,GAA6B,KAAtB3C,EAAIkE,WAAWa,IAAwBA,EAAInC,GAAM,IAAImC,IAEvE,GAAIA,GAAKpC,GAA6B,KAAtB3C,EAAIkE,WAAWa,GAAW,CAExCnC,IACA,QACF,CAGA,MAAMsC,EAAQlF,EAAIgD,MAAMJ,EAAI,EAAGmC,GAC/B,GAAqB,IAAjBG,EAAM1G,OAAc,CAAEoE,IAAK,QAAU,CAEzC,IAAIoY,EACAC,EAEJ,GAAI/e,KAAK0c,WAAWnF,IAAIvO,GAEtB8V,EAAc,QAGDla,IAATma,IACFA,EAAOlE,QAEJ,IAAI7a,KAAK2c,UAAUpF,IAAIvO,GAAQ,CAEpCtC,IACA,QACF,CAAO,GAA4B,KAAxBsC,EAAMhB,WAAW,GAAqB,CAI/C,MAAMgX,EAAYhf,KAAKif,YAAYjW,GACnC,QAAkBpE,IAAdoa,EAAyB,CAE3BtY,IACA,QACF,CACAoY,EAAcE,EACdD,EAAOjE,EACT,KAAO,CAEL,MAAMoE,EAAWlf,KAAKmf,aAAanW,GACnC8V,EAAcI,GAAUle,MACxB+d,EAAOG,GAAUH,IACnB,EAEA,QAAoBna,IAAhBka,GAaJ,GANIpY,EAAIgY,GAAMD,EAAOtX,KAAKrD,EAAIgD,MAAM4X,EAAMhY,IAC1C+X,EAAOtX,KAAK2X,GACZJ,EAAO7V,EAAI,EACXnC,EAAIgY,EAGAG,GAAe7e,KAAKof,YAAYL,GAAO,CACzC,GAAIJ,IACF3e,KAAKwc,mBACDxc,KAAKwc,iBAAmBxc,KAAKyb,qBAC/B,MAAM,IAAI/Z,MAER,2DAAG1B,KAAKwc,sBAAsBxc,KAAKyb,uBAIzC,GAAImD,EAAa,CAEf,MAAMS,EAAQP,EAAYxc,QAAU0G,EAAM1G,OAAS,GACnD,GAAI+c,EAAQ,IACVrf,KAAKyc,iBAAmB4C,EACpBrf,KAAKyc,gBAAkBzc,KAAK2b,oBAC9B,MAAM,IAAIja,MAER,4DAAG1B,KAAKyc,qBAAqBzc,KAAK2b,qBAI1C,CACF,OAlCEjV,GAmCJ,CAGIgY,EAAOjY,GAAKgY,EAAOtX,KAAKrD,EAAIgD,MAAM4X,IAGtC,MAAM7Z,EAA2B,IAAlB4Z,EAAOnc,OAAewB,EAAM2a,EAAOna,KAAK,IAEvD,OAAOtE,KAAK6b,WAAWhX,EAAQ2Z,EACjC,CAYAY,WAAAA,CAAYL,GACV,QAAI/e,KAAK+b,YAAYxE,IAAIwD,KAClB/a,KAAK+b,YAAYxE,IAAIwH,EAC9B,CAcAI,YAAAA,CAAa3d,GAGX,OAAIA,KAAQxB,KAAKuc,UAAkB,CAAEvb,MAAOhB,KAAKuc,UAAU/a,GAAOud,KAAMlE,IACpErZ,KAAQxB,KAAKsc,aAAqB,CAAEtb,MAAOhB,KAAKsc,aAAa9a,GAAOud,KAAMlE,IAC1ErZ,KAAQxB,KAAKmc,SAAiB,CAAEnb,MAAOhB,KAAKmc,SAAS3a,GAAOud,KAAMjE,SAAtE,CAEF,CAeAwE,YAAAA,CAAaC,GAEX,OAAW,IAAPA,EAAiBvf,KAAKud,cAGtBgC,GAAM,OAAUA,GAAM,OAGE,IAAxBvf,KAAKqd,gBACHkC,GAAM,GAAQA,GAAM,KAASlE,GAAiB9D,IAAIgI,GAJfvE,GAAUG,QAO3C,CACV,CAcAqE,eAAAA,CAAgBzB,EAAQ/U,EAAOuW,GAC7B,OAAQxB,GACN,KAAK/C,GAAUC,MAAO,OAAOhL,OAAOwP,cAAcF,GAClD,KAAKvE,GAAUG,OAAQ,MAAO,GAC9B,KAAKH,GAAUE,MAAO,OACtB,KAAKF,GAAUI,MACb,MAAM,IAAI1Z,MAER,2DAAIsH,SAAauW,EAAGpX,SAAS,IAAI4K,cAAc3K,SAAS,EAAG,SAE/D,QAAS,OAAO6H,OAAOwP,cAAcF,GAEzC,CAkBAN,WAAAA,CAAYjW,GAEV,MAAM0W,EAAS1W,EAAMhB,WAAW,GAChC,IAAIuX,EAQJ,GANEA,EADa,MAAXG,GAAqC,KAAXA,EACvBjT,SAASzD,EAAMlC,MAAM,GAAI,IAEzB2F,SAASzD,EAAMlC,MAAM,GAAI,IAI5B6Y,OAAOC,MAAML,IAAOA,EAAK,GAAKA,EAAK,QAAU,OAGjD,MAAMM,EAAU7f,KAAKsf,aAAaC,GAGlC,IAAKvf,KAAKic,iBAAmB4D,EAAU7E,GAAUG,OAAQ,OAGzD,MAAM2E,GAAyB,IAAbD,EACd7f,KAAKsd,YACLJ,KAAKC,IAAInd,KAAKsd,YAAauC,GAG/B,OAAO7f,KAAKwf,gBAAgBM,EAAW9W,EAAOuW,EAChD,ECxnBF,MCoFA,GAhFqB,CACnB,CACEQ,GAAI,yBACJC,YAAa,sEACbvV,QAAS,QAEX,CACEsV,GAAI,mBACJC,YAAa,8DACbvV,QAAS,4CAEX,CACEsV,GAAI,iBACJC,YAAa,yCACbvV,QAAS,0BAEX,CACEsV,GAAI,oBACJC,YAAa,4CACbvV,QAAS,6BAEX,CACEsV,GAAI,kBACJC,YAAa,+BACbvV,QAAS,2BAEX,CACEsV,GAAI,kBACJC,YAAa,wCACbvV,QAAS,2BAEX,CACEsV,GAAI,iBACJC,YAAa,+CAEbvV,QAAS,kCAEX,CACEsV,GAAI,cACJC,YAAa,uDACbvV,QAAS,8BAEX,CACEsV,GAAI,uBACJC,YAAa,4DAEbvV,QAAS,sDAEX,CACEsV,GAAI,wBACJC,YAAa,4BACbvV,QAAS,4BAEX,CACEsV,GAAI,uBACJC,YAAa,4BACbvV,QAAS,4BAEX,CACEsV,GAAI,sBACJC,YAAa,qDACbvV,QAAS,+BAEX,CACEsV,GAAI,oBACJC,YAAa,kDACbvV,QAAS,6BAEX,CACEsV,GAAI,oBACJC,YAAa,uDACbvV,QAAS,0BAEX,CACEsV,GAAI,yBACJC,YAAa,yEACbvV,QAAS,4BClEP+E,GAAM,aCANyQ,GAAmB,CACvBC,KHfoB,CACpB,CACEH,GAAI,mBACJC,YAAa,sBACbvV,QAAS,kBAEX,CACEsV,GAAI,oBACJC,YAAa,wBACbvV,QAAS,mBAEX,CACEsV,GAAI,2BACJC,YAAa,6DAEbvV,QAAS,oHAEX,CACEsV,GAAI,yBACJC,YAAa,uBACbvV,QAAS,wBAEX,CACEsV,GAAI,iBACJC,YAAa,uDACbvV,QAAS,wCAEX,CACEsV,GAAI,kBACJC,YAAa,iCACbvV,QAAS,gDAEX,CACEsV,GAAI,gBACJC,YAAa,+CACbvV,QAAS,6CAEX,CACEsV,GAAI,4BACJC,YAAa,qEAEbvV,QAAS,qBAEX,CACEsV,GAAI,gCACJC,YAAa,gEAEbvV,QAAS,4CAEX,CACEsV,GAAI,oCACJC,YAAa,sFACbvV,QAAS,uEAEX,CACEsV,GAAI,wBACJC,YAAa,+DACbvV,QAAS,qCAEX,CACEsV,GAAI,oBACJC,YAAa,sDACbvV,QAAS,4BAEX,CACEsV,GAAI,gBACJC,YAAa,uDACbvV,QAAS,eAEX,CACEsV,GAAI,oBACJC,YAAa,kDACbvV,QAAS,mDAEX,CACEsV,GAAI,cACJC,YAAa,kEACbvV,QAAS,eAEX,CACEsV,GAAI,cACJC,YAAa,cACbvV,QAAS,kBAEX,CACEsV,GAAI,YACJC,YAAa,yEACbvV,QAAS,iBGvEXiP,ICdmB,CACnB,CACEqG,GAAI,sBACJC,YAAa,qEACbvV,QAAS,gBAEX,CACEsV,GAAI,kBACJC,YAAa,qEACbvV,QAAS,SAEX,CACEsV,GAAI,6BACJC,YAAa,6DACbvV,QAAS,4BAEX,CACEsV,GAAI,wBACJC,YAAa,gEAEbvV,QAAS,yBAEX,CACEsV,GAAI,oBACJC,YAAa,8DACbvV,QAAS,oBAEX,CACEsV,GAAI,oBACJC,YAAa,8DACbvV,QAAS,oBAEX,CACEsV,GAAI,yBACJC,YAAa,6EACbvV,QAAS,kBAEX,CACEsV,GAAI,qBACJC,YAAa,6EAEbvV,QAAS,sBAEX,CACEsV,GAAI,0BACJC,YAAa,0EACbvV,QAAS,iCAEX,CACEsV,GAAI,wBACJC,YAAa,iEACbvV,QAAS,QAEX,CACEsV,GAAI,oBACJC,YAAa,yCACbvV,QAAS,OAEX,CACEsV,GAAI,eACJC,YAAa,gDACbvV,QAAS,QD9CX0V,IEjBmB,CACnB,CACEJ,GAAI,qBACJC,YAAa,iDACbvV,QAAS,kBAEX,CACEsV,GAAI,4BACJC,YAAa,iEACbvV,QAAS,kDAEX,CACEsV,GAAI,sBACJC,YAAa,+DACbvV,QAAS,sCAEX,CACEsV,GAAI,oBACJC,YAAa,8DACbvV,QAAS,yBAEX,CACEsV,GAAI,mBACJC,YAAa,2EAGbvV,QAAS,uEAEX,CACEsV,GAAI,mBACJC,YAAa,6EACbvV,QAAS,8DAEX,CACEsV,GAAI,wBACJC,YAAa,sCACbvV,QAAS,0EAEX,CACEsV,GAAI,qBACJC,YAAa,oEACbvV,QAAS,oDAEX,CACEsV,GAAI,oBACJC,YAAa,8EACbvV,QAAS,gGAEX,CACEsV,GAAI,sBACJC,YAAa,mDACbvV,QAAS,qBAEX,CACEsV,GAAI,qBACJC,YAAa,qEACbvV,QAAS,kDAEX,CACEsV,GAAI,qBACJC,YAAa,2DACbvV,QAAS,oFAEX,CACEsV,GAAI,uBACJC,YAAa,gFACbvV,QAAS,yCFhDX2V,IAAKC,GACL,aGe0B,IAAIA,GAlB9B,CACEN,GAAI,mBACJC,YAAa,+DACbvV,QAAS,cAEX,CACEsV,GAAI,oBACJC,YAAa,mEACbvV,QAAS,sEAEX,CACEsV,GAAI,mBACJC,YAAa,yDACbvV,QAAS,sBHTX6V,MItBqB,CACrB,CACEP,GAAI,4BACJC,YAAa,0DACbvV,QAAS,UAEX,CACEsV,GAAI,+BACJC,YAAa,6DACbvV,QAAS,UAEX,CACEsV,GAAI,+BACJC,YAAa,qDACbvV,QAAS,2BAEX,CACEsV,GAAI,kBACJC,YAAa,8EACbvV,QAAS,YAEX,CACEsV,GAAI,kBACJC,YAAa,0CACbvV,QAAS,KAEX,CACEsV,GAAI,aACJC,YAAa,6BACbvV,QAAS,MAEX,CACEsV,GAAI,qBACJC,YAAa,6BACbvV,QAAS,MAEX,CACEsV,GAAI,oBACJC,YAAa,4BACbvV,QAAS,QAEX,CACEsV,GAAI,iBACJC,YAAa,uCACbvV,QAAS,KAEX,CACEsV,GAAI,qBACJC,YAAa,4CACbvV,QAAS,QAEX,CACEsV,GAAI,qBACJC,YAAa,wEACbvV,QAAS,QAEX,CACEsV,GAAI,qBACJC,YAAa,gDACbvV,QAAS,UAEX,CACEsV,GAAI,oBACJC,YAAa,gCACbvV,QAAS,KAEX,CACEsV,GAAI,0BACJC,YAAa,gEACbvV,QAAS,UAEX,CACEsV,GAAI,kBACJC,YAAa,0DAEbvV,QAAS,aAEX,CACEsV,GAAI,sBACJC,YAAa,2EACbvV,QAAS,gBAEX,CACEsV,GAAI,sBACJC,YAAa,mDACbvV,QAAS,mBAEX,CACEsV,GAAI,kBACJC,YAAa,yEAGbvV,QAAS,kDJrEX8V,MKnBqB,CACrB,CACER,GAAI,+BACJC,YAAa,gFAEbvV,QAAS,wBAEX,CACEsV,GAAI,+BACJC,YAAa,kEACbvV,QAAS,wBAEX,CACEsV,GAAI,sBACJC,YAAa,qEACbvV,QAAS,8BAEX,CACEsV,GAAI,4BACJC,YAAa,yEAEbvV,QAAS,+DAEX,CACEsV,GAAI,yBACJC,YAAa,sDACbvV,QAAS,kCAEX,CACEsV,GAAI,wBACJC,YAAa,yDACbvV,QAAS,mBAEX,CACEsV,GAAI,yBACJC,YAAa,sGAEbvV,QAAS,uBAEX,CACEsV,GAAI,iCACJC,YAAa,6EAEbvV,QAAS,yCLvBX+V,MDNqB,CAErB,CACET,GAAI,uBACJC,YAAa,gEACbvV,QAAS,IAAIvH,OAAO,WAAWsM,KAAO,MAExC,CACEuQ,GAAI,oBACJC,YAAa,4DACbvV,QAAS,IAAIvH,OAAO,QAAQsM,KAAO,MAErC,CACEuQ,GAAI,oBACJC,YAAa,4DACbvV,QAAS,IAAIvH,OAAO,UAAUsM,KAAO,MAEvC,CACEuQ,GAAI,oBACJC,YAAa,2CACbvV,QAAS,IAAIvH,OAAO,UAAUsM,KAAO,MAEvC,CACEuQ,GAAI,uBACJC,YAAa,gFACbvV,QAAS,IAAIvH,OAAO,WAAWsM,KAAO,MAExC,CACEuQ,GAAI,oBACJC,YAAa,0DACbvV,QAAS,IAAIvH,OAAO,QAAQsM,YAAc,MAE5C,CACEuQ,GAAI,qBACJC,YAAa,wCACbvV,QAAS,IAAIvH,OAAO,SAASsM,YAAc,MAE7C,CACEuQ,GAAI,qBACJC,YAAa,wCACbvV,QAAS,IAAIvH,OAAO,SAASsM,YAAc,MAE7C,CACEuQ,GAAI,wBACJC,YAAa,qDACbvV,QAAS,IAAIvH,OAAO,YAAYsM,KAAO,MAEzC,CACEuQ,GAAI,oBACJC,YAAa,0DACbvV,QAAS,IAAIvH,OAAO,QAAQsM,YAAc,MAE5C,CACEuQ,GAAI,sBACJC,YAAa,mEACbvV,QAAS,IAAIvH,OAAO,UAAUsM,KAAO,MAEvC,CACEuQ,GAAI,0BACJC,YAAa,4DACbvV,QAAS,IAAIvH,OAAO,cAAcsM,KAAO,MAE3C,CACEuQ,GAAI,6BACJC,YAAa,gEACbvV,QAAS,IAAIvH,OAAO,iBAAiBsM,KAAO,MAG9C,CACEuQ,GAAI,wBACJC,YAAa,2DACbvV,QAAS,aAEX,CACEsV,GAAI,8BACJC,YAAa,4FAGbvV,QAAS,sCAEX,CACEsV,GAAI,sBACJC,YAAa,uDACbvV,QAAS,0BC5EXgW,IMZmB,CAEnB,CACEV,GAAI,qBACJC,YAAa,2DACbvV,QAAS,UAEX,CACEsV,GAAI,uBACJC,YAAa,qEACbvV,QAAS,YAEX,CACEsV,GAAI,sBACJC,YAAa,kFACbvV,QAAS,kBAIX,CACEsV,GAAI,qBACJC,YAAa,wEACbvV,QAAS,iBAEX,CACEsV,GAAI,2BACJC,YAAa,oFAEbvV,QAAS,WAEX,CACEsV,GAAI,mBACJC,YAAa,8EACbvV,QAAS,8DAIX,CACEsV,GAAI,wBACJC,YAAa,qEACbvV,QAAS,wBAEX,CACEsV,GAAI,sBACJC,YAAa,iEACbvV,QAAS,qBAEX,CACEsV,GAAI,wBACJC,YAAa,6FAIbvV,QAAS,oFAEX,CACEsV,GAAI,uBACJC,YAAa,sDACbvV,QAAS,qBAIX,CACEsV,GAAI,gBACJC,YAAa,yEACbvV,QAAS,YAIX,CACEsV,GAAI,kBACJC,YAAa,mFACbvV,QAAS,YNzDb,MAMaiW,GAAiBrgB,OAAO4Z,OACnC5Z,OAAOsgB,YAAYtgB,OAAOiZ,KAAK2G,IAAkBjS,IAAKjF,GAAM,CAACA,EAAGA,MOwClE,SAAS6X,GAAa5f,EAAO6f,GAC3B,MAAMC,EAAWb,GAAiBY,GAClC,IAAK,MAAME,KAAQD,EACjB,GAAIC,EAAKtW,QAAQ1G,KAAK/C,GACpB,MAAO,CAAE8c,QAAS+C,EAAad,GAAIgB,EAAKhB,GAAIC,YAAae,EAAKf,YAAavV,QAASsW,EAAKtW,SAG7F,OAAO,IACT,CAoBA,SAASuW,GAAShgB,EAAO8c,GAKvB,GAvFF,SAAsB9c,GACpB,GAAqB,iBAAVA,EACT,MAAM,IAAIqD,UACR,0DAA0DrD,EAGhE,CA6EEigB,CAAajgB,GAtEf,SAAuB8c,GACrB,KAAIA,aAAmB5a,QAEvB,GAAuB,iBAAZ4a,EAAX,CASA,IAAI/E,MAAMC,QAAQ8E,GAclB,MAAM,IAAIzZ,UACR,yGAAyGyZ,GAdzG,GAAuB,IAAnBA,EAAQxb,OACV,MAAM,IAAI+B,UAAU,8CAEtB,IAAK,MAAM6c,KAAKpD,EACd,GAAiB,iBAANoD,IAAmBjB,GAAiBiB,GAC7C,MAAM,IAAI7c,UACR,+BAA+B6c,gCAAgC7gB,OAAOiZ,KAAKoH,IAAgBpc,KAAK,QATxG,MANE,IAAK2b,GAAiBnC,GACpB,MAAM,IAAIzZ,UACR,+BAA+ByZ,uBAA6Bzd,OAAOiZ,KAAKoH,IAAgBpc,KAAK,QAuBrG,CA0CE6c,CAAcrD,GAGVA,aAAmB5a,OACrB,OAAO4a,EAAQ/Z,KAAK/C,GAItB,GAAuB,iBAAZ8c,EACT,OAAwC,OAAjC8C,GAAa5f,EAAO8c,GAI7B,IAAK,MAAMoD,KAAKpD,EACd,GAA+B,OAA3B8C,GAAa5f,EAAOkgB,GAAa,OAAO,EAE9C,OAAO,CACT,CClIe,MAAME,GACnB3f,WAAAA,CAAY4f,GAAW,GACrBrhB,KAAKqhB,SAAWA,CAClB,CAQAC,IAAAA,CAAKC,GACHvhB,KAAKuhB,IAAMA,CACb,CAOAvc,KAAAA,GACE,CAYFwc,KAAAA,CAAM5G,EAAK6G,GACT,MAAM,IAAI/f,MAAM,gDAClB,EC9BF,MAAMiQ,GAAiB,CACrB0K,cAAe,IAAK3C,GACpBwC,gBAAgB,EAEhByB,cAAeA,CAACnc,EAAMR,IACpBggB,GAAShgB,EAAO,CAAC0f,GAAehH,MAC5BM,EAAcG,MAAQH,EAAcE,OCGtCwH,GAAe,CAEnB,GAGA,KACA,KAGA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KAGA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KAGA,MAGA,OACA,OACA,OACA,OACA,OAGA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,OACA,OACA,OACA,OACA,QAMIC,GAAW,IAAIld,IAMfmd,GAHU,KAKVC,GAAQ,IAAIC,WADCC,OACsBC,KATvB,KAWlB,IAAK,MAAMC,KAAQP,GACjB,IAAK,IAAIQ,EAAI,EAAGA,EAAI,GAAIA,IAAK,CAC3B,MAAM3C,EAAK0C,EAAOC,EACd3C,GAXQ,MAYVsC,GAAMtC,EAAKqC,IAAgBM,EAE3BP,GAAS5c,IAAIwa,EAAI2C,EAErB,CC5GF,MAiBMC,GAAY,IAAI/K,IAAI,CAAC,KAAQ,MAAQ,QCrBrCgL,GAAW,wBACXC,GAAW,YACXC,GAAW,aACXC,GAAW,qCAIXC,GAAW,CACbC,KAAK,EACLC,QAAQ,EACRC,OAAO,EACPC,cAAc,EACdC,aAAc,IACdC,WAAW,EAEXC,SAAU,WACVC,SAAS,GAGE,SAASC,GAASnf,EAAK0B,EAAU,CAAC,GAE7C,GADAA,EAAUnF,OAAO2R,OAAO,CAAC,EAAGwQ,GAAUhd,IACjC1B,GAAsB,iBAARA,EAAkB,OAAOA,EAE5C,IAAIof,EAAapf,EAAI+D,OAErB,GAA0B,IAAtBqb,EAAW5gB,OAAc,OAAOwB,EAC/B,QAAyBc,IAArBY,EAAQ2d,UAA0B3d,EAAQ2d,SAASpf,KAAKmf,GAAa,OAAOpf,EAChF,GAAmB,MAAfof,EAAoB,OAAO,EAEpC,GAAI1d,EAAQwd,UACRE,EDSR,SAAgBpf,GACd,GAAmB,iBAARA,EAAkB,OAAOA,EAEpC,MAAM2C,EAAM3C,EAAIxB,OAChB,GAAY,IAARmE,EAAW,OAAO3C,EAItB,IAAIsf,GAAY,EAEhB,IAAK,IAAI1c,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,MAAM2c,EAAKvf,EAAIkE,WAAWtB,GAG1B,KAAK2c,GAjDM,IAiDUA,GAhDV,IACI,KA+CuBA,GAGtC,GAAIA,EAAKzB,IACP,GAAIO,GAAU5K,IAAI8L,GAAK,CAAED,EAAW1c,EAAG,KAAO,OAKhD,GAAI2c,GAAM,OAAUA,GAAM,OACxB,GAAI3c,EAAI,EAAID,EAAK,CACf,MAAM6c,EAAMxf,EAAIkE,WAAWtB,EAAI,GAC/B,GAAI4c,GAAO,OAAUA,GAAO,MAAQ,CAClC,MAAM/D,EAAK,OAAY8D,EAAK,OAAW,KAAOC,EAAM,OACpD,GAAI3B,GAASpK,IAAIgI,GAAK,CAAE6D,EAAW1c,EAAG,KAAO,CAC/C,CACF,OAKF,GDmBc,MCnBVmb,GAAMwB,EAAKzB,KAA+BO,GAAU5K,IAAI8L,GAAK,CAC/DD,EAAW1c,EACX,KACF,CACF,CAGA,IAAkB,IAAd0c,EAAiB,OAAOtf,EAG5B,MAAMyf,EAAQ,GAEVH,EAAW,GAAGG,EAAMpc,KAAKrD,EAAIgD,MAAM,EAAGsc,IAE1C,IAAK,IAAI1c,EAAI0c,EAAU1c,EAAID,EAAKC,IAAK,CACnC,MAAM2c,EAAKvf,EAAIkE,WAAWtB,GAG1B,GAAK2c,GAxFM,IAwFUA,GAvFV,IACI,KAsFuBA,EAAmB,CACvDE,EAAMpc,KAAKrD,EAAI4C,IACf,QACF,CAGA,GAAI2c,EAAKzB,GAAc,CACrB2B,EAAMpc,KAAKgb,GAAU5K,IAAI8L,GAAM,IAAMvf,EAAI4C,IACzC,QACF,CAGA,GAAI2c,GAAM,OAAUA,GAAM,MAAQ,CAChC,GAAI3c,EAAI,EAAID,EAAK,CACf,MAAM6c,EAAMxf,EAAIkE,WAAWtB,EAAI,GAC/B,GAAI4c,GAAO,OAAUA,GAAO,MAAQ,CAClC,MAAM/D,EAAK,OAAY8D,EAAK,OAAW,KAAOC,EAAM,OAC9CpB,EAAIP,GAASnhB,IAAI+e,GACvB,QAAU3a,IAANsd,EAAiB,CACnBqB,EAAMpc,KAAK8I,OAAOuT,aAAatB,EAAI,KACnCxb,IACA,QACF,CACF,CACF,CACA6c,EAAMpc,KAAKrD,EAAI4C,IACf,QACF,CAGA,GAAIyb,GAAU5K,IAAI8L,GAAK,CACrBE,EAAMpc,KAAK,KACX,QACF,CACA,MAAM+a,EAAIL,GAAMwB,EAAKzB,IACrB2B,EAAMpc,KDlCQ,MCkCH+a,EAAkBjS,OAAOuT,aAAatB,EAAI,IAAMpe,EAAI4C,GACjE,CAEA,OAAO6c,EAAMjf,KAAK,GACpB,CCrGqBmf,CAAOP,GACD,MAAfA,GAAoB,OAAO,EAEnC,GAAI1d,EAAQid,KAAOL,GAASre,KAAKmf,GAC7B,OAAOQ,GAAUR,EAAY,IAC1B,GAAI1d,EAAQkd,QAAUL,GAASte,KAAKmf,GACvC,OAAOQ,GAAUR,EAAY,GAC1B,GAAI1d,EAAQmd,OAASL,GAASve,KAAKmf,GACtC,OAAOQ,GAAUR,EAAY,GAC1B,GAAKS,SAAST,GAEd,IAAIA,EAAW9e,SAAS,MAAQ8e,EAAW9e,SAAS,KACvD,OAmDR,SAA0BN,EAAKof,EAAY1d,GACvC,IAAKA,EAAQsd,UAAW,OAAOhf,EAC/B,MAAM8f,EAAWV,EAAW1gB,MAAMqhB,IAClC,GAAID,EAAU,CACV,IAAIE,EAAOF,EAAS,IAAM,GAC1B,MAAMG,GAAsC,IAA9BH,EAAS,GAAGzd,QAAQ,KAAc,IAAM,IAChDyc,EAAegB,EAAS,GACxBI,EAA0BF,EAC5BhgB,EAAI8e,EAAatgB,OAAS,KAAOyhB,EAC/BjgB,EAAI8e,EAAatgB,UAAYyhB,EAEnC,OAAInB,EAAatgB,OAAS,GAAK0hB,EAAgClgB,GAC9B,IAAxB8e,EAAatgB,SACdshB,EAAS,GAAG7d,WAAW,IAAIge,MAAYH,EAAS,GAAG,KAAOG,IAEvDnB,EAAatgB,OAAS,EAEzBkD,EAAQod,eAAiBoB,GACzBd,GAAcU,EAAS,IAAM,IAAMA,EAAS,GACrCjE,OAAOuD,IACJpf,EANP6b,OAAOuD,EAWtB,CACI,OAAOpf,CAEf,CA/EemgB,CAAiBngB,EAAKof,EAAY1d,GACtC,CAEH,MAAMhD,EAAQ+f,GAAS2B,KAAKhB,GAE5B,GAAI1gB,EAAO,CACP,MAAMshB,EAAOthB,EAAM,IAAM,GACnBogB,EAAepgB,EAAM,GAC3B,IAAI2hB,GA8EGC,EA9E2B5hB,EAAM,MA+ET,IAAzB4hB,EAAOje,QAAQ,MAEV,OADfie,EAASA,EAAOnhB,QAAQ,MAAO,KACXmhB,EAAS,IACN,MAAdA,EAAO,GAAYA,EAAS,IAAMA,EACJ,MAA9BA,EAAOA,EAAO9hB,OAAS,KAAY8hB,EAASA,EAAOhiB,UAAU,EAAGgiB,EAAO9hB,OAAS,IAClF8hB,GAEJA,EArFC,MAAMC,EAAgCP,EACD,MAAjChgB,EAAI8e,EAAatgB,OAAS,GACK,MAA7BwB,EAAI8e,EAAatgB,QAGvB,IAAKkD,EAAQod,eACLA,EAAatgB,OAAS,GACM,IAAxBsgB,EAAatgB,SAAiB+hB,GAEtC,OAAOvgB,EAEN,CACD,MAAMwgB,EAAM3E,OAAOuD,GACbqB,EAAYtU,OAAOqU,GAEzB,GAAY,IAARA,EAAW,OAAOA,EACtB,IAAkC,IAA9BC,EAAUC,OAAO,QACjB,OAAIhf,EAAQsd,UAAkBwB,EAClBxgB,EACT,IAAiC,IAA7Bof,EAAW/c,QAAQ,KAC1B,MAAkB,MAAdoe,GACKA,IAAcJ,GACdI,IAAc,GAAGT,IAAOK,IAFHG,EAGlBxgB,EAGhB,IAAImK,EAAI2U,EAAeuB,EAAoBjB,EAC3C,OAAIN,EAEQ3U,IAAMsW,GAAeT,EAAO7V,IAAMsW,EAAaD,EAAMxgB,EAGrDmK,IAAMsW,GAAetW,IAAM6V,EAAOS,EAAaD,EAAMxgB,CAErE,CACJ,CACI,OAAOA,CAEf,EAuCJ,IAAmBsgB,EAxFX,OAoHR,SAAwBtgB,EAAKwgB,EAAK9e,GAC9B,MAAMif,EAAaH,IAAQzS,IAE3B,OAAQrM,EAAQud,SAASrM,eACrB,IAAK,OACD,OAAO,KACX,IAAK,WACD,OAAO4N,EACX,IAAK,SACD,OAAOG,EAAa,WAAa,YAErC,QACI,OAAO3gB,EAEnB,CAlIe4gB,CAAe5gB,EAAK6b,OAAOuD,GAAa1d,EAkDvD,CAEA,MAAMqe,GAAgB,0CA+CtB,SAASH,GAAUU,EAAQO,GACvB,MAAM7gB,EAAMsgB,EAAOvc,OAGnB,GAFa,IAAT8c,GAAuB,IAATA,IAAYP,EAAStgB,EAAI1B,UAAU,IAEjDqK,SAAU,OAAOA,SAAS2X,EAAQO,GACjC,GAAIhF,OAAOlT,SAAU,OAAOkT,OAAOlT,SAAS2X,EAAQO,GACpD,GAAIC,QAAUA,OAAOnY,SAAU,OAAOmY,OAAOnY,SAAS2X,EAAQO,GAC9D,MAAM,IAAIjjB,MAAM,+DACzB,CC3Ie,MAAMmjB,WAAkBzD,GACrC3f,WAAAA,CAAY+D,EAASsf,GAAU,GAC7BC,MAAMD,GACN9kB,KAAKwF,QAAUA,GAAW,CAAC,CAC7B,CAOAgc,KAAAA,CAAM5G,GACJ,GAAmB,iBAARA,EAAkB,CAC3B,MAAMoK,EAAS/B,GAASrI,EAAK5a,KAAKwF,SAClC,UAAWwf,IAAWpK,EACpB,OAAO5a,KAAKqhB,SAAW,IAAI4D,GAAWD,GAAUA,CAEpD,CACA,OAAOpK,CACT,ECjBF,MAAMsK,GAAoB,CACxB,OAAU,ILGG,cAA2B9D,GACxC,IAAQ,EACR,GAAW,KACX,GAAW,KAKX3f,WAAAA,CAAY+D,EAASsf,GAAU,GAC7BC,MAAMD,GACN9kB,MAAK,EAAW,IACX2R,MACCnM,GAAW,CAAC,EAEhB6W,cAAe,IAAK1K,GAAe0K,iBAAmB7W,GAAS6W,eAAiB,CAAC,GAErF,CAEA,KAIE,GAHKrc,MAAK,IACRA,MAAK,EAAW,IAAIsb,GAActb,MAAK,KAEpCA,MAAK,EAAO,CACf,MAAMgG,EAAUhG,KAAKuhB,KAAK/gB,IAAI,cACxB2kB,EAAWnlB,KAAKuhB,KAAK/gB,IAAI,iBAC3BwF,GAAShG,MAAK,EAASse,cAActY,GACrCmf,GAAUnlB,MAAK,EAASqe,iBAAiB8G,GAC7CnlB,MAAK,GAAQ,CACf,CACF,CAEAgF,KAAAA,GACEhF,MAAK,GAAQ,EACTA,MAAK,GACPA,MAAK,EAASgF,OAElB,CAEAwc,KAAAA,CAAM5G,GACJ,MAAmB,iBAARA,EAAyBA,GACpC5a,MAAK,IACEA,MAAK,EAASue,OAAO3D,GAC9B,GK5CA,KAAQ,ICTK,cAAsBwG,GACjCI,KAAAA,CAAM5G,GACF,MAAmB,iBAARA,EAAyBA,EAAI/S,OAC5B+S,CAChB,GDMF,GAAM,IEeO,cAA2BwG,GAMxC3f,WAAAA,CAAY+D,EAASsf,GAAU,GAC7BC,MAAMD,GACN,MAAMM,EAAU5f,GAAS4f,SAAW,GAE9BrgB,EAAM,IAAI+R,EAChB,IAAK,MAAM1G,KAASgV,EAClBrgB,EAAIuS,IAAqB,iBAAVlH,EAAqB,IAAI5F,EAAW4F,GAASA,GAE9DrL,EAAI4S,OAGJ3X,KAAKqlB,YAActgB,CACrB,CASAyc,KAAAA,CAAM5G,EAAK2G,GACT,GAAmB,iBAAR3G,EAAkB,OAAOA,EAEpC,GAAI2G,EAAK,CAEP,GAAIA,EAAI+D,YAAa,OAAO1K,EAE5B,GAAI2G,EAAIzU,QAAS,CAEf,GAAkD,aAA9CyU,EAAIzU,QAAQS,iBAAiB,aAA6B,OAAOqN,EAGrE,GAAI5a,KAAKqlB,YAAYvgB,KAAO,GAAK9E,KAAKqlB,YAAYlX,WAAWoT,EAAIzU,SAAU,OAAO8N,CACpF,CACF,CAEA,OAAuBA,EAWd3X,QAAQ,cAAe,KAAK4E,MAVvC,GF1DA,QAAW,IGXE,cAAyBuZ,GACpC3f,WAAAA,CAAY8jB,EAAUC,EAAWV,GAAU,GACvCC,MAAMD,GAEF9kB,KAAKulB,SADLA,GAGgB,CAAC,QAGjBvlB,KAAKwlB,UADLA,GAGiB,CAAC,QAC1B,CACAhE,KAAAA,CAAM5G,GACF,GAAmB,iBAARA,EAAkB,CAEzB,MAAM6K,EAAO7K,EAAIlE,cACjB,IAAqC,IAAjC1W,KAAKulB,SAASpf,QAAQsf,GAAc,OAAOzlB,KAAKqhB,UAAW,IAAI4D,IAAW,GACzE,IAAsC,IAAlCjlB,KAAKwlB,UAAUrf,QAAQsf,GAAc,QAAOzlB,KAAKqhB,UAAW,IAAI4D,IAAW,EACxF,CACA,OAAOrK,CACX,GHTF,OAAU,IAAI8K,GAAa,CAAEjD,KAAK,EAAMG,cAAc,EAAME,WAAW,KAI1D,MAAM6C,GACnBlkB,WAAAA,GACEzB,KAAK4lB,WAAa,IAAKV,GACzB,CAMAW,QAAAA,CAASrkB,EAAMskB,GACb,IAAKtkB,GAAwB,iBAATA,EAClB,MAAM,IAAIE,MAAM,yBAElB,IAAKokB,EACH,MAAM,IAAIpkB,MAAM,sBAElB,IAAKokB,EAAO9gB,OAAiC,mBAAjB8gB,EAAO9gB,MACjC,MAAM,IAAItD,MAAM,iCAElB,IAAKokB,EAAOtE,OAAiC,mBAAjBsE,EAAOtE,MACjC,MAAM,IAAI9f,MAAM,iCAElB1B,KAAK4lB,WAAWpkB,GAAQskB,CAC1B,CAEA9gB,KAAAA,CAAMxD,GACJxB,KAAK4lB,WAAWpkB,IAAOwD,OACzB,CAEA+gB,QAAAA,GACE,IAAK,MAAMC,KAAMhmB,KAAK4lB,WACpB5lB,KAAK4lB,WAAWI,IAAKhhB,OAEzB,CAOAxE,GAAAA,CAAIgB,GACF,MAAMykB,EAAMjmB,KAAK4lB,WAAWpkB,GAC5B,IAAKykB,EACH,MAAM,IAAIvkB,MAAM,qBAAuBF,GAEzC,OAAOykB,CACT,EI3DK,MAAMC,GAOXzkB,WAAAA,CAAY4R,EAAavG,EAASqZ,EAAYb,GAAc,GAC1DtlB,KAAKqT,YAAcA,EACnBrT,KAAK8M,QAAUA,EACf9M,KAAKmmB,WAAaA,EAClBnmB,KAAKslB,YAAcA,CACrB,EA4BK,MAAML,GAEXxjB,WAAAA,CAAYT,GACVhB,KAAKgB,MAAQA,CACf,EAyBK,MAAMolB,GACX,GAAQ,CAAC,EAGTrhB,GAAAA,CAAI5E,EAAKa,GAAShB,MAAK,EAAMG,GAAOa,CAAO,CAG3CR,GAAAA,CAAIL,GAAO,OAAOH,MAAK,EAAMG,EAAM,CAOnCkmB,KAAAA,GAAUrmB,MAAK,EAAQ,CAAC,CAAG,EA+BtB,MAAMsmB,GAMX7kB,WAAAA,CAAY8kB,EAAa,GAAIC,EAAUC,EAAgB,MACrDzmB,KAAKumB,WAAaA,EAClBvmB,KAAKwmB,SAAWA,EAChBxmB,KAAKymB,cAAgBA,GAAiB,IAAIL,GAE1CpmB,KAAK0mB,SAASH,EAChB,CAYAI,GAAAA,CAAI/L,EAAKgM,GACP,IAAK,IAAIlgB,EAAI,EAAGA,EAAI1G,KAAKumB,WAAWjkB,OAAQoE,IAAK,CAC/C,IAAIof,EAAS9lB,KAAKumB,WAAW7f,GAE7B,GADsB,iBAAXof,IAAqBA,EAAS9lB,KAAKwmB,SAAShmB,IAAIslB,IACvDA,EAAQ,CACV,MAAMjhB,EAASihB,EAAOtE,MAAM5G,EAAKgM,GACjC,GAAI/hB,aAAkBogB,GAAY,OAAOpgB,EAAO7D,MAChD4Z,EAAM/V,CACR,CACF,CACA,OAAO+V,CACT,CAEAmL,QAAAA,GACE,IAAK,IAAIrf,EAAI,EAAGA,EAAI1G,KAAKumB,WAAWjkB,OAAQoE,IAAK,CAC/C,IAAIof,EAAS9lB,KAAKumB,WAAW7f,GACP,iBAAXof,IAAqBA,EAAS9lB,KAAKwmB,SAAShmB,IAAIslB,IACvDA,GAAYA,EAAO9gB,OAAiC,mBAAjB8gB,EAAO9gB,OAAsB8gB,EAAO9gB,OAC7E,CACF,CAUA6gB,QAAAA,CAASrkB,EAAMqlB,GACb7mB,KAAKwmB,SAASX,SAASrkB,EAAMqlB,GACzBA,EAASvF,MAAMuF,EAASvF,KAAKthB,KAAKymB,cACxC,CAeAC,QAAAA,CAASI,GACP,IAAK9mB,KAAKymB,cAAe,OACzB,MAAMM,EAAO,IAAI3P,IACjB,IAAK,MAAMxQ,KAAKkgB,EAAW,CACzB,IAAIE,EAAiBpgB,EACJ,iBAANA,IAAgBogB,EAAiBhnB,KAAKwmB,SAAShmB,IAAIoG,IAC1DogB,GAAiD,mBAAxBA,EAAe1F,OAAwByF,EAAKxP,IAAIyP,KAC3ED,EAAKzP,IAAI0P,GACTA,EAAe1F,KAAKthB,KAAKymB,eAE7B,CACF,ECrMF,MAAMQ,GAAsB,CAAC,KAAM,SAAU,UAAW,UAClDC,GAAuB,CAAC,SAAU,UAAW,UAEpC,MAAMC,GASnB1lB,WAAAA,CAAY2lB,EAAeC,EAAgBC,EAAad,EAAUe,GAAiB,GACjFvnB,KAAK8M,QAAUwa,EACftnB,KAAKwnB,UAAY,IACjBxnB,KAAKonB,cAAgBA,EACrBpnB,KAAKqnB,eAAiBA,EACtBrnB,KAAKwmB,SAAWA,EAEhB,MAAMiB,EAAWJ,GAAgB7S,MAAM2D,cAAgB8O,GACjDS,EAAYL,GAAgBjP,YAAYD,cAAgB+O,GAQ9DlnB,KAAKymB,cAAgB,IAAIL,GAOzBpmB,KAAK2nB,aAAe,IAAIrB,GAAoBmB,EAAUznB,KAAKwmB,SAAUxmB,KAAKymB,eAO1EzmB,KAAK4nB,cAAgB,IAAItB,GAAoBoB,EAAW1nB,KAAKwmB,SAAUxmB,KAAKymB,eAExEc,IACFvnB,KAAK2nB,aAAa5B,WAClB/lB,KAAK4nB,cAAc7B,YAGrB/lB,KAAK6nB,kBAAmB,CAC1B,CAUAC,YAAAA,CAAatmB,EAAMR,EAAO8L,GAGX,YAATtL,GAAsBxB,KAAK0O,UAAY1O,KAAKwnB,WAC9CxnB,KAAKymB,eAAe1hB,IAAI,cAAe/D,GAEzC,MAAM+mB,EAAW,GAAG/nB,KAAKonB,cAAchP,WAAW4P,SAASxmB,IAAOxB,KAAKonB,cAAchP,WAAW6P,SAC1FnK,EAAU,IAAIoI,GAAQ1kB,EAAMsL,GAAS,GAAM,GACjD9M,KAAKoY,WAAW2P,GAAY/nB,KAAK4nB,cAAcjB,IAAI3lB,EAAO8c,EAE5D,CAGAoK,SAAAA,CAAU/nB,EAAKya,GAAO,CAOtBuN,UAAAA,CAAWC,GACLpoB,KAAKonB,cAAcnR,KAAKjM,SACxBhK,KAAKonB,cAAclP,QAAQlO,SAC7BhK,KAAKkoB,UAAUloB,KAAKonB,cAAclP,QAAQlO,QAASoe,EAEvD,CAOAC,UAAAA,CAAWD,GACLpoB,KAAKonB,cAAcnR,KAAKqS,QACxBtoB,KAAKonB,cAAclP,QAAQoQ,MAC7BtoB,KAAKkoB,UAAUloB,KAAKonB,cAAclP,QAAQoQ,MAAOF,GAEjDpoB,KAAKuoB,YAAYH,GAAQ,IAE7B,CAMAG,WAAAA,CAAYH,GACVpoB,KAAKwoB,SAASJ,EAChB,CASA/J,gBAAAA,CAAiB8G,GACfnlB,KAAKymB,eAAe1hB,IAAI,gBAAiBogB,EAC3C,CAMAsD,cAAAA,CAAejnB,GACbxB,KAAK0oB,eAAelnB,EACtB,CAMAknB,cAAAA,CAAelnB,GACf,CAQAmnB,UAAAA,CAAWC,EAAWC,GACpB7oB,KAAK6nB,kBAAmB,EACqB,mBAAlC7nB,KAAKonB,cAAcuB,YAC5B3oB,KAAKonB,cAAcuB,WAAWC,EAAWC,EAAY7oB,KAAK8M,QAE9D,CAcAgc,MAAAA,CAAOC,GAAY,EC/JN,MAAMC,GACnBvnB,WAAAA,CAAY4lB,EAAiB,CAAC,GAC5BrnB,KAAKqnB,eAAiBA,EACtBrnB,KAAKwmB,SAAW,IAAIb,EACtB,CASAsD,mBAAAA,CAAoBznB,EAAMwlB,GACxBhnB,KAAKwmB,SAASX,SAASrkB,EAAMwlB,EAC/B,CAWAkC,WAAAA,CAAY9B,EAAeE,GAOzB,MAAM,IAAI5lB,MAAM,iCAClB,ECnCa,MAAMynB,WAA8BH,GACjDvnB,WAAAA,CAAY4lB,EAAiB,CAAC,GAC5BtC,QACA/kB,KAAKqnB,eAAiB1O,EAAa0O,EACrC,CAEA6B,WAAAA,CAAY9B,EAAegC,GACzB,OAAO,IAAIC,GACTjC,EACApnB,KAAKqnB,eACL+B,EACAppB,KAAKwmB,SAET,EAGK,MAAM6C,WAAuBlC,GAQlC1lB,WAAAA,CAAY2lB,EAAeC,EAAgB+B,EAAiB5C,GAC1DzB,MAAMqC,EAAeC,EAAgB+B,EAAiB5C,GACtDxmB,KAAKonB,cAAgBA,EACrBpnB,KAAKqnB,eAAiBA,EACtBrnB,KAAKspB,UAAY,GACjBtpB,KAAKN,KAAO,CAAC,EACbM,KAAKupB,OAASvpB,KAAKN,KACnBM,KAAK0O,QAAU1O,KAAKwnB,UACpBxnB,KAAKgB,MAAQ,CAAC,EACdhB,KAAKwpB,UAAY,GACjBxpB,KAAKoY,WAAa,CAAC,EACnBpY,KAAKypB,eAAgB,CACvB,CAMAC,oBAAAA,GACE,OAAIC,GAAQ3pB,KAAKoY,aACfpY,KAAKypB,eAAgB,EACd,KAETzpB,KAAKypB,eAAgB,EACjBzpB,KAAKonB,cAAchP,WAAWwR,QACzB,CAAE,CAAC5pB,KAAKonB,cAAchP,WAAWwR,SAAU5pB,KAAKoY,YAElDpY,KAAKoY,WACd,CAEAyR,UAAAA,CAAW/d,GACT,MAAM9K,EAAQhB,KAAK0pB,uBACnB1pB,KAAKspB,UAAUniB,KAAK,CAACnH,KAAK0O,QAAS1O,KAAKwpB,UAAWxpB,KAAKgB,MAAOhB,KAAKypB,gBACpEzpB,KAAK0O,QAAU5C,EAAItK,KACnBxB,KAAKgB,MAAQA,EACbhB,KAAKwpB,UAAY,GACjBxpB,KAAKoY,WAAa,CAAC,CACrB,CAkBA0R,kBAAAA,CAAmB3D,GAEjB,IAAI4D,EAMAC,EACJ,GANgBhqB,KAAKqnB,eAAelO,gBAAgBhL,WAAWnO,KAAK8M,WACvDid,GAAa,GAKoB,mBAAnC/pB,KAAKqnB,eAAe9O,WAA2B,CACxD,MAAM1T,EAAS7E,KAAKqnB,eAAe9O,WAAWvY,KAAK8M,QAASqZ,IAC7C,IAAXthB,EAAiBmlB,GAAY,GACb,IAAXnlB,IAAkBmlB,GAAY,EAEzC,CAGA,OAAmB,IAAfD,IAAsC,IAAdC,KACJ,IAAfD,IAAqC,IAAdC,EAElC,CAEAC,YAAAA,GACE,MAAMvb,EAAU1O,KAAK0O,QACrB,IAAI1N,EAAQhB,KAAKgB,MACjB,MAAMwoB,EAAYxpB,KAAKwpB,UACjBC,EAAgBzpB,KAAKypB,cAMrBtD,EAA8B,iBAAVnlB,GACrB+X,MAAMC,QAAQhY,IACd2oB,GAAQ3oB,IACRyoB,EAEC3L,EAAU,IAAIoI,GAClBxX,EACA1O,KAAK8M,QACLqZ,GACA,GAGF,GAAIA,EAAY,CACd,MAAM+D,EAAalqB,KAAK6nB,iBAAmB2B,EAAYxpB,KAAK2nB,aAAahB,IAAI6C,EAAW1L,GAEpF2L,GAKiB,KAAfS,SAAqBA,GAEdlqB,KAAKqnB,eAAe7O,iBAD7BxX,EAAMhB,KAAKonB,cAAclP,QAAQkQ,MAAQ8B,GAM3ClpB,EAFShB,KAAKqnB,eAAe7O,cAErB,CAAE,CAACxY,KAAKonB,cAAclP,QAAQkQ,MAAO8B,GAGrCA,CAEZ,MAAO,GAAIV,EAAUlnB,OAAS,GAAKtC,KAAKqnB,eAAe7O,cAAe,CAGpE,MAAM0R,EAAalqB,KAAK6nB,iBAAmB2B,EAAYxpB,KAAK2nB,aAAahB,IAAI6C,EAAW1L,GACxF9c,EAAMhB,KAAKonB,cAAclP,QAAQkQ,MAAQ8B,CAC3C,CAEA,IAAIC,EAAY,CAAEzb,UAAS1N,SAO3B,MAAMopB,EAAMpqB,KAAKspB,UAAUla,MAC3B,IAAIib,EAAYD,EAAI,GAGpB,MAAME,EAAmBtqB,KAAK8pB,mBAAmB3D,GAEjDkE,EAAYrqB,KAAKuqB,YAAYJ,EAAUzb,QAASyb,EAAUnpB,MAAOqpB,EAAWC,GAE5EtqB,KAAK0O,QAAU0b,EAAI,GACnBpqB,KAAKwpB,UAAYY,EAAI,GACrBpqB,KAAKgB,MAAQqpB,EACbrqB,KAAKypB,cAAgBW,EAAI,GACzBpqB,KAAK6nB,kBAAmB,CAC1B,CAEAK,SAAAA,CAAU/nB,EAAKya,GACa,iBAAf5a,KAAKgB,QACdhB,KAAKgB,MAAQ,CAAE,CAAChB,KAAKonB,cAAclP,QAAQkQ,MAAOpoB,KAAKgB,QAEzDhB,KAAKuqB,YAAYpqB,EAAKya,EAAK5a,KAAKgB,OAAO,GACvChB,KAAKoY,WAAa,CAAC,CACrB,CAEAmS,WAAAA,CAAYpqB,EAAKya,EAAK3L,EAAMsJ,GAS1B,MARoB,iBAATtJ,IAAmBA,EAAO,CAAC,GAEjC5O,OAAOM,UAAUC,eAAeC,KAAKoO,EAAM9O,IAGzC4Y,MAAMC,QAAQ/J,EAAK9O,MAAO8O,EAAK9O,GAAO,CAAC8O,EAAK9O,KACjD8O,EAAK9O,GAAKgH,KAAKyT,IAHf3L,EAAK9O,GAAOoY,EAAa,CAACqC,GAAOA,EAK5B3L,CACT,CAEAuZ,QAAAA,CAASJ,GACHpoB,KAAKwpB,UAAUlnB,OAAS,EAAGtC,KAAKwpB,WAAa,GAAGxpB,KAAKqnB,eAAehP,YAAY+P,IAC/EpoB,KAAKwpB,UAAYpB,CACxB,CAEAM,cAAAA,CAAelnB,GACb,MAAMR,EAAQhB,KAAK0pB,uBACnB1pB,KAAKkoB,UAAU1mB,EAAMR,GACrBhB,KAAKoY,WAAa,CAAC,CACrB,CAEA0Q,MAAAA,CAAOC,GAAY,CAEnByB,SAAAA,GACE,OAAOxqB,KAAKgB,KACd,EAGF,SAAS2oB,GAAQlpB,GACf,OAAmC,IAA5BJ,OAAOiZ,KAAK7Y,GAAK6B,MAC1B,CC/MO,MAAMmoB,WAAmB/oB,MAO9BD,WAAAA,CAAYN,EAASC,EAAMgK,EAAW,CAAC,GACrC2Z,MAAM5jB,GACNnB,KAAKwB,KAAO,aACZxB,KAAKoB,KAAOA,EAEZpB,KAAKkC,MAAQkJ,EAASlJ,YAAS0C,CACjC,CAEAuD,QAAAA,GACE,MAAMmN,EAAMtV,KAAK0qB,UACjB,OAAOpV,EAAM,GAAGtV,KAAKwB,SAASxB,KAAKoB,YAAYkU,MAAQtV,KAAKmB,UAAY,GAAGnB,KAAKwB,SAASxB,KAAKoB,UAAUpB,KAAKmB,SAC/G,CAEAupB,OAAAA,GACE,YAAmB9lB,IAAf5E,KAAKkC,MACA,SAASlC,KAAKkC,QAEhB,IACT,EAKK,MAAMyoB,GAAYtqB,OAAO4Z,OAAO,CAErC2Q,cAAe,gBACfC,eAAgB,iBAGhBC,kBAAmB,oBACnBC,cAAe,gBACfC,oBAAqB,sBAGrBC,eAAgB,iBAChBC,qBAAsB,uBACtBC,qBAAsB,uBACtBC,yBAA0B,2BAC1BC,YAAa,cACbC,eAAgB,iBAChBC,uBAAwB,yBAGxBC,oBAAqB,sBAGrBC,kBAAmB,oBACnBC,oBAAqB,sBACrBC,yBAA0B,2BAC1BC,2BAA4B,6BAG5BC,6BAA8B,+BAC9BC,yBAA0B,2BAC1BC,yBAA0B,2BAG1BC,sBAAuB,wBACvBC,qBAAsB,uBAGtBC,iBAAkB,mBAClBC,gBAAiB,kBACjBC,sBAAuB,wBACvBC,2BAA4B,6BAG5BC,mBAAoB,qBACpBC,qBAAsB,uBAGtBC,qBAAsB,uBACtBC,gBAAiB,kBACjBC,kBAAmB,sBCtEd,SAASC,GAAQ7qB,GACtB,MAAgB,MAATA,GAAyB,OAATA,GAA0B,OAATA,GAA0B,OAATA,GAA0B,OAATA,CAC5E,CAGO,SAAS8qB,GAAYxrB,GAC1B,OAAgB,KAATA,GAAwB,IAATA,GAAuB,KAATA,GAAwB,KAATA,GAAwB,KAATA,CACpE,CAkBO,MAAMyrB,GAA2B,CACtC,iBACA,WACA,UACA,mBACA,mBACA,mBACA,mBACA,iBACA,gBACA,wBAGWC,GAAqB,CAAC,YAAa,cAAe,aA0BxD,SAASC,GAAiBvT,GAC/B,OAAOA,EAAO/W,YAAc+W,EAAOwT,aAAe,EACpD,CAQO,SAASC,GAAgBzT,GAC9B,MAAO,CAAEtX,MAAO6qB,GAAiBvT,GACnC,CAUA,SAASzX,GAAqBmf,GAC5B,OAAOA,GAAK,GAAW,KAANA,GAAkB,KAANA,GAAaA,GAAK,IAAMA,GAAK,EAC5D,CAmBO,SAASgM,GAAgBppB,EAAK0V,GACnC,MAAM/S,EAAM3C,EAAIxB,OAChB,IAAI6qB,GAAQ,EACZ,IAAK,IAAIzmB,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,MAAMwa,EAAIpd,EAAIkE,WAAWtB,GACzB,GAAI3E,GAAqBmf,GACvB,MAAM,IAAIuJ,GACR,+BAA+BvJ,EAAE/Y,SAAS,IAAIC,SAAS,EAAG,2BAC1DuiB,GAAUc,kBACVjS,EAASyT,GAAgBzT,GAAU,CAAC,GAG9B,KAAN0H,IAAUiM,GAAQ,EACxB,CACA,IAAKA,EAAO,OAAOrpB,EAEnB,IAAI2W,EAAM,GACN2S,EAAW,EACf,IAAK,IAAI1mB,EAAI,EAAGA,EAAID,EAAKC,IACG,KAAtB5C,EAAIkE,WAAWtB,KACjB+T,GAAO3W,EAAI1B,UAAUgrB,EAAU1mB,GAAK,KACN,KAA1B5C,EAAIkE,WAAWtB,EAAI,IAAWA,IAClC0mB,EAAW1mB,EAAI,GAInB,OADA+T,GAAO3W,EAAI1B,UAAUgrB,GACd3S,CACT,CAaO,SAAS4S,GAAY7T,EAAQ8T,EAAUC,EAAUC,GAAkB,GACxE,MAAM/mB,EAAM6mB,EAAShrB,OACrB,IAAKkX,EAAOiU,QAAQhnB,GAClB,MAAM,IAAIgkB,GACR,oCAAoC8C,IACpC5C,GAAUM,eACVgC,GAAgBzT,IAIpB,IAAgB,IADAA,EAAOkU,WAAWJ,EAAUE,GAE1C,MAAM,IAAI/C,GACR,WAAW8C,IACX5C,GAAUU,YACV4B,GAAgBzT,IAGpBA,EAAOmU,qBAAqBlnB,EAC9B,CAWO,SAASmnB,GAAcpU,EAAQvL,EAAGsf,GACvC,IAAK/T,EAAOiU,QAAQxf,GAClB,MAAM,IAAIwc,GACR,oCAAoC8C,IACpC5C,GAAUM,eACVgC,GAAgBzT,GAGtB,CCvMA,MAAMqU,GAA8BrsB,GAC9BqrB,GAAyBzoB,SAAS5C,GAC7B,KAAOA,EAETA,EAGImQ,GAAiB,CAG5BsE,KAAM,CACJ6X,aAAa,EACbC,IAAI,EACJ3V,YAAY,EACZkQ,OAAO,EACPte,SAAS,EACTgkB,UAAU,EACVxZ,KAAM,GACNyZ,gBAAgB,GAKlB/V,QAAS,CACPkQ,KAAM,QACNE,MAAO,GACPte,QAAS,IAIXoO,WAAY,CAKV8V,YAAa,QAIbC,UAAW,YACXvE,QAAS,GACT5B,OAAQ,KACRC,OAAQ,IAIVzT,KAAM,CACJ4Z,SAAU,GACVC,UAAW,IAIbC,qBAAqB,EACrBC,oBAAqBV,GASrBW,eAAe,EAGfC,KAAM,GAkBNC,eAAgB,CACdC,SAAS,EACT/c,eAAgB,IAChBE,cAAe,KAsBjB8c,UAAW,KAaXC,OAAQ,CACNC,cAAe,KACfC,oBAAqB,MAoBvBC,SAAU,CACRC,cAAe,SACfC,WAAW,EACXC,eAAgB,KAChBC,WAAY,KAoBdC,OAAQA,KAAM,EAIdC,cAAe,KAoBfC,SAAU,CACRC,SAAU,OACVC,eAAgB,OAKdC,GAAe,IAAItY,IAAI,IAAI0V,MAAuBD,KAGxD,SAAS8C,GAAqB3uB,EAAOyY,GACnC,GAAqB,iBAAVzY,GAAgC,KAAVA,GAC7B0uB,GAAanY,IAAIvW,GACnB,MAAM,IAAIypB,GACR,cAAczpB,6DAAiEyY,IAC/EkR,GAAUmB,yBAGhB,CAEO,MAAMnT,GAAe,SAAUnT,GAEpC,GAAIA,IACEA,EAAQ0S,SAASkQ,MAAMuH,GAAqBnqB,EAAQ0S,QAAQkQ,KAAM,gBAClE5iB,EAAQ0S,SAASoQ,OAAOqH,GAAqBnqB,EAAQ0S,QAAQoQ,MAAO,iBACpE9iB,EAAQ0S,SAASlO,SAAS2lB,GAAqBnqB,EAAQ0S,QAAQlO,QAAS,mBACxExE,EAAQ4S,YAAY4P,QAAQ2H,GAAqBnqB,EAAQ4S,WAAW4P,OAAQ,qBAC5ExiB,EAAQ4S,YAAYwR,SAAS+F,GAAqBnqB,EAAQ4S,WAAWwR,QAAS,2BAG3DhlB,IAAnBY,EAAQqpB,QAA2C,OAAnBrpB,EAAQqpB,QAAiB,CAC3D,GAA8B,iBAAnBrpB,EAAQqpB,OACjB,MAAM,IAAIpE,GAAW,0CAA0CjlB,EAAQqpB,OAAUlE,GAAUC,eAE7F,MAAM,cAAEkE,EAAa,oBAAEC,GAAwBvpB,EAAQqpB,OACvD,GAAIC,UACwB,iBAAlBA,IAA+BnP,OAAOiQ,UAAUd,IAAkBA,EAAgB,GAC1F,MAAM,IAAIrE,GAAW,0DAA0DqE,IAAiBnE,GAAUC,eAE5G,GAAImE,UAC8B,iBAAxBA,IAAqCpP,OAAOiQ,UAAUb,IAAwBA,EAAsB,GAC5G,MAAM,IAAItE,GAAW,oEAAoEsE,IAAuBpE,GAAUC,cAE9H,CAGF,MAAMhS,EAAeC,GAAUlH,IA2B/B,GAzBInM,GACF4T,GAAeR,EAAcpT,GAG1BoT,EAAa0W,gBAChB1W,EAAa0W,cAAgB,IAAInG,IAoB/BpQ,MAAMC,QAAQJ,EAAapE,MAAM6Z,WAAY,CAC/C,MAAMwB,EAAU,IAAI/Y,EACpB8B,EAAapE,KAAK6Z,UAAYzV,EAAapE,KAAK6Z,UAAUrgB,IACvDoC,GAAU0f,GAAkB1f,EAAO,YAAayf,IAEnDA,EAAQlY,OACRiB,EAAapE,KAAKub,aAAeF,CACnC,CAEA,GAAI9W,MAAMC,QAAQJ,EAAa3C,MAAMzB,MAAO,CAC1C,MAAMwb,EAAU,IAAIlZ,EACpB8B,EAAa3C,KAAKzB,KAAOoE,EAAa3C,KAAKzB,KAAKxG,IAC7CoC,GAAU0f,GAAkB1f,EAAO,YAAa4f,IAEnDA,EAAQrY,OACRiB,EAAa3C,KAAKga,QAAUD,CAC9B,CAOA,GALyC,OAArCpX,EAAa2V,sBACf3V,EAAa2V,oBAAsBV,IAIT,OAAxBjV,EAAayW,aAA2CzqB,IAAxBgU,EAAayW,QACZ,mBAAxBzW,EAAayW,OACtB,MAAM,IAAI5E,GACR,2CAA2C7R,EAAayW,OACxD1E,GAAUC,eAQhB,OAFAhS,EAAagW,UAmBf,SAA0BjU,EAAKuV,GAC7B,IAAKvV,EAAK,OAAO,KAEjB,GAAY,SAARA,EAAgB,CAElB,MAAMwV,EAAmBD,EAAK1b,KAAK4Z,UAAY,GACzC/Z,EAAS,IAAI,IAAI+C,IAAI,IAAI+Y,KAAqBC,MAGpD,OAFAF,EAAK1b,KAAO,IAAK0b,EAAK1b,KAAM4Z,SAAU/Z,GAE/B,CACLgc,MAAO,WACPC,WAAY,UACZC,eAAe,EAEnB,CAEA,MAAmB,iBAAR5V,EAEF,CACL0V,MAAO1V,EACP2V,WAAY,QACZC,eAAe,GAIA,iBAAR5V,EACF,CACL0V,MAAO1V,EAAI0V,OAAS,QACpBC,WAAY3V,EAAI2V,YAAc,QAC9BC,cAAe5V,EAAI4V,gBAAiB,GAIjC,IACT,CArD2BC,CAAiB5X,EAAagW,UAAWhW,GAE3DA,CACT,EAGMwX,GAAqB,CACzB,OAAQ,OAAQ,KAAM,MAAO,QAAS,KAAM,MAAO,QACnD,OAAQ,OAAQ,QAAS,SAAU,QAAS,OA6D9C,SAASN,GAAkB1f,EAAOqJ,EAAY1U,GAC5C,IAAI0F,EAASgmB,EAAQC,EAErB,GAAqB,iBAAVtgB,EAAoB,CAC7B,GAAqB,IAAjBA,EAAM9N,OAAc,MAAM,IAAImoB,GAAW,GAAGhR,+BAAyCkR,GAAUC,eACnGngB,EAAU2F,EACVqgB,GAAS,EACTC,EAAiB,EACnB,MAAO,GAAItgB,aAAiB5F,EAE1BC,EAAU2F,EAAMjI,WAChBsoB,EAASrgB,EAAM1F,MAAM+lB,SAAU,EAC/BC,EAAiBtgB,EAAM1F,MAAMgmB,gBAAkB,OAC1C,KAAItgB,GAA0B,iBAAVA,QAA2CxL,IAArBwL,EAAMlC,WAarD,MAAM,IAAIuc,GACR,WAAWhR,8FACXkR,GAAUC,eAfmE,CAC/E,MAAMjQ,EAAMvK,EAAMlC,WAClB,GAAmB,iBAARyM,EAAkB,CAC3B,GAAmB,IAAfA,EAAIrY,OAAc,MAAM,IAAImoB,GAAW,GAAGhR,+BAAyCkR,GAAUC,eACjGngB,EAAUkQ,CACZ,KAAO,MAAIA,aAAenQ,GAGxB,MAAM,IAAIigB,GAAW,GAAGhR,uDAAiEkR,GAAUC,eAFnGngB,EAAUkQ,EAAIxS,UAGhB,CACAsoB,GAA0B,IAAjBrgB,EAAMqgB,OACfC,EAAiB3X,MAAMC,QAAQ5I,EAAMsgB,gBAAkBtgB,EAAMsgB,eAAiB,EAChF,CAKA,CAEA,MAAMvf,EAAO,IAAI3G,EAAWC,EAAS,CAAC,EAAG,CAAEgmB,SAAQC,mBAEnD,OADA3rB,EAAIuS,IAAInG,GACDA,CACT,CAEA,SAAS0H,GAAUpY,GACjB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpD,GAAIsY,MAAMC,QAAQvY,GAAM,OAAOA,EAAIuN,IAAI6K,IACvC,GAAIpY,aAAeyC,OAAQ,OAAOzC,EAClC,GAAIA,aAAe+J,EAAY,OAAO/J,EACtC,MAAM4Y,EAAQ,CAAC,EACf,IAAK,MAAMlZ,KAAOE,OAAOiZ,KAAK7Y,GAC5B4Y,EAAMlZ,GAA2B,mBAAbM,EAAIN,GAAsBM,EAAIN,GAAO0Y,GAAUpY,EAAIN,IAEzE,OAAOkZ,CACT,CAEA,SAASD,GAAeG,EAAQC,GAC9B,IAAK,MAAMrZ,KAAOE,OAAOiZ,KAAKE,GAEhB,cAARrZ,GAA+B,gBAARA,GAAiC,cAARA,IAExC,kBAARA,GAE8B,mBAAhBqZ,EAAOrZ,IAEdqZ,EAAOrZ,aAAgB+C,QAEvB6V,MAAMC,QAAQQ,EAAOrZ,IAL9BoZ,EAAOpZ,GAAOqZ,EAAOrZ,GAOW,iBAAhBqZ,EAAOrZ,IAAqC,OAAhBqZ,EAAOrZ,IACxB,iBAAhBoZ,EAAOpZ,IAAqC,OAAhBoZ,EAAOpZ,KAC5CoZ,EAAOpZ,GAAO,CAAC,GAEjBiZ,GAAeG,EAAOpZ,GAAMqZ,EAAOrZ,KAEnCoZ,EAAOpZ,GAAOqZ,EAAOrZ,GAG3B,CCpaO,SAASwwB,KACd,MAAMC,EAAM5wB,KAAK6wB,OACXpqB,EAAMmqB,EAAItuB,OACVuR,EAAQ7T,KAAKyC,WACbquB,EAAQ9wB,KAAK+wB,YACbC,EAAWF,EAAMxuB,OACvB,IAAI2uB,EAAW,EACXC,GAAW,EACXC,GAAW,EACf,IAAK,IAAIzqB,EAAImN,EAAOnN,EAAID,EAAKC,IAAK,CAChC,MAAMwa,EAAI0P,EAAIlqB,GACd,GAAU,MAANwa,EACGiQ,IACHD,GAAYA,EACRD,EAAWD,IAAUF,EAAMG,KAAcvqB,EAAImN,SAE9C,GAAU,MAANqN,EACJgQ,IACHC,GAAYA,EACRF,EAAWD,IAAUF,EAAMG,KAAcvqB,EAAImN,SAE9C,GAAU,MAANqN,IAAcgQ,IAAaC,EAEpC,OADAnxB,KAAKoxB,eAAiBH,EACfvqB,EAAImN,CAEf,CAEA,OADA7T,KAAKoxB,eAAiBH,GACd,CACV,CAWO,SAASI,KACd,MAAMT,EAAM5wB,KAAK6wB,OACXpqB,EAAMmqB,EAAItuB,OACVuR,EAAQ7T,KAAKyC,WACnB,IAAIyuB,GAAW,EACXC,GAAW,EACf,IAAK,IAAIzqB,EAAImN,EAAOnN,EAAID,EAAKC,IAAK,CAChC,MAAMwa,EAAI0P,EAAIlqB,GACd,GAAU,MAANwa,EACGiQ,IAAUD,GAAYA,QACtB,GAAU,MAANhQ,EACJgQ,IAAUC,GAAYA,QACtB,GAAU,MAANjQ,IAAcgQ,IAAaC,EACpC,OAAOzqB,EAAImN,CAEf,CACA,OAAQ,CACV,CC9Ce,MAAMyd,GAOnB7vB,WAAAA,CAAYqC,EAAK0B,EAAU,CAAC,GAC1BxF,KAAK6wB,OAAS/sB,EAEd9D,KAAKyC,WAAa,EAKlBzC,KAAKgtB,YAAc,EAEnBhtB,KAAKkvB,WAAkC,IAAtB1pB,EAAQ0pB,UACzBlvB,KAAKmvB,eAAiB3pB,EAAQ2pB,gBAAkB,KAKhDnvB,KAAKuxB,OAAS,EAAE,GAAI,GAWpBvxB,KAAK+wB,YAAc,IAAIS,WHES,KGDhCxxB,KAAKoxB,eAAiB,CACxB,CAkBAK,cAAAA,CAAe5iB,EAAQ,GACrB7O,KAAKuxB,OAAO1iB,GAAS7O,KAAKyC,UAC5B,CAUAivB,YAAAA,GACE,CAIFC,SAAAA,GACE3xB,KAAKuxB,OAAO,IAAM,EAClBvxB,KAAKuxB,OAAO,IAAM,CACpB,CASAK,KAAAA,GACE,IAAIC,EAAS7xB,KAAKyC,WAClB,IAAK,MAAM8E,KAAKvH,KAAKuxB,OACfhqB,GAAK,GAAKA,EAAIsqB,IAAQA,EAAStqB,GAErC,GAAIsqB,EAAS,EAAG,CACd7xB,KAAK6wB,OAAS7wB,KAAK6wB,OAAOzuB,UAAUyvB,GACpC,MAAMC,EAAW9xB,KAAKuxB,OAAOjvB,OAC7B,IAAK,IAAIoE,EAAI,EAAGA,EAAIorB,EAAUprB,IACxB1G,KAAKuxB,OAAO7qB,IAAM,IAAG1G,KAAKuxB,OAAO7qB,IAAMmrB,GAE7C7xB,KAAKyC,YAAcovB,EACnB7xB,KAAKgtB,aAAe6E,CACtB,CACF,CAIAE,MAAAA,GACE,OAAO/xB,KAAK6wB,OAAO7wB,KAAKyC,aAC1B,CAEAuvB,QAAAA,CAAS9vB,GACP,OAAOlC,KAAK6wB,OAAO7wB,KAAKyC,WAAaP,EACvC,CAEA+vB,OAAAA,CAAQhkB,EAAGikB,GAET,YADoB,IAATA,IAAsBA,EAAOlyB,KAAKyC,YACtCzC,KAAK6wB,OAAOzuB,UAAU8vB,EAAMA,EAAOjkB,EAC5C,CAqBAyf,UAAAA,CAAWJ,EAAUE,GAAkB,GACrC,MAAM/mB,EAAM6mB,EAAShrB,OACrB,IAAK,IAAIoE,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,IAAIpB,EAAKtF,KAAK6wB,OAAO7wB,KAAKyC,WAAaiE,GACvC,QAAW9B,IAAPU,EAAkB,OAAO,KAE7B,GADIkoB,IAAiBloB,EAAKA,EAAGoR,eACzBpR,IAAOgoB,EAAS5mB,GAAI,OAAO,CACjC,CACA,OAAO,CACT,CAIAiqB,cAAgBA,GAChBU,kBAAoBA,GAEpBc,QAAAA,CAASC,GACP,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAE3B,IAAK,IAAIoE,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,IAAIlE,GAAQ,EACZ,IAAK,IAAIqG,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAErG,GAAQ,EAAO,KAAO,CAEjE,GAAIA,EAAO,CACT,MAAMqC,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI4rB,EACfztB,CACT,CACF,CAEA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,CAUAsH,YAAAA,CAAaC,GACX,MAAM9rB,EAAI1G,KAAK6wB,OAAO1qB,QAAQqsB,EAAUxyB,KAAKyC,YAC7C,IAAW,IAAPiE,EACF,MAAM,IAAI+jB,GAAW,qCAAqC+H,KAAa7H,GAAUM,gBAEnF,MAAMpmB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CAEA4tB,gBAAAA,CAAiBL,GACf,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAC3B,IAAIowB,GAAiB,EAEjBC,EAAQ,EAEZ,IAAK,IAAIjsB,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,GAAc,IAAVisB,EAAa,CACf,MAAMzR,EAAIlhB,KAAK6wB,OAAOnqB,GACtB,GAAIimB,GAAQzL,GAAI,SACN,MAANA,EAAayR,EAAQ,GAClBA,EAAQ,EAAGD,GAAiB,EACrC,KAAO,CAEL,IAAIE,GAAU,EACd,IAAK,IAAI/pB,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAE+pB,GAAU,EAAO,KAAO,CAE/DA,IACFD,EAAQ,EACRD,EAAgBhsB,EAChBA,GAAK4rB,EAAa,EAEtB,CACA,GAAc,IAAVK,EAAa,CACf,MAAM9tB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiwB,GAEtD,OADA1yB,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CACF,CAEA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,CAEA4H,cAAAA,CAAe5kB,EAAG6kB,GAChB,MAAMxtB,EAAW,IAAN2I,EACPjO,KAAK6wB,OAAO7wB,KAAKyC,YACjBzC,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYzC,KAAKyC,WAAawL,GAE7D,OADI6kB,GAAa9yB,KAAK2tB,qBAAqB1f,GACpC3I,CACT,CAYAqoB,oBAAAA,CAAqB1f,EAAI,GACvBjO,KAAKyC,YAAcwL,EAMfjO,KAAKkvB,WAAalvB,KAAKyC,YAAczC,KAAKmvB,gBAC5CnvB,KAAK4xB,OAET,CAQAnE,OAAAA,CAAQxf,EAAI,GACV,OAAOjO,KAAKyC,WAAawL,EAAIjO,KAAK6wB,OAAOvuB,MAC3C,ECzQK,SAASywB,GAAuBC,EAAcC,EAAe,OAAQC,EAAqBA,KAAM,GACrG,MAAO,CACLnB,MAAAA,GACE,MAAM,KAAEjwB,EAAI,MAAEqxB,GAAUH,EAAahzB,KAAK6wB,OAAQ7wB,KAAKyC,YAEvD,OADAzC,KAAKyC,YAAc0wB,EACZrxB,CACT,EAGAkwB,QAAAA,CAAS9vB,GACP,OAAO8wB,EAAahzB,KAAK6wB,OAAQ7wB,KAAKyC,WAAaP,GAAOJ,IAC5D,EAEAmwB,OAAAA,CAAQhkB,EAAGikB,GAET,YADoB,IAATA,IAAsBA,EAAOlyB,KAAKyC,YACtCzC,KAAK6wB,OAAO/pB,MAAMorB,EAAMA,EAAOjkB,GAAG9F,SAAS8qB,EACpD,EAWAvF,UAAAA,CAAWJ,EAAUE,GAAkB,GACrC,MAAM/mB,EAAM6mB,EAAShrB,OACrB,IAAK,IAAIoE,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,MAAM0sB,EAAIpzB,KAAK6wB,OAAO7wB,KAAKyC,WAAaiE,GACxC,QAAU9B,IAANwuB,EAAiB,OAAO,KAC5B,IAAIhyB,EAAOgyB,EAEX,GADI5F,GAAmBpsB,GAAQ,IAAMA,GAAQ,KAAIA,GAAQ,IACrDA,IAASksB,EAAStlB,WAAWtB,GAAI,OAAO,CAC9C,CACA,OAAO,CACT,EAMAiqB,aAAAA,GACE,MAAMC,EAAM5wB,KAAK6wB,OACXpqB,EAAMmqB,EAAItuB,OACVuR,EAAQ7T,KAAKyC,WACbquB,EAAQ9wB,KAAK+wB,YACbC,EAAWF,EAAMxuB,OACvB,IAAI2uB,EAAW,EACXC,GAAW,EACXC,GAAW,EACf,IAAK,IAAIzqB,EAAImN,EAAOnN,EAAID,EAAKC,IAAK,CAChC,MAAMwa,EAAI0P,EAAIlqB,GACd,GAAU,KAANwa,EACGiQ,IACHD,GAAYA,EACRD,EAAWD,IAAUF,EAAMG,KAAcvqB,EAAImN,SAE9C,GAAU,KAANqN,EACJgQ,IACHC,GAAYA,EACRF,EAAWD,IAAUF,EAAMG,KAAcvqB,EAAImN,SAE9C,GAAU,KAANqN,IAAagQ,IAAaC,EAEnC,OADAnxB,KAAKoxB,eAAiBH,EACfvqB,EAAImN,CAEf,CAEA,OADA7T,KAAKoxB,eAAiBH,GACd,CACV,EAEAI,iBAAAA,GACE,MAAMT,EAAM5wB,KAAK6wB,OACXpqB,EAAMmqB,EAAItuB,OACVuR,EAAQ7T,KAAKyC,WACnB,IAAIyuB,GAAW,EACXC,GAAW,EACf,IAAK,IAAIzqB,EAAImN,EAAOnN,EAAID,EAAKC,IAAK,CAChC,MAAMwa,EAAI0P,EAAIlqB,GACd,GAAU,KAANwa,EACGiQ,IAAUD,GAAYA,QACtB,GAAU,KAANhQ,EACJgQ,IAAUC,GAAYA,QACtB,GAAU,KAANjQ,IAAagQ,IAAaC,EACnC,OAAOzqB,EAAImN,CAEf,CACA,OAAQ,CACV,EAEAse,QAAAA,CAASC,GACP,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OACrB+wB,EAAaC,OAAOpB,KAAKE,GAC/B,IAAK,IAAI1rB,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,IAAIlE,GAAQ,EACZ,IAAK,IAAIqG,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOwqB,EAAWxqB,GAAI,CAAErG,GAAQ,EAAO,KAAO,CAEpE,GAAIA,EAAO,CACT,MAAMqC,EAAS7E,KAAK6wB,OAAO/pB,MAAM9G,KAAKyC,WAAYiE,GAAGyB,SAAS8qB,GAE9D,OADAjzB,KAAKyC,WAAaiE,EAAI4rB,EACfztB,CACT,CACF,CACA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,EAEAsH,YAAAA,CAAaC,GACX,MAAMe,EAAWf,EAASxqB,WAAW,GAC/B4oB,EAAM5wB,KAAK6wB,OACXpqB,EAAMmqB,EAAItuB,OAChB,IAAK,IAAIoE,EAAI1G,KAAKyC,WAAYiE,EAAID,EAAKC,IACrC,GAAIkqB,EAAIlqB,KAAO6sB,EAAU,CACvB,MAAM1uB,EAAS+rB,EAAI9pB,MAAM9G,KAAKyC,WAAYiE,GAAGyB,SAAS8qB,GAEtD,OADAjzB,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CAEF,MAAM,IAAI4lB,GAAW,qCAAqC+H,KAAa7H,GAAUM,eACnF,EAEAwH,gBAAAA,CAAiBL,GACf,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OACrB+wB,EAAaC,OAAOpB,KAAKE,GAE/B,IAAIM,GAAiB,EACjBC,EAAQ,EACZ,IAAK,IAAIjsB,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,MAAM0sB,EAAIpzB,KAAK6wB,OAAOnqB,GACtB,GAAc,IAAVisB,EAAa,CACf,GAAI/F,GAAYwG,GAAI,SANb,KAOHA,EAAYT,EAAQ,GACjBA,EAAQ,EAAGD,GAAiB,EACrC,KAAO,CACL,IAAIE,GAAU,EACd,IAAK,IAAI/pB,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOwqB,EAAWxqB,GAAI,CAAE+pB,GAAU,EAAO,KAAO,CAElEA,IAAWD,EAAQ,EAAGD,EAAgBhsB,EAAGA,GAAK4rB,EAAa,EACjE,CACA,GAAc,IAAVK,EAAa,CACf,MAAM9tB,EAAS7E,KAAK6wB,OAAO/pB,MAAM9G,KAAKyC,WAAYiwB,GAAevqB,SAAS8qB,GAE1E,OADAjzB,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CACF,CACA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,EAEA4H,cAAAA,CAAe5kB,EAAGulB,GAChB,GAAU,IAANvlB,EAAS,CACX,MAAM,KAAEnM,EAAI,MAAEqxB,GAAUH,EAAahzB,KAAK6wB,OAAQ7wB,KAAKyC,YAEvD,OADI+wB,GAAcxzB,KAAK2tB,qBAAqBwF,GACrCrxB,CACT,CACA,MAAMwD,EAAKtF,KAAK6wB,OAAO/pB,MAAM9G,KAAKyC,WAAYzC,KAAKyC,WAAawL,GAAG9F,SAAS8qB,GAE5E,OADIO,GAAcxzB,KAAK2tB,qBAAqB1f,GACrC3I,CACT,EAEAqoB,oBAAAA,CAAqB1f,EAAI,GACvBjO,KAAKyC,YAAcwL,EACfjO,KAAKkvB,WAAalvB,KAAKyC,YAAczC,KAAKmvB,gBAAkBnvB,KAAKyzB,YAAc,GACjFzzB,KAAK4xB,OAET,EAGAnE,OAAAA,CAAQxf,EAAI,GACV,OAAOjO,KAAKyC,WAAawL,EAAIjO,KAAK6wB,OAAOvuB,MAC3C,EAEJ,CAIO,SAASoxB,GAAwB9C,EAAKlqB,GAC3C,MAAO,CAAE5E,KAAMmO,OAAOuT,aAAaoN,EAAIlqB,IAAKysB,MAAO,EACrD,CAEO,SAASQ,GAAiB/C,EAAKlqB,GACpC,MAAMktB,EAAKhD,EAAIlqB,GACf,IAAIysB,EAAQ,EAUZ,OATU,IAALS,EACoB,MAAV,IAALA,GAAqBT,EAAQ,EACd,MAAV,IAALS,GAAqBT,EAAQ,EACd,MAAV,IAALS,KAAqBT,EAAQ,GAHbA,EAAQ,EAO9BzsB,EAAIysB,EAAQvC,EAAItuB,SAAQ6wB,EAAQvC,EAAItuB,OAASoE,GAE1C,CAAE5E,KADI8uB,EAAIzoB,SAAS,OAAQzB,EAAGA,EAAIysB,GAC1BA,QACjB,CC3NA,MAUMU,GAAwBd,GAAuBY,GAAkB,QAsBxD,MAAMG,GAiBnBryB,WAAAA,CAAYsyB,EAAUvuB,EAAU,CAAC,EAAGwuB,EAAU,MAI5C,GADAh0B,KAAK6wB,OAASmD,GAASC,UAAYF,EAASG,SAASF,EAAQC,WAAaF,EACtEC,GAASG,YAAa,CAKxB,MAAMC,EAAUJ,EAAQK,WAAWC,gBACnCt0B,KAAK6wB,OAASuD,EAAQG,MAAMv0B,KAAK6wB,QAAUuD,EAAQnuB,KACrD,CACAjG,KAAKyC,WAAa,EAIlBzC,KAAKgtB,YAAc,EAEnBhtB,KAAKkvB,WAAkC,IAAtB1pB,EAAQ0pB,UACzBlvB,KAAKmvB,eAAiB3pB,EAAQ2pB,gBAAkB,KAGhDnvB,KAAKyzB,aAAe,EAKpBzzB,KAAK+wB,YAAc,IAAIS,WLZS,KKahCxxB,KAAKoxB,eAAiB,EAItB,MAAMoD,EAAWR,GAASS,cAAgBZ,GAC1CxzB,OAAO2R,OAAOhS,KAAMw0B,GACpBx0B,KAAK00B,aAAeV,GAASK,YAAY7yB,MAAQ,OAKjDxB,KAAK20B,oBAAoBX,IAAuC,IAA7BA,EAAQY,gBAC7C,CAUAnD,cAAAA,GACEzxB,KAAKyzB,YAAczzB,KAAKyC,UAC1B,CAUAivB,YAAAA,GACE,CAWFE,KAAAA,GACE,MAAMC,EAAS7xB,KAAKyzB,aAAe,EAAIzzB,KAAKyzB,YAAczzB,KAAKyC,WAC3DovB,EAAS,IAGX7xB,KAAK6wB,OAASyC,OAAOpB,KAAKlyB,KAAK6wB,OAAOqD,SAASrC,IAC3C7xB,KAAKyzB,aAAe,GACtBzzB,KAAKyC,YAAcovB,EACnB7xB,KAAKyzB,YAAc,GAEnBzzB,KAAKyC,WAAa,EAEpBzC,KAAKgtB,aAAe6E,EAExB,EC1HK,SAASgD,GAAMC,EAAOtO,GAC3B,MAAMuO,EAmBR,SAAkBD,EAAOtO,GACvB,IAAK,MAAM6N,KAAc7N,EAASwO,gBAAiB,CACjD,MAAMC,EAAMZ,EAAWa,SACvB,GAAIJ,EAAMxyB,QAAU2yB,EAAI3yB,QAAU2yB,EAAIE,OAAOL,EAAMZ,SAAS,EAAGe,EAAI3yB,SACjE,MAAO,CAAE+xB,aAAYJ,UAAWgB,EAAI3yB,OAExC,CACA,OAAO,IACT,CA3BmB8yB,CAASN,EAAOtO,GAC3B6O,EA4BR,SAA0BP,EAAOQ,GAG/B,MACMC,EADST,EAAMZ,SAASoB,EAAQpY,KAAKsY,IAAIV,EAAMxyB,OAAQgzB,EAlDvC,MAkDkEntB,SAAS,UACxE3F,MAAM,yBAC/B,IAAK+yB,EAAW,OAAO,KACvB,MAAME,EAAWF,EAAU,GAAG/yB,MAAM,oCACpC,OAAOizB,EAAWA,EAAS,GAAG/e,cAAgB,IAChD,CApC2Bgf,CAAiBZ,EAAOC,EAAWA,EAASd,UAAY,GAEjF,GAAIc,GAAYM,IAoClB,SAAsBM,EAAGvC,EAAG5M,GAC1B,IACE,OAAOA,EAASoP,QAAQD,GAAGn0B,OAASglB,EAASoP,QAAQxC,GAAG5xB,IAC1D,CAAE,MACA,OAAO,CACT,CACF,CA1CuCq0B,CAAad,EAASV,WAAW7yB,KAAM6zB,EAAkB7O,GAC5F,MAAM,IAAIiE,GACR,8BAA8BsK,EAASV,WAAW7yB,gDAAgD6zB,KAClG1K,GAAU+B,mBAId,OAAIqI,EACK,CAAEvF,SAAUuF,EAASV,WAAW7yB,KAAMyyB,UAAWc,EAASd,UAAWoB,oBAE1EA,EACK,CAAE7F,SAAU6F,EAAkBpB,UAAW,EAAGoB,oBAE9C,CAAE7F,SAAU,OAAQyE,UAAW,EAAGoB,iBAAkB,KAC7D,CCZO,SAASS,GAAyBC,GACvC,MAAM3B,EAAU,IAAI4B,YAAYD,EAAO,CAAEE,OAAO,EAAOC,WAAW,IAClE,MAAO,CACL3B,MAAM3D,GAGGwD,EAAQ7V,OAAOqS,EAAK,CAAEuF,QAAQ,IAEvClwB,IAAGA,IAIMmuB,EAAQ7V,SAGrB,CCbe,MAAM6X,GACnB30B,WAAAA,GACEzB,KAAKq2B,QAAU,IAAI5xB,IACnBzE,KAAKs2B,eACP,CAEAA,aAAAA,GACEt2B,KAAK6lB,SAAS,CACZrkB,KAAM,OACN+0B,QAAS,CAAC,SACVrB,SAAU5B,OAAOpB,KAAK,CAAC,IAAM,IAAM,MACnCsE,mBAAmB,EACnBC,eAAe,EACfnC,cAAeA,IAAMwB,GAAyB,WAEhD91B,KAAK6lB,SAAS,CACZrkB,KAAM,QACN+0B,QAAS,GACTrB,SAAU,KACVsB,mBAAmB,EACnBC,eAAe,EAKfnC,cAAeA,IAAMwB,GAAyB,kBAEhD91B,KAAK6lB,SAAS,CACZrkB,KAAM,SACN+0B,QAAS,CAAC,aAAc,UACxBrB,SAAU,KACVsB,mBAAmB,EACnBC,eAAe,EACfnC,cAAeA,IAAMwB,GAAyB,gBAEhD91B,KAAK6lB,SAAS,CACZrkB,KAAM,UACN+0B,QAAS,CAAC,WAAY,OAAQ,SAC9BrB,SAAU5B,OAAOpB,KAAK,CAAC,IAAM,MAC7BsE,mBAAmB,EACnBC,eAAe,EACfnC,cAAeA,IAAMwB,GAAyB,cAEhD91B,KAAK6lB,SAAS,CACZrkB,KAAM,UACN+0B,QAAS,CAAC,YAGVrB,SAAU5B,OAAOpB,KAAK,CAAC,IAAM,MAC7BsE,mBAAmB,EACnBC,eAAe,EACfnC,cAAeA,ID9Bd,WACL,MAAMoC,EAAQZ,GAAyB,YACvC,IAAIa,EAAU,KAEd,MAAO,CACLpC,KAAAA,CAAM3D,GACJ,IAAIgG,EAAOD,EAejB,SAAqBhB,EAAGvC,GACtB,MAAM3Y,EAAM,IAAIqH,WAAW6T,EAAErzB,OAAS8wB,EAAE9wB,QAGxC,OAFAmY,EAAI1V,IAAI4wB,EAAG,GACXlb,EAAI1V,IAAIquB,EAAGuC,EAAErzB,QACNmY,CACT,CApB2Boc,CAAYF,EAAS/F,GAAOA,EACjD+F,EAAU,KACNC,EAAKt0B,OAAS,GAAM,IACtBq0B,EAAUC,EAAK1C,SAAS0C,EAAKt0B,OAAS,GACtCs0B,EAAOA,EAAK1C,SAAS,EAAG0C,EAAKt0B,OAAS,IAExC,MAAMw0B,EAgBZ,SAAuBhC,GACrB,MAAMgC,EAAU,IAAIhV,WAAWgT,GAC/B,IAAK,IAAIpuB,EAAI,EAAGA,EAAI,EAAIowB,EAAQx0B,OAAQoE,GAAK,EAAG,CAC9C,MAAMqwB,EAAMD,EAAQpwB,GACpBowB,EAAQpwB,GAAKowB,EAAQpwB,EAAI,GACzBowB,EAAQpwB,EAAI,GAAKqwB,CACnB,CACA,OAAOD,CACT,CAxBsBE,CAAcJ,GAC9B,OAAOF,EAAMnC,MAAMuC,EACrB,EACA7wB,IAAGA,IACMywB,EAAMzwB,MAGnB,CCW2BgxB,IAEzB,CAQApR,QAAAA,CAASwO,GACP,IAAKA,GAAyC,iBAApBA,EAAW7yB,OAAsB6yB,EAAW7yB,KACpE,MAAM,IAAIipB,GAAW,kDAAmDE,GAAU8B,iBAEpF,GAAwC,mBAA7B4H,EAAWC,cACpB,MAAM,IAAI7J,GAAW,aAAa4J,EAAW7yB,mCAAoCmpB,GAAU8B,iBAE7F,MAAMyK,EAAQ7C,EAAWC,gBACzB,IAAK4C,GAAgC,mBAAhBA,EAAM3C,OAA6C,mBAAd2C,EAAMjxB,IAC9D,MAAM,IAAIwkB,GACR,aAAa4J,EAAW7yB,mEACxBmpB,GAAU8B,iBAGd,MAAMvN,EAAW,CACfsX,mBAAmB,EACnBC,eAAe,EACfF,QAAS,GACTrB,SAAU,QACPb,GAELr0B,KAAKq2B,QAAQtxB,IAAIma,EAAS1d,KAAKkV,cAAewI,GAC9C,IAAK,MAAMiY,KAASjY,EAASqX,QAASv2B,KAAKq2B,QAAQtxB,IAAIoyB,EAAMzgB,cAAewI,EAC9E,CAEA0W,OAAAA,CAAQp0B,GACN,MAAM6yB,EAAar0B,KAAKq2B,QAAQ71B,IAAIyP,OAAOzO,GAAMkV,eACjD,IAAK2d,EACH,MAAM,IAAI5J,GAAW,yBAAyBjpB,KAASmpB,GAAU6B,sBAEnE,OAAO6H,CACT,CAGAW,aAAAA,GACE,MAAMjO,EAAO,IAAI3P,IACXqD,EAAM,GACZ,IAAK,MAAMyH,KAAKliB,KAAKq2B,QAAQjpB,SACvB8U,EAAEgT,WAAanO,EAAKxP,IAAI2K,EAAE1gB,QAC5BulB,EAAKzP,IAAI4K,EAAE1gB,MACXiZ,EAAItT,KAAK+a,IAGb,OAAOzH,EAAI2c,KAAK,CAACzB,EAAGvC,IAAMA,EAAE8B,SAAS5yB,OAASqzB,EAAET,SAAS5yB,OAC3D,EAGK,MAAM+0B,GAA0B,IAAIjB,GC/D3C,SAASkB,GAAkBpW,GACzB,OAAOA,GAAK,GAAW,KAANA,GAAkB,KAANA,GAAaA,GAAK,IAAMA,GAAK,EAC5D,CAkBA,SAASqW,GAAchvB,EAAS7B,EAAGT,EAAK6f,GACtC,IAAI9kB,EAAQ,GACRosB,EAAW1mB,EACf,KAAOA,EAAIT,EAAKS,IAAK,CACnB,MAAMwa,EAAI3Y,EAAQP,WAAWtB,GAC7B,GAAU,KAANwa,EACFlgB,GAASuH,EAAQnG,UAAUgrB,EAAU1mB,GAAK,IACR,KAA9B6B,EAAQP,WAAWtB,EAAI,IAAWA,IACtC0mB,EAAW1mB,EAAI,OACV,GAAU,KAANwa,GAAkB,IAANA,EACrBlgB,GAASuH,EAAQnG,UAAUgrB,EAAU1mB,GAAK,IAC1C0mB,EAAW1mB,EAAI,OACV,GAAI4wB,GAAkBpW,GAC3B,MAAM,IAAIuJ,GACR,+BAA+BvJ,EAAE/Y,SAAS,IAAIC,SAAS,EAAG,0BAC1DuiB,GAAUc,kBACV3F,EAASmH,GAAgBnH,EAAOtM,QAAU,CAAC,EAGjD,CAEA,OADAxY,GAASuH,EAAQnG,UAAUgrB,EAAU1mB,GAC9B1F,CACT,CAyKO,SAASw2B,GAAgBC,EAAa3R,EAAQ4R,EAAeC,GAClE,IAAKF,GAAsC,IAAvBA,EAAYn1B,OAAc,OAE9C,MAAMs1B,EAAW9R,EAAOtgB,QAAQqpB,QAAQE,oBACxC,GAAI6I,SAA+CD,EAAoBC,EAAU,CAC/E,MAAMlpB,EAAUoX,EAAO+R,kBAAkBr2B,MAAQ,YACjD,MAAM,IAAIipB,GACR,QAAQ/b,UAAgBipB,oCAAoDC,IAC5EjN,GAAUsB,qBACVgB,GAAgBnH,EAAOtM,QAE3B,CAEA,MAAM/S,EAAMgxB,EAAYn1B,OACxB,IAAK,IAAIoE,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,MAAMivB,EAAI8B,EAAY/wB,GAChBoxB,OAA6BlzB,IAAlB8yB,EACb,CAAEx1B,MAAOw1B,EAAgB/B,EAAEzzB,YAC3B0C,EACJkhB,EAAOiS,cAAcjQ,aAAa6N,EAAEn0B,KAAMm0B,EAAE30B,MAAO8kB,EAAOsD,gBAAiB0O,EAC7E,CACF,CCxSO,MAAME,GACXv2B,WAAAA,GACEzB,KAAK0O,QAAU,GACf1O,KAAKi4B,aAAc,EAEnBj4B,KAAKqR,cAAgB,CAAC,EACtBrR,KAAKk4B,UAAY,GACjBl4B,KAAKm4B,oBAAiBvzB,EACtB5E,KAAKo4B,mBAAqB,EAC1Bp4B,KAAKq4B,aAAe,EACtB,EAgFK,SAASC,GAAWxS,GACzBA,EAAOtM,OAAOiY,eAAe,GAI7B,MAAM8G,EAAWxL,GAAiBjH,EAAOtM,QAMnCgf,GAAiB1S,EAAOtgB,QAAQyQ,KAAKmC,WACrCqgB,EAASD,EACX1S,EAAOtM,OAAOmX,gBACd7K,EAAOtM,OAAO6X,oBAElB,IAAgB,IAAZoH,EAUF,MAAM,IAAIhO,GAAW,+CAAgDE,GAAUM,gBAGjF,MAAMyN,EAAM5S,EAAOtM,OAAOyY,QAAQwG,GAClC3S,EAAOtM,OAAOmU,qBAAqB8K,EAAS,GAY5C,MAAME,EAAeH,IAAqD,IAApC1S,EAAOtM,OAAOmb,kBAIpD,OAAOiE,GAAeF,EAAK5S,EAAQyS,GAAU,EAH1BI,EAAe7S,EAAOtM,OAAOuX,iBAAcnsB,EACxC+zB,EAAe7S,EAAOtM,OAAO4X,eAAiB,EAGtE,CA2EA,SAASwH,GAAeF,EAAK5S,EAAQyS,EAAUM,GAAmB,EAAOC,OAAal0B,EAAWm0B,EAAgB,GAC/G,MAAMC,EAAS,IAAIhB,GAIK,MAApBU,EAFWA,EAAIp2B,OAEF,KACf02B,EAAOf,aAAc,EACrBS,EAAMA,EAAI5xB,MAAM,GAAI,IAItB,IAEImyB,EAFAC,EAAW,GACXxyB,EAAI,EAGR,KAAOA,EAAIgyB,EAAIp2B,OAAQoE,IAErB,GAAIimB,GADM+L,EAAIhyB,IACE,CACdsyB,EAAOtqB,QAAUgqB,EAAIt2B,UAAU,EAAGsE,GAClCwyB,EAAWR,EAAIt2B,UAAUsE,EAAI,GAC7BuyB,EAAmBvyB,EAAI,OACN9B,IAAb2zB,IAAwBS,EAAOb,eAAiBI,EAAW7xB,EAAI,GACnE,KACF,CAOF,GAJ8B,IAA1BsyB,EAAOtqB,QAAQpM,QAAgBoE,IAAMgyB,EAAIp2B,SAAQ02B,EAAOtqB,QAAUgqB,GACtEM,EAAOtqB,QAAUsqB,EAAOtqB,QAAQyqB,UAChCH,EAAOd,UAAYgB,GAEdpT,EAAO3gB,aAAa6zB,EAAOtqB,SAC9B,MAAM,IAAI+b,GAAW,mBAAoBE,GAAUyO,kBAiBrD,OAJIP,GAAoBK,EAAS52B,OAAS,IDzDrC,SAA8BiG,EAASud,EAAQkT,EAAQF,EAAYO,EAAaN,GACrF,IAAKxwB,GAA8B,IAAnBA,EAAQjG,OAAc,OAEtC,MAAMiE,EA9FR,SAAyBgC,EAASuwB,EAAYO,EAAaN,EAAgB,EAAGjT,GAC5E,MAAMwT,EAAU,GACV7yB,EAAM8B,EAAQjG,OACpB,IAAIoE,EAAI,EACR,MAAM6yB,OAA0B30B,IAAfk0B,GAA4BC,EAAgB,EAC7D,IAAIS,EAAU,EAEd,KAAO9yB,EAAID,GAAK,CAEd,KAAOC,EAAID,GAAOmmB,GAAYrkB,EAAQP,WAAWtB,KAAKA,IACtD,GAAIA,GAAKD,EAAK,MAGd,MAAMI,EAAYH,EAClB,KAAOA,EAAID,GAAiC,KAA1B8B,EAAQP,WAAWtB,KAAckmB,GAAYrkB,EAAQP,WAAWtB,KAAKA,IACvF,MAAMlF,EAAO+G,EAAQnG,UAAUyE,EAAWH,GAG1C,KAAOA,EAAID,GAAOmmB,GAAYrkB,EAAQP,WAAWtB,KAAKA,IAEtD,GAAIA,GAAKD,GAAiC,KAA1B8B,EAAQP,WAAWtB,GAAW,CAE5C4yB,EAAQnyB,KAAK,CAAE3F,OAAMR,WAAO4D,EAAWnC,WAAYoE,IACnD,QACF,CAKA,IAHAH,IAGOA,EAAID,GAAOmmB,GAAYrkB,EAAQP,WAAWtB,KAAKA,IAKtD,MAAMM,EAAQuB,EAAQP,WAAWtB,GACjC,GAAc,KAAVM,GAA0B,KAAVA,EAClB,MAAM,IAAIyjB,GACR,cAAcjpB,4EACdmpB,GAAUgB,yBACV7F,EAASmH,GAAgBnH,EAAOtM,QAAU,CAAC,GAO/C,IAOIvT,EAPAwzB,GAAc,EAQlB,GAPIF,GAAYC,EAAU,EAAIT,GAAiBD,EAAWU,KAAa9yB,EAAI2yB,IACzEI,EAAaX,EAAWU,EAAU,GAAKH,EACvCG,GAAW,GAGb9yB,IAEI+yB,GAAc,EAChBxzB,EAAMwzB,OAGN,IADAxzB,EAAMS,EACCT,EAAMQ,GAAO8B,EAAQP,WAAW/B,KAASe,GAAOf,IAEzD,MAAMjF,EAAQu2B,GAAchvB,EAAS7B,EAAGT,EAAK6f,GAC7Cpf,EAAIT,EAAM,EACVqzB,EAAQnyB,KAAK,CAAE3F,OAAMR,QAAOyB,WAAYoE,GAC1C,CAEA,OAAOyyB,CACT,CA4BkBI,CAAgBnxB,EAASuwB,EAAYO,EAAaN,EAAejT,GAC3Erf,EAAMF,EAAQjE,OACpB02B,EAAOZ,mBAAqB3xB,EAC5B,MAAMgxB,EAAc,GACpB,IAAI5gB,EAAQ,EAOZ,MAAM8iB,EAAU7T,EAAOtgB,QAAQ4S,YAAY+V,WAAa,YAClDpH,EAAmB,cAAZ4S,EAA0B,IAAIviB,IAAQ,KAC7CwiB,EAAW9T,EAAOtgB,QAAQ4S,YAAY8V,aAAe,QAE3D,IAAK,IAAIxnB,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,MAAMa,EAAIhB,EAAQG,GAElB,GAAIqgB,EAAM,CACR,GAAIA,EAAKxP,IAAIhQ,EAAE/F,MAAO,CACpB,GAAgB,UAAZm4B,EACF,MAAM,IAAIlP,GACR,wBAAwBljB,EAAE/F,QAC1BmpB,GAAUe,oBACVuB,GAAgBnH,EAAOtM,SAG3B,QACF,CACAuN,EAAKzP,IAAI/P,EAAE/F,KACb,CAEA,MAAMq4B,EAAStyB,EAAEvG,MACjB,QAAe4D,IAAXi1B,EAAsB,CACxB,GAAiB,UAAbD,EACF,MAAM,IAAInP,GACR,wBAAwBljB,EAAE/F,uBAC1BmpB,GAAUiB,2BACVqB,GAAgBnH,EAAOtM,SAG3B,GAAiB,WAAbogB,EAAuB,QAC7B,CAEA,MAAMpyB,EAAWse,EAAOgU,gBAAgBvyB,EAAE/F,MAC1C,IAAiB,IAAbgG,EAAoB,SACxBqP,IACA,MAAMkjB,OAAqBn1B,IAAXi1B,GAAuBA,EACvCb,EAAO3nB,cAAc9J,EAAE/F,MAAQu4B,EAC/BtC,EAAYtwB,KAAK,CAAE3F,KAAMgG,EAAUxG,MAAO+4B,EAAS73B,MAAOqF,EAAE9E,YAC9D,CACAu2B,EAAOgB,iBAAmBnjB,EAC1BmiB,EAAOX,aAAeZ,CACxB,CCEIwC,CAAqBf,EAAUpT,EAAQkT,EAAQF,EAAYG,EAAkBF,GAGxEC,CACT,CCjMO,MAAMkB,GAWXz4B,WAAAA,CAAYiN,GAAS,OAAE+hB,GAAS,EAAK,eAAEC,EAAiB,IAAO,CAAC,GAC9D1wB,KAAKm6B,SAAWzrB,EAChB1O,KAAKo6B,QAAU3J,EACfzwB,KAAKq6B,YAAc3J,EAGnB1wB,KAAKs6B,SAAW,GAChBt6B,KAAKu6B,OAAS,EACdv6B,KAAKw6B,SAAU,CACjB,CAGAC,QAAAA,GACE,OAAOz6B,KAAKw6B,OACd,CAMAE,QAAAA,GACE16B,KAAKw6B,SAAU,EACfx6B,KAAKs6B,SAAW,GAChBt6B,KAAKu6B,OAAS,CAChB,CAWAI,kBAAAA,GACE36B,KAAKs6B,SAAW,GAChBt6B,KAAKu6B,OAAS,CAChB,CA4BAK,OAAAA,CAAQphB,GACNA,EAAOiY,eAAe,GAEtB,MAAMoJ,EAAgB76B,KAAKq6B,YAAY/3B,OAEvC,OAAKtC,KAAKo6B,SAA6B,IAAlBS,EAGjB76B,KAAKo6B,SAA6B,IAAlBS,EACX76B,KAAK86B,kBAAkBthB,IAE3BxZ,KAAKo6B,SAAWS,EAAgB,EAC5B76B,KAAK+6B,sBAAsBvhB,GAG7BxZ,KAAKg7B,aAAaxhB,GAThBxZ,KAAKi7B,cAAczhB,EAU9B,CAQAyhB,aAAAA,CAAczhB,GACZ,KAAOA,EAAOiU,WAGZ,GAAW,MAFAjU,EAAOwY,SAAS,GAE3B,CAKA,GAAIhyB,KAAKk7B,oBAAoB1hB,GAAS,OAAOxZ,KAAKm7B,QAAQ3hB,GAC1DxZ,KAAKs6B,UAAY9gB,EAAOuY,QAHxB,MAFE/xB,KAAKs6B,UAAY9gB,EAAOuY,SAQ5B,MAAM/xB,KAAKo7B,gBACb,CAQAN,iBAAAA,CAAkBthB,GAChB,KAAOxZ,KAAKu6B,OAAS,GAEnB,GADA3M,GAAcpU,EAAQ,EAAG,cAAcxZ,KAAKm6B,qBACxCn6B,KAAKq7B,mBAAmB7hB,GAAS,OAAOxZ,KAAKm7B,QAAQ3hB,GAI3D,MAAMxZ,KAAKo7B,gBACb,CAQAL,qBAAAA,CAAsBvhB,GACpB,KAAOA,EAAOiU,WAEZ,IAAIztB,KAAKs7B,kBAAkB9hB,GAI3B,GAAW,MAFAA,EAAOwY,SAAS,GAE3B,CAMA,GAAIhyB,KAAKk7B,oBAAoB1hB,GAAS,OAAOxZ,KAAKm7B,QAAQ3hB,GAC1DxZ,KAAKs6B,UAAY9gB,EAAOuY,QAJxB,MAFE/xB,KAAKs6B,UAAY9gB,EAAOuY,SAS5B,MAAM/xB,KAAKo7B,gBACb,CASAJ,YAAAA,CAAaxhB,GACX,KAAOxZ,KAAKu6B,OAAS,GAInB,GAHA3M,GAAcpU,EAAQ,EAAG,cAAcxZ,KAAKm6B,sBAGxCn6B,KAAKs7B,kBAAkB9hB,IAEvBxZ,KAAKq7B,mBAAmB7hB,GAAS,OAAOxZ,KAAKm7B,QAAQ3hB,GAI3D,MAAMxZ,KAAKo7B,gBACb,CAYAD,OAAAA,CAAQ3hB,GACN,MAAM3U,EAAS7E,KAAKs6B,SACdr0B,EAAM,CAAE/D,MAAO6qB,GAAiBvT,IAItC,OAHAxZ,KAAKw6B,SAAU,EACfx6B,KAAKs6B,SAAW,GAChBt6B,KAAKu6B,OAAS,EACP,CAAE3uB,QAAS/G,EAAQoB,MAC5B,CAUAq1B,iBAAAA,CAAkB9hB,GAChB,MAAM+hB,EAASv7B,KAAKw7B,oBAAoBhiB,GACxC,IAAgB,IAAZ+hB,EAAe,OAAO,EAC1B,MAAME,EAAMz7B,KAAKq6B,YAAYkB,GAC7Bv7B,KAAK07B,WAAWliB,EAAQiiB,EAAIE,KAAKr5B,QACjCtC,KAAKs6B,UAAYmB,EAAIE,KACrB,MAAMC,EAAW57B,KAAK67B,UAAUriB,EAAQiiB,EAAIK,OAE5C,OADA97B,KAAKs6B,UAAYsB,EAAWH,EAAIK,OACzB,CACT,CAUAZ,mBAAAA,CAAoB1hB,GAClB,MAAMuiB,EAAS,KAAO/7B,KAAKm6B,SAC3B,IAAKn6B,KAAKg8B,WAAWxiB,EAAQuiB,GAAS,OAAO,EAE7C,IAAIzG,EAASyG,EAAOz5B,OAChB25B,GAAa,EACjB,OAAa,CACX,MAAM/a,EAAI1H,EAAOwY,SAASsD,GAC1B,GAAU,MAANpU,EAAW,CAAE+a,GAAa,EAAM,KAAO,CAC3C,IAAItP,GAAQzL,GACZ,MADkBoU,GAEpB,CACA,IAAK2G,EAAY,OAAO,EAGxB,IADAj8B,KAAK07B,WAAWliB,EAAQuiB,EAAOz5B,QACxBkX,EAAOiU,WAEF,MADAjU,EAAOuY,WAGnB,OAAO,CACT,CAYAsJ,kBAAAA,CAAmB7hB,GAGjB,GAAW,MAFAA,EAAOwY,SAAS,GAIzB,OADAhyB,KAAKs6B,UAAY9gB,EAAOuY,UACjB,EAST,GALAvY,EAAOuY,SACPnE,GAAcpU,EAAQ,EAAG,cAAcxZ,KAAKm6B,2BAIjC,MAFA3gB,EAAOwY,SAAS,GAEX,CAEdxY,EAAOuY,SACP,MAAMmK,EAAYl8B,KAAKm8B,aAAa3iB,GAC9B4iB,EAAcp8B,KAAKq8B,kBAAkB7iB,GAE3C,OAAI0iB,IAAcl8B,KAAKm6B,WACrBn6B,KAAKu6B,SACe,IAAhBv6B,KAAKu6B,UAEXv6B,KAAKs6B,UAAY,KAAO4B,EAAYE,GAC7B,EACT,CAGA,MAAME,EAAWt8B,KAAKm8B,aAAa3iB,GACnCxZ,KAAKs6B,UAAY,IAAMgC,EAEvB,MAAM,YAAErE,EAAW,SAAEsE,GAAav8B,KAAKw8B,aAAahjB,GAMpD,OALAxZ,KAAKs6B,UAAYiC,EAEZtE,GAAeqE,IAAat8B,KAAKm6B,UACpCn6B,KAAKu6B,UAEA,CACT,CAMAa,cAAAA,GACE,OAAO,IAAI3Q,GACT,uBAAuBzqB,KAAKm6B,sCAC5BxP,GAAUM,eAEd,CAOAuQ,mBAAAA,CAAoBhiB,GAClB,MAAMqhB,EAAgB76B,KAAKq6B,YAAY/3B,OACvC,IAAK,IAAIoE,EAAI,EAAGA,EAAIm0B,EAAen0B,IACjC,GAAI1G,KAAKg8B,WAAWxiB,EAAQxZ,KAAKq6B,YAAY3zB,GAAGi1B,MAAO,OAAOj1B,EAEhE,OAAQ,CACV,CAMAm1B,SAAAA,CAAUriB,EAAQ4Y,GAChB,MAAMqK,EAAKrK,EAAQ,GACbsK,EAAOtK,EAAQ9vB,OACfuR,EAAQ2F,EAAO/W,WACrB,IAAIgE,EAAM,EAEV,KAAO+S,EAAOiU,WAAW,CACvB,GAAIjU,EAAOwY,SAAS,KAAOyK,GAAMz8B,KAAKg8B,WAAWxiB,EAAQ4Y,GAAU,CACjE,MAAMhK,EAAO5O,EAAOyY,QAAQxrB,EAAKoN,GAEjC,OADA7T,KAAK07B,WAAWliB,EAAQkjB,GACjBtU,CACT,CACA5O,EAAOuY,SACPtrB,GACF,CAEA,MAAM,IAAIgkB,GACR,uBAAuBzqB,KAAKm6B,2CAA2C/H,KACvEzH,GAAUM,eAEd,CAMA+Q,UAAAA,CAAWxiB,EAAQ1V,GACjB,MAAM64B,EAAS74B,EAAIxB,OACnB,IAAK,IAAIoE,EAAI,EAAGA,EAAIi2B,EAAQj2B,IAC1B,GAAI8S,EAAOwY,SAAStrB,KAAO5C,EAAI4C,GAAI,OAAO,EAE5C,OAAO,CACT,CAMAg1B,UAAAA,CAAWliB,EAAQvL,GACjB,IAAK,IAAIvH,EAAI,EAAGA,EAAIuH,EAAGvH,IAAK8S,EAAOuY,QACrC,CAMAoK,YAAAA,CAAa3iB,GACX,IAAIhY,EAAO,GACX,KAAOgY,EAAOiU,WAAW,CACvB,MAAMnoB,EAAKkU,EAAOwY,SAAS,GAC3B,GAAW,MAAP1sB,GAAqB,MAAPA,GAAcqnB,GAAQrnB,GAAK,MAC7C9D,GAAQgY,EAAOuY,QACjB,CACA,OAAOvwB,CACT,CAWAg7B,YAAAA,CAAahjB,GACX,MAAM3F,EAAQ2F,EAAO/W,WACrB,IAAIgE,EAAM,EACNyqB,GAAW,EACXC,GAAW,EAEf,KAAO3X,EAAOiU,WAAW,CACvB,MAAMnoB,EAAKkU,EAAOuY,SAGlB,GAFAtrB,IAEW,MAAPnB,GAAe6rB,EAEZ,GAAW,MAAP7rB,GAAe4rB,GAEnB,IAAKA,IAAaC,EAAU,CACjC,GAAW,MAAP7rB,EACF,MAAO,CAAE2yB,aAAa,EAAOsE,SAAU/iB,EAAOyY,QAAQxrB,EAAKoN,IAE7D,GAAW,MAAPvO,GAAckU,EAAOiU,WAAoC,MAAvBjU,EAAOwY,SAAS,GAGpD,OAFAxY,EAAOuY,SACPtrB,IACO,CAAEwxB,aAAa,EAAMsE,SAAU/iB,EAAOyY,QAAQxrB,EAAKoN,GAE9D,OAVEsd,GAAYA,OAFZD,GAAYA,CAahB,CAEA,MAAM,IAAIzG,GACR,uBAAuBzqB,KAAKm6B,wCAC5BxP,GAAUM,eAEd,CAOAoR,iBAAAA,CAAkB7iB,GAChB,MAAM3F,EAAQ2F,EAAO/W,WACrB,IAAIgE,EAAM,EACV,KAAO+S,EAAOiU,WAAW,CACvB,MAAMnoB,EAAKkU,EAAOuY,SAElB,GADAtrB,IACW,MAAPnB,EAAY,OAAOkU,EAAOyY,QAAQxrB,EAAKoN,GAC3C,IAAK8Y,GAAQrnB,GACX,MAAM,IAAImlB,GACR,+BAA+BzqB,KAAKm6B,YACpCxP,GAAUM,eAGhB,CACA,MAAM,IAAIR,GACR,uBAAuBzqB,KAAKm6B,6CAC5BxP,GAAUM,eAEd,ECxfK,SAAS2R,GAAU9W,GACxB,MAAM+W,EAAc/W,EAAOtgB,QAAQyQ,KACnC6P,EAAOtM,OAAOiY,eAAe,GAE7B,IAAIuH,EFoIC,SAAmBlT,GACxBA,EAAOtM,OAAOiY,eAAe,GAC7B,MAAM8G,EAAWxL,GAAiBjH,EAAOtM,QACzC,IAEI9S,EAFAo2B,GAAiB,EACjBC,GAAiB,EAEjBC,GAAM,EAEV,IAAKt2B,EAAI,EAAGof,EAAOtM,OAAOiU,QAAQ/mB,GAAIA,IAAK,CACzC,MAAMu2B,EAAcnX,EAAOtM,OAAOwY,SAAStrB,GACrCw2B,EAAWpX,EAAOtM,OAAOwY,SAAStrB,EAAI,GAQ5C,GANoB,MAAhBu2B,GAAwBF,EAED,MAAhBE,GAAwBH,IACjCC,GAAkBA,GAFlBD,GAAkBA,GAKfA,IAAmBC,GACF,MAAhBE,GAAoC,MAAbC,EAAkB,CAC3CF,GAAM,EACN,KACF,CAEJ,CAEA,IAAKA,EAEH,MAAM,IAAIvS,GAAW,gDAAiDE,GAAUM,gBAC3E,GAAI6R,GAAkBC,EAE3B,MAAM,IAAItS,GAAW,kFAAmFE,GAAUW,gBAOpH,MAAMoN,EAAM5S,EAAOtM,OAAOyY,QAAQvrB,GAElC,OADAof,EAAOtM,OAAOmU,qBAAqBjnB,EAAI,GAChCkyB,GAAeF,EAAK5S,EAAQyS,GAAU,EAC/C,CE7Ke4E,CAAUrX,GACvB,IAAKkT,EACH,MAAM,IAAIvO,GACR,6BACAE,GAAUU,YACV4B,GAAgBnH,EAAOtM,SAEpB,GAAuB,QAAnBwf,EAAOtqB,QAAmB,CAEnC,MAAM1I,EAAUgzB,EAAO3nB,eAAerL,QACtB,QAAZA,IACF8f,EAAOsX,OAAOp3B,QAAU,KAE1B8f,EAAOsX,OAAO5N,SAAWwJ,EAAO3nB,eAAeme,SAC/C1J,EAAOsX,OAAOC,WAAarE,EAAO3nB,eAAegsB,WAYjDvX,EAAOwX,gBAAkB,CAAC,CAC5B,CAMKT,EAAYzkB,YACfof,GAAgBwB,EAAOX,aAAcvS,EAAQkT,EAAOb,eAAgBa,EAAOZ,oBAGtD,QAAnBY,EAAOtqB,QAEJmuB,EAAY/O,aACfhI,EAAOiS,cAActP,eAAe,OAAQ3C,EAAOsX,QAE3CP,EAAY9O,IACtBjI,EAAOiS,cAAcrP,eAAe,IAAMsQ,EAAOtqB,QAErD,CCmGA,SAAS6uB,GAAczX,GACnB,MAAMtM,EAASsM,EAAOtM,OAEtBgkB,GAAqBhkB,GAErBoU,GAAcpU,EAAQ,EAAG,eAEzB,MAAMikB,EAAkBjkB,EAAO/W,WAC/B,IAAIi7B,EAAgB,EACpB,KAAOlkB,EAAOiU,WAAW,CACrB,MAAMnoB,EAAKkU,EAAOuY,SAClB,GAAIpF,GAAQrnB,IAAc,MAAPA,GAAqB,MAAPA,EAAY,MAC7Co4B,GACJ,CACA,MAAM7qB,EAAa2G,EAAOyY,QAAQyL,EAAeD,GAWjD,GARA7P,GAAcpU,EAAQ,EAAG,gBAAgB3G,MAEzCyH,GAAmBzH,EAAYiT,GAC/B0X,GAAqBhkB,GAErBoU,GAAcpU,EAAQ,EAAG,sBAAsB3G,MAG3C2G,EAAOiU,QAAQ,KAC2B,IAAtCjU,EAAOkU,WAAW,UAAU,GAC5B,MAAM,IAAIjD,GAAW,sCACjBE,GAAUU,YACV4B,GAAgBzT,IAI5B,GAA0B,MAAtBA,EAAOyY,QAAQ,GACf,MAAM,IAAIxH,GAAW,uCACjBE,GAAUU,YACV4B,GAAgBzT,IAIxBoU,GAAcpU,EAAQ,EAAG,qBAAqB3G,MAE9C,MAAOD,GAAe+qB,GAAkBnkB,EAAQ,UAE1CokB,EAAK9X,EAAOtgB,SAASkpB,eAC3B,GAAIkP,GAAI9rB,eAAiBc,EAAYtQ,OAASs7B,EAAG9rB,cAC7C,MAAM,IAAI2Y,GACN,WAAW5X,YAAqBD,EAAYtQ,yCAAyCs7B,EAAG9rB,iBACxF6Y,GAAUwB,gBACVc,GAAgBzT,IAOxB,OAFAA,EAAO+Y,aAAa,KAEb,CAAC1f,EAAYD,EACxB,CAMA,SAASirB,GAAe/X,GACpB,MAAMtM,EAASsM,EAAOtM,OAEtBgkB,GAAqBhkB,GAErBoU,GAAcpU,EAAQ,EAAG,gBAEzB,MAAMskB,EAAmBtkB,EAAO/W,WAChC,IAAIs7B,EAAiB,EACrB,KAAOvkB,EAAOiU,YAENd,GADOnT,EAAOuY,WAElBgM,IAEJ,MAAM1qB,EAAcmG,EAAOyY,QAAQ8L,EAAgBD,GAInD,GAFAlQ,GAAcpU,EAAQ,EAAG,iBAEpBsM,EAAOkY,iBAAiB,OAAxBlY,CAAgCzS,GACjC,MAAM,IAAIoX,GAAW,0BAA0BpX,KAC3CsX,GAAUU,YACV4B,GAAgBzT,IAGxBgkB,GAAqBhkB,GAErBoU,GAAcpU,EAAQ,EAAG,gBAEzB,IAAIykB,EAAQzkB,EAAOyY,QAAQ,GAC3B,GAAc,MAAVgM,EAEA,IACI5Q,GAAY7T,EAAQ,QAAS,sCACjC,CAAE,MAAOhJ,GAGL,OADAgJ,EAAO+Y,aAAa,KACb,CAAElf,cAAa6qB,aAAc,GACxC,MACG,GAAc,MAAVD,EACP,IACI5Q,GAAY7T,EAAQ,MAAO,oCAC/B,CAAE,MAAOhJ,GAEL,OADAgJ,EAAO+Y,aAAa,KACb,CAAElf,cAAa6qB,aAAc,GACxC,KACiB,MAAVD,IACPzkB,EAAOmU,qBAAqB,GAC5BnU,EAAO+Y,aAAa,MAIxB,OADA/Y,EAAO+Y,aAAa,KACb,CAAElf,cACb,CAMA,SAAS8qB,GAAerY,GACpBA,EAAOtM,OAAO+Y,aAAa,IAC/B,CAMA,SAAS6L,GAAgBtY,GACrB,MAAMtM,EAASsM,EAAOtM,OAEtBgkB,GAAqBhkB,GAErBoU,GAAcpU,EAAQ,EAAG,iBAEzB,MAAM6kB,EAAoB7kB,EAAO/W,WACjC,IAAI67B,EAAkB,EACtB,KAAO9kB,EAAOiU,YAENd,GADOnT,EAAOuY,WAElBuM,IAEJ,MAAMnrB,EAAeqG,EAAOyY,QAAQqM,EAAiBD,GAUrD,GARAzQ,GAAcpU,EAAQ,EAAG,wBAAwBrG,MAEjDmH,GAAmBnH,EAAc2S,GACjC0X,GAAqBhkB,GAGrBoU,GAAcpU,EAAQ,EAAG,6BAEiB,IAAtCA,EAAOkU,WAAW,UAAU,GAC5BlU,EAAOmU,qBAAqB,GAC5B6P,GAAqBhkB,GACrBmkB,GAAkBnkB,EAAQ,wBACvB,KAA0C,IAAtCA,EAAOkU,WAAW,UAAU,GAWnC,MAAM,IAAIjD,GACN,iDAAiDjR,EAAOyY,QAAQ,MAChEtH,GAAUU,YACV4B,GAAgBzT,IAd+B,CACnDA,EAAOmU,qBAAqB,GAC5B6P,GAAqBhkB,GACrBmkB,GAAkBnkB,EAAQ,oBAC1BgkB,GAAqBhkB,GACrBoU,GAAcpU,EAAQ,EAAG,oCACzB,IAAI+kB,EAAO/kB,EAAOyY,QAAQ,GACb,MAATsM,GAAyB,MAATA,GAChBZ,GAAkBnkB,EAAQ,mBAElC,CAMA,CAEAA,EAAO+Y,aAAa,IACxB,CAOA,SAASoL,GAAkBnkB,EAAQvO,GAC/B2iB,GAAcpU,EAAQ,EAAGvO,EAAO,kBAChC,IAAInI,EAAY0W,EAAOyY,QAAQ,GAC/B,GAAkB,MAAdnvB,GAAmC,MAAdA,EACrB,MAAM,IAAI2nB,GACN,8BAA8Bxf,aAAgBnI,KAC9C6nB,GAAUU,YACV4B,GAAgBzT,IAMxB,OAHAA,EAAOmU,qBAAqB,GAGrB,CADKnU,EAAO+Y,aAAazvB,GAEpC,CAMA,SAAS06B,GAAqBhkB,GAC1B,KAAOA,EAAOiU,WAELd,GADMnT,EAAOwY,SAAS,KAE3BxY,EAAOmU,qBAAqB,EAEpC,CAEA,SAASrT,GAAmB9Y,EAAMskB,GAC9B,GAAIA,EAAOkY,iBAAiB,OAAxBlY,CAAgCtkB,GAAO,OAAOA,EAClD,MAAM,IAAIipB,GACN,wBAAwBjpB,KACxBmpB,GAAU2B,mBACV,CAAC,EAET,CC7WO,MAAMkS,GAAqBn+B,OAAO4Z,OAAO,CAE9CwkB,aAAc,eAMdC,iBAAkB,mBAMlBC,cAAe,gBAMfC,YAAa,gBAGA,MAAMC,GAOnBp9B,WAAAA,CAAYq9B,GACV9+B,KAAKqwB,MAAQyO,EAAiBzO,OAAS,QACvCrwB,KAAKswB,WAAawO,EAAiBxO,YAAc,QACjDtwB,KAAKuwB,cAAgBuO,EAAiBvO,gBAAiB,EACvDvwB,KAAK++B,OAAS,EAChB,CAaAC,SAAAA,CAAUC,GACR,GAAmB,UAAfj/B,KAAKqwB,MACP,MAAM,IAAI5F,GAAW,yCAA0CE,GAAUS,0BAO3E,MAAM,YAAE8T,EAAW,OAAEC,GAAWF,EAEhC,IAAI3xB,EAAU2xB,EAAYpH,iBAE1B,KAAOvqB,IAAYA,EAAQ5N,MACzBM,KAAKo/B,aAAaZ,GAAmBC,aAAc,CACjD3yB,IAAKwB,EAAQ9L,KACb8rB,SAAU,KACVprB,MAAOoL,EAAQpL,QAGjBg9B,IACAC,IAGA7xB,EAAU2xB,EAAYpH,gBAE1B,CAcAwH,cAAAA,CAAeC,EAAgBL,GAC7B,MAAM,UAAE3V,EAAS,iBAAEuO,EAAgB,OAAEre,EAAM,YAAE0lB,GAAgBD,EAE7D,GAAwB,UAApBj/B,KAAKswB,WACP,MAAM,IAAI7F,GACR,2BAA2B6U,iBAA8BzH,EAAiBr2B,QAC1EmpB,GAAUQ,qBACV,CAAEjpB,MAAOsX,EAASuT,GAAiBvT,QAAU5U,IAIjD,GAAwB,YAApB5E,KAAKswB,WAMP,OALAtwB,KAAKo/B,aAAaZ,GAAmBE,iBAAkB,CACrD5yB,IAAKwzB,EACLhS,SAAUuK,EAAiBr2B,KAC3BU,MAAOsX,EAASuT,GAAiBvT,GAAU,OAEtC,CAAEuE,OAAQ,WAUnB,MAAMwhB,EAAgB,IAAIjW,EAAWuO,GAErC,IAAI2H,GAAc,EAClB,MAAMC,EAAsBF,EAAcj9B,OAC1C,IAAK,IAAIoE,EAAI+4B,EAAsB,EAAG/4B,GAAK,EAAGA,IAC5C,GAAI64B,EAAc74B,GAAGlF,OAAS89B,EAAgB,CAC5CE,EAAa94B,EACb,KACF,CAGF,IAAoB,IAAhB84B,EAOF,OALAx/B,KAAKo/B,aAAaZ,GAAmBG,cAAe,CAClD7yB,IAAKwzB,EACLhS,SAAUuK,EAAiBr2B,KAC3BU,MAAOsX,EAASuT,GAAiBvT,GAAU,OAEtC,CAAEuE,OAAQ,WAKnB,MAAM2hB,EAAgBD,EAAsB,EAAID,EAEhD,IAAK,IAAI94B,EAAI,EAAGA,EAAIg5B,EAAeh5B,IAAK,CACtC,MAAMoF,EAAMyzB,EAAcE,EAAsB,EAAI/4B,GAEpD1G,KAAKo/B,aAAaZ,GAAmBE,iBAAkB,CACrD5yB,IAAKA,EAAItK,KACT8rB,SAAUgS,EACVp9B,MAAO4J,EAAI5J,QAGbg9B,IACAD,EAAYE,QACd,CAOA,OAFAF,EAAYpH,iBAAmB0H,EAAcC,GAEtC,CAAEzhB,OAAQ,gBACnB,CASA4hB,gBAAAA,CAAiBC,EAAeX,GAC9Bj/B,KAAKo/B,aAAaZ,GAAmBI,YAAa,CAChD9yB,IAAK+zB,GAAuBD,GAC5BtS,SAAU,KACVprB,MAAO+8B,EAAYzlB,OAASuT,GAAiBkS,EAAYzlB,QAAU,OAIrEylB,EAAYa,YAAc,GAG1B9/B,KAAKg/B,UAAUC,EACjB,CAOAc,SAAAA,GACE,OAAO//B,KAAK++B,OAAOj4B,OACrB,CAKA9B,KAAAA,GACEhF,KAAK++B,OAAS,EAChB,CAIAK,YAAAA,CAAan0B,EAAM+0B,GACZhgC,KAAKuwB,eACVvwB,KAAK++B,OAAO53B,KAAK,CAAE8D,UAAS+0B,GAC9B,EAgBF,SAASH,GAAuBj2B,GAC9B,IAAKA,EAAK,OAAO,KACjB,MAGMq2B,GAHiC,iBAAhBr2B,EAAIzI,QAAuByI,EAAIzI,QAAU8O,OAAOrG,IAG5CpH,MAAM,sCACjC,OAAIy9B,GAAmBA,EAAW,IAC3B,IACT,CCjOA,MACMC,GAAY,CAAE/wB,KAAM,CAAC,cAe3B,SAASgxB,GAAc37B,EAAO47B,EAASC,GACrC,MAAM17B,EAASH,EAAMhE,IAAI4/B,GACzB,QAAex7B,IAAXD,GAAwBH,EAAM+S,IAAI6oB,GAAU,OAAOz7B,EACvD,MAAME,EAASw7B,IAGf,OAFI77B,EAAMM,MApBa,KAoBaN,EAAM6hB,QAC1C7hB,EAAMO,IAAIq7B,EAASv7B,GACZA,CACT,CAEA,MAAMy7B,GAaJ7+B,WAAAA,CAAYD,EAAMU,EAAQ,EAAGq+B,OAAU37B,EAAWw7B,OAAUx7B,GAC1D5E,KAAKwB,KAAOA,EACZxB,KAAKkC,MAAQA,EACblC,KAAKugC,QAAUA,EACfvgC,KAAKogC,QAAUA,CACjB,EAGa,MAAMI,GACnB/+B,WAAAA,CAAY+D,GACVxF,KAAKwF,QAAUA,EAEfxF,KAAK63B,iBAAmB,KACxB73B,KAAK8/B,YAAc,GACnB9/B,KAAKspB,UAAY,GACjBtpB,KAAKygC,cAAe,EACpBzgC,KAAK8M,QAAU,IAAIuB,EAGnBrO,KAAKopB,gBAAkBppB,KAAK8M,QAAQwD,WAGpCtQ,KAAK0gC,iBAAmBl7B,EAAQopB,UAC5B,IAAIiQ,GAAiBr5B,EAAQopB,WAC7B,KAEJ5uB,KAAK2gC,aAAe,IAAIvpB,IAAIpX,KAAKwF,QAAQgP,KAAK4Z,UAK9CpuB,KAAK4gC,uBAAyB5gC,KAAKwF,QAAQgP,KAAKub,cAAgB,IAAIjZ,EAC3B,IAArC9W,KAAK4gC,uBAAuB97B,KAAY9E,KAAK6gC,WAAa,KAAM,EAC/D7gC,KAAK6gC,WAAaA,GACvB7gC,KAAK8gC,sBAAwB9gC,KAAKwF,QAAQyQ,KAAKga,SAAW,IAAInZ,EACtB,IAApC9W,KAAK8gC,sBAAsBh8B,KAAY9E,KAAK+gC,UAAY,KAAM,EAC7D/gC,KAAK+gC,UAAYA,GAItB/gC,KAAKghC,QAAoC,mBAAnBx7B,EAAQ6pB,OAAwB7pB,EAAQ6pB,OAAS,KAAM,EAW7ErvB,KAAKihC,WAAaz7B,EAAQy7B,aAAez7B,EAAQy7B,WAAa,CAAEzsB,KAAM,IAAI/P,IAAOy8B,MAAO,IAAIz8B,KAC9F,CAEA08B,gBAAAA,GACEnhC,KAAK8/B,YAAc,GACnB9/B,KAAKspB,UAAY,GACjBtpB,KAAKygC,cAAe,EACpBzgC,KAAKohC,mBAAqB,KAC1BphC,KAAKqhC,kBAAmB,EASxBrhC,KAAKs9B,gBAAkB,CAAC,EAKxBt9B,KAAKshC,aAAe,IAAIlqB,IACxBpX,KAAKo9B,OAAS,CACZp3B,QAAS,EACTu7B,KAAM,KACN/R,SAAU,KACV6N,WAAY,OAGTr9B,KAAK8M,UACR9M,KAAK8M,QAAU,IAAIuB,EACnBrO,KAAKopB,gBAAkBppB,KAAK8M,QAAQwD,YAGtCtQ,KAAK+3B,cAAgB/3B,KAAKwhC,uBAE1BxhC,KAAKN,KAAO,CAAEA,MAAM,EAAM8B,KAAM,IAChCxB,KAAK63B,iBAAmB73B,KAAKN,IAC/B,CAOA8hC,oBAAAA,GACE,OAAOxhC,KAAKwF,QAAQ8pB,cAAcpG,YAAYlpB,KAAKwF,QAASxF,KAAKopB,gBACnE,CAMAqY,SAAAA,GACE,OAAiC,IAA1BzhC,KAAKqhC,gBACd,CAEA7f,KAAAA,CAAMkgB,GAIJ,OAHA1hC,KAAKwZ,OAAS,IAAI8X,GAAaoQ,GAC/B1hC,KAAKmhC,mBACLnhC,KAAK2hC,oBACE3hC,KAAK+3B,cAAcvN,WAC5B,CAEAoX,aAAAA,CAAcl3B,GACZ,MAAM8b,EAAWxmB,KAAKwF,QAAQ+pB,UAAUsS,UAClC7N,ECpKH,SAA+Bc,EAAOgN,EAAkB,CAAC,EAAGtb,EAAW6Q,IAC5E,MAAM0K,EAAYD,EAAgBtS,UAAY,OAC9C,IAAIhuB,EAAMyyB,EACV,GAAkB,SAAd8N,EAAsB,CACxB,MAAMC,EAAWnN,GAAMC,EAAOtO,GAC9BhlB,EAAOwgC,EAASxS,SAChByE,EAAY+N,EAAS/N,SACvB,MACEzyB,EAAOugC,EACP9N,EAAY,EAEd,MAAMI,EAAa7N,EAASoP,QAAQp0B,GAC9BizB,EAAeJ,EAAWmC,kBAC5BzD,GACsB,SAApBsB,EAAW7yB,KAAkBmyB,GAAmBD,GAChDW,EAAW7yB,MCTV,CACLuwB,MAAAA,GACE,OAAO/xB,KAAK6wB,OAAO7wB,KAAKyC,aAC1B,EAEAuvB,QAAAA,CAAS9vB,GACP,OAAOlC,KAAK6wB,OAAO7wB,KAAKyC,WAAaP,EACvC,EAEA+vB,OAAAA,CAAQhkB,EAAGikB,GAET,YADoB,IAATA,IAAsBA,EAAOlyB,KAAKyC,YACtCzC,KAAK6wB,OAAOzuB,UAAU8vB,EAAMA,EAAOjkB,EAC5C,EAIAyf,UAAAA,CAAWJ,EAAUE,GAAkB,GACrC,MAAM/mB,EAAM6mB,EAAShrB,OACrB,IAAK,IAAIoE,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,IAAIpB,EAAKtF,KAAK6wB,OAAO7wB,KAAKyC,WAAaiE,GACvC,QAAW9B,IAAPU,EAAkB,OAAO,KAE7B,GADIkoB,IAAiBloB,EAAKA,EAAGoR,eACzBpR,IAAOgoB,EAAS5mB,GAAI,OAAO,CACjC,CACA,OAAO,CACT,EAMAiqB,cAAa,GACbU,kBAAiB,GAEjBc,QAAAA,CAASC,GACP,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAC3B,IAAK,IAAIoE,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,IAAIlE,GAAQ,EACZ,IAAK,IAAIqG,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAErG,GAAQ,EAAO,KAAO,CAEjE,GAAIA,EAAO,CACT,MAAMqC,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI4rB,EACfztB,CACT,CACF,CACA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,EAEAsH,YAAAA,CAAaC,GACX,MAAM9rB,EAAI1G,KAAK6wB,OAAO1qB,QAAQqsB,EAAUxyB,KAAKyC,YAC7C,IAAW,IAAPiE,EACF,MAAM,IAAI+jB,GAAW,qCAAqC+H,KAAa7H,GAAUM,gBAEnF,MAAMpmB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,EAEA4tB,gBAAAA,CAAiBL,GACf,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAC3B,IAAIowB,GAAiB,EACjBC,EAAQ,EACZ,IAAK,IAAIjsB,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,GAAc,IAAVisB,EAAa,CACf,MAAMzR,EAAIlhB,KAAK6wB,OAAOnqB,GACtB,GAAIimB,GAAQzL,GAAI,SACN,MAANA,EAAayR,EAAQ,GAClBA,EAAQ,EAAGD,GAAiB,EACrC,KAAO,CACL,IAAIE,GAAU,EACd,IAAK,IAAI/pB,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAE+pB,GAAU,EAAO,KAAO,CAE/DA,IAAWD,EAAQ,EAAGD,EAAgBhsB,EAAGA,GAAK4rB,EAAa,EACjE,CACA,GAAc,IAAVK,EAAa,CACf,MAAM9tB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiwB,GAEtD,OADA1yB,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CACF,CACA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,EAEA4H,cAAAA,CAAe5kB,EAAGulB,GAChB,MAAMluB,EAAW,IAAN2I,EACPjO,KAAK6wB,OAAO7wB,KAAKyC,YACjBzC,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYzC,KAAKyC,WAAawL,GAE7D,OADIulB,GAAcxzB,KAAK2tB,qBAAqB1f,GACrC3I,CACT,EAEAqoB,oBAAAA,CAAqB1f,EAAI,GACvBjO,KAAKyC,YAAcwL,EACfjO,KAAKkvB,WAAalvB,KAAKyC,YAAczC,KAAKmvB,gBAAkBnvB,KAAKyzB,YAAc,GACjFzzB,KAAK4xB,OAET,EAGAnE,OAAAA,CAAQxf,EAAI,GACV,OAAOjO,KAAKyC,WAAawL,EAAIjO,KAAK6wB,OAAOvuB,MAC3C,GDhFIsyB,GAAoBP,EAAWmC,mBAAyC,SAApBnC,EAAW7yB,KAErE,MAAO,CAAE6yB,aAAYJ,YAAWQ,eAAcN,aAAcE,EAAWmC,kBAAmB5B,mBAC5F,CDiIoBqN,CAAsBv3B,EAAM1K,KAAKwF,QAAQ+pB,SAAU/I,GAInE,OAHAxmB,KAAKwZ,OAAS,IAAIsa,GAAappB,EAAM,CAAC,EAAGspB,GACzCh0B,KAAKmhC,mBACLnhC,KAAK2hC,oBACE3hC,KAAK+3B,cAAcvN,WAC5B,CAUA0X,QAAAA,GACE,KAAOliC,KAAKwZ,OAAOiU,YAEbztB,KAAKqhC,kBAFmB,CAS5BrhC,KAAKwZ,OAAOiY,eAAe,GAQ3B,MAAM0Q,EAAalV,GAAgBjtB,KAAKwZ,QAElClU,EAAKtF,KAAKwZ,OAAOuY,SACvB,QAAWntB,IAAPU,GAA2B,KAAPA,EAAW,MAEnC,GAAW,MAAPA,EAAY,CACd,MAAM88B,EAAWD,EAEXjF,EAAWl9B,KAAKwZ,OAAOwY,SAAS,GACtC,GAAiB,KAAbkL,EAAiB,MAAM,IAAIzS,GAC7B,qCAAsCE,GAAUM,eAAgBgC,GAAgBjtB,KAAKwZ,SAItE,MAAb0jB,GACFl9B,KAAKwZ,OAAOmU,uBACZ3tB,KAAKqiC,eAAeD,IACE,MAAblF,GACTl9B,KAAKwZ,OAAOmU,uBACZ3tB,KAAKk/B,cACLl/B,KAAKsiC,eAAepF,IACE,MAAbA,GACTl9B,KAAKwZ,OAAOmU,uBACZ3tB,KAAKk/B,cACLtC,GAAU58B,OAEVA,KAAKuiC,eAAeH,EAExB,KAAO,CAIL,IAAII,EAAS,EACb,OAAa,CACX,MAAMthB,EAAIlhB,KAAKwZ,OAAOwY,SAASwQ,GAC/B,GAAU,MAANthB,QAAmBtc,IAANsc,GAAyB,KAANA,EAAU,MAC9CshB,GACF,CACIA,EAAS,GACXxiC,KAAK8/B,aAAex6B,EAAKtF,KAAKwZ,OAAOyY,QAAQuQ,EAAQxiC,KAAKwZ,OAAO/W,YACjEzC,KAAKwZ,OAAOmU,qBAAqB6U,IAEjCxiC,KAAK8/B,aAAex6B,CAMxB,CACF,CACF,CAMAm9B,WAAAA,GAGE,GAAIziC,KAAKqhC,iBAAkB,OAE3B,MAAMqB,EAAc1iC,KAAKspB,UAAUhnB,OAAS,GACzCtC,KAAK63B,mBAAqB73B,KAAK63B,iBAAiBn4B,KAE7CijC,GACHD,QACoB99B,IAArB5E,KAAK8/B,aACL9/B,KAAK8/B,YAAY3G,UAAU72B,OAAS,EAEtC,GAAIogC,GAAeC,EAAiB,CAClC,IAAI3iC,KAAK0gC,mBAAoBgC,GAAgBC,EAG3C,MAAM,IAAIlY,GAAW,yCAA0CE,GAAUS,yBAA0B6B,GAAgBjtB,KAAKwZ,SAFxHxZ,KAAK0gC,iBAAiB1B,UAAUh/B,KAAK4iC,eAIzC,CACF,CAOAjB,iBAAAA,GACE,IAAIkB,EAAkB,KAClB7iC,KAAK0gC,kBAAkB1gC,KAAK0gC,iBAAiB17B,QAEjD,IACEhF,KAAKkiC,UACP,CAAE,MAAOt4B,GACP,IAAI5J,KAAK0gC,mBAwef,SAAgC92B,GAE9B,OAAIA,aAAe6gB,GACV7gB,EAAIxI,OAASupB,GAAUM,eAG9BrhB,EAAIzI,QAAQ4E,WAAW,6BACvB6D,EAAIzI,QAAQ4E,WAAW,+BAE3B,CAjfmC+8B,CAAuBl5B,GAGlD,MAAMA,EAFNi5B,EAAkBj5B,CAItB,CAEIi5B,EACF7iC,KAAK0gC,iBAAiBf,iBAAiBkD,EAAiB7iC,KAAK4iC,gBAI/D5iC,KAAKyiC,aACP,CAEAJ,cAAAA,CAAeD,GAeb,MAAM90B,EAAUtN,KAAK63B,iBACrB,GAAIvqB,IAAYA,EAAQ5N,WAA4BkF,IAApB0I,EAAQ8yB,QAAuB,CAC7D,MAAM2C,ELtSL,SAAgCvpB,EAAQwpB,GAG7C,IAA2C,IAAvCxpB,EAAOkU,WAAWsV,GAA2B,OAAQ,EACzD,IAAIt8B,EAAIs8B,EAAgB1gC,OACpB4e,EAAI1H,EAAOwY,SAAStrB,GACxB,GAAU,MAANwa,EAAW,OAAOxa,EAAI,EAC1B,KAAOimB,GAAQzL,IAGb,GAFAxa,IACAwa,EAAI1H,EAAOwY,SAAStrB,GACV,MAANwa,EAAW,OAAOxa,EAAI,EAE5B,MAAU,MAANwa,GAAmB,EAChBxa,EAAI,CACb,CKwRuBu8B,CAAuBjjC,KAAKwZ,OAAQlM,EAAQ8yB,SAC7D,IAAkB,IAAd2C,EAAiB,CACnB/iC,KAAKwZ,OAAOmU,qBAAqBoV,GACjC,MAAMG,EAAY,CAChB1hC,KAAM8L,EAAQ9L,KACdU,MAAOkgC,EAASlgC,MAChBihC,SAAUpW,GAAiB/sB,KAAKwZ,SAIlC,OAFAxZ,KAAKk/B,mBACLl/B,KAAKm/B,OAAO+D,EAEd,CACF,CAGA,MAAMx0B,EAAU1O,KAAKojC,eL3RlB,SAA4B5pB,GACjCA,EAAOiY,eAAe,GAMtB,MAAM5d,EAAQ2F,EAAO/W,WACrB,IAEE,OADY+W,EAAO+Y,aAAa,KACrB4G,SACb,CAAE,MAAOvvB,GAMP,MAAMy5B,EAAU7pB,EAAOyY,QAAQtS,OAAO2jB,iBAAkBzvB,GACxD,MAAM,IAAI4W,GAAW,mDAAmD4Y,KAAY1Y,GAAUM,eAChG,CACF,CKuQwCsY,CAAmBvjC,KAAKwZ,SAItD0pB,EAAY,CAChB1hC,KAAMkN,EACNxM,MAAOkgC,EAASlgC,MAChBihC,SAAUpW,GAAiB/sB,KAAKwZ,SAGlC,GAAIxZ,KAAKwjC,WAAW90B,IAAY1O,KAAK6gC,aACnC,MAAM,IAAIpW,GAAW,2BAA2B/b,KAAYic,GAAUO,qBAAsB+B,GAAgBjtB,KAAKwZ,SAGnH,GAAI9K,IAAY1O,KAAK63B,iBAAiBr2B,KAAM,CAC1C,IAAKxB,KAAK0gC,iBACR,MAAM,IAAIjW,GACR,2BAA2B/b,iBAAuB1O,KAAK63B,iBAAiBr2B,QACxEmpB,GAAUQ,qBACV8B,GAAgBjtB,KAAKwZ,SAMzB,GAAwB,YAFPxZ,KAAK0gC,iBAAiBrB,eAAe3wB,EAAS1O,KAAK4iC,gBAEvD7kB,OAAsB,MAErC,CAEK/d,KAAK63B,iBAAiBn4B,MAAMM,KAAKk/B,cACtCl/B,KAAKm/B,OAAO+D,EACd,CAEAX,cAAAA,CAAeH,GACb,MAAM58B,EAAUxF,KAAKwF,QASrB,GARAxF,KAAKk/B,cAQDl/B,KAAKohC,oBAAsBphC,KAAKohC,mBAAmB3G,WAAY,CACjE,MAAM,UAAE7R,EAAS,OAAE6a,GAAWzjC,KAAK0jC,uBACnC1jC,KAAKohC,mBAAmBzG,qBACxBrC,GAAWt4B,MAGX4oB,EAAU2X,QAAUxT,GAAiB/sB,KAAKwZ,QAC1C,MAAM,QAAE5N,EAAS3F,IAAK09B,GAAY3jC,KAAKohC,mBAAmBxG,QAAQ56B,KAAKwZ,QAUvE,OATKiqB,IACHzjC,KAAK+3B,cAAclO,WAAWjB,EAAW5oB,KAAKopB,iBAC9CppB,KAAK+3B,cAAcpP,aAAaC,EAAWhd,EAAS5L,KAAKopB,gBAAiBua,GAC1E3jC,KAAK+3B,cAAcvP,SAAS5c,EAAS5L,KAAKopB,iBAC1CppB,KAAK+3B,cAAc9N,aAAajqB,KAAKopB,gBAAiB,CAAE5nB,KAAMonB,EAAUpnB,KAAM2hC,SAAUQ,EAAQzhC,SAElGlC,KAAK8M,QAAQsC,MACbpP,KAAKohC,mBAAqB,UAC1BphC,KAAK0jC,uBAAyB,KAEhC,CAEA,IAAI1K,EAASV,GAAWt4B,MACxB,MAAM4jC,EAAmB5jC,KAAKojC,eAAepK,EAAOtqB,SAC9Cka,EAAY,IAAI0X,GACpBsD,EACAxB,EAASlgC,MACT6qB,GAAiB/sB,KAAKwZ,QACtBwf,EAAOtqB,SAMHm1B,EAAW7K,EAAOtqB,QAAQvI,QAAQ,KAClC29B,GAA6B,IAAdD,EAAkB7K,EAAOtqB,QAAQ5H,MAAM,EAAG+8B,QAAYj/B,EAIrEm/B,OAAkCn/B,IAAjBk/B,EACnB9K,EAAOtqB,QAAQ5H,MAAM+8B,EAAW,GAChCD,EAGEI,EAAYx+B,EAAQqpB,QAAQC,cAClC,GAAIkV,QAA+C,CACjD,MAAM90B,EAAQlP,KAAKspB,UAAUhnB,OAAS,EACtC,GAAI4M,EAAQ80B,EACV,MAAM,IAAIvZ,GACR,iBAAiBvb,sBAA0B80B,YAAoBJ,MAC/DjZ,GAAUqB,sBACV,CAAE9pB,MAAO0mB,EAAU1mB,OAGzB,CAGA,IAAImP,EAAgB,CAAC,EACjB4yB,EAAa,EACbjL,EAAO3nB,gBACTA,EAAgB2nB,EAAO3nB,cACvB4yB,EAAajL,EAAOgB,kBAGlBiK,EAAa,EACfjkC,KAAK8M,QAAQ3F,KAAK48B,EAAgB1yB,EAAeyyB,EAAc5D,IAG/DlgC,KAAK8M,QAAQ3F,KAAK48B,EAAgB,CAAC,EAAGD,GAKxC,MAAMI,EAAiBlkC,KAAK6gC,aACtBsD,EAAgBD,EAAiB,KAAOlkC,KAAK+gC,YAWnD,GATKv7B,EAAQyQ,KAAKmC,YAAe+rB,GAC/B3M,GAAgBwB,EAAOX,aAAcr4B,KAAMg5B,EAAOb,eAAgBa,EAAOZ,oBAQvEp4B,KAAKwjC,WAAWI,GAClB5jC,KAAK+3B,cAAclO,WAAWjB,EAAW5oB,KAAKopB,iBAG9CppB,KAAK+3B,cAAc9N,aAAajqB,KAAKopB,gBAAiBppB,KAAKokC,cAAcxb,IACzE5oB,KAAK8M,QAAQsC,WACR,GAAI4pB,EAAOf,YACXkM,IACHnkC,KAAK+3B,cAAclO,WAAWjB,EAAW5oB,KAAKopB,iBAE9CppB,KAAK+3B,cAAc9N,aAAajqB,KAAKopB,gBAAiBppB,KAAKokC,cAAcxb,KAE3E5oB,KAAK8M,QAAQsC,WACR,GAAI80B,EAAgB,CAKzBlkC,KAAKohC,mBAAqB,IAAIlH,GAAkBlB,EAAOtqB,QAAS,CAC9D+hB,OAAQyT,EAAezT,OACvBC,eAAgBwT,EAAexT,iBAEjC1wB,KAAK0jC,uBAAyB,CAAE9a,YAAW6a,QAAQ,GACnDzjC,KAAKohC,mBAAmB1G,WACxB,MAAM,QAAE9uB,EAAS3F,IAAK09B,GAAY3jC,KAAKohC,mBAAmBxG,QAAQ56B,KAAKwZ,QACvExZ,KAAK+3B,cAAclO,WAAWjB,EAAW5oB,KAAKopB,iBAC9CppB,KAAK+3B,cAAcpP,aAAaC,EAAWhd,EAAS5L,KAAKopB,gBAAiBua,GAC1E3jC,KAAK+3B,cAAcvP,SAAS5c,EAAS5L,KAAKopB,iBAM1CppB,KAAK+3B,cAAc9N,aAAajqB,KAAKopB,gBAAiB,CAAE5nB,KAAMonB,EAAUpnB,KAAM2hC,SAAUQ,EAAQzhC,QAChGlC,KAAK8M,QAAQsC,MACbpP,KAAKohC,mBAAqB,KAC1BphC,KAAK0jC,uBAAyB,IAChC,MAAO,GAAIS,EAITnkC,KAAKohC,mBAAqB,IAAIlH,GAAkBlB,EAAOtqB,QAAS,CAC9D+hB,OAAQ0T,EAAc1T,OACtBC,eAAgByT,EAAczT,iBAEhC1wB,KAAK0jC,uBAAyB,CAAE9a,YAAW6a,QAAQ,GACnDzjC,KAAKohC,mBAAmB1G,WACxB16B,KAAKohC,mBAAmBxG,QAAQ56B,KAAKwZ,QACrCxZ,KAAK8M,QAAQsC,MACbpP,KAAKohC,mBAAqB,KAC1BphC,KAAK0jC,uBAAyB,UACzB,GAAI1jC,KAAKghC,QAAQhhC,KAAKopB,iBAAkB,CAS7C,MAAMib,EAAYrkC,KAAKspB,UAAUhnB,OAGjC,IAFAtC,KAAK8M,QAAQsC,MAENpP,KAAK63B,mBAAqB73B,KAAK63B,iBAAiBn4B,MACrDM,KAAKk/B,cACLl/B,KAAKm/B,SAIkC,mBAA9Bn/B,KAAK+3B,cAAcjP,QAC5B9oB,KAAK+3B,cAAcjP,OAAO,CACxBF,YACA9b,QAAS9M,KAAKopB,gBACdla,MAAOm1B,IAIXrkC,KAAKqhC,kBAAmB,CAC1B,MACErhC,KAAKskC,QAAQ1b,EAEjB,CAaA0b,OAAAA,CAAQ1b,GACN5oB,KAAKspB,UAAUniB,KAAKnH,KAAK63B,kBACzB73B,KAAK+3B,cAAclO,WAAWjB,EAAW5oB,KAAKopB,iBAC9CppB,KAAK63B,iBAAmBjP,CAC1B,CAcAuW,MAAAA,CAAO+D,GACLljC,KAAK+3B,cAAc9N,aAAajqB,KAAKopB,gBAAiB8Z,GAAa,CAAE1hC,KAAMxB,KAAK63B,kBAAkBr2B,OAClGxB,KAAK8M,QAAQsC,MACbpP,KAAK63B,iBAAmB73B,KAAKspB,UAAUla,KACzC,CAQAg1B,aAAAA,CAAcxb,GACZ,MAAO,CACLpnB,KAAMonB,EAAUpnB,KAChBU,MAAO0mB,EAAU1mB,MACjBihC,SAAUva,EAAU2X,QAExB,CAEA+B,cAAAA,CAAeiC,GACb,GAAgB,MAAZA,EAuBF,MAAM,IAAI9Z,GAAW,iBAAiB8Z,KAAY5Z,GAAUU,YAAa4B,GAAgBjtB,KAAKwZ,SAvB3E,CACnB,IAAI0jB,EAAWl9B,KAAKwZ,OAAOuY,SAC3B,GAAImL,QAA6C,MAAM,IAAIzS,GAAW,sCAAuCE,GAAUM,eAAgBgC,GAAgBjtB,KAAKwZ,SAE5J,GAAiB,MAAb0jB,GH7hBH,SAAqBpX,GAC1BA,EAAOtM,OAAOiY,eAAe,GAE7BpE,GAAYvH,EAAOtM,OAAQ,IAAK,uBAChC,IAAI4O,EAAOtC,EAAOtM,OAAO2Y,SAAS,UAClC/J,EAAO8E,GAAgB9E,EAAMtC,EAAOtM,QACpCsM,EAAOiS,cAAc5P,WAAWC,EAClC,CGuhBQoc,CAAYxkC,WACP,GAAiB,MAAbk9B,GHjmBV,SAAmBpX,GAIxBA,EAAOtM,OAAOiY,eAAe,GAG7BpE,GAAYvH,EAAOtM,OAAQ,SAAU,kBAErC,IAAI4O,EAAOtC,EAAOtM,OAAO2Y,SAAS,OAElC/J,EAAO8E,GAAgB9E,EAAMtC,EAAOtM,QACpCsM,EAAOiS,cAAc1P,WAAWD,EAClC,CGqlBQqc,CAAUzkC,WACL,GAAiB,MAAbk9B,EAAkB,CAG3B,MAAMwH,EFvmBP,SAAqB5e,GACxBA,EAAOtM,OAAOiY,eAAe,GAG7BpE,GAAYvH,EAAOtM,OAAQ,SAAU,oBAGrC,MAAM2L,EAAW,CAAC,EAClB,IAAI/S,EAAc,EACdD,GAAU,EACVwyB,GAAW,EAEf,KAAO7e,EAAOtM,OAAOiU,WAAW,CAQ5B,MAAMmX,EAAc9e,EAAOtM,OAAO/W,WAElC,IAAI6C,EAAKwgB,EAAOtM,OAAOuY,SAEvB,GAAW,MAAPzsB,GAAc6M,IAAYwyB,EAK1B,IACI/W,GAAc9H,EAAOtM,OAAQ,EAAG,mBAChC,IAAIqrB,EAAO/e,EAAOtM,OAAOyY,QAAQ,GAEjC,GADAnM,EAAOtM,OAAOmU,qBAAqB,GACtB,MAATkX,EAAc,MAAM,IAAIpa,GACxB,4CAA4Coa,KAC5Cla,GAAUU,YACV4B,GAAgBnH,EAAOtM,SAG3BoU,GAAc9H,EAAOtM,OAAQ,EAAG,wBAChC,IAAIsrB,EAAWhf,EAAOtM,OAAOyY,QAAQ,GAGrC,GAFAnM,EAAOtM,OAAOmU,qBAAqB,GAElB,MAAbmX,EAAkB,CAElBlX,GAAc9H,EAAOtM,OAAQ,EAAG,mBAChC,IAAIurB,EAAQjf,EAAOtM,OAAOyY,QAAQ,GAElC,GADAnM,EAAOtM,OAAOmU,qBAAqB,GACrB,MAAVoX,EAAe,MAAM,IAAIta,GACzB,6BACAE,GAAUU,YACV4B,GAAgBnH,EAAOtM,SAE3BsM,EAAOtM,OAAO2Y,SAAS,SAE3B,MAAO,GAAiB,MAAb2S,EAAkB,CAEzBlX,GAAc9H,EAAOtM,OAAQ,EAAG,0BAChC,IAAIwrB,EAAYlf,EAAOtM,OAAOyY,QAAQ,GAGtC,GAFAnM,EAAOtM,OAAOmU,qBAAqB,GAEjB,MAAdqX,EAAmB,CAEnB3X,GAAYvH,EAAOtM,OAAQ,OAAQ,0BAEnC,MAAO3G,EAAYD,GAAe2qB,GAAczX,GAEhD,IAAkC,IAA9BlT,EAAYzM,QAAQ,KAAa,CACjC,MAAMy3B,EAAK9X,EAAOtgB,SAASkpB,eAC3B,GAAIkP,GAAIhsB,gBAAkBQ,GAAewrB,EAAGhsB,eACxC,MAAM,IAAI6Y,GACN,iBAAiBrY,EAAc,+BAA+BwrB,EAAGhsB,kBACjE+Y,GAAUuB,iBACVe,GAAgBnH,EAAOtM,SAG/B,MAAMyrB,EAAUpyB,EAAW5P,QAAQ,YAAa,QAChDkiB,EAAStS,GAAc,CACnBqyB,KAAMhiC,OAAO,IAAI+hC,KAAY,KAC7BrqB,IAAKhI,GAETR,GACJ,CAEJ,KAAO,IAAkB,MAAd4yB,EAMP,MAAM,IAAIva,GACN,+BAA+Bua,KAC/Bra,GAAUU,YACV4B,GAAgBnH,EAAOtM,SAP3B6T,GAAYvH,EAAOtM,OAAQ,QAAS,2BACpCqkB,GAAe/X,EAQnB,CAEJ,MAAO,GAAiB,MAAbgf,EAEPzX,GAAYvH,EAAOtM,OAAQ,SAAU,2BACrC2kB,GAAerY,OAEZ,IAAiB,MAAbgf,EAMP,MAAM,IAAIra,GACN,8BAA8Bqa,KAC9Bna,GAAUU,YACV4B,GAAgBnH,EAAOtM,SAP3B6T,GAAYvH,EAAOtM,OAAQ,UAAW,4BACtC4kB,GAAgBtY,EAQpB,CAEJ,CAAE,MAAOlc,GAUL,MATIA,EAAIxI,OAASupB,GAAUM,iBAKvBnF,EAAOtM,OAAO/W,WAAamiC,GAIzBh7B,CACV,MAEG,GAAW,MAAPtE,EACP6M,GAAU,OAEP,GAAW,MAAP7M,EACPq/B,GAAW,OAER,GAAW,MAAPr/B,KACF6M,GAAWwyB,GACZ,OAAOxf,CAKnB,CAEA,MAAM,IAAIsF,GACN,mBACAE,GAAUM,eACVgC,GAAgBnH,EAAOtM,QAE/B,CEmdgC2rB,CAAYnlC,MACpC,GAAIA,KAAKygC,aACP,MAAM,IAAIhW,GAAW,uCAAwCE,GAAUC,cAAeqC,GAAgBjtB,KAAKwZ,SAE7GxZ,KAAKygC,cAAe,EAChBzgC,KAAKwF,QAAQkpB,eAAeC,SAC9B+V,GACArkC,OAAOiZ,KAAKorB,GAAiBpiC,OAAS,GACtCtC,KAAK+3B,cAAc1Z,iBAAiBqmB,EAExC,CACF,CAGF,CAEAxF,WAAAA,QAC2Bt6B,IAArB5E,KAAK8/B,aAAkD,KAArB9/B,KAAK8/B,cAGzC9/B,KAAK8/B,YAAc5S,GAAgBltB,KAAK8/B,YAAa9/B,KAAKwZ,UAErDxZ,KAAKwF,QAAQyQ,KAAKgY,gBAAkBjuB,KAAK8/B,YAAYj4B,OAAOvF,OAAS,IACxEtC,KAAK+3B,cAAcvP,SAASxoB,KAAK8/B,YAAa9/B,KAAKopB,iBAErDppB,KAAK8/B,YAAc,GAEvB,CAgBA36B,YAAAA,CAAa3D,GACX,MAAMgD,EAAQxE,KAAKshC,aACnB,GAAI98B,EAAM+S,IAAI/V,GAAO,OAAO,EAC5B,MAAM4jC,EAAKplC,KAAKg+B,iBAAiB,QAAtBh+B,CAA+BwB,GAK1C,OAJI4jC,IACE5gC,EAAMM,MApoBS,KAooBiBN,EAAM6hB,QAC1C7hB,EAAM8S,IAAI9V,IAEL4jC,CACT,CAgBApH,gBAAAA,CAAiB95B,GACf,IAAIQ,EAAY1E,KAAKs9B,gBAAgBp5B,GACrC,IAAKQ,EAAW,CACd,MAAMd,EAAqC,MAAxB5D,KAAKo9B,OAAOp3B,SAA2C,QAAxBhG,KAAKo9B,OAAOp3B,QAAoB,MAAQ,MAC1FtB,EAAYT,EAAgBC,EAAY,CAAEN,eAC1C5D,KAAKs9B,gBAAgBp5B,GAAcQ,CACrC,CACA,OAAOA,CACT,CAEAo1B,eAAAA,CAAgBuL,GACd,OAAOlF,GAAcngC,KAAKihC,WAAWC,MAAOmE,EAAa,KACvD,MAAM7/B,EAAUxF,KAAKwF,QACrB,IAAIgC,EAAW89B,GAAgBD,EAAa7/B,EAAQyQ,KAAK+X,UACzD,IAAKhuB,KAAKg+B,iBAAiB,QAAtBh+B,CAA+BwH,GAClC,MAAM,IAAIijB,GAAW,2BAA2BjjB,IAAYmjB,GAAUY,uBAAwB0B,GAAgBjtB,KAAKwZ,SAGrH,GADAhS,EAAW+9B,GAAa/9B,EAAUhC,EAAQ+oB,oBAAqB/oB,EAAQgpB,cAAexuB,KAAKwZ,QACvFhU,EAAQ8oB,qBAAuB9mB,IAAahC,EAAQ4S,WAAWwR,QACjE,MAAM,IAAIa,GAAW,8BAA8BjjB,IAAYmjB,GAAUoB,yBAA0BkB,GAAgBjtB,KAAKwZ,SAE1H,OAAOhS,GAEX,CAEA47B,cAAAA,CAAeoC,GACb,OAAOrF,GAAcngC,KAAKihC,WAAWzsB,KAAMgxB,EAAY,KACrD,MAAMhgC,EAAUxF,KAAKwF,QACf0S,EAAU1S,EAAQ0S,QACxB,IAAIxJ,EAAU42B,GAAgBE,EAAYhgC,EAAQyQ,KAAK+X,UAEvD,GADAtf,EAAU62B,GAAa72B,EAASlJ,EAAQ+oB,oBAAqB/oB,EAAQgpB,cAAexuB,KAAKwZ,QACrFhU,EAAQ8oB,sBACV5f,IAAYwJ,EAAQlO,SACpB0E,IAAYwJ,EAAQoQ,OACpB5Z,IAAYwJ,EAAQkQ,MAEpB,MAAM,IAAIqC,GAAW,wBAAwB/b,IAAWic,GAAUoB,yBAA0BkB,GAAgBjtB,KAAKwZ,SAEnH,OAAO9K,GAEX,CAEA80B,UAAAA,CAAW90B,GACT,OAAO1O,KAAK2gC,aAAappB,IAAI7I,EAC/B,CAMAk0B,YAAAA,GACE,MAAM6C,EAAOzlC,KACb,MAAO,CACL,aAAIspB,GAAc,OAAOmc,EAAKnc,SAAW,EACzC,oBAAIuO,GAAqB,OAAO4N,EAAK5N,gBAAkB,EACvD,oBAAIA,CAAiB6N,GAAKD,EAAK5N,iBAAmB6N,CAAG,EACrD,iBAAI3N,GAAkB,OAAO0N,EAAK1N,aAAe,EACjD,mBAAI3O,GAAoB,OAAOqc,EAAKrc,eAAiB,EACrD,WAAItc,GAAY,OAAO24B,EAAK34B,OAAS,EACrC,UAAI0M,GAAW,OAAOisB,EAAKjsB,MAAQ,EACnC,eAAIsmB,GAAgB,OAAO2F,EAAK3F,WAAa,EAC7C,eAAIA,CAAY4F,GAAKD,EAAK3F,YAAc4F,CAAG,EAC3CxG,YAAauG,EAAKvG,YAAYyG,KAAKF,GACnCtG,OAAQsG,EAAKtG,OAAOwG,KAAKF,GAE7B,EAGF,SAASH,GAAgB9jC,EAAMokC,GAC7B,GAAIA,EAAc,CAChB,MAAMC,EAAQrkC,EAAKa,MAAM,KACzB,GAAqB,IAAjBwjC,EAAMvjC,OACR,MAAiB,UAAbujC,EAAM,IACHA,EAAM,GACR,GAAIA,EAAMvjC,OAAS,EACxB,MAAM,IAAImoB,GAAW,gCAAgCjpB,IAAQmpB,GAAUa,oBAAqByB,GAAgBjtB,KAAKwZ,QAErH,CACA,OAAOhY,CACT,CAEA,SAAS+jC,GAAa/jC,EAAM+sB,EAAqBC,EAAehV,GAM9D,GAAIsT,GAAmB1oB,SAAS5C,GAC9B,MAAM,IAAIipB,GAAW,6BAA6BjpB,2EAA+EmpB,GAAUkB,6BAA8BoB,GAAgBzT,IAE3L,OAAsB,IAAlBgV,EAAgChtB,EAChCqrB,GAAyBzoB,SAAS5C,GAC7B+sB,EAAoB/sB,GAEtBA,CACT,CAwBA,SAASq/B,KACP,GAAyC,IAArC7gC,KAAK4gC,uBAAuB97B,KAAY,OAAO,KACnD,MAAM8tB,EAAU5yB,KAAK4gC,uBAAuB/oB,UAAU7X,KAAK8M,SAC3D,OAAO8lB,EAAUA,EAAQloB,KAAO,IAClC,CAOA,SAASq2B,KACP,GAAwC,IAApC/gC,KAAK8gC,sBAAsBh8B,KAAY,OAAO,KAClD,MAAM8tB,EAAU5yB,KAAK8gC,sBAAsBjpB,UAAU7X,KAAK8M,SAC1D,OAAO8lB,EAAUA,EAAQloB,KAAO,IAClC,CGvvBe,MAAMo7B,GACnBrkC,WAAAA,CAAY+D,EAAU,CAAC,GACrBxF,KAAK6wB,OAAS,GACd7wB,KAAKyC,WAAa,EAClBzC,KAAK+lC,YAAa,EAIlB/lC,KAAKgtB,YAAc,EAEnBhtB,KAAKivB,cAAgBzpB,EAAQypB,eAAiB,SAC9CjvB,KAAKkvB,WAAkC,IAAtB1pB,EAAQ0pB,UACzBlvB,KAAKmvB,eAAiB3pB,EAAQ2pB,gBAAkB,KAchDnvB,KAAKgmC,iBAAmBxgC,EAAQ+pB,UAAY,KAC5C,MAAM0W,EAAoBjmC,KAAKgmC,kBAAkBxW,SAGjD,GAFAxvB,KAAKkmC,WAAmC,SAAtBD,EAClBjmC,KAAKmmC,aAAenmC,KAAKkmC,WAAa5S,OAAO8S,MAAM,GAAK,MACnDpmC,KAAKkmC,YAAcD,GAAqBjmC,KAAKgmC,iBAAiBxf,SAAU,CAC3E,MAAMA,EAAWxmB,KAAKgmC,iBAAiBxf,SACvCxmB,KAAKqmC,eAAiB,IAAM7f,EAASoP,QAAQqQ,GAAmB3R,eAClE,MACEt0B,KAAKqmC,eAAkD,mBAA1B7gC,EAAQ8uB,cAA+B9uB,EAAQ8uB,cAAgB,KAe9Ft0B,KAAKuxB,OAAS,CAAC,KAAM,MAarBvxB,KAAKsmC,SAAW,KAOhBtmC,KAAK+wB,YAAc,IAAIS,WlB9DS,KkB+DhCxxB,KAAKoxB,eAAiB,CACxB,CAiBAmV,IAAAA,CAAK77B,GACH,GAAI1K,KAAKkmC,WACP,GAAoB,iBAATx7B,EAGT1K,KAAKkmC,YAAa,MACb,CACL,MAAMM,EAAQlT,OAAOmT,SAAS/7B,GAAQA,EAAO4oB,OAAOpB,KAAKxnB,GACzD1K,KAAKmmC,aAAenmC,KAAKmmC,aAAa7jC,OAASgxB,OAAOoT,OAAO,CAAC1mC,KAAKmmC,aAAcK,IAAUA,EAC3F,MAAMG,EAAsB3mC,KAAKmmC,aAAa/hC,SAASkvB,OAAOpB,KAAK,OACnE,GAAIlyB,KAAKmmC,aAAa7jC,OArJZ,MAqJmCqkC,EAE3C,OAAO,EAETj8B,EAAO1K,KAAK4mC,mBACd,CAGF,IAAIC,EAAU7mC,KAAK8mC,WAAWp8B,GAE9B,MAAMq8B,EAAY/mC,KAAK6wB,OAAOvuB,OAAStC,KAAKyC,WAE5C,GAAIskC,EAAYF,EAAQvkC,OAAStC,KAAKivB,cACpC,MAAM,IAAIxE,GACR,+BAA+Bsc,EAAYF,EAAQvkC,YAAYtC,KAAKivB,wEAEpEtE,GAAUC,eAKd,OADA5qB,KAAK6wB,QAAUgW,EACRA,EAAQvkC,MACjB,CAGAwkC,UAAAA,CAAWp8B,GACT,GAAoB,iBAATA,EAAmB,OAAOA,EACrC,GAAI4oB,OAAOmT,SAAS/7B,GAKlB,OADK1K,KAAKsmC,WAAUtmC,KAAKsmC,SAAWtmC,KAAKqmC,eAAiBrmC,KAAKqmC,iBAAmBvQ,GAAyB,UACpG91B,KAAKsmC,SAAS/R,MAAM7pB,GAE7B,GAAIA,GAAMvC,SAAU,OAAOuC,EAAKvC,WAChC,MAAM,IAAIsiB,GAAW,0CAA2CE,GAAUK,oBAC5E,CAUA4b,iBAAAA,GACE,MAAMpgB,EAAWxmB,KAAKgmC,iBAAiBxf,UACjC,SAAEgJ,EAAQ,UAAEyE,GAAcY,GAAM70B,KAAKmmC,aAAc3f,GACnD6N,EAAa7N,EAASoP,QAAQpG,GACpCxvB,KAAKqmC,eAAiB,IAAMhS,EAAWC,gBACvCt0B,KAAKkmC,YAAa,EAClB,MAAMc,EAAO/S,EAAYj0B,KAAKmmC,aAAajS,SAASD,GAAaj0B,KAAKmmC,aAEtE,OADAnmC,KAAKmmC,aAAe,KACba,CACT,CAGA/gC,GAAAA,GACE,GAAIjG,KAAKkmC,WAAY,CAInB,MAAMc,EAAOhnC,KAAK4mC,oBAClB5mC,KAAK6wB,QAAU7wB,KAAK8mC,WAAWE,EACjC,CACA,GAAIhnC,KAAKsmC,SAAU,CAMjB,MAAMW,EAAOjnC,KAAKsmC,SAASrgC,MACvBghC,IAAMjnC,KAAK6wB,QAAUoW,EAC3B,CACAjnC,KAAK+lC,YAAa,CACpB,CAOAtY,OAAAA,CAAQxf,EAAI,GACV,OAAOjO,KAAKyC,WAAawL,EAAIjO,KAAK6wB,OAAOvuB,MAC3C,CAoBAmvB,cAAAA,CAAe5iB,EAAQ,GACrB7O,KAAKuxB,OAAO1iB,GAAS7O,KAAKyC,UAC5B,CAWAivB,YAAAA,GACyB,OAAnB1xB,KAAKuxB,OAAO,KACdvxB,KAAKyC,WAAazC,KAAKuxB,OAAO,IAEhCvxB,KAAKuxB,OAAO,GAAK,KACjBvxB,KAAKuxB,OAAO,GAAK,IACnB,CAcAI,SAAAA,GACE3xB,KAAKuxB,OAAO,GAAK,KACjBvxB,KAAKuxB,OAAO,GAAK,IACnB,CAMAQ,MAAAA,GACE,OAAO/xB,KAAK6wB,OAAO7wB,KAAKyC,aAC1B,CAOAuvB,QAAAA,CAAS9vB,GACP,OAAOlC,KAAK6wB,OAAO7wB,KAAKyC,WAAaP,EACvC,CAQA+vB,OAAAA,CAAQhkB,EAAGikB,GAET,YADoB,IAATA,IAAsBA,EAAOlyB,KAAKyC,YACtCzC,KAAK6wB,OAAOzuB,UAAU8vB,EAAMA,EAAOjkB,EAC5C,CASAyf,UAAAA,CAAWJ,EAAUE,GAAkB,GACrC,MAAM/mB,EAAM6mB,EAAShrB,OACrB,IAAK,IAAIoE,EAAI,EAAGA,EAAID,EAAKC,IAAK,CAC5B,IAAIpB,EAAKtF,KAAK6wB,OAAO7wB,KAAKyC,WAAaiE,GACvC,QAAW9B,IAAPU,EAAkB,OAAO,KAE7B,GADIkoB,IAAiBloB,EAAKA,EAAGoR,eACzBpR,IAAOgoB,EAAS5mB,GAAI,OAAO,CACjC,CACA,OAAO,CACT,CAQAiqB,cAAgBA,GAChBU,kBAAoBA,GAQpBc,QAAAA,CAASC,GACP,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAE3B,IAAK,IAAIoE,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,IAAIlE,GAAQ,EACZ,IAAK,IAAIqG,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAErG,GAAQ,EAAO,KAAO,CAEjE,GAAIA,EAAO,CACT,MAAMqC,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI4rB,EACfztB,CACT,CACF,CAEA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,CAUAsH,YAAAA,CAAaC,GACX,MAAM9rB,EAAI1G,KAAK6wB,OAAO1qB,QAAQqsB,EAAUxyB,KAAKyC,YAC7C,IAAW,IAAPiE,EACF,MAAM,IAAI+jB,GAAW,qCAAqC+H,KAAa7H,GAAUM,gBAEnF,MAAMpmB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiE,GAEtD,OADA1G,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CAQA4tB,gBAAAA,CAAiBL,GACf,MAAMC,EAAcryB,KAAK6wB,OAAOvuB,OAC1BgwB,EAAaF,EAAQ9vB,OAC3B,IAAIowB,GAAiB,EACjBC,EAAQ,EAEZ,IAAK,IAAIjsB,EAAI1G,KAAKyC,WAAYiE,EAAI2rB,EAAa3rB,IAAK,CAClD,GAAc,IAAVisB,EAAa,CACf,MAAMzR,EAAIlhB,KAAK6wB,OAAOnqB,GACtB,GAAIimB,GAAQzL,GAAI,SACN,MAANA,EAAayR,EAAQ,GAClBA,EAAQ,EAAGD,GAAiB,EACrC,KAAO,CAEL,IAAIE,GAAU,EACd,IAAK,IAAI/pB,EAAI,EAAGA,EAAIypB,EAAYzpB,IAC9B,GAAI7I,KAAK6wB,OAAOnqB,EAAImC,KAAOupB,EAAQvpB,GAAI,CAAE+pB,GAAU,EAAO,KAAO,CAE/DA,IACFD,EAAQ,EACRD,EAAgBhsB,EAChBA,GAAK4rB,EAAa,EAEtB,CACA,GAAc,IAAVK,EAAa,CACf,MAAM9tB,EAAS7E,KAAK6wB,OAAOzuB,UAAUpC,KAAKyC,WAAYiwB,GAEtD,OADA1yB,KAAKyC,WAAaiE,EAAI,EACf7B,CACT,CACF,CAEA,MAAM,IAAI4lB,GAAW,qCAAqC2H,KAAYzH,GAAUM,eAClF,CAYA0C,oBAAAA,CAAqB1f,EAAI,GACvBjO,KAAKyC,YAAcwL,EAQfjO,KAAKkvB,WAAalvB,KAAKyC,YAAczC,KAAKmvB,gBAC5CnvB,KAAK4xB,OAET,CAgBAA,KAAAA,GAEE,IAAIC,EAAS7xB,KAAKyC,WAClB,IAAK,MAAM8E,KAAKvH,KAAKuxB,OACT,OAANhqB,GAAcA,EAAIsqB,IAAQA,EAAStqB,GAGzC,GAAIsqB,EAAS,EAAG,CACd7xB,KAAK6wB,OAAS7wB,KAAK6wB,OAAOzuB,UAAUyvB,GAGpC,MAAMC,EAAW9xB,KAAKuxB,OAAOjvB,OAC7B,IAAK,IAAIoE,EAAI,EAAGA,EAAIorB,EAAUprB,IACL,OAAnB1G,KAAKuxB,OAAO7qB,KAAa1G,KAAKuxB,OAAO7qB,IAAMmrB,GAGjD7xB,KAAKyC,YAAcovB,EACnB7xB,KAAKgtB,aAAe6E,CACtB,CACF,ECtea,MAAMqV,WAAqBpB,GAgBxCqB,YAAAA,CAAaC,EAAUC,EAASC,EAAOC,GACrCH,EAASI,GAAG,OAAQhB,IAClB,IAKExmC,KAAKumC,KAAKC,GACVa,EAAQ,KACV,CAAE,MAAOz9B,GACPy9B,EAAQz9B,EACV,IAGFw9B,EAASI,GAAG,QAASD,GAErBH,EAASI,GAAG,MAAO,KACjB,IACExnC,KAAKiG,MACLqhC,GACF,CAAE,MAAO19B,GACP29B,EAAQ39B,EACV,GAEJ,EC3Ca,MAAM69B,GAEnBhmC,WAAAA,CAAY+D,GAiBV,GAhBAxF,KAAKwF,QAAUmT,GAAanT,GAG5BxF,KAAK0nC,YAAc,KACnB1nC,KAAK2nC,YAAc,KACnB3nC,KAAK4nC,YAAa,EAGlB5nC,KAAK6nC,cAAgB,EACrB7nC,KAAK8nC,gBAAkB9nC,KAAKwF,QAAQwpB,UAAUI,WAO1CpvB,KAAKwF,QAAQ+pB,UAAUE,eAAgB,CACzC,MAAMjJ,EAAW,IAAI4P,GACrB,IAAK,MAAO50B,EAAM6yB,KAAeh0B,OAAO8d,QAAQne,KAAKwF,QAAQ+pB,SAASE,gBACpEjJ,EAASX,SAAS,CAAErkB,UAAS6yB,IAE/Br0B,KAAKwF,QAAQ+pB,SAASsS,UAAYrb,CACpC,MACExmB,KAAKwF,QAAQ+pB,SAAWvvB,KAAKwF,QAAQ+pB,UAAY,CAAC,EAClDvvB,KAAKwF,QAAQ+pB,SAASsS,UAAYxK,GASpCr3B,KAAKwF,QAAQy7B,WAAa,CAAEzsB,KAAM,IAAI/P,IAAOy8B,MAAO,IAAIz8B,IAC1D,CAQA+c,KAAAA,CAAMvf,GACJ,GAAIA,aAAmBqxB,QAAUyU,YAAYC,OAAO/lC,GAKlD,OAAOjC,KAAK4hC,cAAc3/B,GACrB,GAAuB,iBAAZA,EAAsB,CACtC,IAAIA,GAAuC,mBAArBA,EAAQkG,SAG5B,MAAM,IAAIsiB,GAAW,uCAAwCE,GAAUC,eAFvE3oB,EAAUA,EAAQkG,UAItB,CAEA,MAAM2d,EAAS9lB,KAAKioC,gBACdpjC,EAASihB,EAAOtE,MAAMvf,GAG5B,OAFAjC,KAAKyhC,UAAY3b,EAAO2b,YACxBzhC,KAAKkoC,iBAAmBpiB,EAAO4a,kBAAkBX,aAAe,GACzDl7B,CACT,CAMA+8B,aAAAA,CAAc3/B,GACZ,KAAIA,aAAmB6f,YAAcimB,YAAYC,OAAO/lC,IAGtD,MAAM,IAAIwoB,GAAW,oDAAqDE,GAAUC,eAFpF3oB,EAAUqxB,OAAOpB,KAAKjwB,GAKxB,MAAM6jB,EAAS9lB,KAAKioC,gBACdpjC,EAASihB,EAAO8b,cAAc3/B,GAGpC,OAFAjC,KAAKyhC,UAAY3b,EAAO2b,YACxBzhC,KAAKkoC,iBAAmBpiB,EAAO4a,kBAAkBX,aAAe,GACzDl7B,CACT,CAgBAsjC,WAAAA,CAAYf,GACV,GAoPU,QAFYpmC,EAlPAomC,IAqPL,iBAAVpmC,GACe,mBAAfA,EAAMiJ,MACO,mBAAbjJ,EAAMwmC,IACkB,kBAAxBxmC,EAAMonC,cAvPX,MAAM,IAAI3d,GAAW,oDAAqDE,GAAUE,gBAiP1F,IAA0B7pB,EA9OtB,MAAMwY,EAAS,IAAI0tB,GAAa,IAC3BlnC,KAAKwF,QAAQwpB,SAChBO,SAAU,CAAEC,SAAUxvB,KAAKwF,QAAQ+pB,SAASC,SAAUhJ,SAAUxmB,KAAKwF,QAAQ+pB,SAASsS,aAElFwG,EAAeroC,KAAKioC,gBAI1B,OAHAI,EAAa7uB,OAASA,EACtB6uB,EAAalH,mBAEN,IAAImH,QAAQ,CAAC1S,EAAS2S,KAC3B,IAAIC,GAAU,EACd,MAAMC,EAAQ7+B,IACP4+B,IACHA,GAAU,EACVpB,EAASsB,UACTH,EAAO3+B,KAIX4P,EAAO2tB,aACLC,EAICx9B,IACC,GAAIA,EAAO6+B,EAAK7+B,QAChB,IACEy+B,EAAanG,UACf,CAAE,MAAOyG,GACHA,EAASvnC,OAASupB,GAAUM,eAC9BzR,EAAOkY,eAEP+W,EAAKE,EAET,GAGF,KACE,IAAIH,EACJ,IAKEH,EAAanG,WACbmG,EAAa5F,cACbziC,KAAKkoC,iBAAmBG,EAAa3H,kBAAkBX,aAAe,GACtEyI,GAAU,EACV5S,EAAQyS,EAAatQ,cAAcvN,YACrC,CAAE,MAAO5gB,GAAO6+B,EAAK7+B,EAAM,GAG7B6+B,IAGN,CAIAG,SAAAA,GACE,IAAK5oC,KAAK0nC,YAAa,OAEvB,MAAMmB,EAAY9b,GAAiB/sB,KAAK2nC,aAExC,IACE3nC,KAAK0nC,YAAYxF,UACnB,CAAE,MAAOt4B,GACP,GAAIA,EAAIxI,OAASupB,GAAUM,eAGzB,MAAMrhB,EAFN5J,KAAK2nC,YAAYjW,cAIrB,CAEiB3E,GAAiB/sB,KAAK2nC,aACTkB,GAI5B7oC,KAAK6nC,cAAgB,EACrB7nC,KAAK8nC,gBAAkB9nC,KAAKwF,QAAQwpB,SAASI,YAI7CpvB,KAAK8nC,gBAAkB5qB,KAAKsY,IACH,EAAvBx1B,KAAK8nC,gBACL9nC,KAAKwF,QAAQwpB,SAASC,cAG5B,CAiBAsX,IAAAA,CAAK77B,GACE1K,KAAK4nC,YACR5nC,KAAK8oC,mBAUP,MAAMC,EAAiB/oC,KAAK2nC,YAAYpB,KAAK77B,GAQ7C,OAPA1K,KAAK6nC,eAAiBkB,EAElB/oC,KAAK6nC,eAAiB7nC,KAAK8nC,iBAC7B9nC,KAAK4oC,YAIA5oC,IACT,CAoBAiG,GAAAA,GACE,IAAKjG,KAAK4nC,WACR,MAAM,IAAInd,GAAW,yCAA0CE,GAAUI,eAI3E/qB,KAAK4oC,YAEL,IAEE5oC,KAAK2nC,YAAY1hC,MAKjB,IAAI48B,EAAkB,KACtB,MAAMjU,EAAY5uB,KAAK0nC,YAAYhH,iBAC/B9R,GAAWA,EAAU5pB,QAEzB,IACEhF,KAAK0nC,YAAYxF,UACnB,CAAE,MAAOt4B,GACP,GAAIA,EAAIxI,OAASupB,GAAUM,eAUzB,MAAMrhB,EATN,IAAIglB,EAMF,MAAMhlB,EAHNi5B,EAAkBj5B,CAQxB,CAUA,OARIi5B,EACFjU,EAAU+Q,iBAAiBkD,EAAiB7iC,KAAK0nC,YAAY9E,gBAE7D5iC,KAAK0nC,YAAYjF,cAGnBziC,KAAKkoC,iBAAmBtZ,GAAWmR,aAAe,GAClD//B,KAAKyhC,UAAYzhC,KAAK0nC,YAAYjG,YAC3BzhC,KAAK0nC,YAAY3P,cAAcvN,WACxC,CAAE,QACAxqB,KAAKgpC,qBACP,CACF,CAWAC,cAAAA,GACE,OAAOjpC,KAAKkoC,kBAAoB,EAClC,CAKAD,aAAAA,GACE,OAAO,IAAIzH,GAAaxgC,KAAKwF,QAC/B,CAGAsjC,gBAAAA,GACE9oC,KAAK2nC,YAAc,IAAI7B,GAAe,IACjC9lC,KAAKwF,QAAQwpB,SAChBO,SAAU,CAAEC,SAAUxvB,KAAKwF,QAAQ+pB,SAASC,SAAUhJ,SAAUxmB,KAAKwF,QAAQ+pB,SAASsS,aAExF7hC,KAAK0nC,YAAc1nC,KAAKioC,gBACxBjoC,KAAK0nC,YAAYluB,OAASxZ,KAAK2nC,YAC/B3nC,KAAK0nC,YAAYvG,mBACjBnhC,KAAK4nC,YAAa,CACpB,CAGAoB,mBAAAA,GACEhpC,KAAK0nC,YAAc,KACnB1nC,KAAK2nC,YAAc,KACnB3nC,KAAK4nC,YAAa,CACpB,EC9UF,MAAMsB,WAA6B7f,GAC/B5nB,WAAAA,CAAY2lB,EAAeC,EAAgB+B,EAAiB5C,GACxDzB,MAAMqC,EAAeC,EAAgB+B,EAAiB5C,GACtDxmB,KAAKmpC,aAAe,IAAI1kC,GAC5B,CAEAolB,UAAAA,CAAW/d,GAKP,MAAMs9B,EAAQppC,KAAK8M,QAAQ3E,WACrBkhC,EAAUD,EAAMj9B,YAAY,KAC5Bm9B,GAA0B,IAAbD,EAAiB,GAAKD,EAAMhnC,UAAU,EAAGinC,GACvDrpC,KAAKmpC,aAAa5xB,IAAI+xB,IAAatpC,KAAKmpC,aAAapkC,IAAIukC,EAAY,IAC1E,MAAMC,EAAWvpC,KAAKmpC,aAAa3oC,IAAI8oC,GAEnCC,EAASA,EAASjnC,OAAS,KAAOwJ,EAAItK,MAAM+nC,EAASpiC,KAAK2E,EAAItK,MAElEujB,MAAM8E,WAAW/d,EACrB,CAEA0e,SAAAA,GACI,MAAO,CAAE9f,KAAMqa,MAAMyF,YAAa2e,aAAcnpC,KAAKmpC,aACzD,ECzBJ,MAAMzjB,GAAe,IAAI8jB,GAAkB,CAAE/mB,KAAK,EAAMG,cAAc,EAAME,WAAW,EAAME,SAAS,IAQ/F,SAASC,GAASrI,GACrB,MAAM6uB,EAAS/jB,GAAalE,MAAM5G,GAClC,MAAyB,iBAAX6uB,EAAsBA,EAASC,GACjD,CCnBO,MAAMC,GAAS,CAClBC,UAASA,CAACtc,EAAUuc,IACTA,EAAOvnC,QAAUgrB,EAE5Bwc,UAASA,CAACxc,EAAUuc,IACTA,EAAOvnC,QAAUgrB,EAE5BhrB,OAAMA,CAACgrB,EAAUuc,IACNA,EAAOvnC,SAAWgrB,EAE7B7iB,QAAOA,CAAC6iB,EAAUuc,EAAQE,EAAW,KACnB,IAAI7mC,OAAOoqB,EAAUyc,GACtBhmC,KAAK8lC,GAEtBG,MAAKA,CAAC1c,EAAUuc,IACLvc,IAAauc,EAExBI,GAAEA,CAAC3c,EAAUuc,IACFvc,EAASjrB,MAAM,KAAK8D,QAAQ0jC,IAAW,GAIzCvlB,GAAM,CACfkR,IAAGA,CAAClI,EAAUuc,IACH5mB,GAAS4mB,IAAWvc,EAE/BnQ,IAAGA,CAACmQ,EAAUuc,IACH5mB,GAAS4mB,IAAWvc,GAItB4c,GAAO,CAChBC,UAASA,CAAC7c,EAAUuc,IACTA,GAAUvc,EAErB8c,UAASA,CAAC9c,EAAUuc,IACTA,GAAUvc,GAIZ+c,GAmBN7U,CAAClI,EAAUuc,IACHS,KAAK9oB,MAAMqoB,IAAWS,KAAK9oB,MAAM8L,GApBnC+c,GAuBNltB,CAACmQ,EAAUuc,IACHS,KAAK9oB,MAAMqoB,IAAWS,KAAK9oB,MAAM8L,GC/D1Cid,GAAe,CAAC,kBAAmB,UAAW,kBAAmB,UAAW,UAE3E,MAAMC,GAKT/oC,WAAAA,CAAY+D,EAASilC,EAAYtB,GAC7BnpC,KAAKwF,QAAUA,EACfxF,KAAK0qC,SAAW,GAChB1qC,KAAKyqC,WAAaA,EAIlBzqC,KAAK2qC,cAAgB,IAAIlmC,IAIzBzE,KAAK4qC,cAAgBzB,GAAgB,IAAI1kC,GAC7C,CAQAomC,QAAAA,CAASC,EAAK3qC,EAAK4qC,EAAO99B,GACtB,GAAI8L,MAAMC,QAAQ8xB,GAAM,CACpB,QAAclmC,IAAVmmC,QAA2CnmC,IAApBmmC,EAAM,gBACMnmC,IAA/BmmC,EAAM,UAAUC,WAUhB,OATAhrC,KAAKirC,gBAAgBF,EAAM,UAAWD,EAAIxoC,OAAQ2K,QAClD69B,EAAII,QAAQ,CAACtwB,EAAK1Y,KACd,MAAMipC,EAAYl+B,EAAO,IAAM/K,EAAQ,IACnC7B,OAAOiZ,KAAKyxB,GAAOzoC,OAAS,EAC5BtC,KAAKorC,wBAAwBN,EAAI5oC,GAAQ6oC,EAAOI,GAEhDnrC,KAAK6qC,SAASC,EAAI5oC,GAAQ/B,EAAK4qC,EAAOI,KAMtDnrC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,sBACN6L,KAAMA,EAAK2H,OAAO,IAE1B,KAA0B,iBAARk2B,EACd9qC,KAAKorC,wBAAwBN,EAAKC,EAAO99B,IAEpB,iBAAV89B,GAAsBA,EAAM,UACnC/qC,KAAKqrC,iBAAiBP,EAAK3qC,EAAK4qC,EAAO99B,GAChCjN,KAAKsrC,UAAUP,IACtB/qC,KAAKurC,wBAAwBR,EAAO99B,GAExCjN,KAAKwrC,sBAAsBV,EAAKC,EAAO99B,GAE/C,CAEAu+B,qBAAAA,CAAsBV,EAAKC,EAAO99B,GAC9B,GACI89B,EAAM,WACNA,EAAM,UAAUU,SACoC,mBAA7CzrC,KAAKyqC,WAAWM,EAAM,UAAUU,SACzC,CACE,IAAIC,EAAM1rC,KAAKyqC,WAAWM,EAAM,UAAUU,SAASX,EAAK79B,EAAK2H,OAAO,IACjD,iBAAR82B,GACP1rC,KAAK0qC,SAASvjC,KAAKukC,EAE3B,CACJ,CAOAN,uBAAAA,CAAwBN,EAAKC,EAAO99B,GAChC,MAGM0+B,EFxDP,SAAqBjhC,EAAMqgC,GAC9B,MAAMa,EAAQ,CACVlhC,KAAM,GACNqgC,MAAO,GACPc,OAAQ,IAGZ,IAAK,IAAInlC,EAAI,EAAGA,EAAIgE,EAAKpI,OAAQoE,KACG,IAA5BqkC,EAAM5kC,QAAQuE,EAAKhE,IAAYklC,EAAMlhC,KAAKvD,KAAKuD,EAAKhE,IACnDklC,EAAMC,OAAO1kC,KAAKuD,EAAKhE,IAGhC,IAAK,IAAIA,EAAI,EAAGA,EAAIqkC,EAAMzoC,OAAQoE,KACE,IAA5BgE,EAAKvE,QAAQ4kC,EAAMrkC,KAA2B,MAAbqkC,EAAMrkC,IAAYklC,EAAMb,MAAM5jC,KAAK4jC,EAAMrkC,IAElF,OAAOklC,CACX,CEwCqBE,CAHAzrC,OAAOiZ,KAAKwxB,GACPzqC,OAAOiZ,KAAKyxB,IAG9B/qC,KAAK+rC,qBAAqBJ,EAAM1+B,GAChCjN,KAAKgsC,qBAAqBL,EAAMZ,EAAO99B,GAEvCjN,KAAKwrC,sBAAsBV,EAAKC,EAAO99B,GAIvC,MAAMg/B,EAAgBjsC,KAAKksC,oBAAoBpB,EAAKC,EAAOY,EAAKE,QAEhEF,EAAKE,OAAOX,QAAS/qC,IACjB,MAAMgsC,EAAUl/B,EAAO,IAAM9M,EAC7BH,KAAK6qC,SAASC,EAAI3qC,GAAMA,EAAK4qC,EAAM5qC,GAAMgsC,KAI7CnsC,KAAKosC,0BAA0BtB,EAAKC,EAAOY,EAAKE,OAAQ5+B,GACxDjN,KAAKqsC,4BAA4BvB,EAAKC,EAAOY,EAAKE,OAAQ5+B,EAAMg/B,GAChEjsC,KAAKssC,4BAA4BxB,EAAKC,EAAOY,EAAKE,OAAQ5+B,EAC9D,CAmBAm/B,yBAAAA,CAA0BtB,EAAKC,EAAOwB,EAAYt/B,GAE9C,MAAMu/B,EAAWv/B,EAAK2H,OAAO,GACvB63B,EAAQzsC,KAAK4qC,cAAcpqC,IAAIgsC,GAEhCC,GAELF,EAAWrB,QAAS/qC,IAChB,MAAMusC,EAAa3B,EAAM5qC,GACzB,IAAKusC,IAAeA,EAAW,UAAW,OAC1C,MAAMj8B,EAAIi8B,EAAW,UAEfC,EAASF,EAAMtmC,QAAQhG,GAC7B,IAAgB,IAAZwsC,EAAJ,CAEA,GAAIl8B,EAAEm8B,MAAO,CACT,MAAMC,EAASp8B,EAAEm8B,MAEjB,QAAoBhoC,IAAhBkmC,EAAI+B,GAAuB,OAC/B,MAAMC,EAASL,EAAMtmC,QAAQ0mC,GAC7B,IAAgB,IAAZC,EAAe,OACfH,GAAUG,GACV9sC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,QACN6L,MAAOA,EAAO,IAAM9M,GAAKyU,OAAO,GAChCi1B,OAAQ1pC,EACRmtB,SAAUuf,GAGtB,CAEA,GAAIp8B,EAAEs8B,OAAQ,CACV,MAAMF,EAASp8B,EAAEs8B,OAEjB,QAAoBnoC,IAAhBkmC,EAAI+B,GAAuB,OAC/B,MAAMC,EAASL,EAAMtmC,QAAQ0mC,GAC7B,IAAgB,IAAZC,EAAe,OACfH,GAAUG,GACV9sC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,SACN6L,MAAOA,EAAO,IAAM9M,GAAKyU,OAAO,GAChCi1B,OAAQ1pC,EACRmtB,SAAUuf,GAGtB,CAhCyB,GAkCjC,CA4BAP,2BAAAA,CAA4BxB,EAAKC,EAAOwB,EAAYt/B,GAChD,MAAM+/B,EAAmB,CAAC,SAAU,YAAa,WAAY,YAE7DT,EAAWrB,QAAS/qC,IAChB,MAAMusC,EAAa3B,EAAM5qC,GACzB,IAAKusC,IAAeA,EAAW,UAAW,OAC1C,MAAMj8B,EAAIi8B,EAAW,UAGrB,IAAKM,EAAiBjiC,KAAMkiC,QAAqBroC,IAAZ6L,EAAEw8B,IAAsB,OAE7D,MAAMC,EAAYpC,EAAI3qC,GAGtB,GAAI+sC,QAA+C,OACnD,GAAyB,iBAAdA,EAAwB,OAEnC,MAAMC,EAAY18B,EAAExF,MAAQ,SAE5B+hC,EAAiB9B,QAAS+B,IACtB,QAAgBroC,IAAZ6L,EAAEw8B,GAAqB,OAE3B,MAAMJ,EAASp8B,EAAEw8B,GACXG,EAAStC,EAAI+B,GAGnB,GAAIO,QAAyC,OAC7C,GAAsB,iBAAXA,EAAqB,OAGhC,MAAMC,EAAMrtC,KAAKstC,mBAAmBr9B,OAAOi9B,GAAYj9B,OAAOm9B,GAASD,GAGvE,GAAY,OAARE,EAAc,OAElB,IAAIE,GAAW,EACF,WAATN,GAAgC,IAARI,IAAWE,GAAW,GACrC,cAATN,GAAgC,IAARI,IAAWE,GAAW,GACrC,aAATN,GAAwBI,GAAO,IAAIE,GAAW,GACrC,aAATN,GAAwBI,GAAO,IAAIE,GAAW,GAE9CA,GACAvtC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM6rC,EACNhgC,MAAOA,EAAO,IAAM9M,GAAKyU,OAAO,GAChCi1B,OAAQ55B,OAAOi9B,GACf5f,SAAUuf,OAK9B,CAWAS,kBAAAA,CAAmB3X,EAAGvC,EAAG+Z,GACrB,GAAkB,SAAdA,EAAsB,CACtB,MAAMK,EAAKlD,KAAK9oB,MAAMmU,GAChB8X,EAAKnD,KAAK9oB,MAAM4R,GACtB,OAAIxT,MAAM4tB,IAAO5tB,MAAM6tB,GAAY,KAC5BD,EAAKC,CAChB,CACA,IAAyC,IAArClD,GAAapkC,QAAQgnC,GAAmB,CACxC,MAAMO,EAAKzqB,GAAS0S,GACdgY,EAAK1qB,GAASmQ,GACpB,OAAIxT,MAAM8tB,IAAO9tB,MAAM+tB,GAAY,KAC5BD,EAAKC,CAChB,CAEA,OAAIhY,EAAIvC,GAAW,EACfuC,EAAIvC,EAAU,EACX,CACX,CAYA8Y,mBAAAA,CAAoBpB,EAAKC,EAAOwB,GAC5B,MAAMv+B,EAAM,IAAIvJ,IAShB,OARA8nC,EAAWrB,QAAS/qC,IAChB,MAAMya,EAAMkwB,EAAI3qC,GACZ4Y,MAAMC,QAAQ4B,GACd5M,EAAIjJ,IAAI5E,EAAKya,EAAI5M,IAAIiC,SACd2K,SAAoD,iBAARA,GACnD5M,EAAIjJ,IAAI5E,EAAK,CAAC8P,OAAO2K,OAGtB5M,CACX,CAUAq+B,2BAAAA,CAA4BvB,EAAKC,EAAOwB,EAAYt/B,EAAMg/B,GAKtDM,EAAWrB,QAAS/qC,IAChB,MAAMusC,EAAa3B,EAAM5qC,GACzB,IAAKusC,IAAeA,EAAW,UAAW,OAC1C,MAAMkB,EAASlB,EAAW,UAAUkB,OACpC,IAAKA,EAAQ,OAEb,MAAMC,EAAO5B,EAAczrC,IAAIL,IAAQ,GACxB,WAAXytC,GACAC,EAAK3C,QAAQ,CAACxF,EAAGh0B,KACb,MAAMo8B,EAAY/0B,MAAMC,QAAQ8xB,EAAI3qC,KAC7B8M,EAAO,IAAM9M,EAAM,IAAMuR,EAAM,KAAKkD,OAAO,IAC3C3H,EAAO,IAAM9M,GAAKyU,OAAO,GAChC5U,KAAK+tC,oBAAoB5tC,EAAKulC,EAAGoI,MAKjD,CASAE,wBAAAA,CAAyBC,EAAOC,EAAUC,GACtC,MAAMpnB,EAAO,IAAItiB,IACjBwpC,EAAM/C,QAAQ,CAACkD,EAAM18B,KACjB,MAAMkJ,EAAMwzB,EAAKF,GACjB,GAAItzB,SAAoD,iBAARA,EAAkB,OAClE,MAAMyzB,EAASp+B,OAAO2K,GAClBmM,EAAKxP,IAAI82B,GACTruC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,SACN6L,MAAOkhC,EAAW,IAAMz8B,EAAM,KAAOw8B,GAAUt5B,OAAO,GACtD5T,MAAOqtC,IAGXtnB,EAAKhiB,IAAIspC,EAAQ38B,IAG7B,CAQAq8B,mBAAAA,CAAoBO,EAAWttC,EAAO8sC,GAC7B9tC,KAAK2qC,cAAcpzB,IAAI+2B,IACxBtuC,KAAK2qC,cAAc5lC,IAAIupC,EAAW,IAAIl3B,KAE1C,MAAM2P,EAAO/mB,KAAK2qC,cAAcnqC,IAAI8tC,GAChCvnB,EAAKxP,IAAIvW,GACThB,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,SACN6L,KAAM6gC,EACN9sC,UAGJ+lB,EAAKzP,IAAItW,EAEjB,CAWAqqC,gBAAAA,CAAiBzwB,EAAKza,EAAK4qC,EAAO99B,IACK,IAA/B89B,EAAM,UAAUC,YAChBhrC,KAAKirC,gBAAgBF,EAAM,UAAW,EAAG99B,GAE7C,MAAMshC,EAAUxD,EAAM,UAAU9/B,KAG1BujC,EAAgBxuC,KAAKyuC,aAAa1D,GAExC,GAAgB,QAAZwD,IAAuBA,GAAWvuC,KAAKsrC,UAAUP,GAC9B,iBAARnwB,GAAoBA,EAAItY,OAAS,GACxCtC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,4BACN6L,KAAMA,EAAK2H,OAAO,GAClB5T,MAAO4Z,IAGf5a,KAAKurC,wBAAwBR,EAAO99B,QACjC,GAAgB,SAAZshC,EACPvuC,KAAK0uC,aAAa9zB,EAAK2zB,EAASthC,GAChCjN,KAAK2uC,iBAAiB5D,EAAOnwB,EAAK3N,QAC/B,GAAgB,YAAZshC,EACPvuC,KAAK4uC,gBAAgBh0B,EAAK2zB,EAASthC,QAChC,IAAuC,IAAnCs9B,GAAapkC,QAAQooC,GACvBvuC,KAAK6uC,WAAWN,EAAS3zB,GAG1B5a,KAAK8uC,YAAYN,EAAc,UAAW,MAAOvrB,GAASrI,GAAM3N,GAFhEjN,KAAK+uC,mBAAmBR,EAASthC,EAAM2N,OAIxC,IAAgB,WAAZ2zB,GAAyBA,EAGhC,MAAM,IAAI7sC,MAAM,kCAAoC6sC,GAFpDvuC,KAAK8uC,YAAYN,EAAc,UAAW,SAAU5zB,EAAK3N,EAG7D,CACJ,CAOAwhC,YAAAA,CAAa1D,GACT,IAAKA,EAAM,YAAcA,EAAM,UAAUiE,MAAO,OAAOjE,EACvD,MAAMtB,EF5XP,SAAoBuF,GACvB,GAAqB,iBAAVA,EAAoB,OAAO,KACtC,MAAMnJ,EAAQmJ,EAAM3sC,MAAM,MAC1B,GAAqB,IAAjBwjC,EAAMvjC,OAAc,OAAO,KAC/B,MAAMkzB,EAAMvS,GAAS4iB,EAAM,IACrB1oB,EAAM8F,GAAS4iB,EAAM,IAC3B,OAAIjmB,MAAM4V,IAAQ5V,MAAMzC,GAAa,KAC9B,CAAEqY,MAAKrY,MAClB,CEoXuB8xB,CAAWlE,EAAM,UAAUiE,OAC1C,OAAKvF,EAEE,IACAsB,EACH,SAAU,IACHA,EAAM,UACTvV,SAA6B5wB,IAAxBmmC,EAAM,UAAUvV,IAAoBuV,EAAM,UAAUvV,IAAMvlB,OAAOw5B,EAAOjU,KAC7ErY,SAA6BvY,IAAxBmmC,EAAM,UAAU5tB,IAAoB4tB,EAAM,UAAU5tB,IAAMlN,OAAOw5B,EAAOtsB,OAPjE4tB,CAUxB,CAUA4D,gBAAAA,CAAiB5D,EAAOnwB,EAAK3N,GACzB,GAAI2S,MAAM0qB,KAAK9oB,MAAM5G,IAAO,OAC5B,MAAMnK,EAAIs6B,EAAM,UAEhB,IAAIvV,EAAM/kB,EAAE+kB,IACRrY,EAAM1M,EAAE0M,IAGZ,GAAI1M,EAAEu+B,SAAWxZ,IAAQrY,GAAM,CAC3B,MAAMssB,EF1YX,SAAwBuF,GAC3B,GAAqB,iBAAVA,EAAoB,OAAO,KACtC,MAAMnJ,EAAQmJ,EAAM3sC,MAAM,MAC1B,GAAqB,IAAjBwjC,EAAMvjC,OAAc,OAAO,KAC/B,MAAOkzB,EAAKrY,GAAO0oB,EACnB,OAAIjmB,MAAM0qB,KAAK9oB,MAAMgU,KAAS5V,MAAM0qB,KAAK9oB,MAAMrE,IAAc,KACtD,CAAEqY,MAAKrY,MAClB,CEmY2B+xB,CAAez+B,EAAEu+B,OAC5BvF,IACKjU,IAAKA,EAAMiU,EAAOjU,KAClBrY,IAAKA,EAAMssB,EAAOtsB,KAE/B,MAEYvY,IAAR4wB,GAAsB2Z,GAAqB3Z,EAAK5a,IAChD5a,KAAKovC,qBAAqB,MAAOniC,EAAM2N,EAAK4a,QAEpC5wB,IAARuY,GAAsBgyB,GAAqBhyB,EAAKvC,IAChD5a,KAAKovC,qBAAqB,MAAOniC,EAAM2N,EAAKuC,EAEpD,CAMAouB,uBAAAA,CAAwBR,EAAOoB,GAC3B,MAAM7yB,EAAOjZ,OAAOiZ,KAAKyxB,GACzB,IAAK,IAAIrkC,EAAI,EAAGA,EAAI4S,EAAKhX,OAAQoE,IAAK,CAClC,GAAgB,WAAZ4S,EAAK5S,GAAiB,SAC1B,MACM2oC,EADQtE,EAAMzxB,EAAK5S,IACG,UACxB2oC,IACIA,EAAclF,WAAakF,EAAclF,UAAY,QAKnBvlC,IAA3ByqC,EAAcC,UAAqD,UAA3BD,EAAcC,WAJ7DtvC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,UACN6L,MAAOk/B,EAAU,IAAM7yB,EAAK5S,IAAIkO,OAAO,IASvD,CACJ,CAMA02B,SAAAA,CAAUx/B,GACN,GAAmB,iBAARA,EAAkB,OAAO,EACpC,MAAMwN,EAAOjZ,OAAOiZ,KAAKxN,GACzB,OAAIA,EAAI,UACgB,IAAhBwN,EAAKhX,aAAT,EACuB,IAAhBgX,EAAKhX,MAKpB,CAEA8sC,oBAAAA,CAAqBruB,EAAMorB,EAAStC,EAAQvc,GACxCttB,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM2f,EACN9T,KAAMk/B,EAAQv3B,OAAO,GACrBi1B,SACAvc,YAER,CAEA2d,eAAAA,CAAgBF,EAAOlB,EAAQsC,GAC3B,CAAC,YAAa,aAAajB,QAASnqB,IAChC,QAAoBnc,IAAhBmmC,EAAMhqB,GAAqB,CAC3B,MAAMuM,EAAW3N,OAAOorB,EAAMhqB,IACzBouB,GAAiBpuB,GAAMuM,EAAUuc,IAClC7pC,KAAKovC,qBAAqBruB,EAAMorB,EAAStC,EAAQvc,EAEzD,GAER,CAEAiiB,YAAAA,CAAaxE,EAAOlB,EAAQsC,GACxB,CAAC,MAAO,OAAOjB,QAASnqB,IACpB,QAAoBnc,IAAhBmmC,EAAMhqB,GAAqB,CAC3B,MAAMuM,EAAW3N,OAAOorB,EAAMhqB,IACzBouB,GAAgBpuB,GAAMuM,EAAUuc,IACjC7pC,KAAKovC,qBAAqBruB,EAAMorB,EAAStC,EAAQvc,EAEzD,GAER,CAEAkiB,WAAAA,CAAYzE,EAAOlB,EAAQsC,GACvB,CAAC,YAAa,YAAa,UAAUjB,QAASnqB,IAC1C,QAAoBnc,IAAhBmmC,EAAMhqB,GAAqB,CAC3B,MAAMuM,EAAW3N,OAAOorB,EAAMhqB,IACzBouB,GAAmBpuB,GAAMuM,EAAUuc,IACpC7pC,KAAKovC,qBAAqBruB,EAAMorB,EAAStC,EAAQvc,EAEzD,IAEJ,CAAC,QAAS,MAAM4d,QAASnqB,IACrB,QAAoBnc,IAAhBmmC,EAAMhqB,GAAqB,CAC3B,MAAMuM,EAAWyd,EAAMhqB,GAClBouB,GAAmBpuB,GAAMuM,EAAUuc,IACpC7pC,KAAKovC,qBAAqBruB,EAAMorB,EAAStC,EAAQvc,EAEzD,IAEJ,CAAC,UAAW,YAAa,aAAc,cAAc4d,QAASnqB,IAC1D,QAAoBnc,IAAhBmmC,EAAMhqB,GAAqB,CAC3B,IAAIgpB,EAAW,GACXhpB,EAAKze,OAAS,IAAGynC,EAAWhpB,EAAK3e,UAAU,IAC/C,MAAMkrB,EAAWyd,EAAMhqB,GAClBouB,GAA4B,QAAE7hB,EAAUuc,EAAQE,IACjD/pC,KAAKovC,qBAAqB,UAAWjD,EAAStC,EAAQvc,EAE9D,GAER,CAEAwhB,WAAAA,CAAY/D,EAAOwD,EAAS1E,EAAQsC,GAChB,WAAZoC,EAAsBvuC,KAAKwvC,YAAYzE,EAAOlB,EAAQsC,GACrC,QAAZoC,GAAmBvuC,KAAKuvC,aAAaxE,EAAOlB,EAAQsC,EACjE,CAEAyC,eAAAA,CAAgB/E,EAAQ0E,EAASpC,IACiB,IAA1CnsC,KAAKwF,QAAQiqC,QAAQtpC,QAAQ0jC,IAC7B7pC,KAAK+uC,mBAAmBR,EAASpC,EAAStC,EAElD,CAEAgF,UAAAA,CAAWN,EAAS3zB,GAChB,MAAM0J,EAAMrB,GAASrI,GACrB,OAAKgF,MAAM0E,MAEU,oBAAZiqB,GAAiCjqB,EAAM,GAC3B,oBAAZiqB,GAAiCjqB,EAAM,GAC3B,YAAZiqB,IA+GjB,SAAejqB,GACX,OAAc,EAANA,KAAaA,CACzB,CAjH2CorB,CAAMprB,GAQ7C,CAEAoqB,YAAAA,CAAa9zB,EAAK2zB,EAASpC,GACvB,MAAM9B,EAAOC,KAAK9oB,MAAM5G,GACpBgF,MAAMyqB,IAAOrqC,KAAK+uC,mBAAmBR,EAASpC,EAASvxB,EAC/D,CAEAm0B,kBAAAA,CAAmBR,EAASpC,EAASvxB,GACjC5a,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,SAAWmtC,EACjBthC,KAAMk/B,EAAQv3B,OAAO,GACrB5T,MAAO4Z,GAEf,CAOAoxB,oBAAAA,CAAqBL,EAAMZ,EAAO99B,GAC1B0+B,EAAKZ,OACLY,EAAKZ,MAAMG,QAASyE,IAChB,MAAMC,EAAY7E,EAAM4E,GAAS,UAC7BC,IAAqC,UAAvBA,EAAUN,UAAwBM,EAAUzF,UAAY,IACtEnqC,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,UACN6L,MAAOA,EAAO,IAAM0iC,GAAS/6B,OAAO,MAKxD,CAMAm3B,oBAAAA,CAAqBJ,EAAM1+B,GACnB0+B,EAAKjhC,OAAS1K,KAAKwF,QAAQqqC,cAC3BlE,EAAKjhC,KAAKwgC,QAASp/B,IACf9L,KAAK0qC,SAASvjC,KAAK,CACf/F,KAAM,UACN6L,MAAOA,EAAO,IAAMnB,GAAK8I,OAAO,MAIhD,EAMJ,MAAMk7B,GAAoBtF,GAAU7pC,UAAUkqC,SAE9CL,GAAU7pC,UAAUkqC,SAAW,SAAkBC,EAAK3qC,EAAK4qC,EAAO99B,GAC9D,GACI8L,MAAMC,QAAQ8xB,SACJlmC,IAAVmmC,QACoBnmC,IAApBmmC,EAAM,gBACyBnmC,IAA/BmmC,EAAM,UAAUC,WAmBhB,OAhBAhrC,KAAKirC,gBAAgBF,EAAM,UAAWD,EAAIxoC,OAAQ2K,GAClD69B,EAAII,QAAQ,CAACtwB,EAAK1Y,KACd,MAAMipC,EAAYl+B,EAAO,IAAM/K,EAAQ,IACvC,GAAI7B,OAAOiZ,KAAKyxB,GAAOzoC,OAAS,EAC5BtC,KAAKorC,wBAAwBN,EAAI5oC,GAAQ6oC,EAAOI,OAC7C,CAEH,MAAM4E,EAAchF,EACpB+E,GAAkBjvC,KAAKb,KAAM8qC,EAAI5oC,GAAQ/B,EAAK4vC,EAAa5E,EAC/D,SAIAL,EAAIxoC,OAAS,GAAuB,iBAAXwoC,EAAI,IAC7B9qC,KAAKgwC,sBAAsBlF,EAAKC,EAAO99B,IAM/C6iC,GAAkBjvC,KAAKb,KAAM8qC,EAAK3qC,EAAK4qC,EAAO99B,EAClD,EASAu9B,GAAU7pC,UAAUqvC,sBAAwB,SAAU/B,EAAOlD,EAAO99B,GAC9C5M,OAAOiZ,KAAKyxB,GAAOkF,OAAQlnC,GAAY,WAANA,GACzCmiC,QAASgD,IACf,MAAMxB,EAAa3B,EAAMmD,GACpBxB,GAAeA,EAAW,WACK,SAAhCA,EAAW,UAAUkB,QACzB5tC,KAAKguC,yBAAyBC,EAAOC,EAAUjhC,IAEvD,EC5rBA,MAAM0E,GAAiB,CACnBk+B,cAAc,EACdJ,QAAS,CAAC,OAAQ,UAMhBS,GAAoB,CAAE17B,KAAM,CAAE2D,aAAc,IAAMC,WAAY,CAAED,aAAc,KAM9Eg4B,GAAa,IAAI1I,GAAU,CAC7BxxB,KAAM,CAAEmC,YAAY,GACpBA,WAAY,CAAE4P,OAAQ,GAAI4B,QAAS,SAAUsE,aAAa,GAC1DoB,cAAe,IAAInG,GAAsB+mB,MAGvCE,GAAa,IAAI3I,GAAU,CAC7BxxB,KAAM,CAAEmC,YAAY,GACpBA,WAAY,CAAE4P,OAAQ,GAAI4B,QAAS,MACnC0F,cAAe,IJUZ,cAA0CnG,GAC7CD,WAAAA,CAAY9B,EAAegC,GACvB,OAAO,IAAI8f,GAAqB9hB,EAAepnB,KAAKqnB,eAAgB+B,EAAiBppB,KAAKwmB,SAC9F,GIb+C0pB,MAmD7CG,GAA2B,yBAC3BC,GAAsB,qBAM5B,SAASC,GAAuBvvC,GAC5B,GAAI+X,MAAMC,QAAQhY,GACd,IAAK,MAAMotC,KAAQptC,EAAOuvC,GAAuBnC,QAC9C,GAAc,OAAVptC,GAAmC,iBAAVA,EAAoB,CAChDX,OAAOM,UAAUC,eAAeC,KAAKG,EAAOqvC,MAC5CrvC,EAba,MAaaA,EAAMqvC,WACzBrvC,EAAMqvC,KAEjB,IAAK,MAAMlwC,KAAOE,OAAOiZ,KAAKtY,GAAQuvC,GAAuBvvC,EAAMb,GACvE,CACA,OAAOa,CACX,CAEA,SAASwvC,GAAgBC,GACrB,IAAKA,EAAS,MAAM,IAAI/uC,MAAM,cAC9B,GAAuB,iBAAZ+uC,EAAsB,MAAM,IAAI/uC,MAAM,qBACrD,CAYA,SAASgvC,GAAa5qB,EAAQ2qB,GAC1B,IACI,OAAO3qB,EAAOtE,MAAMivB,EACxB,CAAE,MAAO7mC,GACL,GAAIA,aAAe6gB,GAAY,CAC3B,IAAItpB,EAAUyI,EAAIzI,QAClB,GAAiB,2BAAbyI,EAAIxI,KAAmC,CACvC,MAAMI,EAAOL,EAAQ2F,MAAM3F,EAAQgF,QAAQ,MAAQ,GACnDhF,EAAU,cAAcK,wBAC5B,CAIA,QAAkBoD,IAAdgF,EAAI1H,MAAqB,MAAM,IAAIR,MAAMP,GAC7C,MAAME,EAAOovC,EAAQruC,UAAU,EAAGwH,EAAI1H,OAAOG,MAAM,MAAMC,OACzD,MAAM,IAAIZ,MAAMP,EAAU,IAAME,EACpC,CACA,MAAMuI,CACV,CACJ,CAEA,SAxGO,MAKHnI,WAAAA,CAAYspC,EAAOvlC,GACfgrC,GAAgBzF,GAChB/qC,KAAK+qC,MAAQwF,GAAuBG,GAAaP,GA4CzD,SAAgCpF,GAC5B,OAAOA,EAAM9nC,QAAQqtC,GAAqB,MAAMD,OACpD,CA9CqEM,CAAuB5F,KACpF/qC,KAAKwF,QAAUnF,OAAO2R,OAAO,CAAC,EAAGL,GAAgBnM,GACjDxF,KAAKyqC,WAAa,CAAC,EAEnBzqC,KAAK0K,KAAO,IAChB,CAOAmb,QAAAA,CAASrkB,EAAMovC,GACX5wC,KAAKyqC,WAAWjpC,GAAQovC,CAC5B,CAOA9qC,QAAAA,CAAS2qC,GACLD,GAAgBC,GAEhB,MAAM,KAAE/lC,EAAI,aAAEy+B,GAAiBuH,GAAaN,GAAYK,GACxDzwC,KAAK0K,KAAOA,EAEZ,MAAMmmC,EAAY,IAAIrG,GAAUxqC,KAAKwF,QAASxF,KAAKyqC,WAAYtB,GAE/D,OADA0H,EAAUhG,SAASngC,EAAM,GAAI1K,KAAK+qC,MAAO,IAClC8F,EAAUnG,QACrB,G","sources":["webpack://fxp/webpack/universalModuleDefinition","webpack://fxp/webpack/bootstrap","webpack://fxp/webpack/runtime/define property getters","webpack://fxp/webpack/runtime/hasOwnProperty shorthand","webpack://fxp/webpack/runtime/make namespace object","webpack://fxp/./src/ValidationError.js","webpack://fxp/./src/util.js","webpack://fxp/./node_modules/xml-naming/src/index.js","webpack://fxp/./src/validators/XmlNameValidator.js","webpack://fxp/./src/validators/XmlDeclarationValidator.js","webpack://fxp/./src/validators/ControlCharValidator.js","webpack://fxp/./src/validators/AttributeStringValidator.js","webpack://fxp/./src/validators/CommentCdataReader.js","webpack://fxp/./node_modules/path-expression-matcher/src/Expression.js","webpack://fxp/./node_modules/path-expression-matcher/src/Matcher.js","webpack://fxp/./src/validators/SkipTagMatcher.js","webpack://fxp/./src/validators/RawSubtreeReader.js","webpack://fxp/./src/validators/DocTypeValidator.js","webpack://fxp/./src/SyntaxValidator.js","webpack://fxp/./node_modules/path-expression-matcher/src/ExpressionSet.js","webpack://fxp/./node_modules/@nodable/compact-builder/src/ParserOptionsBuilder.js","webpack://fxp/./node_modules/@nodable/entities/src/entities.js","webpack://fxp/./node_modules/@nodable/entities/src/EntityDecoder.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/html.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/sql.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/nosql.js","webpack://fxp/./node_modules/is-unsafe/src/registry.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/xml.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/svg.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/sql-strict.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/shell.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/redos.js","webpack://fxp/./node_modules/is-unsafe/src/contexts/log.js","webpack://fxp/./node_modules/is-unsafe/src/index.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/BaseValueParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/EntityParser.js","webpack://fxp/./node_modules/anynum/digitTable.js","webpack://fxp/./node_modules/anynum/anynum.js","webpack://fxp/./node_modules/strnum/strnum.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/number.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParserRegistry.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/trim.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/WSNormalizer.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParsers/booleanParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/ValueParser.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/BaseOutputBuilder.js","webpack://fxp/./node_modules/@nodable/base-output-builder/src/BaseOutputBuilderFactory.js","webpack://fxp/./node_modules/@nodable/compact-builder/src/CompactBuilder.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/ParseError.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/util.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/OptionsBuilder.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/scanTagExpEnd.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/StringSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/ScanStrategy/ByteScanStrategy.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/BufferSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingDetector.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/TextDecoderAdapter.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingRegistry.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/AttributeProcessor.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XmlPartReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/StopNodeProcessor.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XmlSpecialTagsReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/DocTypeReader.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/AutoCloseHandler.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Xml2JsParser.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/EncodingProfile.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/Encoding/ScanStrategy/CharScanStrategy.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/FeedableSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/InputSource/StreamSource.js","webpack://fxp/./node_modules/@nodable/flexible-xml-parser/src/XMLParser.js","webpack://fxp/./node_modules/detailed-xml-validator/src/OrderTrackingBuilder.js","webpack://fxp/./node_modules/detailed-xml-validator/src/util.js","webpack://fxp/./node_modules/detailed-xml-validator/src/validations.js","webpack://fxp/./node_modules/detailed-xml-validator/src/Traverser.js","webpack://fxp/./node_modules/detailed-xml-validator/src/validator.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"fxp\"] = factory();\n\telse\n\t\troot[\"fxp\"] = factory();\n})(this, () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export default class ValidationError extends Error {\n constructor(message, code, line, col) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.line = line;\n this.col = col;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}","'use strict';\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\nexport function isWhiteSpace(char) {\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\n}\n\n/**\n * True for character codes that are illegal as literal characters anywhere in\n * an XML document (element text, attribute values, comments): 0x00-0x08,\n * 0x0B, 0x0C, 0x0E-0x1F. Tab/LF/CR (0x09/0x0A/0x0D) are legal whitespace and\n * are excluded.\n */\nexport function isIllegalControlCode(code) {\n return code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31);\n}\n\nexport function getLineNumberForPosition(xmlData, index) {\n const lines = xmlData.substring(0, index).split(/\\r?\\n/);\n return {\n line: lines.length,\n col: lines[lines.length - 1].length + 1,\n };\n}\n\nexport function getPositionFromMatch(match) {\n return match.startIndex + match[1].length;\n}\n","/**\n * xml-naming\n * Validates XML Name productions as defined in the XML 1.0 and 1.1 specifications.\n * Covers: Name, NCName, QName, NMToken, NMTokens\n *\n * XML 1.0 spec: https://www.w3.org/TR/xml/#NT-Name\n * XML 1.1 spec: https://www.w3.org/TR/xml11/#NT-NameStartChar\n * XML NS spec: https://www.w3.org/TR/xml-names/#NT-NCName\n */\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.0\n//\n// NameStartChar ::= \":\" | [A-Z] | \"_\" | [a-z]\n// | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF]\n// | [#x370-#x37D] | [#x37F-#x1FFF] <- split to exclude #x0487\n// | [#x200C-#x200D]\n// | [#x2070-#x218F] | [#x2C00-#x2FEF]\n// | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD]\n//\n// NameChar ::= NameStartChar | \"-\" | \".\" | [0-9]\n// | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]\n//\n// Note: \\u0487 (Combining Cyrillic Millions Sign) was added in Unicode 4.0,\n// after XML 1.0 was defined against Unicode 2.0. It falls inside the range\n// \\u037F-\\u1FFF but must be excluded. We split that range into\n// \\u037F-\\u0486 and \\u0488-\\u1FFF to exclude it explicitly.\n// ---------------------------------------------------------------------------\n\nconst nameStartChar10 =\n ':A-Za-z_' +\n '\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF' +\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD';\n\nconst nameChar10 =\n nameStartChar10 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.1\n//\n// Differences from XML 1.0:\n//\n// NameStartChar:\n// 1.0 has split ranges: \\u00C0-\\u00D6, \\u00D8-\\u00F6, \\u00F8-\\u02FF\n// 1.1 merges them into: \\u00C0-\\u02FF\n// (\\u00D7 x and \\u00F7 / are division symbols, excluded in both versions)\n//\n// 1.0 tops out at \\uFFFD (BMP only)\n// 1.1 adds \\u{10000}-\\u{EFFFF} (supplementary planes)\n// These require the /u flag on the RegExp — see buildRegexes below.\n//\n// NameChar:\n// 1.1 adds \\u0487 (Combining Cyrillic Millions Sign, added in Unicode 4.0)\n// ---------------------------------------------------------------------------\n\nconst nameStartChar11 =\n ':A-Za-z_' +\n '\\u00C0-\\u02FF' + // merged — 1.0 had three split ranges here\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487 (combining mark, never a NameStartChar)\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD' +\n '\\u{10000}-\\u{EFFFF}'; // supplementary planes — REQUIRES /u flag on RegExp\n\nconst nameChar11 =\n nameStartChar11 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u0487' + // Combining Cyrillic Millions Sign — valid in 1.1, not 1.0\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Regex builders\n//\n// XML 1.0 regexes: no flags — BMP only, standard JS regex behaviour.\n// XML 1.1 regexes: /u flag — required for \\u{10000}-\\u{EFFFF} to match actual\n// supplementary code points rather than lone surrogates (which are illegal XML).\n// ---------------------------------------------------------------------------\n\nconst buildRegexes = (startChar, char, flags = '') => {\n const ncStart = startChar.replace(':', '');\n const ncChar = char.replace(':', '');\n const ncNamePat = `[${ncStart}][${ncChar}]*`;\n\n return {\n name: new RegExp(`^[${startChar}][${char}]*$`, flags),\n ncName: new RegExp(`^${ncNamePat}$`, flags),\n qName: new RegExp(`^${ncNamePat}(?::${ncNamePat})?$`, flags),\n nmToken: new RegExp(`^[${char}]+$`, flags),\n nmTokens: new RegExp(`^[${char}]+(?:\\\\s+[${char}]+)*$`, flags),\n };\n};\n\nconst regexes10 = buildRegexes(nameStartChar10, nameChar10); // no /u — BMP only\nconst regexes11 = buildRegexes(nameStartChar11, nameChar11, 'u'); // /u — enables \\u{10000}-\\u{EFFFF}\n\n// ---------------------------------------------------------------------------\n// ASCII-only fast path (opt-in, off by default)\n//\n// The XML 1.0 vs 1.1 NameStartChar/NameChar productions differ *only* in\n// their non-ASCII ranges (merged vs split Latin-1 ranges, \\u0487, and\n// supplementary planes). Restricted to ASCII, both versions collapse to the\n// same character classes, so a single regex pair covers both xmlVersion\n// values — no /u flag needed.\n//\n// Rationale: unicode-aware regexes (the /u flag, required for XML 1.1's\n// supplementary-plane range) are measurably slower in V8 than plain\n// non-unicode regexes on the same input, even when the input is pure ASCII.\n// For the common case — HTML/SVG ids, XML tags — names are ASCII, so callers\n// who know this can opt in to skip the unicode-aware matching path entirely.\n// This is a real but *conditional* win: mainly for XML 1.1 input (avoids /u),\n// or at scale where the larger unicode character classes add engine\n// overhead. It also changes behaviour (rejects legitimate non-ASCII XML\n// 1.0/1.1 names), so it must never be silently enabled — hence off by\n// default.\n// ---------------------------------------------------------------------------\n\nconst nameStartCharAscii = ':A-Za-z_';\nconst nameCharAscii = nameStartCharAscii + '\\\\-\\\\.\\\\d';\n\nconst regexesAscii = buildRegexes(nameStartCharAscii, nameCharAscii); // no /u — ASCII only\n\nconst getRegexes = (xmlVersion = '1.0', asciiOnly = false) => {\n if (asciiOnly) return regexesAscii;\n return xmlVersion === '1.1' ? regexes11 : regexes10;\n};\n\n// ---------------------------------------------------------------------------\n// Boolean validators\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if the string is a valid XML Name.\n * Colons are allowed anywhere (Name production).\n * Used for: DOCTYPE entity names, notation names, DTD element declarations.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const name = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).name.test(str);\n\n/**\n * Returns true if the string is a valid NCName (Non-Colonized Name).\n * Colons are not permitted.\n * Used for: namespace prefixes, local names, SVG id attributes.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const ncName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).ncName.test(str);\n\n/**\n * Returns true if the string is a valid QName (Qualified Name).\n * Allows exactly one colon as a prefix separator: prefix:localName.\n * Used for: element and attribute names in namespace-aware XML/SVG.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const qName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).qName.test(str);\n\n/**\n * Returns true if the string is a valid NMToken.\n * Like Name but no restriction on the first character.\n * Used for: DTD NMTOKEN attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmToken = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmToken.test(str);\n\n/**\n * Returns true if the string is a valid NMTokens value.\n * A whitespace-separated list of NMToken values.\n * Used for: DTD NMTOKENS attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmTokens = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmTokens.test(str);\n\n// ---------------------------------------------------------------------------\n// Memoized validator factory\n//\n// Real documents reuse a small vocabulary of tag/attribute names across many\n// siblings (e.g. `id`, `class`, `href` repeated across hundreds of elements).\n// The plain boolean validators above re-run the regex on every call\n// regardless of repeats. `createValidator` returns a closure with a private\n// string -> boolean cache, so repeated names after the first become O(1)\n// lookups instead of regex tests.\n//\n// - opts (xmlVersion, asciiOnly) are fixed at creation time, so the regex is\n// resolved once, not on every call.\n// - The cache is private to the returned closure — no shared/global state,\n// no cross-caller pollution.\n// - `maxCacheSize` bounds memory: once the cache reaches this many entries,\n// it stops accepting new ones (existing entries keep serving hits; new\n// misses just fall through to the regex, uncached). This avoids unbounded\n// growth against adversarial/high-cardinality input (e.g. validating\n// attacker-supplied names with no repeats) without the cost/complexity of\n// a full LRU, and without the perf cliff of reset-and-refill thrashing.\n// - Call `.reset()` on the returned function to clear the cache manually\n// (e.g. between unrelated parse calls).\n// ---------------------------------------------------------------------------\n\nconst PRODUCTIONS = ['name', 'ncName', 'qName', 'nmToken', 'nmTokens'];\n\n/**\n * Returns a memoized boolean validator function for a single production,\n * with opts fixed at creation time.\n *\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean, maxCacheSize?: number }} [opts]\n * maxCacheSize: max number of distinct strings to cache (default 2048).\n * Once reached, new strings are validated but not cached; existing cached\n * entries keep being served.\n * @returns {((str: string) => boolean) & { reset: () => void }}\n */\nexport const createValidator = (production, { xmlVersion = '1.0', asciiOnly = false, maxCacheSize = 2048 } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const regex = getRegexes(xmlVersion, asciiOnly)[production];\n let cache = new Map();\n\n const validator = (str) => {\n const cached = cache.get(str);\n if (cached !== undefined) return cached;\n\n const result = regex.test(str);\n if (cache.size < maxCacheSize) cache.set(str, result);\n return result;\n };\n\n validator.reset = () => { cache = new Map(); };\n\n return validator;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostic validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates a string against a named production and returns a detailed result.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {{ valid: boolean, production: string, input: string, reason?: string, position?: number }}\n */\nexport const validate = (str, production, { xmlVersion = '1.0', asciiOnly = false } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const validators = { name, ncName, qName, nmToken, nmTokens };\n const isValid = validators[production](str, { xmlVersion, asciiOnly });\n\n if (isValid) return { valid: true, production, input: str };\n\n let reason = 'Does not match the production rules';\n let position;\n\n // Diagnostic fallback char checks must mirror the same character set the\n // boolean validator above used, or the reported reason/position could\n // contradict the `valid: false` result (e.g. flagging a char as illegal\n // that the unicode-aware check would have accepted).\n const startCharPattern = asciiOnly ? /^[:A-Za-z_]/ : /^[:A-Za-z_\\u00C0-\\uFFFD]/;\n const namePattern = asciiOnly ? /[\\w\\-\\\\.:]/ : /[\\w\\-\\\\.:\\u00B7\\u00C0-\\uFFFD]/;\n\n if (str.length === 0) {\n reason = 'Input is empty';\n } else if (production === 'ncName' && str.includes(':')) {\n position = str.indexOf(':');\n reason = 'Colon is not allowed in NCName';\n } else if (production === 'qName' && str.startsWith(':')) {\n reason = 'QName cannot start with a colon';\n position = 0;\n } else if (production === 'qName' && str.endsWith(':')) {\n reason = 'QName cannot end with a colon';\n position = str.length - 1;\n } else if (production === 'qName' && (str.match(/:/g) || []).length > 1) {\n reason = 'QName can have at most one colon';\n position = str.lastIndexOf(':');\n } else if (\n ['name', 'ncName', 'qName'].includes(production) &&\n !startCharPattern.test(str[0])\n ) {\n reason = `First character \"${str[0]}\" is not a valid NameStartChar`;\n position = 0;\n } else {\n for (let i = 0; i < str.length; i++) {\n if (!namePattern.test(str[i])) {\n reason = `Character \"${str[i]}\" at position ${i} is not a valid NameChar`;\n position = i;\n break;\n }\n }\n }\n\n return { valid: false, production, input: str, reason, position };\n};\n\n// ---------------------------------------------------------------------------\n// Batch validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates an array of strings against a named production.\n *\n * @param {string[]} strings\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {Array<{ valid: boolean, production: string, input: string, reason?: string, position?: number }>}\n */\nexport const validateAll = (strings, production, opts = {}) =>\n strings.map(str => validate(str, production, opts));\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms an invalid string into the nearest valid XML name for the given production.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ replacement?: string, asciiOnly?: boolean }} [opts]\n * asciiOnly: also replace any non-ASCII character, not just XML-illegal\n * ones (default false).\n * @returns {string}\n */\nexport const sanitize = (str, production = 'name', { replacement = '_', asciiOnly = false } = {}) => {\n if (!str) return replacement;\n\n let result = str;\n\n // Strip colons for NCName\n if (production === 'ncName') {\n result = result.replace(/:/g, '');\n }\n\n // Replace illegal characters\n const allowedCharPattern = asciiOnly ? /[^\\w\\-\\.:]/g : /[^\\w\\-\\.:\\u00B7\\u00C0-\\uFFFD]/g;\n result = result.replace(allowedCharPattern, replacement);\n\n // Fix invalid start character for Name / NCName / QName\n if (production !== 'nmToken' && production !== 'nmTokens') {\n if (/^[\\-\\.\\d]/.test(result)) {\n result = replacement + result;\n }\n }\n\n return result || replacement;\n};","'use strict';\n\nimport { createValidator } from 'xml-naming';\n\n/**\n * XmlNameValidator — single responsibility: expose XML Name / QName\n * validators for a given xmlVersion, memoized per instance since element\n * and attribute names repeat heavily across a document.\n */\nexport default class XmlNameValidator {\n constructor(xmlVersion = '1.0') {\n this.xmlVersion = xmlVersion;\n this.qName = createValidator('qName', { xmlVersion });\n this.name = createValidator('name', { xmlVersion });\n }\n\n isValidQName(value) {\n return this.qName(value);\n }\n\n isValidName(value) {\n return this.name(value);\n }\n}\n","'use strict';\n\nimport { isWhiteSpace } from '../util.js';\n\nfunction isWordChar(ch) {\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch === '_';\n}\n\n/**\n * Walk the `<?xml ... ?>` body once, left to right, pulling out\n * `name=\"value\"` / `name='value'` pairs (each one preceded by required\n * whitespace) and collecting everything else as leftover text.\n *\n * This replaces two regexes that used to do the same job:\n * /\\s+([\\w]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g - to find attributes\n * /\\s+[\\w]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*')/g (via replace) - to find leftovers\n *\n * Both led with a required-but-greedy `\\s+`. On a long run of whitespace\n * that's never followed by a valid `name=value`, the engine backtracks that\n * whitespace group one character at a time before giving up on that\n * starting position — and since none of those backtracked attempts can ever\n * succeed (a whitespace character is never a valid name character, at any\n * backtrack length), all of that retrying was wasted work. A `<?xml ?>`\n * declaration padded with thousands of spaces could make this step take\n * seconds. Scanning forward once, without ever revisiting a position, is\n * both faster and gives the identical result — the retried attempts could\n * never have succeeded anyway.\n *\n * @param {string} declBody\n * @returns {{ matches: Array<{name: string, value: string, index: number}>, leftover: string }}\n */\nfunction scanDeclAttributes(declBody) {\n const matches = [];\n let leftover = '';\n const len = declBody.length;\n let i = 0;\n\n while (i < len) {\n if (!isWhiteSpace(declBody[i])) {\n leftover += declBody[i];\n i++;\n continue;\n }\n\n const matchStart = i;\n let p = i;\n while (p < len && isWhiteSpace(declBody[p])) p++;\n\n const nameStart = p;\n while (p < len && isWordChar(declBody[p])) p++;\n if (p === nameStart) {\n // Whitespace not followed by a name character — can't become a match\n // no matter how much of the whitespace we consider, so it's leftover.\n leftover += declBody.slice(matchStart, p + 1 <= len ? p : len);\n i = p > matchStart ? p : matchStart + 1;\n continue;\n }\n const name = declBody.slice(nameStart, p);\n\n let q = p;\n while (q < len && isWhiteSpace(declBody[q])) q++;\n if (declBody[q] !== '=') {\n leftover += declBody.slice(matchStart, p);\n i = p;\n continue;\n }\n q++; // skip '='\n while (q < len && isWhiteSpace(declBody[q])) q++;\n\n const quote = declBody[q];\n if (quote !== '\"' && quote !== \"'\") {\n leftover += declBody.slice(matchStart, p);\n i = p;\n continue;\n }\n const valueStart = q + 1;\n const closeIdx = declBody.indexOf(quote, valueStart);\n if (closeIdx === -1) {\n leftover += declBody.slice(matchStart, p);\n i = p;\n continue;\n }\n\n matches.push({ name, value: declBody.slice(valueStart, closeIdx), index: matchStart });\n i = closeIdx + 1;\n }\n\n return { matches, leftover };\n}\n\n/**\n * XmlDeclarationValidator — single responsibility: parse and validate the\n * `<?xml version=\"...\" encoding=\"...\" standalone=\"...\"?>` prolog.\n *\n * Behaviour is controlled by the `xmlDeclaraion` config block:\n *\n * xmlDeclaraion: {\n * optional: true, // when false, the document MUST start with a decl\n * argPosition: true, // when true, version/encoding/standalone must\n * // appear in that fixed order (spec-mandated);\n * // set false to allow any order.\n * }\n *\n * (`argPostion` — the spelling used in the original design note — is also\n * accepted as an alias for `argPosition`.)\n */\nexport default class XmlDeclarationValidator {\n constructor(options = {}, throwError) {\n this.optional = options.optional !== undefined ? options.optional : true;\n this.argPosition = options.argPosition !== undefined\n ? options.argPosition\n : (options.argPostion !== undefined ? options.argPostion : true);\n this._throwError = throwError;\n }\n\n /**\n * @param {string} xmlData - full document (BOM already stripped)\n * @returns {{ version: string, end: number }} version defaults to '1.0'\n * and end is 0 when there is no declaration present.\n */\n validate(xmlData) {\n const hasDecl = xmlData.startsWith('<?xml');\n\n if (!hasDecl) {\n if (!this.optional) {\n this._throwError('InvalidXml', 'XML declaration is required but missing.', { line: 1, col: 1 });\n }\n return { version: '1.0', end: 0 };\n }\n\n const piEnd = xmlData.indexOf('?>');\n if (piEnd === -1) {\n this._throwError('InvalidXml', 'XML declaration is not closed with \"?>\".', { line: 1, col: 1 });\n }\n\n const declBody = xmlData.substring(5, piEnd);\n const end = piEnd + 2;\n\n const ALLOWED = ['version', 'encoding', 'standalone'];\n const { matches: declAttrs, leftover } = scanDeclAttributes(declBody);\n\n let version = '1.0';\n let lastAllowedIdx = -1;\n\n for (let m = 0; m < declAttrs.length; m++) {\n const attrName = declAttrs[m].name;\n const attrValue = declAttrs[m].value;\n const attrPos = declAttrs[m].index;\n\n const allowedIdx = ALLOWED.indexOf(attrName);\n if (allowedIdx === -1) {\n this._throwError('InvalidXml',\n 'XML declaration contains unknown attribute \"' + attrName + '\". Allowed: version, encoding, standalone.',\n { line: 1, col: 6 + attrPos });\n }\n\n if (this.argPosition && allowedIdx <= lastAllowedIdx) {\n this._throwError('InvalidXml',\n 'XML declaration attribute \"' + attrName + '\" is out of order. Required order: version, encoding, standalone.',\n { line: 1, col: 6 + attrPos });\n }\n lastAllowedIdx = allowedIdx;\n\n if (attrName === 'version') {\n if (attrValue !== '1.0' && attrValue !== '1.1') {\n this._throwError('InvalidXml',\n 'XML declaration version \"' + attrValue + '\" is not supported. Must be \"1.0\" or \"1.1\".',\n { line: 1, col: 6 + attrPos });\n }\n version = attrValue;\n } else if (attrName === 'standalone') {\n if (attrValue !== 'yes' && attrValue !== 'no') {\n this._throwError('InvalidXml',\n 'XML declaration standalone \"' + attrValue + '\" is invalid. Must be \"yes\" or \"no\".',\n { line: 1, col: 6 + attrPos });\n }\n }\n // encoding: value format validation is out of scope\n }\n\n const trimmedLeftover = leftover.trim();\n if (trimmedLeftover.length > 0) {\n this._throwError('InvalidXml',\n 'XML declaration contains invalid content: \"' + trimmedLeftover + '\".',\n { line: 1, col: 1 });\n }\n\n return { version, end };\n }\n}","'use strict';\n\nimport { isIllegalControlCode } from '../util.js';\n\n/**\n * ControlCharValidator — single responsibility: detect illegal control\n * characters (0x00-0x08, 0x0B, 0x0C, 0x0E-0x1F) anywhere they appear as\n * literal characters in the document (element text, attribute values,\n * comments, CDATA, PI content, etc.).\n *\n * This check is always on — it is not gated by any option — since illegal\n * control characters make a document non-well-formed per the XML spec.\n */\nexport default class ControlCharValidator {\n /**\n * Scan `str` for the first illegal control character.\n * @param {string} str\n * @returns {number} local index (within `str`) of the first offending\n * character, or -1 if none found.\n */\n static findIllegalIndex(str) {\n const len = str.length;\n for (let i = 0; i < len; i++) {\n if (isIllegalControlCode(str.charCodeAt(i))) return i;\n }\n return -1;\n }\n\n /**\n * @param {string} str\n * @returns {boolean}\n */\n static hasIllegalChar(str) {\n return this.findIllegalIndex(str) !== -1;\n }\n\n /**\n * Convenience formatter for error messages.\n * @param {number} code - char code\n */\n static describe(code) {\n return `0x${code.toString(16).padStart(2, '0')}`;\n }\n}\n","'use strict';\n\nimport { getPositionFromMatch, isWhiteSpace } from '../util.js';\nimport ControlCharValidator from './ControlCharValidator.js';\n\n/**\n * Walk `attrStr` once, left to right, splitting it into attribute tokens.\n *\n * This replaces a regex that used to do the same job\n * (`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['\"])(([\\s\\S])*?)\\5)?`). That regex led with an\n * optional whitespace group followed by a required \"non-whitespace\" group.\n * On a long run of whitespace that never resolves into an attribute name\n * (e.g. a tag with thousands of trailing spaces before `>`), the engine\n * backtracks the whitespace group one character at a time before giving up\n * and moving to the next starting position — one full backtrack per\n * position, which is quadratic in the length of the run. A ~64KB tag made\n * entirely of spaces could stall the whole process for seconds.\n *\n * A single forward-only scan can never backtrack, so it can't be made slow\n * this way no matter how much whitespace the input contains — it's always\n * proportional to the length of the string, once.\n *\n * Each returned token mirrors the shape the old regex match array had, so\n * the validation logic below (which reads token[1]..token[6]) didn't need\n * to change:\n * token.startIndex - where this token begins in attrStr\n * token[1] - leading whitespace before the name\n * token[2] - the attribute name\n * token[3] - whitespace + '=' if present, else undefined\n * token[4] - marker (any defined value) if a quoted value was found\n * token[5] - the quote character used ('\"' or \"'\")\n * token[6] - the value's text, without the surrounding quotes\n *\n * A malformed leading character (e.g. a stray '=' with no name before it)\n * is simply skipped over, one character at a time — the same outcome the\n * old regex produced by failing to match at that position and retrying at\n * the next one.\n */\nfunction scanAttributeTokens(attrStr) {\n const tokens = [];\n const len = attrStr.length;\n let i = 0;\n\n while (i < len) {\n const tokenStart = i;\n\n // Leading whitespace before the name.\n while (i < len && isWhiteSpace(attrStr[i])) i++;\n if (i >= len) break; // trailing whitespace only — nothing left to read\n\n if (attrStr[i] === '=') {\n // No name before this '=' — not a valid attribute start. Move past\n // just this one character and try again from the next position.\n i = tokenStart + 1;\n continue;\n }\n\n const leadingWs = attrStr.slice(tokenStart, i);\n\n // Attribute name — everything up to the next whitespace or '='.\n const nameStart = i;\n while (i < len && !isWhiteSpace(attrStr[i]) && attrStr[i] !== '=') i++;\n const name = attrStr.slice(nameStart, i);\n\n // Optional whitespace + '='.\n let equalsGroup; // whitespace + '=' text, or undefined if absent\n let j = i;\n while (j < len && isWhiteSpace(attrStr[j])) j++;\n if (j < len && attrStr[j] === '=') {\n equalsGroup = attrStr.slice(i, j + 1);\n i = j + 1;\n }\n\n // Optional whitespace + quoted value.\n let quoteChar;\n let value;\n let k = i;\n while (k < len && isWhiteSpace(attrStr[k])) k++;\n if (k < len && (attrStr[k] === '\"' || attrStr[k] === \"'\")) {\n const valueStart = k + 1;\n const closeIdx = attrStr.indexOf(attrStr[k], valueStart);\n if (closeIdx !== -1) {\n quoteChar = attrStr[k];\n value = attrStr.slice(valueStart, closeIdx);\n i = closeIdx + 1;\n }\n // No closing quote found anywhere in the rest of the string — leave\n // quoteChar/value undefined, same as the old regex's group failing\n // to match a backreference-less run.\n }\n\n const token = { startIndex: tokenStart };\n token[1] = leadingWs;\n token[2] = name;\n token[3] = equalsGroup;\n token[4] = quoteChar !== undefined ? true : undefined;\n token[5] = quoteChar;\n token[6] = value;\n tokens.push(token);\n }\n\n return tokens;\n}\n\n/**\n * AttributeStringValidator — single responsibility: validate the raw\n * attribute-expression substring of a start tag (everything between the tag\n * name and the closing `>`/`/>`).\n *\n * Checks performed (always on):\n * - proper whitespace separation between attributes\n * - attribute names are valid QNames\n * - no duplicate attribute names\n * - `=` present without a value -> \"without value\"\n * - value present but not quoted -> UnquotedAttributeValue\n * - boolean attributes rejected unless `allowBooleanAttributes`\n * - illegal control characters inside the attribute value\n * - `xmlns:x=\"\"` only allowed in XML 1.1\n *\n * Checks performed (opt-in via `invalidCharSequence.attrLt`):\n * - literal `<` inside an attribute value\n */\nexport default class AttributeStringValidator {\n /**\n * @param {object} options - the full validation options object\n * @param {import('./XmlNameValidator.js').default} nameValidator\n * @param {string} xmlVersion\n */\n constructor(options, nameValidator, xmlVersion) {\n this.options = options;\n this.nameValidator = nameValidator;\n this.xmlVersion = xmlVersion;\n }\n\n /**\n * @param {string} attrStr\n * @returns {true | { err: { code: string, msg: string, line: number } }}\n */\n validate(attrStr) {\n const matches = scanAttributeTokens(attrStr);\n const attrNames = {};\n const checkAttrLt = this.options.invalidCharSequence?.attrLt === true;\n\n for (let i = 0; i < matches.length; i++) {\n const m = matches[i];\n\n if (m[1].length === 0) {\n return this._err('InvalidAttr', \"Attribute '\" + m[2] + \"' has no space in starting.\", m);\n }\n\n const hasEquals = m[3] !== undefined;\n const hasQuotedValue = m[4] !== undefined;\n\n if (hasEquals && !hasQuotedValue) {\n // Either `attr=` with nothing after it, or `attr=value` where value\n // isn't wrapped in quotes — XML requires every attribute value to be\n // quoted, so both shapes are rejected as UnquotedAttributeValue.\n return this._err(\n 'UnquotedAttributeValue',\n \"Attribute '\" + m[2] + \"' is without value.\",\n m\n );\n } else if (!hasEquals && !this.options.allowBooleanAttributes) {\n return this._err('InvalidAttr', \"boolean attribute '\" + m[2] + \"' is not allowed.\", m);\n }\n\n const attrName = m[2];\n\n if (!this.nameValidator.isValidQName(attrName)) {\n return this._err('InvalidAttr', \"Attribute '\" + attrName + \"' is an invalid name.\", m);\n }\n\n const attrValue = m[6] || '';\n\n if (hasQuotedValue) {\n const illegalIdx = ControlCharValidator.findIllegalIndex(attrValue);\n if (illegalIdx !== -1) {\n const code = attrValue.charCodeAt(illegalIdx);\n return this._err(\n 'IllegalCharacter',\n `Illegal control character ${ControlCharValidator.describe(code)} in attribute '${attrName}' value.`,\n m\n );\n }\n\n if (checkAttrLt && attrValue.indexOf('<') !== -1) {\n return this._err('InvalidAttr', `Attribute '${attrName}' value must not contain '<'.`, m);\n }\n }\n\n if (attrName.startsWith('xmlns:') && m[6] === '' && this.xmlVersion === '1.0') {\n return this._err(\n 'InvalidAttr',\n `Undeclaring the prefixed namespace ${attrName}=\"\" is only permitted in XML 1.1 documents.`,\n m\n );\n }\n\n if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {\n attrNames[attrName] = 1;\n } else {\n return this._err('InvalidAttr', \"Attribute '\" + attrName + \"' is repeated.\", m);\n }\n }\n\n return true;\n }\n\n _err(code, msg, match) {\n return { err: { code, msg, line: getPositionFromMatch(match) } };\n }\n}","'use strict';\n\nimport ControlCharValidator from './ControlCharValidator.js';\n\n/**\n * CommentCdataReader — single responsibility: recognize and consume\n * `<!-- comment -->`, `<![CDATA[ ... ]]>` and `<!DOCTYPE ...>` constructs\n * starting at a given index (which points at the `!`), returning the index\n * of the last character consumed (the closing `>`).\n *\n * DOCTYPE handling is fully delegated to a DocTypeValidator instance passed\n * to `read()` by the caller (SyntaxValidator), since DOCTYPE parsing is a\n * large, separate concern in its own right.\n *\n * Opt-in checks (via `invalidCharSequence`):\n * - `comment: true` -> literal `--` inside a comment's body is rejected\n * (besides the sequence immediately before the closing `-->`).\n */\nexport default class CommentCdataReader {\n constructor(options = {}) {\n this.checkCommentDashes = options.invalidCharSequence?.comment === true;\n }\n\n /**\n * @param {string} xmlData\n * @param {number} i - index of the '!' character\n * @param {import('./DocTypeValidator.js').default} docTypeValidator\n * @returns {number} index of the last character consumed\n */\n read(xmlData, i, docTypeValidator) {\n if (xmlData.length > i + 2 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {\n return this._readComment(xmlData, i);\n } else if (\n xmlData.length > i + 7 &&\n xmlData[i + 1] === 'D' &&\n xmlData[i + 2] === 'O' &&\n xmlData[i + 3] === 'C' &&\n xmlData[i + 4] === 'T' &&\n xmlData[i + 5] === 'Y' &&\n xmlData[i + 6] === 'P' &&\n xmlData[i + 7] === 'E'\n ) {\n return docTypeValidator.validateDocType(xmlData, i - 1);\n } else if (\n xmlData.length > i + 8 &&\n xmlData[i + 1] === '[' &&\n xmlData[i + 2] === 'C' &&\n xmlData[i + 3] === 'D' &&\n xmlData[i + 4] === 'A' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'A' &&\n xmlData[i + 7] === '['\n ) {\n return this._readCdata(xmlData, i);\n } else {\n throw new Error(\"Invalid construct starting with '<!'.\");\n }\n }\n\n _readComment(xmlData, i) {\n const bodyStart = i + 3;\n for (i = bodyStart; i < xmlData.length; i++) {\n if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n if (xmlData[i] !== '>') {\n throw new Error('Comment is not closed with \"-->\".');\n }\n\n const body = xmlData.substring(bodyStart, i - 2);\n const illegalIdx = ControlCharValidator.findIllegalIndex(body);\n if (illegalIdx !== -1) {\n const code = body.charCodeAt(illegalIdx);\n throw new Error(`Illegal control character ${ControlCharValidator.describe(code)} in comment.`);\n }\n if (this.checkCommentDashes && body.indexOf('--') !== -1) {\n throw new Error(`Comment must not contain '--'.`);\n }\n\n return i;\n }\n\n _readCdata(xmlData, i) {\n const bodyStart = i + 8;\n for (i = bodyStart; i < xmlData.length; i++) {\n if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n if (xmlData[i] !== '>') {\n throw new Error('CDATA section is not closed with \"]]>\".');\n }\n\n const body = xmlData.substring(bodyStart, i - 2);\n const illegalIdx = ControlCharValidator.findIllegalIndex(body);\n if (illegalIdx !== -1) {\n const code = body.charCodeAt(illegalIdx);\n throw new Error(`Illegal control character ${ControlCharValidator.describe(code)} in CDATA section.`);\n }\n\n return i;\n }\n}\n","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}, data) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n this.data = data;\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","import ExpressionSet from \"./ExpressionSet.js\";\n\n/**\n * MatcherView - A lightweight read-only view over a Matcher's internal state.\n *\n * Created once by Matcher and reused across all callbacks. Holds a direct\n * reference to the parent Matcher so it always reflects current parser state\n * with zero copying or freezing overhead.\n *\n * Users receive this via {@link Matcher#readOnly} or directly from parser\n * callbacks. It exposes all query and matching methods but has no mutation\n * methods — misuse is caught at the TypeScript level rather than at runtime.\n *\n * @example\n * const matcher = new Matcher();\n * const view = matcher.readOnly();\n *\n * matcher.push(\"root\", {});\n * view.getCurrentTag(); // \"root\"\n * view.getDepth(); // 1\n */\nexport class MatcherView {\n /**\n * @param {Matcher} matcher - The parent Matcher instance to read from.\n */\n constructor(matcher) {\n this._matcher = matcher;\n }\n\n /**\n * Get the path separator used by the parent matcher.\n * @returns {string}\n */\n get separator() {\n return this._matcher.separator;\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return undefined;\n return path[path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return false;\n const current = path[path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {*}\n */\n getAnyParentAttr(attrName) {\n return this._matcher.getAnyParentAttr(attrName);\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n return this._matcher.hasAnyParentAttr(attrName);\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this._matcher.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n return this._matcher.toString(separator, includeNamespace);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this._matcher.path.map(n => n.tag);\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n return this._matcher.matches(expression);\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this._matcher);\n }\n}\n\n/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions.\n *\n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n *\n * Use {@link Matcher#readOnly} to obtain a {@link MatcherView} safe to pass to\n * user callbacks — it always reflects current state with no Proxy overhead.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n *\n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\nexport default class Matcher {\n /**\n * Create a new Matcher.\n * @param {Object} [options={}]\n * @param {string} [options.separator='.'] - Default path separator\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag, values, position, counter, namespace? }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n this._pathStringCache = null;\n this._view = new MatcherView(this);\n\n // Kept-attribute stack: only populated when push() is called with options.keep.\n this._keptAttrs = [];\n }\n\n /**\n * Push a new tag onto the path.\n * @param {string} tagName\n * @param {Object|null} [attrValues=null]\n * @param {string|null} [namespace=null]\n * @param {Object|null} [options=null]\n * @param {string[]} [options.keep] - Names of attributes (from attrValues)\n */\n push(tagName, attrValues = null, namespace = null, options = null) {\n this._pathStringCache = null;\n\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n this.path[this.path.length - 1].values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n let level = this.siblingStacks[currentLevel];\n if (!level) {\n // `counts` tells same-name siblings apart (the \"counter\" — nth <item>\n // among other <item>s). `total` is every child seen at this level so\n // far, kept as a running number instead of re-added from `counts` on\n // every push — a parent with many differently-named children would\n // otherwise cost more per child the more distinct names it has.\n level = { counts: new Map(), total: 0 };\n this.siblingStacks[currentLevel] = level;\n }\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = level.counts.get(siblingKey) || 0;\n\n // Position = total children at this level seen before this one.\n const position = level.total;\n\n // Update sibling count for this tag, and the level's running total.\n level.counts.set(siblingKey, counter + 1);\n level.total++;\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n\n // Depth of the node we just pushed (1-based, matches this.path.length)\n const depth = this.path.length;\n\n // Copy only the requested attributes into the kept-attrs stack. This is\n // the one part of push() whose cost scales with input (O(keep.length))\n // rather than being O(1) — by design, since the caller is explicitly\n // opting in for specific attribute names. No options/keep => zero added\n // cost beyond the two property reads below.\n const keep = options !== null ? options.keep : null;\n if (keep !== null && keep !== undefined && keep.length > 0 && attrValues) {\n for (let i = 0; i < keep.length; i++) {\n const name = keep[i];\n if (attrValues[name] !== undefined) {\n this._keptAttrs.push({ depth, name, value: attrValues[name] });\n }\n }\n }\n }\n\n /**\n * Pop the last tag from the path.\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) return undefined;\n this._pathStringCache = null;\n\n const node = this.path.pop();\n\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n // Drop any kept attributes that belonged to the popped node (or deeper).\n // _keptAttrs is depth-ordered (push only ever appends increasing depths),\n // so this is a backward scan that stops at the first surviving entry —\n // typically O(1) since kept attrs are rare by design.\n const poppedDepth = this.path.length + 1;\n while (\n this._keptAttrs.length > 0 &&\n this._keptAttrs[this._keptAttrs.length - 1].depth >= poppedDepth\n ) {\n this._keptAttrs.pop();\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values.\n * Useful when attributes are parsed after push.\n * @param {Object} attrValues\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n }\n }\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n return this.path[this.path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * Unlike getAttrValue(), this works regardless of how deep the path has\n * gone since the attribute was pushed — but only for attribute names that\n * were explicitly marked with `keep` at push time. Cost is proportional to\n * the number of currently-kept attributes (typically 0-3), not path depth.\n * @param {string} attrName\n * @returns {*} the value, or undefined if no ancestor kept this attribute\n */\n getAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return kept[i].value;\n }\n return undefined;\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return true;\n }\n return false;\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty.\n */\n reset() {\n this._pathStringCache = null;\n this.path = [];\n this.siblingStacks = [];\n this._keptAttrs = [];\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n return this._matchSimple(segments);\n }\n\n /**\n * @private\n */\n _matchSimple(segments) {\n if (this.path.length !== segments.length) {\n return false;\n }\n\n for (let i = 0; i < segments.length; i++) {\n if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1;\n let segIdx = segments.length - 1;\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n segIdx--;\n\n if (segIdx < 0) {\n return true;\n }\n\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n return segIdx < 0;\n }\n\n /**\n * @private\n */\n _matchSegment(segment, node, isCurrentNode) {\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n if (segment.namespace !== undefined) {\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n if (segment.attrValue !== undefined) {\n if (String(node.values[segment.attrName]) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth' && counter !== segment.positionValue) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this);\n }\n\n /**\n * Create a snapshot of current state.\n * @returns {Object}\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level),\n keptAttrs: this._keptAttrs.map(entry => ({ ...entry }))\n };\n }\n\n /**\n * Restore state from snapshot.\n * @param {Object} snapshot\n */\n restore(snapshot) {\n this._pathStringCache = null;\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level);\n this._keptAttrs = (snapshot.keptAttrs || []).map(entry => ({ ...entry }));\n }\n\n /**\n * Return the read-only {@link MatcherView} for this matcher.\n *\n * The same instance is returned on every call — no allocation occurs.\n * It always reflects the current parser state and is safe to pass to\n * user callbacks without risk of accidental mutation.\n *\n * @returns {MatcherView}\n *\n * @example\n * const view = matcher.readOnly();\n * // pass view to callbacks — it stays in sync automatically\n * view.matches(expr); // ✓\n * view.getCurrentTag(); // ✓\n * // view.push(...) // ✗ method does not exist — caught by TypeScript\n */\n readOnly() {\n return this._view;\n }\n}\n","'use strict';\n\nimport { Expression, Matcher } from 'path-expression-matcher';\n\n/**\n * SkipTagMatcher — single responsibility: decide whether a tag currently\n * being opened should be excluded from validation, based on a list of path\n * expressions supplied via `options.skipTags` (same expression syntax as\n * `path-expression-matcher`, e.g. `\"..script\"`, `\"root.data.raw\"`,\n * `\"row[type=raw]\"`).\n *\n * Skip decisions are only ever made for tags reached through normal\n * (non-skipped) parsing — once a match is found, the caller\n * (`SyntaxValidator`) consumes the whole matched subtree as opaque raw text\n * (mirroring how CDATA is treated) and calls `pop()` once for that tag, so\n * this matcher's path stack never needs to represent the skipped subtree's\n * internal structure.\n */\nexport default class SkipTagMatcher {\n /**\n * @param {Array<string>} [skipTags]\n */\n constructor(skipTags = []) {\n this._expressions = skipTags.map((expr) => new Expression(expr));\n this._matcher = new Matcher();\n this._enabled = this._expressions.length > 0;\n }\n\n get enabled() {\n return this._enabled;\n }\n\n /**\n * Push the tag currently being opened onto the path and report whether it\n * (and its whole subtree) should be skipped.\n * @param {string} tagName\n * @param {Record<string,string>} [rawAttributes] - for `[attr=value]` conditions\n * @returns {boolean}\n */\n push(tagName, rawAttributes) {\n if (!this._enabled) return false;\n this._matcher.push(tagName, rawAttributes || null);\n for (let i = 0; i < this._expressions.length; i++) {\n if (this._matcher.matches(this._expressions[i])) return true;\n }\n return false;\n }\n\n /** Pop the most recently pushed tag off the path (tag fully closed). */\n pop() {\n if (!this._enabled) return;\n this._matcher.pop();\n }\n}\n","'use strict';\n\n/**\n * RawSubtreeReader — single responsibility: given the index right after a\n * skipped tag's opening `>`, consume raw characters until the literal\n * closing tag `</tagName>` (optional whitespace before `>`) is found.\n *\n * Deliberately does NOT try to parse anything in between: skipped content is\n * assumed to be non-XML or otherwise not meant to be validated (script/style\n * bodies, vendor blobs, etc.), so it may contain unmatched `<`, quotes, or\n * anything else. This mirrors how CDATA content is treated — opaque text,\n * found by looking for its one true terminator.\n *\n * Same-name tags never \"nest\" under this strategy — the first `</tagName>`\n * found ends the skip, matching the common script/style use case. Use a more\n * specific skip expression (e.g. targeting only the outermost occurrence) if\n * your skipped content can itself contain literal `</tagName>` text.\n */\nexport default class RawSubtreeReader {\n /**\n * @param {string} xmlData\n * @param {string} tagName\n * @param {number} i - index right after the opening tag's '>'\n * @returns {number} index of the matching closing tag's own '>'\n */\n static consume(xmlData, tagName, i) {\n const needle = '</' + tagName;\n let searchFrom = i;\n\n while (true) {\n const idx = xmlData.indexOf(needle, searchFrom);\n if (idx === -1) {\n throw new Error(`Unclosed skipped tag '${tagName}'.`);\n }\n\n let j = idx + needle.length;\n while (j < xmlData.length && /\\s/.test(xmlData[j])) j++;\n\n if (xmlData[j] === '>') {\n return j;\n }\n // Not actually a match (e.g. tagName is a prefix of a longer name) —\n // keep looking further in the string.\n searchFrom = idx + needle.length;\n }\n }\n}\n","import { name as isXmlName } from 'xml-naming';\n\nconst defaultOptions = {\n maxEntityCount: Infinity,\n maxEntitySize: Infinity,\n};\n\nexport default class DocTypeValidator {\n constructor(options, xmlVersion = '1.0') {\n this.options = Object.assign({}, defaultOptions, options);\n this.suppressValidationErr = !options;\n this.xmlVersion = xmlVersion;\n }\n\n validateDocType(xmlData, i) {\n if (\n xmlData[i + 3] !== 'O' ||\n xmlData[i + 4] !== 'C' ||\n xmlData[i + 5] !== 'T' ||\n xmlData[i + 6] !== 'Y' ||\n xmlData[i + 7] !== 'P' ||\n xmlData[i + 8] !== 'E'\n ) {\n throw new Error(`Invalid Tag instead of DOCTYPE`);\n }\n\n i += 9;\n let angleBracketsCount = 1;\n let hasBody = false;\n let comment = false;\n let quoteChar = null; // tracks an open SYSTEM/PUBLIC literal before the '[' body\n let entityCount = 0;\n\n for (; i < xmlData.length; i++) {\n // Inside a quoted external-identifier literal — '<' and '>' are\n // plain data here per the XML grammar, not DOCTYPE structure.\n if (quoteChar !== null) {\n if (xmlData[i] === quoteChar) quoteChar = null;\n continue;\n }\n if (!hasBody && !comment && (xmlData[i] === '\"' || xmlData[i] === \"'\")) {\n quoteChar = xmlData[i];\n continue;\n }\n\n if (xmlData[i] === '<' && !comment) {\n if (hasBody && hasSeq(xmlData, '!ENTITY', i)) {\n i += 7;\n [entityCount, i] = this._validateEntityExp(xmlData, i + 1, entityCount);\n // continue;\n } else if (hasBody && hasSeq(xmlData, '!ELEMENT', i)) {\n i += 8;\n i = this._validateElementExp(xmlData, i + 1);\n // continue;\n } else if (hasBody && hasSeq(xmlData, '!ATTLIST', i)) {\n i += 8;\n i = this._validateAttlistExp(xmlData, i + 1);\n continue;\n } else if (hasBody && hasSeq(xmlData, '!NOTATION', i)) {\n i += 9;\n i = this._validateNotationExp(xmlData, i + 1);\n // continue;\n } else if (hasSeq(xmlData, '!--', i)) {\n comment = true;\n } else {\n throw new Error(`Invalid DOCTYPE`);\n }\n angleBracketsCount++;\n } else if (xmlData[i] === '>') {\n if (comment) {\n if (xmlData[i - 1] === '-' && xmlData[i - 2] === '-') {\n comment = false;\n angleBracketsCount--;\n }\n } else {\n angleBracketsCount--;\n }\n if (angleBracketsCount === 0) break;\n } else if (xmlData[i] === '[') {\n hasBody = true;\n }\n }\n\n if (quoteChar !== null || angleBracketsCount !== 0) {\n throw new Error(`Unclosed DOCTYPE`);\n }\n\n return i;\n }\n\n _validateEntityExp(xmlData, i, entityCount) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate entity name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i]) && xmlData[i] !== '\"' && xmlData[i] !== \"'\") {\n i++;\n }\n const entityName = xmlData.substring(nameStart, i);\n validateName(entityName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Check for unsupported constructs\n if (!this.suppressValidationErr) {\n if (xmlData.substring(i, i + 6).toUpperCase() === 'SYSTEM') {\n throw new Error('External entities are not supported');\n } else if (xmlData[i] === '%') {\n throw new Error('Parameter entities are not supported');\n }\n }\n\n // Read and validate the entity value (validates quoting + size limit)\n let entityValue;\n [i, entityValue] = readQuotedValue(xmlData, i, 'entity');\n\n const { maxEntitySize, maxEntityCount } = this.options;\n\n if (entityValue.length > maxEntitySize) {\n throw new Error(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${maxEntitySize})`\n );\n }\n\n // Only count non-parameter entities (parameter entities contain '&')\n if (entityValue.indexOf('&') === -1) {\n entityCount++;\n if (entityCount > maxEntityCount) {\n throw new Error(\n `Entity count (${entityCount}) exceeds maximum allowed (${maxEntityCount})`\n );\n }\n }\n\n return [entityCount, i - 1];\n }\n\n _validateNotationExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate notation name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const notationName = xmlData.substring(nameStart, i);\n !this.suppressValidationErr && validateName(notationName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n const identifierType = xmlData.substring(i, i + 6).toUpperCase();\n if (!this.suppressValidationErr && identifierType !== 'SYSTEM' && identifierType !== 'PUBLIC') {\n throw new Error(`Expected SYSTEM or PUBLIC, found \"${identifierType}\"`);\n }\n i += identifierType.length;\n i = skipWhitespace(xmlData, i);\n\n if (identifierType === 'PUBLIC') {\n [i] = readQuotedValue(xmlData, i, 'publicIdentifier');\n i = skipWhitespace(xmlData, i);\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n [i] = readQuotedValue(xmlData, i, 'systemIdentifier');\n }\n } else if (identifierType === 'SYSTEM') {\n [i] = readQuotedValue(xmlData, i, 'systemIdentifier');\n }\n\n return --i;\n }\n\n _validateElementExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read and validate element name\n const nameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const elementName = xmlData.substring(nameStart, i);\n if (!this.suppressValidationErr && !isXmlName(elementName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid element name: \"${elementName}\"`);\n }\n\n i = skipWhitespace(xmlData, i);\n\n // Validate content model keyword or group\n if (xmlData[i] === 'E' && hasSeq(xmlData, 'MPTY', i)) {\n i += 4;\n } else if (xmlData[i] === 'A' && hasSeq(xmlData, 'NY', i)) {\n i += 2;\n } else if (xmlData[i] === '(') {\n i++;\n while (i < xmlData.length && xmlData[i] !== ')') i++;\n if (xmlData[i] !== ')') throw new Error('Unterminated content model');\n } else if (!this.suppressValidationErr) {\n throw new Error(`Invalid Element Expression, found \"${xmlData[i]}\"`);\n }\n\n return i;\n }\n\n _validateAttlistExp(xmlData, i) {\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n const elemNameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const elementName = xmlData.substring(elemNameStart, i);\n validateName(elementName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Read attribute name\n const attrNameStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const attributeName = xmlData.substring(attrNameStart, i);\n validateName(attributeName, this.xmlVersion);\n\n i = skipWhitespace(xmlData, i);\n\n // Read attribute type\n if (xmlData.substring(i, i + 8).toUpperCase() === 'NOTATION') {\n i += 8;\n i = skipWhitespace(xmlData, i);\n if (xmlData[i] !== '(') throw new Error(`Expected '(', found \"${xmlData[i]}\"`);\n i++;\n while (i < xmlData.length && xmlData[i] !== ')') {\n const notationStart = i;\n while (i < xmlData.length && xmlData[i] !== '|' && xmlData[i] !== ')') i++;\n const notation = xmlData.substring(notationStart, i).trim();\n validateName(notation, this.xmlVersion);\n if (xmlData[i] === '|') {\n i++;\n i = skipWhitespace(xmlData, i);\n }\n }\n if (xmlData[i] !== ')') throw new Error('Unterminated list of notations');\n i++;\n } else {\n const typeStart = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) i++;\n const attributeType = xmlData.substring(typeStart, i);\n const validTypes = ['CDATA', 'ID', 'IDREF', 'IDREFS', 'ENTITY', 'ENTITIES', 'NMTOKEN', 'NMTOKENS'];\n if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {\n throw new Error(`Invalid attribute type: \"${attributeType}\"`);\n }\n }\n\n i = skipWhitespace(xmlData, i);\n\n // Read default value declaration\n if (xmlData.substring(i, i + 9).toUpperCase() === '#REQUIRED') {\n i += 9;\n } else if (xmlData.substring(i, i + 8).toUpperCase() === '#IMPLIED') {\n i += 8;\n } else {\n [i] = readQuotedValue(xmlData, i, 'ATTLIST default value');\n }\n\n return i;\n }\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction skipWhitespace(data, index) {\n while (index < data.length && /\\s/.test(data[index])) index++;\n return index;\n}\n\nfunction hasSeq(data, seq, i) {\n for (let j = 0; j < seq.length; j++) {\n if (seq[j] !== data[i + j + 1]) return false;\n }\n return true;\n}\n\nfunction validateName(name, xmlVersion = '1.0') {\n if (!isXmlName(name, { xmlVersion })) throw new Error(`Invalid entity name \"${name}\"`);\n}\n\n/**\n * Read a single- or double-quoted string and return [nextIndex, value].\n */\nfunction readQuotedValue(xmlData, i, type) {\n const startChar = xmlData[i];\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new Error(`Expected quoted string for ${type}, found \"${startChar}\"`);\n }\n i++;\n const start = i;\n while (i < xmlData.length && xmlData[i] !== startChar) i++;\n if (xmlData[i] !== startChar) throw new Error(`Unterminated ${type} value`);\n const value = xmlData.substring(start, i);\n return [++i, value];\n}","'use strict';\n\nimport ValidationError from './ValidationError.js';\nimport { isWhiteSpace, getLineNumberForPosition, isIllegalControlCode } from './util.js';\nimport XmlNameValidator from './validators/XmlNameValidator.js';\nimport XmlDeclarationValidator from './validators/XmlDeclarationValidator.js';\nimport AttributeStringValidator from './validators/AttributeStringValidator.js';\nimport CommentCdataReader from './validators/CommentCdataReader.js';\nimport ControlCharValidator from './validators/ControlCharValidator.js';\nimport SkipTagMatcher from './validators/SkipTagMatcher.js';\nimport RawSubtreeReader from './validators/RawSubtreeReader.js';\nimport DocTypeValidator from './validators/DocTypeValidator.js';\n\nconst defaultOptions = {\n allowBooleanAttributes: false,\n unpairedTags: [],\n docType: {\n maxEntityCount: Infinity,\n maxEntitySize: Infinity,\n },\n invalidCharSequence: {\n comment: false, // '--' inside a comment body\n tagValue: false, // ']]>' inside element text content\n attrLt: false, // '<' inside an attribute value\n },\n xmlDeclaraion: {\n optional: true,\n argPosition: true,\n },\n multipleRoots: true,\n skipTags: [],\n};\n\n/**\n * SyntaxValidator — validates that an XML document is well-formed.\n *\n * This is an orchestrator: it owns the top-level character scan and the\n * open/close tag stack, and delegates every other concern to a focused,\n * single-responsibility collaborator:\n *\n * - XmlDeclarationValidator — the `<?xml ... ?>` prolog\n * - DocTypeValidator — `<!DOCTYPE ...>`\n * - CommentCdataReader — `<!-- -->` and `<![CDATA[ ]]>`\n * - AttributeStringValidator — a tag's attribute expression\n * - XmlNameValidator — element/attribute/PI name well-formedness\n * - ControlCharValidator — illegal control characters (always on)\n * - SkipTagMatcher / RawSubtreeReader — `skipTags` support\n *\n * Usage: `new SyntaxValidator(options).validate(xmlData)` or the static\n * convenience `SyntaxValidator.validate(xmlData, options)`.\n */\nexport default class SyntaxValidator {\n constructor(options) {\n this.options = mergeOptions(defaultOptions, options);\n }\n\n /** Static convenience wrapper — matches the historical fxv v1 API. */\n static validate(xmlData, options) {\n return new SyntaxValidator(options).validate(xmlData);\n }\n\n validate(xmlData) {\n const options = this.options;\n\n const tags = [];\n let tagFound = false;\n let reachedRoot = false;\n let docTypeSeen = false;\n\n if (xmlData[0] === '\\ufeff') {\n xmlData = xmlData.substr(1);\n }\n\n const throwError = (code, message, lineNumber) => {\n throw new ValidationError(message, code, lineNumber.line || lineNumber, lineNumber.col);\n };\n\n const declValidator = new XmlDeclarationValidator(options.xmlDeclaraion, throwError);\n const declResult = declValidator.validate(xmlData);\n const xmlVersion = declResult.version;\n xmlData = xmlData.substring(declResult.end);\n\n const docTypeValidator = new DocTypeValidator(options.docType, xmlVersion);\n const commentCdataReader = new CommentCdataReader(options);\n const nameValidator = new XmlNameValidator(xmlVersion);\n const attrValidator = new AttributeStringValidator(options, nameValidator, xmlVersion);\n const skipMatcher = new SkipTagMatcher(options.skipTags);\n\n const checkTagValueSeq = options.invalidCharSequence?.tagValue === true;\n\n const checkDocTypePlacement = (startBang) => {\n const isDoctype = xmlData.substring(startBang, startBang + 9).toUpperCase() === '<!DOCTYPE';\n if (!isDoctype) return;\n if (docTypeSeen) {\n throwError('InvalidXml', 'Multiple DOCTYPE declarations found.', getLineNumberForPosition(xmlData, startBang));\n }\n if (tagFound || tags.length > 0) {\n throwError('InvalidXml', 'DOCTYPE must appear before the root element.', getLineNumberForPosition(xmlData, startBang));\n }\n docTypeSeen = true;\n };\n\n // Character data (text, entity references, CDATA) is only legal inside an\n // element. At depth 0 it is \"extra\" data, whether before or after the root.\n const checkTopLevelText = (pos) => {\n if (tags.length === 0) {\n throwError('InvalidXml', 'Extra text at the end', getLineNumberForPosition(xmlData, pos));\n }\n };\n\n const checkCdataPlacement = (startBang) => {\n if (tags.length === 0 && xmlData.startsWith('<![CDATA[', startBang)) {\n throwError(\n 'InvalidXml',\n 'CDATA section is not allowed outside the root element.',\n getLineNumberForPosition(xmlData, startBang)\n );\n }\n };\n\n for (let i = 0; i < xmlData.length; i++) {\n if (xmlData[i] === '<' && xmlData[i + 1] === '?') {\n i += 2;\n i = this._readPI(xmlData, i, nameValidator, throwError);\n } else if (xmlData[i] === '<') {\n let tagStartPos = i;\n i++;\n\n if (xmlData[i] === '!') {\n checkCdataPlacement(tagStartPos);\n try {\n checkDocTypePlacement(tagStartPos);\n i = commentCdataReader.read(xmlData, i, docTypeValidator);\n } catch (err) {\n throwError('InvalidDocType', err.message, getLineNumberForPosition(xmlData, i - 1));\n }\n continue;\n } else {\n let closingTag = false;\n if (xmlData[i] === '/') {\n closingTag = true;\n i++;\n }\n\n let tagName = '';\n for (;\n i < xmlData.length &&\n xmlData[i] !== '>' &&\n xmlData[i] !== ' ' &&\n xmlData[i] !== '\\t' &&\n xmlData[i] !== '\\n' &&\n xmlData[i] !== '\\r';\n i++\n ) {\n tagName += xmlData[i];\n }\n tagName = tagName.trim();\n\n if (tagName[tagName.length - 1] === '/') {\n tagName = tagName.substring(0, tagName.length - 1);\n i--;\n }\n\n if (!nameValidator.isValidQName(tagName)) {\n let msg;\n if (tagName.trim().length === 0) {\n msg = \"Invalid space after '<'.\";\n } else {\n msg = \"Tag '\" + tagName + \"' is an invalid name.\";\n }\n throwError('InvalidTag', msg, getLineNumberForPosition(xmlData, tagStartPos));\n }\n\n const result = this._readAttributeStr(xmlData, i);\n if (result === false) {\n throwError('InvalidAttr', \"Attributes for '\" + tagName + \"' have open quote.\", getLineNumberForPosition(xmlData, i));\n }\n let attrStr = result.value;\n i = result.index;\n\n if (attrStr[attrStr.length - 1] === '/') {\n const attrStrStart = i - attrStr.length;\n attrStr = attrStr.substring(0, attrStr.length - 1);\n const isValid = attrValidator.validate(attrStr);\n if (isValid === true) {\n if (reachedRoot === true && !options.multipleRoots) {\n throwError('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, tagStartPos));\n }\n tagFound = true;\n if (tags.length === 0) {\n reachedRoot = true;\n }\n skipMatcher.push(tagName);\n skipMatcher.pop();\n } else {\n throwError(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));\n }\n } else if (closingTag) {\n if (!result.tagClosed) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' doesn't have proper closing.\", getLineNumberForPosition(xmlData, i));\n } else if (attrStr.trim().length > 0) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' can't have attributes or invalid starting.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else if (tags.length === 0) {\n throwError('InvalidTag', \"Closing tag '\" + tagName + \"' has not been opened.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else {\n const otg = tags.pop();\n if (tagName !== otg.tagName) {\n let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);\n throwError(\n 'InvalidTag',\n \"Expected closing tag '\" + otg.tagName + \"' (opened in line \" + openPos.line + \", col \" + openPos.col + \") instead of closing tag '\" + tagName + \"'.\",\n getLineNumberForPosition(xmlData, tagStartPos)\n );\n }\n skipMatcher.pop();\n if (tags.length === 0) {\n reachedRoot = true;\n }\n }\n } else {\n // Opening tag — decide whether this subtree should be skipped\n // from validation entirely before validating its attributes.\n const skip = skipMatcher.push(tagName);\n\n if (!skip) {\n const isValid = attrValidator.validate(attrStr);\n if (isValid !== true) {\n throwError(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));\n }\n }\n\n if (reachedRoot === true && !options.multipleRoots) {\n throwError('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));\n } else if (options.unpairedTags.indexOf(tagName) !== -1) {\n skipMatcher.pop();\n // An unpaired tag at depth 0 is a complete top-level element.\n if (tags.length === 0) reachedRoot = true;\n } else if (skip) {\n // Consume the whole skipped subtree now, as opaque raw text,\n // then fall through to the text scan below so that whatever\n // follows the skipped tag's closing '>' (text, entities,\n // comments, the next tag) is handled like after any other tag.\n i = RawSubtreeReader.consume(xmlData, tagName, i + 1);\n skipMatcher.pop();\n if (tags.length === 0) reachedRoot = true;\n } else {\n tags.push({ tagName, tagStartPos });\n }\n tagFound = true;\n }\n\n //skip tag text value\n //It may include comments and CDATA value\n let prevTwo = '';\n for (i++; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n if (xmlData[i + 1] === '!') {\n i++;\n checkCdataPlacement(i - 1);\n try {\n checkDocTypePlacement(i - 1);\n i = commentCdataReader.read(xmlData, i, docTypeValidator);\n } catch (err) {\n throwError('InvalidDocType', err.message, getLineNumberForPosition(xmlData, i - 1));\n }\n prevTwo = '';\n continue;\n } else if (xmlData[i + 1] === '?') {\n i += 2;\n i = this._readPI(xmlData, i, nameValidator, throwError);\n prevTwo = '';\n } else {\n break;\n }\n } else if (xmlData[i] === '&') {\n checkTopLevelText(i);\n const afterAmp = this._validateAmpersand(xmlData, i);\n if (afterAmp === -1)\n throwError('InvalidChar', \"char '&' is not expected.\", getLineNumberForPosition(xmlData, i));\n i = afterAmp;\n prevTwo = '';\n } else {\n const code = xmlData.charCodeAt(i);\n if (isIllegalControlCode(code)) {\n throwError('IllegalCharacter', `Illegal control character ${ControlCharValidator.describe(code)} in document content.`, getLineNumberForPosition(xmlData, i));\n }\n if (tags.length === 0 && !isWhiteSpace(xmlData[i])) {\n throwError('InvalidXml', 'Extra text at the end', getLineNumberForPosition(xmlData, i));\n }\n if (checkTagValueSeq) {\n prevTwo = (prevTwo + xmlData[i]).slice(-2);\n if (prevTwo === ']]' && xmlData[i + 1] === '>') {\n throwError('InvalidXml', \"Element text content must not contain ']]>'.\", getLineNumberForPosition(xmlData, i - 1));\n }\n }\n }\n } //end of reading tag text value\n if (xmlData[i] === '<') {\n i--;\n }\n }\n } else {\n if (isWhiteSpace(xmlData[i])) {\n continue;\n }\n if (reachedRoot) {\n throwError('InvalidXml', \"Extra text at the end: '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n throwError('InvalidChar', \"char '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n }\n\n if (!tagFound) {\n throwError('InvalidXml', 'Start tag expected.', { line: 1, col: 1 });\n } else if (tags.length === 1) {\n throwError('InvalidTag', \"Unclosed tag '\" + tags[0].tagName + \"'.\", getLineNumberForPosition(xmlData, tags[0].tagStartPos));\n } else if (tags.length > 0) {\n throwError('InvalidXml', \"Invalid '\" +\n JSON.stringify(tags.map(t => t.tagName)).replace(/\\r?\\n/g, '') +\n \"' found.\", { line: 1, col: 1 });\n }\n\n return true;\n }\n\n _readPI(xmlData, i, nameValidator, throwError) {\n const piStart = i;\n const nameStart = i;\n let nameValidated = false;\n\n for (; i < xmlData.length; i++) {\n const ch = xmlData[i];\n\n if (!nameValidated && (ch === ' ' || ch === '?')) {\n const piName = xmlData.substr(nameStart, i - nameStart);\n nameValidated = true;\n\n if (piName.toLowerCase() === 'xml') {\n throwError(\n 'InvalidXml',\n 'XML declaration allowed only at the start of the document.',\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n } else if (!nameValidator.isValidName(piName)) {\n throwError(\n 'InvalidXml',\n `Processing instruction target \"${piName}\" is not a valid XML Name.`,\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n }\n }\n\n if (ch === '?' && xmlData[i + 1] === '>') {\n i++;\n return i;\n }\n }\n\n throwError(\n 'InvalidXml',\n 'Processing instruction is not closed with \"?>\".',\n getLineNumberForPosition(xmlData, piStart - 2)\n );\n }\n\n _readAttributeStr(xmlData, i) {\n let attrStr = '';\n let startChar = '';\n let tagClosed = false;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n if (startChar === '') {\n startChar = xmlData[i];\n } else if (startChar !== xmlData[i]) {\n // opposite quote inside value — allowed\n } else {\n startChar = '';\n }\n } else if (xmlData[i] === '>') {\n if (startChar === '') {\n tagClosed = true;\n break;\n }\n }\n attrStr += xmlData[i];\n }\n if (startChar !== '') {\n return false;\n }\n return { value: attrStr, index: i, tagClosed };\n }\n\n _validateNumberAmpersand(xmlData, i) {\n let re = /\\d/;\n if (xmlData[i] === 'x') {\n i++;\n re = /[\\da-fA-F]/;\n }\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === ';') return i;\n if (!xmlData[i].match(re)) break;\n }\n return -1;\n }\n\n _validateAmpersand(xmlData, i) {\n i++;\n if (xmlData[i] === ';') return -1;\n if (xmlData[i] === '#') {\n i++;\n return this._validateNumberAmpersand(xmlData, i);\n }\n let count = 0;\n for (; i < xmlData.length; i++, count++) {\n if (xmlData[i].match(/\\w/) && count < 20) continue;\n if (xmlData[i] === ';') break;\n return -1;\n }\n return i;\n }\n}\n\nfunction mergeOptions(defaults, options) {\n const merged = Object.assign({}, defaults, options);\n merged.docType = Object.assign({}, defaults.docType, options?.docType);\n merged.invalidCharSequence = Object.assign({}, defaults.invalidCharSequence, options?.invalidCharSequence);\n merged.xmlDeclaraion = Object.assign(\n {},\n defaults.xmlDeclaraion,\n options?.xmlDeclaraion,\n options?.xmlDeclaration // accept the correctly-spelled alias too\n );\n merged.skipTags = options?.skipTags || defaults.skipTags;\n return merged;\n}\n\nexport { SyntaxValidator };\n","/**\n * ExpressionSet - An indexed collection of Expressions for efficient bulk matching\n *\n * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes\n * them at insertion time by depth and terminal tag name. At match time, only\n * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)\n * lookup plus O(small bucket) matches.\n *\n * Three buckets are maintained:\n * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)\n * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)\n * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)\n *\n * @example\n * import { Expression, ExpressionSet } from 'fast-xml-tagger';\n *\n * // Build once at config time\n * const stopNodes = new ExpressionSet();\n * stopNodes.add(new Expression('root.users.user'));\n * stopNodes.add(new Expression('root.config.setting'));\n * stopNodes.add(new Expression('..script'));\n *\n * // Query on every tag — hot path\n * if (stopNodes.matchesAny(matcher)) { ... }\n */\nexport default class ExpressionSet {\n constructor() {\n /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */\n this._byDepthAndTag = new Map();\n\n /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */\n this._wildcardByDepth = new Map();\n\n /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */\n this._deepWildcards = [];\n\n /** @type {Map<string, import('./Expression.js').default[]>} terminalTag → deep wildcard expressions */\n this._deepByTerminalTag = new Map();\n\n /** @type {Set<string>} pattern strings already added — used for deduplication */\n this._patterns = new Set();\n\n /** @type {boolean} whether the set is sealed against further additions */\n this._sealed = false;\n }\n\n /**\n * Add an Expression to the set.\n * Duplicate patterns (same pattern string) are silently ignored.\n *\n * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance\n * @returns {this} for chaining\n * @throws {TypeError} if called after seal()\n *\n * @example\n * set.add(new Expression('root.users.user'));\n * set.add(new Expression('..script'));\n */\n add(expression) {\n if (this._sealed) {\n throw new TypeError(\n 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'\n );\n }\n\n // Deduplicate by pattern string\n if (this._patterns.has(expression.pattern)) return this;\n this._patterns.add(expression.pattern);\n\n if (expression.hasDeepWildcard()) {\n const lastSeg = expression.segments[expression.segments.length - 1];\n if (lastSeg && lastSeg.type !== 'deep-wildcard' && lastSeg.tag !== '*') {\n const tag = lastSeg.tag;\n if (!this._deepByTerminalTag.has(tag)) this._deepByTerminalTag.set(tag, []);\n this._deepByTerminalTag.get(tag).push(expression);\n } else {\n this._deepWildcards.push(expression);\n }\n return this;\n }\n\n const depth = expression.length;\n const lastSeg = expression.segments[expression.segments.length - 1];\n const tag = lastSeg?.tag;\n\n if (!tag || tag === '*') {\n // Can index by depth but not by tag\n if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);\n this._wildcardByDepth.get(depth).push(expression);\n } else {\n // Tightest bucket: depth + tag\n const key = `${depth}:${tag}`;\n if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);\n this._byDepthAndTag.get(key).push(expression);\n }\n\n return this;\n }\n\n /**\n * Add multiple expressions at once.\n *\n * @param {import('./Expression.js').default[]} expressions - Array of Expression instances\n * @returns {this} for chaining\n *\n * @example\n * set.addAll([\n * new Expression('root.users.user'),\n * new Expression('root.config.setting'),\n * ]);\n */\n addAll(expressions) {\n for (const expr of expressions) this.add(expr);\n return this;\n }\n\n /**\n * Check whether a pattern string is already present in the set.\n *\n * @param {import('./Expression.js').default} expression\n * @returns {boolean}\n */\n has(expression) {\n return this._patterns.has(expression.pattern);\n }\n\n /**\n * Number of expressions in the set.\n * @type {number}\n */\n get size() {\n return this._patterns.size;\n }\n\n /**\n * Seal the set against further modifications.\n * Useful to prevent accidental mutations after config is built.\n * Calling add() or addAll() on a sealed set throws a TypeError.\n *\n * @returns {this}\n */\n seal() {\n this._sealed = true;\n return this;\n }\n\n /**\n * Whether the set has been sealed.\n * @type {boolean}\n */\n get isSealed() {\n return this._sealed;\n }\n\n /**\n * Test whether the matcher's current path matches any expression in the set.\n *\n * Evaluation order (cheapest → most expensive):\n * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions\n * 2. Depth-only wildcard bucket — O(1) lookup, rare\n * 3. Deep-wildcard list — always checked, but usually small\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {boolean} true if any expression matches the current path\n *\n * @example\n * if (stopNodes.matchesAny(matcher)) {\n * // handle stop node\n * }\n */\n matchesAny(matcher) {\n return this.findMatch(matcher) !== null;\n }\n /**\n * Find and return the first Expression that matches the matcher's current path.\n *\n * Uses the same evaluation order as matchesAny (cheapest → most expensive):\n * 1. Exact depth + tag bucket\n * 2. Depth-only wildcard bucket\n * 3. Deep-wildcard list\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {import('./Expression.js').default | null} the first matching Expression, or null\n *\n * @example\n * const expr = stopNodes.findMatch(matcher);\n * if (expr) {\n * // access expr.config, expr.pattern, etc.\n * }\n */\n findMatch(matcher) {\n const depth = matcher.getDepth();\n const tag = matcher.getCurrentTag();\n\n // 1. Tightest bucket — most expressions live here\n const exactKey = `${depth}:${tag}`;\n const exactBucket = this._byDepthAndTag.get(exactKey);\n if (exactBucket) {\n for (let i = 0; i < exactBucket.length; i++) {\n if (matcher.matches(exactBucket[i])) return exactBucket[i];\n }\n }\n\n // 2. Depth-matched wildcard-tag expressions\n const wildcardBucket = this._wildcardByDepth.get(depth);\n if (wildcardBucket) {\n for (let i = 0; i < wildcardBucket.length; i++) {\n if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];\n }\n }\n\n // 3. Deep wildcards — indexed by terminal tag, then unindexed fallback\n const deepBucket = this._deepByTerminalTag.get(tag);\n if (deepBucket) {\n for (let i = 0; i < deepBucket.length; i++) {\n if (matcher.matches(deepBucket[i])) return deepBucket[i];\n }\n }\n for (let i = 0; i < this._deepWildcards.length; i++) {\n if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];\n }\n\n return null;\n }\n}\n","import { Expression, ExpressionSet } from 'path-expression-matcher';\n\nconst defaultOptions = {\n nameFor: {\n // text: \"#text\",\n // comment: \"\",\n // cdata: \"\",\n },\n skip: {\n // declaration: false,\n // pi: false,\n // attributes: true,\n // cdata: false,\n // comment: false,\n // nsPrefix: false,\n // tags: false,\n },\n tags: {\n valueParsers: [],\n // stopNodes: [],\n },\n attributes: {\n // prefix: \"@_\",\n // suffix: \"\",\n // groupBy: \"\",\n valueParsers: [],\n },\n textJoint: \"\",\n /**\n * Array of strings (tag names) or Expression objects.\n * Any match votes true; no match abstains (does not veto).\n * Combined with forceArray using equal-priority voting:\n * - Either explicit false → false (veto wins)\n * - Any true, none false → true\n * - All abstain → false (default)\n */\n alwaysArray: [],\n /**\n * Function to determine if a tag should be forced into an array.\n * Called with (matcher, isLeafNode) where:\n * - matcher: ReadOnlyMatcher - path matcher for current tag\n * - isLeafNode: boolean|null - null when not yet determinable\n * Returns: boolean - true to force array, false to veto, undefined to abstain\n */\n forceArray: null,\n\n /**\n * Boolean flag that forces creation of a text node for every tag.\n * When true, a text node is always created under nameFor.text even if\n * the tag has no other children or attributes.\n * Default: false (text node created only when tag has attributes or children)\n */\n forceTextNode: false,\n};\n\n// Default chains: replaceEntities first (expand references), then type coercion.\nconst defaultTagParsers = [\"ws\", \"entity\", \"boolean\", \"number\"];\nconst defaultAttrParsers = [\"entity\", \"number\", \"boolean\"];\n\n\nexport function buildOptions(options) {\n const finalOptions = deepClone(defaultOptions);\n\n if (!options || options.tags?.valueParsers === undefined) {\n finalOptions.tags.valueParsers = [...defaultTagParsers];\n }\n if (!options || options.attributes?.valueParsers === undefined) {\n finalOptions.attributes.valueParsers = [...defaultAttrParsers];\n }\n\n // Always build _alwaysArraySet so _resolveForceArray never crashes on a missing set.\n // Uses the user-supplied alwaysArray if provided, otherwise the default empty array.\n const alwaysArraySource = Array.isArray(options?.alwaysArray)\n ? options.alwaysArray\n : finalOptions.alwaysArray;\n const alwaysArraySet = new ExpressionSet();\n for (const entry of alwaysArraySource) {\n normalizeEntry(entry, 'alwaysArray', alwaysArraySet);\n }\n alwaysArraySet.seal();\n finalOptions._alwaysArraySet = alwaysArraySet;\n\n if (options) {\n copyProperties(finalOptions, options);\n }\n\n return finalOptions;\n}\n\nfunction deepClone(obj) {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(deepClone);\n const clone = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone(obj[key]);\n }\n return clone;\n}\n\nfunction copyProperties(target, source) {\n for (const key of Object.keys(source)) {\n // Guard against prototype pollution via option keys\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n if (typeof source[key] === 'function') {\n target[key] = source[key];\n } else if (Array.isArray(source[key])) {\n target[key] = source[key];\n } else if (typeof source[key] === 'object' && source[key] !== null) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n copyProperties(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n}\n\n\nfunction normalizeEntry(entry, optionName, set) {\n let pattern;\n\n if (typeof entry === 'string') {\n if (entry.length === 0) throw new Error(`${optionName} expression cannot be empty`);\n pattern = entry;\n } else if (typeof entry?.pattern === 'string' && entry.pattern.length > 0 && Array.isArray(entry?.segments)) {\n // Duck-type as Expression — avoids instanceof failure when the caller's copy of\n // path-expression-matcher resolves to a different module instance than ours\n // (e.g. parent project has its own node_modules/ copy, or CJS vs ESM mismatch).\n pattern = entry.toString();\n } else {\n // console.log(entry);\n // console.log(entry instanceof Expression);\n // console.log(typeof entry);\n throw new Error(\n `Invalid ${optionName} entry: expected a string, or Expression.`\n );\n }\n\n const expr = new Expression(pattern);\n set.add(expr);\n return expr;\n}","// ---------------------------------------------------------------------------\n// Complete HTML5 named entity reference\n// Organized by logical categories for easy maintenance and selective importing\n// ---------------------------------------------------------------------------\n\n/**\n * Basic Latin & Special Characters\n * @type {Record<string, string>}\n */\nexport const BASIC_LATIN = {\n amp: '&',\n AMP: '&',\n lt: '<',\n LT: '<',\n gt: '>',\n GT: '>',\n quot: '\"',\n QUOT: '\"',\n apos: \"'\",\n lsquo: '‘',\n rsquo: '’',\n ldquo: '“',\n rdquo: '”',\n lsquor: '‚',\n rsquor: '’',\n ldquor: '„',\n bdquo: '„',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n excl: '!',\n quest: '?',\n num: '#',\n dollar: '$',\n percent: '%',\n ast: '*',\n commat: '@',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n sol: '/',\n bsol: '\\\\',\n lbrace: '{',\n rbrace: '}',\n lbrack: '[',\n rbrack: ']',\n lpar: '(',\n rpar: ')',\n nbsp: '\\u00a0',\n iexcl: '¡',\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n COPY: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n REG: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n half: '½',\n frac34: '¾',\n iquest: '¿',\n times: '×',\n div: '÷',\n divide: '÷',\n};\n\n/**\n * Latin Extended & Accented Letters (A-Z)\n * @type {Record<string, string>}\n */\nexport const LATIN_ACCENTS = {\n Agrave: 'À',\n agrave: 'à',\n Aacute: 'Á',\n aacute: 'á',\n Acirc: 'Â',\n acirc: 'â',\n Atilde: 'Ã',\n atilde: 'ã',\n Auml: 'Ä',\n auml: 'ä',\n Aring: 'Å',\n aring: 'å',\n AElig: 'Æ',\n aelig: 'æ',\n Ccedil: 'Ç',\n ccedil: 'ç',\n Egrave: 'È',\n egrave: 'è',\n Eacute: 'É',\n eacute: 'é',\n Ecirc: 'Ê',\n ecirc: 'ê',\n Euml: 'Ë',\n euml: 'ë',\n Igrave: 'Ì',\n igrave: 'ì',\n Iacute: 'Í',\n iacute: 'í',\n Icirc: 'Î',\n icirc: 'î',\n Iuml: 'Ï',\n iuml: 'ï',\n ETH: 'Ð',\n eth: 'ð',\n Ntilde: 'Ñ',\n ntilde: 'ñ',\n Ograve: 'Ò',\n ograve: 'ò',\n Oacute: 'Ó',\n oacute: 'ó',\n Ocirc: 'Ô',\n ocirc: 'ô',\n Otilde: 'Õ',\n otilde: 'õ',\n Ouml: 'Ö',\n ouml: 'ö',\n Oslash: 'Ø',\n oslash: 'ø',\n Ugrave: 'Ù',\n ugrave: 'ù',\n Uacute: 'Ú',\n uacute: 'ú',\n Ucirc: 'Û',\n ucirc: 'û',\n Uuml: 'Ü',\n uuml: 'ü',\n Yacute: 'Ý',\n yacute: 'ý',\n THORN: 'Þ',\n thorn: 'þ',\n szlig: 'ß',\n yuml: 'ÿ',\n Yuml: 'Ÿ',\n};\n\n/**\n * Latin Extended (Letters with diacritics)\n * @type {Record<string, string>}\n */\nexport const LATIN_EXTENDED = {\n Amacr: 'Ā',\n amacr: 'ā',\n Abreve: 'Ă',\n abreve: 'ă',\n Aogon: 'Ą',\n aogon: 'ą',\n Cacute: 'Ć',\n cacute: 'ć',\n Ccirc: 'Ĉ',\n ccirc: 'ĉ',\n Cdot: 'Ċ',\n cdot: 'ċ',\n Ccaron: 'Č',\n ccaron: 'č',\n Dcaron: 'Ď',\n dcaron: 'ď',\n Dstrok: 'Đ',\n dstrok: 'đ',\n Emacr: 'Ē',\n emacr: 'ē',\n Ecaron: 'Ě',\n ecaron: 'ě',\n Edot: 'Ė',\n edot: 'ė',\n Eogon: 'Ę',\n eogon: 'ę',\n Gcirc: 'Ĝ',\n gcirc: 'ĝ',\n Gbreve: 'Ğ',\n gbreve: 'ğ',\n Gdot: 'Ġ',\n gdot: 'ġ',\n Gcedil: 'Ģ',\n Hcirc: 'Ĥ',\n hcirc: 'ĥ',\n Hstrok: 'Ħ',\n hstrok: 'ħ',\n Itilde: 'Ĩ',\n itilde: 'ĩ',\n Imacr: 'Ī',\n imacr: 'ī',\n Iogon: 'Į',\n iogon: 'į',\n Idot: 'İ',\n IJlig: 'IJ',\n ijlig: 'ij',\n Jcirc: 'Ĵ',\n jcirc: 'ĵ',\n Kcedil: 'Ķ',\n kcedil: 'ķ',\n kgreen: 'ĸ',\n Lacute: 'Ĺ',\n lacute: 'ĺ',\n Lcedil: 'Ļ',\n lcedil: 'ļ',\n Lcaron: 'Ľ',\n lcaron: 'ľ',\n Lmidot: 'Ŀ',\n lmidot: 'ŀ',\n Lstrok: 'Ł',\n lstrok: 'ł',\n Nacute: 'Ń',\n nacute: 'ń',\n Ncaron: 'Ň',\n ncaron: 'ň',\n Ncedil: 'Ņ',\n ncedil: 'ņ',\n ENG: 'Ŋ',\n eng: 'ŋ',\n Omacr: 'Ō',\n omacr: 'ō',\n Odblac: 'Ő',\n odblac: 'ő',\n OElig: 'Œ',\n oelig: 'œ',\n Racute: 'Ŕ',\n racute: 'ŕ',\n Rcaron: 'Ř',\n rcaron: 'ř',\n Rcedil: 'Ŗ',\n rcedil: 'ŗ',\n Sacute: 'Ś',\n sacute: 'ś',\n Scirc: 'Ŝ',\n scirc: 'ŝ',\n Scedil: 'Ş',\n scedil: 'ş',\n Scaron: 'Š',\n scaron: 'š',\n Tcedil: 'Ţ',\n tcedil: 'ţ',\n Tcaron: 'Ť',\n tcaron: 'ť',\n Tstrok: 'Ŧ',\n tstrok: 'ŧ',\n Utilde: 'Ũ',\n utilde: 'ũ',\n Umacr: 'Ū',\n umacr: 'ū',\n Ubreve: 'Ŭ',\n ubreve: 'ŭ',\n Uring: 'Ů',\n uring: 'ů',\n Udblac: 'Ű',\n udblac: 'ű',\n Uogon: 'Ų',\n uogon: 'ų',\n Wcirc: 'Ŵ',\n wcirc: 'ŵ',\n Ycirc: 'Ŷ',\n ycirc: 'ŷ',\n Zacute: 'Ź',\n zacute: 'ź',\n Zdot: 'Ż',\n zdot: 'ż',\n Zcaron: 'Ž',\n zcaron: 'ž',\n};\n\n/**\n * Greek Letters\n * @type {Record<string, string>}\n */\nexport const GREEK = {\n Alpha: 'Α',\n alpha: 'α',\n Beta: 'Β',\n beta: 'β',\n Gamma: 'Γ',\n gamma: 'γ',\n Delta: 'Δ',\n delta: 'δ',\n Epsilon: 'Ε',\n epsilon: 'ε',\n epsiv: 'ϵ',\n varepsilon: 'ϵ',\n Zeta: 'Ζ',\n zeta: 'ζ',\n Eta: 'Η',\n eta: 'η',\n Theta: 'Θ',\n theta: 'θ',\n thetasym: 'ϑ',\n vartheta: 'ϑ',\n Iota: 'Ι',\n iota: 'ι',\n Kappa: 'Κ',\n kappa: 'κ',\n kappav: 'ϰ',\n varkappa: 'ϰ',\n Lambda: 'Λ',\n lambda: 'λ',\n Mu: 'Μ',\n mu: 'μ',\n Nu: 'Ν',\n nu: 'ν',\n Xi: 'Ξ',\n xi: 'ξ',\n Omicron: 'Ο',\n omicron: 'ο',\n Pi: 'Π',\n pi: 'π',\n piv: 'ϖ',\n varpi: 'ϖ',\n Rho: 'Ρ',\n rho: 'ρ',\n rhov: 'ϱ',\n varrho: 'ϱ',\n Sigma: 'Σ',\n sigma: 'σ',\n sigmaf: 'ς',\n sigmav: 'ς',\n varsigma: 'ς',\n Tau: 'Τ',\n tau: 'τ',\n Upsilon: 'Υ',\n upsilon: 'υ',\n upsi: 'υ',\n Upsi: 'ϒ',\n upsih: 'ϒ',\n Phi: 'Φ',\n phi: 'φ',\n phiv: 'ϕ',\n varphi: 'ϕ',\n Chi: 'Χ',\n chi: 'χ',\n Psi: 'Ψ',\n psi: 'ψ',\n Omega: 'Ω',\n omega: 'ω',\n ohm: 'Ω',\n Gammad: 'Ϝ',\n gammad: 'ϝ',\n digamma: 'ϝ',\n};\n\n/**\n * Cyrillic Letters\n * @type {Record<string, string>}\n */\nexport const CYRILLIC = {\n Afr: '𝔄',\n afr: '𝔞',\n Acy: 'А',\n acy: 'а',\n Bcy: 'Б',\n bcy: 'б',\n Vcy: 'В',\n vcy: 'в',\n Gcy: 'Г',\n gcy: 'г',\n Dcy: 'Д',\n dcy: 'д',\n IEcy: 'Е',\n iecy: 'е',\n IOcy: 'Ё',\n iocy: 'ё',\n ZHcy: 'Ж',\n zhcy: 'ж',\n Zcy: 'З',\n zcy: 'з',\n Icy: 'И',\n icy: 'и',\n Jcy: 'Й',\n jcy: 'й',\n Kcy: 'К',\n kcy: 'к',\n Lcy: 'Л',\n lcy: 'л',\n Mcy: 'М',\n mcy: 'м',\n Ncy: 'Н',\n ncy: 'н',\n Ocy: 'О',\n ocy: 'о',\n Pcy: 'П',\n pcy: 'п',\n Rcy: 'Р',\n rcy: 'р',\n Scy: 'С',\n scy: 'с',\n Tcy: 'Т',\n tcy: 'т',\n Ucy: 'У',\n ucy: 'у',\n Fcy: 'Ф',\n fcy: 'ф',\n KHcy: 'Х',\n khcy: 'х',\n TScy: 'Ц',\n tscy: 'ц',\n CHcy: 'Ч',\n chcy: 'ч',\n SHcy: 'Ш',\n shcy: 'ш',\n SHCHcy: 'Щ',\n shchcy: 'щ',\n HARDcy: 'Ъ',\n hardcy: 'ъ',\n Ycy: 'Ы',\n ycy: 'ы',\n SOFTcy: 'Ь',\n softcy: 'ь',\n Ecy: 'Э',\n ecy: 'э',\n YUcy: 'Ю',\n yucy: 'ю',\n YAcy: 'Я',\n yacy: 'я',\n DJcy: 'Ђ',\n djcy: 'ђ',\n GJcy: 'Ѓ',\n gjcy: 'ѓ',\n Jukcy: 'Є',\n jukcy: 'є',\n DScy: 'Ѕ',\n dscy: 'ѕ',\n Iukcy: 'І',\n iukcy: 'і',\n YIcy: 'Ї',\n yicy: 'ї',\n Jsercy: 'Ј',\n jsercy: 'ј',\n LJcy: 'Љ',\n ljcy: 'љ',\n NJcy: 'Њ',\n njcy: 'њ',\n TSHcy: 'Ћ',\n tshcy: 'ћ',\n KJcy: 'Ќ',\n kjcy: 'ќ',\n Ubrcy: 'Ў',\n ubrcy: 'ў',\n DZcy: 'Џ',\n dzcy: 'џ',\n};\n\n/**\n * Mathematical Operators & Relations\n * @type {Record<string, string>}\n */\nexport const MATH = {\n plus: '+',\n pm: '±',\n times: '×',\n div: '÷',\n divide: '÷',\n sdot: '⋅',\n star: '☆',\n starf: '★',\n bigstar: '★',\n lowast: '∗',\n ast: '*',\n midast: '*',\n compfn: '∘',\n smallcircle: '∘',\n bullet: '•',\n bull: '•',\n nbsp: '\\u00a0',\n hellip: '…',\n mldr: '…',\n prime: '′',\n Prime: '″',\n tprime: '‴',\n bprime: '‵',\n backprime: '‵',\n minus: '−',\n minusd: '∸',\n dotminus: '∸',\n plusdo: '∔',\n dotplus: '∔',\n plusmn: '±',\n minusplus: '∓',\n mnplus: '∓',\n mp: '∓',\n setminus: '∖',\n smallsetminus: '∖',\n Backslash: '∖',\n setmn: '∖',\n ssetmn: '∖',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n VerticalLine: '|',\n colon: ':',\n Colon: '∷',\n Proportion: '∷',\n ratio: '∶',\n equals: '=',\n ne: '≠',\n nequiv: '≢',\n equiv: '≡',\n Congruent: '≡',\n sim: '∼',\n thicksim: '∼',\n thksim: '∼',\n sime: '≃',\n simeq: '≃',\n TildeEqual: '≃',\n asymp: '≈',\n approx: '≈',\n thickapprox: '≈',\n thkap: '≈',\n TildeTilde: '≈',\n ncong: '≇',\n cong: '≅',\n TildeFullEqual: '≅',\n asympeq: '≍',\n CupCap: '≍',\n bump: '≎',\n Bumpeq: '≎',\n HumpDownHump: '≎',\n bumpe: '≏',\n bumpeq: '≏',\n HumpEqual: '≏',\n le: '≤',\n LessEqual: '≤',\n ge: '≥',\n GreaterEqual: '≥',\n lesseqgtr: '⋚',\n lesseqqgtr: '⪋',\n greater: '>',\n less: '<',\n};\n\n/**\n * Mathematical Operators (Advanced)\n * @type {Record<string, string>}\n */\nexport const MATH_ADVANCED = {\n alefsym: 'ℵ',\n aleph: 'ℵ',\n beth: 'ℶ',\n gimel: 'ℷ',\n daleth: 'ℸ',\n forall: '∀',\n ForAll: '∀',\n part: '∂',\n PartialD: '∂',\n exist: '∃',\n Exists: '∃',\n nexist: '∄',\n nexists: '∄',\n empty: '∅',\n emptyset: '∅',\n emptyv: '∅',\n varnothing: '∅',\n nabla: '∇',\n Del: '∇',\n isin: '∈',\n isinv: '∈',\n in: '∈',\n Element: '∈',\n notin: '∉',\n notinva: '∉',\n ni: '∋',\n niv: '∋',\n SuchThat: '∋',\n ReverseElement: '∋',\n notni: '∌',\n notniva: '∌',\n prod: '∏',\n Product: '∏',\n coprod: '∐',\n Coproduct: '∐',\n sum: '∑',\n Sum: '∑',\n minus: '−',\n mp: '∓',\n plusdo: '∔',\n dotplus: '∔',\n setminus: '∖',\n lowast: '∗',\n radic: '√',\n Sqrt: '√',\n prop: '∝',\n propto: '∝',\n Proportional: '∝',\n varpropto: '∝',\n infin: '∞',\n infintie: '⧝',\n ang: '∠',\n angle: '∠',\n angmsd: '∡',\n measuredangle: '∡',\n angsph: '∢',\n mid: '∣',\n VerticalBar: '∣',\n nmid: '∤',\n nsmid: '∤',\n npar: '∦',\n parallel: '∥',\n spar: '∥',\n nparallel: '∦',\n nspar: '∦',\n and: '∧',\n wedge: '∧',\n or: '∨',\n vee: '∨',\n cap: '∩',\n cup: '∪',\n int: '∫',\n Integral: '∫',\n conint: '∮',\n ContourIntegral: '∮',\n Conint: '∯',\n DoubleContourIntegral: '∯',\n Cconint: '∰',\n there4: '∴',\n therefore: '∴',\n Therefore: '∴',\n becaus: '∵',\n because: '∵',\n Because: '∵',\n ratio: '∶',\n Proportion: '∷',\n minusd: '∸',\n dotminus: '∸',\n mDDot: '∺',\n homtht: '∻',\n sim: '∼',\n bsimg: '∽',\n backsim: '∽',\n ac: '∾',\n mstpos: '∾',\n acd: '∿',\n VerticalTilde: '≀',\n wr: '≀',\n wreath: '≀',\n nsime: '≄',\n nsimeq: '≄',\n ncong: '≇',\n simne: '≆',\n ncongdot: '⩭̸',\n ngsim: '≵',\n nsim: '≁',\n napprox: '≉',\n nap: '≉',\n ngeq: '≱',\n nge: '≱',\n nleq: '≰',\n nle: '≰',\n ngtr: '≯',\n ngt: '≯',\n nless: '≮',\n nlt: '≮',\n nprec: '⊀',\n npr: '⊀',\n nsucc: '⊁',\n nsc: '⊁',\n};\n\n/**\n * Arrows\n * @type {Record<string, string>}\n */\nexport const ARROWS = {\n larr: '←',\n leftarrow: '←',\n LeftArrow: '←',\n uarr: '↑',\n uparrow: '↑',\n UpArrow: '↑',\n rarr: '→',\n rightarrow: '→',\n RightArrow: '→',\n darr: '↓',\n downarrow: '↓',\n DownArrow: '↓',\n harr: '↔',\n leftrightarrow: '↔',\n LeftRightArrow: '↔',\n varr: '↕',\n updownarrow: '↕',\n UpDownArrow: '↕',\n nwarr: '↖',\n nwarrow: '↖',\n UpperLeftArrow: '↖',\n nearr: '↗',\n nearrow: '↗',\n UpperRightArrow: '↗',\n searr: '↘',\n searrow: '↘',\n LowerRightArrow: '↘',\n swarr: '↙',\n swarrow: '↙',\n LowerLeftArrow: '↙',\n lArr: '⇐',\n Leftarrow: '⇐',\n uArr: '⇑',\n Uparrow: '⇑',\n rArr: '⇒',\n Rightarrow: '⇒',\n dArr: '⇓',\n Downarrow: '⇓',\n hArr: '⇔',\n Leftrightarrow: '⇔',\n iff: '⇔',\n vArr: '⇕',\n Updownarrow: '⇕',\n lAarr: '⇚',\n Lleftarrow: '⇚',\n rAarr: '⇛',\n Rrightarrow: '⇛',\n lrarr: '⇆',\n leftrightarrows: '⇆',\n rlarr: '⇄',\n rightleftarrows: '⇄',\n lrhar: '⇋',\n leftrightharpoons: '⇋',\n ReverseEquilibrium: '⇋',\n rlhar: '⇌',\n rightleftharpoons: '⇌',\n Equilibrium: '⇌',\n udarr: '⇅',\n UpArrowDownArrow: '⇅',\n duarr: '⇵',\n DownArrowUpArrow: '⇵',\n llarr: '⇇',\n leftleftarrows: '⇇',\n rrarr: '⇉',\n rightrightarrows: '⇉',\n ddarr: '⇊',\n downdownarrows: '⇊',\n har: '↽',\n lhard: '↽',\n leftharpoondown: '↽',\n lharu: '↼',\n leftharpoonup: '↼',\n rhard: '⇁',\n rightharpoondown: '⇁',\n rharu: '⇀',\n rightharpoonup: '⇀',\n lsh: '↰',\n Lsh: '↰',\n rsh: '↱',\n Rsh: '↱',\n ldsh: '↲',\n rdsh: '↳',\n hookleftarrow: '↩',\n hookrightarrow: '↪',\n mapstoleft: '↤',\n mapstoup: '↥',\n map: '↦',\n mapsto: '↦',\n mapstodown: '↧',\n crarr: '↵',\n nleftarrow: '↚',\n nleftrightarrow: '↮',\n nrightarrow: '↛',\n nrarr: '↛',\n larrtl: '↢',\n rarrtl: '↣',\n leftarrowtail: '↢',\n rightarrowtail: '↣',\n twoheadleftarrow: '↞',\n twoheadrightarrow: '↠',\n Larr: '↞',\n Rarr: '↠',\n larrhk: '↩',\n rarrhk: '↪',\n larrlp: '↫',\n looparrowleft: '↫',\n rarrlp: '↬',\n looparrowright: '↬',\n harrw: '↭',\n leftrightsquigarrow: '↭',\n nrarrw: '↝̸',\n rarrw: '↝',\n rightsquigarrow: '↝',\n larrbfs: '⤟',\n rarrbfs: '⤠',\n nvHarr: '⤄',\n nvlArr: '⤂',\n nvrArr: '⤃',\n larrfs: '⤝',\n rarrfs: '⤞',\n Map: '⤅',\n larrsim: '⥳',\n rarrsim: '⥴',\n harrcir: '⥈',\n Uarrocir: '⥉',\n lurdshar: '⥊',\n ldrdhar: '⥧',\n ldrushar: '⥋',\n rdldhar: '⥩',\n lrhard: '⥭',\n uharr: '↾',\n uharl: '↿',\n dharr: '⇂',\n dharl: '⇃',\n Uarr: '↟',\n Darr: '↡',\n zigrarr: '⇝',\n nwArr: '⇖',\n neArr: '⇗',\n seArr: '⇘',\n swArr: '⇙',\n nharr: '↮',\n nhArr: '⇎',\n nlarr: '↚',\n nlArr: '⇍',\n nrArr: '⇏',\n larrb: '⇤',\n LeftArrowBar: '⇤',\n rarrb: '⇥',\n RightArrowBar: '⇥',\n};\n\n/**\n * Geometric Shapes\n * @type {Record<string, string>}\n */\nexport const SHAPES = {\n square: '□',\n Square: '□',\n squ: '□',\n squf: '▪',\n squarf: '▪',\n blacksquar: '▪',\n blacksquare: '▪',\n FilledVerySmallSquare: '▪',\n blk34: '▓',\n blk12: '▒',\n blk14: '░',\n block: '█',\n srect: '▭',\n rect: '▭',\n sdot: '⋅',\n sdotb: '⊡',\n dotsquare: '⊡',\n triangle: '▵',\n tri: '▵',\n trine: '▵',\n utri: '▵',\n triangledown: '▿',\n dtri: '▿',\n tridown: '▿',\n triangleleft: '◃',\n ltri: '◃',\n triangleright: '▹',\n rtri: '▹',\n blacktriangle: '▴',\n utrif: '▴',\n blacktriangledown: '▾',\n dtrif: '▾',\n blacktriangleleft: '◂',\n ltrif: '◂',\n blacktriangleright: '▸',\n rtrif: '▸',\n loz: '◊',\n lozenge: '◊',\n blacklozenge: '⧫',\n lozf: '⧫',\n bigcirc: '◯',\n xcirc: '◯',\n circ: 'ˆ',\n Circle: '○',\n cir: '○',\n o: '○',\n bullet: '•',\n bull: '•',\n hellip: '…',\n mldr: '…',\n nldr: '‥',\n boxh: '─',\n HorizontalLine: '─',\n boxv: '│',\n boxdr: '┌',\n boxdl: '┐',\n boxur: '└',\n boxul: '┘',\n boxvr: '├',\n boxvl: '┤',\n boxhd: '┬',\n boxhu: '┴',\n boxvh: '┼',\n boxH: '═',\n boxV: '║',\n boxdR: '╒',\n boxDr: '╓',\n boxDR: '╔',\n boxDl: '╕',\n boxdL: '╖',\n boxDL: '╗',\n boxuR: '╘',\n boxUr: '╙',\n boxUR: '╚',\n boxUl: '╜',\n boxuL: '╛',\n boxUL: '╝',\n boxvR: '╞',\n boxVr: '╟',\n boxVR: '╠',\n boxVl: '╢',\n boxvL: '╡',\n boxVL: '╣',\n boxHd: '╤',\n boxhD: '╥',\n boxHD: '╦',\n boxHu: '╧',\n boxhU: '╨',\n boxHU: '╩',\n boxvH: '╪',\n boxVh: '╫',\n boxVH: '╬',\n};\n\n/**\n * Punctuation & Diacritics\n * @type {Record<string, string>}\n */\nexport const PUNCTUATION = {\n excl: '!',\n iexcl: '¡',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n frac34: '¾',\n iquest: '¿',\n nbsp: '\\u00a0',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n vert: '|',\n Verbar: '‖',\n verbar: '|',\n dblac: '˝',\n circ: 'ˆ',\n caron: 'ˇ',\n breve: '˘',\n dot: '˙',\n ring: '˚',\n ogon: '˛',\n tilde: '˜',\n DiacriticalGrave: '`',\n DiacriticalAcute: '´',\n DiacriticalTilde: '˜',\n DiacriticalDot: '˙',\n DiacriticalDoubleAcute: '˝',\n grave: '`',\n};\n\n/**\n * Currency Symbols\n * @type {Record<string, string>}\n */\nexport const CURRENCY = {\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n euro: '€',\n dollar: '$',\n fnof: 'ƒ',\n inr: '₹',\n af: '؋',\n birr: 'ብር',\n peso: '₱',\n rub: '₽',\n won: '₩',\n yuan: '¥',\n cedil: '¸',\n};\n\n/**\n * Fractions\n * @type {Record<string, string>}\n */\nexport const FRACTIONS = {\n frac12: '½',\n half: '½',\n frac13: '⅓',\n frac14: '¼',\n frac15: '⅕',\n frac16: '⅙',\n frac18: '⅛',\n frac23: '⅔',\n frac25: '⅖',\n frac34: '¾',\n frac35: '⅗',\n frac38: '⅜',\n frac45: '⅘',\n frac56: '⅚',\n frac58: '⅝',\n frac78: '⅞',\n frasl: '⁄',\n};\n\n/**\n * Miscellaneous Symbols\n * @type {Record<string, string>}\n */\nexport const MISC_SYMBOLS = {\n trade: '™',\n TRADE: '™',\n telrec: '⌕',\n target: '⌖',\n ulcorn: '⌜',\n ulcorner: '⌜',\n urcorn: '⌝',\n urcorner: '⌝',\n dlcorn: '⌞',\n llcorner: '⌞',\n drcorn: '⌟',\n lrcorner: '⌟',\n intercal: '⊺',\n intcal: '⊺',\n oplus: '⊕',\n CirclePlus: '⊕',\n ominus: '⊖',\n CircleMinus: '⊖',\n otimes: '⊗',\n CircleTimes: '⊗',\n osol: '⊘',\n odot: '⊙',\n CircleDot: '⊙',\n oast: '⊛',\n circledast: '⊛',\n odash: '⊝',\n circleddash: '⊝',\n ocirc: '⊚',\n circledcirc: '⊚',\n boxplus: '⊞',\n plusb: '⊞',\n boxminus: '⊟',\n minusb: '⊟',\n boxtimes: '⊠',\n timesb: '⊠',\n boxdot: '⊡',\n sdotb: '⊡',\n veebar: '⊻',\n vee: '∨',\n barvee: '⊽',\n and: '∧',\n wedge: '∧',\n Cap: '⋒',\n Cup: '⋓',\n Fork: '⋔',\n pitchfork: '⋔',\n epar: '⋕',\n ltlarr: '⥶',\n nvap: '≍⃒',\n nvsim: '∼⃒',\n nvge: '≥⃒',\n nvle: '≤⃒',\n nvlt: '<⃒',\n nvgt: '>⃒',\n nvltrie: '⊴⃒',\n nvrtrie: '⊵⃒',\n Vdash: '⊩',\n dashv: '⊣',\n vDash: '⊨',\n Vvdash: '⊪',\n nvdash: '⊬',\n nvDash: '⊭',\n nVdash: '⊮',\n nVDash: '⊯',\n};\n\n/**\n * All entities combined (if you need everything)\n * @type {Record<string, string>}\n */\nexport const ALL_ENTITIES = {\n ...BASIC_LATIN,\n ...LATIN_ACCENTS,\n ...LATIN_EXTENDED,\n ...GREEK,\n ...CYRILLIC,\n ...MATH,\n ...MATH_ADVANCED,\n ...ARROWS,\n ...SHAPES,\n ...PUNCTUATION,\n ...CURRENCY,\n ...FRACTIONS,\n ...MISC_SYMBOLS,\n};\n\nexport const XML = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n quot: \"\\\"\"\n}\nexport const COMMON_HTML = {\n nbsp: '\\u00a0',\n copy: '\\u00a9',\n reg: '\\u00ae',\n trade: '\\u2122',\n mdash: '\\u2014',\n ndash: '\\u2013',\n hellip: '\\u2026',\n laquo: '\\u00ab',\n raquo: '\\u00bb',\n lsquo: '\\u2018',\n rsquo: '\\u2019',\n ldquo: '\\u201c',\n rdquo: '\\u201d',\n bull: '\\u2022',\n para: '\\u00b6',\n sect: '\\u00a7',\n deg: '\\u00b0',\n frac12: '\\u00bd',\n frac14: '\\u00bc',\n frac34: '\\u00be',\n}\n// ---------------------------------------------------------------------------\n// Note: NUMERIC_ENTITIES (&#NNN; / &#xHH;) are handled by the scanner directly\n// via String.fromCodePoint() without any map lookup.\n// ---------------------------------------------------------------------------","// ---------------------------------------------------------------------------\n// Built-in named entity map (name → replacement string)\n// No regex, no {regex,val} objects — just flat key/value pairs.\n// ---------------------------------------------------------------------------\n\nimport { XML as DEFAULT_XML_ENTITIES } from \"./entities.js\"\n\n// ---------------------------------------------------------------------------\n// Entity hook action constants\n// ---------------------------------------------------------------------------\n\n/**\n * Action constants for `onExternalEntity` and `onInputEntity` hooks.\n *\n * Use these instead of raw strings to avoid typos:\n *\n * @example\n * import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';\n * const dec = new EntityDecoder({\n * onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,\n * });\n */\nexport const ENTITY_ACTION = Object.freeze({\n /** Resolve and expand the entity normally. */\n ALLOW: 'allow',\n /** Silently skip this entity — it will not be registered. */\n BLOCK: 'block',\n /** Throw an error, aborting entity registration entirely. */\n THROW: 'throw',\n});\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst SPECIAL_CHARS = new Set('!?\\\\\\\\/[]$%{}^&*()<>|+');\n\n/**\n * Validate that an entity name contains no dangerous characters.\n * @param {string} name\n * @returns {string} the name, unchanged\n * @throws {Error} on invalid characters\n */\nfunction validateEntityName(name) {\n if (name[0] === '#') {\n throw new Error(`[EntityReplacer] Invalid character '#' in entity name: \"${name}\"`);\n }\n for (const ch of name) {\n if (SPECIAL_CHARS.has(ch)) {\n throw new Error(`[EntityReplacer] Invalid character '${ch}' in entity name: \"${name}\"`);\n }\n }\n return name;\n}\n\n/**\n * Merge one or more entity maps into a flat name→string map.\n * Accepts either:\n * - plain string values: { amp: '&' }\n * - legacy {regex,val} / {regx,val}: { lt: { regex: /.../, val: '<' } }\n *\n * Values containing '&' are skipped (recursive expansion risk).\n *\n * @param {...object} maps\n * @returns {Record<string, string>}\n */\nfunction mergeEntityMaps(...maps) {\n const out = Object.create(null);\n for (const map of maps) {\n if (!map) continue;\n for (const key of Object.keys(map)) {\n const raw = map[key];\n if (typeof raw === 'string') {\n out[key] = raw;\n } else if (raw && typeof raw === 'object' && raw.val !== undefined) {\n // Legacy {regex,val} or {regx,val} — extract the string val only\n const val = raw.val;\n if (typeof val === 'string') {\n out[key] = val;\n }\n // function vals are not supported in the scanner — skip\n }\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// applyLimitsTo helpers\n// ---------------------------------------------------------------------------\n\nconst LIMIT_TIER_EXTERNAL = 'external'; // input/runtime + persistent external maps\nconst LIMIT_TIER_BASE = 'base'; // DEFAULT_XML_ENTITIES + namedEntities (system) maps\nconst LIMIT_TIER_ALL = 'all'; // every entity regardless of tier\n\n/**\n * Resolve `applyLimitsTo` option into a normalised Set of tier strings.\n * Accepted values: 'external' | 'base' | 'all' | string[]\n * Default: 'external' (only untrusted injected entities are counted).\n * @param {string|string[]|undefined} raw\n * @returns {Set<string>}\n */\nfunction parseLimitTiers(raw) {\n if (!raw || raw === LIMIT_TIER_EXTERNAL) return new Set([LIMIT_TIER_EXTERNAL]);\n if (raw === LIMIT_TIER_ALL) return new Set([LIMIT_TIER_ALL]);\n if (raw === LIMIT_TIER_BASE) return new Set([LIMIT_TIER_BASE]);\n if (Array.isArray(raw)) return new Set(raw);\n return new Set([LIMIT_TIER_EXTERNAL]); // safe default for unrecognised values\n}\n\n// ---------------------------------------------------------------------------\n// NCR (Numeric Character Reference) classification\n// ---------------------------------------------------------------------------\n\n// Severity order — higher number = stricter action.\n// Used to enforce minimum action levels for specific codepoint ranges.\nconst NCR_LEVEL = Object.freeze({ allow: 0, leave: 1, remove: 2, throw: 3 });\n\n// XML 1.0 §2.2: allowed chars are #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n// Restricted C0: U+0001–U+001F excluding U+0009, U+000A, U+000D\nconst XML10_ALLOWED_C0 = new Set([0x09, 0x0A, 0x0D]);\n\n/**\n * Parse the `ncr` constructor option into flat, hot-path-friendly fields.\n * @param {object|undefined} ncr\n * @returns {{ xmlVersion: number, onLevel: number, nullLevel: number }}\n */\nfunction parseNCRConfig(ncr) {\n if (!ncr) {\n return { xmlVersion: 1.0, onLevel: NCR_LEVEL.allow, nullLevel: NCR_LEVEL.remove };\n }\n const xmlVersion = ncr.xmlVersion === 1.1 ? 1.1 : 1.0;\n const onLevel = NCR_LEVEL[ncr.onNCR] ?? NCR_LEVEL.allow;\n const nullLevel = NCR_LEVEL[ncr.nullNCR] ?? NCR_LEVEL.remove;\n // 'allow' is not meaningful for null — clamp to at least 'remove'\n const clampedNull = Math.max(nullLevel, NCR_LEVEL.remove);\n return { xmlVersion, onLevel, nullLevel: clampedNull };\n}\n\n// ---------------------------------------------------------------------------\n// EntityReplacer\n// ---------------------------------------------------------------------------\n\n/**\n * Single-pass, zero-regex entity replacer for XML/HTML content.\n *\n * Algorithm: scan the string once for '&', read to ';', resolve via map\n * or direct codepoint conversion, build output chunks, join once at the end.\n *\n * Entity lookup priority (highest → lowest):\n * 1. input / runtime (DOCTYPE entities for current document)\n * 2. persistent external (survive across documents)\n * 3. base named map (DEFAULT_XML_ENTITIES + user-supplied namedEntities)\n *\n * Both input and external resolve as the 'external' tier for limit purposes.\n * Base map entities resolve as the 'base' tier.\n *\n * Numeric / hex references (&#NNN; / &#xHH;) are resolved directly via\n * String.fromCodePoint() — no map needed. They count as 'base' tier.\n *\n * @example\n * const replacer = new EntityReplacer({ namedEntities: COMMON_HTML });\n * replacer.setExternalEntities({ brand: 'Acme' });\n *\n * const instance = replacer.reset();\n * instance.addInputEntities({ version: '1.0' });\n * instance.encode('&brand; v&version; <'); // 'Acme v1.0 <'\n */\nexport default class EntityDecoder {\n /**\n * @param {object} [options]\n * @param {object|null} [options.namedEntities] — extra named entities merged into base map\n * @param {object} [options.limit] — security limits\n * @param {number} [options.limit.maxTotalExpansions=0] — 0 = unlimited\n * @param {number} [options.limit.maxExpandedLength=0] — 0 = unlimited\n * @param {'external'|'base'|'all'|string[]} [options.limit.applyLimitsTo='external']\n * Which entity tiers count against the security limits:\n * - 'external' (default) — only input/runtime + persistent external entities\n * - 'base' — only DEFAULT_XML_ENTITIES + namedEntities\n * - 'all' — every entity regardless of tier\n * - string[] — explicit combination, e.g. ['external', 'base']\n * @param {((resolved: string, original: string) => string)|null} [options.postCheck=null]\n * @param {string[]} [options.remove=[]] — entity names (e.g. ['nbsp', '#13']) to delete (replace with empty string)\n * @param {string[]} [options.leave=[]] — entity names to keep as literal (unchanged in output)\n * @param {object} [options.ncr] — Numeric Character Reference controls\n * @param {1.0|1.1} [options.ncr.xmlVersion=1.0]\n * XML version governing which codepoint ranges are restricted:\n * - 1.0 — C0 controls U+0001–U+001F (except U+0009/000A/000D) are prohibited\n * - 1.1 — C0 controls are allowed when written as NCRs; C1 (U+007F–U+009F) decoded as-is\n * @param {'allow'|'leave'|'remove'|'throw'} [options.ncr.onNCR='allow']\n * Base action for numeric references. Severity order: allow < leave < remove < throw.\n * For codepoint ranges that carry a minimum level (surrogates → remove, XML 1.0 C0 → remove),\n * the effective action is max(onNCR, rangeMinimum).\n * @param {'remove'|'throw'} [options.ncr.nullNCR='remove']\n * Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]\n * Hook called when an external entity is registered via `setExternalEntities()` or\n * `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,\n * `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]\n * Hook called when an input entity is registered via `addInputEntities()`. Return\n * `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or\n * `ENTITY_ACTION.THROW` to abort with an error.\n */\n constructor(options = {}) {\n this._limit = options.limit || {};\n this._maxTotalExpansions = this._limit.maxTotalExpansions || 0;\n this._maxExpandedLength = this._limit.maxExpandedLength || 0;\n this._postCheck = typeof options.postCheck === 'function' ? options.postCheck : r => r;\n this._limitTiers = parseLimitTiers(this._limit.applyLimitsTo ?? LIMIT_TIER_EXTERNAL);\n this._numericAllowed = options.numericAllowed ?? true;\n // Base map: DEFAULT_XML_ENTITIES + user-supplied extras. Immutable after construction.\n this._baseMap = mergeEntityMaps(DEFAULT_XML_ENTITIES, options.namedEntities || null);\n\n // Persistent external entities — survive across documents.\n // Stored as a separate map so reset() never touches them.\n /** @type {Record<string, string>} */\n this._externalMap = Object.create(null);\n\n // Input / runtime entities — current document only, wiped on reset().\n /** @type {Record<string, string>} */\n this._inputMap = Object.create(null);\n\n // Per-document counters\n this._totalExpansions = 0;\n this._expandedLength = 0;\n\n // --- New: remove / leave sets ---\n /** @type {Set<string>} */\n this._removeSet = new Set(options.remove && Array.isArray(options.remove) ? options.remove : []);\n /** @type {Set<string>} */\n this._leaveSet = new Set(options.leave && Array.isArray(options.leave) ? options.leave : []);\n\n // --- NCR config (parsed into flat fields for hot-path speed) ---\n const ncrCfg = parseNCRConfig(options.ncr);\n this._ncrXmlVersion = ncrCfg.xmlVersion;\n this._ncrOnLevel = ncrCfg.onLevel;\n this._ncrNullLevel = ncrCfg.nullLevel;\n\n // --- Registration hooks ---\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onExternalEntity = typeof options.onExternalEntity === 'function'\n ? options.onExternalEntity\n : null;\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onInputEntity = typeof options.onInputEntity === 'function'\n ? options.onInputEntity\n : null;\n }\n\n // -------------------------------------------------------------------------\n // Private: registration hook dispatch\n // -------------------------------------------------------------------------\n\n /**\n * Invoke a registration hook for a single entity name/value pair.\n * Returns true when the entity should be accepted, false when it should be\n * silently skipped (BLOCK), and throws when the hook returns THROW.\n *\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook\n * @param {string} name\n * @param {string} value\n * @param {string} context — used in error messages ('external' | 'input')\n * @returns {boolean} true = accept, false = skip\n */\n _applyRegistrationHook(hook, name, value, context) {\n if (!hook) return true; // no hook → always accept\n const action = hook(name, value);\n if (action === ENTITY_ACTION.BLOCK) return false;\n if (action === ENTITY_ACTION.THROW) {\n throw new Error(\n `[EntityDecoder] Registration of ${context} entity \"&${name};\" was rejected by hook`\n );\n }\n return true; // ALLOW or any unknown return value → accept\n }\n\n // -------------------------------------------------------------------------\n // Persistent external entity registration\n // -------------------------------------------------------------------------\n\n /**\n * Replace the full set of persistent external entities.\n * All keys are validated — throws on invalid characters.\n * If `onExternalEntity` is set, it is called once per entry; entries that\n * return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`\n * aborts the whole call.\n * @param {Record<string, string | { regex?: RegExp, val: string }>} map\n */\n setExternalEntities(map) {\n if (map) {\n for (const key of Object.keys(map)) {\n validateEntityName(key);\n }\n }\n if (!this._onExternalEntity) {\n this._externalMap = mergeEntityMaps(map);\n return;\n }\n // Hook present — resolve values first, then filter\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {\n filtered[name] = value;\n }\n }\n this._externalMap = filtered;\n }\n\n /**\n * Add a single persistent external entity.\n * If `onExternalEntity` is set it is called before the entity is stored;\n * `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.\n * @param {string} key\n * @param {string} value\n */\n addExternalEntity(key, value) {\n validateEntityName(key);\n if (typeof value === 'string' && value.indexOf('&') === -1) {\n if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {\n this._externalMap[key] = value;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Input / runtime entity registration (per document)\n // -------------------------------------------------------------------------\n\n /**\n * Inject DOCTYPE entities for the current document.\n * Also resets per-document expansion counters.\n * If `onInputEntity` is set it is called once per entry; entries returning\n * `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.\n * @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map\n */\n addInputEntities(map) {\n this._totalExpansions = 0;\n this._expandedLength = 0;\n if (!this._onInputEntity) {\n this._inputMap = mergeEntityMaps(map);\n return;\n }\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {\n filtered[name] = value;\n }\n }\n this._inputMap = filtered;\n }\n\n // -------------------------------------------------------------------------\n // Per-document reset\n // -------------------------------------------------------------------------\n\n /**\n * Wipe input/runtime entities and reset counters.\n * Call this before processing each new document.\n * @returns {this}\n */\n reset() {\n this._inputMap = Object.create(null);\n this._totalExpansions = 0;\n this._expandedLength = 0;\n return this;\n }\n\n // -------------------------------------------------------------------------\n // XML version (can be set after construction, e.g. once parser reads <?xml?>)\n // -------------------------------------------------------------------------\n\n /**\n * Update the XML version used for NCR classification.\n * Call this as soon as the document's `<?xml version=\"...\">` declaration is parsed.\n * @param {1.0|1.1|number} version\n */\n setXmlVersion(version) {\n this._ncrXmlVersion = version === 1.1 ? 1.1 : 1.0;\n }\n\n // -------------------------------------------------------------------------\n // Primary API\n // -------------------------------------------------------------------------\n\n /**\n * Replace all entity references in `str` in a single pass.\n *\n * @param {string} str\n * @returns {string}\n */\n decode(str) {\n if (typeof str !== 'string' || str.length === 0) return str;\n //TODO: check if needed\n if (str.indexOf('&') === -1) return str; // fast path — no entities at all\n\n const original = str;\n const chunks = [];\n const len = str.length;\n let last = 0; // start of next unprocessed literal chunk\n let i = 0;\n\n const limitExpansions = this._maxTotalExpansions > 0;\n const limitLength = this._maxExpandedLength > 0;\n const checkLimits = limitExpansions || limitLength;\n\n while (i < len) {\n // Scan forward to next '&'\n if (str.charCodeAt(i) !== 38 /* '&' */) { i++; continue; }\n\n // --- Found '&' at position i ---\n\n // Scan forward to ';'\n let j = i + 1;\n while (j < len && str.charCodeAt(j) !== 59 /* ';' */ && (j - i) <= 32) j++;\n\n if (j >= len || str.charCodeAt(j) !== 59) {\n // No closing ';' within window — treat '&' as literal\n i++;\n continue;\n }\n\n // Raw token between '&' and ';' (exclusive)\n const token = str.slice(i + 1, j);\n if (token.length === 0) { i++; continue; }\n\n let replacement;\n let tier; // which limit tier this entity belongs to\n\n if (this._removeSet.has(token)) {\n // Remove entity: replace with empty string\n replacement = '';\n // If entity was unknown (replacement undefined), we still need a tier for limits.\n // Treat as external tier because it's user-directed removal of an unknown reference.\n if (tier === undefined) {\n tier = LIMIT_TIER_EXTERNAL;\n }\n } else if (this._leaveSet.has(token)) {\n // Do not replace — keep original &token; as literal\n i++;\n continue;\n } else if (token.charCodeAt(0) === 35 /* '#' */) {\n // ---- Numeric / NCR reference ----\n // NCR classification always runs first — prohibited codepoints must be\n // caught regardless of numericAllowed.\n const ncrResult = this._resolveNCR(token);\n if (ncrResult === undefined) {\n // 'leave' action — keep original &token; as-is\n i++;\n continue;\n }\n replacement = ncrResult; // '' for remove, char string for allow\n tier = LIMIT_TIER_BASE;\n } else {\n // ---- Named reference ----\n const resolved = this._resolveName(token);\n replacement = resolved?.value;\n tier = resolved?.tier;\n }\n\n if (replacement === undefined) {\n // Unknown entity — leave as-is, advance past '&' only\n i++;\n continue;\n }\n\n // Flush literal chunk before this entity\n if (i > last) chunks.push(str.slice(last, i));\n chunks.push(replacement);\n last = j + 1; // skip past ';'\n i = last;\n\n // Apply expansion limits only if this tier is being tracked\n if (checkLimits && this._tierCounts(tier)) {\n if (limitExpansions) {\n this._totalExpansions++;\n if (this._totalExpansions > this._maxTotalExpansions) {\n throw new Error(\n `[EntityReplacer] Entity expansion count limit exceeded: ` +\n `${this._totalExpansions} > ${this._maxTotalExpansions}`\n );\n }\n }\n if (limitLength) {\n // delta: replacement.length minus the raw &token; length (token.length + 2 for '&' and ';')\n const delta = replacement.length - (token.length + 2);\n if (delta > 0) {\n this._expandedLength += delta;\n if (this._expandedLength > this._maxExpandedLength) {\n throw new Error(\n `[EntityReplacer] Expanded content length limit exceeded: ` +\n `${this._expandedLength} > ${this._maxExpandedLength}`\n );\n }\n }\n }\n }\n }\n\n // Flush trailing literal\n if (last < len) chunks.push(str.slice(last));\n\n // If nothing was replaced, chunks is empty — return original\n const result = chunks.length === 0 ? str : chunks.join('');\n\n return this._postCheck(result, original);\n }\n\n // -------------------------------------------------------------------------\n // Private: limit tier check\n // -------------------------------------------------------------------------\n\n /**\n * Returns true if a resolved entity of the given tier should count\n * against the expansion/length limits.\n * @param {string} tier — LIMIT_TIER_EXTERNAL | LIMIT_TIER_BASE\n * @returns {boolean}\n */\n _tierCounts(tier) {\n if (this._limitTiers.has(LIMIT_TIER_ALL)) return true;\n return this._limitTiers.has(tier);\n }\n\n // -------------------------------------------------------------------------\n // Private: entity resolution\n // -------------------------------------------------------------------------\n\n /**\n * Resolve a named entity token (without & and ;).\n * Priority: inputMap > externalMap > baseMap\n * Returns the resolved value tagged with its limit tier.\n *\n * @param {string} name\n * @returns {{ value: string, tier: string }|undefined}\n */\n _resolveName(name) {\n // input and external both count as 'external' tier for limit purposes —\n // they are injected at runtime and are the untrusted surface.\n if (name in this._inputMap) return { value: this._inputMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._externalMap) return { value: this._externalMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._baseMap) return { value: this._baseMap[name], tier: LIMIT_TIER_BASE };\n return undefined;\n }\n\n /**\n * Classify a codepoint and return the minimum action level that must be applied.\n * Returns -1 when no minimum is imposed (normal allow path).\n *\n * Ranges checked (in priority order):\n * 1. U+0000 — null, governed by nullNCR (always ≥ remove)\n * 2. U+D800–U+DFFF — surrogates, always prohibited (min: remove)\n * 3. U+0001–U+001F \\ {0x09,0x0A,0x0D} — XML 1.0 restricted C0 (min: remove)\n * (skipped in XML 1.1 — C0 controls are allowed when written as NCRs)\n *\n * @param {number} cp — codepoint\n * @returns {number} — minimum NCR_LEVEL value, or -1 for no restriction\n */\n _classifyNCR(cp) {\n // 1. Null\n if (cp === 0) return this._ncrNullLevel;\n\n // 2. Surrogates — always prohibited, minimum 'remove'\n if (cp >= 0xD800 && cp <= 0xDFFF) return NCR_LEVEL.remove;\n\n // 3. XML 1.0 restricted C0 controls\n if (this._ncrXmlVersion === 1.0) {\n if (cp >= 0x01 && cp <= 0x1F && !XML10_ALLOWED_C0.has(cp)) return NCR_LEVEL.remove;\n }\n\n return -1; // no restriction\n }\n\n /**\n * Execute a resolved NCR action.\n *\n * @param {number} action — NCR_LEVEL value\n * @param {string} token — raw token (e.g. '#38') for error messages\n * @param {number} cp — codepoint, used only for error messages\n * @returns {string|undefined}\n * - decoded character string → 'allow'\n * - '' → 'remove'\n * - undefined → 'leave' (caller must skip past '&' only)\n * - throws Error → 'throw'\n */\n _applyNCRAction(action, token, cp) {\n switch (action) {\n case NCR_LEVEL.allow: return String.fromCodePoint(cp);\n case NCR_LEVEL.remove: return '';\n case NCR_LEVEL.leave: return undefined; // signal: keep literal\n case NCR_LEVEL.throw:\n throw new Error(\n `[EntityDecoder] Prohibited numeric character reference ` +\n `&${token}; (U+${cp.toString(16).toUpperCase().padStart(4, '0')})`\n );\n default: return String.fromCodePoint(cp);\n }\n }\n\n /**\n * Full NCR resolution pipeline for a numeric token.\n *\n * Steps:\n * 1. Parse the codepoint (decimal or hex).\n * 2. Validate the raw codepoint range (NaN, <0, >0x10FFFF).\n * 3. If numericAllowed is false and no minimum restriction applies → leave as-is.\n * 4. Classify the codepoint to find the minimum required action level.\n * 5. Resolve effective action = max(onNCR, minimum).\n * 6. Apply and return.\n *\n * @param {string} token — e.g. '#38', '#x26', '#X26'\n * @returns {string|undefined}\n * - string (incl. '') — replacement ('' = remove)\n * - undefined — leave original &token; as-is\n */\n _resolveNCR(token) {\n // Step 1: parse codepoint\n const second = token.charCodeAt(1);\n let cp;\n if (second === 120 /* x */ || second === 88 /* X */) {\n cp = parseInt(token.slice(2), 16);\n } else {\n cp = parseInt(token.slice(1), 10);\n }\n\n // Step 2: out-of-range → leave as-is unconditionally\n if (Number.isNaN(cp) || cp < 0 || cp > 0x10FFFF) return undefined;\n\n // Step 3: classify to get minimum action level\n const minimum = this._classifyNCR(cp);\n\n // Step 4: if numericAllowed is false and no hard minimum → leave\n if (!this._numericAllowed && minimum < NCR_LEVEL.remove) return undefined;\n\n // Step 5: effective action = max(configured onNCR, range minimum)\n const effective = minimum === -1\n ? this._ncrOnLevel\n : Math.max(this._ncrOnLevel, minimum);\n\n // Step 6: apply\n return this._applyNCRAction(effective, token, cp);\n }\n}","/**\n * HTML context patterns.\n *\n * Detects XSS vectors that are dangerous when a string ends up rendered as HTML.\n * All patterns use bounded quantifiers to ensure linear-time matching (ReDoS-safe).\n *\n * Each entry is { pattern: RegExp, id: string, description: string }\n * so callers can inspect which rule fired if they need to.\n */\n\nconst HTML_PATTERNS = [\n {\n id: 'html-script-open',\n description: '<script opening tag',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'html-script-close',\n description: '</script closing tag',\n pattern: /<\\/script[\\s>]/i,\n },\n {\n id: 'html-javascript-protocol',\n description: 'javascript: URI scheme (with optional whitespace/encoding)',\n // Handles javascript:, j\\u0061vascript:, and whitespace variants\n pattern: /j[\\t\\n\\r ]*a[\\t\\n\\r ]*v[\\t\\n\\r ]*a[\\t\\n\\r ]*s[\\t\\n\\r ]*c[\\t\\n\\r ]*r[\\t\\n\\r ]*i[\\t\\n\\r ]*p[\\t\\n\\r ]*t[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-vbscript-protocol',\n description: 'vbscript: URI scheme',\n pattern: /vbscript[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-data-html',\n description: 'data:text/html URI — can execute scripts in browsers',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*text\\/html/i,\n },\n {\n id: 'html-data-xhtml',\n description: 'data:application/xhtml+xml URI',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*application\\/xhtml/i,\n },\n {\n id: 'html-data-svg',\n description: 'data:image/svg+xml URI — can execute scripts',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*image\\/svg\\+xml/i,\n },\n {\n id: 'html-inline-event-handler',\n description: 'Inline event handler attributes: onclick=, onerror=, onload=, etc.',\n // \\bon ensures we match a word boundary so \"phonetic=\" is not caught\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'html-entity-obfuscated-script',\n description: 'HTML-entity-encoded <script (e.g. <script or <script)',\n // Entities include optional trailing semicolon: < or < (both valid in HTML5)\n pattern: /(?:�*3[Cc];?|�*60;?|<)\\s*script/i,\n },\n {\n id: 'html-entity-obfuscated-javascript',\n description: 'HTML-entity-encoded javascript: (partial — catches common j or j for \"j\")',\n pattern: /(?:�*6[Aa];?|�*106;?)\\s*(?:�*61;?|a)[\\s\\S]{0,80}script\\s*:/i,\n },\n {\n id: 'html-style-expression',\n description: 'CSS expression() — IE-era code execution in style attributes',\n pattern: /style[\\s\\S]{0,20}expression\\s*\\(/i,\n },\n {\n id: 'html-object-embed',\n description: '<object or <embed tags that can load active content',\n pattern: /<(?:object|embed)[\\s>/]/i,\n },\n {\n id: 'html-base-tag',\n description: '<base href= — can hijack all relative URLs on a page',\n pattern: /<base[\\s>]/i,\n },\n {\n id: 'html-meta-refresh',\n description: '<meta http-equiv=\"refresh\" — can redirect users',\n pattern: /<meta[\\s\\S]{0,40}http-equiv[\\s\\S]{0,20}refresh/i,\n },\n {\n id: 'html-srcdoc',\n description: 'srcdoc= attribute on iframes — embeds HTML that can run scripts',\n pattern: /srcdoc\\s*=/i,\n },\n {\n id: 'html-iframe',\n description: '<iframe tag',\n pattern: /<iframe[\\s>/]/i,\n },\n {\n id: 'html-form',\n description: '<form tag — can be used for phishing / credential harvesting injection',\n pattern: /<form[\\s>/]/i,\n },\n];\n\nexport default HTML_PATTERNS;\n","/**\n * SQL context patterns — high-precision rules only.\n *\n * These rules have very low false-positive risk and are safe to apply to\n * general user text (names, descriptions, search queries, etc.).\n * All patterns are ReDoS-safe — unlike the `sql-injection` npm package\n * which has an active CVE on its own detection regexes.\n *\n * For exhaustive coverage including noisier heuristics (comment sequences,\n * hex literals, stacked queries with semicolons), use 'SQL-STRICT' instead.\n * Apply 'SQL-STRICT' only to strings that are specifically SQL fragments,\n * not to general free-text fields.\n */\n\nconst SQL_PATTERNS = [\n {\n id: 'sql-block-comment-open',\n description: 'SQL block comment open: /* ... */ — unusual in legitimate user text',\n pattern: /\\/\\*/,\n },\n {\n id: 'sql-union-select',\n description: 'UNION SELECT — most common SQL injection aggregation attack',\n pattern: /\\bUNION\\s{1,20}(?:ALL\\s{1,20})?SELECT\\b/i,\n },\n {\n id: 'sql-drop-table',\n description: 'DROP TABLE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}TABLE\\b/i,\n },\n {\n id: 'sql-drop-database',\n description: 'DROP DATABASE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}DATABASE\\b/i,\n },\n {\n id: 'sql-insert-into',\n description: 'INSERT INTO — data injection',\n pattern: /\\bINSERT\\s{1,20}INTO\\b/i,\n },\n {\n id: 'sql-delete-from',\n description: 'DELETE FROM — data deletion injection',\n pattern: /\\bDELETE\\s{1,20}FROM\\b/i,\n },\n {\n id: 'sql-update-set',\n description: 'UPDATE ... SET — data modification injection',\n // Allows arbitrary content between UPDATE and SET (table name, alias, etc.)\n pattern: /\\bUPDATE\\b[\\s\\S]{1,60}\\bSET\\b/i,\n },\n {\n id: 'sql-exec-xp',\n description: 'EXEC xp_ — MSSQL extended stored procedure execution',\n pattern: /\\bEXEC(?:UTE)?\\s{1,20}xp_/i,\n },\n {\n id: 'sql-tautology-string',\n description: \"Classic string tautology: ' OR '1'='1 or \\\" OR \\\"1\\\"=\\\"1\\\"\",\n // Last quote is optional — injection may truncate it: ' OR '1'='1--\n pattern: /'\\s{0,10}OR\\s{0,10}'[^']{0,20}'\\s*=\\s*'[^']{0,20}/i,\n },\n {\n id: 'sql-tautology-numeric',\n description: 'Numeric tautology: OR 1=1',\n pattern: /\\bOR\\s{1,10}1\\s*=\\s*1\\b/i,\n },\n {\n id: 'sql-always-true-zero',\n description: 'Numeric tautology: OR 0=0',\n pattern: /\\bOR\\s{1,10}0\\s*=\\s*0\\b/i,\n },\n {\n id: 'sql-sleep-benchmark',\n description: 'Time-based blind injection: SLEEP() or BENCHMARK()',\n pattern: /\\b(?:SLEEP|BENCHMARK)\\s*\\(/i,\n },\n {\n id: 'sql-waitfor-delay',\n description: 'MSSQL time-based blind injection: WAITFOR DELAY',\n pattern: /\\bWAITFOR\\s{1,20}DELAY\\b/i,\n },\n {\n id: 'sql-char-function',\n description: 'CHAR() function — used to obfuscate injected strings',\n pattern: /\\bCHAR\\s*\\(\\s*\\d{1,3}/i,\n },\n {\n id: 'sql-information-schema',\n description: 'INFORMATION_SCHEMA — reconnaissance query for table/column enumeration',\n pattern: /\\bINFORMATION_SCHEMA\\b/i,\n },\n];\n\nexport default SQL_PATTERNS;\n","/**\n * NOSQL context patterns.\n *\n * Detects injection vectors specific to NoSQL databases (primarily MongoDB)\n * and JavaScript-evaluated queries.\n *\n * Attack categories:\n * 1. MongoDB query operator injection: $where, $ne, $gt, $regex, $or, $and, etc.\n * These operators, when injected into a JSON query object, can bypass\n * authentication or exfiltrate data without knowing passwords.\n *\n * 2. JavaScript execution: $where clauses execute arbitrary JS server-side.\n *\n * 3. Prototype pollution: __proto__, constructor.prototype — can corrupt\n * the prototype chain of all objects in the Node.js process.\n *\n * Pattern note: MongoDB operators appear as JSON keys. In JSON, keys are\n * quoted: {\"$where\": ...} so the pattern must allow an optional closing\n * quote between the operator name and the colon: /\\$where[\"'\\s]*:/\n */\n\n// Shared suffix: optional closing quote/whitespace before the colon\n// Handles: $op: (bare), \"$op\": (JSON), '$op': (single-quoted)\nconst SEP = /[\"'\\s]*:/;\nconst sep = '[\"\\'\\\\s]*:';\n\nconst NOSQL_PATTERNS = [\n // ─── MongoDB $ operator injection ────────────────────────────────────────\n {\n id: 'nosql-where-operator',\n description: '$where — executes arbitrary JavaScript server-side in MongoDB',\n pattern: new RegExp(`\\\\$where${sep}`, 'i'),\n },\n {\n id: 'nosql-ne-operator',\n description: '$ne — \"not equal\" operator used to bypass equality checks',\n pattern: new RegExp(`\\\\$ne${sep}`, 'i'),\n },\n {\n id: 'nosql-gt-operator',\n description: '$gt — \"greater than\" used to bypass password/value checks',\n pattern: new RegExp(`\\\\$gte?${sep}`, 'i'),\n },\n {\n id: 'nosql-lt-operator',\n description: '$lt / $lte — \"less than\" bypass variants',\n pattern: new RegExp(`\\\\$lte?${sep}`, 'i'),\n },\n {\n id: 'nosql-regex-operator',\n description: '$regex — can be used to extract data character by character (blind injection)',\n pattern: new RegExp(`\\\\$regex${sep}`, 'i'),\n },\n {\n id: 'nosql-or-operator',\n description: '$or — logical OR; used to create always-true conditions',\n pattern: new RegExp(`\\\\$or${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-and-operator',\n description: '$and — logical AND operator injection',\n pattern: new RegExp(`\\\\$and${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-nor-operator',\n description: '$nor — logical NOR operator injection',\n pattern: new RegExp(`\\\\$nor${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-exists-operator',\n description: '$exists — can enumerate fields to determine schema',\n pattern: new RegExp(`\\\\$exists${sep}`, 'i'),\n },\n {\n id: 'nosql-in-operator',\n description: '$in — matches any value in a list; can enumerate values',\n pattern: new RegExp(`\\\\$in${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-expr-operator',\n description: '$expr — allows aggregation expressions in queries (MongoDB 3.6+)',\n pattern: new RegExp(`\\\\$expr${sep}`, 'i'),\n },\n {\n id: 'nosql-function-operator',\n description: '$function — executes arbitrary JavaScript in MongoDB 4.4+',\n pattern: new RegExp(`\\\\$function${sep}`, 'i'),\n },\n {\n id: 'nosql-accumulator-operator',\n description: '$accumulator — custom aggregation with arbitrary JS execution',\n pattern: new RegExp(`\\\\$accumulator${sep}`, 'i'),\n },\n // ─── Prototype pollution ─────────────────────────────────────────────────\n {\n id: 'nosql-proto-pollution',\n description: '__proto__ — prototype pollution via object key injection',\n pattern: /__proto__/,\n },\n {\n id: 'nosql-constructor-prototype',\n description: 'constructor.prototype — alternative prototype pollution vector (dot notation or JSON key)',\n // Matches dot-notation (obj.constructor.prototype) and JSON key adjacency\n // (\"constructor\": {\"prototype\": ...})\n pattern: /constructor[\\s\"':.,{\\[]*prototype/i,\n },\n {\n id: 'nosql-proto-bracket',\n description: '[\"__proto__\"] — bracket-notation prototype pollution',\n pattern: /\\[[\"']__proto__[\"']\\]/,\n },\n];\n\nexport default NOSQL_PATTERNS;\n","/**\n * Context registry — maps context name strings to their pattern arrays.\n *\n * Adding a new context: create a file in ./contexts/, export a default array\n * of pattern objects, and register it here.\n *\n * Context name guide:\n * SQL — high-precision rules; safe for general text fields\n * SQL-STRICT — SQL + three noisier rules (line comments, stacked queries, hex);\n * use only for SQL-specific inputs\n * REDOS — detects ReDoS-prone patterns when string will be compiled as RegExp\n */\n\nimport HTML_PATTERNS from './contexts/html.js';\nimport XML_PATTERNS from './contexts/xml.js';\nimport SVG_PATTERNS from './contexts/svg.js';\nimport SQL_PATTERNS from './contexts/sql.js';\nimport SQL_STRICT_PATTERNS from './contexts/sql-strict.js';\nimport SHELL_PATTERNS from './contexts/shell.js';\nimport REDOS_PATTERNS from './contexts/redos.js';\nimport NOSQL_PATTERNS from './contexts/nosql.js';\nimport LOG_PATTERNS from './contexts/log.js';\n\n/** @type {Record<string, Array<{id: string, description: string, pattern: RegExp}>>} */\nconst CONTEXT_REGISTRY = {\n HTML: HTML_PATTERNS,\n XML: XML_PATTERNS,\n SVG: SVG_PATTERNS,\n SQL: SQL_PATTERNS,\n 'SQL-STRICT': SQL_STRICT_PATTERNS,\n SHELL: SHELL_PATTERNS,\n REDOS: REDOS_PATTERNS,\n NOSQL: NOSQL_PATTERNS,\n LOG: LOG_PATTERNS,\n};\n\nexport default CONTEXT_REGISTRY;\n\n/**\n * Enum of valid context names — e.g. `VALID_CONTEXTS.HTML === 'HTML'`.\n * @type {Record<string, string>}\n */\nexport const VALID_CONTEXTS = Object.freeze(\n Object.fromEntries(Object.keys(CONTEXT_REGISTRY).map((k) => [k, k]))\n);","/**\n * XML context patterns.\n *\n * Detects injection vectors that are specifically dangerous when a string\n * is inserted into an XML document (not HTML rendering context).\n *\n * Key distinction from HTML: these patterns target parser-level attacks —\n * things that can confuse or subvert an XML parser, trigger external entity\n * resolution, or inject DTD content. HTML rendering concerns (XSS) belong\n * in the HTML context.\n */\n\nconst XML_PATTERNS = [\n {\n id: 'xml-cdata-injection',\n description: 'CDATA section injection: <![CDATA[ breaks out of text node context',\n pattern: /<!\\[CDATA\\[/i,\n },\n {\n id: 'xml-cdata-close',\n description: 'CDATA close sequence: ]]> can terminate an enclosing CDATA section',\n pattern: /\\]\\]>/,\n },\n {\n id: 'xml-processing-instruction',\n description: 'XML processing instruction: <?xml-stylesheet or <?php etc.',\n pattern: /<\\?(?:xml[\\- ]|php|asp)/i,\n },\n {\n id: 'xml-doctype-injection',\n description: 'DOCTYPE declaration embedded in content — can define entities',\n // Match <!DOCTYPE followed by end-of-string, whitespace, or [ (internal subset)\n pattern: /<!DOCTYPE(?:[\\s[]|$)/i,\n },\n {\n id: 'xml-entity-system',\n description: 'SYSTEM keyword — used in external entity declarations (XXE)',\n pattern: /\\bSYSTEM\\s+[\"']/i,\n },\n {\n id: 'xml-entity-public',\n description: 'PUBLIC keyword — used in external entity declarations (XXE)',\n pattern: /\\bPUBLIC\\s+[\"']/i,\n },\n {\n id: 'xml-entity-declaration',\n description: '<!ENTITY declaration — defines entities, potential XXE or entity expansion',\n pattern: /<!ENTITY[\\s%]/i,\n },\n {\n id: 'xml-billion-laughs',\n description: 'Entity reference chaining / billion laughs: repeated &eX; style references',\n // Heuristic: 3+ consecutive entity refs suggests expansion attack\n pattern: /(?:&\\w{1,20};){3,}/,\n },\n {\n id: 'xml-namespace-confusion',\n description: 'xmlns: attribute injection — can redefine namespaces to confuse parsers',\n pattern: /\\bxmlns\\s*(?::\\w{1,40})?\\s*=/i,\n },\n {\n id: 'xml-comment-injection',\n description: '<!-- comment injection — can hide content from some parsers',\n pattern: /<!--/,\n },\n {\n id: 'xml-comment-close',\n description: '--> closes an enclosing XML comment',\n pattern: /-->/,\n },\n {\n id: 'xml-pi-close',\n description: '?> closes an enclosing processing instruction',\n pattern: /\\?>/,\n },\n];\n\nexport default XML_PATTERNS;\n","/**\n * SVG context patterns.\n *\n * SVG is XML-based but renders in browsers, giving it a unique attack surface\n * that combines XML parser behaviour with browser rendering and JavaScript execution.\n *\n * Many of these vectors bypass HTML sanitizers that don't understand SVG semantics\n * (DOMPurify has documented bypass vulnerabilities specifically in SVG/XML context).\n */\n\nconst SVG_PATTERNS = [\n {\n id: 'svg-script-element',\n description: '<script element inside SVG executes JavaScript',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'svg-xlink-href-javascript',\n description: 'xlink:href with javascript: — classic SVG XSS via <a> or <use>',\n pattern: /xlink\\s*:\\s*href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-href-javascript',\n description: 'href= with javascript: in SVG context (<a>, <animate>, etc.)',\n pattern: /href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-foreignobject',\n description: '<foreignObject embeds HTML inside SVG — can execute scripts',\n pattern: /<foreignObject[\\s>/]/i,\n },\n {\n id: 'svg-use-external',\n description: '<use xlink:href or href pointing to external resource (non-fragment URL)',\n // Match <use with href= where the value starts with a non-# character (external URL)\n // [\\\"'][^#] catches quoted values not starting with #; [^\\\"'#\\s>] catches unquoted\n pattern: /<use[\\s\\S]{0,60}(?:xlink\\s*:\\s*)?href\\s*=\\s*(?:[\"'][^#]|[^\"'#\\s>])/i,\n },\n {\n id: 'svg-animate-href',\n description: '<animate attributeName=\"href\" — can dynamically change href to javascript:',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*href[\"']/i,\n },\n {\n id: 'svg-animate-xlinkhref',\n description: '<animate attributeName=\"xlink:href\"',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*xlink\\s*:\\s*href[\"']/i,\n },\n {\n id: 'svg-set-javascript',\n description: '<set to=\"javascript:...\" — sets an attribute to a javascript: URI',\n pattern: /<set[\\s\\S]{0,80}to\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-event-handler',\n description: 'SVG-specific event handler attributes: onload=, onerror=, onactivate=, etc.',\n pattern: /\\bon(?:load|error|activate|begin|end|repeat|focus|blur|click|mouse\\w{1,20}|key\\w{1,20})\\s*=/i,\n },\n {\n id: 'svg-handler-generic',\n description: 'Generic on* handler catch-all for SVG attributes',\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'svg-filter-feimage',\n description: '<feImage href= — filter primitive that can load external resources',\n pattern: /<feImage[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=/i,\n },\n {\n id: 'svg-image-external',\n description: '<image xlink:href with http/https or javascript protocol',\n pattern: /<image[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=\\s*[\"']?\\s*(?:https?|javascript)\\s*:/i,\n },\n {\n id: 'svg-style-javascript',\n description: 'style= attribute containing javascript: (e.g. background:url(javascript:...))',\n pattern: /style\\s*=[\\s\\S]{0,60}javascript\\s*:/i,\n },\n];\n\nexport default SVG_PATTERNS;\n","/**\n * SQL-STRICT context patterns.\n *\n * Extends the base 'SQL' context with three additional rules that are\n * effective at detecting real injections but carry a higher false-positive\n * risk on general free-text input.\n *\n * Use 'SQL-STRICT' when:\n * - The string is specifically a SQL fragment or database identifier\n * - You control the input domain (e.g. a dedicated SQL search field)\n * - You can tolerate occasional false positives in exchange for broader coverage\n *\n * Use 'SQL' (not STRICT) when:\n * - The field is general user text (names, descriptions, comments)\n * - False positives would block legitimate content (e.g. \"see note -- above\")\n *\n * Rules moved here from 'SQL' due to false-positive risk:\n *\n * sql-line-comment — \"--\" fires on \"see note -- above\", \"value--\", CSS var(--primary)\n * sql-stacked-query — \"; SELECT\" fires on legitimate prose with semicolons + SQL words\n * sql-hex-encoding — \"0xDEAD\" fires on hex values in technical docs and log output\n */\n\nimport SQL_PATTERNS from './sql.js';\n\nconst SQL_STRICT_EXTRA = [\n {\n id: 'sql-line-comment',\n description: 'SQL line comment: -- followed by whitespace or end of string',\n pattern: /--(?:\\s|$)/,\n },\n {\n id: 'sql-stacked-query',\n description: 'Stacked queries: semicolon immediately followed by a SQL keyword',\n pattern: /;\\s{0,10}(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC)\\b/i,\n },\n {\n id: 'sql-hex-encoding',\n description: 'Hex-encoded string injection: 0x41414141 style (MySQL)',\n pattern: /\\b0x[0-9a-f]{4,}/i,\n },\n];\n\n// SQL-STRICT = all base SQL rules + the three noisy extras\nconst SQL_STRICT_PATTERNS = [...SQL_PATTERNS, ...SQL_STRICT_EXTRA];\n\nexport default SQL_STRICT_PATTERNS;\n","/**\n * SHELL context patterns.\n *\n * Detects shell injection vectors and path traversal patterns.\n * Designed for use when a string will be passed to a shell command,\n * used as a file path, or interpolated into OS-level operations.\n */\n\nconst SHELL_PATTERNS = [\n {\n id: 'shell-path-traversal-unix',\n description: 'Unix path traversal: ../ — climbing the directory tree',\n pattern: /\\.\\.\\//,\n },\n {\n id: 'shell-path-traversal-windows',\n description: 'Windows path traversal: ..\\\\ — climbing the directory tree',\n pattern: /\\.\\.\\\\/,\n },\n {\n id: 'shell-path-traversal-encoded',\n description: 'URL-encoded path traversal: %2e%2e or %2f variants',\n pattern: /%2e%2e|%2f\\.\\.|\\.\\.%2f/i,\n },\n {\n id: 'shell-null-byte',\n description: 'Null byte injection: \\\\x00 or %00 — truncates strings in C-backed functions',\n pattern: /\\x00|%00/,\n },\n {\n id: 'shell-semicolon',\n description: 'Semicolon command separator: cmd1; cmd2',\n pattern: /;/,\n },\n {\n id: 'shell-pipe',\n description: 'Pipe operator: cmd1 | cmd2',\n pattern: /\\|/,\n },\n {\n id: 'shell-and-operator',\n description: 'AND operator: cmd1 && cmd2',\n pattern: /&&/,\n },\n {\n id: 'shell-or-operator',\n description: 'OR operator: cmd1 || cmd2',\n pattern: /\\|\\|/,\n },\n {\n id: 'shell-backtick',\n description: 'Backtick command substitution: `cmd`',\n pattern: /`/,\n },\n {\n id: 'shell-dollar-paren',\n description: 'Dollar-paren command substitution: $(cmd)',\n pattern: /\\$\\(/,\n },\n {\n id: 'shell-dollar-brace',\n description: 'Dollar-brace variable expansion: ${var} — can be abused for injection',\n pattern: /\\$\\{/,\n },\n {\n id: 'shell-redirect-out',\n description: 'Output redirection: cmd > file or cmd >> file',\n pattern: />{1,2}/,\n },\n {\n id: 'shell-redirect-in',\n description: 'Input redirection: cmd < file',\n pattern: /</,\n },\n {\n id: 'shell-newline-injection',\n description: 'Newline injection: \\\\n or \\\\r — can inject new shell commands',\n pattern: /[\\n\\r]/,\n },\n {\n id: 'shell-glob-star',\n description: 'Glob expansion: * or ? — can expand to unintended files',\n // Only flag when combined with path separators to reduce false positives\n pattern: /[/\\\\][*?]/,\n },\n {\n id: 'shell-absolute-root',\n description: 'Absolute root path injection: string starting with / or \\\\ (Windows UNC)',\n pattern: /^(?:\\/|\\\\\\\\)/,\n },\n {\n id: 'shell-windows-drive',\n description: 'Windows drive letter path injection: C:\\\\ or D:/',\n pattern: /^[a-zA-Z]:[/\\\\]/,\n },\n {\n id: 'shell-curl-wget',\n description: 'curl/wget with URL or flags — can exfiltrate data or download payloads',\n // Require a URL scheme (http/https/ftp) or a flag (-) to reduce false positives\n // \"curl is a tool\" won't match; \"curl http://...\" or \"curl -s ...\" will\n pattern: /\\b(?:curl|wget)\\s+(?:https?:\\/\\/|ftp:\\/\\/|-)/i,\n },\n];\n\nexport default SHELL_PATTERNS;\n","/**\n * REDOS context patterns.\n *\n * Detects strings that, if used as regular expressions, could cause\n * catastrophic backtracking (ReDoS — Regular Expression Denial of Service).\n *\n * These patterns detect the structural forms that lead to exponential or\n * polynomial backtracking in NFA-based regex engines (V8, PCRE, Java, etc.).\n *\n * Use this context when user-supplied strings will be compiled into RegExp objects.\n */\n\nconst REDOS_PATTERNS = [\n {\n id: 'redos-nested-quantifier-plus',\n description: 'Nested + quantifier inside a group with outer quantifier: (a+)+, (.+b)*, etc.',\n // Matches any group containing a + quantifier, with an outer * or + — catches (a+)+, (.+b)*, etc.\n pattern: /\\([^)]*\\+[^)]*\\)[+*]/,\n },\n {\n id: 'redos-nested-quantifier-star',\n description: 'Nested * quantifier: (a*)* or (a*)+ — catastrophic backtracking',\n pattern: /\\([^)]*\\*[^)]*\\)[*+]/,\n },\n {\n id: 'redos-nested-groups',\n description: 'Doubly nested quantified groups: ((a+)+) — guaranteed catastrophic',\n pattern: /\\(\\([^)]{0,40}\\)[+*]\\)[+*]/,\n },\n {\n id: 'redos-alternation-overlap',\n description: 'Overlapping alternation under quantifier: (a|a)+ — ambiguous NFA paths',\n // Detect repeated identical alternatives under a quantifier\n pattern: /\\(([^|()]{1,20})\\|(?:\\1)(?:\\|[^|()]{1,20}){0,5}\\)[+*?]{1,2}/,\n },\n {\n id: 'redos-star-plus-concat',\n description: '(x*x)+ pattern — triggers super-linear backtracking',\n pattern: /\\([^)]{0,10}\\*[^)]{0,10}\\)[+*]/,\n },\n {\n id: 'redos-dot-star-greedy',\n description: '(.*){n,} or (.+){n,} — repeated greedy dot quantifiers',\n pattern: /\\(\\.[*+]\\)\\{?\\d/,\n },\n {\n id: 'redos-large-repetition',\n description: 'Very large fixed or range repetition count {1000,} or {1000,n} — denial of service via backtracking',\n // Matches { followed by 4+ digits (≥1000), then optional ,digits }\n pattern: /\\{\\d{4,}(?:,\\d*)?\\}/,\n },\n {\n id: 'redos-catastrophic-alternation',\n description: 'Long alternation with many similar branches — polynomial backtracking risk',\n // Heuristic: 10+ pipe-separated alternatives in a single group\n pattern: /\\([^)]{0,200}(?:\\|[^|)]{0,50}){9,}\\)/,\n },\n];\n\nexport default REDOS_PATTERNS;\n","/**\n * LOG context patterns.\n *\n * Detects injection vectors that are dangerous when a string is written\n * to a log file, passed to a logging framework, or interpolated into\n * a log message that will be parsed or displayed.\n *\n * Attack categories:\n * 1. CRLF injection — injects fake log lines by embedding newlines\n * 2. Log4Shell (CVE-2021-44228) — ${jndi:...} triggers JNDI lookup in Log4j\n * 3. SSTI in log templates — {{...}}, #{...} trigger template evaluation\n * if the log message is passed through a template engine\n * 4. Null byte injection — truncates log entries in some implementations\n * 5. ANSI escape injection — manipulates terminal output when logs are\n * tailed in a terminal (colour codes, cursor movement, etc.)\n *\n * Note: Newline characters (\\n, \\r) will produce false positives for\n * multi-line legitimate values. Use this context only for single-line\n * log field values (usernames, IDs, request parameters, etc.).\n */\n\nconst LOG_PATTERNS = [\n // ─── CRLF / newline injection ─────────────────────────────────────────────\n {\n id: 'log-crlf-injection',\n description: 'CRLF injection: literal \\\\r or \\\\n embeds fake log lines',\n pattern: /[\\r\\n]/,\n },\n {\n id: 'log-url-encoded-crlf',\n description: 'URL-encoded CRLF: %0d, %0a, %0D, %0A — decoded by some log parsers',\n pattern: /%0[dDaA]/,\n },\n {\n id: 'log-unicode-newline',\n description: 'Unicode newline variants: U+2028 (line separator), U+2029 (paragraph separator)',\n pattern: /[\\u2028\\u2029]/,\n },\n\n // ─── Log4Shell / JNDI injection (CVE-2021-44228) ─────────────────────────\n {\n id: 'log-log4shell-jndi',\n description: 'Log4Shell: ${jndi:...} triggers remote code execution in Apache Log4j',\n pattern: /\\$\\{jndi\\s*:/i,\n },\n {\n id: 'log-log4shell-obfuscated',\n description: 'Obfuscated Log4Shell: ${::-j}... lookup-bypass prefix used to evade WAF detection',\n // ${::- is the Log4j lookup-bypass escape sequence; presence alone is suspicious\n pattern: /\\$\\{::-/,\n },\n {\n id: 'log-log4j-lookup',\n description: 'Log4j lookup syntax: ${env:...}, ${sys:...}, ${ctx:...} — data exfiltration',\n pattern: /\\$\\{(?:env|sys|ctx|main|map|sd|web|docker|k8s|spring)\\s*:/i,\n },\n\n // ─── Server-Side Template Injection (SSTI) in log messages ───────────────\n {\n id: 'log-ssti-double-brace',\n description: 'SSTI double-brace: {{expression}} — Jinja2, Twig, Handlebars, etc.',\n pattern: /\\{\\{[\\s\\S]{0,80}\\}\\}/,\n },\n {\n id: 'log-ssti-hash-brace',\n description: 'SSTI hash-brace: #{expression} — Thymeleaf, Velocity, Ruby ERB',\n pattern: /#\\{[\\s\\S]{0,80}\\}/,\n },\n {\n id: 'log-ssti-dollar-brace',\n description: 'SSTI/EL injection: ${expression with operators or method calls} — JSP EL, Freemarker, SpEL',\n // Require that the ${...} content looks like an expression, not a plain variable name.\n // Flags if the content contains: . ( * + operators, or known SSTI keywords.\n // This avoids flagging ${PATH}, ${HOME} etc. (plain shell variables).\n pattern: /\\$\\{[^}]*(?:\\.|\\(|\\*|\\+|\\bclass\\b|\\bruntime\\b|\\bprocess\\b|\\bexec\\b)[^}]{0,80}\\}/i,\n },\n {\n id: 'log-ssti-percent-tag',\n description: 'SSTI ERB/ASP tag: <%= expression %> — Ruby ERB, ASP',\n pattern: /<%=[\\s\\S]{0,80}%>/,\n },\n\n // ─── Null byte ────────────────────────────────────────────────────────────\n {\n id: 'log-null-byte',\n description: 'Null byte: \\\\x00 or %00 — can truncate log entries in C-backed loggers',\n pattern: /\\x00|%00/,\n },\n\n // ─── ANSI escape injection ────────────────────────────────────────────────\n {\n id: 'log-ansi-escape',\n description: 'ANSI escape sequence: ESC[ — can manipulate terminal output when logs are tailed',\n pattern: /\\x1b\\[/,\n },\n];\n\nexport default LOG_PATTERNS;\n","/**\n * is-unsafe\n *\n * Zero-dependency, DOM-free, pure predicate for detecting unsafe strings\n * across HTML, XML, SVG, SQL, SQL-STRICT, SHELL, REDOS, NOSQL, and LOG contexts.\n *\n * @module is-unsafe\n */\n\nimport CONTEXT_REGISTRY, { VALID_CONTEXTS } from './registry.js';\n\n/**\n * @typedef {'HTML'|'XML'|'SVG'|'SQL'|'SQL-STRICT'|'SHELL'|'REDOS'|'NOSQL'|'LOG'} ContextName\n */\n\n/**\n * @typedef {Object} MatchResult\n * @property {string} context - The context in which the match was found\n * @property {string} id - Rule identifier\n * @property {string} description - Human-readable description of what was matched\n * @property {RegExp} pattern - The pattern that matched\n */\n\n// ─── Validation helpers ────────────────────────────────────────────────────\n\n/**\n * Validate that `value` is a string. Throws TypeError if not.\n * @param {unknown} value\n */\nfunction assertString(value) {\n if (typeof value !== 'string') {\n throw new TypeError(\n `is-unsafe: first argument must be a string, got ${typeof value}`\n );\n }\n}\n\n/**\n * Validate that `context` is a recognised context name, an array of them,\n * or a RegExp instance. Throws TypeError if not.\n * @param {ContextName|ContextName[]|RegExp} context\n */\nfunction assertContext(context) {\n if (context instanceof RegExp) return;\n\n if (typeof context === 'string') {\n if (!CONTEXT_REGISTRY[context]) {\n throw new TypeError(\n `is-unsafe: unknown context \"${context}\". Valid contexts: ${Object.keys(VALID_CONTEXTS).join(', ')}`\n );\n }\n return;\n }\n\n if (Array.isArray(context)) {\n if (context.length === 0) {\n throw new TypeError('is-unsafe: context array must not be empty');\n }\n for (const c of context) {\n if (typeof c !== 'string' || !CONTEXT_REGISTRY[c]) {\n throw new TypeError(\n `is-unsafe: unknown context \"${c}\" in array. Valid contexts: ${Object.keys(VALID_CONTEXTS).join(', ')}`\n );\n }\n }\n return;\n }\n\n throw new TypeError(\n `is-unsafe: second argument must be a context string, array of context strings, or RegExp. Got: ${typeof context}`\n );\n}\n\n// ─── Core matching logic ───────────────────────────────────────────────────\n\n/**\n * Test a single value against one named context's patterns.\n * Returns the first matching MatchResult, or null if nothing matched.\n *\n * @param {string} value\n * @param {string} contextName\n * @returns {MatchResult|null}\n */\nfunction matchContext(value, contextName) {\n const patterns = CONTEXT_REGISTRY[contextName];\n for (const rule of patterns) {\n if (rule.pattern.test(value)) {\n return { context: contextName, id: rule.id, description: rule.description, pattern: rule.pattern };\n }\n }\n return null;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if `value` is unsafe in the given context(s), `false` otherwise.\n *\n * @param {string} value - The string to test\n * @param {ContextName|ContextName[]|RegExp} context\n * - A named context ('HTML', 'XML', 'SVG', 'SQL', 'SQL-STRICT', 'SHELL', 'REDOS', 'NOSQL', 'LOG')\n * - An array of named contexts — returns true if unsafe in **any** of them\n * - A custom RegExp — returns true if the pattern matches\n * @returns {boolean}\n *\n * @example\n * isUnsafe('<script>alert(1)</script>', 'HTML') // true\n * isUnsafe('hello world', 'HTML') // false\n * isUnsafe('value', ['HTML', 'SQL']) // false\n * isUnsafe('value', /my-pattern/i) // false\n */\nfunction isUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n // Custom RegExp — caller-supplied pattern\n if (context instanceof RegExp) {\n return context.test(value);\n }\n\n // Single named context\n if (typeof context === 'string') {\n return matchContext(value, context) !== null;\n }\n\n // Array of named contexts — unsafe if ANY context matches\n for (const c of context) {\n if (matchContext(value, c) !== null) return true;\n }\n return false;\n}\n\n/**\n * Like `isUnsafe`, but instead of a boolean returns the first `MatchResult`\n * describing **why** the value was flagged, or `null` if it is safe.\n *\n * Useful for logging, error messages, or policy reporting.\n *\n * @param {string} value\n * @param {ContextName|ContextName[]|RegExp} context\n * @returns {MatchResult|null}\n *\n * @example\n * whyUnsafe('<script>alert(1)</script>', 'HTML')\n * // { context: 'HTML', id: 'html-script-open', description: '...', pattern: /.../ }\n */\nfunction whyUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n if (context instanceof RegExp) {\n return context.test(value)\n ? { context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: context }\n : null;\n }\n\n if (typeof context === 'string') {\n return matchContext(value, context);\n }\n\n for (const c of context) {\n const result = matchContext(value, c);\n if (result !== null) return result;\n }\n return null;\n}\n\n/**\n * Returns all matching rules across the given context(s), or an empty array\n * if the value is safe. Useful for comprehensive auditing.\n *\n * @param {string} value\n * @param {ContextName|ContextName[]|RegExp} context\n * @returns {MatchResult[]}\n */\nfunction allUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const results = [];\n\n if (context instanceof RegExp) {\n if (context.test(value)) {\n results.push({ context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: context });\n }\n return results;\n }\n\n const contexts = typeof context === 'string' ? [context] : context;\n\n for (const c of contexts) {\n const patterns = CONTEXT_REGISTRY[c];\n for (const rule of patterns) {\n if (rule.pattern.test(value)) {\n results.push({ context: c, id: rule.id, description: rule.description, pattern: rule.pattern });\n }\n }\n }\n\n return results;\n}\n\nexport { isUnsafe, whyUnsafe, allUnsafe, VALID_CONTEXTS };\nexport default isUnsafe;","export default class BaseValueParser {\n constructor(IS_FINAL = false) {\n this.IS_FINAL = IS_FINAL;\n }\n /**\n * Called once per parse by `ValueParserPipeline` after construction.\n * Subclasses that need shared XML metadata (version, entities) read it\n * from `ctx` lazily inside `parse()` — it is fully populated by the time\n * the first value is parsed.\n * @param {import('../ValueParser.js').SharedContext} ctx\n */\n init(ctx) { // By ValueParserPipeline\n this.ctx = ctx;\n }\n\n /**\n * Reset internal state between document parses.\n * Called by `BaseOutputBuilderFactory.resetValueParsers()` before each parse.\n * Stateful subclasses (e.g. `EntityParser`) must override this.\n */\n reset() {\n //Do nothing\n }\n\n /**\n * Transform `val` and return the result.\n * Return a `FinalValue` instance to stop the pipeline early (unless\n * `this.IS_FINAL` is false, in which case the pipeline continues).\n *\n * @param {*} val\n * @param {import('../ValueParser.js').Context} [runTimeContext]\n * @returns {*}\n */\n parse(val, runTimeContext) {\n throw new Error(\"You must implement parse() in a value parser.\");\n }\n\n}\n","import { EntityDecoder, XML, COMMON_HTML, ENTITY_ACTION } from '@nodable/entities';\nimport { isUnsafe, VALID_CONTEXTS } from \"is-unsafe\"\n\nimport BaseValueParser from \"./BaseValueParser.js\"\n\nconst defaultOptions = {\n namedEntities: { ...XML },\n numericAllowed: true,\n //limit: {},\n onInputEntity: (name, value) =>\n isUnsafe(value, [VALID_CONTEXTS.XML])\n ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW,\n}\n\nexport default class EntityParser extends BaseValueParser {\n #seen = false;\n #decoder = null;\n #options = null;\n /**\n * @param {object} options \n * @param {boolean} isFinal true if it should skip rest of the value parsers in the chain\n */\n constructor(options, isFinal = false) { // By user options (optional)\n super(isFinal);\n this.#options = {\n ...defaultOptions,\n ...(options || {}),\n // Additive merge: user-supplied namedEntities extend, not replace, the defaults.\n namedEntities: { ...defaultOptions.namedEntities, ...(options?.namedEntities || {}) },\n };\n }\n\n #ensureDecoder() {\n if (!this.#decoder) {\n this.#decoder = new EntityDecoder(this.#options);\n }\n if (!this.#seen) {\n const version = this.ctx?.get('xmlVersion');\n const entities = this.ctx?.get('inputEntities');\n if (version) this.#decoder.setXmlVersion(version);\n if (entities) this.#decoder.addInputEntities(entities);\n this.#seen = true;\n }\n }\n\n reset() {\n this.#seen = false;\n if (this.#decoder) {\n this.#decoder.reset();\n }\n }\n\n parse(val) {\n if (typeof val !== 'string') return val;\n this.#ensureDecoder();\n return this.#decoder.decode(val);\n }\n\n}\n","/**\n * Flat lookup table: maps Unicode code point → ASCII digit (0-9).\n * Only decimal digit characters (Unicode category Nd) are included.\n *\n * Strategy: Int32Array of size (maxCodePoint - minCodePoint + 1).\n * Value 0xFF means \"not a digit\". Value 0-9 is the ASCII digit value.\n * This gives O(1) lookup with no branching, no bisect, no loop.\n *\n * Memory: range is 0x0660 to 0x1FBF0 → ~129,936 entries × 1 byte = ~127 KB.\n * Acceptable for a one-time init; lookup is a single array index.\n */\n\n// All known Unicode Nd (decimal digit) script zero code points.\n// Each script has exactly 10 consecutive digits: zero+0 .. zero+9.\nconst SCRIPT_ZEROS = [\n // Basic Latin (ASCII) — included for completeness / pass-through\n 0x0030, // 0-9\n\n // Arabic scripts\n 0x0660, // Arabic-Indic ٠١٢٣٤٥٦٧٨٩\n 0x06F0, // Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳\n\n // Indic scripts\n 0x0966, // Devanagari ०१२३४५६७८९\n 0x09E6, // Bengali ০১২৩৪৫৬৭৮৯\n 0x0A66, // Gurmukhi ੦੧੨੩੪੫੬੭੮੯\n 0x0AE6, // Gujarati ૦૧૨૩૪૫૬૭૮૯\n 0x0B66, // Odia ୦୧୨୩୪୫୬୭୮୯\n 0x0BE6, // Tamil ௦௧௨௩௪௫௬௭௮௯\n 0x0C66, // Telugu ౦౧౨౩౪౫౬౭౮౯\n 0x0CE6, // Kannada ೦೧೨೩೪೫೬೭೮೯\n 0x0D66, // Malayalam ൦൧൨൩൪൫൬൭൮൯\n 0x0DE6, // Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯\n\n // Southeast Asian scripts\n 0x0E50, // Thai ๐๑๒๓๔๕๖๗๘๙\n 0x0ED0, // Lao ໐໑໒໓໔໕໖໗໘໙\n 0x0F20, // Tibetan ༠༡༢༣༤༥༦༧༨༩\n 0x1040, // Myanmar ၀၁၂၃၄၅၆၇၈၉\n 0x1090, // Myanmar Shan ႐႑႒႓႔႕႖႗႘႙\n 0x17E0, // Khmer ០១២៣៤៥៦៧៨៩\n 0x1810, // Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙\n 0x1946, // Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏\n 0x19D0, // New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙\n 0x1A80, // Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉\n 0x1A90, // Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙\n 0x1B50, // Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙\n 0x1BB0, // Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹\n 0x1C40, // Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉\n 0x1C50, // Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙\n\n // Fullwidth (CJK context)\n 0xFF10, // Fullwidth 0123456789\n\n // Mathematical digit variants (Unicode math block)\n 0x1D7CE, // Mathematical Bold\n 0x1D7D8, // Mathematical Double-Struck\n 0x1D7E2, // Mathematical Sans-Serif\n 0x1D7EC, // Mathematical Sans-Serif Bold\n 0x1D7F6, // Mathematical Monospace\n\n // Other scripts\n 0x104A0, // Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩\n 0x10D30, // Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹\n 0x11066, // Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯\n 0x110F0, // Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹\n 0x11136, // Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿\n 0x111D0, // Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙\n 0x112F0, // Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹\n 0x11450, // Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙\n 0x114D0, // Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙\n 0x11650, // Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙\n 0x116C0, // Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉\n 0x11730, // Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹\n 0x118E0, // Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩\n 0x11950, // Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙\n 0x11BF0, // Khitan Small Script \n 0x11C50, // Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙\n 0x11D50, // Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙\n 0x11DA0, // Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩\n 0x11F50, // Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙\n 0x16A60, // Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩\n 0x16AC0, // Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉\n 0x16B50, // Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙\n 0x1E140, // Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉\n 0x1E2F0, // Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹\n 0x1E4F0, // Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹\n 0x1E950, // Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙\n 0x1FBF0, // Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹\n];\n\n// Build a sparse Map for scripts above 0xFFFF (surrogate-pair range).\n// These can't go into a flat Uint8Array indexed by code point efficiently.\nconst NOT_DIGIT = 0xFF;\nconst HIGH_MAP = new Map(); // codePoint → digit value (0-9)\n\nconst LOW_MAX = 0xFFFF;\nconst LOW_MIN = 0x0660; // first non-ASCII digit script\n\n// Flat Uint8Array covering 0x0660 .. 0xFFFF\nconst TABLE_OFFSET = LOW_MIN;\nconst TABLE_SIZE = LOW_MAX - LOW_MIN + 1;\nconst TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);\n\nfor (const zero of SCRIPT_ZEROS) {\n for (let d = 0; d < 10; d++) {\n const cp = zero + d;\n if (cp <= LOW_MAX) {\n TABLE[cp - TABLE_OFFSET] = d;\n } else {\n HIGH_MAP.set(cp, d);\n }\n }\n}\n\nexport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT };\n","'use strict';\n\nimport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT } from './digitTable.js';\n\nconst CHAR_0 = 48; // '0'.charCodeAt(0)\nconst CHAR_9 = 57; // '9'.charCodeAt(0)\nconst CHAR_MINUS = 45; // '-'.charCodeAt(0)\n\n// Unicode minus/hyphen variants worth normalizing to ASCII '-' in numeric context:\n// U+2212 MINUS SIGN − (mathematically correct minus)\n// U+FF0D FULLWIDTH HYPHEN-MINUS - (Japanese fullwidth context)\n// U+FE63 SMALL HYPHEN-MINUS ﹣ (small form variant)\n//\n// NOT normalized (deliberate):\n// U+2013 EN DASH – (punctuation, not a numeric sign)\n// U+2014 EM DASH — (punctuation)\n// U+2010 HYPHEN ‐ (typographic hyphen)\n//\n// Rationale: only characters a human or locale formatter would plausibly use\n// as a numeric minus sign are normalized. Dashes used for punctuation are left\n// alone to avoid mangling non-numeric strings.\nconst MINUS_SET = new Set([0x2212, 0xFF0D, 0xFE63]);\n\n/**\n * Normalize all Unicode decimal digit characters in a string to ASCII (0-9),\n * and normalize Unicode minus variants to ASCII '-' (U+002D).\n *\n * Non-digit, non-minus characters are passed through unchanged.\n *\n * Performance design:\n * - Fast path: if the string has no convertible characters, return it unchanged\n * (zero allocation).\n * - BMP digits (0x0660..0xFFFF excl. surrogates): flat Uint8Array lookup (O(1)).\n * - Supplementary plane digits (> 0xFFFF, encoded as surrogate pairs): Map lookup.\n * - Minus variants: checked inline with a small fixed Set.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction anynum(str) {\n if (typeof str !== 'string') return str;\n\n const len = str.length;\n if (len === 0) return str;\n\n // Scan for first character needing conversion.\n // If none found, return original string (zero allocation).\n let firstHit = -1;\n\n for (let i = 0; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — already normalized, skip fast\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) continue;\n\n // Below first unicode digit script — check minus variants only\n if (cc < TABLE_OFFSET) {\n if (MINUS_SET.has(cc)) { firstHit = i; break; }\n continue;\n }\n\n // Surrogate pairs live in BMP range 0xD800-0xDFFF — check before TABLE\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n if (HIGH_MAP.has(cp)) { firstHit = i; break; }\n }\n }\n continue;\n }\n\n // BMP non-surrogate: flat table lookup; also check minus variants in this range\n if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {\n firstHit = i;\n break;\n }\n }\n\n // Nothing to replace — return original, zero allocation\n if (firstHit === -1) return str;\n\n // Build result: copy unchanged prefix, then convert from firstHit onward\n const chars = [];\n\n if (firstHit > 0) chars.push(str.slice(0, firstHit));\n\n for (let i = firstHit; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — pass through\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) {\n chars.push(str[i]);\n continue;\n }\n\n // Below TABLE_OFFSET — check minus variants, else pass through\n if (cc < TABLE_OFFSET) {\n chars.push(MINUS_SET.has(cc) ? '-' : str[i]);\n continue;\n }\n\n // Surrogate pairs\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n const d = HIGH_MAP.get(cp);\n if (d !== undefined) {\n chars.push(String.fromCharCode(d + 48));\n i++; // consume low surrogate\n continue;\n }\n }\n }\n chars.push(str[i]);\n continue;\n }\n\n // BMP non-surrogate: flat table lookup + minus variants\n if (MINUS_SET.has(cc)) {\n chars.push('-');\n continue;\n }\n const d = TABLE[cc - TABLE_OFFSET];\n chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str[i]);\n }\n\n return chars.join('');\n}\n\nexport { anynum };\nexport default anynum;","const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;\nconst binRegex = /^0b[01]+$/;\nconst octRegex = /^0o[0-7]+$/;\nconst numRegex = /^([\\-\\+])?(0*)([0-9]*(\\.[0-9]*)?)$/;\n\nimport anynum from \"anynum\";\n\nconst consider = {\n hex: true,\n binary: false,\n octal: false,\n leadingZeros: true,\n decimalPoint: \"\\.\",\n eNotation: true,\n //skipLike: /regex/,\n infinity: \"original\", // \"null\", \"infinity\" (Infinity type), \"string\" (\"Infinity\" (the string literal))\n unicode: false,\n};\n\nexport default function toNumber(str, options = {}) {\n options = Object.assign({}, consider, options);\n if (!str || typeof str !== \"string\") return str;\n\n let trimmedStr = str.trim();\n\n if (trimmedStr.length === 0) return str;\n else if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;\n else if (trimmedStr === \"0\") return 0;\n\n if (options.unicode) {\n trimmedStr = anynum(trimmedStr);\n if (trimmedStr === \"0\") return 0; // re-check after normalization\n }\n if (options.hex && hexRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 16);\n } else if (options.binary && binRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 2);\n } else if (options.octal && octRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 8);\n } else if (!isFinite(trimmedStr)) { //Infinity\n return handleInfinity(str, Number(trimmedStr), options);\n } else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation\n return resolveEnotation(str, trimmedStr, options);\n } else {\n //separate negative sign, leading zeros, and rest number\n const match = numRegex.exec(trimmedStr);\n // +00.123 => [ , '+', '00', '.123', ..\n if (match) {\n const sign = match[1] || \"\";\n const leadingZeros = match[2];\n let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros\n const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000.\n str[leadingZeros.length + 1] === \".\"\n : str[leadingZeros.length] === \".\";\n\n //trim ending zeros for floating number\n if (!options.leadingZeros //leading zeros are not allowed\n && (leadingZeros.length > 1\n || (leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros))) {\n // 00, 00.3, +03.24, 03, 03.24\n return str;\n }\n else {//no leading zeros or leading zeros are allowed\n const num = Number(trimmedStr);\n const parsedStr = String(num);\n\n if (num === 0) return num;\n if (parsedStr.search(/[eE]/) !== -1) { //given number is long and parsed to eNotation\n if (options.eNotation) return num;\n else return str;\n } else if (trimmedStr.indexOf(\".\") !== -1) { //floating number\n if (parsedStr === \"0\") return num; //0.0\n else if (parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000\n else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;\n else return str;\n }\n\n let n = leadingZeros ? numTrimmedByZeros : trimmedStr;\n if (leadingZeros) {\n // -009 => -9\n return (n === parsedStr) || (sign + n === parsedStr) ? num : str\n } else {\n // +9\n return (n === parsedStr) || (n === sign + parsedStr) ? num : str\n }\n }\n } else { //non-numeric string\n return str;\n }\n }\n}\n\nconst eNotationRegx = /^([-+])?(0*)(\\d*(\\.\\d*)?[eE][-\\+]?\\d+)$/;\nfunction resolveEnotation(str, trimmedStr, options) {\n if (!options.eNotation) return str;\n const notation = trimmedStr.match(eNotationRegx);\n if (notation) {\n let sign = notation[1] || \"\";\n const eChar = notation[3].indexOf(\"e\") === -1 ? \"E\" : \"e\";\n const leadingZeros = notation[2];\n const eAdjacentToLeadingZeros = sign ? // 0E.\n str[leadingZeros.length + 1] === eChar\n : str[leadingZeros.length] === eChar;\n\n if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str;\n else if (leadingZeros.length === 1\n && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {\n return Number(trimmedStr);\n } else if (leadingZeros.length > 0) {\n // Has leading zeros — only accept if leadingZeros option allows it\n if (options.leadingZeros && !eAdjacentToLeadingZeros) {\n trimmedStr = (notation[1] || \"\") + notation[3];\n return Number(trimmedStr);\n } else return str;\n } else {\n // No leading zeros — always valid e-notation, parse it\n return Number(trimmedStr);\n }\n } else {\n return str;\n }\n}\n\n/**\n * \n * @param {string} numStr without leading zeros\n * @returns \n */\nfunction trimZeros(numStr) {\n if (numStr && numStr.indexOf(\".\") !== -1) {//float\n numStr = numStr.replace(/0+$/, \"\"); //remove ending zeros\n if (numStr === \".\") numStr = \"0\";\n else if (numStr[0] === \".\") numStr = \"0\" + numStr;\n else if (numStr[numStr.length - 1] === \".\") numStr = numStr.substring(0, numStr.length - 1);\n return numStr;\n }\n return numStr;\n}\n\nfunction parse_int(numStr, base) {\n const str = numStr.trim();\n if (base === 2 || base === 8) numStr = str.substring(2);\n\n if (parseInt) return parseInt(numStr, base);\n else if (Number.parseInt) return Number.parseInt(numStr, base);\n else if (window && window.parseInt) return window.parseInt(numStr, base);\n else throw new Error(\"parseInt, Number.parseInt, window.parseInt are not supported\");\n}\n\n/**\n * Handle infinite values based on user option\n * @param {string} str - original input string\n * @param {number} num - parsed number (Infinity or -Infinity)\n * @param {object} options - user options\n * @returns {string|number|null} based on infinity option\n */\nfunction handleInfinity(str, num, options) {\n const isPositive = num === Infinity;\n\n switch (options.infinity.toLowerCase()) {\n case \"null\":\n return null;\n case \"infinity\":\n return num; // Return Infinity or -Infinity\n case \"string\":\n return isPositive ? \"Infinity\" : \"-Infinity\";\n case \"original\":\n default:\n return str; // Return original string like \"1e1000\"\n }\n}","import toNumber from 'strnum';\nimport BaseValueParser from \"./BaseValueParser.js\"\nimport { FinalValue } from \"./../ValueParser.js\"\n\n/**\n * Number parser class that wraps the strnum toNumber function\n * Provides consistent API for value parsing in flexible-xml-parser\n */\nexport default class numParser extends BaseValueParser {\n constructor(options, isFinal = false) {\n super(isFinal);\n this.options = options || {};\n }\n\n /**\n * Parse a value, converting strings to numbers based on options\n * @param {*} val - Value to parse\n * @returns {*} Parsed value (number if successfully parsed, otherwise original value)\n */\n parse(val) {\n if (typeof val === 'string') {\n const newval = toNumber(val, this.options);\n if (typeof newval !== val) {\n return this.IS_FINAL ? new FinalValue(newval) : newval;\n }\n }\n return val;\n }\n}\n","\nimport EntityParser from './ValueParsers/EntityParser.js';\nimport WSNormalizer from './ValueParsers/WSNormalizer.js';\nimport trimParser from './ValueParsers/trim.js';\nimport booleanParser from './ValueParsers/booleanParser.js';\nimport numberParser from './ValueParsers/number.js';\n// import currencyParser from './ValueParsers/currency.js';\nimport BaseValueParser from './ValueParsers/BaseValueParser.js';\n\n\nconst defaultValParsers = {\n \"entity\": new EntityParser(),\n \"trim\": new trimParser(),\n \"ws\": new WSNormalizer(),\n \"boolean\": new booleanParser(),\n \"number\": new numberParser({ hex: true, leadingZeros: true, eNotation: true }),\n // \"currency\": new currencyParser(),\n};\n\nexport default class ValueParserRegistry {\n constructor() {\n this.registered = { ...defaultValParsers };\n }\n\n /**\n * @param {string} name\n * @param {BaseValueParser} parser\n */\n register(name, parser) {\n if (!name || typeof name !== 'string') {\n throw new Error('name must be a string');\n }\n if (!parser) {\n throw new Error('parser is required');\n }\n if (!parser.reset || typeof parser.reset !== 'function') {\n throw new Error('parser must implement reset()');\n }\n if (!parser.parse || typeof parser.parse !== 'function') {\n throw new Error('parser must implement parse()');\n }\n this.registered[name] = parser;\n }\n\n reset(name) {\n this.registered[name]?.reset();\n }\n\n resetAll() {\n for (const vp in this.registered) {\n this.registered[vp]?.reset();\n }\n }\n\n /**\n * \n * @param {string} name \n * @returns {BaseValueParser}\n */\n get(name) {\n const ret = this.registered[name];\n if (!ret) {\n throw new Error('parser not found: ' + name);\n }\n return ret;\n }\n}","import BaseValueParser from \"./BaseValueParser.js\"\n// import { FinalValue } from \"./../ValueParser.js\"\n\nexport default class trimmer extends BaseValueParser {\n parse(val) {\n if (typeof val === \"string\") return val.trim();\n else return val;\n }\n}\n","import { Expression, ExpressionSet } from 'path-expression-matcher';\nimport BaseValueParser from \"./BaseValueParser.js\"\n// import { FinalValue } from \"./../ValueParser.js\"\n\n/**\n * Whitespace-normalizing value parser.\n *\n * Collapses runs of whitespace (spaces, tabs, newlines) to a single space\n * and trims leading/trailing whitespace from string tag values. Replaces\n * the `'trim'` parser in the default chain.\n *\n * Normalization is skipped when any of these conditions hold:\n * 1. The value is not a string.\n * 2. The element is an attribute.\n * 3. Any ancestor element has `xml:space=\"preserve\"` (requires the XML\n * parser to be configured with `{ keep: ['space'] }` on that tag).\n * 4. The current tag path matches a user-supplied exclusion expression\n * (e.g. `[\"..pre\", \"..code\", \"..script\"]`).\n *\n * @example\n * import { WSNormalizer } from '@nodable/base-output-builder';\n * import { CompactBuilderFactory } from '@nodable/compact-builder';\n *\n * const ws = new WSNormalizer({ tags: ['..pre', '..code'] });\n * const builder = new CompactBuilderFactory({\n * tags: { valueParsers: ['entity', ws, 'boolean', 'number'] },\n * });\n */\nexport default class WSNormalizer extends BaseValueParser {\n /**\n * @param {{ exclude?: Array<string|import('path-expression-matcher').Expression> }} [options]\n * `exclude` — paths whose whitespace should be left untouched (e.g. `[\"..pre\", \"..code\"]`).\n * Accepts plain expression strings or pre-built `Expression` objects.\n */\n constructor(options, isFinal = false) {\n super(isFinal);\n const exclude = options?.exclude ?? [];\n\n const set = new ExpressionSet();\n for (const entry of exclude) {\n set.add(typeof entry === 'string' ? new Expression(entry) : entry);\n }\n set.seal();\n\n /** @type {ExpressionSet} */\n this._excludeSet = set;\n }\n\n /**\n * Normalize whitespace in `val` unless an exclusion condition applies.\n *\n * @param {*} val\n * @param {import('../ValueParser.js').Context} [ctx]\n * @returns {*}\n */\n parse(val, ctx) {\n if (typeof val !== 'string') return val;\n\n if (ctx) {\n // Only normalize element text, not attribute values\n if (ctx.isAttribute) return val;\n\n if (ctx.matcher) {\n // Respect xml:space=\"preserve\" on any ancestor\n if (ctx.matcher.getAnyParentAttr('xml:space') === 'preserve') return val;\n\n // Respect user-configured exclusion paths\n if (this._excludeSet.size > 0 && this._excludeSet.matchesAny(ctx.matcher)) return val;\n }\n }\n\n return normalizeSpaces(val);\n }\n}\n\n/**\n * Collapse all whitespace runs to a single space and trim both ends.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction normalizeSpaces(str) {\n return str.replace(/[ \\t\\r\\n]+/g, ' ').trim();\n}\n","import BaseValueParser from \"./BaseValueParser.js\";\nimport { FinalValue } from \"../ValueParser.js\";\n\nexport default class boolParser extends BaseValueParser {\n constructor(trueList, falseList, isFinal = false) {\n super(isFinal)\n if (trueList)\n this.trueList = trueList;\n else\n this.trueList = [\"true\"];\n\n if (falseList)\n this.falseList = falseList;\n else\n this.falseList = [\"false\"];\n }\n parse(val) {\n if (typeof val === 'string') {\n //TODO: performance: don't convert\n const temp = val.toLowerCase();\n if (this.trueList.indexOf(temp) !== -1) return this.IS_FINAL ? new FinalValue(true) : true;\n else if (this.falseList.indexOf(temp) !== -1) return this.IS_FINAL ? new FinalValue(false) : false;\n }\n return val;\n }\n}\n","import ValueParserRegistry from './ValueParserRegistry.js';\nimport BaseValueParser from './ValueParsers/BaseValueParser.js';\n\n/**\n * Context object passed to every value parser's `parse(val, context)` call.\n */\nexport class Context {\n /**\n * @param {string} elementName Tag or attribute name\n * @param {import('path-expression-matcher').MatcherView} matcher Read-only path inspector\n * @param {boolean|null} isLeafNode True when element has no child elements\n * @param {boolean} isAttribute True when attribute type\n */\n constructor(elementName, matcher, isLeafNode, isAttribute = false) {\n this.elementName = elementName;\n this.matcher = matcher;\n this.isLeafNode = isLeafNode;\n this.isAttribute = isAttribute;\n }\n}\n\n/**\n * Sentinel returned by a value parser to stop the pipeline immediately.\n *\n * When `ValueParserPipeline.run()` sees a `FinalValue` result it unwraps\n * `.value` and returns it without running any subsequent parsers.\n *\n * Whether to emit `FinalValue` is the parser's own decision, controlled by\n * its `IS_FINAL` constructor option (not injected by the pipeline).\n *\n * @example\n * import { FinalValue } from '@nodable/base-output-builder/ValueParser.js';\n *\n * class StopOnNull extends BaseValueParser {\n * constructor(options, IS_FINAL) {\n * super(IS_FINAL);\n * this.options = options || {};\n * }\n * parse(val) {\n * if (val === 'null') {\n * return this.IS_FINAL ? new FinalValue(null) : null;\n * }\n * return val;\n * }\n * }\n */\nexport class FinalValue {\n /** @param {*} value The resolved value to return from the pipeline */\n constructor(value) {\n this.value = value;\n }\n}\n\n/**\n * Mutable key-value store owned by `BaseOutputBuilder` and shared with every\n * value parser that opts in via `init(ctx)`.\n *\n * A fresh `SharedContext` is created for each document parse by\n * `BaseOutputBuilderFactory.getInstance()`, so there is no risk of stale\n * data from a previous document leaking into the next.\n *\n * ## Well-known keys (set by `BaseOutputBuilder`)\n * - `'xmlVersion'` — numeric XML version from `<?xml version=\"…\"?>`\n * - `'inputEntities'` — entity map from the DOCTYPE block\n *\n * ## Extension\n * Parsers may also write to the context to communicate with later parsers in\n * the same pipeline (e.g. a locale detector writing `'detectedLocale'` for a\n * downstream date formatter).\n *\n * @example\n * const ctx = new SharedContext();\n * ctx.set('xmlVersion', 1.1);\n * ctx.get('xmlVersion'); // → 1.1\n */\nexport class SharedContext {\n #data = {};\n\n /** @param {string} key @param {*} value */\n set(key, value) { this.#data[key] = value; }\n\n /** @param {string} key @returns {*} */\n get(key) { return this.#data[key]; }\n\n /**\n * Remove all entries.\n * Not needed when the factory creates a fresh SharedContext per parse\n * (the current approach), but available for alternative setups.\n */\n clear() { this.#data = {}; }\n}\n\n/**\n * Owns a value-parser registry and a configured parser chain, and executes\n * them in order on demand.\n *\n * Builders receive two pre-built pipelines from their factory:\n * - `tagsPipeline` — for tag text content\n * - `attrsPipeline` — for attribute values\n *\n * Both pipelines share the same `SharedContext` instance and the same parser\n * instances from the registry. `init(ctx)` is called on every parser that\n * implements it when the pipeline is constructed, and again on any parser\n * added later via `register()`.\n *\n * Because a fresh pipeline is built for every document parse, parsers receive\n * an up-to-date `ctx` reference each time — no stale state.\n *\n * `IS_FINAL` is a parser-level decision set in each parser's own constructor,\n * not injected by the pipeline.\n *\n * @example\n * const ctx = new SharedContext();\n * const pipeline = new ValueParserPipeline(\n * ['entity', 'ws', 'boolean', 'number'],\n * registry,\n * ctx,\n * );\n * const result = pipeline.run(' true ', context); // → true\n */\nexport class ValueParserPipeline {\n /**\n * @param {Array<BaseValueParser>} valParsers Ordered parser names or instances\n * @param {ValueParserRegistry} registry Map of name → parser instance\n * @param {SharedContext} [sharedContext]\n */\n constructor(valParsers = [], registry, sharedContext = null) {\n this.valParsers = valParsers;\n this.registry = registry;\n this.sharedContext = sharedContext || new SharedContext();\n\n this._initAll(valParsers);\n }\n\n /**\n * Execute the parser chain on `val`.\n *\n * Each parser receives `(currentValue, context)`. If a parser returns a\n * {@link FinalValue} the chain stops and the wrapped value is returned.\n *\n * @param {*} val\n * @param {Context} [runtimContext]\n * @returns {*}\n */\n run(val, runtimContext) {\n for (let i = 0; i < this.valParsers.length; i++) {\n let parser = this.valParsers[i];\n if (typeof parser === 'string') parser = this.registry.get(parser);\n if (parser) {\n const result = parser.parse(val, runtimContext);\n if (result instanceof FinalValue) return result.value;\n val = result;\n }\n }\n return val;\n }\n\n resetAll() {\n for (let i = 0; i < this.valParsers.length; i++) {\n let parser = this.valParsers[i];\n if (typeof parser === 'string') parser = this.registry.get(parser);\n if (parser) if (parser.reset && typeof parser.reset === 'function') parser.reset();\n }\n }\n\n /**\n * Add or replace a named parser in the registry.\n * If the instance implements `init()` and the pipeline has a `SharedContext`,\n * `init` is called immediately so the parser is ready before the next `run()`.\n *\n * @param {string} name\n * @param {object} instance Must implement `parse(val, context?)`\n */\n register(name, instance) {\n this.registry.register(name, instance);\n if (instance.init) instance.init(this.sharedContext);\n }\n\n // ── private ──────────────────────────────────────────────────────────────\n\n /**\n * Call `init(sharedContext)` on each parser in `instances` that implements it.\n * Used only during construction to wire up the initial registry.\n *\n * A local `Set` deduplicates in case the same instance is registered under\n * multiple names (e.g. an alias). A module-level WeakSet is intentionally\n * NOT used: pipelines are rebuilt on every `getInstance()` call, so parsers\n * must receive `init()` fresh each parse with the new `SharedContext` ref.\n *\n * @param {object[]} instances\n */\n _initAll(instances) {\n if (!this.sharedContext) return;\n const seen = new Set();\n for (const p of instances) {\n let parserInstance = p;\n if (typeof p === 'string') parserInstance = this.registry.get(p);\n if (parserInstance && typeof parserInstance.init === 'function' && !seen.has(parserInstance)) {\n seen.add(parserInstance);\n parserInstance.init(this.sharedContext);\n }\n }\n }\n}","import { MatcherView } from 'path-expression-matcher';\nimport { Context, SharedContext, ValueParserPipeline } from './ValueParser.js';\nimport ValueParserRegistry from './ValueParserRegistry.js';\n\n// Default parser chains.\nconst DEFAULT_TAG_PARSERS = ['ws', 'entity', 'boolean', 'number'];\nconst DEFAULT_ATTR_PARSERS = ['entity', 'boolean', 'number'];\n\nexport default class BaseOutputBuilder {\n\n /**\n * @param {object} parserOptions\n * @param {object} builderOptions\n * @param {MatcherView} matcherView\n * @param {ValueParserRegistry} registry\n * @param {boolean} resetPipelines\n */\n constructor(parserOptions, builderOptions, matcherView, registry, resetPipelines = true) {\n this.matcher = matcherView;\n this._rootName = \"^\";\n this.parserOptions = parserOptions;\n this.builderOptions = builderOptions;\n this.registry = registry;\n\n const tagChain = builderOptions?.tags?.valueParsers ?? DEFAULT_TAG_PARSERS;\n const attrChain = builderOptions?.attributes?.valueParsers ?? DEFAULT_ATTR_PARSERS;\n\n /**\n * Shared mutable context distributed to all value parsers.\n * `BaseOutputBuilder` is the sole writer; parsers are readers (or\n * co-writers for cross-parser communication).\n * @type {SharedContext|null}\n */\n this.sharedContext = new SharedContext();\n /**\n * Pipeline for tag text values.\n * Call `this.tagsPipeline.run(val, context)` from subclass\n * `closeElement()` implementations.\n * @type {ValueParserPipeline}\n */\n this.tagsPipeline = new ValueParserPipeline(tagChain, this.registry, this.sharedContext);\n /**\n * Pipeline for attribute values.\n * Call `this.attrsPipeline.run(val, context)` from subclass\n * `addAttribute()` implementations.\n * @type {ValueParserPipeline}\n */\n this.attrsPipeline = new ValueParserPipeline(attrChain, this.registry, this.sharedContext);\n\n if (resetPipelines) {\n this.tagsPipeline.resetAll();\n this.attrsPipeline.resetAll();\n }\n\n this._pendingStopNode = false;\n }\n\n /**\n * Add a parsed attribute to the current element.\n * Only called when skip.attributes is false.\n *\n * @param {string} name Processed attribute name (prefix stripped, sanitised)\n * @param {*} value Raw attribute value\n * @param {object} matcher Read-only Matcher proxy from the parser\n */\n addAttribute(name, value, matcher) {\n // Capture XML version from the declaration tag and make it available to\n // value parsers (e.g. EntityParser) via SharedContext.\n if (name === \"version\" && this.tagName === this._rootName) {\n this.sharedContext?.set('xmlVersion', +value);\n }\n const prefixed = `${this.parserOptions.attributes.prefix}${name}${this.parserOptions.attributes.suffix}`;\n const context = new Context(name, matcher, true, true);// attributes are always leaf values\n this.attributes[prefixed] = this.attrsPipeline.run(value, context);\n\n }\n\n /** Hook for subclasses to append a named child node. */\n _addChild(key, val) { }\n\n /**\n * Add a comment node.\n * - Dropped entirely when skip.comment is true.\n * - Stored under nameFor.comment when set; '' = omit from output.\n */\n addComment(text) {\n if (this.parserOptions.skip.comment) return;\n if (this.parserOptions.nameFor.comment) {\n this._addChild(this.parserOptions.nameFor.comment, text);\n }\n }\n\n /**\n * Add a CDATA section.\n * - Dropped entirely when skip.cdata is true.\n * - Stored under nameFor.cdata when set; '' = merge into element text value.\n */\n addLiteral(text) {\n if (this.parserOptions.skip.cdata) return;\n if (this.parserOptions.nameFor.cdata) {\n this._addChild(this.parserOptions.nameFor.cdata, text);\n } else {\n this.addRawValue(text || \"\");\n }\n }\n\n /**\n * Add raw text directly to the current element's text value, bypassing the\n * value-parser chain. Used by addLiteral() when CDATA merges into element text.\n */\n addRawValue(text) {\n this.addValue(text);\n }\n\n /**\n * Receive DOCTYPE entities from the XML parser and store them in\n * SharedContext so that value parsers can access them on their first\n * `parse()` call.\n *\n * @param {object} entities — raw entity map from DocTypeReader\n */\n addInputEntities(entities) {\n this.sharedContext?.set('inputEntities', entities);\n }\n\n /**\n * Handle XML declaration (<?xml ... ?>).\n * Dropped when skip.declaration is true.\n */\n addDeclaration(name) {\n this.addInstruction(name);\n }\n\n /**\n * Handle a processing instruction.\n * Subclasses override; base is a no-op.\n */\n addInstruction(name) {\n }\n\n /**\n * Called when a stop node is fully collected, before `addValue`.\n *\n * @param {TagDetail} tagDetail - name, line, col, index of the stop node\n * @param {string} rawContent - raw unparsed content between the tags\n */\n onStopNode(tagDetail, rawContent) {\n this._pendingStopNode = true;\n if (typeof this.parserOptions.onStopNode === 'function') {\n this.parserOptions.onStopNode(tagDetail, rawContent, this.matcher);\n }\n }\n\n /**\n * Called by the parser when `exitIf` returns true for the current tag.\n * Receives a snapshot of the parser state at the moment of exit, after\n * all open tags have been cleanly closed by the parser.\n *\n * Override in subclasses to record the exit position or annotate output.\n *\n * @param {object} exitInfo\n * @param {object} exitInfo.tagDetail `{ name, line, col, index }` of the tag that triggered the exit\n * @param {object} exitInfo.matcher Read-only matcher at the moment exitIf fired\n * @param {number} exitInfo.depth Nesting depth at exit (0 = root children)\n */\n onExit(exitInfo) { }\n}\n","import BaseOutputBuilder from './BaseOutputBuilder.js';\nimport ValueParserRegistry from './ValueParserRegistry.js';\nimport { MatcherView } from 'path-expression-matcher';\n\n\n\nexport default class BaseOutputBuilderFactory {\n constructor(builderOptions = {}) {\n this.builderOptions = builderOptions;\n this.registry = new ValueParserRegistry();\n }\n\n /**\n * Add or replace a named value parser in the shared registry.\n * Takes effect for all builder instances created after this call.\n *\n * @param {string} name\n * @param {object} parserInstance Must implement `parse(val, context?)`\n */\n registerValueParser(name, parserInstance) {\n this.registry.register(name, parserInstance);\n }\n\n /**\n * Called by XMLParser before each document parse to obtain a fresh builder.\n * This resets all value parsers, builds separate pipeline to process values \n * of tags and attributes and builds a fresh shared context per document.\n *\n * @param {object} options XML Parser options\n * @param {MatcherView} matcherView\n * @returns {BaseOutputBuilder}\n */\n getInstance(parserOptions, matcherView) {\n // return new BaseOutputBuilder(\n // parserOptions,\n // this.builderOptions,\n // matcherView,\n // this.registry, true\n // );\n throw new Error(\"getInstance is not implemented\");\n }\n}","import { buildOptions } from './ParserOptionsBuilder.js';\nimport { BaseOutputBuilder, BaseOutputBuilderFactory, Context } from '@nodable/base-output-builder';\nimport { MatcherView } from 'path-expression-matcher';\n\n\nexport default class CompactBuilderFactory extends BaseOutputBuilderFactory {\n constructor(builderOptions = {}) {\n super();\n this.builderOptions = buildOptions(builderOptions);\n }\n\n getInstance(parserOptions, readonlyMatcher) {\n return new CompactBuilder(\n parserOptions,\n this.builderOptions,\n readonlyMatcher,\n this.registry\n );\n }\n}\n\nexport class CompactBuilder extends BaseOutputBuilder {\n\n /**\n * @param {object} parserOptions\n * @param {object} builderOptions\n * @param {MatcherView} readonlyMatcher\n * @param {object} registry\n */\n constructor(parserOptions, builderOptions, readonlyMatcher, registry) {\n super(parserOptions, builderOptions, readonlyMatcher, registry);\n this.parserOptions = parserOptions;\n this.builderOptions = builderOptions;\n this.tagsStack = [];\n this.root = {};\n this.parent = this.root;\n this.tagName = this._rootName;\n this.value = {};\n this.textValue = \"\";\n this.attributes = {};\n this.hasAttributes = false;\n }\n\n /**\n * Builds the initial value object from current attributes.\n * Returns \"\" when there are no attributes.\n */\n _buildAttributeValue() {\n if (isEmpty(this.attributes)) {\n this.hasAttributes = false;\n return \"\";\n }\n this.hasAttributes = true;\n if (this.parserOptions.attributes.groupBy) {\n return { [this.parserOptions.attributes.groupBy]: this.attributes };\n }\n return this.attributes;\n }\n\n addElement(tag) {\n const value = this._buildAttributeValue();\n this.tagsStack.push([this.tagName, this.textValue, this.value, this.hasAttributes]);\n this.tagName = tag.name;\n this.value = value;\n this.textValue = \"\";\n this.attributes = {};\n }\n\n /**\n * Combines votes from `alwaysArray` and `forceArray` with equal priority.\n *\n * Voting rules:\n * - `alwaysArray`: true if any entry matches, undefined (abstain) if none match.\n * It never votes false — no match is not a veto.\n * - `forceArray`: returns true / false / undefined directly.\n *\n * Resolution:\n * - Any voter returns explicit false → false (veto wins)\n * - Any voter returns true, none false → true\n * - All abstain (undefined) → false (default)\n *\n * @param {boolean|null} isLeafNode\n * @returns {boolean}\n */\n _resolveForceArray(isLeafNode) {\n // --- alwaysArray vote ---\n let alwaysVote; // undefined = abstain\n const matched = this.builderOptions._alwaysArraySet.matchesAny(this.matcher);\n if (matched) alwaysVote = true;\n // no match → alwaysVote stays undefined (abstain, not false)\n\n // --- forceArray vote ---\n let forceVote; // undefined = abstain\n if (typeof this.builderOptions.forceArray === 'function') {\n const result = this.builderOptions.forceArray(this.matcher, isLeafNode);\n if (result === true) forceVote = true;\n else if (result === false) forceVote = false;\n // anything else (undefined, null, …) → abstain\n }\n\n // --- resolution ---\n if (alwaysVote === false || forceVote === false) return false; // veto\n else if (alwaysVote === true || forceVote === true) return true; // at least one yes\n return false; // all abstain → default\n }\n\n closeElement() {\n const tagName = this.tagName;\n let value = this.value; // contains attributes if not skipped\n const textValue = this.textValue;\n const hasAttributes = this.hasAttributes;\n\n // A tag is a leaf node if it has no child elements.\n // It can have attributes and still be a leaf node.\n // hasAttributes is tracked explicitly by _buildAttributeValue() so we never\n // need to reverse-engineer this from key names, prefixes, or groupBy keys.\n const isLeafNode = typeof value !== \"object\" // plain string value (no attrs, no children)\n || Array.isArray(value) // unexpected array → treat as leaf\n || isEmpty(value) // no attributes, no children\n || hasAttributes; // only attributes present, no child elements yet\n\n const context = new Context(\n tagName,\n this.matcher,\n isLeafNode,\n false,\n );\n\n if (isLeafNode) {\n const parsedText = this._pendingStopNode ? textValue : this.tagsPipeline.run(textValue, context);\n\n if (hasAttributes) {\n // Attributes are present — value is already an object.\n // Only write the text node when there is actual text content; an empty\n // parsedText alongside attributes would produce a spurious #text:\"\" key.\n // forceTextNode overrides this and writes the node even when empty.\n if (parsedText !== \"\" && parsedText !== null && parsedText !== undefined) {\n value[this.parserOptions.nameFor.text] = parsedText;\n } else if (this.builderOptions.forceTextNode) {\n value[this.parserOptions.nameFor.text] = parsedText;\n }\n } else if (this.builderOptions.forceTextNode) {\n // No attributes — wrap in an object so the shape is always consistent\n value = { [this.parserOptions.nameFor.text]: parsedText };\n } else {\n // No attributes, no forceTextNode — use the plain parsed value\n value = parsedText;\n }\n } else if (textValue.length > 0 || this.builderOptions.forceTextNode) {\n // Non-leaf node with actual text content sitting between child elements\n // mixed content: element has both child tags and text\n const parsedText = this._pendingStopNode ? textValue : this.tagsPipeline.run(textValue, context);\n value[this.parserOptions.nameFor.text] = parsedText;\n }\n\n let resultTag = { tagName, value };\n\n // if (this.parserOptions.onTagClose !== undefined) {\n // resultTag = this.parserOptions.onTagClose(tagName, value, textValue, this.matcher);\n // if (!resultTag) return;\n // }\n\n const arr = this.tagsStack.pop();\n let parentTag = arr[2];\n\n // Check if this tag should be forced into an array\n const shouldForceArray = this._resolveForceArray(isLeafNode);\n\n parentTag = this._addChildTo(resultTag.tagName, resultTag.value, parentTag, shouldForceArray);\n\n this.tagName = arr[0];\n this.textValue = arr[1];\n this.value = parentTag;\n this.hasAttributes = arr[3]; // restore parent tag's flag\n this._pendingStopNode = false;\n }\n\n _addChild(key, val) {\n if (typeof this.value === \"string\") {\n this.value = { [this.parserOptions.nameFor.text]: this.value };\n }\n this._addChildTo(key, val, this.value, false);\n this.attributes = {};\n }\n\n _addChildTo(key, val, node, forceArray) {\n if (typeof node === 'string') node = {};\n\n if (!Object.prototype.hasOwnProperty.call(node, key)) {\n node[key] = forceArray ? [val] : val;\n } else {\n if (!Array.isArray(node[key])) node[key] = [node[key]];\n node[key].push(val);\n }\n return node;\n }\n\n addValue(text) {\n if (this.textValue.length > 0) this.textValue += `${this.builderOptions.textJoint}${text}`;\n else this.textValue = text;\n }\n\n addInstruction(name) {\n const value = this._buildAttributeValue();\n this._addChild(name, value);\n this.attributes = {};\n }\n\n onExit(exitInfo) { }\n\n getOutput() {\n return this.value;\n }\n}\n\nfunction isEmpty(obj) {\n return Object.keys(obj).length === 0;\n}\n\n","/**\n * ParseError — structured error class for flexible-xml-parser.\n *\n * All errors thrown by the parser are instances of ParseError so callers can\n * distinguish library errors from generic runtime errors and reliably inspect\n * position information.\n *\n * @property {string} code - Machine-readable error code (e.g. 'UNEXPECTED_CLOSE_TAG')\n * @property {number|undefined} index - 0-based character offset from document start (when available)\n */\nexport class ParseError extends Error {\n /**\n * @param {string} message - Human-readable error message\n * @param {string} code - Machine-readable error code\n * @param {object} [position] - Optional position info\n * @param {number} [position.index]\n */\n constructor(message, code, position = {}) {\n super(message);\n this.name = 'ParseError';\n this.code = code;\n\n this.index = position.index ?? undefined;\n }\n\n toString() {\n const pos = this._posStr();\n return pos ? `${this.name} [${this.code}] at ${pos}: ${this.message}` : `${this.name} [${this.code}]: ${this.message}`;\n }\n\n _posStr() {\n if (this.index !== undefined) {\n return `index ${this.index}`;\n }\n return null;\n }\n}\n\n// ─── Error codes ─────────────────────────────────────────────────────────────\n\nexport const ErrorCode = Object.freeze({\n // Input type errors\n INVALID_INPUT: 'INVALID_INPUT',\n INVALID_STREAM: 'INVALID_STREAM',\n\n // Streaming / feed API\n ALREADY_STREAMING: 'ALREADY_STREAMING',\n NOT_STREAMING: 'NOT_STREAMING',\n DATA_MUST_BE_STRING: 'DATA_MUST_BE_STRING',\n\n // Tag structure\n UNEXPECTED_END: 'UNEXPECTED_END',\n UNEXPECTED_CLOSE_TAG: 'UNEXPECTED_CLOSE_TAG',\n MISMATCHED_CLOSE_TAG: 'MISMATCHED_CLOSE_TAG',\n UNEXPECTED_TRAILING_DATA: 'UNEXPECTED_TRAILING_DATA',\n INVALID_TAG: 'INVALID_TAG',\n UNCLOSED_QUOTE: 'UNCLOSED_QUOTE',\n INVALID_ATTRIBUTE_NAME: 'INVALID_ATTRIBUTE_NAME',\n\n // Namespace\n MULTIPLE_NAMESPACES: 'MULTIPLE_NAMESPACES',\n\n // Conformance (illegal chars, attribute value rules)\n ILLEGAL_CHARACTER: 'ILLEGAL_CHARACTER',\n DUPLICATE_ATTRIBUTE: 'DUPLICATE_ATTRIBUTE',\n UNQUOTED_ATTRIBUTE_VALUE: 'UNQUOTED_ATTRIBUTE_VALUE',\n BOOLEAN_ATTRIBUTE_REJECTED: 'BOOLEAN_ATTRIBUTE_REJECTED',\n\n // Security\n SECURITY_PROTOTYPE_POLLUTION: 'SECURITY_PROTOTYPE_POLLUTION',\n SECURITY_RESERVED_OPTION: 'SECURITY_RESERVED_OPTION',\n SECURITY_RESTRICTED_NAME: 'SECURITY_RESTRICTED_NAME',\n\n // Limits (DoS prevention)\n LIMIT_MAX_NESTED_TAGS: 'LIMIT_MAX_NESTED_TAGS',\n LIMIT_MAX_ATTRIBUTES: 'LIMIT_MAX_ATTRIBUTES',\n\n // Entity limits\n ENTITY_MAX_COUNT: 'ENTITY_MAX_COUNT',\n ENTITY_MAX_SIZE: 'ENTITY_MAX_SIZE',\n ENTITY_MAX_EXPANSIONS: 'ENTITY_MAX_EXPANSIONS',\n ENTITY_MAX_EXPANDED_LENGTH: 'ENTITY_MAX_EXPANDED_LENGTH',\n\n // Entity registration\n ENTITY_INVALID_KEY: 'ENTITY_INVALID_KEY',\n ENTITY_INVALID_VALUE: 'ENTITY_INVALID_VALUE',\n\n // Encoding\n UNSUPPORTED_ENCODING: 'UNSUPPORTED_ENCODING',\n INVALID_DECODER: 'INVALID_DECODER',\n ENCODING_MISMATCH: 'ENCODING_MISMATCH',\n});\n\nexport default ParseError;\n","import { ParseError, ErrorCode } from './ParseError.js';\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\n\n\nexport function isSpace(char) {\n return char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\" || char === \"\\f\";\n}\n\n\nexport function isSpaceCode(code) {\n return code === 32 || code === 9 || code === 10 || code === 13 || code === 12; // space \\t \\n \\r \\f\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\nexport const DANGEROUS_PROPERTY_NAMES = [\n 'hasOwnProperty',\n 'toString',\n 'valueOf',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__',\n \"toLocaleString\",\n \"isPrototypeOf\",\n \"propertyIsEnumerable\"\n];\n\nexport const criticalProperties = [\"__proto__\", \"constructor\", \"prototype\"];\n\n// Capacity (in numbers, i.e. QUOTE_PAIRS_CAPACITY/2 quoted-attribute-values)\n// of the reusable typed array each InputSource uses to record quote\n// positions found by scanTagExpEnd(), reused by\n// AttributeProcessor.parseAttributes(). A plain fixed cap, not a growable\n// array — see scanTagExpEnd()'s doc for why: comfortably covers real-world\n// tags, and a tag with more quoted attributes than this just falls back to\n// per-character quote scanning for the overflow, so there's no correctness\n// cost to keeping this small and allocation-free.\nexport const QUOTE_PAIRS_CAPACITY = 128; // 64 quoted attribute values per tag\n\n/**\n * True document-start-relative offset for a source's current read position.\n *\n * `source.startIndex` is only an offset into the source's *live buffer* —\n * every flush() trims already-consumed characters off the front of that\n * buffer and rebases startIndex back down, so startIndex alone drifts from\n * the true document offset the moment a flush has happened. Each source\n * tracks how much it has trimmed away so far in `_baseOffset` (bumped by the\n * trimmed amount inside flush()); the real position is always the sum of\n * the two. This is the one place that sum is computed — every caller that\n * needs an absolute, document-start-relative position (errors, tag index/\n * openEnd/closeEnd, attribute offsets, stop-node end) must go through this\n * function rather than reading `startIndex` directly.\n */\nexport function absolutePosition(source) {\n return source.startIndex + (source._baseOffset || 0);\n}\n\n/**\n * Uniform error-position accessor across all InputSource types.\n *\n * Position reporting is index-only (absolute offset from document start) —\n * no line/column.\n */\nexport function errorPositionOf(source) {\n return { index: absolutePosition(source) };\n}\n\n/**\n * True for character codes that are illegal as literal characters anywhere in\n * an XML document (element text, attribute values, CDATA, comments): 0x00-0x08,\n * 0x0B, 0x0C, 0x0E-0x1F. Tab/LF/CR (0x09/0x0A/0x0D) are legal whitespace and\n * excluded. A numeric character reference like `�` is just ASCII text at\n * this stage (entity expansion happens later, in the output builder), so it\n * never reaches this check as a raw byte.\n */\nfunction isIllegalControlCode(c) {\n return c <= 8 || c === 11 || c === 12 || (c >= 14 && c <= 31);\n}\n\n/**\n * Normalize a complete piece of document content (element text, CDATA\n * content, or comment content) per XML §2.11: a real `\\r\\n` pair or a lone\n * `\\r` becomes a single `\\n`; a bare `\\n` is left alone. Also rejects illegal\n * literal control characters (see isIllegalControlCode) — this check always\n * runs, regardless of autoClose/lenient settings.\n *\n * Meant to be called exactly once per finished token (a whole text run, a\n * whole CDATA block, a whole comment) — never mid-scan — so no chunk-boundary\n * carry logic is needed: by the time a reader has a complete string, any\n * `\\r\\n` that happened to straddle a feed() chunk boundary has already been\n * concatenated back together by the caller.\n *\n * @param {string} str\n * @param {object} [source] - for error position only; omit if unavailable.\n * @returns {string}\n */\nexport function sanitizeContent(str, source) {\n const len = str.length;\n let hasCR = false;\n for (let i = 0; i < len; i++) {\n const c = str.charCodeAt(i);\n if (isIllegalControlCode(c)) {\n throw new ParseError(\n `Illegal control character 0x${c.toString(16).padStart(2, '0')} in document content`,\n ErrorCode.ILLEGAL_CHARACTER,\n source ? errorPositionOf(source) : {}\n );\n }\n if (c === 13) hasCR = true;\n }\n if (!hasCR) return str; // fast path — nothing to fold, no reallocation\n\n let out = '';\n let segStart = 0;\n for (let i = 0; i < len; i++) {\n if (str.charCodeAt(i) === 13) {\n out += str.substring(segStart, i) + '\\n';\n if (str.charCodeAt(i + 1) === 10) i++; // \\r\\n pair → one \\n, not two\n segStart = i + 1;\n }\n }\n out += str.substring(segStart);\n return out;\n}\n\n/**\n * Assert that the upcoming characters in the source match the expected string.\n * If not enough data → throws UNEXPECTED_END.\n * If mismatch → throws INVALID_TAG with the given errorMsg.\n * On success, consumes the matched characters (advances startIndex).\n *\n * @param {object} source - input source (must have canRead, matchAhead, updateBufferBoundary)\n * @param {string} expected - string to match\n * @param {string} errorMsg - description of what is being read (used in error messages)\n * @param {boolean} [caseInsensitive=false]\n */\nexport function expectMatch(source, expected, errorMsg, caseInsensitive = false) {\n const len = expected.length;\n if (!source.canRead(len)) {\n throw new ParseError(\n `Unexpected end of source reading ${errorMsg}`,\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(source)\n );\n }\n const matched = source.matchAhead(expected, caseInsensitive);\n if (matched !== true) {\n throw new ParseError(\n `Invalid ${errorMsg}`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n source.updateBufferBoundary(len);\n}\n\n/**\n * Assert that the source has at least `n` characters available from the current position.\n * Throws UNEXPECTED_END if not enough data.\n * Does NOT consume any characters.\n *\n * @param {object} source - input source (must have canRead)\n * @param {number} n - number of characters needed\n * @param {string} errorMsg - description of what is being read (used in error message)\n */\nexport function ensureCanRead(source, n, errorMsg) {\n if (!source.canRead(n)) {\n throw new ParseError(\n `Unexpected end of source reading ${errorMsg}`,\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(source)\n );\n }\n}","import { CompactBuilderFactory } from '@nodable/compact-builder';\nimport { Expression, ExpressionSet } from 'path-expression-matcher';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { DANGEROUS_PROPERTY_NAMES, criticalProperties } from './util.js';\n\nconst defaultOnDangerousProperty = (name) => {\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return \"__\" + name;\n }\n return name;\n};\n\nexport const defaultOptions = {\n // --- skip group ---\n // Controls which node types are excluded from output\n skip: {\n declaration: false, // Skip <?xml ... ?> declaration\n pi: false, // Skip processing instructions (other than declaration)\n attributes: true, // Skip all attributes\n cdata: false, // Exclude CDATA sections from output entirely\n comment: false, // Exclude comments from output entirely\n nsPrefix: false, // Strip namespace prefixes (e.g. ns:tag → tag)\n tags: [], // Tag paths to skip entirely — content is silently dropped from output\n whitespaceText: true // addValue() of a builder would not be called if text is only whitespaces\n },\n\n // --- nameFor group ---\n // Property names used when including special nodes in output.\n nameFor: {\n text: '#text', // Property for mixed text content\n cdata: '', // '' = merge CDATA into text value\n comment: '', // '' = omit comments from output\n },\n\n // --- attributes group ---\n attributes: {\n // 'allow' (default) keeps a valueless attribute (e.g. <e flag/>) as `true`.\n // 'ignore' drops it silently; 'throw' rejects the document as soon as one\n // is found. Replaces a previous same-named boolean option that had no\n // effect on parsing (it was never read anywhere in the parsing code).\n booleanType: 'allow',\n // 'overwrite' (default) — last occurrence of a repeated attribute name wins.\n // 'ignore' — first occurrence wins, later ones are dropped entirely.\n // 'throw' — reject the document as soon as a repeated name is found on one tag.\n duplicate: 'overwrite',\n groupBy: '', // Group all attributes under this key; '' = inline with tag\n prefix: '@_', // Prepended to attribute names in output\n suffix: '', // Appended to attribute names in output\n },\n\n // --- tags group ---\n tags: {\n unpaired: [], // Tags that never have a closing tag (e.g. br, img, hr)\n stopNodes: [], // Tag paths whose content is captured raw without parsing\n },\n\n // --- security ---\n strictReservedNames: false,\n onDangerousProperty: defaultOnDangerousProperty,\n // sanitizeNames: false skips the dangerous-property rename step only\n // (hasOwnProperty, toString, valueOf, ... pass through unprefixed,\n // onDangerousProperty is never called for them). It does NOT disable the\n // critical-property check (__proto__, constructor, prototype) — that\n // guards against actual prototype pollution and always runs. Only for\n // trusted input — internal XML-to-XML pipelines, generated documents —\n // where the source is known not to contain hostile names. Does NOT affect\n // strictReservedNames, which is a separate, independent check.\n sanitizeNames: true,\n\n // --- filtering (path-expression-matcher) ---\n only: [], // for future\n\n // --- DOCTYPE parsing ---\n // Controls whether DOCTYPE entities are collected and read-time security limits.\n //\n // enabled — false (default) → DOCTYPE is read (to consume it) but entities\n // are discarded and never forwarded to output builders\n // true → collect DOCTYPE entities and forward them to the output builder\n // Note: the output builder must have an EntitiesValueParser registered\n // under 'entity' and 'entity' must be in its\n // valueParsers chain for replacement to actually happen.\n //\n // Read-time security limits (enforced by DocTypeReader at declaration time):\n // maxEntityCount — max entities declared in a DOCTYPE (default: 100)\n // maxEntitySize — max bytes per entity definition value (default: 10000)\n //\n // Replacement-time limits (maxTotalExpansions, maxExpandedLength) are configured\n // on EntitiesValueParser directly — they are not part of doctypeOptions.\n doctypeOptions: {\n enabled: false,\n maxEntityCount: 100,\n maxEntitySize: 10000,\n },\n\n // --- autoClose ---\n // Controls parser behaviour when tags are unclosed or mismatched.\n //\n // onEof — what to do when EOF is reached with open tags still on the stack\n // 'throw' (default) → throw an error\n // 'closeAll' → silently close all remaining open tags\n //\n // onMismatch — what to do when a closing tag doesn't match the current open tag\n // 'throw' (default) → throw an error\n // 'recover' → pop the stack toward the nearest matching opener;\n // if no match is found the tag is discarded\n // 'discard' → silently ignore the bad closing tag\n //\n // collectErrors — when true, errors are recorded in result.__parseErrors instead\n // of being silently dropped. Each entry has the shape:\n // { type, tag, expected, index }\n //\n // Shorthand: autoClose: 'html' sets onEof:'closeAll', onMismatch:'discard',\n // collectErrors:true, and adds the standard HTML void elements to tags.unpaired.\n autoClose: null, // null = feature disabled; throws on any malformed input\n\n // --- limits (DoS prevention) ---\n // Group structural limits that guard against resource exhaustion.\n //\n // maxNestedTags — maximum tag nesting depth; throws when exceeded.\n // Prevents stack-overflow attacks via deeply nested XML.\n // Default: null (no limit)\n //\n // maxAttributesPerTag — maximum number of attributes on a single tag.\n // Throws when a tag exceeds this count.\n // Default: null (no limit)\n //\n limits: {\n maxNestedTags: null,\n maxAttributesPerTag: null,\n },\n\n // --- feedable (feed/end and parseStream input options) ---\n // Controls buffer behaviour for the FeedableSource and StreamSource.\n //\n // maxBufferSize — maximum number of characters allowed in the buffer at\n // any one time. Prevents memory exhaustion when a caller\n // feeds data faster than it is consumed.\n // Default: 10 MB (10 * 1024 * 1024 characters)\n //\n // autoFlush — when true (default), already-processed characters are\n // automatically discarded from the front of the buffer\n // whenever the processed portion exceeds flushThreshold.\n // Keeps memory usage flat for large documents.\n //\n // flushThreshold — number of processed characters that triggers an auto-\n // flush. Lower values free memory sooner but incur more\n // string-slice operations. Default: 1024 characters (1 KB)\n //\n feedable: {\n maxBufferSize: 10 * 1024 * 1024,\n autoFlush: true,\n flushThreshold: 1024,\n bufferSize: 256\n },\n\n // --- exitIf ---\n // Stops parsing as soon as the predicate returns true for the current tag.\n //\n // The callback receives a read-only matcher positioned at the just-opened tag:\n // exitIf(matcher) → boolean\n //\n // When exitIf returns true the parser immediately:\n // 1. Closes all currently open tags (innermost first) by calling addTextNode()\n // and popTag() for each, so the output builder can finalise its tree.\n // 2. Calls outputBuilder.onExit({ tagDetail, matcher, tagsStack }) so the\n // builder can record that the parse was intentionally truncated.\n // 3. Breaks the parse loop — no further source characters are read.\n //\n // The parse call returns the partial-but-consistent output as normal.\n // No error is thrown.\n //\n // Default: null (feature disabled)\n exitIf: () => false,\n\n //onStopNode(tagDetail, rawContent, matcher)\n // --- output ---\n OutputBuilder: null, //TODO: accept lower case\n\n // --- decoding ---\n // Controls how raw bytes (Buffer/Uint8Array input to parse()/parseBytesArr(),\n // or chunks fed to feed()/parseStream()) are turned into text.\n //\n // encoding — 'auto' (default) sniffs BOM + a leading <?xml ... encoding=\"...\">\n // declaration per XML 1.0 Appendix F, falling back to utf8 if\n // neither is present. Set explicitly (e.g. 'utf8', 'utf16le',\n // 'latin1', 'ascii', or a custom-registered name) to skip\n // detection entirely.\n // NOTE: streaming inputs (feed()/parseStream()) do not yet\n // support 'auto' detection — see docs/16-encoding.md — and\n // fall back to utf8 the same way they always have.\n //\n // customDecoders — { name: descriptor } map merged into the encoding\n // registry before resolution, for encodings FXP doesn't\n // ship natively (e.g. Shift_JIS via iconv-lite). See\n // docs/16-encoding.md for the descriptor shape.\n //\n decoding: {\n encoding: 'auto',\n customDecoders: null,\n },\n};\n\n// All names that should never appear as property keys\nconst ALL_RESERVED = new Set([...criticalProperties, ...DANGEROUS_PROPERTY_NAMES]);\nexport { ALL_RESERVED as RESERVED_JS_NAMES };\n\nfunction validatePropertyName(value, optionName) {\n if (typeof value !== 'string' || value === '') return;\n if (ALL_RESERVED.has(value)) {\n throw new ParseError(\n `SECURITY: '${value}' is a reserved JavaScript keyword and cannot be used as ${optionName}`,\n ErrorCode.SECURITY_RESERVED_OPTION\n );\n }\n}\n\nexport const buildOptions = function (options) {\n // Validate security-sensitive option values BEFORE merging\n if (options) {\n if (options.nameFor?.text) validatePropertyName(options.nameFor.text, 'nameFor.text');\n if (options.nameFor?.cdata) validatePropertyName(options.nameFor.cdata, 'nameFor.cdata');\n if (options.nameFor?.comment) validatePropertyName(options.nameFor.comment, 'nameFor.comment');\n if (options.attributes?.prefix) validatePropertyName(options.attributes.prefix, 'attributes.prefix');\n if (options.attributes?.groupBy) validatePropertyName(options.attributes.groupBy, 'attributes.groupBy');\n\n // Validate limits option\n if (options.limits !== undefined && options.limits !== null) {\n if (typeof options.limits !== 'object') {\n throw new ParseError(`'limits' must be an object, got ${typeof options.limits}`, ErrorCode.INVALID_INPUT);\n }\n const { maxNestedTags, maxAttributesPerTag } = options.limits;\n if (maxNestedTags !== undefined && maxNestedTags !== null &&\n (typeof maxNestedTags !== 'number' || !Number.isInteger(maxNestedTags) || maxNestedTags < 1)) {\n throw new ParseError(`'limits.maxNestedTags' must be a positive integer, got ${maxNestedTags}`, ErrorCode.INVALID_INPUT);\n }\n if (maxAttributesPerTag !== undefined && maxAttributesPerTag !== null &&\n (typeof maxAttributesPerTag !== 'number' || !Number.isInteger(maxAttributesPerTag) || maxAttributesPerTag < 0)) {\n throw new ParseError(`'limits.maxAttributesPerTag' must be a non-negative integer, got ${maxAttributesPerTag}`, ErrorCode.INVALID_INPUT);\n }\n }\n }\n\n const finalOptions = deepClone(defaultOptions);\n\n if (options) {\n copyProperties(finalOptions, options);\n }\n\n if (!finalOptions.OutputBuilder) {\n finalOptions.OutputBuilder = new CompactBuilderFactory();\n }\n\n // Normalize stopNodes and skip.tags entries into Expression objects with config embedded\n // in Expression.data as { nested, skipEnclosures }. Build a sealed ExpressionSet for\n // O(1) hot-path matching in the parser.\n //\n // Accepted entry forms (identical for both stopNodes and skip.tags):\n // \"..script\"\n // → Expression(\"..script\", {}, { nested: false, skipEnclosures: [] })\n //\n // Expression instance\n // → re-wrapped with { nested: false, skipEnclosures: [] } in data\n //\n // { expression: \"..script\", nested?: boolean, skipEnclosures?: [] }\n // { expression: Expression, nested?: boolean, skipEnclosures?: [] }\n // → Expression with the given config embedded in .data\n //\n // `nested` defaults to false; `skipEnclosures` defaults to [].\n // The two flags are fully independent — any combination is valid.\n if (Array.isArray(finalOptions.tags?.stopNodes)) {\n const stopSet = new ExpressionSet();\n finalOptions.tags.stopNodes = finalOptions.tags.stopNodes.map(\n (entry) => normalizeTagEntry(entry, 'stopNodes', stopSet)\n );\n stopSet.seal();\n finalOptions.tags.stopNodesSet = stopSet;\n }\n\n if (Array.isArray(finalOptions.skip?.tags)) {\n const skipSet = new ExpressionSet();\n finalOptions.skip.tags = finalOptions.skip.tags.map(\n (entry) => normalizeTagEntry(entry, 'skip.tags', skipSet)\n );\n skipSet.seal();\n finalOptions.skip.tagsSet = skipSet;\n }\n\n if (finalOptions.onDangerousProperty === null) {\n finalOptions.onDangerousProperty = defaultOnDangerousProperty;\n }\n\n // Validate exitIf\n if (finalOptions.exitIf !== null && finalOptions.exitIf !== undefined) {\n if (typeof finalOptions.exitIf !== 'function') {\n throw new ParseError(\n `'exitIf' must be a function, got ${typeof finalOptions.exitIf}`,\n ErrorCode.INVALID_INPUT,\n );\n }\n }\n\n // Resolve autoClose: expand the 'html' preset and normalise to an object\n finalOptions.autoClose = resolveAutoClose(finalOptions.autoClose, finalOptions);\n\n return finalOptions;\n};\n\n/** Standard HTML void elements — never have a closing tag. */\nconst HTML_VOID_ELEMENTS = [\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n];\n\n/**\n * Normalise the raw `autoClose` option value into either null (disabled)\n * or a fully-resolved options object.\n *\n * @param {null|string|object} raw - Value supplied by the user\n * @param {object} opts - The already-merged final options (mutated for html preset)\n * @returns {null|object}\n */\nfunction resolveAutoClose(raw, opts) {\n if (!raw) return null;\n\n if (raw === 'html') {\n // Apply HTML-specific tag defaults\n const existingUnpaired = opts.tags.unpaired || [];\n const merged = [...new Set([...existingUnpaired, ...HTML_VOID_ELEMENTS])];\n opts.tags = { ...opts.tags, unpaired: merged };\n\n return {\n onEof: 'closeAll',\n onMismatch: 'discard',\n collectErrors: true,\n };\n }\n\n if (typeof raw === 'string') {\n // e.g. autoClose: 'closeAll' — treat as shorthand for onEof\n return {\n onEof: raw,\n onMismatch: 'throw',\n collectErrors: false,\n };\n }\n\n if (typeof raw === 'object') {\n return {\n onEof: raw.onEof || 'throw',\n onMismatch: raw.onMismatch || 'throw',\n collectErrors: raw.collectErrors || false,\n };\n }\n\n return null;\n}\n\n/**\n * Normalize one entry from `tags.stopNodes` or `skip.tags` into an Expression\n * whose `.data` carries `{ nested, skipEnclosures }`, and register it in `set`.\n *\n * Accepted forms:\n * string → bare pattern, defaults applied\n * Expression instance → re-wrapped with defaults\n * { expression: string|Expression, nested?, skipEnclosures? }\n *\n * @param {string|Expression|object} entry\n * @param {string} optionName - Used in error messages (\"stopNodes\" or \"skip.tags\")\n * @param {ExpressionSet} set - The set to register the resulting Expression into\n * @returns {Expression}\n */\nfunction normalizeTagEntry(entry, optionName, set) {\n let pattern, nested, skipEnclosures;\n\n if (typeof entry === 'string') {\n if (entry.length === 0) throw new ParseError(`${optionName} expression cannot be empty`, ErrorCode.INVALID_INPUT);\n pattern = entry;\n nested = false;\n skipEnclosures = [];\n } else if (entry instanceof Expression) {\n // Bare Expression — keep its pattern, apply defaults for missing data fields\n pattern = entry.toString();\n nested = entry.data?.nested ?? false;\n skipEnclosures = entry.data?.skipEnclosures ?? [];\n } else if (entry && typeof entry === 'object' && entry.expression !== undefined) {\n const raw = entry.expression;\n if (typeof raw === 'string') {\n if (raw.length === 0) throw new ParseError(`${optionName} expression cannot be empty`, ErrorCode.INVALID_INPUT);\n pattern = raw;\n } else if (raw instanceof Expression) {\n pattern = raw.toString();\n } else {\n throw new ParseError(`${optionName} expression must be a string or Expression instance`, ErrorCode.INVALID_INPUT);\n }\n nested = entry.nested === true;\n skipEnclosures = Array.isArray(entry.skipEnclosures) ? entry.skipEnclosures : [];\n } else {\n throw new ParseError(\n `Invalid ${optionName} entry: expected a string, Expression, or { expression, nested?, skipEnclosures? } object.`,\n ErrorCode.INVALID_INPUT,\n );\n }\n\n const expr = new Expression(pattern, {}, { nested, skipEnclosures });\n set.add(expr);\n return expr;\n}\n\nfunction deepClone(obj) {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(deepClone);\n if (obj instanceof RegExp) return obj; // ← guard\n if (obj instanceof Expression) return obj; // ← guard — Expression instances are immutable\n const clone = {};\n for (const key of Object.keys(obj)) {\n clone[key] = typeof obj[key] === 'function' ? obj[key] : deepClone(obj[key]);\n }\n return clone;\n}\n\nfunction copyProperties(target, source) {\n for (const key of Object.keys(source)) {\n // Guard against prototype pollution via option keys\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n if (key === 'OutputBuilder') {\n target[key] = source[key];\n } else if (typeof source[key] === 'function') {\n target[key] = source[key];\n } else if (source[key] instanceof RegExp) { // ← guard, before the generic object check\n target[key] = source[key];\n } else if (Array.isArray(source[key])) {\n target[key] = source[key];\n } else if (typeof source[key] === 'object' && source[key] !== null) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n copyProperties(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n}","/**\n * Shared scanTagExpEnd implementations for sources whose buffer is a JS string\n * (StringSource, FeedableSource, CharScanStrategy).\n *\n * Two separate functions instead of one with a flag — the flag was evaluated\n * inside the loop on every character, which adds overhead to the hottest path\n * in the parser. The caller resolves which variant to use once, before the\n * scan begins.\n *\n * Neither function is exported as a method — each source assigns both onto\n * itself so `this` binding works correctly with zero indirection.\n */\n\n/**\n * Scan for the unquoted '>' that ends a tag expression, and record each\n * quote boundary in `this._quotePairs` as it goes. Used when attributes\n * will be parsed (skip.attributes is false).\n *\n * @returns {number} relative offset of the unquoted '>' from startIndex,\n * or -1 if the buffer is exhausted (chunk boundary for FeedableSource,\n * malformed input for StringSource/CharScanStrategy).\n */\nexport function scanTagExpEnd() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n const pairs = this._quotePairs;\n const capacity = pairs.length;\n let pairsLen = 0;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === \"'\") {\n if (!inDouble) {\n inSingle = !inSingle;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === '\"') {\n if (!inSingle) {\n inDouble = !inDouble;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === '>' && !inSingle && !inDouble) {\n this._quotePairsLen = pairsLen;\n return i - start;\n }\n }\n this._quotePairsLen = pairsLen;\n return -1;\n}\n\n/**\n * Scan for the unquoted '>' that ends a tag expression, without recording\n * quote positions. Used when attributes are being skipped entirely\n * (skip.attributes is true) — nobody will read _quotePairs, so don't pay\n * to populate it.\n *\n * @returns {number} relative offset of the unquoted '>' from startIndex,\n * or -1 if the buffer is exhausted.\n */\nexport function scanTagExpEndFast() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === \"'\") {\n if (!inDouble) inSingle = !inSingle;\n } else if (c === '\"') {\n if (!inSingle) inDouble = !inDouble;\n } else if (c === '>' && !inSingle && !inDouble) {\n return i - start;\n }\n }\n return -1;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { isSpace, QUOTE_PAIRS_CAPACITY } from '../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from './scanTagExpEnd.js';\n\n/**\n * StringSource — input source backed by an in-memory string.\n *\n * ### Memory reclamation\n *\n * Unlike FeedableSource, the full document is available from the start, so\n * there is no chunk-boundary risk and rewindToMark() is a safe no-op.\n * However, the parsed prefix of the string is still held in memory until the\n * parse finishes. flush() reclaims that prefix by slicing the buffer and\n * resetting startIndex to 0.\n *\n * The same mark/flush protocol used by FeedableSource is implemented here so\n * all reader functions (readTagExp, readClosingTagName, readCdata, etc.) work\n * without any source-type conditionals:\n *\n * markTokenStart() — save the current read position at the start of a token\n * rewindToMark() — no-op for StringSource (full doc always present)\n * flush() — drop the already-parsed prefix to free memory\n *\n * Auto-flush fires inside updateBufferBoundary() whenever the processed\n * portion exceeds flushThreshold and no token checkpoint is active.\n *\n * Position reporting is index-only (absolute character offset from document\n * start) — no line/column tracking. That bookkeeping used to run on every\n * character and every bulk-read span for a field most callers never read;\n * dropping it is a straight speed win. A caller that wants line/column can\n * derive it from `index` plus the original document text.\n */\nexport default class StringSource {\n /**\n * @param {string} str — the full XML document string\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed chars\n */\n constructor(str, options = {}) {\n this.buffer = str;\n // Boundary pointer: data before this index has been consumed and may be freed.\n this.startIndex = 0;\n // Running total of characters trimmed off the front by flush() so far.\n // startIndex alone is only an offset into the live (post-trim) buffer;\n // the true document-start-relative position is startIndex + _baseOffset.\n // See util.js#absolutePosition — the single place that sum is computed.\n this._baseOffset = 0;\n\n this.autoFlush = options.autoFlush !== false;\n this.flushThreshold = options.flushThreshold ?? 1024;\n\n // Two-level mark stack matching FeedableSource's API.\n // _marks[0] = outer mark (parseXml loop), _marks[1] = inner mark (readers).\n // -1 means \"not set\" for that level.\n this._marks = [-1, -1];\n\n // Reused across every scanTagExpEnd() call, never reallocated. Holds\n // flat [openIdx, closeIdx, ...] pairs of quoted-attribute-value\n // positions found while scanning for the tag's closing '>'. Fixed-size\n // typed array + a manually-tracked length (`_quotePairsLen`), NOT\n // Array.push() — push() was measured to cost more than the per-character\n // quote re-scan it was meant to replace (bounds/growth checks on every\n // call add up across thousands of tags). Plain indexed writes with a\n // local counter avoid that entirely. See scanTagExpEnd() doc for the\n // capacity rationale and overflow behaviour.\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n }\n\n // ─── Token-start checkpoint ───────────────────────────────────────────────\n\n /**\n * Save the current read position into the two-level mark stack.\n *\n * Mirrors FeedableSource's two-level API so all reader functions work\n * identically regardless of source type:\n *\n * level 0 (default) — outer mark, set by parseXml()'s main loop.\n * level 1 — inner mark, set by individual reader functions.\n *\n * For StringSource the distinction only matters for flush() boundary\n * calculations — rewindToMark() is always a no-op here.\n *\n * @param {0|1} [level=0]\n */\n markTokenStart(level = 0) {\n this._marks[level] = this.startIndex;\n }\n\n /**\n * Restore startIndex to the last markTokenStart() position.\n *\n * StringSource always has the full document available, so a mid-token end\n * of input cannot occur and this method is a safe no-op. It exists solely\n * so caller code (XMLParser.feed / parseXml) can call rewindToMark()\n * unconditionally without branching on source type.\n */\n rewindToMark() {\n // No-op: the complete document is in memory; no rewind is ever needed.\n }\n\n /** Clear both mark slots (mirrors FeedableSource.clearMark). */\n clearMark() {\n this._marks[0] = -1;\n this._marks[1] = -1;\n }\n\n /**\n * Discard the already-processed prefix of the buffer to free memory.\n *\n * The flush origin is the minimum of all active mark positions so that any\n * in-progress token (at either mark level) is preserved in the buffer.\n * If no marks are active, the origin is startIndex itself.\n */\n flush() {\n let origin = this.startIndex;\n for (const m of this._marks) {\n if (m >= 0 && m < origin) origin = m;\n }\n if (origin > 0) {\n this.buffer = this.buffer.substring(origin);\n const marksLen = this._marks.length;\n for (let i = 0; i < marksLen; i++) {\n if (this._marks[i] >= 0) this._marks[i] -= origin;\n }\n this.startIndex -= origin;\n this._baseOffset += origin;\n }\n }\n\n // ─── Core read interface ──────────────────────────────────────────────────\n\n readCh() {\n return this.buffer[this.startIndex++];\n }\n\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n }\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n }\n\n /**\n * Check whether the upcoming characters equal `expected`, without\n * consuming or allocating anything — no substring is built even for a\n * full match. Contrast with the `readStr(n).toUpperCase() === \"X\"` style,\n * which allocates two throwaway strings on every call, matching or not.\n *\n * Caller decides what to do with a match — this never consumes. Follow a\n * `true` result with `updateBufferBoundary(expected.length)` (or more, if\n * something like trailing whitespace should also be consumed).\n *\n * @param {string} expected - literal to match against, already in the\n * case matchAhead should compare against when caseInsensitive is true\n * (e.g. pass \"system\", not \"SYSTEM\", together with caseInsensitive=true)\n * @param {boolean} [caseInsensitive=false]\n * @returns {boolean|null} true on full match, false on a definite\n * mismatch, null if the buffer runs out before enough characters are\n * available to decide either way (treat like any other\n * not-enough-data-yet case — same handling as scanTagExpEnd's -1)\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n }\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd = scanTagExpEnd;\n scanTagExpEndFast = scanTagExpEndFast;\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Single-character variant of readUpto — faster because there is no inner\n * match loop. Reads until `stopChar` is found, consumes it, and returns\n * the text before it.\n *\n * @param {string} stopChar Exactly one character.\n * @returns {string}\n */\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n }\n\n readUptoCloseTag(stopStr) { // stopStr: \"</tagname\"\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n // 0: scanning, 1: tag-name matched (scanning for '>'), 2: full match\n let state = 0;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; } // false match e.g. </scriptX>\n } else {\n // Try to match stopStr at position i\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) {\n state = 1;\n tagMatchStart = i;\n i += stopLength - 1; // skip past matched string\n }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n readFromBuffer(n, updateIndex) {\n const ch = n === 1\n ? this.buffer[this.startIndex]\n : this.buffer.substring(this.startIndex, this.startIndex + n);\n if (updateIndex) this.updateBufferBoundary(n);\n return ch;\n }\n\n /**\n * Advance the read cursor by n characters.\n *\n * Triggers an automatic flush of already-processed data when autoFlush is\n * enabled, the processed portion has grown past flushThreshold, and no\n * token checkpoint is currently active (a flush while a checkpoint is live\n * would invalidate the saved position).\n *\n * @param {number} [n=1]\n */\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n // See FeedableSource.updateBufferBoundary() for why there is no \"any mark\n // active\" gate here — flush()'s own min-origin computation already\n // protects any in-progress token; a separate gate was redundant and, since\n // marks are effectively always set in normal operation, made flush()\n // permanently unreachable. See specs/flushArchitecture_spec.js.\n if (this.autoFlush && this.startIndex >= this.flushThreshold) {\n this.flush();\n }\n }\n\n /**\n * Returns true when there is at least one character available at or after\n * the given offset (relative to startIndex). Mirrors FeedableSource's\n * formula so all three sources answer the same question the same way.\n * @param {number} [n=0]\n */\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n }\n}\n","import { ParseError, ErrorCode } from '../../ParseError.js';\nimport { isSpaceCode } from '../../util.js';\n\n/**\n * ByteScanStrategy — for self-synchronizing encodings only (utf8/ascii/latin1\n * by default; any custom encoding that explicitly asserts\n * `selfSynchronizing: true`). All positions/indices here (`n`, `index`, loop\n * counters passed in from XmlPartReader/DocTypeReader/StopNodeProcessor) are\n * BYTE offsets, matching BufferSource's original convention throughout —\n * unchanged by the encoding feature. Delimiters (<,>,\",') are matched at the\n * byte level for speed, safe here precisely because self-synchronizing means\n * an ASCII delimiter byte can never occur as a continuation byte of a\n * different character.\n *\n * `decodeCharAt(buf, i)` -> `{ char, width }` is the one piece that varies\n * per encoding (width in BYTES for the character starting at byte offset i).\n * ascii/latin1 are always width 1. utf8 needs the real multi-byte decode so\n * `startIndex` advances by the correct byte count per character — that part\n * is still required for correct scanning, independent of any position\n * reporting: the parser only ever exposes the absolute byte/char `index`,\n * not line/col, so no per-character column bookkeeping is needed here.\n *\n * Functions here are plain (non-arrow) so they can be assigned directly onto\n * a BufferSource instance and keep correct `this` binding with zero extra\n * indirection — resolved once at construction, no per-call branching.\n */\nexport function createByteScanStrategy(decodeCharAt, nodeEncoding = 'utf8', isContinuationByte = () => false) {\n return {\n readCh() {\n const { char, width } = decodeCharAt(this.buffer, this.startIndex);\n this.startIndex += width;\n return char;\n },\n\n /** index is a BYTE offset ahead of startIndex, same as original BufferSource. */\n readChAt(index) {\n return decodeCharAt(this.buffer, this.startIndex + index).char;\n },\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.slice(from, from + n).toString(nodeEncoding);\n },\n\n /**\n * See StringSource.js for the full doc — same contract. Compares raw\n * byte codes directly rather than going through decodeCharAt/readChAt:\n * safe here because every literal this is ever called with (DOCTYPE\n * keywords, tag names for closing-tag matching) is plain ASCII, and\n * ASCII is always exactly one byte per character in every encoding this\n * strategy handles (UTF-8, Latin-1, ASCII — see the module doc above\n * for why that guarantee is what lets this whole strategy exist).\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n const b = this.buffer[this.startIndex + i];\n if (b === undefined) return null;\n let code = b;\n if (caseInsensitive && code >= 65 && code <= 90) code += 32; // 'A'-'Z' -> 'a'-'z'\n if (code !== expected.charCodeAt(i)) return false;\n }\n return true;\n },\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Compares raw byte codes (39=', 34=\", 62=>)\n // rather than characters — safe here because self-synchronizing encodings\n // guarantee these byte values never appear as continuation bytes.\n scanTagExpEnd() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n const pairs = this._quotePairs;\n const capacity = pairs.length;\n let pairsLen = 0;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === 39) {\n if (!inDouble) {\n inSingle = !inSingle;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === 34) {\n if (!inSingle) {\n inDouble = !inDouble;\n if (pairsLen < capacity) pairs[pairsLen++] = i - start;\n }\n } else if (c === 62 && !inSingle && !inDouble) {\n this._quotePairsLen = pairsLen;\n return i - start;\n }\n }\n this._quotePairsLen = pairsLen;\n return -1;\n },\n\n scanTagExpEndFast() {\n const buf = this.buffer;\n const len = buf.length;\n const start = this.startIndex;\n let inSingle = false;\n let inDouble = false;\n for (let i = start; i < len; i++) {\n const c = buf[i];\n if (c === 39) {\n if (!inDouble) inSingle = !inSingle;\n } else if (c === 34) {\n if (!inSingle) inDouble = !inDouble;\n } else if (c === 62 && !inSingle && !inDouble) {\n return i - start;\n }\n }\n return -1;\n },\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n const stopBuffer = Buffer.from(stopStr);\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopBuffer[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.slice(this.startIndex, i).toString(nodeEncoding);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoChar(stopChar) {\n const stopCode = stopChar.charCodeAt(0);\n const buf = this.buffer;\n const len = buf.length;\n for (let i = this.startIndex; i < len; i++) {\n if (buf[i] === stopCode) {\n const result = buf.slice(this.startIndex, i).toString(nodeEncoding);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n const stopBuffer = Buffer.from(stopStr);\n const GT = 62;\n let tagMatchStart = -1;\n let state = 0;\n for (let i = this.startIndex; i < inputLength; i++) {\n const b = this.buffer[i];\n if (state === 1) {\n if (isSpaceCode(b)) continue;\n if (b === GT) { state = 2; }\n else { state = 0; tagMatchStart = -1; }\n } else {\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopBuffer[j]) { matched = false; break; }\n }\n if (matched) { state = 1; tagMatchStart = i; i += stopLength - 1; }\n }\n if (state === 2) {\n const result = this.buffer.slice(this.startIndex, tagMatchStart).toString(nodeEncoding);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readFromBuffer(n, shouldUpdate) {\n if (n === 1) {\n const { char, width } = decodeCharAt(this.buffer, this.startIndex);\n if (shouldUpdate) this.updateBufferBoundary(width);\n return char;\n }\n const ch = this.buffer.slice(this.startIndex, this.startIndex + n).toString(nodeEncoding);\n if (shouldUpdate) this.updateBufferBoundary(n);\n return ch;\n },\n\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n if (this.autoFlush && this.startIndex >= this.flushThreshold && this._tokenStart < 0) {\n this.flush();\n }\n },\n\n // Relative to current position, matching FeedableSource's formula.\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n },\n };\n}\n\n// ─── decodeCharAt implementations ───────────────────────────────────────────\n\nexport function decodeCharAtFixedWidth1(buf, i) {\n return { char: String.fromCharCode(buf[i]), width: 1 };\n}\n\nexport function decodeCharAtUtf8(buf, i) {\n const b0 = buf[i];\n let width = 1;\n if ((b0 & 0x80) === 0x00) width = 1;\n else if ((b0 & 0xe0) === 0xc0) width = 2;\n else if ((b0 & 0xf0) === 0xe0) width = 3;\n else if ((b0 & 0xf8) === 0xf0) width = 4;\n // Invalid/truncated lead byte falls through with width 1; toString()\n // below then surfaces U+FFFD, consistent with Node's own UTF-8 handling\n // rather than us inventing a different fallback.\n if (i + width > buf.length) width = buf.length - i;\n const char = buf.toString('utf8', i, i + width);\n return { char, width };\n}\n\n/** UTF-8 continuation bytes (10xxxxxx) are not separate characters. Kept as\n * an export for callers that still classify byte kinds; no longer consumed\n * by this strategy's own position bookkeeping since only the absolute index\n * is tracked now. */\nexport function isUtf8ContinuationByte(b) {\n return (b & 0xc0) === 0x80;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { createByteScanStrategy, decodeCharAtUtf8 } from '../Encoding/ScanStrategy/ByteScanStrategy.js';\nimport { QUOTE_PAIRS_CAPACITY } from '../util.js';\n\nconst Constants = {\n space: 32,\n tab: 9,\n};\n\n// Zero-config default when no profile is supplied (e.g. tests/callers that\n// construct BufferSource directly rather than through XMLParser): UTF-8\n// byte-scan. Identical output/cost to the pre-encoding-feature code for pure\n// ASCII content (the common case) and correctly decodes multi-byte UTF-8,\n// which the old raw fromCharCode() path did not.\nconst DEFAULT_SCAN_STRATEGY = createByteScanStrategy(decodeCharAtUtf8, 'utf8');\n\n/**\n * BufferSource — input source backed by a Node.js Buffer (byte array).\n *\n * ### Memory reclamation\n *\n * The full document is available from the start, so there is no chunk-boundary\n * risk and rewindToMark() is a safe no-op. However, the parsed prefix of the\n * Buffer is held in memory until the parse finishes. flush() reclaims it by\n * slicing the Buffer and resetting startIndex to 0.\n *\n * The same mark/flush protocol used by FeedableSource is implemented here so\n * all reader functions work without source-type conditionals:\n *\n * markTokenStart() — save current read position at the start of a token\n * rewindToMark() — no-op for BufferSource (full doc always present)\n * flush() — drop the already-parsed prefix to free memory\n *\n * Auto-flush fires inside updateBufferBoundary() whenever the processed\n * portion exceeds flushThreshold and no token checkpoint is active.\n */\nexport default class BufferSource {\n /**\n * @param {Buffer} bytesArr — the full XML document as a Node.js Buffer\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed bytes\n */\n /**\n * @param {Buffer} bytesArr — the full XML document as a Node.js Buffer\n * @param {object} [options]\n * @param {boolean} [options.autoFlush=true] — enable automatic flushing\n * @param {number} [options.flushThreshold=1024] — flush after this many processed bytes\n * @param {object} [profile] — resolved encoding profile from\n * Encoding/EncodingProfile.js#buildProfileForBuffer. Omit for the\n * zero-config UTF-8 default (used directly by tests/callers that don't\n * go through XMLParser).\n */\n constructor(bytesArr, options = {}, profile = null) {\n // BOM bytes (if any) are detection artifacts, not content — strip them\n // and exclude from the index.\n this.buffer = profile?.bomLength ? bytesArr.subarray(profile.bomLength) : bytesArr;\n if (profile?.decodeFirst) {\n // Not self-synchronizing (UTF-16, or a custom encoding that didn't\n // assert selfSynchronizing) — byte-level delimiter scanning is unsafe,\n // so decode the whole buffer once up front and hand off to\n // CharScanStrategy, which then behaves exactly like StringSource.\n const decoder = profile.descriptor.createDecoder();\n this.buffer = decoder.write(this.buffer) + decoder.end();\n }\n this.startIndex = 0;\n // Running total of bytes/chars trimmed off the front by flush() so far.\n // See StringSource.js's copy of this field / util.js#absolutePosition —\n // startIndex alone drifts from the true document offset after any flush.\n this._baseOffset = 0;\n\n this.autoFlush = options.autoFlush !== false;\n this.flushThreshold = options.flushThreshold ?? 1024;\n\n // Token-start checkpoint for mark/rewind (mirrors FeedableSource API).\n this._tokenStart = -1;\n\n // Reused across every scanTagExpEnd() call, never reallocated. See\n // StringSource.js's copy of this field for the full doc. Populated by\n // whichever ScanStrategy (byte or char) is assigned below.\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n\n // Resolve once, dispatch polymorphically from here on — no encoding\n // branching anywhere else in this class. See EncodingProfile.js.\n const strategy = profile?.scanStrategy ?? DEFAULT_SCAN_STRATEGY;\n Object.assign(this, strategy);\n this.encodingName = profile?.descriptor?.name ?? 'utf8';\n // See EncodingProfile.js's buildProfileForBuffer() doc — false only for\n // ByteScanStrategy+utf8, where a raw byte offset can land mid-character\n // once decoded. DEFAULT_SCAN_STRATEGY (used when no profile is supplied)\n // is itself utf8 byte-scan, so the no-profile default must also be false.\n this._quotePairsUsable = profile ? profile.quotePairsUsable !== false : false;\n }\n\n // ─── Token-start checkpoint ───────────────────────────────────────────────\n\n /**\n * Save the current read position as the start of a new logical token.\n *\n * For BufferSource this primarily guards flush() from reclaiming data that\n * is still being read, mirroring the same safety invariant as FeedableSource.\n */\n markTokenStart() {\n this._tokenStart = this.startIndex;\n }\n\n /**\n * Restore startIndex to the last markTokenStart() position.\n *\n * BufferSource always has the full document available, so a mid-token end\n * of input cannot occur and this method is a safe no-op. It exists solely\n * so caller code can call rewindToMark() unconditionally without branching\n * on source type.\n */\n rewindToMark() {\n // No-op: the complete document is in memory; no rewind is ever needed.\n }\n\n /**\n * Discard the already-processed prefix of the buffer to free memory.\n *\n * Uses Buffer.subarray() (zero-copy view) rather than Buffer.slice() for\n * clarity, then copies to a fresh Buffer so the original allocation can be\n * GC'd. If a token checkpoint is active, the flush origin is moved back to\n * the checkpoint so the in-progress token is preserved.\n */\n flush() {\n const origin = this._tokenStart >= 0 ? this._tokenStart : this.startIndex;\n if (origin > 0) {\n // Buffer.from(subarray) copies the bytes so the original large Buffer\n // can be released by the GC once no other references remain.\n this.buffer = Buffer.from(this.buffer.subarray(origin));\n if (this._tokenStart >= 0) {\n this.startIndex -= origin;\n this._tokenStart = 0;\n } else {\n this.startIndex = 0;\n }\n this._baseOffset += origin;\n }\n }\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\n\nconst DECL_PEEK_BYTES = 200; // more than enough for a <?xml ... ?> declaration\n\n/**\n * sniff(bytes, registry) -> { encoding, bomLength, declaredEncoding }\n *\n * Pure function — no state, easy to unit test standalone. Only needs the\n * leading bytes of the document (caller decides how many it has available).\n *\n * Algorithm (XML 1.0 Appendix F, practical subset):\n * 1. Check for a known BOM signature.\n * 2. ASCII-sniff far enough to find `encoding=\"...\"` inside a leading\n * `<?xml ... ?>` declaration (the declaration's own bytes are ASCII-\n * stable across UTF-8/ASCII/Latin-1/most single-byte sets, which is\n * exactly why this step doesn't need to already know the encoding).\n * 3. BOM present + declared encoding present + they disagree -> hard error.\n * 4. Neither found -> default to utf8 (spec default).\n */\nexport function sniff(bytes, registry) {\n const bomMatch = matchBom(bytes, registry);\n const declaredEncoding = sniffDeclaration(bytes, bomMatch ? bomMatch.bomLength : 0);\n\n if (bomMatch && declaredEncoding && !sameEncoding(bomMatch.descriptor.name, declaredEncoding, registry)) {\n throw new ParseError(\n `Byte-order mark indicates \"${bomMatch.descriptor.name}\" but the XML declaration says encoding=\"${declaredEncoding}\"`,\n ErrorCode.ENCODING_MISMATCH\n );\n }\n\n if (bomMatch) {\n return { encoding: bomMatch.descriptor.name, bomLength: bomMatch.bomLength, declaredEncoding };\n }\n if (declaredEncoding) {\n return { encoding: declaredEncoding, bomLength: 0, declaredEncoding };\n }\n return { encoding: 'utf8', bomLength: 0, declaredEncoding: null };\n}\n\nfunction matchBom(bytes, registry) {\n for (const descriptor of registry.bomCandidates()) {\n const sig = descriptor.bomBytes;\n if (bytes.length >= sig.length && sig.equals(bytes.subarray(0, sig.length))) {\n return { descriptor, bomLength: sig.length };\n }\n }\n return null;\n}\n\nfunction sniffDeclaration(bytes, offset) {\n // ASCII-decode a bounded prefix; non-ASCII-safe encodings (UTF-16 etc.) are\n // already handled via BOM before we'd ever reach here without one.\n const prefix = bytes.subarray(offset, Math.min(bytes.length, offset + DECL_PEEK_BYTES)).toString('latin1');\n const declMatch = prefix.match(/^\\s*<\\?xml\\s+[^?]*\\?>/);\n if (!declMatch) return null;\n const encMatch = declMatch[0].match(/encoding\\s*=\\s*[\"']([^\"']+)[\"']/i);\n return encMatch ? encMatch[1].toLowerCase() : null;\n}\n\nfunction sameEncoding(a, b, registry) {\n try {\n return registry.resolve(a).name === registry.resolve(b).name;\n } catch {\n return false;\n }\n}\n","/**\n * TextDecoderAdapter — gives a { write(buf): string, end(): string } shaped\n * stateful decoder, same contract every EncodingRegistry descriptor already\n * promises, but built on the global TextDecoder instead of Node's\n * node:string_decoder.\n *\n * Why this exists: node:string_decoder is Node-only. Bundling it for a\n * browser target fails outright (no browser polyfill is registered by\n * default). TextDecoder is available natively in every modern browser *and*\n * in Node (global since Node 11), and it already supports incremental,\n * chunk-safe decoding via { stream: true } — a multi-byte character split\n * across two write() calls is buffered internally and stitched together on\n * the next call, same safety guarantee StringDecoder gave.\n *\n * Scope: only replaces the five *built-in* encodings (utf8, ascii, latin1,\n * utf16le, utf16be). Fully custom Node-only decoders (e.g. iconv-lite based\n * Shift_JIS) registered via `decoding.customDecoders` are untouched — they\n * keep supplying their own createDecoder() and are expected to only run\n * under Node, same as before.\n */\n\n/**\n * @param {string} label - a valid TextDecoder label ('utf-8', 'utf-16le', 'windows-1252', ...)\n * @returns {{ write(buf: Uint8Array): string, end(): string }}\n */\nexport function createTextDecoderAdapter(label) {\n const decoder = new TextDecoder(label, { fatal: false, ignoreBOM: true });\n return {\n write(buf) {\n // stream:true holds back a trailing partial multi-byte sequence\n // instead of emitting U+FFFD for it, and prepends it on the next call.\n return decoder.decode(buf, { stream: true });\n },\n end() {\n // Final call, no more bytes coming — flush anything held back.\n // A non-empty result here means genuinely truncated input; TextDecoder\n // substitutes U+FFFD for it, matching StringDecoder's prior behavior.\n return decoder.decode();\n },\n };\n}\n\n/**\n * utf16be has no native TextDecoder label. Same approach as the previous\n * Node-based implementation: byte-swap to little-endian, then decode as\n * utf-16le. Kept here (rather than EncodingRegistry) since it's just another\n * flavor of \"adapter around a decoder\".\n */\nexport function createUtf16BeAdapter() {\n const inner = createTextDecoderAdapter('utf-16le');\n let pending = null; // holds a single odd leftover byte across writes\n\n return {\n write(buf) {\n let work = pending ? concatBytes(pending, buf) : buf;\n pending = null;\n if (work.length % 2 === 1) {\n pending = work.subarray(work.length - 1);\n work = work.subarray(0, work.length - 1);\n }\n const swapped = swapBytePairs(work);\n return inner.write(swapped);\n },\n end() {\n return inner.end();\n },\n };\n}\n\nfunction concatBytes(a, b) {\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n}\n\nfunction swapBytePairs(bytes) {\n const swapped = new Uint8Array(bytes);\n for (let i = 0; i + 1 < swapped.length; i += 2) {\n const tmp = swapped[i];\n swapped[i] = swapped[i + 1];\n swapped[i + 1] = tmp;\n }\n return swapped;\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { createTextDecoderAdapter, createUtf16BeAdapter } from './TextDecoderAdapter.js';\n\n/**\n * EncodingRegistry — owns the set of known EncodingDescriptors.\n *\n * A descriptor is a pure data + factory bundle:\n * {\n * name, aliases,\n * bomBytes // Buffer | null — signature to detect this encoding via BOM\n * selfSynchronizing // boolean — true if an ASCII delimiter byte (<,>,\",')\n * // can never appear as part of a multi-byte sequence.\n * // Only self-synchronizing encodings are eligible for\n * // BufferSource's byte-level scan fast path.\n * variableWidth // boolean — true if bytes-per-character varies.\n * // Informational only for now (position reporting\n * // is index-only, so nothing currently branches\n * // on this) — kept on the descriptor shape so\n * // existing custom-encoding registrations don't\n * // need to change.\n * createDecoder() // -> { write(buf): string, end(): string } stateful decoder\n * }\n *\n * SRP: this class only stores/validates/resolves descriptors. It knows nothing\n * about scanning strategy — see ScanStrategy/, composed together in\n * EncodingProfile.js.\n */\nexport default class EncodingRegistry {\n constructor() {\n this._byName = new Map();\n this._seedDefaults();\n }\n\n _seedDefaults() {\n this.register({\n name: 'utf8',\n aliases: ['utf-8'],\n bomBytes: Buffer.from([0xef, 0xbb, 0xbf]),\n selfSynchronizing: true,\n variableWidth: true,\n createDecoder: () => createTextDecoderAdapter('utf-8'),\n });\n this.register({\n name: 'ascii',\n aliases: [],\n bomBytes: null,\n selfSynchronizing: true,\n variableWidth: false,\n // TextDecoder has no dedicated 'ascii' label. windows-1252 is a strict\n // superset of ASCII and decodes any valid ASCII byte identically —\n // only bytes 0x80-0x9F (never legal ASCII) would differ, so behavior\n // for real ASCII input is unchanged.\n createDecoder: () => createTextDecoderAdapter('windows-1252'),\n });\n this.register({\n name: 'latin1',\n aliases: ['iso-8859-1', 'binary'],\n bomBytes: null,\n selfSynchronizing: true,\n variableWidth: false,\n createDecoder: () => createTextDecoderAdapter('iso-8859-1'),\n });\n this.register({\n name: 'utf16le',\n aliases: ['utf-16le', 'ucs2', 'ucs-2'],\n bomBytes: Buffer.from([0xff, 0xfe]),\n selfSynchronizing: false,\n variableWidth: true,\n createDecoder: () => createTextDecoderAdapter('utf-16le'),\n });\n this.register({\n name: 'utf16be',\n aliases: ['utf-16be'],\n // No native TextDecoder label for utf16be either; byte-swap then\n // decode as utf16le, same trick as before.\n bomBytes: Buffer.from([0xfe, 0xff]),\n selfSynchronizing: false,\n variableWidth: true,\n createDecoder: () => createUtf16BeAdapter(),\n });\n }\n\n /**\n * Register a descriptor. Validates shape immediately (fail-fast), same\n * spirit as ValueParserRegistry.register() in base-output-builder: a\n * broken custom encoding should throw at registration time, not silently\n * corrupt data three parses later.\n */\n register(descriptor) {\n if (!descriptor || typeof descriptor.name !== 'string' || !descriptor.name) {\n throw new ParseError('Encoding descriptor requires a non-empty \"name\"', ErrorCode.INVALID_DECODER);\n }\n if (typeof descriptor.createDecoder !== 'function') {\n throw new ParseError(`Encoding \"${descriptor.name}\" is missing createDecoder()`, ErrorCode.INVALID_DECODER);\n }\n const probe = descriptor.createDecoder();\n if (!probe || typeof probe.write !== 'function' || typeof probe.end !== 'function') {\n throw new ParseError(\n `Encoding \"${descriptor.name}\"'s createDecoder() must return an object with write()/end()`,\n ErrorCode.INVALID_DECODER\n );\n }\n const resolved = {\n selfSynchronizing: false, // safe default per savepoint §3 — opt-in speed, not opt-in correctness\n variableWidth: true,\n aliases: [],\n bomBytes: null,\n ...descriptor,\n };\n this._byName.set(resolved.name.toLowerCase(), resolved);\n for (const alias of resolved.aliases) this._byName.set(alias.toLowerCase(), resolved);\n }\n\n resolve(name) {\n const descriptor = this._byName.get(String(name).toLowerCase());\n if (!descriptor) {\n throw new ParseError(`Unsupported encoding \"${name}\"`, ErrorCode.UNSUPPORTED_ENCODING);\n }\n return descriptor;\n }\n\n /** All descriptors that carry a BOM signature, for detection (longest first). */\n bomCandidates() {\n const seen = new Set();\n const out = [];\n for (const d of this._byName.values()) {\n if (d.bomBytes && !seen.has(d.name)) {\n seen.add(d.name);\n out.push(d);\n }\n }\n return out.sort((a, b) => b.bomBytes.length - a.bomBytes.length);\n }\n}\n\nexport const defaultEncodingRegistry = new EncodingRegistry();\n","'use strict';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { isSpaceCode, errorPositionOf } from \"./util.js\"\n\n/**\n * AttributeProcessor — owns all attribute parsing logic.\n *\n * Two-pass attribute processing:\n *\n * Pass 1 — collectRawAttributes()\n * Populates the rawAttributes map from the raw attribute expression string.\n * Called inside buildTagExpObj() (via XmlPartReader) so rawAttributes is\n * ready before readOpeningTag() calls matcher.updateCurrent(rawAttributes).\n * The matcher must reflect all raw attribute values before any value-parser\n * runs so that attribute-based path expressions (e.g. \"div[class=code]\")\n * resolve correctly during pass 2.\n *\n * Pass 2 — flushAttributes()\n * Calls outputBuilder.addAttribute() for each attribute, running the full\n * value-parser chain. Called from readOpeningTag() AFTER\n * matcher.updateCurrent(), so the read-only matcher already carries the\n * complete attribute context when value parsers execute.\n */\n\n// Module-level regex kept for reference only — no longer called from this\n// module. parseAttributes() below replaces it with an O(n) linear scanner\n// that is immune to catastrophic backtracking and stack overflow.\n// const attrsRegx = new RegExp('([^\\\\s=]+)\\\\s*(=\\\\s*([\\'\"])([\\\\s\\\\S]*?)\\\\3)?', 'gm');\n\n/**\n * Parse an attribute expression string into an array of match tuples.\n *\n * Each element is `{ name, value, startIndex }` — `value` is `undefined` for\n * a boolean attribute (no `=`). Earlier versions of this function also built\n * a full-match string and an `'=value'` string per attribute (matching an\n * old regex-based getAllMatches() return shape) — neither was ever read by\n * collectRawAttributes()/flushAttributes() (only `name`, `value`, and\n * `.startIndex` are), so building them was pure wasted string concatenation\n * on every attribute, on every tag. Dropped.\n *\n * The implementation is a single O(n) pass over char codes with no regex and\n * no recursion, making it safe for arbitrarily long attribute strings.\n *\n * State machine:\n * SEEK_NAME — skipping whitespace looking for the start of an attr name\n * IN_NAME — accumulating a name token until whitespace or '='\n * SEEK_VALUE — saw name + optional whitespace, now expecting '=' or next name\n * IN_VALUE — inside a quoted value, accumulating until the closing quote\n *\n * @param {string} attrStr\n * @param {Array<number>} [quotePairs] - flat [openIdx, closeIdx, ...] list from\n * scanTagExpEnd(), offsets relative to the *tag expression* (not attrStr).\n * When a value's opening quote lines up with the next expected pair, the\n * closing quote's position is taken directly instead of re-scanning for\n * it — the per-character `!== quote` comparison is skipped entirely for\n * that value. Falls back to the old per-character scan (undefined, or a\n * mismatch — belt-and-braces, should never trigger given how the pairs\n * are produced, but costs nothing to check once per value) so correctness\n * never depends on the fast path succeeding.\n * @param {number} [attrsOffset] - offset of attrStr's first character within\n * the coordinate system quotePairs is expressed in — required whenever\n * quotePairs is passed.\n * @param {number} [quotePairsLen=0] - how many entries in `quotePairs` are\n * valid — it's a reused fixed-capacity typed array, not sized to this tag,\n * so `quotePairs.length` itself is not the right bound to loop against.\n * @returns {Array<{name: string, value: string|undefined, startIndex: number}>}\n */\n/**\n * True for character codes illegal as literal content anywhere in the\n * document (mirrors util.js's isIllegalControlCode — not imported directly\n * to keep this hot loop free of a cross-module call for a one-line check).\n */\nfunction isIllegalAttrCode(c) {\n return c <= 8 || c === 11 || c === 12 || (c >= 14 && c <= 31);\n}\n\n/**\n * Scan one quoted attribute value's characters from `i` up to `end`\n * (exclusive), folding whitespace per XML §3.3.3 and rejecting illegal\n * control characters. Shared by both the fast path (closing quote position\n * already known from scanTagExpEnd's quote pairs) and the slow path (closing\n * quote found by scanning for `quote`) below — same rules either way.\n *\n * - real `\\r\\n` pair → exactly one space (not two)\n * - lone `\\r` or lone `\\n` → one space\n * - literal tab (0x09) → one space (a `	` reference is left\n * untouched — that's resolved later, by\n * the entity value-parser, not here)\n * - other illegal control code → throws ILLEGAL_CHARACTER\n *\n * @returns {string} the folded value (does not include the closing quote)\n */\nfunction scanAttrValue(attrStr, i, end, parser) {\n let value = '';\n let segStart = i;\n for (; i < end; i++) {\n const c = attrStr.charCodeAt(i);\n if (c === 13) { // \\r — possibly paired with a following \\n\n value += attrStr.substring(segStart, i) + ' ';\n if (attrStr.charCodeAt(i + 1) === 10) i++;\n segStart = i + 1;\n } else if (c === 10 || c === 9) { // lone \\n, or literal tab\n value += attrStr.substring(segStart, i) + ' ';\n segStart = i + 1;\n } else if (isIllegalAttrCode(c)) {\n throw new ParseError(\n `Illegal control character 0x${c.toString(16).padStart(2, '0')} in attribute value`,\n ErrorCode.ILLEGAL_CHARACTER,\n parser ? errorPositionOf(parser.source) : {}\n );\n }\n }\n value += attrStr.substring(segStart, i);\n return value;\n}\n\nfunction parseAttributes(attrStr, quotePairs, attrsOffset, quotePairsLen = 0, parser) {\n const results = [];\n const len = attrStr.length;\n let i = 0;\n const usePairs = quotePairs !== undefined && quotePairsLen > 0;\n let pairIdx = 0;\n\n while (i < len) {\n // Skip whitespace between attributes\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n if (i >= len) break;\n\n // Read name\n const nameStart = i;\n while (i < len && attrStr.charCodeAt(i) !== 61 && !isSpaceCode(attrStr.charCodeAt(i))) i++;\n const name = attrStr.substring(nameStart, i);\n\n // Skip whitespace before '='\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n\n if (i >= len || attrStr.charCodeAt(i) !== 61) {\n // Boolean attribute — no '='\n results.push({ name, value: undefined, startIndex: nameStart });\n continue;\n }\n\n i++; // skip '='\n\n // Skip whitespace after '='\n while (i < len && isSpaceCode(attrStr.charCodeAt(i))) i++;\n\n // The character right after '=' (mod whitespace) MUST be a quote — this\n // is never relaxed, in any mode. Reject before consuming anything, so an\n // unquoted value never partially reaches the output builder.\n const quote = attrStr.charCodeAt(i); // NaN when i >= len — also fails both checks below\n if (quote !== 34 && quote !== 39) { // not \" or '\n throw new ParseError(\n `Attribute '${name}' has an unquoted value — attribute values must be wrapped in '\"' or \"'\"`,\n ErrorCode.UNQUOTED_ATTRIBUTE_VALUE,\n parser ? errorPositionOf(parser.source) : {}\n );\n }\n\n // Fast path: the tag-end scanner already found this exact quote pair\n // while looking for '>' — reuse its closing-quote position instead of\n // re-scanning character-by-character for it.\n let closeLocal = -1;\n if (usePairs && pairIdx + 1 < quotePairsLen && quotePairs[pairIdx] === i + attrsOffset) {\n closeLocal = quotePairs[pairIdx + 1] - attrsOffset;\n pairIdx += 2;\n }\n\n i++; // skip opening quote\n let end;\n if (closeLocal >= 0) {\n end = closeLocal;\n } else {\n end = i;\n while (end < len && attrStr.charCodeAt(end) !== quote) end++;\n }\n const value = scanAttrValue(attrStr, i, end, parser);\n i = end + 1; // skip closing quote\n results.push({ name, value, startIndex: nameStart });\n }\n\n return results;\n}\n\n/**\n * Pass 1: extract raw (unparsed) attribute values into rawAttributes, AND\n * build tagExp._parsedAttrs — the processed-name/value list pass 2 will\n * consume directly.\n *\n * Previously, pass 2 (flushAttributes) re-ran parseAttributes() from scratch\n * on the same attrStr, and re-ran parser.processAttrName() (ns-prefix\n * resolution + name validation + sanitizeName + reserved-name check) on\n * every attribute a second time — full re-tokenization plus full re-validation\n * of work already done here. processAttrName() is a pure function of\n * (rawName, options) — nothing between pass 1 and pass 2 (matcher.push,\n * stop/skip resolution) can change its result — so it's safe to compute once\n * and cache. The matcher still gets the *raw* (pre-resolveNsPrefix/sanitize)\n * name as its rawAttributes key, unchanged, since PEM's attribute-condition\n * matching (`div[class=code]`) matches against attribute names as written.\n *\n * @param {string} attrStr - raw attribute expression substring\n * @param {object} parser - Xml2JsParser instance (for processAttrName)\n * @param {object} tagExp - tagExp object to populate rawAttributes\n * @param {Array<number>} [quotePairs] - see parseAttributes() doc.\n * @param {number} [attrsOffset] - see parseAttributes() doc.\n * @param {number} [quotePairsLen] - see parseAttributes() doc.\n */\nexport function collectRawAttributes(attrStr, parser, tagExp, quotePairs, attrsOffset, quotePairsLen) {\n if (!attrStr || attrStr.length === 0) return;\n\n const matches = parseAttributes(attrStr, quotePairs, attrsOffset, quotePairsLen, parser);\n const len = matches.length;\n tagExp._rawAttrMatchCount = len; // total parsed attrs, incl. dropped (xmlns:) ones — for maxAttributesPerTag parity with old behavior\n const parsedAttrs = [];\n let count = 0;\n\n // attributes.duplicate: 'overwrite' (default) needs no bookkeeping — last\n // occurrence naturally wins via the rawAttributes object-assign + the\n // builder's own last-write-wins addAttribute() calls below, exactly as\n // before this option existed. 'ignore'/'throw' need to track names seen\n // so far on *this* tag only — a fresh Set per call, never shared across tags.\n const dupMode = parser.options.attributes?.duplicate || 'overwrite';\n const seen = dupMode !== 'overwrite' ? new Set() : null;\n const boolMode = parser.options.attributes?.booleanType || 'allow';\n\n for (let i = 0; i < len; i++) {\n const m = matches[i];\n\n if (seen) {\n if (seen.has(m.name)) {\n if (dupMode === 'throw') {\n throw new ParseError(\n `Duplicate attribute '${m.name}'`,\n ErrorCode.DUPLICATE_ATTRIBUTE,\n errorPositionOf(parser.source)\n );\n }\n continue; // 'ignore' — first occurrence wins, later ones dropped entirely\n }\n seen.add(m.name);\n }\n\n const rawVal = m.value;\n if (rawVal === undefined) {\n if (boolMode === 'throw') {\n throw new ParseError(\n `Valueless attribute '${m.name}' is not allowed`,\n ErrorCode.BOOLEAN_ATTRIBUTE_REJECTED,\n errorPositionOf(parser.source)\n );\n }\n if (boolMode === 'ignore') continue; // drop silently, rest of tag unaffected\n }\n\n const attrName = parser.processAttrName(m.name);\n if (attrName === false) continue;\n count++;\n const attrVal = rawVal !== undefined ? rawVal : true;\n tagExp.rawAttributes[m.name] = attrVal;\n parsedAttrs.push({ name: attrName, value: attrVal, index: m.startIndex });\n }\n tagExp.rawAttributesLen = count;\n tagExp._parsedAttrs = parsedAttrs;\n}\n\n/**\n * Pass 2: push each attribute (already parsed + name-processed by pass 1,\n * see tagExp._parsedAttrs) to the output builder. No re-parsing, no\n * re-running processAttrName — this is now a plain loop over cached data.\n *\n * @param {Array<{name: string, value: *, index: number}>} parsedAttrs - tagExp._parsedAttrs from collectRawAttributes\n * @param {object} parser - Xml2JsParser instance\n * @param {number} [attrsExpStart] - absolute document offset where the\n * attribute expression began (tagExp._attrsExpStart). When provided, each\n * attribute's absolute document index is computed and passed to\n * addAttribute() as a 4th argument: { index }. Line/col are intentionally\n * NOT computed here — doing so would require re-scanning attrStr for\n * newlines on every call, for a field most builders won't use; callers\n * that need it can derive line/col from `index` plus the document text.\n * @param {number} rawAttrMatchCount - tagExp._rawAttrMatchCount, used for the\n * maxAttributesPerTag limit check (counts all parsed attrs, including any\n * dropped by processAttrName, matching the limit's pre-existing semantics).\n */\nexport function flushAttributes(parsedAttrs, parser, attrsExpStart, rawAttrMatchCount) {\n if (!parsedAttrs || parsedAttrs.length === 0) return;\n\n const maxAttrs = parser.options.limits?.maxAttributesPerTag;\n if (maxAttrs !== undefined && maxAttrs !== null && rawAttrMatchCount > maxAttrs) {\n const tagName = parser.currentTagDetail?.name ?? '(unknown)';\n throw new ParseError(\n `Tag '${tagName}' has ${rawAttrMatchCount} attributes, exceeding limit of ${maxAttrs}`,\n ErrorCode.LIMIT_MAX_ATTRIBUTES,\n errorPositionOf(parser.source)\n );\n }\n\n const len = parsedAttrs.length;\n for (let i = 0; i < len; i++) {\n const a = parsedAttrs[i];\n const attrMeta = attrsExpStart !== undefined\n ? { index: attrsExpStart + a.index }\n : undefined;\n parser.outputBuilder.addAttribute(a.name, a.value, parser.readonlyMatcher, attrMeta);\n }\n}","'use strict';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { collectRawAttributes } from './AttributeProcessor.js';\nimport { isSpace, absolutePosition } from \"./util.js\"\n// Re-export flushAttributes so Xml2JsParser and XmlSpecialTagsReader can\n// continue to import it from here without changing their import lines.\nexport { flushAttributes } from './AttributeProcessor.js';\n\nexport class TagExp {\n constructor() {\n this.tagName = \"\";\n this.selfClosing = false;\n // rawAttributes= Object.create(null),\n this.rawAttributes = {};\n this._attrsExp = \"\" // stored for two-pass attribute flushing in readOpeningTag\n this._attrsExpStart = undefined; // absolute document offset of _attrsExp's first char\n this._rawAttrMatchCount = 0;\n this._parsedAttrs = [];\n }\n}\n\n/**\n * Try to match an upcoming closing tag against the name we already expect\n * (the tag sitting on top of the stack) without reading it into a string\n * first. Peeks character-by-character (no consumption) — a mismatch, or\n * running out of buffered data, costs nothing to undo since nothing was\n * consumed. Caller falls back to the normal read+validate+compare path in\n * either case, so this never needs its own error handling or chunk-boundary\n * logic.\n *\n * On success, only whitespace is allowed between the name and '>' — matches\n * XML's own grammar for ETag (`</tag ... >`, no attributes permitted).\n *\n * @param {Source} source\n * @param {string} expectedRawName - the raw (pre namespace-stripped) name\n * the currently-open tag was written with\n * @returns {number} characters to consume (name + whitespace + '>'), or -1\n * if this isn't a match (or not enough data yet to tell)\n */\nexport function tryMatchClosingTagName(source, expectedRawName) {\n // false (mismatch) and null (not enough buffered data yet) both fall back\n // to the same slow path below, so both collapse to -1 here.\n if (source.matchAhead(expectedRawName) !== true) return -1;\n let i = expectedRawName.length;\n let c = source.readChAt(i);\n if (c === '>') return i + 1;\n while (isSpace(c)) {\n i++;\n c = source.readChAt(i);\n if (c === '>') return i + 1;\n }\n if (c !== '>') return -1;\n return i + 1;\n}\n\n/**\n * Read closing tag name.\n *\n * Uses level-1 (inner) mark so flush() knows the safe trim boundary while\n * this reader is in progress. Does NOT overwrite the level-0 outer mark set\n * by parseXml()'s loop, which rewindToMark() always restores to.\n *\n * @param {Source} source\n * @returns {string} tag name\n */\nexport function readClosingTagName(source) {\n source.markTokenStart(1);\n // Closing tags never carry attributes, so unlike an opening tag's\n // expression there is no quoting to worry about — the very first '>' is\n // always the real end. That means the whole name can be found with one\n // direct scan of whatever is already buffered (readUptoChar), instead of\n // asking \"is there more data yet?\" before every single character.\n const start = source.startIndex;\n try {\n const str = source.readUptoChar(\">\");\n return str.trimEnd();\n } catch (err) {\n // Buffer ran out before '>' showed up — the retryable chunk-boundary\n // case (readUptoChar didn't consume anything on failure). Re-throw with\n // whatever was buffered so far in the message so autoClose's truncation\n // recovery (which reads it back out of the message) can still report a\n // useful partial tag name.\n const partial = source.readStr(Number.MAX_SAFE_INTEGER, start);\n throw new ParseError(`Unexpected end of source reading closing tag '</${partial}'`, ErrorCode.UNEXPECTED_END);\n }\n}\n\n/**\n * Read an XML opening tag expression and return a tag descriptor.\n *\n * Handles normal tags — not comments, CDATA, or DOCTYPE.\n * Example input (from source, after '<'): `tag attr='some\"' attr2=\">\" bool>`\n *\n * Uses level-1 (inner) mark — see readClosingTagName for rationale.\n *\n * @param {object} parser - Xml2JsParser instance\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp }}\n */\nexport function readTagExp(parser) {\n parser.source.markTokenStart(1);\n // Absolute document offset where `exp` (tag name onward, right after '<')\n // begins — captured before any reads so buildTagExpObj can compute each\n // attribute's absolute document position from its offset within attrsExp.\n const expStart = absolutePosition(parser.source);\n\n // Pick the scan variant once — scanTagExpEnd records quote positions for\n // AttributeProcessor to reuse; scanTagExpEndFast skips that bookkeeping\n // entirely when attributes are being skipped (nobody reads _quotePairs).\n // No flag is passed into the loop; the decision is made here, once.\n const collectQuotes = !parser.options.skip.attributes;\n const relEnd = collectQuotes\n ? parser.source.scanTagExpEnd()\n : parser.source.scanTagExpEndFast();\n\n if (relEnd === -1) {\n // Buffer exhausted before an unquoted '>' was found — chunk boundary\n // mid-tag. Throw UNEXPECTED_END so feed()/parseStream() rewinds to the\n // level-0 outer mark and retries. (Note: scanTagExpEnd() only returns a\n // non-negative index once both quote flags are already balanced-closed —\n // by construction, not by a separate post-scan check — so there is no\n // longer a distinct \"unclosed quote but '>' was found\" case to detect;\n // the old UNCLOSED_QUOTE branch here was checking the same two flags\n // immediately after the only code path that requires them both false,\n // making it permanently unreachable.)\n throw new ParseError(\"Unexpected closing of source waiting for '>'\", ErrorCode.UNEXPECTED_END);\n }\n\n const exp = parser.source.readStr(relEnd);\n parser.source.updateBufferBoundary(relEnd + 1);\n\n // Quote positions the scan above already found while looking for '>' —\n // handed to AttributeProcessor so it doesn't re-scan for quotes itself.\n // Only usable when this source's offsets are guaranteed to line up 1:1\n // with characters in the decoded `exp` string — false for BufferSource\n // reading UTF-8, where a byte offset can land mid-character (see\n // BufferSource._quotePairsUsable doc). Consumed synchronously by\n // buildTagExpObj below, before any other tag scan can touch the array\n // again, so no staleness risk. `_quotePairs` is a fixed-capacity reusable\n // typed array — `_quotePairsLen` says how many of its slots are valid for\n // this tag (not `_quotePairs.length`, which is always the full capacity).\n const usableQuotes = collectQuotes && parser.source._quotePairsUsable !== false;\n const quotePairs = usableQuotes ? parser.source._quotePairs : undefined;\n const quotePairsLen = usableQuotes ? parser.source._quotePairsLen : 0;\n\n return buildTagExpObj(exp, parser, expStart, false, quotePairs, quotePairsLen);\n}\n\n/**\n * Read a processing-instruction tag expression (<?name attrs?>).\n *\n * Uses level-1 (inner) mark — see readClosingTagName for rationale.\n *\n * @param {object} parser\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp }}\n */\nexport function readPiExp(parser) {\n parser.source.markTokenStart(1);\n const expStart = absolutePosition(parser.source);\n let inSingleQuotes = false;\n let inDoubleQuotes = false;\n let i;\n let EOE = false;\n\n for (i = 0; parser.source.canRead(i); i++) {\n const currentChar = parser.source.readChAt(i);\n const nextChar = parser.source.readChAt(i + 1);\n\n if (currentChar === \"'\" && !inDoubleQuotes) {\n inSingleQuotes = !inSingleQuotes;\n } else if (currentChar === '\"' && !inSingleQuotes) {\n inDoubleQuotes = !inDoubleQuotes;\n }\n\n if (!inSingleQuotes && !inDoubleQuotes) {\n if (currentChar === '?' && nextChar === '>') {\n EOE = true;\n break;\n }\n }\n }\n\n if (!EOE) {\n // Buffer exhausted before '?>' — chunk boundary mid-PI-tag.\n throw new ParseError(\"Unexpected closing of source waiting for '?>'\", ErrorCode.UNEXPECTED_END);\n } else if (inSingleQuotes || inDoubleQuotes) {\n // '?>' found but a quote was never closed — real syntax error.\n throw new ParseError(\"Invalid attribute expression. Quote is not properly closed in PI tag expression\", ErrorCode.UNCLOSED_QUOTE);\n }\n\n // if (!parser.options.skip.attributes) {\n // //TODO: use regex to verify attributes if not set to ignore\n // }\n\n const exp = parser.source.readStr(i);\n parser.source.updateBufferBoundary(i + 2);\n return buildTagExpObj(exp, parser, expStart, true);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Parse a raw tag expression string into a structured tag descriptor.\n *\n * @param {string} exp - everything between '<' and '>' (exclusive)\n * @param {object} parser\n * @param {number} [expStart] - absolute document offset where `exp` begins\n * (i.e. right after '<' or '<?'). Used to compute each attribute's absolute\n * document position (tagExp._attrsExpStart) for addAttribute()'s meta arg.\n * Optional so callers that don't have/need it can omit it — attribute\n * position metadata is simply unavailable in that case, not an error.\n * @param {Array<number>} [quotePairs] - flat [openIdx, closeIdx, ...] list\n * from scanTagExpEnd(), offsets relative to `exp`'s start. Passed through\n * to collectRawAttributes so parseAttributes() can skip re-scanning for\n * quotes. Undefined when unavailable/unsafe for this source or call site\n * (readPiExp never supplies one) — collectRawAttributes falls back to its\n * own scan in that case.\n * @param {number} [quotePairsLen=0] - how many entries in `quotePairs` are\n * valid (it's a reused fixed-capacity array, not sized to this tag).\n * @returns {{ tagName, selfClosing, rawAttributes, _attrsExp, _attrsExpStart }}\n */\nfunction buildTagExpObj(exp, parser, expStart, forceToReadAttrs = false, quotePairs = undefined, quotePairsLen = 0) {\n const tagExp = new TagExp();\n\n const expLen = exp.length;\n\n if (exp[expLen - 1] === \"/\") {\n tagExp.selfClosing = true;\n exp = exp.slice(0, -1); // Remove the trailing slash\n }\n\n // Separate tag name from attribute expression\n let attrsExp = \"\";\n let i = 0;\n let attrsLocalOffset; // exp-relative offset where attrsExp begins — rebases quotePairs\n\n for (; i < exp.length; i++) {\n const c = exp[i];\n if (isSpace(c)) {\n tagExp.tagName = exp.substring(0, i);\n attrsExp = exp.substring(i + 1);\n attrsLocalOffset = i + 1;\n if (expStart !== undefined) tagExp._attrsExpStart = expStart + i + 1;\n break;\n }\n }\n //only tag\n if (tagExp.tagName.length === 0 && i === exp.length) tagExp.tagName = exp;\n tagExp.tagName = tagExp.tagName.trimEnd();\n tagExp._attrsExp = attrsExp;\n\n if (!parser.isValidQName(tagExp.tagName)) {\n throw new ParseError(\"Invalid tag name\", ErrorCode.INVALID_TAG_NAME);\n }\n\n // Pass 1: collect raw attribute values for matcher.updateCurrent().\n // Pass 2 (flushAttributes) runs later in readOpeningTag, after updateCurrent()\n // — and is still gated by skip.attributes there, so output/matcher visibility\n // of attribute values is unaffected by the change below.\n //\n // This pass itself, however, always runs whenever an attribute expression is\n // present — even when skip.attributes is true. Malformed attribute syntax\n // (unquoted values, illegal duplicates, illegal control characters) is a\n // document-validity problem, not an output-shaping one, so skip.attributes\n // must not be able to silently let broken markup through.\n if (forceToReadAttrs || attrsExp.length > 0) {\n collectRawAttributes(attrsExp, parser, tagExp, quotePairs, attrsLocalOffset, quotePairsLen);\n }\n // console.log(tagExp)\n return tagExp;\n}\n\n","import { ParseError, ErrorCode } from './ParseError.js';\nimport { isSpace, ensureCanRead, absolutePosition } from './util.js';\n\n/**\n * Well-known enclosure presets.\n *\n * Import these in your parser config to compose skipEnclosures arrays:\n *\n * import { xmlEnclosures, quoteEnclosures } from '@nodable/flexible-xml-parser';\n *\n * stopNodes: [\n * \"..script\", // plain — no enclosures (default)\n * { expression: \"body..pre\", skipEnclosures: [...xmlEnclosures] },\n * { expression: \"head..style\", skipEnclosures: [...xmlEnclosures, ...quoteEnclosures] },\n * { expression: \"root.stopNode\", nested: true, skipEnclosures: [{ open: '<!--', close: '-->' }] },\n * ]\n */\n\n/** XML structural delimiters — comments, CDATA, processing instructions. */\nexport const xmlEnclosures = [\n { open: '<!--', close: '-->' }, // comment\n { open: '<![CDATA[', close: ']]>' }, // CDATA section\n { open: '<?', close: '?>' }, // processing instruction\n];\n\n/** String literal delimiters — useful for JS / CSS stop-node content. */\nexport const quoteEnclosures = [\n { open: \"'\", close: \"'\" },\n { open: '\"', close: '\"' },\n { open: '`', close: '`' }, // template literal\n];\n\n/**\n * StopNodeProcessor — self-contained processor for stop nodes.\n *\n * A stop node is a \"sealed envelope\": the parser goes blind the moment it\n * enters one, collecting raw characters until the matching closing tag is\n * found. The content is returned as a raw string and never parsed by the\n * XML engine.\n *\n * ### Modes\n *\n * The behaviour is controlled by two independent flags:\n *\n * **`nested`** (boolean, default false):\n * When true, the processor tracks the depth of nested same-name opening\n * tags. The stop node ends only when the depth returns to zero — i.e. the\n * closing tag that matches the original opening tag. When false (default),\n * the very first `</tagName>` ends the stop node regardless of nesting.\n *\n * **`skipEnclosures`** (array, default []):\n * A list of `{ open: string, close: string }` pairs. When the processor\n * encounters an open marker it consumes everything up to the close marker\n * wholesale, suppressing all closing-tag (and depth) logic for that span.\n * Enclosures are checked in array order; the first match wins. When the\n * array is empty, no enclosure skipping is performed.\n *\n * The two flags compose freely:\n *\n * | nested | skipEnclosures | Behaviour |\n * |--------|----------------|------------------------------------------------------------------------|\n * | false | [] | Plain: stop at first `</tagName>`. |\n * | true | [] | Depth-only: track nested open tags, no enclosures. |\n * | false | [...] | Enclosure-only: skip interiors, stop at first close tag outside them. |\n * | true | [...] | Full: depth tracking + enclosure skipping. |\n *\n * ### Chunk-boundary survival (feedable / stream sources)\n *\n * When input runs out mid-collection, `collect()` throws `UNEXPECTED_END`.\n * The caller (`feed()` in XMLParser) catches it and rewinds the source to the\n * outer mark (the `<` of the stop node's opening tag). On the next `feed()`\n * call `readOpeningTag()` sees the reader is already active (`isActive()`) and\n * calls `resumeAfterOpenTag()` to re-consume the opening tag before calling\n * `collect()` again. All accumulated content and state are preserved in\n * instance fields between attempts.\n */\nexport class StopNodeProcessor {\n /**\n * @param {string} tagName\n * The stop-node tag name to watch for.\n * @param {object} [opts]\n * @param {boolean} [opts.nested=false]\n * When true, nested same-name open tags increment a depth counter; the\n * stop node ends only when depth returns to zero.\n * @param {Array<{open:string,close:string}>} [opts.skipEnclosures=[]]\n * Enclosure pairs whose interiors suppress closing-tag detection.\n */\n constructor(tagName, { nested = false, skipEnclosures = [] } = {}) {\n this._tagName = tagName;\n this._nested = nested;\n this._enclosures = skipEnclosures;\n\n // Runtime state — reset in activate() / resumeAfterOpenTag()\n this._content = '';\n this._depth = 1; // already inside the opening tag\n this._active = false;\n }\n\n /** True once activated; cleared when `collect()` returns successfully. */\n isActive() {\n return this._active;\n }\n\n /**\n * Activate this processor. Called by `readOpeningTag` the first time it\n * encounters the stop node (after `readTagExp` has consumed the opening tag).\n */\n activate() {\n this._active = true;\n this._content = '';\n this._depth = 1;\n }\n\n /**\n * Called on resume (chunk boundary): the source was rewound to the `<` of\n * the stop node's opening tag, so the caller must re-consume the opening tag\n * via `readTagExp` before calling `collect()`.\n *\n * Because the rewind replays the entire opening tag, any content accumulated\n * during the failed attempt is invalid. Reset to a clean post-activation\n * state so the next `collect()` starts fresh from right after the opening tag.\n */\n resumeAfterOpenTag() {\n this._content = '';\n this._depth = 1;\n }\n\n /**\n * Collect raw content from `source` until the matching closing tag is found.\n *\n * Dispatches to one of four internal strategies based on the `nested` flag\n * and whether `skipEnclosures` is non-empty:\n *\n * - Plain (`nested:false`, no enclosures): fastest path — scan for\n * the literal `</tagName>` string and stop immediately.\n * - Depth-only (`nested:true`, no enclosures): track open/close tags\n * for depth, no enclosure skipping.\n * - Enclosure-only (`nested:false`, enclosures): skip enclosure interiors,\n * stop at the first closing tag found outside them.\n * - Full (`nested:true`, enclosures): depth tracking AND\n * enclosure skipping.\n *\n * Progress (`_content`, `_depth`) is stored in instance fields so a\n * chunk-boundary `UNEXPECTED_END` can be retried seamlessly.\n *\n * @param {object} source Any source object with the standard read interface.\n * @returns {{content: string, end: {index: number}}}\n * `content` is the raw text between the opening and closing tags.\n * `end` is the position immediately after the matched closing tag's '>' —\n * mirrors `TagDetail.openEnd` / closeMeta.closeEnd for the opening-tag side,\n * letting a caller recover the exact span of `<tag>...</tag>` including\n * both delimiters, not just the inner content.\n */\n collect(source) {\n source.markTokenStart(1);\n\n const enclosuresLen = this._enclosures.length;//dont inline\n\n if (!this._nested && enclosuresLen === 0) {\n return this._collectPlain(source);\n }\n if (this._nested && enclosuresLen === 0) {\n return this._collectDepthOnly(source);\n }\n if (!this._nested && enclosuresLen > 0) {\n return this._collectEnclosureOnly(source);\n }\n // nested && enclosures.length > 0\n return this._collectFull(source);\n }\n\n // ── Strategy 1: Plain ──────────────────────────────────────────────────────\n\n /**\n * Fastest path. No depth tracking, no enclosure skipping.\n * Scans for the literal `</tagName>` followed by optional whitespace then `>`.\n */\n _collectPlain(source) {\n while (source.canRead()) {\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n continue;\n }\n\n if (this._tryConsumeCloseTag(source)) return this._finish(source);\n this._content += source.readCh();\n }\n\n throw this._unclosedError();\n }\n\n // ── Strategy 2: Depth-only ─────────────────────────────────────────────────\n\n /**\n * Depth tracking without enclosure skipping.\n * Properly handles nested same-name open tags. No enclosure awareness.\n */\n _collectDepthOnly(source) {\n while (this._depth > 0) {\n ensureCanRead(source, 0, `stop node <${this._tagName}> content`);\n if (this._stepDepthTracking(source)) return this._finish(source);\n }\n\n /* istanbul ignore next */\n throw this._unclosedError();\n }\n\n // ── Strategy 3: Enclosure-only ─────────────────────────────────────────────\n\n /**\n * Enclosure skipping without depth tracking.\n * Skips enclosure interiors; stops at the first `</tagName>` found outside them.\n */\n _collectEnclosureOnly(source) {\n while (source.canRead()) {\n // Enclosure openers take priority over everything else\n if (this._trySkipEnclosure(source)) continue;\n\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n continue;\n }\n\n // At '<' outside any enclosure — check for our closing tag\n if (this._tryConsumeCloseTag(source)) return this._finish(source);\n this._content += source.readCh();\n }\n\n throw this._unclosedError();\n }\n\n // ── Strategy 4: Full (nested + enclosures) ─────────────────────────────────\n\n /**\n * Full mode: enclosure skipping AND depth tracking.\n * Enclosure interiors suppress all closing-tag and depth logic for their span.\n * Depth tracks nested same-name open tags; the stop node ends at depth zero.\n */\n _collectFull(source) {\n while (this._depth > 0) {\n ensureCanRead(source, 0, `stop node <${this._tagName}> content`);\n\n // Enclosure openers take priority over tag scanning\n if (this._trySkipEnclosure(source)) continue;\n\n if (this._stepDepthTracking(source)) return this._finish(source);\n }\n\n /* istanbul ignore next */\n throw this._unclosedError();\n }\n\n // ── Shared finish helper ───────────────────────────────────────────────────\n\n /**\n * Reset runtime state and return the accumulated content plus the end\n * position (immediately after the matched closing tag's '>').\n * Called by every strategy when the closing tag is confirmed — always\n * right after that '>' has just been consumed from `source`.\n * @param {object} source\n * @returns {{content: string, end: {index: number}}}\n */\n _finish(source) {\n const result = this._content;\n const end = { index: absolutePosition(source) };\n this._active = false;\n this._content = '';\n this._depth = 1;\n return { content: result, end };\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n /**\n * If an enclosure opens at the current position, consume it and its whole\n * interior (added to `_content` verbatim) and return true. Returns false,\n * consuming nothing, if no enclosure opens here. Shared by the two\n * enclosure-aware strategies (`_collectEnclosureOnly`, `_collectFull`).\n */\n _trySkipEnclosure(source) {\n const encIdx = this._matchEnclosureOpen(source);\n if (encIdx === -1) return false;\n const enc = this._enclosures[encIdx];\n this._skipChars(source, enc.open.length);\n this._content += enc.open;\n const interior = this._readUpto(source, enc.close);\n this._content += interior + enc.close;\n return true;\n }\n\n /**\n * At a '<' that isn't inside an enclosure: check whether it starts our own\n * closing tag (`</tagName` + optional whitespace + `>`). If so, consume it\n * and return true — the caller then calls `_finish()`. If not a match,\n * returns false without consuming anything, so the caller falls back to\n * treating the `<` as ordinary content. Shared by the two depth-unaware\n * strategies (`_collectPlain`, `_collectEnclosureOnly`).\n */\n _tryConsumeCloseTag(source) {\n const needed = '</' + this._tagName;\n if (!this._peekMatch(source, needed)) return false;\n\n let offset = needed.length;\n let validClose = false;\n while (true) {\n const c = source.readChAt(offset);\n if (c === '>') { validClose = true; break; }\n if (isSpace(c)) { offset++; continue; }\n break;\n }\n if (!validClose) return false;\n\n this._skipChars(source, needed.length);\n while (source.canRead()) {\n const c = source.readCh();\n if (c === '>') break;\n }\n return true;\n }\n\n /**\n * Process one tag encountered while depth-tracking is active: plain content\n * is appended as-is, a matching closing tag decrements depth (returning\n * true once depth reaches zero, telling the caller to finish), and any\n * other tag — including a nested opener of our own tag name — is appended\n * verbatim and, for a same-name non-self-closing opener, increments depth.\n * Shared by the two depth-tracking strategies (`_collectDepthOnly`,\n * `_collectFull`), which differ only in whether they check for enclosures\n * before calling this.\n */\n _stepDepthTracking(source) {\n const ch = source.readChAt(0);\n\n if (ch !== '<') {\n this._content += source.readCh();\n return false;\n }\n\n // Consume '<'\n source.readCh();\n ensureCanRead(source, 0, `stop node <${this._tagName}> tag after '<'`);\n\n const c0 = source.readChAt(0);\n\n if (c0 === '/') {\n // Closing tag\n source.readCh(); // consume '/'\n const closeName = this._readTagName(source);\n const closeSuffix = this._readToAngleClose(source);\n\n if (closeName === this._tagName) {\n this._depth--;\n if (this._depth === 0) return true;\n }\n this._content += '</' + closeName + closeSuffix;\n return false;\n }\n\n // Opening tag (including self-closing)\n const openName = this._readTagName(source);\n this._content += '<' + openName;\n\n const { selfClosing, attrText } = this._readTagTail(source);\n this._content += attrText;\n\n if (!selfClosing && openName === this._tagName) {\n this._depth++;\n }\n return false;\n }\n\n /**\n * The \"ran out of input before the stop node closed\" error, identical\n * across all four collection strategies.\n */\n _unclosedError() {\n return new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end of input`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * Check whether any enclosure's `open` marker starts at the current source\n * position (without consuming). Returns the index of the first matching\n * enclosure, or -1 if none match.\n */\n _matchEnclosureOpen(source) {\n const enclosuresLen = this._enclosures.length;\n for (let i = 0; i < enclosuresLen; i++) {\n if (this._peekMatch(source, this._enclosures[i].open)) return i;\n }\n return -1;\n }\n\n /**\n * Read until `stopStr` is found, consuming `stopStr` itself.\n * Returns the text before `stopStr`. Throws UNEXPECTED_END if input runs out.\n */\n _readUpto(source, stopStr) {\n const s0 = stopStr[0];\n const sLen = stopStr.length;\n const start = source.startIndex;\n let len = 0;\n\n while (source.canRead()) {\n if (source.readChAt(0) === s0 && this._peekMatch(source, stopStr)) {\n const text = source.readStr(len, start);\n this._skipChars(source, sLen);\n return text;\n }\n source.readCh();\n len++;\n }\n\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end looking for '${stopStr}'`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * Check whether the source (starting at current position) starts with `str`.\n * Does NOT consume.\n */\n _peekMatch(source, str) {\n const strLen = str.length;\n for (let i = 0; i < strLen; i++) {\n if (source.readChAt(i) !== str[i]) return false;\n }\n return true;\n }\n\n /**\n * Consume exactly `n` characters from source (discarding them — the caller\n * is responsible for appending to `_content` if needed).\n */\n _skipChars(source, n) {\n for (let i = 0; i < n; i++) source.readCh();\n }\n\n /**\n * Read an XML name (tag name) from the current source position.\n * Stops at `>`, `/`, or any whitespace. Does NOT consume the delimiter.\n */\n _readTagName(source) {\n let name = '';\n while (source.canRead()) {\n const ch = source.readChAt(0);\n if (ch === '>' || ch === '/' || isSpace(ch)) break;\n name += source.readCh();\n }\n return name;\n }\n\n /**\n * Read from after the tag name up to and including the closing `>`,\n * detecting self-closing `/>` and respecting quoted attribute values so\n * a `>` inside a value does not prematurely end the tag.\n *\n * Returns `{ selfClosing: boolean, attrText: string }` where `attrText`\n * includes everything from the first attribute character up to and\n * including the closing `>` (or `/>`).\n */\n _readTagTail(source) {\n const start = source.startIndex;\n let len = 0;\n let inSingle = false;\n let inDouble = false;\n\n while (source.canRead()) {\n const ch = source.readCh();\n len++;\n\n if (ch === \"'\" && !inDouble) {\n inSingle = !inSingle;\n } else if (ch === '\"' && !inSingle) {\n inDouble = !inDouble;\n } else if (!inSingle && !inDouble) {\n if (ch === '>') {\n return { selfClosing: false, attrText: source.readStr(len, start) };\n }\n if (ch === '/' && source.canRead() && source.readChAt(0) === '>') {\n source.readCh(); // consume '>'\n len++;\n return { selfClosing: true, attrText: source.readStr(len, start) };\n }\n }\n }\n\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end inside tag`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n\n /**\n * After reading a closing tag name, read optional whitespace and the `>`\n * returning them as a raw string (e.g. `' >'` or `'>'`).\n * Preserves original spacing when reconstructing inner closing tags.\n */\n _readToAngleClose(source) {\n const start = source.startIndex;\n let len = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n len++;\n if (ch === '>') return source.readStr(len, start);\n if (!isSpace(ch)) {\n throw new ParseError(\n `Malformed closing tag for </${this._tagName}>`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n }\n throw new ParseError(\n `Unclosed stop node <${this._tagName}> — unexpected end looking for '>'`,\n ErrorCode.UNEXPECTED_END,\n );\n }\n}\n","import { readPiExp, flushAttributes } from './XmlPartReader.js';\nimport { expectMatch, errorPositionOf, sanitizeContent } from './util.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\n\nexport function readCdata(parser) {\n // Level-1 inner mark: records where this reader began, used only by flush()\n // as a safe trim boundary. Does NOT overwrite the level-0 outer mark set by\n // parseXml()'s loop before it consumed '<![', which rewindToMark() restores to.\n parser.source.markTokenStart(1);\n\n //<![ already consumed up to this point\n expectMatch(parser.source, \"CDATA[\", \"CDATA preamble\");\n\n let text = parser.source.readUpto(\"]]>\");\n // Normalization is unconditional — applies even under xml:space=\"preserve\".\n text = sanitizeContent(text, parser.source);\n parser.outputBuilder.addLiteral(text);\n}\n\nexport function readPiTag(parser) {\n const skipOptions = parser.options.skip;\n parser.source.markTokenStart(1);\n //<? already consumed\n let tagExp = readPiExp(parser, \"?>\");\n if (!tagExp) {\n throw new ParseError(\n \"Invalid Pi Tag expression.\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n )\n } else if (tagExp.tagName === \"xml\") {\n // Read version from the declaration and store it on the parser for validators.\n const version = tagExp.rawAttributes?.version;\n if (version === '1.1') {\n parser.xmlDec.version = 1.1;\n }\n parser.xmlDec.encoding = tagExp.rawAttributes?.encoding;\n parser.xmlDec.standalone = tagExp.rawAttributes?.standalone;\n\n // BUG FIX: getNameValidator('qName') was already called (and memoized)\n // above the moment this PI tag's own name (\"xml\") got validated — before\n // xmlDec.version was known, so it was always cached with the '1.0'\n // default. Every subsequent tag/attribute name in the document —\n // including the root element — would silently be checked against XML\n // 1.0 rules even for a document declaring version=\"1.1\". Reset the\n // cache now that the real version is known; this runs at most once per\n // document (a <?xml?> declaration can only appear once), so the cost is\n // negligible.\n // parser._nameValidators = Object.create(null);\n parser._nameValidators = {};\n }\n\n // Flush attributes into the output builder's this.attributes accumulator\n // so addDeclaration() / addInstruction() pick them up, mirroring what readOpeningTag\n // does for regular tags. PI tags are not pushed onto the matcher, so no\n // updateCurrent() call is needed here.\n if (!skipOptions.attributes) {\n flushAttributes(tagExp._parsedAttrs, parser, tagExp._attrsExpStart, tagExp._rawAttrMatchCount);\n }\n\n if (tagExp.tagName === \"xml\") {//TODO: move it to above if condition\n //TODO: verify it is very first tag else error\n if (!skipOptions.declaration) { //TODO: unnecessary. builder can ommit it from response if not needed\n parser.outputBuilder.addDeclaration(\"?xml\", parser.xmlDec);\n }\n } else if (!skipOptions.pi) { //TODO: unnecessary. builder can ommit it from response if not needed\n parser.outputBuilder.addInstruction(\"?\" + tagExp.tagName); // TODO: send without '?'\n }\n}\n\nexport function readComment(parser) {\n parser.source.markTokenStart(1);\n //<!- already consumed\n expectMatch(parser.source, \"-\", \"comment second dash\");\n let text = parser.source.readUpto(\"-->\");\n text = sanitizeContent(text, parser.source);\n parser.outputBuilder.addComment(text);\n}","import { ParseError, ErrorCode } from './ParseError.js';\nimport { expectMatch, ensureCanRead, errorPositionOf, isSpace } from './util.js';\n\nexport function readDocType(parser) {\n parser.source.markTokenStart(1);\n\n // <!D are already consumed by the caller up to this point\n expectMatch(parser.source, \"OCTYPE\", \"DOCTYPE preamble\");\n\n // const entities = Object.create(null);\n const entities = {};\n let entityCount = 0;\n let hasBody = false;\n let bodyDone = false;\n\n while (parser.source.canRead()) {\n // Save a local snapshot of startIndex BEFORE consuming this character.\n // If the sub-tag dispatch below throws UNEXPECTED_END we restore here\n // and re-throw so that feed()'s catch calls rewindToMark(), which\n // restores all the way back to the '<' that began the DOCTYPE tag\n // (the level-0 mark set by parseXml's loop). We must NOT call\n // markTokenStart(0) here because that would overwrite parseXml's\n // level-0 mark and cause rewindToMark() to land at the wrong position.\n const subTagStart = parser.source.startIndex;\n\n let ch = parser.source.readCh();\n\n if (ch === '<' && hasBody && !bodyDone) {\n // ── \"<!…\" sub-tag inside [...] body ───────────────────────────────\n // If any read below hits a chunk boundary we restore to subTagStart\n // (the '<') and re-throw UNEXPECTED_END so the outer rewind via\n // rewindToMark() lands at parseXml's level-0 mark (the DOCTYPE '<').\n try {\n ensureCanRead(parser.source, 0, \"DOCTYPE sub-tag\");\n let bang = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n if (bang !== \"!\") throw new ParseError(\n `Invalid DOCTYPE body tag starting with \"<${bang}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n\n ensureCanRead(parser.source, 0, \"DOCTYPE sub-tag type\");\n let typeChar = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n\n if (typeChar === \"-\") {\n // <!-- comment -->\n ensureCanRead(parser.source, 0, \"DOCTYPE comment\");\n let dash2 = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n if (dash2 !== \"-\") throw new ParseError(\n \"Invalid comment in DOCTYPE\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n parser.source.readUpto(\"-->\");\n\n } else if (typeChar === \"E\") {\n // ENTITY or ELEMENT — one more char to distinguish\n ensureCanRead(parser.source, 0, \"DOCTYPE E-type sub-tag\");\n let typeChar2 = parser.source.readStr(1);\n parser.source.updateBufferBoundary(1);\n\n if (typeChar2 === \"N\") {\n // <!ENTITY — need 4 more chars for \"TITY\"\n expectMatch(parser.source, \"TITY\", \"DOCTYPE ENTITY keyword\");\n\n const [entityName, entityValue] = readEntityExp(parser);\n\n if (entityValue.indexOf(\"&\") === -1) {\n const ep = parser.options?.doctypeOptions;\n if (ep?.maxEntityCount && entityCount >= ep.maxEntityCount) {\n throw new ParseError(\n `Entity count (${entityCount + 1}) exceeds maximum allowed (${ep.maxEntityCount})`,\n ErrorCode.ENTITY_MAX_COUNT,\n errorPositionOf(parser.source)\n );\n }\n const escaped = entityName.replace(/[.\\-+*:]/g, '\\\\$&');\n entities[entityName] = {\n regx: RegExp(`&${escaped};`, \"g\"),\n val: entityValue\n };\n entityCount++;\n }\n\n } else if (typeChar2 === \"L\") {\n // <!ELEMENT — need 5 more chars for \"EMENT\"\n expectMatch(parser.source, \"EMENT\", \"DOCTYPE ELEMENT keyword\");\n readElementExp(parser);\n\n } else {\n throw new ParseError(\n `Invalid DOCTYPE sub-tag \"<!E${typeChar2}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n }\n\n } else if (typeChar === \"A\") {\n // <!ATTLIST — need 6 more chars for \"TTLIST\"\n expectMatch(parser.source, \"TTLIST\", \"DOCTYPE ATTLIST keyword\");\n readAttlistExp(parser);\n\n } else if (typeChar === \"N\") {\n // <!NOTATION — need 7 more chars for \"OTATION\"\n expectMatch(parser.source, \"OTATION\", \"DOCTYPE NOTATION keyword\");\n readNotationExp(parser);\n\n } else {\n throw new ParseError(\n `Invalid DOCTYPE sub-tag \"<!${typeChar}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(parser.source)\n );\n }\n\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n // Restore cursor to the '<' that started this sub-tag so\n // that when feed() calls rewindToMark() (which goes all the\n // way back to the DOCTYPE '<' via parseXml's level-0 mark)\n // the full DOCTYPE — including this sub-tag — is replayed.\n parser.source.startIndex = subTagStart;\n }\n // Always re-throw: UNEXPECTED_END bubbles up to feed() for rewind;\n // INVALID_TAG and others bubble up as real parse failures.\n throw err;\n }\n\n } else if (ch === '[') {\n hasBody = true;\n\n } else if (ch === ']') {\n bodyDone = true;\n\n } else if (ch === '>') {\n if (!hasBody || bodyDone) {\n return entities;\n }\n // '>' before '[' is part of the external identifier — skip it\n }\n // whitespace, external identifier text, public id text — all skipped\n }\n\n throw new ParseError(\n \"Unclosed DOCTYPE\",\n ErrorCode.UNEXPECTED_END,\n errorPositionOf(parser.source)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sub-expression readers\n// ---------------------------------------------------------------------------\n\n/**\n * Read an ENTITY declaration body.\n * \"<!ENTITY\" has already been consumed by the caller.\n *\n * All canRead() guards throw UNEXPECTED_END on chunk boundaries. The caller's\n * try/catch restores startIndex to the '<' of this sub-tag, then re-throws\n * so feed() → rewindToMark() resets all the way to the DOCTYPE opening '<'.\n *\n * @returns {[string, string]} [entityName, entityValue]\n */\nfunction readEntityExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"entity name\");\n\n const entityNameStart = source.startIndex;\n let entityNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch) || ch === '\"' || ch === \"'\") break;\n entityNameLen++;\n }\n const entityName = source.readStr(entityNameLen, entityNameStart);\n\n // Ran out mid-name without hitting a terminator — wait for more data\n ensureCanRead(source, 1, `entity name \"${entityName}\"`);\n\n validateEntityName(entityName, parser);\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 0, `after entity name \"${entityName}\"`);\n\n // SYSTEM check requires 6 chars; only peek when they are available\n if (source.canRead(5)) {\n if (source.matchAhead(\"system\", true) === true) {\n throw new ParseError(\"External entities are not supported\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n }\n\n if (source.readStr(1) === \"%\") {\n throw new ParseError(\"Parameter entities are not supported\",\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n\n // Need at least the opening quote char\n ensureCanRead(source, 0, `entity value for \"${entityName}\"`);\n\n const [entityValue] = readIdentifierVal(source, \"entity\");\n\n const ep = parser.options?.doctypeOptions;\n if (ep?.maxEntitySize && entityValue.length > ep.maxEntitySize) {\n throw new ParseError(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${ep.maxEntitySize})`,\n ErrorCode.ENTITY_MAX_SIZE,\n errorPositionOf(source)\n );\n }\n\n // readUpto throws UNEXPECTED_END automatically if \">\" is not in the buffer yet\n source.readUptoChar(\">\");\n\n return [entityName, entityValue];\n}\n\n/**\n * Read an ELEMENT declaration body.\n * \"<!ELEMENT\" has already been consumed by the caller.\n */\nfunction readElementExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n const elementNameStart = source.startIndex;\n let elementNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch)) break;\n elementNameLen++;\n }\n const elementName = source.readStr(elementNameLen, elementNameStart);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n if (!parser.getNameValidator('name')(elementName)) {\n throw new ParseError(`Invalid element name: \"${elementName}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source));\n }\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"ELEMENT name\");\n\n let peek1 = source.readStr(1);\n if (peek1 === \"E\") {\n // Use expectMatch for \"EMPTY\"\n try {\n expectMatch(source, \"EMPTY\", \"ELEMENT content model keyword EMPTY\");\n } catch (e) {\n // If not EMPTY, it might be something else – we fall back to skipping until '>'\n source.readUptoChar(\">\");\n return { elementName, contentModel: \"\" };\n }\n } else if (peek1 === \"A\") {\n try {\n expectMatch(source, \"ANY\", \"ELEMENT content model keyword ANY\");\n } catch (e) {\n source.readUptoChar(\">\");\n return { elementName, contentModel: \"\" };\n }\n } else if (peek1 === \"(\") {\n source.updateBufferBoundary(1);\n source.readUptoChar(\")\");\n }\n\n source.readUptoChar(\">\");\n return { elementName };\n}\n\n/**\n * Read an ATTLIST declaration body.\n * \"<!ATTLIST\" has already been consumed by the caller.\n */\nfunction readAttlistExp(parser) {\n parser.source.readUptoChar(\">\");\n}\n\n/**\n * Read a NOTATION declaration body.\n * \"<!NOTATION\" has already been consumed by the caller.\n */\nfunction readNotationExp(parser) {\n const source = parser.source;\n\n skipSourceWhitespace(source);\n\n ensureCanRead(source, 1, \"NOTATION name\");\n\n const notationNameStart = source.startIndex;\n let notationNameLen = 0;\n while (source.canRead()) {\n const ch = source.readCh();\n if (isSpace(ch)) break;\n notationNameLen++;\n }\n const notationName = source.readStr(notationNameLen, notationNameStart);\n\n ensureCanRead(source, 1, `after NOTATION name \"${notationName}\"`);\n\n validateEntityName(notationName, parser);\n skipSourceWhitespace(source);\n\n // Need all 6 chars of \"SYSTEM\" / \"PUBLIC\" before we can classify\n ensureCanRead(source, 6, \"NOTATION identifier type\");\n\n if (source.matchAhead(\"system\", true) === true) {\n source.updateBufferBoundary(6);\n skipSourceWhitespace(source);\n readIdentifierVal(source, \"systemIdentifier\");\n } else if (source.matchAhead(\"public\", true) === true) {\n source.updateBufferBoundary(6);\n skipSourceWhitespace(source);\n readIdentifierVal(source, \"publicIdentifier\");\n skipSourceWhitespace(source);\n ensureCanRead(source, 1, \"after NOTATION PUBLIC identifier\");\n let next = source.readStr(1);\n if (next === '\"' || next === \"'\") {\n readIdentifierVal(source, \"systemIdentifier\");\n }\n } else {\n throw new ParseError(\n `Expected SYSTEM or PUBLIC in NOTATION, found \"${source.readStr(6)}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n\n source.readUptoChar(\">\");\n}\n\n/**\n * Read a quoted identifier value from the source.\n * Consumes the opening quote, the content, and the closing quote.\n * @returns {[string]} [value]\n */\nfunction readIdentifierVal(source, type) {\n ensureCanRead(source, 1, type + \" opening quote\")\n let startChar = source.readStr(1);\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new ParseError(\n `Expected quoted string for ${type}, found \"${startChar}\"`,\n ErrorCode.INVALID_TAG,\n errorPositionOf(source)\n );\n }\n source.updateBufferBoundary(1);\n // readUpto throws UNEXPECTED_END automatically when the closing quote is absent\n let value = source.readUptoChar(startChar);\n return [value];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction skipSourceWhitespace(source) {\n while (source.canRead()) {\n const ch = source.readChAt(0);\n if (!isSpace(ch)) break;\n source.updateBufferBoundary(1)\n }\n}\n\nfunction validateEntityName(name, parser) {\n if (parser.getNameValidator('name')(name)) return name;\n throw new ParseError(\n `Invalid entity name \"${name}\"`,\n ErrorCode.ENTITY_INVALID_KEY,\n {}\n );\n}\n","import { ParseError, ErrorCode } from './ParseError.js';\nimport { absolutePosition } from './util.js';\n\n/**\n * AutoCloseHandler\n *\n * Handles two distinct failure modes that arise when XML is malformed\n * or a data stream is interrupted:\n *\n * 1. EOF with open tags — `onEof` option\n * 2. Mismatched close tag — `onMismatch` option\n *\n * The handler is stateless; it receives the parser's live state on each\n * call and mutates it directly (matching how the parser normally works).\n */\n\n/**\n * Error types returned by getParseErrors() when `collectErrors` is true.\n * @enum {string}\n */\nexport const AutoCloseErrorType = Object.freeze({\n /** A tag was still open when the document ended. */\n UNCLOSED_EOF: 'unclosed-eof',\n\n /**\n * A closing tag didn't match the current open tag.\n * The handler popped up the stack to find the nearest match.\n */\n MISMATCHED_CLOSE: 'mismatched-close',\n\n /**\n * A closing tag appeared whose opener doesn't exist anywhere in the stack.\n * The tag is discarded.\n */\n PHANTOM_CLOSE: 'phantom-close',\n\n /**\n * The source ended mid-way through a tag — e.g. `<div><p` or `</di`.\n * The partial tag is discarded; any already-open tags are closed by handleEof.\n */\n PARTIAL_TAG: 'partial-tag',\n});\n\nexport default class AutoCloseHandler {\n /**\n * @param {object} autoCloseOptions - Resolved autoClose options\n * @param {string} autoCloseOptions.onEof - 'throw' | 'closeAll'\n * @param {string} autoCloseOptions.onMismatch - 'throw' | 'recover' | 'discard'\n * @param {boolean} autoCloseOptions.collectErrors\n */\n constructor(autoCloseOptions) {\n this.onEof = autoCloseOptions.onEof || 'throw';\n this.onMismatch = autoCloseOptions.onMismatch || 'throw';\n this.collectErrors = autoCloseOptions.collectErrors || false;\n this.errors = [];\n }\n\n /**\n * Called at end-of-document when `tagsStack` is non-empty.\n *\n * @param {object} parserState\n * @param {Array} parserState.tagsStack - Parser's open-tag stack\n * @param {object} parserState.currentTagDetail - The currently open TagDetail\n * @param {object} parserState.outputBuilder - Live OutputBuilder instance\n * @param {object} parserState.readonlyMatcher - Read-only Matcher proxy\n * @param {object} parserState.source - Current InputSource (for position)\n * @param {Function} parserState.addTextNode - Bound addTextNode on the parser\n */\n handleEof(parserState) {\n if (this.onEof === 'throw') {\n throw new ParseError('Unexpected data in the end of document', ErrorCode.UNEXPECTED_TRAILING_DATA);\n }\n\n // onEof === 'closeAll'\n // Close from innermost outward using the parser's canonical popTag(),\n // which keeps the parser stack and output builder in sync automatically.\n\n const { addTextNode, popTag } = parserState;\n\n let current = parserState.currentTagDetail;\n\n while (current && !current.root) {\n this._recordError(AutoCloseErrorType.UNCLOSED_EOF, {\n tag: current.name,\n expected: null,\n index: current.index,\n });\n\n addTextNode();\n popTag();\n\n // popTag() already updated currentTagDetail via tagsStack.pop()\n current = parserState.currentTagDetail;\n }\n }\n\n /**\n * Called when a closing tag name doesn't match `currentTagDetail.name`.\n *\n * Returns an object describing what the caller should do:\n * { action: 'close-matched' } — handler already closed intermediates;\n * caller should now close the matched tag normally\n * { action: 'discard' } — caller should skip this closing tag entirely\n *\n * @param {string} closingTagName - The mismatched closing tag we just read\n * @param {object} parserState - Same shape as handleEof\n * @returns {{ action: string }}\n */\n handleMismatch(closingTagName, parserState) {\n const { tagsStack, currentTagDetail, source, addTextNode } = parserState;\n\n if (this.onMismatch === 'throw') {\n throw new ParseError(\n `Unexpected closing tag '${closingTagName}' expecting '${currentTagDetail.name}'`,\n ErrorCode.MISMATCHED_CLOSE_TAG,\n { index: source ? absolutePosition(source) : undefined }\n );\n }\n\n if (this.onMismatch === 'discard') {\n this._recordError(AutoCloseErrorType.MISMATCHED_CLOSE, {\n tag: closingTagName,\n expected: currentTagDetail.name,\n index: source ? absolutePosition(source) : null,\n });\n return { action: 'discard' };\n }\n\n // onMismatch === 'recover'\n // Scan the stack (top → bottom) for the closest matching opener.\n // tagsStack holds ancestors with index 0 = root, last = parent of current.\n // currentTagDetail is the open tag at the top that didn't match.\n\n // Build a unified view: [root...ancestors, current] — we check current first\n // (it's the top), then walk down toward the root.\n const stackSnapshot = [...tagsStack, currentTagDetail];\n\n let matchIndex = -1;\n const stackSnapshotLength = stackSnapshot.length;\n for (let i = stackSnapshotLength - 1; i >= 0; i--) {\n if (stackSnapshot[i].name === closingTagName) {\n matchIndex = i;\n break;\n }\n }\n\n if (matchIndex === -1) {\n // No match anywhere — phantom closing tag\n this._recordError(AutoCloseErrorType.PHANTOM_CLOSE, {\n tag: closingTagName,\n expected: currentTagDetail.name,\n index: source ? absolutePosition(source) : null,\n });\n return { action: 'discard' };\n }\n\n // Close everything above the match (innermost first), then signal the\n // caller to close the matched tag itself in the normal path.\n const levelsToClose = stackSnapshotLength - 1 - matchIndex;\n\n for (let i = 0; i < levelsToClose; i++) {\n const tag = stackSnapshot[stackSnapshotLength - 1 - i];\n\n this._recordError(AutoCloseErrorType.MISMATCHED_CLOSE, {\n tag: tag.name,\n expected: closingTagName,\n index: tag.index,\n });\n\n addTextNode();\n parserState.popTag();\n }\n\n // Update currentTagDetail to the matched one so the normal close path works.\n // popTag() has already walked the stack up by levelsToClose steps; the next\n // currentTagDetail is the one we want to match against.\n parserState.currentTagDetail = stackSnapshot[matchIndex];\n\n return { action: 'close-matched' };\n }\n\n /**\n * Called when the source ended mid-way through a tag token.\n * Records the partial-tag error and delegates remaining open tags to handleEof.\n *\n * @param {Error} originalError - The error thrown by the read function\n * @param {object} parserState - Same shape as handleEof\n */\n handlePartialTag(originalError, parserState) {\n this._recordError(AutoCloseErrorType.PARTIAL_TAG, {\n tag: _extractPartialTagName(originalError),\n expected: null,\n index: parserState.source ? absolutePosition(parserState.source) : null,\n });\n\n // Discard any partially-accumulated text from the broken tag\n parserState.tagTextData = '';\n\n // Close whatever was legitimately open before this truncation\n this.handleEof(parserState);\n }\n\n /**\n * Return a copy of the collected error list.\n * Empty array when collectErrors is false or no errors occurred.\n * @returns {Array}\n */\n getErrors() {\n return this.errors.slice();\n }\n\n /**\n * Reset error log (useful if the same handler instance is reused).\n */\n reset() {\n this.errors = [];\n }\n\n // ── Private ──────────────────────────────────────────────────────────────\n\n _recordError(type, detail) {\n if (!this.collectErrors) return;\n this.errors.push({ type, ...detail });\n }\n}\n\n/**\n * Best-effort extraction of a partial tag name from a source-exhausted error.\n * Accepts the full error object so it can inspect both message and code.\n *\n * ParseError from readClosingTagName (new format):\n * message: \"Unexpected end of source reading closing tag '</di'\"\n *\n * Legacy plain Error (old format, kept for safety):\n * message: \"Unexpected end of source. Reading closing tag '</di'\"\n *\n * ParseError from readTagExp / readPiExp — opening tag truncated before '>':\n * No tag name is embedded; returns null.\n */\nfunction _extractPartialTagName(err) {\n if (!err) return null;\n const message = typeof err.message === 'string' ? err.message : String(err);\n // Match both \"reading closing tag\" (new, lowercase) and\n // \"Reading closing tag\" (old, capitalised, period-separated)\n const closeMatch = message.match(/[Rr]eading closing tag '<\\/([^']*)/);\n if (closeMatch) return closeMatch[1] || null;\n return null;\n}","import StringSource from './InputSource/StringSource.js';\nimport BufferSource from './InputSource/BufferSource.js';\nimport { buildProfileForBuffer } from './Encoding/EncodingProfile.js';\nimport { errorPositionOf, absolutePosition, sanitizeContent } from './util.js';\nimport { readTagExp, readClosingTagName, flushAttributes, tryMatchClosingTagName } from './XmlPartReader.js';\nimport { StopNodeProcessor } from './StopNodeProcessor.js';\nimport { readComment, readCdata, readPiTag } from './XmlSpecialTagsReader.js';\nimport { Expression, ExpressionSet, Matcher } from 'path-expression-matcher';\nimport { readDocType } from './DocTypeReader.js';\nimport { DANGEROUS_PROPERTY_NAMES, criticalProperties } from './util.js';\nimport AutoCloseHandler from './AutoCloseHandler.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { createValidator } from 'xml-naming';\n\n// Cap on the tag-name and attribute-name caches (each capped independently —\n// see SAVEPOINT_name_cache.md for why: real documents have a small closed\n// vocabulary of names, so this is a not-unbounded-growth guard for a\n// pathological one-off document, not a security control (that's what\n// limits.maxAttributesPerTag/maxNestedTags are for). Not user-configurable —\n// an internal implementation detail, not a knob most users could tune\n// meaningfully. On overflow the whole map is cleared rather than evicting\n// individual entries (LRU) — simpler, and this ceiling is rare in practice.\nconst NAME_CACHE_LIMIT = 2000;\nconst keepSpace = { keep: [\"xml:space\"] }\n/**\n * Returns the cached result of computeFn(rawName) if present, otherwise\n * calls computeFn(), caches the result, and returns it.\n *\n * If computeFn() throws (invalid name, restricted name, prototype-pollution\n * attempt, etc.) nothing is cached — the next occurrence of the same bad\n * name re-runs the full check and throws again from scratch. These are rare\n * error paths where correctness matters, not speed.\n *\n * A plain Map is used (not a plain object) so a legitimately falsy cached\n * value (resolveNsPrefix returns `false` for a dropped xmlns declaration)\n * is never confused with \"not cached\" — Map.get() only returns undefined\n * when the key is genuinely absent.\n */\nfunction getCachedName(cache, rawName, computeFn) {\n const cached = cache.get(rawName);\n if (cached !== undefined || cache.has(rawName)) return cached;\n const result = computeFn();\n if (cache.size >= NAME_CACHE_LIMIT) cache.clear();\n cache.set(rawName, result);\n return result;\n}\n\nclass TagDetail {\n /**\n * @param {string} name - Tag name\n * @param {number} index - Character offset of '<' from document start\n * @param {number} [openEnd] - Character offset immediately after the opening\n * tag's closing '>' (i.e. end of `<tag attr=\"x\">`). Undefined until the\n * opening tag expression has been fully read; set in readOpeningTag().\n * For self-closing tags this is the offset after '/>'.\n * @param {string} [rawName] - the tag name exactly as written (before\n * namespace-prefix stripping) — what a matching closing tag will\n * literally contain. Used by readClosingTag()'s fast path; undefined for\n * the synthetic root node.\n */\n constructor(name, index = 0, openEnd = undefined, rawName = undefined) {\n this.name = name;\n this.index = index;\n this.openEnd = openEnd;\n this.rawName = rawName;\n }\n}\n\nexport default class Xml2JsParser {\n constructor(options) {\n this.options = options;\n\n this.currentTagDetail = null;\n this.tagTextData = \"\";\n this.tagsStack = [];\n this.doctypeFound = false;\n this.matcher = new Matcher();\n\n //create once and reuse\n this.readonlyMatcher = this.matcher.readOnly();\n\n // AutoClose handler — created once per parser instance, reset on each parse\n this.autoCloseHandler = options.autoClose\n ? new AutoCloseHandler(options.autoClose)\n : null;\n\n this._unpairedSet = new Set(this.options.tags.unpaired);\n\n // Reuse the sealed ExpressionSets built by OptionsBuilder.\n // Each Expression carries its config ({ nested, skipEnclosures }) in .data.\n // findMatch() returns the matched Expression directly — O(1) indexed lookup.\n this.stopNodeExpressionsSet = this.options.tags.stopNodesSet ?? new ExpressionSet();\n if (this.stopNodeExpressionsSet.size === 0) this.isStopNode = () => false;\n else this.isStopNode = isStopNode;\n this.skipTagExpressionsSet = this.options.skip.tagsSet ?? new ExpressionSet();\n if (this.skipTagExpressionsSet.size === 0) this.isSkipTag = () => false;\n else this.isSkipTag = isSkipTag;\n\n // exitIf: optional predicate called after each opening tag is pushed.\n // Stored directly — it's a plain function, not an ExpressionSet.\n this._exitIf = typeof options.exitIf === 'function' ? options.exitIf : () => false;\n\n // Tag/attribute name cache — read from `options`, not created fresh here.\n // `XMLParser` creates a brand-new Xml2JsParser on every parse() call but\n // passes the *same* options object each time, so a cache stored on\n // `options` survives across those calls. Deliberately NOT reset in\n // initializeParser() (unlike _nameValidators) — it depends only on the\n // raw name string plus options that are fixed for this instance's\n // lifetime, never on anything document-specific like xmlVersion.\n // Guarded fallback here in case Xml2JsParser is ever constructed\n // directly (e.g. in tests) without going through XMLParser.\n this._nameCache = options._nameCache || (options._nameCache = { tags: new Map(), attrs: new Map() });\n }\n\n initializeParser() {\n this.tagTextData = \"\";\n this.tagsStack = [];\n this.doctypeFound = false;\n this._stopNodeProcessor = null;\n this._exitIfTriggered = false;\n // Lazily-built, memoized xml-naming validators (v0.3.0 createValidator).\n // Lazy because xmlDec.version isn't final until the optional <?xml?>\n // declaration (if any) has been read — which happens after this method\n // runs but before any tag name is ever validated. Reset here (once per\n // document/session, see XMLParser._createParser / feed() call sites) so\n // a reused Xml2JsParser instance never validates against a stale\n // xmlVersion or leaks one document's name cache into the next.\n // this._nameValidators = Object.create(null);\n this._nameValidators = {};\n // Validity (unlike sanitize/ns-resolution in this._nameCache) depends on\n // xmlVersion, which is itself document-scoped — reset alongside\n // _nameValidators for the same reason, not on `options` with the rest of\n // _nameCache. See isValidQName().\n this._validQNames = new Set();\n this.xmlDec = {\n version: 1.0,\n lang: null,\n encoding: null,\n standalone: \"yes\"\n }\n\n if (!this.matcher) {\n this.matcher = new Matcher();\n this.readonlyMatcher = this.matcher.readOnly();\n }\n\n this.outputBuilder = this._createOutputBuilder();\n\n this.root = { root: true, name: \"\" };\n this.currentTagDetail = this.root;\n }\n\n /**\n * Create an OutputBuilder instance for this parse run.\n * The output builder owns all value parser registration, including\n * EntitiesValueParser — no injection needed from the parser side.\n */\n _createOutputBuilder() {\n return this.options.OutputBuilder.getInstance(this.options, this.readonlyMatcher);\n }\n\n /**\n * Returns true if the last parse call was terminated early by exitIf.\n * Useful when the caller needs to know whether parsing completed or stopped.\n */\n wasExited() {\n return this._exitIfTriggered === true;\n }\n\n parse(strData) {\n this.source = new StringSource(strData);\n this.initializeParser();\n this._parseAndFinalize();\n return this.outputBuilder.getOutput();\n }\n\n parseBytesArr(data) {\n const registry = this.options.decoding?._registry;\n const profile = buildProfileForBuffer(data, this.options.decoding, registry);\n this.source = new BufferSource(data, {}, profile);\n this.initializeParser();\n this._parseAndFinalize();\n return this.outputBuilder.getOutput();\n }\n\n /**\n * Advance the parser state machine as far as the source buffer allows.\n * Stops naturally when canRead() returns false — no EOF handling here.\n * Call finalizeXml() once all input is consumed to validate end-of-document.\n *\n * parseStream() and feed()/end() call this per chunk; _parseAndFinalize()\n * (used by parse() / parseBytesArr()) calls it then finalizeXml() immediately.\n */\n parseXml() {\n while (this.source.canRead()) {\n // exitIf triggered in this iteration — stop consuming input immediately.\n if (this._exitIfTriggered) break;\n\n // Level-0 outer mark: set before consuming any character so that if a\n // '<' dispatch throws UNEXPECTED_END (chunk boundary mid-tag), feed()\n // rewinds to here and the full token — including '<', '![', '</' etc. —\n // is re-read on the next chunk. Inner reader functions use level-1 marks\n // which never overwrite this position.\n this.source.markTokenStart(0);\n\n // Position of the next character, captured before it's read. When that\n // character turns out to be '<', this is exactly the position of '<'\n // itself — used below as the authoritative tag-start position for both\n // TagDetail (open tags) and closeMeta (close tags), instead of deriving\n // it after the fact from source.startIndex once the tag name/attrs have\n // already been consumed (which points past the tag, not at its start).\n const preReadPos = errorPositionOf(this.source);\n\n const ch = this.source.readCh();\n if (ch === undefined || ch === '') break;\n\n if (ch === '<') {\n const tagStart = preReadPos;\n\n const nextChar = this.source.readChAt(0);\n if (nextChar === '') throw new ParseError(\n \"Unexpected end of source after '<'\", ErrorCode.UNEXPECTED_END, errorPositionOf(this.source)\n );\n\n //sorted frequency wise\n if (nextChar === '/') {\n this.source.updateBufferBoundary();\n this.readClosingTag(tagStart);\n } else if (nextChar === '!') {\n this.source.updateBufferBoundary();\n this.addTextNode();\n this.readSpecialTag(nextChar);\n } else if (nextChar === '?') {\n this.source.updateBufferBoundary();\n this.addTextNode();\n readPiTag(this);\n } else {\n this.readOpeningTag(tagStart);\n }\n } else {\n // ch is already consumed. Peek ahead for more non-'<' chars and grab\n // the whole run in one readStr call rather than concatenating one char\n // at a time through every loop iteration.\n let runLen = 0;\n while (true) {\n const c = this.source.readChAt(runLen);\n if (c === '<' || c === undefined || c === '') break;\n runLen++;\n }\n if (runLen > 0) {\n this.tagTextData += ch + this.source.readStr(runLen, this.source.startIndex);\n this.source.updateBufferBoundary(runLen);\n } else {\n this.tagTextData += ch;\n }\n\n //TODO: why does below code doesn't work\n // const text = this.source.readUptoChar(\"<\");\n // this.tagTextData += text;\n }\n }\n }\n\n /**\n * Validate end-of-document state and apply autoClose recovery if configured.\n * Must be called exactly once after all input has been consumed.\n */\n finalizeXml() {\n // When exitIf fired, the parser already closed all open tags and notified\n // the builder — treat the partial parse as complete and skip EOF checks.\n if (this._exitIfTriggered) return;\n\n const hasOpenTags = this.tagsStack.length > 0 ||\n (this.currentTagDetail && !this.currentTagDetail.root);\n\n const hasTrailingText =\n !hasOpenTags &&\n this.tagTextData !== undefined &&\n this.tagTextData.trimEnd().length > 0;\n\n if (hasOpenTags || hasTrailingText) {\n if (this.autoCloseHandler && hasOpenTags && !hasTrailingText) {\n this.autoCloseHandler.handleEof(this._parserState());\n } else {\n throw new ParseError('Unexpected data in the end of document', ErrorCode.UNEXPECTED_TRAILING_DATA, errorPositionOf(this.source));\n }\n }\n }\n\n /**\n * One-shot helper used by parse() and parseBytesArr().\n * Runs parseXml() with autoClose partial-tag recovery, then finalizeXml().\n * @private\n */\n _parseAndFinalize() {\n let partialTagError = null;\n if (this.autoCloseHandler) this.autoCloseHandler.reset();\n\n try {\n this.parseXml();\n } catch (err) {\n if (this.autoCloseHandler && isSourceExhaustedError(err)) {\n partialTagError = err;\n } else {\n throw err;\n }\n }\n\n if (partialTagError) {\n this.autoCloseHandler.handlePartialTag(partialTagError, this._parserState());\n return;\n }\n\n this.finalizeXml();\n }\n\n readClosingTag(tagStart) {\n // ── Fast path ────────────────────────────────────────────────────────────\n // The overwhelming majority of closing tags match the tag already sitting\n // on top of the stack. Try that directly, character-by-character, before\n // reading the name into a fresh string and re-validating/re-sanitizing a\n // name we've already validated once (see\n // SAVEPOINT_closing_tag_and_double_scan.md). Peek-only — a mismatch or a\n // buffer that runs out mid-check costs nothing to abandon, so any failure\n // here falls straight through to the exact original slow path below.\n //\n // Skipping isUnpaired()/isStopNode() on this path is safe by construction,\n // not just by observation: only pushTag() ever sets currentTagDetail, and\n // pushTag() is never reached for an unpaired tag or a stop-node/skip-tag\n // match (both close themselves inline in readOpeningTag) — so whenever\n // currentTagDetail is a real open tag, both checks are guaranteed false.\n const current = this.currentTagDetail;\n if (current && !current.root && current.rawName !== undefined) {\n const consumed = tryMatchClosingTagName(this.source, current.rawName);\n if (consumed !== -1) {\n this.source.updateBufferBoundary(consumed);\n const closeMeta = {\n name: current.name,\n index: tagStart.index,\n closeEnd: absolutePosition(this.source),\n };\n this.addTextNode();\n this.popTag(closeMeta);\n return;\n }\n }\n\n // ── Slow path — unchanged ────────────────────────────────────────────────\n const tagName = this.processTagName(readClosingTagName(this.source));\n // closeMeta: position of this closing tag's '</' (tagStart, passed in from\n // parseXml's dispatch) plus the offset right after its '>' (closeEnd) —\n // mirrors tagDetail.index / tagDetail.openEnd for the opening-tag side.\n const closeMeta = {\n name: tagName,\n index: tagStart.index,\n closeEnd: absolutePosition(this.source),\n };\n\n if (this.isUnpaired(tagName) || this.isStopNode()) {\n throw new ParseError(`Unexpected closing tag '${tagName}'`, ErrorCode.UNEXPECTED_CLOSE_TAG, errorPositionOf(this.source));\n }\n\n if (tagName !== this.currentTagDetail.name) {\n if (!this.autoCloseHandler) {\n throw new ParseError(\n `Unexpected closing tag '${tagName}' expecting '${this.currentTagDetail.name}'`,\n ErrorCode.MISMATCHED_CLOSE_TAG,\n errorPositionOf(this.source)\n );\n }\n\n const decision = this.autoCloseHandler.handleMismatch(tagName, this._parserState());\n\n if (decision.action === 'discard') return;\n // 'close-matched': handler updated currentTagDetail; fall through to normal close\n }\n\n if (!this.currentTagDetail.root) this.addTextNode();\n this.popTag(closeMeta);\n }\n\n readOpeningTag(tagStart) {\n const options = this.options;\n this.addTextNode();\n\n // ── Stop-node resume ─────────────────────────────────────────────────────\n // When a chunk boundary fell inside StopNodeProcessor.collect(), feed() caught\n // UNEXPECTED_END and rewound the source to the '<' of the stop node's\n // opening tag. On the next feed() we re-enter here with the processor active.\n // Re-consume the opening tag (source was rewound to its '<'), then resume\n // collection — the processor remembers all accumulated content and depth.\n if (this._stopNodeProcessor && this._stopNodeProcessor.isActive()) {\n const { tagDetail, isSkip } = this._stopNodeProcessorMeta;\n this._stopNodeProcessor.resumeAfterOpenTag();\n readTagExp(this); // re-consume the opening tag from the rewound source\n // openEnd reflects the offset right after this opening tag's '>' — stable\n // across retries since the opening tag is fully re-read every time.\n tagDetail.openEnd = absolutePosition(this.source);\n const { content, end: stopEnd } = this._stopNodeProcessor.collect(this.source);\n if (!isSkip) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.outputBuilder.onStopNode?.(tagDetail, content, this.readonlyMatcher, stopEnd);\n this.outputBuilder.addValue(content, this.readonlyMatcher);\n this.outputBuilder.closeElement(this.readonlyMatcher, { name: tagDetail.name, closeEnd: stopEnd.index });\n }\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n return;\n }\n\n let tagExp = readTagExp(this);\n const processedTagName = this.processTagName(tagExp.tagName);\n const tagDetail = new TagDetail(\n processedTagName,\n tagStart.index,\n absolutePosition(this.source), // openEnd: offset right after this opening tag's '>'\n tagExp.tagName, // rawName: exactly as written, for readClosingTag()'s fast path\n );\n\n // Extract namespace prefix and local name from raw tag name (e.g. \"ns:tag\" → \"ns\", \"tag\").\n // Always done from the raw name (tagExp.tagName), before processTagName strips the prefix,\n // so these values are stable regardless of skip.nsPrefix.\n const colonIdx = tagExp.tagName.indexOf(':');\n const tagNamespace = colonIdx !== -1 ? tagExp.tagName.slice(0, colonIdx) : undefined;\n // Local name for the matcher: prefix-free always (e.g. \"code\" from \"ns:code\").\n // The matcher library tracks namespace separately via the 3rd push() argument —\n // passing the full \"ns:code\" as the tag name would break ns::code expression matching.\n const matcherTagName = tagNamespace !== undefined\n ? tagExp.tagName.slice(colonIdx + 1)\n : processedTagName;\n\n // ── Limit: maxNestedTags ─────────────────────────────────────────────────\n const maxNested = options.limits?.maxNestedTags;\n if (maxNested !== undefined && maxNested !== null) {\n const depth = this.tagsStack.length + 1;\n if (depth > maxNested) {\n throw new ParseError(\n `Nesting depth ${depth} exceeds limit of ${maxNested} (tag: '${processedTagName}')`,\n ErrorCode.LIMIT_MAX_NESTED_TAGS,\n { index: tagDetail.index }\n );\n }\n }\n\n // ── Two-pass attribute handling ──────────────────────────────────────────\n let rawAttributes = {};\n let raeAttrLen = 0;\n if (tagExp.rawAttributes) {\n rawAttributes = tagExp.rawAttributes;\n raeAttrLen = tagExp.rawAttributesLen;\n }\n\n if (raeAttrLen > 0) {\n this.matcher.push(matcherTagName, rawAttributes, tagNamespace, keepSpace);\n // this.matcher.updateCurrent(rawAttributes);\n } else {\n this.matcher.push(matcherTagName, {}, tagNamespace);\n\n }\n\n // Resolve skip/stop BEFORE touching the output builder\n const stopNodeConfig = this.isStopNode();\n const skipTagConfig = stopNodeConfig ? null : this.isSkipTag();\n\n if (!options.skip.attributes && !skipTagConfig) {\n flushAttributes(tagExp._parsedAttrs, this, tagExp._attrsExpStart, tagExp._rawAttrMatchCount);\n }\n\n // Stop-node and skip-tag checks AFTER attributes are set so attribute conditions work.\n // const stopNodeConfig = this.isStopNode();\n // Skip tag is only checked when this tag is not already a stop node — they are mutually exclusive.\n // const skipTagConfig = stopNodeConfig ? null : this.isSkipTag();\n\n if (this.isUnpaired(processedTagName)) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n // Unpaired tags (e.g. <br>, <img>) have no separate closing tag — the\n // close position is the same as the open tag's end.\n this.outputBuilder.closeElement(this.readonlyMatcher, this._closeMetaFor(tagDetail));\n this.matcher.pop();\n } else if (tagExp.selfClosing) {\n if (!skipTagConfig) {\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n // Self-closing tags (<tag/>) likewise have no distinct closing tag.\n this.outputBuilder.closeElement(this.readonlyMatcher, this._closeMetaFor(tagDetail));\n }\n this.matcher.pop();\n } else if (stopNodeConfig) {\n // Create a fresh processor with the matching nested + skipEnclosures config.\n // Raw tag name (tagExp.tagName) is used — the processor scans the source\n // character-by-character and must match the prefix-as-written (e.g. \"ns:code\"),\n // independent of what skip.nsPrefix does to the processed output name.\n this._stopNodeProcessor = new StopNodeProcessor(tagExp.tagName, {\n nested: stopNodeConfig.nested,\n skipEnclosures: stopNodeConfig.skipEnclosures,\n });\n this._stopNodeProcessorMeta = { tagDetail, isSkip: false };\n this._stopNodeProcessor.activate();\n const { content, end: stopEnd } = this._stopNodeProcessor.collect(this.source);\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.outputBuilder.onStopNode?.(tagDetail, content, this.readonlyMatcher, stopEnd);\n this.outputBuilder.addValue(content, this.readonlyMatcher);\n // closeMeta for a stop node carries only `closeEnd` (offset right after\n // the matched </tagname> was consumed) — StopNodeProcessor scans the\n // closing tag opaquely and doesn't track where '</tagname' itself starts,\n // so unlike the normal close path we don't have a real index\n // for the close tag's own start, only its end.\n this.outputBuilder.closeElement(this.readonlyMatcher, { name: tagDetail.name, closeEnd: stopEnd.index });\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n } else if (skipTagConfig) {\n // Skip tag: collect raw content (to advance the source past the closing tag)\n // but call no output builder methods — the tag is silently dropped.\n // Raw tag name used for the same reason as the stop-node branch above.\n this._stopNodeProcessor = new StopNodeProcessor(tagExp.tagName, {\n nested: skipTagConfig.nested,\n skipEnclosures: skipTagConfig.skipEnclosures,\n });\n this._stopNodeProcessorMeta = { tagDetail, isSkip: true };\n this._stopNodeProcessor.activate();\n this._stopNodeProcessor.collect(this.source); // advance source; content discarded\n this.matcher.pop();\n this._stopNodeProcessor = null;\n this._stopNodeProcessorMeta = null;\n } else if (this._exitIf(this.readonlyMatcher)) {\n // ── exitIf ───────────────────────────────────────────────────────────────\n // Checked BEFORE addElement so the triggering tag is never added to the\n // output builder. The matcher is already positioned (push + updateCurrent\n // above), so attribute-based predicates work correctly.\n //\n // We pop the matcher entry for this tag (it was never added to the builder),\n // then close all already-open ancestors so the builder can finalise its tree.\n\n const exitDepth = this.tagsStack.length; // number of ancestors open before this tag\n this.matcher.pop(); // undo the push for the triggering tag\n\n while (this.currentTagDetail && !this.currentTagDetail.root) {\n this.addTextNode();\n this.popTag();\n }\n\n // Notify the output builder that parsing was intentionally truncated.\n if (typeof this.outputBuilder.onExit === 'function') {\n this.outputBuilder.onExit({\n tagDetail,\n matcher: this.readonlyMatcher,\n depth: exitDepth,\n });\n }\n\n this._exitIfTriggered = true;\n } else {\n this.pushTag(tagDetail);\n }\n }\n\n /**\n * Push a tag onto the parser stack and notify the output builder.\n * This is the single point of entry for opening a non-self-closing tag —\n * both the parser-side stack (currentTagDetail / tagsStack) and the\n * output builder are updated together, keeping them in sync.\n *\n * Custom OutputBuilder implementations that maintain their own tag stack\n * should override addElement() rather than calling pushTag() directly.\n *\n * @param {TagDetail} tagDetail\n */\n pushTag(tagDetail) {\n this.tagsStack.push(this.currentTagDetail);\n this.outputBuilder.addElement(tagDetail, this.readonlyMatcher);\n this.currentTagDetail = tagDetail;\n }\n\n /**\n * Pop the current tag from the parser stack and notify the output builder.\n * This is the single point of exit for closing a tag — both stacks are\n * updated together.\n *\n * @param {object} [closeMeta] - Position info for the closing tag:\n * { name, index, closeEnd }. Omitted when there is no real\n * closing tag to report a position for — e.g. AutoCloseHandler synthesizing\n * a close at EOF, or exitIf closing already-open ancestors. In that case a\n * minimal `{ name }` is passed to the builder instead of nothing, so\n * closeElement() never has to special-case \"no second argument at all\".\n */\n popTag(closeMeta) {\n this.outputBuilder.closeElement(this.readonlyMatcher, closeMeta ?? { name: this.currentTagDetail?.name });\n this.matcher.pop();\n this.currentTagDetail = this.tagsStack.pop();\n }\n\n /**\n * Build a closeMeta object for tags with no distinct closing token\n * (unpaired tags like <br>, and self-closing tags like <tag/>) — the close\n * position is just the opening tag's own end.\n * @param {TagDetail} tagDetail\n */\n _closeMetaFor(tagDetail) {\n return {\n name: tagDetail.name,\n index: tagDetail.index,\n closeEnd: tagDetail.openEnd,\n };\n }\n\n readSpecialTag(startCh) {\n if (startCh === \"!\") {\n let nextChar = this.source.readCh();\n if (nextChar === null || nextChar === undefined) throw new ParseError(\"Unexpected end of source after '<!'\", ErrorCode.UNEXPECTED_END, errorPositionOf(this.source));\n\n if (nextChar === \"-\") {\n readComment(this);\n } else if (nextChar === \"[\") {\n readCdata(this);\n } else if (nextChar === \"D\") {\n // DOCTYPE is always read to consume its content and advance the cursor.\n // Entities are forwarded to the output builder only when doctypeOptions.enabled is true.\n const docTypeEntities = readDocType(this);\n if (this.doctypeFound) {\n throw new ParseError(\"Multiple DOCTYPE declarations found.\", ErrorCode.INVALID_INPUT, errorPositionOf(this.source));\n }\n this.doctypeFound = true;\n if (this.options.doctypeOptions.enabled &&\n docTypeEntities &&\n Object.keys(docTypeEntities).length > 0) {\n this.outputBuilder.addInputEntities(docTypeEntities);\n }\n }\n } else {\n throw new ParseError(`Invalid tag '<${startCh}'`, ErrorCode.INVALID_TAG, errorPositionOf(this.source));\n }\n }\n\n addTextNode() {\n if (this.tagTextData !== undefined && this.tagTextData !== \"\") {\n // Line-ending normalization + illegal-control-character rejection,\n // applied once per complete text run (never mid-chunk — see util.js).\n this.tagTextData = sanitizeContent(this.tagTextData, this.source);\n // Pass raw text — entity expansion is handled by 'entities' ValueParser in the chain\n if (!this.options.skip.whitespaceText || this.tagTextData.trim().length > 0) {\n this.outputBuilder.addValue(this.tagTextData, this.readonlyMatcher);\n }\n this.tagTextData = \"\";\n }\n }\n\n /**\n * Cached wrapper around getNameValidator('qName') — shape-validating a tag\n * name is a pure function of the name string alone (for a fixed xmlVersion,\n * itself fixed for the whole document), so a name seen once and found valid\n * never needs the regex re-run for its later occurrences. Only opening tag\n * names go through this (see buildTagExpObj in XmlPartReader.js); closing\n * tags are checked by exact-match against the already-validated opening\n * name instead (readClosingTag), so they never need shape validation of\n * their own.\n *\n * Only valid names are cached, matching processTagName/processAttrName's\n * existing convention — an invalid name throws every time it's seen, never\n * silently let through after a first failure.\n */\n isValidQName(name) {\n const cache = this._validQNames;\n if (cache.has(name)) return true;\n const ok = this.getNameValidator('qName')(name);\n if (ok) {\n if (cache.size >= NAME_CACHE_LIMIT) cache.clear();\n cache.add(name);\n }\n return ok;\n }\n\n /**\n * Returns a memoized xml-naming validator for the given production\n * ('qName' for tag/attribute names, 'name' for DOCTYPE entity/element/\n * notation names), built lazily on first use and cached per parser\n * instance for the rest of the document/session.\n *\n * xmlDec.version is stored as a number (1.0 / 1.1) but xml-naming's\n * xmlVersion option is the string '1.0'/'1.1' — normalized here rather\n * than changing xmlDec's public shape (it's forwarded as-is to\n * outputBuilder.addDeclaration(), so its type is part of the builder\n * contract, not just an internal detail).\n *\n * @param {'name'|'qName'} production\n */\n getNameValidator(production) {\n let validator = this._nameValidators[production];\n if (!validator) {\n const xmlVersion = this.xmlDec.version === 1.1 || this.xmlDec.version === '1.1' ? '1.1' : '1.0';\n validator = createValidator(production, { xmlVersion });\n this._nameValidators[production] = validator;\n }\n return validator;\n }\n\n processAttrName(rawAttrName) {\n return getCachedName(this._nameCache.attrs, rawAttrName, () => {\n const options = this.options;\n let attrName = resolveNsPrefix(rawAttrName, options.skip.nsPrefix);\n if (!this.getNameValidator('qName')(attrName)) { //TODO: make it optional\n throw new ParseError(`Invalid attribute name: ${attrName}`, ErrorCode.INVALID_ATTRIBUTE_NAME, errorPositionOf(this.source));\n }\n attrName = sanitizeName(attrName, options.onDangerousProperty, options.sanitizeNames, this.source);\n if (options.strictReservedNames && attrName === options.attributes.groupBy) {\n throw new ParseError(`Restricted attribute name: ${attrName}`, ErrorCode.SECURITY_RESTRICTED_NAME, errorPositionOf(this.source));\n }\n return attrName;\n });\n }\n\n processTagName(rawTagName) {\n return getCachedName(this._nameCache.tags, rawTagName, () => {\n const options = this.options;\n const nameFor = options.nameFor;\n let tagName = resolveNsPrefix(rawTagName, options.skip.nsPrefix);\n tagName = sanitizeName(tagName, options.onDangerousProperty, options.sanitizeNames, this.source);\n if (options.strictReservedNames && (\n tagName === nameFor.comment ||\n tagName === nameFor.cdata ||\n tagName === nameFor.text\n )) {\n throw new ParseError(`Restricted tag name: ${tagName}`, ErrorCode.SECURITY_RESTRICTED_NAME, errorPositionOf(this.source));\n }\n return tagName;\n });\n }\n\n isUnpaired(tagName) {\n return this._unpairedSet.has(tagName);\n }\n\n /**\n * Snapshot of mutable parser state passed to AutoCloseHandler.\n * Returns a live object — properties read from it reflect current state.\n */\n _parserState() {\n const self = this;\n return {\n get tagsStack() { return self.tagsStack; },\n get currentTagDetail() { return self.currentTagDetail; },\n set currentTagDetail(v) { self.currentTagDetail = v; },\n get outputBuilder() { return self.outputBuilder; },\n get readonlyMatcher() { return self.readonlyMatcher; },\n get matcher() { return self.matcher; },\n get source() { return self.source; },\n get tagTextData() { return self.tagTextData; },\n set tagTextData(v) { self.tagTextData = v; },\n addTextNode: self.addTextNode.bind(self),\n popTag: self.popTag.bind(self),\n };\n }\n}\n\nfunction resolveNsPrefix(name, skipNsPrefix) {\n if (skipNsPrefix) {\n const parts = name.split(':');\n if (parts.length === 2) {\n if (parts[0] === 'xmlns') return false; // drop xmlns declarations\n return parts[1];\n } else if (parts.length > 2) {\n throw new ParseError(`Multiple namespaces in name: ${name}`, ErrorCode.MULTIPLE_NAMESPACES, errorPositionOf(this.source));\n }\n }\n return name;\n}\n\nfunction sanitizeName(name, onDangerousProperty, sanitizeNames, source) {\n // criticalProperties (__proto__, constructor, prototype) guard against an\n // actual prototype-pollution vulnerability in the output object, not just\n // a naming collision — this check always runs, `sanitizeNames` cannot\n // turn it off. Only the DANGEROUS_PROPERTY_NAMES rename step (a milder,\n // cosmetic shadowing concern) is skippable for trusted input.\n if (criticalProperties.includes(name)) {\n throw new ParseError(`[SECURITY] Invalid name: \"${name}\" is a reserved JavaScript keyword that could cause prototype pollution`, ErrorCode.SECURITY_PROTOTYPE_POLLUTION, errorPositionOf(source));\n }\n if (sanitizeNames === false) return name;\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return onDangerousProperty(name);\n }\n return name;\n}\n\n/**\n * Returns true for errors thrown by read functions when the source ran out\n * mid-token — i.e. the document was truncated inside a tag.\n * These are the only errors we intercept for autoClose recovery.\n * Syntax errors (unclosed quotes) are NOT intercepted — they rethrow.\n */\nfunction isSourceExhaustedError(err) {\n // Accept both ParseError (with codes) and plain Error from lower-level readers\n if (err instanceof ParseError) {\n return err.code === ErrorCode.UNEXPECTED_END;\n }\n return (\n err.message.startsWith('Unexpected end of source') ||\n err.message.startsWith('Unexpected closing of source')\n );\n}\n\n/**\n * Returns the matched stop-node config `{ nested, skipEnclosures }` (from Expression.data)\n * if the current matcher position matches any stop-node expression, or `null` if not.\n * Uses ExpressionSet.findMatch() for O(1) indexed lookup.\n */\nfunction isStopNode() {\n if (this.stopNodeExpressionsSet.size === 0) return null;\n const matched = this.stopNodeExpressionsSet.findMatch(this.matcher);\n return matched ? matched.data : null;\n}\n\n/**\n * Returns the matched skip-tag config `{ nested, skipEnclosures }` (from Expression.data)\n * if the current matcher position matches any skip.tags expression, or `null` if not.\n * Uses ExpressionSet.findMatch() for O(1) indexed lookup.\n */\nfunction isSkipTag() {\n if (this.skipTagExpressionsSet.size === 0) return null;\n const matched = this.skipTagExpressionsSet.findMatch(this.matcher);\n return matched ? matched.data : null;\n}","import { createByteScanStrategy, decodeCharAtFixedWidth1, decodeCharAtUtf8 } from './ScanStrategy/ByteScanStrategy.js';\nimport { createCharScanStrategy } from './ScanStrategy/CharScanStrategy.js';\nimport { sniff } from './EncodingDetector.js';\nimport { defaultEncodingRegistry } from './EncodingRegistry.js';\n\n/**\n * buildProfileForBuffer(bytes, decodingOptions, registry) -> { descriptor, bomLength, scanStrategy }\n *\n * The ONE place encoding decisions are made for BufferSource. Called once\n * per parseBytesArr() call, never per-token — every field on the returned\n * object is a concrete, already-resolved strategy, so BufferSource itself\n * never branches on an encoding name again (Dependency Inversion: BufferSource\n * depends on the ScanStrategy interface, not on \"which encoding is this\").\n */\nexport function buildProfileForBuffer(bytes, decodingOptions = {}, registry = defaultEncodingRegistry) {\n const requested = decodingOptions.encoding || 'auto';\n let name, bomLength;\n if (requested === 'auto') {\n const detected = sniff(bytes, registry);\n name = detected.encoding;\n bomLength = detected.bomLength;\n } else {\n name = requested;\n bomLength = 0;\n }\n const descriptor = registry.resolve(name);\n const scanStrategy = descriptor.selfSynchronizing\n ? createByteScanStrategy(\n descriptor.name === 'utf8' ? decodeCharAtUtf8 : decodeCharAtFixedWidth1,\n descriptor.name // 'utf8'/'ascii'/'latin1' — all valid Buffer#toString() encodings\n )\n : createCharScanStrategy();\n\n // scanTagExpEnd() records quote positions as offsets into the *raw\n // buffer* it's scanning. AttributeProcessor.parseAttributes() wants to\n // reuse those offsets directly as indices into the *decoded* attrStr\n // string it receives from readStr(). Those two only line up when one\n // buffer unit == one decoded character:\n // - CharScanStrategy: always safe — it scans the already-decoded string,\n // same string readStr() hands back.\n // - ByteScanStrategy + fixed-width decode (ascii/latin1, and any custom\n // self-synchronizing single-byte encoding): safe — 1 byte == 1 char.\n // - ByteScanStrategy + utf8: NOT safe — non-ASCII characters are 2-4\n // bytes each, so a byte offset recorded mid-scan can land in the\n // middle of a character once decoded. AttributeProcessor falls back to\n // its own quote scan in this case (see `quotePairsUsable`).\n const quotePairsUsable = !descriptor.selfSynchronizing || descriptor.name !== 'utf8';\n\n return { descriptor, bomLength, scanStrategy, decodeFirst: !descriptor.selfSynchronizing, quotePairsUsable };\n}\n\n/**\n * buildDecoderForStream(decodingOptions, registry) -> descriptor's stateful\n * decoder, for FeedableSource/StreamSource. These two are already decode-\n * first architecturally (see CharScanStrategy's doc comment) so they only\n * ever need the decoder half of a profile, never a scan strategy.\n *\n * Streaming auto-detection (peeking enough of the first feed() chunk before\n * a decoder can even be constructed) is NOT implemented in this pass — see\n * the companion doc's \"Known follow-ups\" section. Until then, 'auto' on a\n * streaming source falls back to utf8, same as today's hardcoded behavior.\n */\nexport function buildDecoderForStream(decodingOptions = {}, registry = defaultEncodingRegistry) {\n const requested = decodingOptions.encoding && decodingOptions.encoding !== 'auto'\n ? decodingOptions.encoding\n : 'utf8';\n return registry.resolve(requested).createDecoder();\n}\n","import { ParseError, ErrorCode } from '../../ParseError.js';\nimport { isSpace } from '../../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from '../../InputSource/scanTagExpEnd.js';\n\n/**\n * CharScanStrategy — for encodings that are NOT self-synchronizing (UTF-16\n * LE/BE by default, or any custom multi-byte encoding that doesn't assert\n * `selfSynchronizing: true`). Byte-level delimiter scanning is unsafe for\n * these (an ASCII delimiter byte value can legitimately occur as part of a\n * different character), so the only correct option is to decode fully up\n * front and scan on the resulting JS string — exactly what StringSource\n * already does. This is that same algorithm, extracted so BufferSource can\n * reuse it verbatim instead of re-deriving a second copy.\n *\n * Cost model: one eager decode of the whole buffer at construction (not\n * per-token, not per-chunk). Only paid by documents that actually use one of\n * these encodings; the default UTF-8/ASCII/Latin-1 path never touches this\n * file.\n */\nexport function createCharScanStrategy() {\n return {\n readCh() {\n return this.buffer[this.startIndex++];\n },\n\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n },\n\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n },\n\n /** See StringSource.js for the full doc — identical contract, same\n * plain-string buffer shape. */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n },\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Always character-indexed (scans an already-\n // decoded string), so quote offsets are always safe for AttributeProcessor\n // to reuse. See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd,\n scanTagExpEndFast,\n\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n },\n\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n let state = 0;\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; }\n } else {\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) { state = 1; tagMatchStart = i; i += stopLength - 1; }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n },\n\n readFromBuffer(n, shouldUpdate) {\n const ch = n === 1\n ? this.buffer[this.startIndex]\n : this.buffer.substring(this.startIndex, this.startIndex + n);\n if (shouldUpdate) this.updateBufferBoundary(n);\n return ch;\n },\n\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n if (this.autoFlush && this.startIndex >= this.flushThreshold && this._tokenStart < 0) {\n this.flush();\n }\n },\n\n // Relative to current position, matching FeedableSource's formula.\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n },\n };\n}\n","import { ParseError, ErrorCode } from '../ParseError.js';\nimport { isSpace, QUOTE_PAIRS_CAPACITY } from '../util.js';\nimport { scanTagExpEnd, scanTagExpEndFast } from './scanTagExpEnd.js';\nimport { createTextDecoderAdapter } from '../Encoding/TextDecoderAdapter.js';\nimport { sniff } from '../Encoding/EncodingDetector.js';\n\n// Matches EncodingDetector's own declaration-peek window — bounds how much\n// raw (undecoded) data 'auto' mode ever holds before giving up and resolving\n// on whatever it has (a document with no BOM and no <?xml?> declaration is\n// legitimately using the utf8 default, not something to keep waiting on).\nconst SNIFF_CAP = 200;\n\n/**\n * FeedableSource — input source for the feed()/end() API.\n *\n * Accepts incremental string/Buffer chunks via feed(), accumulates them in a\n * single string buffer, and exposes the same read interface as StringSource so\n * Xml2JsParser can use it without modification.\n *\n * ### Incremental parsing\n *\n * The parser calls parseXml() after every feed() call, consuming as much of\n * the buffer as possible. When a chunk boundary falls mid-token (e.g. a CDATA\n * section split across two feeds), every reader function marks its start\n * position with markTokenStart() before it begins. If the reader throws\n * UNEXPECTED_END, the caller (XMLParser.feed) catches it and calls\n * rewindToMark() to restore startIndex to the beginning of the incomplete\n * token. The incomplete bytes stay in the buffer and are re-parsed on the\n * next feed() once the rest of the token has arrived.\n *\n * ### Two-level mark stack\n *\n * There are two mark levels:\n *\n * Level 0 — outer mark, set by parseXml()'s main loop BEFORE it reads the\n * '<' character that begins a tag dispatch. This is the position\n * that rewindToMark() always restores to, so the full tag (including\n * its '<![', '</', etc. prefix) is replayed correctly on the next\n * feed().\n *\n * Level 1 — inner mark, set by individual reader functions (readCdata,\n * readClosingTagName, readTagExp, …) at the point where *they*\n * begin. This does NOT affect rewindToMark(); it is used only by\n * flush() to determine the safe trim boundary while a reader is\n * in progress.\n *\n * Using two levels instead of a single slot prevents inner markTokenStart()\n * calls from overwriting the outer mark that feed() needs to rewind to.\n *\n * ### Memory\n *\n * Parsed data is reclaimed from the buffer automatically (autoFlush) once the\n * processed portion exceeds flushThreshold bytes. Because parseXml() runs per\n * chunk and completed tokens are consumed before the next chunk arrives, only\n * incomplete tokens at the current chunk boundary are retained — not the whole\n * document.\n *\n * maxBufferSize is checked against the live (unprocessed) portion of the\n * buffer plus the incoming chunk, not the raw buffer.length, so post-flush\n * sizing stays accurate.\n */\nexport default class FeedableSource {\n constructor(options = {}) {\n this.buffer = '';\n this.startIndex = 0;\n this.isComplete = false;\n // Running total of characters trimmed off the front by flush() so far.\n // See StringSource.js's copy of this field / util.js#absolutePosition —\n // startIndex alone drifts from the true document offset after any flush.\n this._baseOffset = 0;\n\n this.maxBufferSize = options.maxBufferSize || 10 * 1024 * 1024; // 10 MB\n this.autoFlush = options.autoFlush !== false; // true by default\n this.flushThreshold = options.flushThreshold || 1024; // 1 KB\n\n // Encoding resolution. Three modes:\n // - explicit name (options.decoding.encoding, e.g. 'utf16le'): resolve\n // a decoder immediately via the registry.\n // - 'auto': can't build a decoder yet — not enough bytes seen. Buffer\n // raw (undecoded) bytes in `_sniffBuffer` until either a BOM+enough\n // bytes, a complete `<?xml ... ?>` declaration, or SNIFF_CAP bytes\n // have accumulated, then resolve once via _resolveDetection() and\n // replay the held bytes through the real decoder. See feed() below.\n // - neither supplied (direct FeedableSource construction, bypassing\n // XMLParser): falls back to a caller-supplied `options.createDecoder`\n // if given, else plain utf8 — identical to this class's behavior\n // before this feature existed.\n this._decodingOptions = options.decoding || null;\n const requestedEncoding = this._decodingOptions?.encoding;\n this._detecting = requestedEncoding === 'auto';\n this._sniffBuffer = this._detecting ? Buffer.alloc(0) : null;\n if (!this._detecting && requestedEncoding && this._decodingOptions.registry) {\n const registry = this._decodingOptions.registry;\n this._createDecoder = () => registry.resolve(requestedEncoding).createDecoder();\n } else {\n this._createDecoder = typeof options.createDecoder === 'function' ? options.createDecoder : null;\n }\n\n /**\n * Two-level mark stack.\n *\n * _marks[0] — outer mark: set by parseXml()'s loop before consuming '<'.\n * rewindToMark() always restores startIndex here.\n * _marks[1] — inner mark: set by individual reader functions.\n * Used only by flush() as the safe trim boundary.\n *\n * `null` means \"not set\" for that level. Each entry is a plain startIndex\n * number — no line/col to carry alongside it, since position reporting\n * is index-only.\n */\n this._marks = [null, null];\n\n /**\n * Lazily-created, persistent across the whole feed() session. Buffer\n * chunks must go through this rather than Buffer#toString() per chunk —\n * toString() decodes each chunk in isolation, so a multi-byte UTF-8\n * character whose bytes straddle a chunk boundary gets corrupted (each\n * half independently replaced with U+FFFD). The decoder holds back an\n * incomplete trailing sequence internally and prepends it to the next\n * write(), so a split character decodes correctly once the rest of its\n * bytes arrive. Only created if Buffer input is ever fed — string-only\n * callers never pay for it.\n */\n this._decoder = null;\n\n // Reused across every scanTagExpEnd() call, never reallocated. See\n // StringSource.js's copy of this field for the full doc (fixed-capacity\n // typed array + manual length, not push()). Safe across a\n // chunk-boundary rewind: the failed scan's contents are irrelevant the\n // moment the tag is re-scanned from scratch on the next feed().\n this._quotePairs = new Int32Array(QUOTE_PAIRS_CAPACITY);\n this._quotePairsLen = 0;\n }\n\n /**\n * Append a data chunk to the buffer.\n *\n * maxBufferSize is checked against the live unprocessed portion\n * (buffer.length - startIndex) plus the incoming data length. Data that has\n * already been parsed and is waiting to be flushed does not count against\n * the limit.\n *\n * @param {string|Buffer} data\n * @returns {number} number of characters appended to the buffer (after\n * decoding) — callers that track fed-byte totals (e.g. XMLParser.feed's\n * batch threshold) should use this rather than the raw input length,\n * since a Buffer chunk ending mid-character may decode to fewer chars\n * than its byte length until the next chunk completes the sequence.\n */\n feed(data) {\n if (this._detecting) {\n if (typeof data === 'string') {\n // Already decoded upstream (e.g. stream.setEncoding() was called by\n // the caller) — detection is moot, nothing left to sniff.\n this._detecting = false;\n } else {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);\n this._sniffBuffer = this._sniffBuffer.length ? Buffer.concat([this._sniffBuffer, chunk]) : chunk;\n const declarationComplete = this._sniffBuffer.includes(Buffer.from('?>'));\n if (this._sniffBuffer.length < SNIFF_CAP && !declarationComplete) {\n // Not enough to decide yet — hold everything, decode nothing.\n return 0;\n }\n data = this._resolveDetection();\n }\n }\n\n let newData = this._decodeNow(data);\n\n const liveBytes = this.buffer.length - this.startIndex;\n\n if (liveBytes + newData.length > this.maxBufferSize) {\n throw new ParseError(\n `Buffer size limit exceeded (${liveBytes + newData.length} > ${this.maxBufferSize}). ` +\n `Increase feedable.maxBufferSize or reduce chunk size.`,\n ErrorCode.INVALID_INPUT\n );\n }\n\n this.buffer += newData;\n return newData.length;\n }\n\n /** @private */\n _decodeNow(data) {\n if (typeof data === 'string') return data;\n if (Buffer.isBuffer(data)) {\n // Stateful decode: bytes of a multi-byte char split across two feed()\n // calls are buffered internally by the decoder and correctly stitched\n // together, instead of each chunk being decoded in isolation.\n if (!this._decoder) this._decoder = this._createDecoder ? this._createDecoder() : createTextDecoderAdapter('utf-8');\n return this._decoder.write(data);\n }\n if (data?.toString) return data.toString();\n throw new ParseError('feed() data must be a string or Buffer.', ErrorCode.DATA_MUST_BE_STRING);\n }\n\n /**\n * Resolve 'auto' encoding from `_sniffBuffer` (BOM + `<?xml encoding=\"...\">`\n * sniffing, XML 1.0 Appendix F — see Encoding/EncodingDetector.js), build\n * the real decoder, strip any BOM, and return the held bytes ready to be\n * decoded normally by the caller in feed(). Runs exactly once per session.\n * @private\n * @returns {Buffer}\n */\n _resolveDetection() {\n const registry = this._decodingOptions.registry;\n const { encoding, bomLength } = sniff(this._sniffBuffer, registry);\n const descriptor = registry.resolve(encoding);\n this._createDecoder = () => descriptor.createDecoder();\n this._detecting = false;\n const held = bomLength ? this._sniffBuffer.subarray(bomLength) : this._sniffBuffer;\n this._sniffBuffer = null;\n return held;\n }\n\n /** Signal that no more data will be fed. */\n end() {\n if (this._detecting) {\n // Whole document arrived without ever reaching SNIFF_CAP or a\n // complete declaration (a short, unadorned document like <root/>) —\n // resolve now, on whatever bytes we have.\n const held = this._resolveDetection();\n this.buffer += this._decodeNow(held);\n }\n if (this._decoder) {\n // Flush any final incomplete byte sequence held by the decoder. For\n // well-formed UTF-8 input this is normally '' (nothing pending); a\n // non-empty result here means the input was genuinely truncated\n // mid-character, and the decoder's own U+FFFD substitution is the\n // correct, standard behavior for that case.\n const tail = this._decoder.end();\n if (tail) this.buffer += tail;\n }\n this.isComplete = true;\n }\n\n /**\n * Returns true when there is at least one character available at or after\n * the given offset (relative to startIndex).\n * @param {number} [n=0]\n */\n canRead(n = 0) {\n return this.startIndex + n < this.buffer.length;\n }\n\n // ─── Two-level mark API ───────────────────────────────────────────────────\n\n /**\n * Save the current read position into the mark stack.\n *\n * The `level` parameter selects which mark slot to write:\n *\n * level 0 (default) — outer mark, written by parseXml()'s main loop\n * before it reads the '<' that begins a dispatch.\n * level 1 — inner mark, written by reader functions\n * (readCdata, readClosingTagName, readTagExp, …)\n * at the start of their own logic.\n *\n * The two levels are independent. An inner markTokenStart(1) never\n * overwrites the outer mark[0] that rewindToMark() relies on.\n *\n * @param {0|1} [level=0]\n */\n markTokenStart(level = 0) {\n this._marks[level] = this.startIndex;\n }\n\n /**\n * Restore startIndex to the OUTER mark (level 0) and clear both marks.\n *\n * Always rewinds to the outermost saved position so the full tag —\n * including any prefix characters consumed by parseXml() before the\n * dispatch (e.g. '<', '!', '[') — is replayed on the next feed().\n *\n * Called by XMLParser.feed() when a reader throws UNEXPECTED_END.\n */\n rewindToMark() {\n if (this._marks[0] !== null) {\n this.startIndex = this._marks[0];\n }\n this._marks[0] = null;\n this._marks[1] = null;\n }\n\n /**\n * Clear both mark slots after a token completes successfully.\n *\n * Should be called (or marks allowed to be overwritten) once a dispatch\n * fully succeeds so stale positions don't block flush().\n *\n * In practice the outer mark is overwritten at the top of every\n * parseXml() loop iteration, so explicit clearing is only needed when\n * the loop does NOT continue (e.g. after a non-'<' character is consumed\n * as plain text). The flush guard uses the minimum of set marks, so a\n * stale mark only delays flushing — it does not cause correctness issues.\n */\n clearMark() {\n this._marks[0] = null;\n this._marks[1] = null;\n }\n\n /**\n * Read next character and advance position.\n * @returns {string}\n */\n readCh() {\n return this.buffer[this.startIndex++];\n }\n\n /**\n * Read character at offset without advancing.\n * @param {number} index - Offset from current position\n * @returns {string}\n */\n readChAt(index) {\n return this.buffer[this.startIndex + index];\n }\n\n /**\n * Read n characters as string.\n * @param {number} n - Number of characters to read\n * @param {number} from - Start position (default: current position)\n * @returns {string}\n */\n readStr(n, from) {\n if (typeof from === 'undefined') from = this.startIndex;\n return this.buffer.substring(from, from + n);\n }\n\n /**\n * See StringSource.js's copy of this method for the full doc — identical\n * contract here. `null` (not enough buffered data yet) is the routine\n * case for this source in particular, since a chunk boundary can land\n * mid-check; callers already have to handle that the same way they\n * handle scanTagExpEnd's -1.\n */\n matchAhead(expected, caseInsensitive = false) {\n const len = expected.length;\n for (let i = 0; i < len; i++) {\n let ch = this.buffer[this.startIndex + i];\n if (ch === undefined) return null;\n if (caseInsensitive) ch = ch.toLowerCase();\n if (ch !== expected[i]) return false;\n }\n return true;\n }\n\n // Two variants — caller picks once based on skip.attributes, no flag\n // evaluated inside the loop. Bracket access (not charCodeAt) is load-bearing\n // here: this buffer is a V8 ConsString/rope built via repeated +=, and\n // charCodeAt would force a full flatten on every access (confirmed O(n^2)\n // memory regression). The shared implementations use bracket access throughout.\n // See src/InputSource/scanTagExpEnd.js.\n scanTagExpEnd = scanTagExpEnd;\n scanTagExpEndFast = scanTagExpEndFast;\n\n /**\n * Read until stop string is found.\n * @param {string} stopStr\n * @returns {string} content before the stop string (stop string is consumed)\n * @throws {ParseError} UNEXPECTED_END when stop string is not found\n */\n readUpto(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n\n for (let i = this.startIndex; i < inputLength; i++) {\n let match = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { match = false; break; }\n }\n if (match) {\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + stopLength;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Single-character variant of readUpto — faster because there is no inner\n * match loop. Reads until `stopChar` is found, consumes it, and returns\n * the text before it.\n *\n * @param {string} stopChar Exactly one character.\n * @returns {string}\n */\n readUptoChar(stopChar) {\n const i = this.buffer.indexOf(stopChar, this.startIndex);\n if (i === -1) {\n throw new ParseError(`Unexpected end of source reading '${stopChar}'`, ErrorCode.UNEXPECTED_END);\n }\n const result = this.buffer.substring(this.startIndex, i);\n this.startIndex = i + 1;\n return result;\n }\n\n /**\n * Read until a closing tag is found (used for stop nodes).\n * @param {string} stopStr e.g. `\"</tagname\"`\n * @returns {string} raw content between the current position and the closing tag\n * @throws {ParseError} UNEXPECTED_END when the closing tag is not found\n */\n readUptoCloseTag(stopStr) {\n const inputLength = this.buffer.length;\n const stopLength = stopStr.length;\n let tagMatchStart = -1;\n let state = 0; // 0=scanning, 1=tag-name matched (scanning for '>'), 2=full match\n\n for (let i = this.startIndex; i < inputLength; i++) {\n if (state === 1) {\n const c = this.buffer[i];\n if (isSpace(c)) continue;\n if (c === '>') { state = 2; }\n else { state = 0; tagMatchStart = -1; } // false match e.g. </scriptX>\n } else {\n // Try to match stopStr at position i\n let matched = true;\n for (let j = 0; j < stopLength; j++) {\n if (this.buffer[i + j] !== stopStr[j]) { matched = false; break; }\n }\n if (matched) {\n state = 1;\n tagMatchStart = i;\n i += stopLength - 1; // skip past matched string\n }\n }\n if (state === 2) {\n const result = this.buffer.substring(this.startIndex, tagMatchStart);\n this.startIndex = i + 1;\n return result;\n }\n }\n\n throw new ParseError(`Unexpected end of source reading '${stopStr}'`, ErrorCode.UNEXPECTED_END);\n }\n\n /**\n * Advance the read cursor by n characters.\n *\n * Triggers an automatic flush of already-processed data when autoFlush is\n * enabled, the processed portion has grown past flushThreshold, and no\n * mark is currently active. Any active mark (either level) blocks the\n * flush to prevent the saved position from becoming invalid.\n *\n * @param {number} [n=1]\n */\n updateBufferBoundary(n = 1) {\n this.startIndex += n;\n // No \"any mark active\" gate here — flush()'s own min(startIndex, marks...)\n // origin computation already guarantees any in-progress token (at either\n // mark level) survives the trim. A separate boolean gate on top of that\n // was redundant, and since _marks[0] is set on every parseXml() loop\n // iteration and never nulled outside of rewindToMark() (an error path),\n // that gate was effectively permanent — flush() never ran in normal\n // operation. See specs/flushArchitecture_spec.js for the regression test.\n if (this.autoFlush && this.startIndex >= this.flushThreshold) {\n this.flush();\n }\n }\n\n /**\n * Discard already-processed data from the front of the buffer to free memory.\n * startIndex is reset to 0 after the trim.\n *\n * The flush origin is the minimum of all active mark positions, so that any\n * in-progress token (at either mark level) is preserved in the buffer and\n * can be re-read after the flush. This is the sole safety mechanism for\n * flush() — callers do not need to additionally check \"is a mark active\"\n * before calling this; an active mark simply caps how much origin can\n * advance, rather than blocking the call outright.\n *\n * If no marks are active, the origin is startIndex itself — everything\n * before the current read position is discarded.\n */\n flush() {\n // Determine the earliest position that must be kept.\n let origin = this.startIndex;\n for (const m of this._marks) {\n if (m !== null && m < origin) origin = m;\n }\n\n if (origin > 0) {\n this.buffer = this.buffer.substring(origin);\n\n // Adjust all mark offsets by the amount trimmed.\n const marksLen = this._marks.length;\n for (let i = 0; i < marksLen; i++) {\n if (this._marks[i] !== null) this._marks[i] -= origin;\n }\n\n this.startIndex -= origin;\n this._baseOffset += origin;\n }\n }\n}\n","import FeedableSource from './FeedableSource.js';\n\n/**\n * StreamSource — input source that reads from a Node.js Readable stream.\n *\n * Extends FeedableSource so it shares the same buffer management and read\n * interface. attachStream() wires Node.js stream events. On each 'data'\n * event the chunk is appended to the buffer and onChunk is called so the\n * caller can run parseXml() incrementally. Parsing is therefore driven\n * chunk-by-chunk rather than once over the full accumulated document.\n */\nexport default class StreamSource extends FeedableSource {\n /**\n * Wire a Readable stream to this source.\n *\n * @param {NodeJS.ReadableStream} readable\n * @param {function(Error|null):void} onChunk\n * Called after each successful feed() with null, or immediately with the\n * feed error if the buffer limit is exceeded. The caller runs parseXml()\n * inside this callback and handles UNEXPECTED_END (chunk boundary mid-token)\n * by calling rewindToMark().\n * @param {function():void} onEnd\n * Called when the stream ends cleanly. The caller should finalise the parse\n * (finalizeXml) here.\n * @param {function(Error):void} onError\n * Called with any stream-level error (e.g. 'error' event from the readable).\n */\n attachStream(readable, onChunk, onEnd, onError) {\n readable.on('data', chunk => {\n try {\n // Pass the raw chunk (Buffer or string) straight through — feed()\n // decodes Buffers via a persistent stateful decoder so a multi-byte\n // UTF-8 character split across two chunks decodes correctly instead\n // of each half being independently mangled by a per-chunk toString().\n this.feed(chunk);\n onChunk(null); // chunk appended successfully — caller runs parseXml()\n } catch (err) {\n onChunk(err); // buffer overflow or coercion failure\n }\n });\n\n readable.on('error', onError);\n\n readable.on('end', () => {\n try {\n this.end();\n onEnd();\n } catch (err) {\n onError(err);\n }\n });\n }\n}","import { buildOptions } from './OptionsBuilder.js';\nimport { ParseError, ErrorCode } from './ParseError.js';\nimport { absolutePosition } from './util.js';\nimport Xml2JsParser from './Xml2JsParser.js';\nimport FeedableSource from './InputSource/FeedableSource.js';\nimport StreamSource from './InputSource/StreamSource.js';\nimport EncodingRegistry, { defaultEncodingRegistry } from './Encoding/EncodingRegistry.js';\n\nexport default class XMLParser {\n\n constructor(options) {\n this.options = buildOptions(options);\n\n // feed()/end() session state\n this._feedParser = null;\n this._feedSource = null;\n this._isFeeding = false;\n\n // ── Batching state ──────────────────────────────────\n this._pendingBytes = 0;\n this._batchThreshold = this.options.feedable?.bufferSize;\n\n // Per-instance encoding registry only when custom decoders are supplied\n // — avoids mutating the shared default registry (which would leak a\n // customDecoder registered on one XMLParser instance into every other\n // instance in the process). The common case (no customDecoders) reuses\n // the shared default registry, seeded once at module load.\n if (this.options.decoding?.customDecoders) {\n const registry = new EncodingRegistry();\n for (const [name, descriptor] of Object.entries(this.options.decoding.customDecoders)) {\n registry.register({ name, ...descriptor });\n }\n this.options.decoding._registry = registry;\n } else {\n this.options.decoding = this.options.decoding || {};\n this.options.decoding._registry = defaultEncodingRegistry;\n }\n\n // Shared tag/attribute name cache — lives on `options`, not on any one\n // Xml2JsParser instance, because `.parse()` creates a fresh Xml2JsParser\n // every call while `this.options` is passed by reference to all of them.\n // This lets repeated names skip re-validation/re-sanitization across\n // separate parse() calls on the same XMLParser instance, not just within\n // one document. See Xml2JsParser.js for what's cached and why.\n this.options._nameCache = { tags: new Map(), attrs: new Map() };\n }\n\n // ─── One-shot parse methods ───────────────────────────────────────────────\n\n /**\n * Parse an XML string or Buffer and return a JS object.\n * @param {string|Buffer} xmlData\n */\n parse(xmlData) {\n if (xmlData instanceof Buffer || ArrayBuffer.isView(xmlData)) {\n // Route through the encoding-aware path (auto-detect / configured\n // `decoding.encoding`) instead of an unconditional utf8 toString() —\n // otherwise a non-utf8 `decoding.encoding` option would silently be\n // ignored for Buffer input given directly to parse().\n return this.parseBytesArr(xmlData);\n } else if (typeof xmlData !== 'string') {\n if (xmlData && typeof xmlData.toString === 'function') {\n xmlData = xmlData.toString();\n } else {\n throw new ParseError('XML data must be a string or Buffer.', ErrorCode.INVALID_INPUT);\n }\n }\n\n const parser = this._createParser();\n const result = parser.parse(xmlData);\n this.wasExited = parser.wasExited();\n this._lastParseErrors = parser.autoCloseHandler?.getErrors() ?? [];\n return result;\n }\n\n /**\n * Parse a Uint8Array / byte array and return a JS object.\n * @param {Uint8Array|ArrayBufferView} xmlData\n */\n parseBytesArr(xmlData) {\n if (xmlData instanceof Uint8Array || ArrayBuffer.isView(xmlData)) {\n xmlData = Buffer.from(xmlData);\n } else {\n throw new ParseError('XML data must be a Uint8Array or ArrayBufferView.', ErrorCode.INVALID_INPUT);\n }\n\n const parser = this._createParser();\n const result = parser.parseBytesArr(xmlData);\n this.wasExited = parser.wasExited();\n this._lastParseErrors = parser.autoCloseHandler?.getErrors() ?? [];\n return result;\n }\n\n // ─── Stream input ─────────────────────────────────────────────────────────\n\n /**\n * Parse an XML Node.js Readable stream and return a Promise that resolves\n * with the parsed JS object.\n *\n * Chunks are processed incrementally as they arrive — parseXml() runs after\n * each 'data' event and already-consumed input is freed before the next\n * chunk arrives, so memory stays proportional to the largest incomplete token\n * at any chunk boundary rather than the total document size.\n *\n * @param {NodeJS.ReadableStream} readable\n * @returns {Promise<any>}\n */\n parseStream(readable) {\n if (!isReadableStream(readable)) {\n throw new ParseError('parseStream() requires a Node.js Readable stream.', ErrorCode.INVALID_STREAM);\n }\n\n const source = new StreamSource({\n ...this.options.feedable,\n decoding: { encoding: this.options.decoding.encoding, registry: this.options.decoding._registry },\n });\n const streamParser = this._createParser();\n streamParser.source = source;\n streamParser.initializeParser();\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const fail = (err) => {\n if (!settled) {\n settled = true;\n readable.destroy(); // stop further data/end events and free the handle\n reject(err);\n }\n };\n\n source.attachStream(\n readable,\n // onChunk — run the parser incrementally after each chunk arrives.\n // Mirrors what feed() does: advance as far as possible, rewind on\n // UNEXPECTED_END (chunk boundary mid-token), re-throw real errors.\n (err) => {\n if (err) { fail(err); return; }\n try {\n streamParser.parseXml();\n } catch (parseErr) {\n if (parseErr.code === ErrorCode.UNEXPECTED_END) {\n source.rewindToMark();\n } else {\n fail(parseErr);\n }\n }\n },\n // onEnd — stream finished cleanly; finalise the document.\n () => {\n if (settled) return;\n try {\n // source.end() (called by attachStream just before this) can\n // release content that was held back pending encoding detection\n // (see FeedableSource's 'auto' mode) — run parseXml() once more\n // to consume it before finalizing. No-op if there's nothing new.\n streamParser.parseXml();\n streamParser.finalizeXml();\n this._lastParseErrors = streamParser.autoCloseHandler?.getErrors() ?? [];\n settled = true;\n resolve(streamParser.outputBuilder.getOutput());\n } catch (err) { fail(err); }\n },\n // onError — stream-level error (e.g. file not found, network drop)\n fail,\n );\n });\n }\n\n // ─── Incremental feed()/end() API ────────────────────────────────────────\n\n _runParse() {\n if (!this._feedParser) return;\n\n const beforePos = absolutePosition(this._feedSource); // bytes consumed so far, flush-proof\n\n try {\n this._feedParser.parseXml();\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n this._feedSource.rewindToMark();\n } else {\n throw err;\n }\n }\n\n const afterPos = absolutePosition(this._feedSource);\n const didAdvance = afterPos > beforePos;\n\n if (didAdvance) {\n // Real progress made — reset threshold normally\n this._pendingBytes = 0;\n this._batchThreshold = this.options.feedable.bufferSize;\n } else {\n // Parser is stuck mid-token — grow the threshold to avoid\n // hammering parseXml() until significantly more data arrives\n this._batchThreshold = Math.min(\n this._batchThreshold * 2,\n this.options.feedable.maxBufferSize\n );\n }\n }\n\n /**\n * Feed an XML data chunk for incremental parsing.\n *\n * After appending the chunk, parseXml() is run immediately so the parser\n * advances as far as possible. If a chunk boundary falls mid-token, the\n * reader throws UNEXPECTED_END; this is caught here and the source is\n * rewound to the start of the incomplete token so it will be re-parsed on\n * the next feed() call once more data has arrived.\n *\n * Any other ParseError (unclosed quote, mismatched tag, etc.) is a real\n * parse failure and is re-thrown after cleaning up the session.\n *\n * @param {string|Buffer} data\n * @returns {XMLParser} for chaining\n */\n feed(data) {\n if (!this._isFeeding) {\n this._initFeedSession();\n }\n\n // Pass raw data straight through — do NOT pre-convert Buffers to string\n // here. FeedableSource.feed() decodes Buffers via a persistent stateful\n // decoder so a multi-byte UTF-8 character split across two feed()\n // calls decodes correctly; converting each chunk with .toString() first\n // (as this used to do) decodes each chunk in isolation and corrupts a\n // split character. feed() itself validates the type and throws\n // DATA_MUST_BE_STRING for anything unsupported.\n const appendedLength = this._feedSource.feed(data);\n this._pendingBytes += appendedLength;\n\n if (this._pendingBytes >= this._batchThreshold) {\n this._runParse();\n }\n // Otherwise, delay parsing until next feed() or end()\n\n return this;\n }\n\n /**\n * Signal end of input, validate end-of-document state, and return the\n * parsed result. Throws if called before any feed() call.\n *\n * parseXml() is called one final time after marking the source complete.\n * This replays any bytes that were rewound during the last feed() call\n * (e.g. a tag that was split across the final chunk boundary). Now that\n * isComplete is true, any UNEXPECTED_END thrown by a reader means the\n * document is genuinely truncated — not a chunk boundary — so it is\n * treated as a real parse error rather than silently swallowed.\n *\n * autoClose partial-tag recovery works the same way it does in\n * _parseAndFinalize(): if autoCloseHandler is configured and parseXml()\n * throws UNEXPECTED_END, the handler is given a chance to recover before\n * finalizeXml() runs.\n *\n * @returns {any}\n */\n end() {\n if (!this._isFeeding) {\n throw new ParseError('No data fed. Call feed() before end().', ErrorCode.NOT_STREAMING);\n }\n\n // Force a final parse (any pending bytes are now processed)\n this._runParse();\n\n try {\n // Mark the source as complete so readers know there is no more data.\n this._feedSource.end();\n\n // Replay any bytes rewound during the last feed() call (e.g. an\n // incomplete tag at the very end of the input stream). Any\n // UNEXPECTED_END thrown here is a genuine truncation error.\n let partialTagError = null;\n const autoClose = this._feedParser.autoCloseHandler;\n if (autoClose) autoClose.reset();\n\n try {\n this._feedParser.parseXml();\n } catch (err) {\n if (err.code === ErrorCode.UNEXPECTED_END) {\n if (autoClose) {\n // autoClose recovery: treat the truncated tag the same way\n // _parseAndFinalize() does for the one-shot parse path.\n partialTagError = err;\n } else {\n // No recovery configured — truncated document is a hard error.\n throw err;\n }\n } else {\n throw err;\n }\n }\n\n if (partialTagError) {\n autoClose.handlePartialTag(partialTagError, this._feedParser._parserState());\n } else {\n this._feedParser.finalizeXml();\n }\n\n this._lastParseErrors = autoClose?.getErrors() ?? [];\n this.wasExited = this._feedParser.wasExited();\n return this._feedParser.outputBuilder.getOutput();\n } finally {\n this._cleanupFeedSession();\n }\n }\n\n // ─── Error reporting ──────────────────────────────────────────────────────\n\n /**\n * Return structural errors collected during the last parse call.\n * Only populated when autoClose.collectErrors is true.\n * Each entry: { type, tag, expected, index }\n *\n * @returns {Array}\n */\n getParseErrors() {\n return this._lastParseErrors ?? [];\n }\n\n // ─── Private helpers ──────────────────────────────────────────────────────\n\n /** @private */\n _createParser() {\n return new Xml2JsParser(this.options);\n }\n\n /** @private */\n _initFeedSession() {\n this._feedSource = new FeedableSource({\n ...this.options.feedable,\n decoding: { encoding: this.options.decoding.encoding, registry: this.options.decoding._registry },\n });\n this._feedParser = this._createParser();\n this._feedParser.source = this._feedSource;\n this._feedParser.initializeParser();\n this._isFeeding = true;\n }\n\n /** @private */\n _cleanupFeedSession() {\n this._feedParser = null;\n this._feedSource = null;\n this._isFeeding = false;\n }\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction isReadableStream(value) {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof value.read === 'function' &&\n typeof value.on === 'function' &&\n typeof value.readableEnded === 'boolean'\n );\n}","import { CompactBuilderFactory, CompactBuilder } from \"@nodable/compact-builder\";\n\n/**\n * Same shape as CompactBuilder's output, plus a side-channel recording the\n * insertion order of child tags per parent path (Map<parentPath, string[]>,\n * dot-notation paths matching Traverser's own path format).\n *\n * Needed because plain object key order isn't a reliable stand-in for\n * document order once repeated sibling tags collapse into one key backed by\n * an array — this fires once per tag in document order regardless.\n */\nclass OrderTrackingBuilder extends CompactBuilder {\n constructor(parserOptions, builderOptions, readonlyMatcher, registry) {\n super(parserOptions, builderOptions, readonlyMatcher, registry);\n this.siblingOrder = new Map();\n }\n\n addElement(tag) {\n // this.matcher already reflects the tag being opened (Xml2JsParser\n // pushes onto the matcher before addElement is called), so its path\n // string is \"root.parent.child\" — the parent path is everything\n // before the last segment.\n const jPath = this.matcher.toString();\n const lastDot = jPath.lastIndexOf(\".\");\n const parentPath = lastDot === -1 ? \"\" : jPath.substring(0, lastDot);\n if (!this.siblingOrder.has(parentPath)) this.siblingOrder.set(parentPath, []);\n const siblings = this.siblingOrder.get(parentPath);\n // Only push if not already recorded (repeated tags share one entry)\n if (siblings[siblings.length - 1] !== tag.name) siblings.push(tag.name);\n\n super.addElement(tag);\n }\n\n getOutput() {\n return { data: super.getOutput(), siblingOrder: this.siblingOrder };\n }\n}\n\nexport class OrderTrackingBuilderFactory extends CompactBuilderFactory {\n getInstance(parserOptions, readonlyMatcher) {\n return new OrderTrackingBuilder(parserOptions, this.builderOptions, readonlyMatcher, this.registry);\n }\n}\n","import { NumberValueParser } from \"@nodable/base-output-builder\";\n\n// dxv's own numeric type-checking (Traverser.js, validations.js, and\n// parseRange below) used plain Number()/parseFloat()/isNaN(), none of which\n// understand full-width (unicode) numerals such as \"1000\". Centralizing\n// coercion here, backed by BOB's own number value parser configured with\n// unicode:true, gives every numeric check in dxv that support in one place.\n// FXP's tag/attribute value-parser pipeline stays disabled for dxv data\n// (see RAW_VALUE_PARSERS in validator.js) — this is a separate, dxv-local\n// use of the same parser class, not a re-enabling of that pipeline.\nconst numberParser = new NumberValueParser({ hex: true, leadingZeros: true, eNotation: true, unicode: true });\n\n/**\n * Coerce a raw XML string value to a number, accepting unicode (full-width)\n * numerals in addition to plain ASCII digits/hex/e-notation.\n * @param {string} val\n * @returns {number} the parsed number, or NaN if val isn't numeric\n */\nexport function toNumber(val) {\n const parsed = numberParser.parse(val);\n return typeof parsed === \"number\" ? parsed : NaN;\n}\n\n/**\n * @param {string[]} data\n * @param {string[]} rules\n * @returns {{ data: string[], rules: string[], common: string[] }}\n */\nexport function breakInSets(data, rules) {\n const extra = {\n data: [],\n rules: [],\n common: [],\n };\n\n for (let i = 0; i < data.length; i++) {\n if (rules.indexOf(data[i]) === -1) extra.data.push(data[i]);\n else extra.common.push(data[i]);\n }\n\n for (let i = 0; i < rules.length; i++) {\n if (data.indexOf(rules[i]) === -1 && rules[i] !== \"@\") extra.rules.push(rules[i]);\n }\n return extra;\n}\n\n/**\n * Parse a range shorthand \"min..max\" string into { min, max } numbers.\n * Returns null if the string is not a valid range.\n * @param {string} range\n * @returns {{ min: number, max: number } | null}\n */\nexport function parseRange(range) {\n if (typeof range !== \"string\") return null;\n const parts = range.split(\"..\");\n if (parts.length !== 2) return null;\n const min = toNumber(parts[0]);\n const max = toNumber(parts[1]);\n if (isNaN(min) || isNaN(max)) return null;\n return { min, max };\n}\n\n/**\n * Parse a date range shorthand \"minDate..maxDate\" string into { min, max } date strings.\n * Returns null if either part is not a parseable date.\n * @param {string} range\n * @returns {{ min: string, max: string } | null}\n */\nexport function parseDateRange(range) {\n if (typeof range !== \"string\") return null;\n const parts = range.split(\"..\");\n if (parts.length !== 2) return null;\n const [min, max] = parts;\n if (isNaN(Date.parse(min)) || isNaN(Date.parse(max))) return null;\n return { min, max };\n}\n","import { toNumber } from \"./util.js\";\n\nexport const string = {\n minLength(expected, actual) {\n return actual.length >= expected;\n },\n maxLength(expected, actual) {\n return actual.length <= expected;\n },\n length(expected, actual) {\n return actual.length === expected;\n },\n pattern(expected, actual, modifier = \"\") {\n const regxp = new RegExp(expected, modifier);\n return regxp.test(actual);\n },\n fixed(expected, actual) {\n return expected === actual;\n },\n in(expected, actual) {\n return expected.split(\",\").indexOf(actual) > -1;\n },\n};\n\nexport const num = {\n min(expected, actual) {\n return toNumber(actual) >= expected;\n },\n max(expected, actual) {\n return toNumber(actual) <= expected;\n },\n};\n\nexport const list = {\n minOccurs(expected, actual) {\n return actual >= expected;\n },\n maxOccurs(expected, actual) {\n return actual <= expected;\n },\n};\n\nexport const date = {\n /**\n * Compare two date strings.\n * @param {string} a\n * @param {string} b\n * @returns {number}\n */\n compare(a, b) {\n return Date.parse(a) - Date.parse(b);\n },\n\n isBefore(actual, reference) {\n return Date.parse(actual) < Date.parse(reference);\n },\n\n isAfter(actual, reference) {\n return Date.parse(actual) > Date.parse(reference);\n },\n\n min(expected, actual) {\n return Date.parse(actual) >= Date.parse(expected);\n },\n\n max(expected, actual) {\n return Date.parse(actual) <= Date.parse(expected);\n },\n};\n","import * as validations from \"./validations.js\";\nimport { breakInSets, parseRange, parseDateRange, toNumber } from \"./util.js\";\n\nconst numericTypes = [\"positiveInteger\", \"integer\", \"positiveDecimal\", \"decimal\", \"number\"];\n\nexport class Traverser {\n /**\n * @param {object} options\n * @param {object} validators\n */\n constructor(options, validators, siblingOrder) {\n this.options = options;\n this.failures = [];\n this.validators = validators;\n\n // uniqueness tracking: field path -> array of seen values (sibling-scoped)\n // global uniqueness tracking: field name -> array of seen values (document-scoped)\n this._globalUnique = new Map(); // Map<fieldName, Set<string>>\n\n // Side-channel sibling order map built during XML parsing.\n // Map<parentPath, string[]> where parentPath matches Traverser's dot-notation path.\n this._siblingOrder = siblingOrder || new Map();\n }\n\n /**\n * @param {object|string} ele\n * @param {string} key\n * @param {object|string} rules\n * @param {string} path\n */\n traverse(ele, key, rules, path) {\n if (Array.isArray(ele)) {\n if (rules !== undefined && rules[\"@rules\"] !== undefined) {\n if (rules[\"@rules\"].repeatable !== undefined) {\n this.checkOccurences(rules[\"@rules\"], ele.length, path);\n ele.forEach((val, index) => {\n const arrayPath = path + \"[\" + index + \"]\";\n if (Object.keys(rules).length > 1) {\n this.callForCommonProperties(ele[index], rules, arrayPath);\n } else {\n this.traverse(ele[index], key, rules, arrayPath);\n }\n });\n return;\n }\n }\n this.failures.push({\n code: \"unexpected sequence\",\n path: path.substr(1),\n });\n } else if (typeof ele === \"object\") {\n this.callForCommonProperties(ele, rules, path);\n } else {\n if (typeof rules === \"object\" && rules[\"@rules\"]) {\n this.checkDataAndType(ele, key, rules, path);\n } else if (this.isMapType(rules)) {\n this.validateMandatoryFields(rules, path);\n }\n this.applyCustomValidators(ele, rules, path);\n }\n }\n\n applyCustomValidators(ele, rules, path) {\n if (\n rules[\"@rules\"] &&\n rules[\"@rules\"].checkBy &&\n typeof this.validators[rules[\"@rules\"].checkBy] === \"function\"\n ) {\n let res = this.validators[rules[\"@rules\"].checkBy](ele, path.substr(1));\n if (typeof res === \"object\") {\n this.failures.push(res);\n }\n }\n }\n\n /**\n * @param {object} ele\n * @param {object} rules\n * @param {string} path\n */\n callForCommonProperties(ele, rules, path) {\n const tags = Object.keys(ele);\n const rulesTags = Object.keys(rules);\n\n const sets = breakInSets(tags, rulesTags);\n this.checkUnknownSiblings(sets, path);\n this.checkMissingSiblings(sets, rules, path);\n\n this.applyCustomValidators(ele, rules, path);\n\n // Collect sibling values for uniqueness checks before traversing\n // Build a map of fieldName -> array of values for this sibling scope\n const siblingValues = this._buildSiblingValues(ele, rules, sets.common);\n\n sets.common.forEach((key) => {\n const newpath = path + \".\" + key;\n this.traverse(ele[key], key, rules[key], newpath);\n });\n\n // After traversing, run ordering, uniqueness, and relational checks for this sibling scope\n this._checkOrderingConstraints(ele, rules, sets.common, path);\n this._checkUniquenessConstraints(ele, rules, sets.common, path, siblingValues);\n this._checkRelationalConstraints(ele, rules, sets.common, path);\n }\n\n // ─── Ordering Constraints (before / after) ────────────────────────────────\n\n /**\n * For every field that has `before` or `after` in its rules, validate that\n * it appears positionally before or after the referenced sibling tag in the\n * original XML document order.\n *\n * Order is sourced from the side-channel `_siblingOrder` map built during\n * parsing — not from JS object key order, which is unreliable for integer keys.\n *\n * Works for any tag type, not just dates.\n *\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path - current traversal path (e.g. \".event\" or \".order[0]\")\n */\n _checkOrderingConstraints(ele, rules, commonKeys, path) {\n // The siblingOrder map is keyed by the parent path without the leading dot.\n const orderKey = path.substr(1);\n const order = this._siblingOrder.get(orderKey);\n // If we have no order info for this path, skip — can happen for empty elements.\n if (!order) return;\n\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const r = fieldRules[\"@rules\"];\n\n const keyIdx = order.indexOf(key);\n if (keyIdx === -1) return; // tag not recorded — skip\n\n if (r.after) {\n const refKey = r.after;\n // Skip silently if referenced sibling is absent from data\n if (ele[refKey] === undefined) return;\n const refIdx = order.indexOf(refKey);\n if (refIdx === -1) return;\n if (keyIdx <= refIdx) {\n this.failures.push({\n code: \"after\",\n path: (path + \".\" + key).substr(1),\n actual: key,\n expected: refKey,\n });\n }\n }\n\n if (r.before) {\n const refKey = r.before;\n // Skip silently if referenced sibling is absent from data\n if (ele[refKey] === undefined) return;\n const refIdx = order.indexOf(refKey);\n if (refIdx === -1) return;\n if (keyIdx >= refIdx) {\n this.failures.push({\n code: \"before\",\n path: (path + \".\" + key).substr(1),\n actual: key,\n expected: refKey,\n });\n }\n }\n });\n }\n\n // ─── Relational Constraints (sameAs / notSameAs / lessThan / moreThan) ──────\n\n /**\n * Compare this field's value against another sibling field's value.\n *\n * Supported rule attributes:\n * sameAs=\"refField\" — values must be equal\n * notSameAs=\"refField\" — values must differ\n * lessThan=\"refField\" — this value must be strictly less than ref value\n * moreThan=\"refField\" — this value must be strictly greater than ref value\n *\n * Comparison is type-aware based on the field's declared `type`:\n * date types → Date.parse() comparison\n * numeric types → Number() comparison\n * all others → lexicographic string comparison\n *\n * Skips silently when:\n * - The referenced sibling is absent from data\n * - Either value is an object/map (not a primitive)\n * - The ref value cannot be coerced to the expected type\n *\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path\n */\n _checkRelationalConstraints(ele, rules, commonKeys, path) {\n const RELATIONAL_RULES = [\"sameAs\", \"notSameAs\", \"lessThan\", \"moreThan\"];\n\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const r = fieldRules[\"@rules\"];\n\n // Check if any relational rule is present\n if (!RELATIONAL_RULES.some((attr) => r[attr] !== undefined)) return;\n\n const actualRaw = ele[key];\n\n // Skip maps and arrays — only primitives are comparable\n if (actualRaw === null || actualRaw === undefined) return;\n if (typeof actualRaw === \"object\") return;\n\n const fieldType = r.type || \"string\";\n\n RELATIONAL_RULES.forEach((attr) => {\n if (r[attr] === undefined) return;\n\n const refKey = r[attr];\n const refRaw = ele[refKey];\n\n // Skip silently if ref sibling is absent or is a map/array\n if (refRaw === undefined || refRaw === null) return;\n if (typeof refRaw === \"object\") return;\n\n // Coerce both values and compare\n const cmp = this._relationalCompare(String(actualRaw), String(refRaw), fieldType);\n\n // cmp is null when coercion fails — skip silently\n if (cmp === null) return;\n\n let violated = false;\n if (attr === \"sameAs\" && cmp !== 0) violated = true;\n if (attr === \"notSameAs\" && cmp === 0) violated = true;\n if (attr === \"lessThan\" && cmp >= 0) violated = true;\n if (attr === \"moreThan\" && cmp <= 0) violated = true;\n\n if (violated) {\n this.failures.push({\n code: attr,\n path: (path + \".\" + key).substr(1),\n actual: String(actualRaw),\n expected: refKey,\n });\n }\n });\n });\n }\n\n /**\n * Compare two string-encoded values using the appropriate strategy for the field type.\n * Returns a negative number, zero, or positive number (like Array.sort comparator).\n * Returns null if coercion fails (caller should skip silently).\n * @param {string} a\n * @param {string} b\n * @param {string} fieldType\n * @returns {number|null}\n */\n _relationalCompare(a, b, fieldType) {\n if (fieldType === \"date\") {\n const da = Date.parse(a);\n const db = Date.parse(b);\n if (isNaN(da) || isNaN(db)) return null;\n return da - db;\n }\n if (numericTypes.indexOf(fieldType) !== -1) {\n const na = toNumber(a);\n const nb = toNumber(b);\n if (isNaN(na) || isNaN(nb)) return null;\n return na - nb;\n }\n // string / no type — lexicographic\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n }\n\n // ─── Uniqueness Constraints ───────────────────────────────────────────────\n\n /**\n * Build a map of fieldName -> array of values found in this sibling scope.\n * Works for both scalar values and array (repeatable) values.\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @returns {Map<string, string[]>}\n */\n _buildSiblingValues(ele, rules, commonKeys) {\n const map = new Map();\n commonKeys.forEach((key) => {\n const val = ele[key];\n if (Array.isArray(val)) {\n map.set(key, val.map(String));\n } else if (val !== undefined && val !== null && typeof val !== \"object\") {\n map.set(key, [String(val)]);\n }\n });\n return map;\n }\n\n /**\n * Check uniqueness constraints for fields in this sibling scope.\n * @param {object} ele\n * @param {object} rules\n * @param {string[]} commonKeys\n * @param {string} path\n * @param {Map<string, string[]>} siblingValues\n */\n _checkUniquenessConstraints(ele, rules, commonKeys, path, siblingValues) {\n // For repeatable items (array context), ele is a single item; sibling-scope\n // uniqueness is managed by the parent array traversal which collects all items.\n // We handle uniqueness at the collection level in `_checkUniqueInCollection`.\n // This method handles per-field unique=\"global\".\n commonKeys.forEach((key) => {\n const fieldRules = rules[key];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n const unique = fieldRules[\"@rules\"].unique;\n if (!unique) return;\n\n const vals = siblingValues.get(key) || [];\n if (unique === \"global\") {\n vals.forEach((v, idx) => {\n const fieldPath = Array.isArray(ele[key])\n ? (path + \".\" + key + \"[\" + idx + \"]\").substr(1)\n : (path + \".\" + key).substr(1);\n this._assertGlobalUnique(key, v, fieldPath);\n });\n }\n // unique=\"true\" (sibling scope) is handled at the repeatable-collection level\n });\n }\n\n /**\n * Check uniqueness within a collection of repeatable items.\n * Called from _traverseRepeatableCollection when sibling-scoped uniqueness is needed.\n * @param {Array<object>} items\n * @param {string} fieldKey - field to check\n * @param {string} basePath - path prefix for error messages\n */\n _checkUniqueInCollection(items, fieldKey, basePath) {\n const seen = new Map(); // value -> first index\n items.forEach((item, idx) => {\n const val = item[fieldKey];\n if (val === undefined || val === null || typeof val === \"object\") return;\n const strVal = String(val);\n if (seen.has(strVal)) {\n this.failures.push({\n code: \"unique\",\n path: (basePath + \"[\" + idx + \"].\" + fieldKey).substr(1),\n value: strVal,\n });\n } else {\n seen.set(strVal, idx);\n }\n });\n }\n\n /**\n * Assert a value is unique globally (across the whole document).\n * @param {string} fieldName\n * @param {string} value\n * @param {string} fieldPath\n */\n _assertGlobalUnique(fieldName, value, fieldPath) {\n if (!this._globalUnique.has(fieldName)) {\n this._globalUnique.set(fieldName, new Set());\n }\n const seen = this._globalUnique.get(fieldName);\n if (seen.has(value)) {\n this.failures.push({\n code: \"unique\",\n path: fieldPath,\n value,\n });\n } else {\n seen.add(value);\n }\n }\n\n // ─── Data & Type ──────────────────────────────────────────────────────────\n\n /**\n * Check if the leaf node has correct type and passes validations.\n * @param {string} val\n * @param {string} key\n * @param {object} rules\n * @param {string} path\n */\n checkDataAndType(val, key, rules, path) {\n if (rules[\"@rules\"].repeatable === true) {\n this.checkOccurences(rules[\"@rules\"], 1, path);\n }\n const eleType = rules[\"@rules\"].type;\n\n // Expand range shorthand before numeric validation\n const expandedRules = this._expandRange(rules);\n\n if (eleType === \"map\" || (!eleType && this.isMapType(rules))) {\n if (typeof val === \"string\" && val.length > 0) {\n this.failures.push({\n code: \"unexpected value in a map\",\n path: path.substr(1),\n value: val,\n });\n }\n this.validateMandatoryFields(rules, path);\n } else if (eleType === \"date\") {\n this.validateDate(val, eleType, path);\n this._checkDateBounds(rules, val, path);\n } else if (eleType === \"boolean\") {\n this.validateBoolean(val, eleType, path);\n } else if (numericTypes.indexOf(eleType) !== -1) {\n if (!this.isValidNum(eleType, val)) {\n this.setInvalidDataType(eleType, path, val);\n } else {\n this.assertValue(expandedRules[\"@rules\"], \"num\", toNumber(val), path);\n }\n } else if (eleType === \"string\" || !eleType) {\n this.assertValue(expandedRules[\"@rules\"], \"string\", val, path);\n } else {\n throw new Error(\"Unsupported data type in Rules:\" + eleType);\n }\n }\n\n /**\n * Expand `range=\"min..max\"` shorthand into `min` and `max` on a cloned rules object.\n * @param {object} rules\n * @returns {object}\n */\n _expandRange(rules) {\n if (!rules[\"@rules\"] || !rules[\"@rules\"].range) return rules;\n const parsed = parseRange(rules[\"@rules\"].range);\n if (!parsed) return rules;\n // Clone to avoid mutating the shared rules object\n return {\n ...rules,\n \"@rules\": {\n ...rules[\"@rules\"],\n min: rules[\"@rules\"].min !== undefined ? rules[\"@rules\"].min : String(parsed.min),\n max: rules[\"@rules\"].max !== undefined ? rules[\"@rules\"].max : String(parsed.max),\n },\n };\n }\n\n /**\n * Check `min`, `max`, and `range` bounds for a date field.\n * Only runs after the value has already passed `validateDate` (i.e. it is a valid date).\n * Explicit `min`/`max` take precedence over `range` shorthand.\n * @param {object} rules\n * @param {string} val\n * @param {string} path\n */\n _checkDateBounds(rules, val, path) {\n if (isNaN(Date.parse(val))) return; // invalid date — type check already reported it\n const r = rules[\"@rules\"];\n\n let min = r.min;\n let max = r.max;\n\n // Expand range shorthand only for the parts not already set explicitly\n if (r.range && (!min || !max)) {\n const parsed = parseDateRange(r.range);\n if (parsed) {\n if (!min) min = parsed.min;\n if (!max) max = parsed.max;\n }\n }\n\n if (min !== undefined && !validations.date.min(min, val)) {\n this.setInvalidValueError(\"min\", path, val, min);\n }\n if (max !== undefined && !validations.date.max(max, val)) {\n this.setInvalidValueError(\"max\", path, val, max);\n }\n }\n\n /**\n * @param {object} rules\n * @param {string} newpath\n */\n validateMandatoryFields(rules, newpath) {\n const keys = Object.keys(rules);\n for (let i = 0; i < keys.length; i++) {\n if (keys[i] === \"@rules\") continue;\n const child = rules[keys[i]];\n const rulesForChild = child[\"@rules\"];\n if (rulesForChild) {\n if (rulesForChild.minOccurs && rulesForChild.minOccurs > 0) {\n this.failures.push({\n code: \"missing\",\n path: (newpath + \".\" + keys[i]).substr(1),\n });\n } else if (rulesForChild.nillable !== undefined && rulesForChild.nillable === \"false\") {\n this.failures.push({\n code: \"missing\",\n path: (newpath + \".\" + keys[i]).substr(1),\n });\n }\n }\n }\n }\n\n /**\n * @param {any} tag\n * @returns {boolean}\n */\n isMapType(tag) {\n if (typeof tag === \"string\") return false;\n const keys = Object.keys(tag);\n if (tag[\"@rules\"]) {\n if (keys.length === 1) return false;\n } else if (keys.length === 0) {\n return false;\n } else {\n return true;\n }\n }\n\n setInvalidValueError(rule, newpath, actual, expected) {\n this.failures.push({\n code: rule,\n path: newpath.substr(1),\n actual,\n expected,\n });\n }\n\n checkOccurences(rules, actual, newpath) {\n [\"minOccurs\", \"maxOccurs\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.list[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n }\n\n checkNumeric(rules, actual, newpath) {\n [\"min\", \"max\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.num[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n }\n\n checkString(rules, actual, newpath) {\n [\"minLength\", \"maxLength\", \"length\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = Number(rules[rule]);\n if (!validations.string[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n [\"fixed\", \"in\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n const expected = rules[rule];\n if (!validations.string[rule](expected, actual)) {\n this.setInvalidValueError(rule, newpath, actual, expected);\n }\n }\n });\n [\"pattern\", \"pattern_i\", \"pattern_im\", \"pattern_mi\"].forEach((rule) => {\n if (rules[rule] !== undefined) {\n let modifier = \"\";\n if (rule.length > 8) modifier = rule.substring(8);\n const expected = rules[rule];\n if (!validations.string[\"pattern\"](expected, actual, modifier)) {\n this.setInvalidValueError(\"pattern\", newpath, actual, expected);\n }\n }\n });\n }\n\n assertValue(rules, eleType, actual, newpath) {\n if (eleType === \"string\") this.checkString(rules, actual, newpath);\n else if (eleType === \"num\") this.checkNumeric(rules, actual, newpath);\n }\n\n validateBoolean(actual, eleType, newpath) {\n if (this.options.boolean.indexOf(actual) === -1) {\n this.setInvalidDataType(eleType, newpath, actual);\n }\n }\n\n isValidNum(eleType, val) {\n const num = toNumber(val);\n if (!isNaN(num)) {\n if (\n (eleType === \"positiveInteger\" && num < 0) ||\n (eleType === \"positiveDecimal\" && num < 0) ||\n (eleType === \"integer\" && !isInt(num))\n ) {\n return false;\n }\n } else {\n return false;\n }\n return true;\n }\n\n validateDate(val, eleType, newpath) {\n const date = Date.parse(val);\n if (isNaN(date)) this.setInvalidDataType(eleType, newpath, val);\n }\n\n setInvalidDataType(eleType, newpath, val) {\n this.failures.push({\n code: \"not a \" + eleType,\n path: newpath.substr(1),\n value: val,\n });\n }\n\n /**\n * @param {object} sets\n * @param {object} rules\n * @param {string} path\n */\n checkMissingSiblings(sets, rules, path) {\n if (sets.rules) {\n sets.rules.forEach((tagRule) => {\n const rulesNode = rules[tagRule][\"@rules\"];\n if (rulesNode && (rulesNode.nillable === \"false\" || rulesNode.minOccurs > 0)) {\n this.failures.push({\n code: \"missing\",\n path: (path + \".\" + tagRule).substr(1),\n });\n }\n });\n }\n }\n\n /**\n * @param {object} sets\n * @param {string} path\n */\n checkUnknownSiblings(sets, path) {\n if (sets.data && !this.options.unknownAllow) {\n sets.data.forEach((tag) => {\n this.failures.push({\n code: \"unknown\",\n path: (path + \".\" + tag).substr(1),\n });\n });\n }\n }\n}\n\n// ─── Traverser subclass that adds repeatable-collection uniqueness ─────────────\n// We override the array-handling branch of traverse() to also run sibling uniqueness.\n\nconst _originalTraverse = Traverser.prototype.traverse;\n\nTraverser.prototype.traverse = function traverse(ele, key, rules, path) {\n if (\n Array.isArray(ele) &&\n rules !== undefined &&\n rules[\"@rules\"] !== undefined &&\n rules[\"@rules\"].repeatable !== undefined\n ) {\n // Run the original logic (occurrence checks + per-item traversal)\n this.checkOccurences(rules[\"@rules\"], ele.length, path);\n ele.forEach((val, index) => {\n const arrayPath = path + \"[\" + index + \"]\";\n if (Object.keys(rules).length > 1) {\n this.callForCommonProperties(ele[index], rules, arrayPath);\n } else {\n // Simple scalar repeatable\n const scalarRules = rules;\n _originalTraverse.call(this, ele[index], key, scalarRules, arrayPath);\n }\n });\n\n // After all items processed, check sibling-scoped uniqueness\n if (ele.length > 0 && typeof ele[0] === \"object\") {\n this._runSiblingUniqueness(ele, rules, path);\n }\n return;\n }\n\n // For everything else, delegate to the original implementation\n _originalTraverse.call(this, ele, key, rules, path);\n};\n\n/**\n * Scan child field rules of a repeatable element for `unique=\"true\"` and\n * validate all occurrences within the array scope.\n * @param {Array<object>} items\n * @param {object} rules - rules for the repeatable element\n * @param {string} path\n */\nTraverser.prototype._runSiblingUniqueness = function (items, rules, path) {\n const fieldKeys = Object.keys(rules).filter((k) => k !== \"@rules\");\n fieldKeys.forEach((fieldKey) => {\n const fieldRules = rules[fieldKey];\n if (!fieldRules || !fieldRules[\"@rules\"]) return;\n if (fieldRules[\"@rules\"].unique !== \"true\") return;\n this._checkUniqueInCollection(items, fieldKey, path);\n });\n};\n\nfunction isInt(num) {\n return (num | 0) === num;\n}\n","import XMLParser, { ParseError } from \"@nodable/flexible-xml-parser\";\nimport { CompactBuilderFactory } from \"@nodable/compact-builder\";\nimport { OrderTrackingBuilderFactory } from \"./OrderTrackingBuilder.js\";\nimport { Traverser } from \"./Traverser.js\";\n\nconst defaultOptions = {\n unknownAllow: true,\n boolean: [\"true\", \"false\"],\n};\n\n// Keep every tag/attribute value as a raw, untouched string — Traverser and\n// validations.js do their own number/date/boolean coercion, so no parser\n// pipeline should get there first.\nconst RAW_VALUE_PARSERS = { tags: { valueParsers: [] }, attributes: { valueParsers: [] } };\n\n// Parsers are stateless w.r.t. any single Validator instance and safe to\n// reuse across parses (each .parse() call gets a fresh internal tokenizer\n// and a fresh builder instance from the factory) — building them once here\n// avoids rebuilding the value-parser pipelines on every Validator/validate() call.\nconst ruleParser = new XMLParser({\n skip: { attributes: false },\n attributes: { prefix: \"\", groupBy: \"@rules\", booleanType: true },\n OutputBuilder: new CompactBuilderFactory(RAW_VALUE_PARSERS),\n});\n\nconst dataParser = new XMLParser({\n skip: { attributes: false },\n attributes: { prefix: \"\", groupBy: \":a\" },\n OutputBuilder: new OrderTrackingBuilderFactory(RAW_VALUE_PARSERS),\n});\n\nexport class Validator {\n /**\n * @param {string} rules - XML string containing rule definitions\n * @param {object} [options]\n */\n constructor(rules, options) {\n assertXmlString(rules);\n this.rules = restoreAttrsBucketKeys(parseOrThrow(ruleParser, sanitizeAttrsBucketTag(rules)));\n this.options = Object.assign({}, defaultOptions, options);\n this.validators = {};\n /** @type {object|null} Parsed XML data after the last validate() call */\n this.data = null;\n }\n\n /**\n * Register a custom validator function.\n * @param {string} name\n * @param {(value: string, path: string) => object | undefined} fn\n */\n register(name, fn) {\n this.validators[name] = fn;\n }\n\n /**\n * Validate XML data against the rules.\n * @param {string} xmldata\n * @returns {Array<object>} Array of failure objects (empty if valid)\n */\n validate(xmldata) {\n assertXmlString(xmldata);\n\n const { data, siblingOrder } = parseOrThrow(dataParser, xmldata);\n this.data = data;\n\n const traverser = new Traverser(this.options, this.validators, siblingOrder);\n traverser.traverse(data, \"\", this.rules, \"\");\n return traverser.failures;\n }\n}\n\n// Rules files may define a tag literally named \":a\" — a stand-in element\n// holding validation rules for a tag's attributes, mirroring the \":a\" key\n// data gets grouped under. A leading colon is not a legal tag name under\n// namespace-aware naming rules, which the new parser enforces and the old\n// one didn't. Rather than force every existing rules file to change, swap\n// it for a legal placeholder before parsing and rename the resulting object\n// key straight back — invisible to anyone authoring or consuming rules.\nconst ATTRS_BUCKET_TAG = \":a\";\nconst ATTRS_BUCKET_PLACEHOLDER = \"__nodableAttrsBucket__\";\nconst ATTRS_BUCKET_TAG_RE = /<(\\/?):a(\\s|>|\\/)/g;\n\nfunction sanitizeAttrsBucketTag(rules) {\n return rules.replace(ATTRS_BUCKET_TAG_RE, `<$1${ATTRS_BUCKET_PLACEHOLDER}$2`);\n}\n\nfunction restoreAttrsBucketKeys(value) {\n if (Array.isArray(value)) {\n for (const item of value) restoreAttrsBucketKeys(item);\n } else if (value !== null && typeof value === \"object\") {\n if (Object.prototype.hasOwnProperty.call(value, ATTRS_BUCKET_PLACEHOLDER)) {\n value[ATTRS_BUCKET_TAG] = value[ATTRS_BUCKET_PLACEHOLDER];\n delete value[ATTRS_BUCKET_PLACEHOLDER];\n }\n for (const key of Object.keys(value)) restoreAttrsBucketKeys(value[key]);\n }\n return value;\n}\n\nfunction assertXmlString(xmldata) {\n if (!xmldata) throw new Error(\"Empty data\");\n if (typeof xmldata !== \"string\") throw new Error(\"Not a valid string\");\n}\n\n/**\n * Runs a shared parser instance and turns a syntax-level ParseError into the\n * same plain \"message:line\" Error shape dxv has always thrown. Line is not\n * provided by the parser (position is index-only) so it's derived by\n * counting newlines up to the error's character offset.\n *\n * One error code gets its wording translated back to dxv's pre-migration\n * text, so existing callers matching on that exact message keep working\n * without forcing that wording onto every other FXP consumer.\n */\nfunction parseOrThrow(parser, xmldata) {\n try {\n return parser.parse(xmldata);\n } catch (err) {\n if (err instanceof ParseError) {\n let message = err.message;\n if (err.code === \"INVALID_ATTRIBUTE_NAME\") {\n const name = message.slice(message.indexOf(\": \") + 2);\n message = `Attribute '${name}' is an invalid name.`;\n }\n // Not every ParseError carries a position (position is index-only,\n // and not every code attaches one) — don't fabricate a line number\n // when it's missing.\n if (err.index === undefined) throw new Error(message);\n const line = xmldata.substring(0, err.index).split(\"\\n\").length;\n throw new Error(message + \":\" + line);\n }\n throw err;\n }\n}\n\nexport default Validator;\n"],"names":["root","factory","exports","module","define","amd","this","__webpack_require__","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","ValidationError","_Error","message","code","line","col","_this","name","constructor","Error","captureStackTrace","_wrapNativeSuper","isWhiteSpace","char","isIllegalControlCode","getLineNumberForPosition","xmlData","index","lines","substring","split","length","getPositionFromMatch","match","startIndex","nameStartChar10","nameStartChar11","nameChar11","buildRegexes","startChar","flags","ncNamePat","replace","RegExp","ncName","qName","nmToken","nmTokens","regexes10","regexes11","nameStartCharAscii","regexesAscii","getRegexes","xmlVersion","asciiOnly","str","test","PRODUCTIONS","createValidator","production","maxCacheSize","includes","TypeError","join","regex","cache","Map","validator","cached","undefined","result","size","set","reset","XmlNameValidator","_proto","isValidQName","isValidName","isWordChar","ch","XmlDeclarationValidator","options","throwError","optional","argPosition","argPostion","_throwError","validate","startsWith","version","end","piEnd","indexOf","ALLOWED","_scanDeclAttributes","declBody","matches","leftover","len","i","matchStart","p","nameStart","slice","q","quote","valueStart","closeIdx","push","scanDeclAttributes","declAttrs","lastAllowedIdx","m","attrName","attrValue","attrPos","allowedIdx","trimmedLeftover","trim","ControlCharValidator","findIllegalIndex","charCodeAt","hasIllegalChar","describe","toString","padStart","AttributeStringValidator","nameValidator","attrStr","_this$options$invalid","tokens","tokenStart","leadingWs","equalsGroup","j","quoteChar","k","token","scanAttributeTokens","attrNames","checkAttrLt","invalidCharSequence","attrLt","_err","hasEquals","hasQuotedValue","allowBooleanAttributes","illegalIdx","msg","err","CommentCdataReader","_options$invalidCharS","checkCommentDashes","comment","read","docTypeValidator","_readComment","validateDocType","_readCdata","bodyStart","body","Expression","pattern","data","separator","segments","_parse","_hasDeepWildcard","some","seg","type","_hasAttributeCondition","_hasPositionSelector","position","currentPart","_parseSegment","part","segment","bracketContent","withoutBrackets","bracketMatch","content","namespace","tag","tagAndPosition","nsIndex","positionMatch","colonIndex","lastIndexOf","tagPart","posPart","eqIndex","nthMatch","positionValue","parseInt","hasDeepWildcard","hasAttributeCondition","hasPositionSelector","MatcherView","matcher","_matcher","getCurrentTag","path","getCurrentNamespace","getAttrValue","values","hasAttr","current","getAnyParentAttr","hasAnyParentAttr","getPosition","getCounter","counter","getIndex","getDepth","includeNamespace","toArray","map","n","expression","matchesAny","exprSet","Matcher","siblingStacks","_pathStringCache","_view","_keptAttrs","tagName","attrValues","currentLevel","level","counts","total","siblingKey","node","depth","keep","pop","poppedDepth","updateCurrent","kept","sep","_matchWithDeepWildcard","_matchSimple","_matchSegment","pathIdx","segIdx","nextSeg","found","isCurrentNode","String","snapshot","keptAttrs","entry","restore","readOnly","_defineProperties","e","r","t","configurable","writable","_toPropertyKey","toPrimitive","_toPrimitive","SkipTagMatcher","skipTags","_expressions","expr","_enabled","rawAttributes","RawSubtreeReader","consume","needle","searchFrom","idx","defaultOptions","maxEntityCount","Infinity","maxEntitySize","DocTypeValidator","assign","suppressValidationErr","angleBracketsCount","hasBody","entityCount","hasSeq","_this$_validateEntity","_validateEntityExp","_validateElementExp","_validateAttlistExp","_validateNotationExp","skipWhitespace","entityValue","entityName","validateName","toUpperCase","_readQuotedValue","readQuotedValue","_this$options","notationName","identifierType","elementName","isXmlName","elemNameStart","attrNameStart","notationStart","typeStart","attributeType","seq","start","unpairedTags","docType","tagValue","xmlDeclaraion","multipleRoots","SyntaxValidator","defaults","merged","xmlDeclaration","mergeOptions","tags","tagFound","reachedRoot","docTypeSeen","substr","lineNumber","declResult","commentCdataReader","attrValidator","skipMatcher","checkTagValueSeq","checkDocTypePlacement","startBang","checkTopLevelText","pos","checkCdataPlacement","_readPI","tagStartPos","closingTag","_readAttributeStr","attrStrStart","isValid","tagClosed","otg","openPos","skip","prevTwo","afterAmp","_validateAmpersand","JSON","stringify","piStart","nameValidated","piName","toLowerCase","_validateNumberAmpersand","re","count","ExpressionSet","_byDepthAndTag","_wildcardByDepth","_deepWildcards","_deepByTerminalTag","_patterns","Set","_sealed","add","has","lastSeg","addAll","expressions","seal","isSealed","findMatch","exactKey","exactBucket","wildcardBucket","deepBucket","nameFor","valueParsers","attributes","textJoint","alwaysArray","forceArray","forceTextNode","defaultTagParsers","defaultAttrParsers","buildOptions","finalOptions","deepClone","alwaysArraySource","Array","isArray","alwaysArraySet","normalizeEntry","_alwaysArraySet","copyProperties","clone","keys","target","source","optionName","XML","amp","apos","gt","lt","quot","ENTITY_ACTION","freeze","ALLOW","BLOCK","THROW","SPECIAL_CHARS","validateEntityName","mergeEntityMaps","maps","out","create","raw","val","LIMIT_TIER_EXTERNAL","LIMIT_TIER_BASE","LIMIT_TIER_ALL","NCR_LEVEL","allow","leave","remove","throw","XML10_ALLOWED_C0","EntityDecoder","_limit","limit","_maxTotalExpansions","maxTotalExpansions","_maxExpandedLength","maxExpandedLength","_postCheck","postCheck","_limitTiers","applyLimitsTo","_numericAllowed","numericAllowed","_baseMap","DEFAULT_XML_ENTITIES","namedEntities","_externalMap","_inputMap","_totalExpansions","_expandedLength","_removeSet","_leaveSet","ncrCfg","ncr","onLevel","nullLevel","onNCR","nullNCR","Math","max","parseNCRConfig","_ncrXmlVersion","_ncrOnLevel","_ncrNullLevel","_onExternalEntity","onExternalEntity","_onInputEntity","onInputEntity","_applyRegistrationHook","hook","context","action","setExternalEntities","flat","filtered","entries","addExternalEntity","addInputEntities","setXmlVersion","decode","original","chunks","last","limitExpansions","limitLength","checkLimits","replacement","tier","ncrResult","_resolveNCR","resolved","_resolveName","_tierCounts","delta","_classifyNCR","cp","_applyNCRAction","fromCodePoint","second","Number","isNaN","minimum","effective","id","description","CONTEXT_REGISTRY","HTML","SVG","SQL","SQL_PATTERNS","SHELL","REDOS","NOSQL","LOG","VALID_CONTEXTS","fromEntries","matchContext","contextName","patterns","rule","isUnsafe","assertString","c","assertContext","BaseValueParser","IS_FINAL","init","ctx","parse","runTimeContext","SCRIPT_ZEROS","HIGH_MAP","TABLE_OFFSET","TABLE","Uint8Array","LOW_MAX","fill","zero","d","MINUS_SET","hexRegex","binRegex","octRegex","numRegex","consider","hex","binary","octal","leadingZeros","decimalPoint","eNotation","infinity","unicode","toNumber","trimmedStr","skipLike","firstHit","cc","low","chars","fromCharCode","anynum","parse_int","isFinite","notation","eNotationRegx","sign","eChar","eAdjacentToLeadingZeros","resolveEnotation","exec","numTrimmedByZeros","numStr","decimalAdjacentToLeadingZeros","num","parsedStr","search","isPositive","handleInfinity","base","window","numParser","isFinal","super","newval","FinalValue","defaultValParsers","entities","exclude","_excludeSet","isAttribute","trueList","falseList","temp","numberParser","ValueParserRegistry","registered","register","parser","resetAll","vp","ret","Context","isLeafNode","SharedContext","clear","ValueParserPipeline","valParsers","registry","sharedContext","_initAll","run","runtimContext","instance","instances","seen","parserInstance","DEFAULT_TAG_PARSERS","DEFAULT_ATTR_PARSERS","BaseOutputBuilder","parserOptions","builderOptions","matcherView","resetPipelines","_rootName","tagChain","attrChain","tagsPipeline","attrsPipeline","_pendingStopNode","addAttribute","prefixed","prefix","suffix","_addChild","addComment","text","addLiteral","cdata","addRawValue","addValue","addDeclaration","addInstruction","onStopNode","tagDetail","rawContent","onExit","exitInfo","BaseOutputBuilderFactory","registerValueParser","getInstance","CompactBuilderFactory","readonlyMatcher","CompactBuilder","tagsStack","parent","textValue","hasAttributes","_buildAttributeValue","isEmpty","groupBy","addElement","_resolveForceArray","alwaysVote","forceVote","closeElement","parsedText","resultTag","arr","parentTag","shouldForceArray","_addChildTo","getOutput","ParseError","_posStr","ErrorCode","INVALID_INPUT","INVALID_STREAM","ALREADY_STREAMING","NOT_STREAMING","DATA_MUST_BE_STRING","UNEXPECTED_END","UNEXPECTED_CLOSE_TAG","MISMATCHED_CLOSE_TAG","UNEXPECTED_TRAILING_DATA","INVALID_TAG","UNCLOSED_QUOTE","INVALID_ATTRIBUTE_NAME","MULTIPLE_NAMESPACES","ILLEGAL_CHARACTER","DUPLICATE_ATTRIBUTE","UNQUOTED_ATTRIBUTE_VALUE","BOOLEAN_ATTRIBUTE_REJECTED","SECURITY_PROTOTYPE_POLLUTION","SECURITY_RESERVED_OPTION","SECURITY_RESTRICTED_NAME","LIMIT_MAX_NESTED_TAGS","LIMIT_MAX_ATTRIBUTES","ENTITY_MAX_COUNT","ENTITY_MAX_SIZE","ENTITY_MAX_EXPANSIONS","ENTITY_MAX_EXPANDED_LENGTH","ENTITY_INVALID_KEY","ENTITY_INVALID_VALUE","UNSUPPORTED_ENCODING","INVALID_DECODER","ENCODING_MISMATCH","isSpace","isSpaceCode","DANGEROUS_PROPERTY_NAMES","criticalProperties","absolutePosition","_baseOffset","errorPositionOf","sanitizeContent","hasCR","segStart","expectMatch","expected","errorMsg","caseInsensitive","canRead","matchAhead","updateBufferBoundary","ensureCanRead","defaultOnDangerousProperty","declaration","pi","nsPrefix","whitespaceText","booleanType","duplicate","unpaired","stopNodes","strictReservedNames","onDangerousProperty","sanitizeNames","only","doctypeOptions","enabled","autoClose","limits","maxNestedTags","maxAttributesPerTag","feedable","maxBufferSize","autoFlush","flushThreshold","bufferSize","exitIf","OutputBuilder","decoding","encoding","customDecoders","ALL_RESERVED","validatePropertyName","isInteger","stopSet","normalizeTagEntry","stopNodesSet","skipSet","tagsSet","opts","existingUnpaired","HTML_VOID_ELEMENTS","onEof","onMismatch","collectErrors","resolveAutoClose","nested","skipEnclosures","scanTagExpEnd","buf","buffer","pairs","_quotePairs","capacity","pairsLen","inSingle","inDouble","_quotePairsLen","scanTagExpEndFast","StringSource","_marks","Int32Array","markTokenStart","rewindToMark","clearMark","flush","origin","marksLen","readCh","readChAt","readStr","from","readUpto","stopStr","inputLength","stopLength","readUptoChar","stopChar","readUptoCloseTag","tagMatchStart","state","matched","readFromBuffer","updateIndex","createByteScanStrategy","decodeCharAt","nodeEncoding","isContinuationByte","width","b","stopBuffer","Buffer","stopCode","shouldUpdate","_tokenStart","decodeCharAtFixedWidth1","decodeCharAtUtf8","b0","DEFAULT_SCAN_STRATEGY","BufferSource","bytesArr","profile","bomLength","subarray","decodeFirst","decoder","descriptor","createDecoder","write","strategy","scanStrategy","encodingName","_quotePairsUsable","quotePairsUsable","sniff","bytes","bomMatch","bomCandidates","sig","bomBytes","equals","matchBom","declaredEncoding","offset","declMatch","min","encMatch","sniffDeclaration","a","resolve","sameEncoding","createTextDecoderAdapter","label","TextDecoder","fatal","ignoreBOM","stream","EncodingRegistry","_byName","_seedDefaults","aliases","selfSynchronizing","variableWidth","inner","pending","work","concatBytes","swapped","tmp","swapBytePairs","createUtf16BeAdapter","probe","alias","sort","defaultEncodingRegistry","isIllegalAttrCode","scanAttrValue","flushAttributes","parsedAttrs","attrsExpStart","rawAttrMatchCount","maxAttrs","currentTagDetail","attrMeta","outputBuilder","TagExp","selfClosing","_attrsExp","_attrsExpStart","_rawAttrMatchCount","_parsedAttrs","readTagExp","expStart","collectQuotes","relEnd","exp","usableQuotes","buildTagExpObj","forceToReadAttrs","quotePairs","quotePairsLen","tagExp","attrsLocalOffset","attrsExp","trimEnd","INVALID_TAG_NAME","attrsOffset","results","usePairs","pairIdx","closeLocal","parseAttributes","dupMode","boolMode","rawVal","processAttrName","attrVal","rawAttributesLen","collectRawAttributes","StopNodeProcessor","_tagName","_nested","_enclosures","_content","_depth","_active","isActive","activate","resumeAfterOpenTag","collect","enclosuresLen","_collectDepthOnly","_collectEnclosureOnly","_collectFull","_collectPlain","_tryConsumeCloseTag","_finish","_unclosedError","_stepDepthTracking","_trySkipEnclosure","encIdx","_matchEnclosureOpen","enc","_skipChars","open","interior","_readUpto","close","needed","_peekMatch","validClose","closeName","_readTagName","closeSuffix","_readToAngleClose","openName","attrText","_readTagTail","s0","sLen","strLen","readPiTag","skipOptions","inSingleQuotes","inDoubleQuotes","EOE","currentChar","nextChar","readPiExp","xmlDec","standalone","_nameValidators","readEntityExp","skipSourceWhitespace","entityNameStart","entityNameLen","readIdentifierVal","ep","readElementExp","elementNameStart","elementNameLen","getNameValidator","peek1","contentModel","readAttlistExp","readNotationExp","notationNameStart","notationNameLen","next","AutoCloseErrorType","UNCLOSED_EOF","MISMATCHED_CLOSE","PHANTOM_CLOSE","PARTIAL_TAG","AutoCloseHandler","autoCloseOptions","errors","handleEof","parserState","addTextNode","popTag","_recordError","handleMismatch","closingTagName","stackSnapshot","matchIndex","stackSnapshotLength","levelsToClose","handlePartialTag","originalError","_extractPartialTagName","tagTextData","getErrors","detail","closeMatch","keepSpace","getCachedName","rawName","computeFn","TagDetail","openEnd","Xml2JsParser","doctypeFound","autoCloseHandler","_unpairedSet","stopNodeExpressionsSet","isStopNode","skipTagExpressionsSet","isSkipTag","_exitIf","_nameCache","attrs","initializeParser","_stopNodeProcessor","_exitIfTriggered","_validQNames","lang","_createOutputBuilder","wasExited","strData","_parseAndFinalize","parseBytesArr","_registry","decodingOptions","requested","detected","buildProfileForBuffer","parseXml","preReadPos","tagStart","readClosingTag","readSpecialTag","readOpeningTag","runLen","finalizeXml","hasOpenTags","hasTrailingText","_parserState","partialTagError","isSourceExhaustedError","consumed","expectedRawName","tryMatchClosingTagName","closeMeta","closeEnd","processTagName","partial","MAX_SAFE_INTEGER","readClosingTagName","isUnpaired","isSkip","_stopNodeProcessorMeta","stopEnd","processedTagName","colonIdx","tagNamespace","matcherTagName","maxNested","raeAttrLen","stopNodeConfig","skipTagConfig","_closeMetaFor","exitDepth","pushTag","startCh","readComment","readCdata","docTypeEntities","bodyDone","subTagStart","bang","typeChar","dash2","typeChar2","escaped","regx","readDocType","ok","rawAttrName","resolveNsPrefix","sanitizeName","rawTagName","self","v","bind","skipNsPrefix","parts","FeedableSource","isComplete","_decodingOptions","requestedEncoding","_detecting","_sniffBuffer","alloc","_createDecoder","_decoder","feed","chunk","isBuffer","concat","declarationComplete","_resolveDetection","newData","_decodeNow","liveBytes","held","tail","StreamSource","attachStream","readable","onChunk","onEnd","onError","on","XMLParser","_feedParser","_feedSource","_isFeeding","_pendingBytes","_batchThreshold","ArrayBuffer","isView","_createParser","_lastParseErrors","parseStream","readableEnded","streamParser","Promise","reject","settled","fail","destroy","parseErr","_runParse","beforePos","_initFeedSession","appendedLength","_cleanupFeedSession","getParseErrors","OrderTrackingBuilder","siblingOrder","jPath","lastDot","parentPath","siblings","NumberValueParser","parsed","NaN","string","minLength","actual","maxLength","modifier","fixed","in","list","minOccurs","maxOccurs","date","Date","numericTypes","Traverser","validators","failures","_globalUnique","_siblingOrder","traverse","ele","rules","repeatable","checkOccurences","forEach","arrayPath","callForCommonProperties","checkDataAndType","isMapType","validateMandatoryFields","applyCustomValidators","checkBy","res","sets","extra","common","breakInSets","checkUnknownSiblings","checkMissingSiblings","siblingValues","_buildSiblingValues","newpath","_checkOrderingConstraints","_checkUniquenessConstraints","_checkRelationalConstraints","commonKeys","orderKey","order","fieldRules","keyIdx","after","refKey","refIdx","before","RELATIONAL_RULES","attr","actualRaw","fieldType","refRaw","cmp","_relationalCompare","violated","da","db","na","nb","unique","vals","fieldPath","_assertGlobalUnique","_checkUniqueInCollection","items","fieldKey","basePath","item","strVal","fieldName","eleType","expandedRules","_expandRange","validateDate","_checkDateBounds","validateBoolean","isValidNum","assertValue","setInvalidDataType","range","parseRange","parseDateRange","validations","setInvalidValueError","rulesForChild","nillable","checkNumeric","checkString","boolean","isInt","tagRule","rulesNode","unknownAllow","_originalTraverse","scalarRules","_runSiblingUniqueness","filter","RAW_VALUE_PARSERS","ruleParser","dataParser","ATTRS_BUCKET_PLACEHOLDER","ATTRS_BUCKET_TAG_RE","restoreAttrsBucketKeys","assertXmlString","xmldata","parseOrThrow","sanitizeAttrsBucketTag","fn","traverser"],"sourceRoot":""}
|