UNPKG

stax-xml

Version:
1,049 lines (1,044 loc) 32.8 kB
// src/types.ts var XmlEventType; ((XmlEventType2) => { XmlEventType2["START_DOCUMENT"] = "START_DOCUMENT"; XmlEventType2["END_DOCUMENT"] = "END_DOCUMENT"; XmlEventType2["START_ELEMENT"] = "START_ELEMENT"; XmlEventType2["END_ELEMENT"] = "END_ELEMENT"; XmlEventType2["CHARACTERS"] = "CHARACTERS"; XmlEventType2["CDATA"] = "CDATA"; XmlEventType2["ERROR"] = "ERROR"; })(XmlEventType ||= {}); // src/StaxXmlParser.ts class StaxXmlParser { reader = null; decoder; buffer; bufferLength = 0; position = 0; eventQueue = []; resolveNext = null; error = null; isStreamEnded = false; parserFinished = false; currentTextBuffer = ""; elementStack = []; namespaceStack = []; options; constructor(xmlStream, options = {}) { if (!(xmlStream instanceof ReadableStream)) { throw new Error("xmlStream must be a web standard ReadableStream."); } this.options = { encoding: "utf-8", autoDecodeEntities: true, maxBufferSize: 64 * 1024, enableBufferCompaction: true, ...options }; this.decoder = new TextDecoder(this.options.encoding); this.buffer = new Uint8Array(this.options.maxBufferSize || 64 * 1024); this.reader = xmlStream.getReader(); this._startReading(); this._addEvent({ type: "START_DOCUMENT" /* START_DOCUMENT */ }); } async _startReading() { try { while (true) { const { done, value } = await this.reader.read(); if (done) { this.isStreamEnded = true; this._parseBuffer(); if (!this.parserFinished && this.elementStack.length > 0) { this._addError(new Error("Unexpected end of document. Not all elements were closed.")); } if (!this.parserFinished) { this._flushCharacters(); this._addEvent({ type: "END_DOCUMENT" /* END_DOCUMENT */ }); this.parserFinished = true; } if (this.resolveNext && this.eventQueue.length === 0) { this.resolveNext({ value: undefined, done: true }); this.resolveNext = null; } break; } this._appendToBuffer(value); this._parseBuffer(); this._compactBufferIfNeeded(); } } catch (err) { this._addError(err); if (this.resolveNext) { this.resolveNext({ value: undefined, done: true }); this.resolveNext = null; } } } _parseBuffer() { while (this.position < this.bufferLength && !this.parserFinished) { const ltPos = this._findPattern("<"); if (ltPos === -1) { if (this.isStreamEnded) { const remainingText = this._readBuffer(); this.currentTextBuffer += remainingText; this._flushCharacters(); } else { break; } break; } if (ltPos > this.position) { try { const textLength = ltPos - this.position; const text = this._readBuffer(textLength); this.currentTextBuffer += text; } catch (error) { if (!this.isStreamEnded) { break; } throw error; } } this.position = ltPos; if (this._matchesPattern("<?xml")) { if (!this._parseXmlDeclaration()) break; } else if (this._matchesPattern("<!--")) { if (!this._parseComment()) break; } else if (this._matchesPattern("<![CDATA[")) { if (!this._parseCData()) break; } else if (this._matchesPattern("<?")) { if (!this._parseProcessingInstruction()) break; } else if (this._matchesPattern("</")) { this._flushCharacters(); if (!this._parseEndTag()) break; } else if (this._matchesPattern("<")) { this._flushCharacters(); if (!this._parseStartTag()) break; } else { if (this.isStreamEnded) { this._addError(new Error(`Malformed XML near position ${this.position}`)); return; } break; } this._compactBufferIfNeeded(); } } _flushCharacters() { if (this.currentTextBuffer.length > 0) { const decodedText = this._unescapeXml(this.currentTextBuffer); if (decodedText.trim().length > 0) { this._addEvent({ type: "CHARACTERS" /* CHARACTERS */, value: decodedText }); } this.currentTextBuffer = ""; } } _compactBufferIfNeeded() { if (!this.options.enableBufferCompaction) return; const maxSize = this.options.maxBufferSize || 64 * 1024; if (this.position > 8192 || this.bufferLength > maxSize && this.position > maxSize / 4) { this._compactBuffer(); } } _compactBuffer() { if (this.position > 0 && this.position < this.bufferLength) { const remainingLength = this.bufferLength - this.position; this.buffer.copyWithin(0, this.position, this.bufferLength); this.bufferLength = remainingLength; this.position = 0; } } _clearBuffers() { this.bufferLength = 0; this.position = 0; this.currentTextBuffer = ""; } _addEvent(event) { this.eventQueue.push(event); if (this.resolveNext) { this.resolveNext(this._popNextEvent()); this.resolveNext = null; } } _addError(err) { if (this.error === null) { this.error = err; this._addEvent({ type: "ERROR" /* ERROR */, error: err }); this.parserFinished = true; this._clearBuffers(); if (this.reader) { this.reader.releaseLock(); this.reader = null; } } } _popNextEvent() { if (this.eventQueue.length > 0) { return { value: this.eventQueue.shift(), done: false }; } if (this.parserFinished) { return { value: undefined, done: true }; } return null; } async next() { if (this.error) { throw this.error; } const nextEvent = this._popNextEvent(); if (nextEvent) { return nextEvent; } if (this.parserFinished) { return { value: undefined, done: true }; } return new Promise((resolve) => { this.resolveNext = resolve; }); } [Symbol.asyncIterator]() { return this; } _unescapeXml(text) { if (!text) { return ""; } if (!this.options.autoDecodeEntities) { return text; } let entityMap = { "&lt;": "<", "&gt;": ">", "&quot;": '"', "&apos;": "'", ...this.options.addEntities?.reduce((map, entity) => { if (entity.entity && entity.value) { map[entity.entity] = entity.value; } return map; }, {}), "&amp;": "&" }; const regex = new RegExp(Object.keys(entityMap).map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g"); return text.replace(regex, (match) => { if (entityMap[match]) { return entityMap[match]; } else { return match; } }); } _parseQualifiedName(qname, namespaces, isAttribute = false) { const colonIndex = qname.indexOf(":"); if (colonIndex === -1) { if (isAttribute) { return { localName: qname, prefix: undefined, uri: undefined }; } else { const defaultUri = namespaces.get(""); return { localName: qname, prefix: undefined, uri: defaultUri }; } } else { const prefix = qname.substring(0, colonIndex); const localName = qname.substring(colonIndex + 1); const uri = namespaces.get(prefix); return { localName, prefix, uri }; } } get XmlEventType() { return XmlEventType; } _appendToBuffer(newData) { const requiredSize = this.bufferLength + newData.length; if (requiredSize > this.buffer.length) { const newSize = Math.max(this.buffer.length * 2, requiredSize); const newBuffer = new Uint8Array(newSize); newBuffer.set(this.buffer.subarray(0, this.bufferLength)); this.buffer = newBuffer; } this.buffer.set(newData, this.bufferLength); this.bufferLength += newData.length; } _readBuffer(length) { const originalPos = this.position; const endPos = length ? Math.min(this.position + length, this.bufferLength) : this.bufferLength; const slice = this.buffer.subarray(this.position, endPos); try { const result = this.decoder.decode(slice, { stream: !this.isStreamEnded }); this.position = endPos; return result; } catch (error) { if (!this.isStreamEnded && endPos === this.bufferLength) { for (let i = 1;i <= 4 && endPos - i > this.position; i++) { try { const safeSlice = this.buffer.subarray(this.position, endPos - i); const result = this.decoder.decode(safeSlice, { stream: true }); this.position = endPos - i; return result; } catch { continue; } } } this.position = originalPos; throw error; } } _matchesPattern(pattern) { const patternBytes = new TextEncoder().encode(pattern); if (this.position + patternBytes.length > this.bufferLength) { return false; } for (let i = 0;i < patternBytes.length; i++) { if (this.buffer[this.position + i] !== patternBytes[i]) { return false; } } return true; } _findPattern(pattern) { const patternBytes = new TextEncoder().encode(pattern); const searchEnd = this.bufferLength - patternBytes.length + 1; for (let i = this.position;i < searchEnd; i++) { let found = true; for (let j = 0;j < patternBytes.length; j++) { if (this.buffer[i + j] !== patternBytes[j]) { found = false; break; } } if (found) { return i; } } return -1; } _parseXmlDeclaration() { const endPos = this._findPattern("?>"); if (endPos === -1) { return false; } this.position = endPos + 2; return true; } _parseComment() { const endPos = this._findPattern("-->"); if (endPos === -1) { return false; } this.position = endPos + 3; return true; } _parseCData() { const startPos = this.position + 9; const endPos = this._findPattern("]]>"); if (endPos === -1) { return false; } try { const cdataContent = this.decoder.decode(this.buffer.subarray(startPos, endPos)); this._addEvent({ type: "CDATA" /* CDATA */, value: cdataContent }); this.position = endPos + 3; return true; } catch (error) { if (!this.isStreamEnded) { return false; } throw error; } } _parseProcessingInstruction() { const endPos = this._findPattern("?>"); if (endPos === -1) { return false; } this.position = endPos + 2; return true; } _parseEndTag() { const gtPos = this._findPattern(">"); if (gtPos === -1) { return false; } try { const tagContent = this.decoder.decode(this.buffer.subarray(this.position, gtPos + 1)); const closeTagMatch = tagContent.match(/^<\/([a-zA-Z0-9_:.-]+)\s*>$/); if (!closeTagMatch) { this._addError(new Error("Malformed closing tag")); return true; } const tagName = closeTagMatch[1]; if (this.elementStack.length === 0 || this.elementStack[this.elementStack.length - 1] !== tagName) { this._addError(new Error(`Mismatched closing tag: </${tagName}>. Expected </${this.elementStack[this.elementStack.length - 1] || "nothing"}>`)); return true; } const currentNamespaces = this.namespaceStack.length > 0 ? this.namespaceStack[this.namespaceStack.length - 1] : new Map; const { localName, prefix, uri } = this._parseQualifiedName(tagName, currentNamespaces); this.elementStack.pop(); this.namespaceStack.pop(); this._addEvent({ type: "END_ELEMENT" /* END_ELEMENT */, name: tagName, localName, prefix, uri }); this.position = gtPos + 1; return true; } catch (error) { if (!this.isStreamEnded) { return false; } throw error; } } _parseStartTag() { const gtPos = this._findPattern(">"); if (gtPos === -1) { return false; } try { const tagContent = this.decoder.decode(this.buffer.subarray(this.position, gtPos + 1)); const tagMatch = tagContent.match(/^<([a-zA-Z0-9_:.-]+)(\s+[^>]*?)?\s*(\/?)>$/); if (!tagMatch) { this._addError(new Error("Malformed start tag")); return true; } const tagName = tagMatch[1]; const attributesString = tagMatch[2] || ""; const isSelfClosing = tagMatch[3] === "/"; const currentNamespaces = new Map; if (this.namespaceStack.length > 0) { const parentNamespaces = this.namespaceStack[this.namespaceStack.length - 1]; for (const [prefix2, uri2] of parentNamespaces) { currentNamespaces.set(prefix2, uri2); } } const attributes = {}; const attributesWithPrefix = {}; const attrMatches = attributesString.matchAll(/([a-zA-Z0-9_:.-]+)="([^"]*)"/g); for (const match of attrMatches) { const attrName = match[1]; const attrValue = this._unescapeXml(match[2]); attributes[attrName] = attrValue; const attrNamespaceInfo = this._parseQualifiedName(attrName, currentNamespaces, true); attributesWithPrefix[attrNamespaceInfo.localName] = { value: attrValue, prefix: attrNamespaceInfo.prefix, uri: attrNamespaceInfo.uri }; if (attrName === "xmlns") { currentNamespaces.set("", attrValue); } else if (attrName.startsWith("xmlns:")) { const prefix2 = attrName.substring(6); currentNamespaces.set(prefix2, attrValue); } } const { localName, prefix, uri } = this._parseQualifiedName(tagName, currentNamespaces); this._addEvent({ type: "START_ELEMENT" /* START_ELEMENT */, name: tagName, localName, prefix, uri, attributes, attributesWithPrefix }); this.position = gtPos + 1; if (!isSelfClosing) { this.elementStack.push(tagName); this.namespaceStack.push(currentNamespaces); } else { this._addEvent({ type: "END_ELEMENT" /* END_ELEMENT */, name: tagName, localName, prefix, uri }); } return true; } catch (error) { if (!this.isStreamEnded) { return false; } throw error; } } } var StaxXmlParser_default = StaxXmlParser; // src/StaxXmlWriter.ts var defaultOptions = { encoding: "utf-8", prettyPrint: false, indentString: " ", autoEncodeEntities: true, namespaces: [] }; class StaxXmlWriter { xmlString = ""; state = 0 /* INITIAL */; elementStack = []; hasTextContentStack = []; namespaceStack = []; options; currentIndentLevel = 0; needsIndent = false; entityMap = {}; constructor(options = {}) { this.options = { ...defaultOptions, ...options }; this.namespaceStack = [new Map]; if (this.options.addEntities && Array.isArray(this.options.addEntities)) { for (const entity of this.options.addEntities) { if (entity.entity && entity.value) { this.entityMap[entity.entity] = entity.value; } } } } writeStartDocument(version = "1.0", encoding) { if (this.state !== 0 /* INITIAL */) { throw new Error("writeStartDocument can only be called once at the beginning of the document."); } this.state = 3 /* AFTER_ELEMENT */; let declaration = `<?xml version="${version}"`; if (encoding) { declaration += ` encoding="${encoding.toUpperCase()}"`; this.options.encoding = encoding; } else { declaration += ` encoding="${this.options.encoding.toUpperCase()}"`; } declaration += "?>"; this._write(declaration); if (this.options.prettyPrint) { this.needsIndent = true; } return this; } writeEndDocument() { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { return; } while (this.elementStack.length > 0) { this.writeEndElement(); } this.state = 4 /* CLOSED */; } getXmlString() { return this.xmlString; } writeStartElement(localName, options) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeStartElement: Writer is closed or in error state."); } this._closeStartElementTag(); const prefix = options?.prefix; const uri = options?.uri; const attributes = options?.attributes; const selfClosing = options?.selfClosing ?? false; this._writeIndent(); const tagName = prefix ? `${prefix}:${localName}` : localName; this._write(`<${tagName}`); const currentNamespaces = new Map(this.namespaceStack[this.namespaceStack.length - 1]); if (prefix && uri) { this._write(` xmlns:${prefix}="${this._escapeXml(uri)}"`); currentNamespaces.set(prefix, uri); } if (attributes) { for (const [key, value] of Object.entries(attributes)) { if (typeof value === "string") { this._write(` ${key}="${this._escapeXml(value)}"`); } else { const attrPrefix = value.prefix; const attrValue = value.value; if (attrPrefix) { if (!currentNamespaces.has(attrPrefix)) { throw new Error(`Namespace prefix '${attrPrefix}' is not defined for attribute '${key}'`); } this._write(` ${attrPrefix}:${key}="${this._escapeXml(attrValue)}"`); } else { this._write(` ${key}="${this._escapeXml(attrValue)}"`); } } } } if (selfClosing) { this._write("/>"); this.state = 3 /* AFTER_ELEMENT */; this._writeNewline(); return this; } this.elementStack.push({ localName, prefix }); this.hasTextContentStack.push(false); this.namespaceStack.push(currentNamespaces); this.state = 1 /* START_ELEMENT_OPEN */; this.currentIndentLevel++; return this; } writeAttribute(localName, value, prefix) { if (this.state !== 1 /* START_ELEMENT_OPEN */) { throw new Error("writeAttribute can only be called after writeStartElement."); } let attrName = prefix ? `${prefix}:${localName}` : localName; let attr = ` ${attrName}="${this._escapeXml(value)}"`; this._write(attr); return this; } writeNamespace(prefix, uri) { if (this.state !== 1 /* START_ELEMENT_OPEN */) { throw new Error("writeNamespace can only be called after writeStartElement."); } const currentNamespaces = this.namespaceStack[this.namespaceStack.length - 1]; if (prefix) { this._write(` xmlns:${prefix}="${this._escapeXml(uri)}"`); currentNamespaces.set(prefix, uri); } else { this._write(` xmlns="${this._escapeXml(uri)}"`); currentNamespaces.set("", uri); } return this; } writeCharacters(text) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeCharacters: Writer is closed or in error state."); } this._closeStartElementTag(); this._write(this._escapeXml(text)); this.state = 2 /* IN_ELEMENT */; if (this.hasTextContentStack.length > 0) { this.hasTextContentStack[this.hasTextContentStack.length - 1] = true; } this.needsIndent = false; return this; } writeCData(cdata) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeCData: Writer is closed or in error state."); } this._closeStartElementTag(); if (cdata.includes("]]>")) { throw new Error('CDATA section cannot contain "]]>" sequence.'); } this._write(`<![CDATA[${cdata}]]>`); this.state = 2 /* IN_ELEMENT */; if (this.hasTextContentStack.length > 0) { this.hasTextContentStack[this.hasTextContentStack.length - 1] = true; } this.needsIndent = false; return this; } writeComment(comment) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeComment: Writer is closed or in error state."); } this._closeStartElementTag(); if (comment.includes("--")) { throw new Error('XML comment cannot contain "--" sequence.'); } this._writeIndent(); this._write(`<!-- ${comment} -->`); this.state = 3 /* AFTER_ELEMENT */; this._writeNewline(); return this; } writeProcessingInstruction(target, data) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeProcessingInstruction: Writer is closed or in error state."); } this._closeStartElementTag(); let pi = `<?${target}`; if (data) { if (data.includes("?>")) { throw new Error('Processing instruction data cannot contain "?>" sequence.'); } pi += ` ${data}`; } pi += "?>"; this._writeIndent(); this._write(pi); this.state = 3 /* AFTER_ELEMENT */; this._writeNewline(); return this; } writeEndElement() { if (this.elementStack.length === 0) { throw new Error("No open element to close."); } if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) { throw new Error("Cannot writeEndElement: Writer is closed or in error state."); } this.currentIndentLevel--; const hasTextContent = this.hasTextContentStack.pop() || false; if (!hasTextContent && this.state !== 1 /* START_ELEMENT_OPEN */) { this._writeIndent(); } this._closeStartElementTag(); const elementInfo = this.elementStack.pop(); this.namespaceStack.pop(); const closingTagName = elementInfo.prefix ? `${elementInfo.prefix}:${elementInfo.localName}` : elementInfo.localName; this._write(`</${closingTagName}>`); this.state = 3 /* AFTER_ELEMENT */; if (this.options.prettyPrint) { this.needsIndent = true; } return this; } setPrettyPrint(enabled) { this.options.prettyPrint = enabled; return this; } setIndentString(indentString) { this.options.indentString = indentString; return this; } isPrettyPrintEnabled() { return this.options.prettyPrint; } getIndentString() { return this.options.indentString; } _closeStartElementTag() { if (this.state === 1 /* START_ELEMENT_OPEN */) { this._write(">"); this.state = 2 /* IN_ELEMENT */; if (this.options.prettyPrint) { this.needsIndent = true; } } } _writeIndent() { if (this.options.prettyPrint && this.needsIndent) { this.xmlString += ` `; this.xmlString += this.options.indentString.repeat(this.currentIndentLevel); this.needsIndent = false; } } _writeNewline() { if (this.options.prettyPrint) { this.xmlString += ` `; this.needsIndent = true; } } _write(chunk) { if (this.state === 4 /* CLOSED */ || this.state === 5 /* ERROR */) return; this.xmlString += chunk; } _escapeXml(text) { if (!text) { return ""; } if (!this.options.autoEncodeEntities) { return text; } let entityMap = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&apos;", ...this.options.addEntities?.reduce((map, entity) => { if (entity.entity && entity.value) { map[entity.entity] = entity.value; } return map; }, {}) }; const regex = new RegExp(Object.keys(entityMap).join("|"), "g"); return text.replace(regex, (match) => { if (entityMap[match]) { return entityMap[match]; } else { return match; } }); } } var StaxXmlWriter_default = StaxXmlWriter; // src/StaxXmlParserSync.ts class StaxXmlParserSync { xml; pos = 0; elementStack = []; namespaceStack = []; options; xmlnsRegex; attrRegex; constructor(xml, options = {}) { this.xml = xml; this.options = { autoDecodeEntities: true, ...options }; this.namespaceStack.push(new Map); this.xmlnsRegex = /(xmlns(?::([a-zA-Z0-9_.-]+))?)="([^"]*)"/g; this.attrRegex = /([a-zA-Z0-9_:.-]+)(?:\s*=\s*"([^"]*)")?/g; } *[Symbol.iterator]() { yield { type: "START_DOCUMENT" /* START_DOCUMENT */ }; while (this.pos < this.xml.length) { const nextTagOpen = this.xml.indexOf("<", this.pos); if (nextTagOpen === -1) { const text = this.xml.substring(this.pos); if (text.trim().length > 0) { yield { type: "CHARACTERS" /* CHARACTERS */, value: this._unescapeXml(text.trim()) }; } this.pos = this.xml.length; break; } if (nextTagOpen > this.pos) { const text = this.xml.substring(this.pos, nextTagOpen); if (text.trim().length > 0) { yield { type: "CHARACTERS" /* CHARACTERS */, value: this._unescapeXml(text.trim()) }; } } this.pos = nextTagOpen; const charAfterAngle = this.xml[this.pos + 1]; switch (charAfterAngle) { case "/": yield* this._parseEndTag(); break; case "!": yield* this._parseCdataCommentDoctype(); break; case "?": yield* this._parseProcessingInstruction(); break; default: yield* this._parseStartTag(); break; } } yield { type: "END_DOCUMENT" /* END_DOCUMENT */ }; } *_parseEndTag() { const tagClose = this.xml.indexOf(">", this.pos); if (tagClose === -1) throw new Error("Unclosed end tag"); const fullTagName = this.xml.substring(this.pos + 2, tagClose).trim(); if (this.elementStack.length === 0) { throw new Error(`Mismatched closing tag: </${fullTagName}>. No open elements.`); } const expectedTagName = this.elementStack[this.elementStack.length - 1]; if (fullTagName !== expectedTagName) { throw new Error(`Mismatched closing tag: </${fullTagName}>. Expected </${expectedTagName}>.`); } this.elementStack.pop(); const currentNamespaces = this.namespaceStack.pop(); const { localName, prefix, uri } = this._parseQualifiedName(fullTagName, currentNamespaces || new Map, false); yield { type: "END_ELEMENT" /* END_ELEMENT */, name: fullTagName, localName, prefix, uri }; this.pos = tagClose + 1; } *_parseCdataCommentDoctype() { if (this.xml.startsWith("<![CDATA[", this.pos)) { const cdataEnd = this.xml.indexOf("]]>", this.pos); if (cdataEnd === -1) throw new Error("Unclosed CDATA section"); const cdataContent = this.xml.substring(this.pos + 9, cdataEnd); yield { type: "CDATA" /* CDATA */, value: cdataContent }; this.pos = cdataEnd + 3; } else if (this.xml.startsWith("<!--", this.pos)) { const commentEnd = this.xml.indexOf("-->", this.pos); if (commentEnd === -1) throw new Error("Unclosed comment"); this.pos = commentEnd + 3; } else if (this.xml.startsWith("<!DOCTYPE", this.pos)) { const doctypeEnd = this.xml.indexOf(">", this.pos); if (doctypeEnd === -1) throw new Error("Unclosed DOCTYPE declaration"); this.pos = doctypeEnd + 1; } } *_parseProcessingInstruction() { const piEnd = this.xml.indexOf("?>", this.pos); if (piEnd === -1) throw new Error("Unclosed processing instruction"); this.pos = piEnd + 2; } *_parseStartTag() { const tagClose = this._findTagEnd(this.pos + 1); if (tagClose === -1) throw new Error("Unclosed start tag"); const tagContent = this.xml.substring(this.pos + 1, tagClose); let attributes = {}; let attributesWithPrefix = {}; let isSelfClosing = false; const currentNamespaces = new Map; if (this.namespaceStack.length > 0) { const parentNamespaces = this.namespaceStack[this.namespaceStack.length - 1]; for (const [prefix2, uri2] of parentNamespaces) { currentNamespaces.set(prefix2, uri2); } } let rawTagName; let rawAttrStr = ""; if (tagContent.endsWith("/")) { isSelfClosing = true; const actualTagContent = tagContent.substring(0, tagContent.length - 1).trim(); const spaceIndex = actualTagContent.indexOf(" "); if (spaceIndex === -1) { rawTagName = actualTagContent; } else { rawTagName = actualTagContent.substring(0, spaceIndex); rawAttrStr = actualTagContent.substring(spaceIndex + 1); } } else { const spaceIndex = tagContent.indexOf(" "); if (spaceIndex === -1) { rawTagName = tagContent; } else { rawTagName = tagContent.substring(0, spaceIndex); rawAttrStr = tagContent.substring(spaceIndex + 1); } } this.xmlnsRegex.lastIndex = 0; let match; while ((match = this.xmlnsRegex.exec(rawAttrStr)) !== null) { const fullAttr = match[1]; const prefix2 = match[2]; const uri2 = match[3]; if (prefix2) { currentNamespaces.set(prefix2, uri2); } else { currentNamespaces.set("", uri2); } attributes[fullAttr] = uri2; attributesWithPrefix[fullAttr] = { value: uri2, localName: prefix2 || "xmlns", prefix: prefix2 ? "xmlns" : undefined, uri: undefined }; } this.attrRegex.lastIndex = 0; let attrMatch; while ((attrMatch = this.attrRegex.exec(rawAttrStr)) !== null) { const fullAttrName = attrMatch[1]; if (fullAttrName.startsWith("xmlns")) continue; const attrValue = attrMatch[2] ? this._unescapeXml(attrMatch[2]) : "true"; attributes[fullAttrName] = attrValue; const { localName: localName2, prefix: prefix2, uri: uri2 } = this._parseQualifiedName(fullAttrName, currentNamespaces, true); attributesWithPrefix[fullAttrName] = { value: attrValue, localName: localName2, prefix: prefix2, uri: uri2 }; } const { localName, prefix, uri } = this._parseQualifiedName(rawTagName, currentNamespaces, false); yield { type: "START_ELEMENT" /* START_ELEMENT */, name: rawTagName, localName, prefix, uri, attributes, attributesWithPrefix }; this.elementStack.push(rawTagName); if (!isSelfClosing) { this.namespaceStack.push(currentNamespaces); } else { yield { type: "END_ELEMENT" /* END_ELEMENT */, name: rawTagName, localName, prefix, uri }; this.elementStack.pop(); } this.pos = tagClose + 1; } _findTagEnd(startIndex) { let i = startIndex; let inQuote = false; let quoteChar = ""; while (i < this.xml.length) { const char = this.xml[i]; if (char === "'" || char === '"') { if (!inQuote) { inQuote = true; quoteChar = char; } else if (char === quoteChar) { inQuote = false; quoteChar = ""; } } else if (char === ">" && !inQuote) { return i; } i++; } return -1; } _unescapeXml(text) { if (!text) { return ""; } if (!this.options.autoDecodeEntities) { return text; } let entityMap = { "&lt;": "<", "&gt;": ">", "&quot;": '"', "&apos;": "'", ...this.options.addEntities?.reduce((map, entity) => { if (entity.entity && entity.value) { map[entity.entity] = entity.value; } return map; }, {}), "&amp;": "&" }; const regex = new RegExp(Object.keys(entityMap).map((key) => key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g"); return text.replace(regex, (match) => { if (entityMap[match]) { return entityMap[match]; } else { return match; } }); } _parseQualifiedName(qname, namespaces, isAttribute = false) { const colonIndex = qname.indexOf(":"); if (colonIndex === -1) { if (isAttribute) { return { localName: qname, prefix: undefined, uri: undefined }; } else { const defaultUri = namespaces.get(""); return { localName: qname, prefix: undefined, uri: defaultUri }; } } else { const prefix = qname.substring(0, colonIndex); const localName = qname.substring(colonIndex + 1); const uri = namespaces.get(prefix); return { localName, prefix, uri }; } } } export { XmlEventType, StaxXmlWriter_default as StaxXmlWriter, StaxXmlParserSync, StaxXmlParser_default as StaxXmlParser };