react-xml-viewer
Version:
Simple xml viewer component for React
1,486 lines • 86.8 kB
JavaScript
import { jsx as p, jsxs as A, Fragment as de } from "react/jsx-runtime";
import * as se from "react";
import { useRef as F, useEffect as M, useCallback as B, createContext as fe, useState as V, useMemo as R, useContext as pe, memo as Be } from "react";
const ie = ":@", X = "#text", ee = "#comment", te = "#cdata", ge = "DECLARATION_TAG", Xe = "TAG", U = {
tagColor: "#d43900",
textColor: "#333",
attributeKeyColor: "#2a7ab0",
attributeValueColor: "#008000",
separatorColor: "#333",
commentColor: "#aaa",
cdataColor: "#1d781d",
fontFamily: "monospace",
lineNumberBackground: "#eee",
lineNumberColor: "#222"
};
function me(n, e) {
const t = F(n), r = F();
return M(() => {
t.current = n;
}, [n]), M(() => () => {
r.current && clearTimeout(r.current);
}, []), B(
(...s) => {
r.current && clearTimeout(r.current), r.current = setTimeout(() => {
t.current(...s);
}, e);
},
[e]
);
}
const Ue = {
lines: {},
push: () => {
}
}, be = fe(Ue), We = ({ children: n, enabled: e = !1 }) => {
const [t, r] = V({}), s = F({}), i = B(() => {
r(s.current);
}, []), a = me(i, 100), o = B((h, u) => {
var c;
e && (((c = s.current[h]) == null ? void 0 : c.element) !== u.element && (s.current[h] = u), a());
}, []), l = R(() => ({ lines: t, push: o }), [t, o, e]);
return /* @__PURE__ */ p(be.Provider, { value: l, children: n });
}, ye = () => pe(be), ze = {
theme: U,
collapsible: !1,
indentSize: 2
}, Ee = fe(ze), _ = () => pe(Ee);
function Ye(n, e) {
return new Array(e * n + 1).join(" ");
}
function Ne(n) {
return typeof n == "string" && n.includes(`
`);
}
function Ke(n) {
return typeof n == "string" ? !1 : n.length === 1 && X in n[0] && !Ne(n[0][X]);
}
function Ge(n) {
switch (n) {
case X:
case te:
case ee:
return n;
default:
return n.startsWith("?") ? ge : Xe;
}
}
function qe(n, e) {
const t = {};
for (const r in n)
r !== e && (t[r] = n[r]);
return t;
}
function Ze(n) {
const e = n[ie], t = qe(n, ie), [[r, s]] = Object.entries(t), i = Ge(r);
return { attributes: e, tagKey: r, subElements: s, type: i };
}
function Je(n, e) {
return Object.keys(U).every((r) => n[r] === e[r]);
}
const we = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD", Qe = we + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040", He = "[" + we + "][" + Qe + "]*", De = new RegExp("^" + He + "$");
function Te(n, e) {
const t = [];
let r = e.exec(n);
for (; r; ) {
const s = [];
s.startIndex = e.lastIndex - r[0].length;
const i = r.length;
for (let a = 0; a < i; a++)
s.push(r[a]);
t.push(s), r = e.exec(n);
}
return t;
}
const Ce = function(n) {
const e = De.exec(n);
return !(e === null || typeof e > "u");
};
function et(n) {
return typeof n < "u";
}
const ne = [
// '__proto__',
// 'constructor',
// 'prototype',
"hasOwnProperty",
"toString",
"valueOf",
"__defineGetter__",
"__defineSetter__",
"__lookupGetter__",
"__lookupSetter__"
], _e = ["__proto__", "constructor", "prototype"], tt = {
allowBooleanAttributes: !1,
//A tag can have attributes without any value
unpairedTags: []
};
function ve(n, e) {
e = Object.assign({}, tt, e);
const t = [];
let r = !1, s = !1;
n[0] === "\uFEFF" && (n = n.substr(1));
for (let i = 0; i < n.length; i++)
if (n[i] === "<" && n[i + 1] === "?") {
if (i += 2, i = ae(n, i), i.err)
return i;
} else if (n[i] === "<") {
let a = i;
if (i++, n[i] === "!") {
i = le(n, i);
continue;
} else {
let o = !1;
n[i] === "/" && (o = !0, i++);
let l = "";
for (; i < n.length && n[i] !== ">" && n[i] !== " " && n[i] !== " " && n[i] !== `
` && n[i] !== "\r"; i++)
l += n[i];
if (l = l.trim(), l[l.length - 1] === "/" && (l = l.substring(0, l.length - 1), i--), !ut(l)) {
let c;
return l.trim().length === 0 ? c = "Invalid space after '<'." : c = "Tag '" + l + "' is an invalid name.", b("InvalidTag", c, E(n, i));
}
const h = st(n, i);
if (h === !1)
return b("InvalidAttr", "Attributes for '" + l + "' have open quote.", E(n, i));
let u = h.value;
if (i = h.index, u[u.length - 1] === "/") {
const c = i - u.length;
u = u.substring(0, u.length - 1);
const d = ue(u, e);
if (d === !0)
r = !0;
else
return b(d.err.code, d.err.msg, E(n, c + d.err.line));
} else if (o)
if (h.tagClosed) {
if (u.trim().length > 0)
return b("InvalidTag", "Closing tag '" + l + "' can't have attributes or invalid starting.", E(n, a));
if (t.length === 0)
return b("InvalidTag", "Closing tag '" + l + "' has not been opened.", E(n, a));
{
const c = t.pop();
if (l !== c.tagName) {
let d = E(n, c.tagStartPos);
return b(
"InvalidTag",
"Expected closing tag '" + c.tagName + "' (opened in line " + d.line + ", col " + d.col + ") instead of closing tag '" + l + "'.",
E(n, a)
);
}
t.length == 0 && (s = !0);
}
} else
return b("InvalidTag", "Closing tag '" + l + "' doesn't have proper closing.", E(n, i));
else {
const c = ue(u, e);
if (c !== !0)
return b(c.err.code, c.err.msg, E(n, i - u.length + c.err.line));
if (s === !0)
return b("InvalidXml", "Multiple possible root nodes found.", E(n, i));
e.unpairedTags.indexOf(l) !== -1 || t.push({ tagName: l, tagStartPos: a }), r = !0;
}
for (i++; i < n.length; i++)
if (n[i] === "<")
if (n[i + 1] === "!") {
i++, i = le(n, i);
continue;
} else if (n[i + 1] === "?") {
if (i = ae(n, ++i), i.err)
return i;
} else
break;
else if (n[i] === "&") {
const c = at(n, i);
if (c == -1)
return b("InvalidChar", "char '&' is not expected.", E(n, i));
i = c;
} else if (s === !0 && !oe(n[i]))
return b("InvalidXml", "Extra text at the end", E(n, i));
n[i] === "<" && i--;
}
} else {
if (oe(n[i]))
continue;
return b("InvalidChar", "char '" + n[i] + "' is not expected.", E(n, i));
}
if (r) {
if (t.length == 1)
return b("InvalidTag", "Unclosed tag '" + t[0].tagName + "'.", E(n, t[0].tagStartPos));
if (t.length > 0)
return b("InvalidXml", "Invalid '" + JSON.stringify(t.map((i) => i.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 });
} else
return b("InvalidXml", "Start tag expected.", 1);
return !0;
}
function oe(n) {
return n === " " || n === " " || n === `
` || n === "\r";
}
function ae(n, e) {
const t = e;
for (; e < n.length; e++)
if (n[e] == "?" || n[e] == " ") {
const r = n.substr(t, e - t);
if (e > 5 && r === "xml")
return b("InvalidXml", "XML declaration allowed only at the start of the document.", E(n, e));
if (n[e] == "?" && n[e + 1] == ">") {
e++;
break;
} else
continue;
}
return e;
}
function le(n, e) {
if (n.length > e + 5 && n[e + 1] === "-" && n[e + 2] === "-") {
for (e += 3; e < n.length; e++)
if (n[e] === "-" && n[e + 1] === "-" && n[e + 2] === ">") {
e += 2;
break;
}
} else if (n.length > e + 8 && n[e + 1] === "D" && n[e + 2] === "O" && n[e + 3] === "C" && n[e + 4] === "T" && n[e + 5] === "Y" && n[e + 6] === "P" && n[e + 7] === "E") {
let t = 1;
for (e += 8; e < n.length; e++)
if (n[e] === "<")
t++;
else if (n[e] === ">" && (t--, t === 0))
break;
} else if (n.length > e + 9 && n[e + 1] === "[" && n[e + 2] === "C" && n[e + 3] === "D" && n[e + 4] === "A" && n[e + 5] === "T" && n[e + 6] === "A" && n[e + 7] === "[") {
for (e += 8; e < n.length; e++)
if (n[e] === "]" && n[e + 1] === "]" && n[e + 2] === ">") {
e += 2;
break;
}
}
return e;
}
const nt = '"', rt = "'";
function st(n, e) {
let t = "", r = "", s = !1;
for (; e < n.length; e++) {
if (n[e] === nt || n[e] === rt)
r === "" ? r = n[e] : r !== n[e] || (r = "");
else if (n[e] === ">" && r === "") {
s = !0;
break;
}
t += n[e];
}
return r !== "" ? !1 : {
value: t,
index: e,
tagClosed: s
};
}
const it = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g");
function ue(n, e) {
const t = Te(n, it), r = {};
for (let s = 0; s < t.length; s++) {
if (t[s][1].length === 0)
return b("InvalidAttr", "Attribute '" + t[s][2] + "' has no space in starting.", O(t[s]));
if (t[s][3] !== void 0 && t[s][4] === void 0)
return b("InvalidAttr", "Attribute '" + t[s][2] + "' is without value.", O(t[s]));
if (t[s][3] === void 0 && !e.allowBooleanAttributes)
return b("InvalidAttr", "boolean attribute '" + t[s][2] + "' is not allowed.", O(t[s]));
const i = t[s][2];
if (!lt(i))
return b("InvalidAttr", "Attribute '" + i + "' is an invalid name.", O(t[s]));
if (!Object.prototype.hasOwnProperty.call(r, i))
r[i] = 1;
else
return b("InvalidAttr", "Attribute '" + i + "' is repeated.", O(t[s]));
}
return !0;
}
function ot(n, e) {
let t = /\d/;
for (n[e] === "x" && (e++, t = /[\da-fA-F]/); e < n.length; e++) {
if (n[e] === ";")
return e;
if (!n[e].match(t))
break;
}
return -1;
}
function at(n, e) {
if (e++, n[e] === ";")
return -1;
if (n[e] === "#")
return e++, ot(n, e);
let t = 0;
for (; e < n.length; e++, t++)
if (!(n[e].match(/\w/) && t < 20)) {
if (n[e] === ";")
break;
return -1;
}
return e;
}
function b(n, e, t) {
return {
err: {
code: n,
msg: e,
line: t.line || t,
col: t.col
}
};
}
function lt(n) {
return Ce(n);
}
function ut(n) {
return Ce(n);
}
function E(n, e) {
const t = n.substring(0, e).split(/\r?\n/);
return {
line: t.length,
// column number is last line's length + 1, because column numbering starts at 1:
col: t[t.length - 1].length + 1
};
}
function O(n) {
return n.startIndex + n[1].length;
}
const ct = {
cent: "¢",
pound: "£",
curren: "¤",
yen: "¥",
euro: "€",
dollar: "$",
euro: "€",
fnof: "ƒ",
inr: "₹",
af: "؋",
birr: "ብር",
peso: "₱",
rub: "₽",
won: "₩",
yuan: "¥",
cedil: "¸"
}, Ae = {
amp: "&",
apos: "'",
gt: ">",
lt: "<",
quot: '"'
}, ht = {
nbsp: " ",
copy: "©",
reg: "®",
trade: "™",
mdash: "—",
ndash: "–",
hellip: "…",
laquo: "«",
raquo: "»",
lsquo: "‘",
rsquo: "’",
ldquo: "“",
rdquo: "”",
bull: "•",
para: "¶",
sect: "§",
deg: "°",
frac12: "½",
frac14: "¼",
frac34: "¾"
}, dt = new Set("!?\\\\/[]$%{}^&*()<>|+");
function ce(n) {
if (n[0] === "#")
throw new Error(`[EntityReplacer] Invalid character '#' in entity name: "${n}"`);
for (const e of n)
if (dt.has(e))
throw new Error(`[EntityReplacer] Invalid character '${e}' in entity name: "${n}"`);
return n;
}
function G(...n) {
const e = /* @__PURE__ */ Object.create(null);
for (const t of n)
if (t)
for (const r of Object.keys(t)) {
const s = t[r];
if (typeof s == "string")
e[r] = s;
else if (s && typeof s == "object" && s.val !== void 0) {
const i = s.val;
typeof i == "string" && (e[r] = i);
}
}
return e;
}
const P = "external", W = "base", Q = "all";
function ft(n) {
return !n || n === P ? /* @__PURE__ */ new Set([P]) : n === Q ? /* @__PURE__ */ new Set([Q]) : n === W ? /* @__PURE__ */ new Set([W]) : Array.isArray(n) ? new Set(n) : /* @__PURE__ */ new Set([P]);
}
const T = Object.freeze({ allow: 0, leave: 1, remove: 2, throw: 3 }), pt = /* @__PURE__ */ new Set([9, 10, 13]);
function gt(n) {
if (!n)
return { xmlVersion: 1, onLevel: T.allow, nullLevel: T.remove };
const e = n.xmlVersion === 1.1 ? 1.1 : 1, t = T[n.onNCR] ?? T.allow, r = T[n.nullNCR] ?? T.remove, s = Math.max(r, T.remove);
return { xmlVersion: e, onLevel: t, nullLevel: s };
}
class mt {
/**
* @param {object} [options]
* @param {object|null} [options.namedEntities] — extra named entities merged into base map
* @param {object} [options.limit] — security limits
* @param {number} [options.limit.maxTotalExpansions=0] — 0 = unlimited
* @param {number} [options.limit.maxExpandedLength=0] — 0 = unlimited
* @param {'external'|'base'|'all'|string[]} [options.limit.applyLimitsTo='external']
* Which entity tiers count against the security limits:
* - 'external' (default) — only input/runtime + persistent external entities
* - 'base' — only DEFAULT_XML_ENTITIES + namedEntities
* - 'all' — every entity regardless of tier
* - string[] — explicit combination, e.g. ['external', 'base']
* @param {((resolved: string, original: string) => string)|null} [options.postCheck=null]
* @param {string[]} [options.remove=[]] — entity names (e.g. ['nbsp', '#13']) to delete (replace with empty string)
* @param {string[]} [options.leave=[]] — entity names to keep as literal (unchanged in output)
* @param {object} [options.ncr] — Numeric Character Reference controls
* @param {1.0|1.1} [options.ncr.xmlVersion=1.0]
* XML version governing which codepoint ranges are restricted:
* - 1.0 — C0 controls U+0001–U+001F (except U+0009/000A/000D) are prohibited
* - 1.1 — C0 controls are allowed when written as NCRs; C1 (U+007F–U+009F) decoded as-is
* @param {'allow'|'leave'|'remove'|'throw'} [options.ncr.onNCR='allow']
* Base action for numeric references. Severity order: allow < leave < remove < throw.
* For codepoint ranges that carry a minimum level (surrogates → remove, XML 1.0 C0 → remove),
* the effective action is max(onNCR, rangeMinimum).
* @param {'remove'|'throw'} [options.ncr.nullNCR='remove']
* Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.
*/
constructor(e = {}) {
this._limit = e.limit || {}, this._maxTotalExpansions = this._limit.maxTotalExpansions || 0, this._maxExpandedLength = this._limit.maxExpandedLength || 0, this._postCheck = typeof e.postCheck == "function" ? e.postCheck : (r) => r, this._limitTiers = ft(this._limit.applyLimitsTo ?? P), this._numericAllowed = e.numericAllowed ?? !0, this._baseMap = G(Ae, e.namedEntities || null), this._externalMap = /* @__PURE__ */ Object.create(null), this._inputMap = /* @__PURE__ */ Object.create(null), this._totalExpansions = 0, this._expandedLength = 0, this._removeSet = new Set(e.remove && Array.isArray(e.remove) ? e.remove : []), this._leaveSet = new Set(e.leave && Array.isArray(e.leave) ? e.leave : []);
const t = gt(e.ncr);
this._ncrXmlVersion = t.xmlVersion, this._ncrOnLevel = t.onLevel, this._ncrNullLevel = t.nullLevel;
}
// -------------------------------------------------------------------------
// Persistent external entity registration
// -------------------------------------------------------------------------
/**
* Replace the full set of persistent external entities.
* All keys are validated — throws on invalid characters.
* @param {Record<string, string | { regex?: RegExp, val: string }>} map
*/
setExternalEntities(e) {
if (e)
for (const t of Object.keys(e))
ce(t);
this._externalMap = G(e);
}
/**
* Add a single persistent external entity.
* @param {string} key
* @param {string} value
*/
addExternalEntity(e, t) {
ce(e), typeof t == "string" && t.indexOf("&") === -1 && (this._externalMap[e] = t);
}
// -------------------------------------------------------------------------
// Input / runtime entity registration (per document)
// -------------------------------------------------------------------------
/**
* Inject DOCTYPE entities for the current document.
* Also resets per-document expansion counters.
* @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map
*/
addInputEntities(e) {
this._totalExpansions = 0, this._expandedLength = 0, this._inputMap = G(e);
}
// -------------------------------------------------------------------------
// Per-document reset
// -------------------------------------------------------------------------
/**
* Wipe input/runtime entities and reset counters.
* Call this before processing each new document.
* @returns {this}
*/
reset() {
return this._inputMap = /* @__PURE__ */ Object.create(null), this._totalExpansions = 0, this._expandedLength = 0, this;
}
// -------------------------------------------------------------------------
// XML version (can be set after construction, e.g. once parser reads <?xml?>)
// -------------------------------------------------------------------------
/**
* Update the XML version used for NCR classification.
* Call this as soon as the document's `<?xml version="...">` declaration is parsed.
* @param {1.0|1.1|number} version
*/
setXmlVersion(e) {
this._ncrXmlVersion = e === 1.1 ? 1.1 : 1;
}
// -------------------------------------------------------------------------
// Primary API
// -------------------------------------------------------------------------
/**
* Replace all entity references in `str` in a single pass.
*
* @param {string} str
* @returns {string}
*/
decode(e) {
if (typeof e != "string" || e.length === 0)
return e;
const t = e, r = [], s = e.length;
let i = 0, a = 0;
const o = this._maxTotalExpansions > 0, l = this._maxExpandedLength > 0, h = o || l;
for (; a < s; ) {
if (e.charCodeAt(a) !== 38) {
a++;
continue;
}
let c = a + 1;
for (; c < s && e.charCodeAt(c) !== 59 && c - a <= 32; )
c++;
if (c >= s || e.charCodeAt(c) !== 59) {
a++;
continue;
}
const d = e.slice(a + 1, c);
if (d.length === 0) {
a++;
continue;
}
let f, g;
if (this._removeSet.has(d))
f = "", g === void 0 && (g = P);
else if (this._leaveSet.has(d)) {
a++;
continue;
} else if (d.charCodeAt(0) === 35) {
const m = this._resolveNCR(d);
if (m === void 0) {
a++;
continue;
}
f = m, g = W;
} else {
const m = this._resolveName(d);
f = m == null ? void 0 : m.value, g = m == null ? void 0 : m.tier;
}
if (f === void 0) {
a++;
continue;
}
if (a > i && r.push(e.slice(i, a)), r.push(f), i = c + 1, a = i, h && this._tierCounts(g)) {
if (o && (this._totalExpansions++, this._totalExpansions > this._maxTotalExpansions))
throw new Error(
`[EntityReplacer] Entity expansion count limit exceeded: ${this._totalExpansions} > ${this._maxTotalExpansions}`
);
if (l) {
const m = f.length - (d.length + 2);
if (m > 0 && (this._expandedLength += m, this._expandedLength > this._maxExpandedLength))
throw new Error(
`[EntityReplacer] Expanded content length limit exceeded: ${this._expandedLength} > ${this._maxExpandedLength}`
);
}
}
}
i < s && r.push(e.slice(i));
const u = r.length === 0 ? e : r.join("");
return this._postCheck(u, t);
}
// -------------------------------------------------------------------------
// Private: limit tier check
// -------------------------------------------------------------------------
/**
* Returns true if a resolved entity of the given tier should count
* against the expansion/length limits.
* @param {string} tier — LIMIT_TIER_EXTERNAL | LIMIT_TIER_BASE
* @returns {boolean}
*/
_tierCounts(e) {
return this._limitTiers.has(Q) ? !0 : this._limitTiers.has(e);
}
// -------------------------------------------------------------------------
// Private: entity resolution
// -------------------------------------------------------------------------
/**
* Resolve a named entity token (without & and ;).
* Priority: inputMap > externalMap > baseMap
* Returns the resolved value tagged with its limit tier.
*
* @param {string} name
* @returns {{ value: string, tier: string }|undefined}
*/
_resolveName(e) {
if (e in this._inputMap)
return { value: this._inputMap[e], tier: P };
if (e in this._externalMap)
return { value: this._externalMap[e], tier: P };
if (e in this._baseMap)
return { value: this._baseMap[e], tier: W };
}
/**
* Classify a codepoint and return the minimum action level that must be applied.
* Returns -1 when no minimum is imposed (normal allow path).
*
* Ranges checked (in priority order):
* 1. U+0000 — null, governed by nullNCR (always ≥ remove)
* 2. U+D800–U+DFFF — surrogates, always prohibited (min: remove)
* 3. U+0001–U+001F \ {0x09,0x0A,0x0D} — XML 1.0 restricted C0 (min: remove)
* (skipped in XML 1.1 — C0 controls are allowed when written as NCRs)
*
* @param {number} cp — codepoint
* @returns {number} — minimum NCR_LEVEL value, or -1 for no restriction
*/
_classifyNCR(e) {
return e === 0 ? this._ncrNullLevel : e >= 55296 && e <= 57343 || this._ncrXmlVersion === 1 && e >= 1 && e <= 31 && !pt.has(e) ? T.remove : -1;
}
/**
* Execute a resolved NCR action.
*
* @param {number} action — NCR_LEVEL value
* @param {string} token — raw token (e.g. '#38') for error messages
* @param {number} cp — codepoint, used only for error messages
* @returns {string|undefined}
* - decoded character string → 'allow'
* - '' → 'remove'
* - undefined → 'leave' (caller must skip past '&' only)
* - throws Error → 'throw'
*/
_applyNCRAction(e, t, r) {
switch (e) {
case T.allow:
return String.fromCodePoint(r);
case T.remove:
return "";
case T.leave:
return;
case T.throw:
throw new Error(
`[EntityDecoder] Prohibited numeric character reference &${t}; (U+${r.toString(16).toUpperCase().padStart(4, "0")})`
);
default:
return String.fromCodePoint(r);
}
}
/**
* Full NCR resolution pipeline for a numeric token.
*
* Steps:
* 1. Parse the codepoint (decimal or hex).
* 2. Validate the raw codepoint range (NaN, <0, >0x10FFFF).
* 3. If numericAllowed is false and no minimum restriction applies → leave as-is.
* 4. Classify the codepoint to find the minimum required action level.
* 5. Resolve effective action = max(onNCR, minimum).
* 6. Apply and return.
*
* @param {string} token — e.g. '#38', '#x26', '#X26'
* @returns {string|undefined}
* - string (incl. '') — replacement ('' = remove)
* - undefined — leave original &token; as-is
*/
_resolveNCR(e) {
const t = e.charCodeAt(1);
let r;
if (t === 120 || t === 88 ? r = parseInt(e.slice(2), 16) : r = parseInt(e.slice(1), 10), Number.isNaN(r) || r < 0 || r > 1114111)
return;
const s = this._classifyNCR(r);
if (!this._numericAllowed && s < T.remove)
return;
const i = s === -1 ? this._ncrOnLevel : Math.max(this._ncrOnLevel, s);
return this._applyNCRAction(i, e, r);
}
}
const Ie = (n) => ne.includes(n) ? "__" + n : n, bt = {
preserveOrder: !1,
attributeNamePrefix: "@_",
attributesGroupName: !1,
textNodeName: "#text",
ignoreAttributes: !0,
removeNSPrefix: !1,
// remove NS from tag name or attribute name if true
allowBooleanAttributes: !1,
//a tag can have attributes without any value
//ignoreRootElement : false,
parseTagValue: !0,
parseAttributeValue: !1,
trimValues: !0,
//Trim string values of tag and attributes
cdataPropName: !1,
numberParseOptions: {
hex: !0,
leadingZeros: !0,
eNotation: !0
},
tagValueProcessor: function(n, e) {
return e;
},
attributeValueProcessor: function(n, e) {
return e;
},
stopNodes: [],
//nested tags will not be parsed even for errors
alwaysCreateTextNode: !1,
isArray: () => !1,
commentPropName: !1,
unpairedTags: [],
processEntities: !0,
htmlEntities: !1,
entityDecoder: null,
ignoreDeclaration: !1,
ignorePiTags: !1,
transformTagName: !1,
transformAttributeName: !1,
updateTag: function(n, e, t) {
return n;
},
// skipEmptyListItem: false
captureMetaData: !1,
maxNestedTags: 100,
strictReservedNames: !0,
jPath: !0,
// if true, pass jPath string to callbacks; if false, pass matcher instance
onDangerousProperty: Ie
};
function yt(n, e) {
if (typeof n != "string")
return;
const t = n.toLowerCase();
if (ne.some((r) => t === r.toLowerCase()))
throw new Error(
`[SECURITY] Invalid ${e}: "${n}" is a reserved JavaScript keyword that could cause prototype pollution`
);
if (_e.some((r) => t === r.toLowerCase()))
throw new Error(
`[SECURITY] Invalid ${e}: "${n}" is a reserved JavaScript keyword that could cause prototype pollution`
);
}
function Se(n, e) {
return typeof n == "boolean" ? {
enabled: n,
// true or false
maxEntitySize: 1e4,
maxExpansionDepth: 1e4,
maxTotalExpansions: 1 / 0,
maxExpandedLength: 1e5,
maxEntityCount: 1e3,
allowedTags: null,
tagFilter: null,
appliesTo: "all"
} : typeof n == "object" && n !== null ? {
enabled: n.enabled !== !1,
maxEntitySize: Math.max(1, n.maxEntitySize ?? 1e4),
maxExpansionDepth: Math.max(1, n.maxExpansionDepth ?? 1e4),
maxTotalExpansions: Math.max(1, n.maxTotalExpansions ?? 1 / 0),
maxExpandedLength: Math.max(1, n.maxExpandedLength ?? 1e5),
maxEntityCount: Math.max(1, n.maxEntityCount ?? 1e3),
allowedTags: n.allowedTags ?? null,
tagFilter: n.tagFilter ?? null,
appliesTo: n.appliesTo ?? "all"
} : Se(!0);
}
const Et = function(n) {
const e = Object.assign({}, bt, n), t = [
{ value: e.attributeNamePrefix, name: "attributeNamePrefix" },
{ value: e.attributesGroupName, name: "attributesGroupName" },
{ value: e.textNodeName, name: "textNodeName" },
{ value: e.cdataPropName, name: "cdataPropName" },
{ value: e.commentPropName, name: "commentPropName" }
];
for (const { value: r, name: s } of t)
r && yt(r, s);
return e.onDangerousProperty === null && (e.onDangerousProperty = Ie), e.processEntities = Se(e.processEntities, e.htmlEntities), e.unpairedTagsSet = new Set(e.unpairedTags), e.stopNodes && Array.isArray(e.stopNodes) && (e.stopNodes = e.stopNodes.map((r) => typeof r == "string" && r.startsWith("*.") ? ".." + r.substring(2) : r)), e;
};
let z;
typeof Symbol != "function" ? z = "@@xmlMetadata" : z = Symbol("XML Node Metadata");
class I {
constructor(e) {
this.tagname = e, this.child = [], this[":@"] = /* @__PURE__ */ Object.create(null);
}
add(e, t) {
e === "__proto__" && (e = "#__proto__"), this.child.push({ [e]: t });
}
addChild(e, t) {
e.tagname === "__proto__" && (e.tagname = "#__proto__"), e[":@"] && Object.keys(e[":@"]).length > 0 ? this.child.push({ [e.tagname]: e.child, ":@": e[":@"] }) : this.child.push({ [e.tagname]: e.child }), t !== void 0 && (this.child[this.child.length - 1][z] = { startIndex: t });
}
/** symbol used for metadata */
static getMetaDataSymbol() {
return z;
}
}
const xe = ":A-Za-z_À-ÖØ-öø-˿Ͱ-ͽͿ-҆҈--⁰-Ⰰ-、-豈-﷏ﷰ-�", Nt = xe + "\\-\\.\\d·̀-ͯ‿-⁀", Pe = ":A-Za-z_À-˿Ͱ-ͽͿ-҆҈--⁰-Ⰰ-、-豈-﷏ﷰ-�𐀀-", wt = Pe + "\\-\\.\\d·̀-ͯ҇‿-⁀", Me = (n, e, t = "") => {
const r = n.replace(":", ""), s = e.replace(":", ""), i = `[${r}][${s}]*`;
return {
name: new RegExp(`^[${n}][${e}]*$`, t),
ncName: new RegExp(`^${i}$`, t),
qName: new RegExp(`^${i}(?::${i})?$`, t),
nmToken: new RegExp(`^[${e}]+$`, t),
nmTokens: new RegExp(`^[${e}]+(?:\\s+[${e}]+)*$`, t)
};
}, Tt = Me(xe, Nt), Ct = Me(Pe, wt, "u"), _t = (n = "1.0") => n === "1.1" ? Ct : Tt, $e = (n, { xmlVersion: e = "1.0" } = {}) => _t(e).qName.test(n);
class vt {
constructor(e, t) {
this.suppressValidationErr = !e, this.options = e, this.xmlVersion = t || 1;
}
setXmlVersion(e = 1) {
this.xmlVersion = e;
}
readDocType(e, t) {
const r = /* @__PURE__ */ Object.create(null);
let s = 0;
if (e[t + 3] === "O" && e[t + 4] === "C" && e[t + 5] === "T" && e[t + 6] === "Y" && e[t + 7] === "P" && e[t + 8] === "E") {
t = t + 9;
let i = 1, a = !1, o = !1, l = "";
for (; t < e.length; t++)
if (e[t] === "<" && !o) {
if (a && x(e, "!ENTITY", t)) {
t += 7;
let h, u;
if ([h, u, t] = this.readEntityExp(e, t + 1, this.suppressValidationErr), u.indexOf("&") === -1) {
if (this.options.enabled !== !1 && this.options.maxEntityCount != null && s >= this.options.maxEntityCount)
throw new Error(
`Entity count (${s + 1}) exceeds maximum allowed (${this.options.maxEntityCount})`
);
r[h] = u, s++;
}
} else if (a && x(e, "!ELEMENT", t)) {
t += 8;
const { index: h } = this.readElementExp(e, t + 1);
t = h;
} else if (a && x(e, "!ATTLIST", t))
t += 8;
else if (a && x(e, "!NOTATION", t)) {
t += 9;
const { index: h } = this.readNotationExp(e, t + 1, this.suppressValidationErr);
t = h;
} else if (x(e, "!--", t))
o = !0;
else
throw new Error("Invalid DOCTYPE");
i++, l = "";
} else if (e[t] === ">") {
if (o ? e[t - 1] === "-" && e[t - 2] === "-" && (o = !1, i--) : i--, i === 0)
break;
} else
e[t] === "[" ? a = !0 : l += e[t];
if (i !== 0)
throw new Error("Unclosed DOCTYPE");
} else
throw new Error("Invalid Tag instead of DOCTYPE");
return { entities: r, i: t };
}
readEntityExp(e, t) {
t = w(e, t);
const r = t;
for (; t < e.length && !/\s/.test(e[t]) && e[t] !== '"' && e[t] !== "'"; )
t++;
let s = e.substring(r, t);
if (L(s, { xmlVersion: this.xmlVersion }), t = w(e, t), !this.suppressValidationErr) {
if (e.substring(t, t + 6).toUpperCase() === "SYSTEM")
throw new Error("External entities are not supported");
if (e[t] === "%")
throw new Error("Parameter entities are not supported");
}
let i = "";
if ([t, i] = this.readIdentifierVal(e, t, "entity"), this.options.enabled !== !1 && this.options.maxEntitySize != null && i.length > this.options.maxEntitySize)
throw new Error(
`Entity "${s}" size (${i.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`
);
return t--, [s, i, t];
}
readNotationExp(e, t) {
t = w(e, t);
const r = t;
for (; t < e.length && !/\s/.test(e[t]); )
t++;
let s = e.substring(r, t);
!this.suppressValidationErr && L(s, { xmlVersion: this.xmlVersion }), t = w(e, t);
const i = e.substring(t, t + 6).toUpperCase();
if (!this.suppressValidationErr && i !== "SYSTEM" && i !== "PUBLIC")
throw new Error(`Expected SYSTEM or PUBLIC, found "${i}"`);
t += i.length, t = w(e, t);
let a = null, o = null;
if (i === "PUBLIC")
[t, a] = this.readIdentifierVal(e, t, "publicIdentifier"), t = w(e, t), (e[t] === '"' || e[t] === "'") && ([t, o] = this.readIdentifierVal(e, t, "systemIdentifier"));
else if (i === "SYSTEM" && ([t, o] = this.readIdentifierVal(e, t, "systemIdentifier"), !this.suppressValidationErr && !o))
throw new Error("Missing mandatory system identifier for SYSTEM notation");
return { notationName: s, publicIdentifier: a, systemIdentifier: o, index: --t };
}
readIdentifierVal(e, t, r) {
let s = "";
const i = e[t];
if (i !== '"' && i !== "'")
throw new Error(`Expected quoted string, found "${i}"`);
t++;
const a = t;
for (; t < e.length && e[t] !== i; )
t++;
if (s = e.substring(a, t), e[t] !== i)
throw new Error(`Unterminated ${r} value`);
return t++, [t, s];
}
readElementExp(e, t) {
t = w(e, t);
const r = t;
for (; t < e.length && !/\s/.test(e[t]); )
t++;
let s = e.substring(r, t);
if (!this.suppressValidationErr && !$e(s, { xmlVersion: this.xmlVersion }))
throw new Error(`Invalid element name: "${s}"`);
t = w(e, t);
let i = "";
if (e[t] === "E" && x(e, "MPTY", t))
t += 4;
else if (e[t] === "A" && x(e, "NY", t))
t += 2;
else if (e[t] === "(") {
t++;
const a = t;
for (; t < e.length && e[t] !== ")"; )
t++;
if (i = e.substring(a, t), e[t] !== ")")
throw new Error("Unterminated content model");
} else if (!this.suppressValidationErr)
throw new Error(`Invalid Element Expression, found "${e[t]}"`);
return {
elementName: s,
contentModel: i.trim(),
index: t
};
}
readAttlistExp(e, t) {
t = w(e, t);
let r = t;
for (; t < e.length && !/\s/.test(e[t]); )
t++;
let s = e.substring(r, t);
for (L(s, { xmlVersion: this.xmlVersion }), t = w(e, t), r = t; t < e.length && !/\s/.test(e[t]); )
t++;
let i = e.substring(r, t);
if (!L(i, { xmlVersion: this.xmlVersion }))
throw new Error(`Invalid attribute name: "${i}"`);
t = w(e, t);
let a = "";
if (e.substring(t, t + 8).toUpperCase() === "NOTATION") {
if (a = "NOTATION", t += 8, t = w(e, t), e[t] !== "(")
throw new Error(`Expected '(', found "${e[t]}"`);
t++;
let l = [];
for (; t < e.length && e[t] !== ")"; ) {
const h = t;
for (; t < e.length && e[t] !== "|" && e[t] !== ")"; )
t++;
let u = e.substring(h, t);
if (u = u.trim(), !L(u, { xmlVersion: this.xmlVersion }))
throw new Error(`Invalid notation name: "${u}"`);
l.push(u), e[t] === "|" && (t++, t = w(e, t));
}
if (e[t] !== ")")
throw new Error("Unterminated list of notations");
t++, a += " (" + l.join("|") + ")";
} else {
const l = t;
for (; t < e.length && !/\s/.test(e[t]); )
t++;
a += e.substring(l, t);
const h = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
if (!this.suppressValidationErr && !h.includes(a.toUpperCase()))
throw new Error(`Invalid attribute type: "${a}"`);
}
t = w(e, t);
let o = "";
return e.substring(t, t + 8).toUpperCase() === "#REQUIRED" ? (o = "#REQUIRED", t += 8) : e.substring(t, t + 7).toUpperCase() === "#IMPLIED" ? (o = "#IMPLIED", t += 7) : [t, o] = this.readIdentifierVal(e, t, "ATTLIST"), {
elementName: s,
attributeName: i,
attributeType: a,
defaultValue: o,
index: t
};
}
}
const w = (n, e) => {
for (; e < n.length && /\s/.test(n[e]); )
e++;
return e;
};
function x(n, e, t) {
for (let r = 0; r < e.length; r++)
if (e[r] !== n[t + r + 1])
return !1;
return !0;
}
function L(n, e) {
if ($e(n, { xmlVersion: e }))
return n;
throw new Error(`Invalid entity name ${n}`);
}
const At = /^[-+]?0x[a-fA-F0-9]+$/, It = /^0b[01]+$/, St = /^0o[0-7]+$/, xt = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/, Pt = {
hex: !0,
binary: !1,
octal: !1,
leadingZeros: !0,
decimalPoint: ".",
eNotation: !0,
//skipLike: /regex/,
infinity: "original"
// "null", "infinity" (Infinity type), "string" ("Infinity" (the string literal))
};
function Mt(n, e = {}) {
if (e = Object.assign({}, Pt, e), !n || typeof n != "string")
return n;
let t = n.trim();
if (t.length === 0)
return n;
if (e.skipLike !== void 0 && e.skipLike.test(t))
return n;
if (t === "0")
return 0;
if (e.hex && At.test(t))
return q(t, 16);
if (e.binary && It.test(t))
return q(t, 2);
if (e.octal && St.test(t))
return q(t, 8);
if (isFinite(t)) {
if (t.includes("e") || t.includes("E"))
return Ot(n, t, e);
{
const r = xt.exec(t);
if (r) {
const s = r[1] || "", i = r[2];
let a = Lt(r[3]);
const o = s ? (
// 0., -00., 000.
n[i.length + 1] === "."
) : n[i.length] === ".";
if (!e.leadingZeros && (i.length > 1 || i.length === 1 && !o))
return n;
{
const l = Number(t), h = String(l);
if (l === 0)
return l;
if (h.search(/[eE]/) !== -1)
return e.eNotation ? l : n;
if (t.indexOf(".") !== -1)
return h === "0" || h === a || h === `${s}${a}` ? l : n;
let u = i ? a : t;
return i ? u === h || s + u === h ? l : n : u === h || u === s + h ? l : n;
}
} else
return n;
}
} else
return Ft(n, Number(t), e);
}
const $t = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/;
function Ot(n, e, t) {
if (!t.eNotation)
return n;
const r = e.match($t);
if (r) {
let s = r[1] || "";
const i = r[3].indexOf("e") === -1 ? "E" : "e", a = r[2], o = s ? (
// 0E.
n[a.length + 1] === i
) : n[a.length] === i;
return a.length > 1 && o ? n : a.length === 1 && (r[3].startsWith(`.${i}`) || r[3][0] === i) ? Number(e) : a.length > 0 ? t.leadingZeros && !o ? (e = (r[1] || "") + r[3], Number(e)) : n : Number(e);
} else
return n;
}
function Lt(n) {
return n && n.indexOf(".") !== -1 && (n = n.replace(/0+$/, ""), n === "." ? n = "0" : n[0] === "." ? n = "0" + n : n[n.length - 1] === "." && (n = n.substring(0, n.length - 1))), n;
}
function q(n, e) {
const t = n.trim();
if ((e === 2 || e === 8) && (n = t.substring(2)), parseInt)
return parseInt(n, e);
if (Number.parseInt)
return Number.parseInt(n, e);
if (window && window.parseInt)
return window.parseInt(n, e);
throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
}
function Ft(n, e, t) {
const r = e === 1 / 0;
switch (t.infinity.toLowerCase()) {
case "null":
return null;
case "infinity":
return e;
case "string":
return r ? "Infinity" : "-Infinity";
case "original":
default:
return n;
}
}
function Vt(n) {
return typeof n == "function" ? n : Array.isArray(n) ? (e) => {
for (const t of n)
if (typeof t == "string" && e === t || t instanceof RegExp && t.test(e))
return !0;
} : () => !1;
}
class he {
/**
* Create a new Expression
* @param {string} pattern - Pattern string (e.g., "root.users.user", "..user[id]")
* @param {Object} options - Configuration options
* @param {string} options.separator - Path separator (default: '.')
*/
constructor(e, t = {}, r) {
this.pattern = e, this.separator = t.separator || ".", this.segments = this._parse(e), this.data = r, this._hasDeepWildcard = this.segments.some((s) => s.type === "deep-wildcard"), this._hasAttributeCondition = this.segments.some((s) => s.attrName !== void 0), this._hasPositionSelector = this.segments.some((s) => s.position !== void 0);
}
/**
* Parse pattern string into segments
* @private
* @param {string} pattern - Pattern to parse
* @returns {Array} Array of segment objects
*/
_parse(e) {
const t = [];
let r = 0, s = "";
for (; r < e.length; )
e[r] === this.separator ? r + 1 < e.length && e[r + 1] === this.separator ? (s.trim() && (t.push(this._parseSegment(s.trim())), s = ""), t.push({ type: "deep-wildcard" }), r += 2) : (s.trim() && t.push(this._parseSegment(s.trim())), s = "", r++) : (s += e[r], r++);
return s.trim() && t.push(this._parseSegment(s.trim())), t;
}
/**
* Parse a single segment
* @private
* @param {string} part - Segment string (e.g., "user", "ns::user", "user[id]", "ns::user:first")
* @returns {Object} Segment object
*/
_parseSegment(e) {
const t = { type: "tag" };
let r = null, s = e;
const i = e.match(/^([^\[]+)(\[[^\]]*\])(.*)$/);
if (i && (s = i[1] + i[3], i[2])) {
const u = i[2].slice(1, -1);
u && (r = u);
}
let a, o = s;
if (s.includes("::")) {
const u = s.indexOf("::");
if (a = s.substring(0, u).trim(), o = s.substring(u + 2).trim(), !a)
throw new Error(`Invalid namespace in pattern: ${e}`);
}
let l, h = null;
if (o.includes(":")) {
const u = o.lastIndexOf(":"), c = o.substring(0, u).trim(), d = o.substring(u + 1).trim();
["first", "last", "odd", "even"].includes(d) || /^nth\(\d+\)$/.test(d) ? (l = c, h = d) : l = o;
} else
l = o;
if (!l)
throw new Error(`Invalid segment pattern: ${e}`);
if (t.tag = l, a && (t.namespace = a), r)
if (r.includes("=")) {
const u = r.indexOf("=");
t.attrName = r.substring(0, u).trim(), t.attrValue = r.substring(u + 1).trim();
} else
t.attrName = r.trim();
if (h) {
const u = h.match(/^nth\((\d+)\)$/);
u ? (t.position = "nth", t.positionValue = parseInt(u[1], 10)) : t.position = h;
}
return t;
}
/**
* Get the number of segments
* @returns {number}
*/
get length() {
return this.segments.length;
}
/**
* Check if expression contains deep wildcard
* @returns {boolean}
*/
hasDeepWildcard() {
return this._hasDeepWildcard;
}
/**
* Check if expression has attribute conditions
* @returns {boolean}
*/
hasAttributeCondition() {
return this._hasAttributeCondition;
}
/**
* Check if expression has position selectors
* @returns {boolean}
*/
hasPositionSelector() {
return this._hasPositionSelector;
}
/**
* Get string representation
* @returns {string}
*/
toString() {
return this.pattern;
}
}
class Rt {
constructor() {
this._byDepthAndTag = /* @__PURE__ */ new Map(), this._wildcardByDepth = /* @__PURE__ */ new Map(), this._deepWildcards = [], this._patterns = /* @__PURE__ */ new Set(), this._sealed = !1;
}
/**
* Add an Expression to the set.
* Duplicate patterns (same pattern string) are silently ignored.
*
* @param {import('./Expression.js').default} expression - A pre-constructed Expression instance
* @returns {this} for chaining
* @throws {TypeError} if called after seal()
*
* @example
* set.add(new Expression('root.users.user'));
* set.add(new Expression('..script'));
*/
add(e) {
if (this._sealed)
throw new TypeError(
"ExpressionSet is sealed. Create a new ExpressionSet to add more expressions."
);
if (this._patterns.has(e.pattern))
return this;
if (this._patterns.add(e.pattern), e.hasDeepWildcard())
return this._deepWildcards.push(e), this;
const t = e.length, r = e.segments[e.segments.length - 1], s = r == null ? void 0 : r.tag;
if (!s || s === "*")
this._wildcardByDepth.has(t) || this._wildcardByDepth.set(t, []), this._wildcardByDepth.get(t).push(e);
else {
const i = `${t}:${s}`;
this._byDepthAndTag.has(i) || this._byDepthAndTag.set(i, []), this._byDepthAndTag.get(i).push(e);
}
return this;
}
/**
* Add multiple expressions at once.
*
* @param {import('./Expression.js').default[]} expressions - Array of Expression instances
* @returns {this} for chaining
*
* @example
* set.addAll([
* new Expression('root.users.user'),
* new Expression('root.config.setting'),
* ]);
*/
addAll(e) {
for (const t of e)
this.add(t);
return this;
}
/**
* Check whether a pattern string is already present in the set.
*
* @param {import('./Expression.js').default} expression
* @returns {boolean}
*/
has(e) {
return this._patterns.has(e.pattern);
}
/**
* Number of expressions in the set.
* @type {number}
*/
get size() {
return this._patterns.size;
}
/**
* Seal the set against further modifications.
* Useful to prevent accidental mutations after config is built.
* Calling add() or addAll() on a sealed set throws a TypeError.
*
* @returns {this}
*/
seal() {
return this._sealed = !0, this;
}
/**
* Whether the set has been sealed.
* @type {boolean}
*/
get isSealed() {
return this._sealed;
}
/**
* Test whether the matcher's current path matches any expression in the set.
*
* Evaluation order (cheapest → most expensive):
* 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions
* 2. Depth-only wildcard bucket — O(1) lookup, rare
* 3. Deep-wildcard list — always checked, but usually small
*
* @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)
* @returns {boolean} true if any expression matches the current path
*
* @example
* if (stopNodes.matchesAny(matcher)) {
* // handle stop node
* }
*/
matchesAny(e) {
return this.findMatch(e) !== null;
}
/**
* Find and return the first Expression that matches the matcher's current path.
*
* Uses the same evaluation order as matchesAny (cheapest → most expensive):
* 1. Exact depth + tag bucket
* 2. Depth-only wildcard bucket
* 3. Deep-wildcard list
*
* @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)
* @returns {import('./Expression.js').default | null} the first matching Expression, or null
*
* @example
* const expr = stopNodes.findMatch(matcher);
* if (expr) {
* // access expr.config, expr.pattern, etc.
* }
*/
findMatch(e) {
const t = e.getDepth(), r = e.getCurrentTag(), s = `${t}:${r}`, i = this._byDepthAndTag.get(s);
if (i) {
for (let o = 0; o < i.length; o++)
if (e.matches(i[o]))
return i[o];
}
const a = this._wildcardByDepth.get(t);
if (a) {
for (let o = 0; o < a.length; o++)
if (e.matches(a[o]))
return a[o];
}
for (let o = 0; o < this._deepWildcards.length; o++)
if (e.matches(this._deepWildcards[o]))
return this._deepWildcards[o];
return null;
}
}
class kt {
/**
* @param {Matcher} matcher - The parent Matcher instance to read from.
*/
constructor(e) {
this._matcher = e;
}
/**
* Get the path separator used by the parent matcher.
* @returns {string}
*/
get separator() {
return this._matcher.separator;
}
/**
* Get current tag name.
* @returns {string|undefined}
*/
getCurrentTag() {
const e = this._matcher.path;
return e.length > 0 ? e[e.length - 1].tag : void 0;
}
/**
* Get current namespace.
* @returns {string|undefined}
*/
getCurrentNamespace() {
const e = this._matcher.path;
return e.length > 0 ? e[e.length - 1].namespace : void 0;
}
/**
* Get current node's attribute value.
* @param {string} attrName
* @returns {*}
*/
getAttrValue(e) {
var r;
const t = this._matcher.path;
if (t.length !== 0)
return (r = t[t.length - 1].values) == null ? void 0 : r[e];
}
/**
* Check if current node has an attribute.
* @param {string} attrName
* @returns {boolean}
*/
hasAttr(e) {
const t = this._matcher.path;
if (t.length === 0)
return !1;
const r = t[t.length - 1];
return r.values !== void 0 && e in r.values;
}
/**
* Get current node's sibling position (child index in parent).
* @returns {number}
*/
getPosition() {
const e = this._matcher.path;
return e.length === 0 ? -1 : e[e.length - 1].position ?? 0;
}
/**
* Get current node's repeat counter (occurrence count of this tag name).
* @returns {number}
*/
getCounter() {
const e = this._matcher.path;
return e.length === 0 ? -1 : e[e.length - 1].counter ?? 0;
}
/**
* Get current node's sibling index (alias for getPosition).
* @returns {number}
* @deprecated Use getPosition() or getCounter() instead
*/
getIndex() {
return this.getPosition();
}
/**
* Get current path depth.
* @returns {number}
*/
getDepth() {
return this._matcher.path.length;
}
/**
* Get path as string.
* @param {string} [separator] - Optional separator (uses default if not provided)
* @param {boolean} [includeNamespace=true]
* @returns {string}
*/
toString(e, t = !0) {
return this._matcher.toString(e, t);
}
/**
* Get path as array of tag names.
* @returns {string[]}
*/
toArray() {
return this._matcher.path.map((e) => e.tag);
}
/**
* Match current path against an Expression.
* @param {Expression} expression
* @returns {boolean}
*/
matches(e) {
return this._matcher.matches(e);
}
/**
* Match any expression in the given set against the current path.
* @param {ExpressionSet} exprSet
* @returns {boolean}
*/
matchesAny(e) {
return e.matchesAny(this._matcher);
}
}
class jt {
/**
* Create a new Matcher.
* @param {Object} [options={}]
* @param {string} [options.separator='.'] - Default path separator
*/
constructor(e = {}) {
this.separator = e.separator || ".", this.path = [], this.siblingStacks = [], this._pathStringCache = null, this._view = new kt(this);
}
/**
* Push a new tag onto the path.
* @param {string} tagName
* @param {Object|null} [attrValues=null]
* @param {string|null} [namespace=