UNPKG

lambda-live-debugger

Version:

Debug Lambda functions locally like it is running in the cloud

235 lines (234 loc) 7.1 kB
export function parseXML(xml) { const state = new AwsXmlParser(xml); return state.parse(); } class AwsXmlParser { x; i = 0; z; constructor(x) { this.x = x; this.x = x.replace(/\r\n?/g, "\n"); this.z = this.x.length; } parse() { const p = this; const { z } = p; while (p.i < z) { p.trim(); if (p.i >= z) { break; } if (p.isNext("<?")) { p.readTo("?>"); p.trim(); } else if (p.isNext("<!--")) { p.readTo("-->"); p.trim(); } else if (p.isNext("<!DOCTYPE", false)) { p.skipDoctype(); p.trim(); } else if (p.x[p.i] === "<") { const root = p.parseTag(); return { [root.tag]: root.value }; } else { throw new Error("@aws-sdk XML parse error: unexpected content."); } } throw new Error("@aws-sdk XML parse error: no root element."); } isNext(s, caseSensitive = true) { const p = this; if (caseSensitive) { return p.x.startsWith(s, p.i); } return p.x.toLowerCase().startsWith(s.toLowerCase(), p.i); } readTo(stop) { const p = this; const _i = p.x.indexOf(stop, p.i); if (_i === -1) { throw new Error(`@aws-sdk XML parse error: expected "${stop}" not found.`); } const result = p.x.slice(p.i, _i); p.i = _i + stop.length; return result; } trim() { const p = this; while (p.i < p.z && " \t\r\n".includes(p.x[p.i])) { ++p.i; } } readAttrValue() { const p = this; const quote = p.x[p.i]; ++p.i; let value = ""; while (p.i < p.z && p.x[p.i] !== quote) { value += p.x[p.i++]; } ++p.i; return p.decodeEntities(value); } parseTag() { const p = this; ++p.i; let tag = ""; while (p.i < p.z && !" \t\r\n>/".includes(p.x[p.i])) { tag += p.x[p.i++]; } let hasAttrs = false; const attrs = Object.create(null); while (p.i < p.z) { p.trim(); if (">/".includes(p.x[p.i])) { break; } let name = ""; while (p.i < p.z && !"= \t\r\n>/?".includes(p.x[p.i])) { name += p.x[p.i++]; } p.trim(); if (p.x[p.i] !== "=") { break; } ++p.i; p.trim(); attrs[name] = p.readAttrValue(); hasAttrs = true; } if (p.i >= p.z) { throw new Error("@aws-sdk XML parse error: unexpected end of input."); } if (p.x[p.i] === "/") { ++p.i; if (p.i >= p.z || p.x[p.i] !== ">") { throw new Error("@aws-sdk XML parse error: expected > at the end of self-closing tag."); } ++p.i; Object.setPrototypeOf(attrs, Object.prototype); return { tag, value: hasAttrs ? attrs : "" }; } if (p.x[p.i] !== ">") { throw new Error("@aws-sdk XML parse error: expected > at the end of opening tag."); } ++p.i; const textParts = []; const childTags = []; let hasElementChild = false; while (p.i < p.z) { if (p.isNext("</")) { break; } if (p.x[p.i] === "<") { if (p.isNext("<!--")) { p.readTo("-->"); } else if (p.isNext("<![CDATA[")) { p.i += 9; textParts.push(p.readTo("]]>")); } else if (p.isNext("<?")) { p.readTo("?>"); } else { hasElementChild = true; childTags.push(p.parseTag()); } } else { let text = ""; while (p.i < p.z && p.x[p.i] !== "<") { text += p.x[p.i++]; } textParts.push(p.decodeEntities(text)); } } if (!p.isNext("</")) { throw new Error(`@aws-sdk XML parse error: missing closing tag </${tag}>.`); } p.i += 2; const closeTag = p.readTo(">").trim(); if (closeTag !== tag) { throw new Error(`@aws-sdk XML parse error: mismatched tags <${tag}> and </${closeTag}>.`); } if (!hasAttrs && textParts.length === 0 && !hasElementChild) { return { tag, value: "" }; } if (!hasAttrs && !hasElementChild) { const text = textParts.length === 1 ? textParts[0] : textParts.join(""); if (text.trim() === "" && text.includes("\n")) { return { tag, value: "" }; } return { tag, value: text }; } const obj = Object.create(null); for (const text of textParts) { if (text.trim() === "" && text.includes("\n")) { continue; } obj["#text"] = "#text" in obj ? obj["#text"] + text : text; } for (const child of childTags) { if (child.tag in obj) { if (Array.isArray(obj[child.tag])) { obj[child.tag].push(child.value); } else { obj[child.tag] = [obj[child.tag], child.value]; } } else { obj[child.tag] = child.value; } } for (const [k, v] of Object.entries(attrs)) { obj[k] = v; } Object.setPrototypeOf(obj, Object.prototype); return { tag, value: obj }; } static ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", }; skipDoctype() { const p = this; p.i += 9; let depth = 0; while (p.i < p.z) { const c = p.x[p.i]; if (c === "[") { ++depth; } else if (c === "]") { --depth; } else if (c === ">" && depth === 0) { ++p.i; return; } ++p.i; } throw new Error("@aws-sdk XML parse error: unclosed DOCTYPE."); } decodeEntities(s) { return s.replace(/&(?:#x([0-9a-fA-F]{1,6})|#(\d{1,7})|([a-zA-Z][a-zA-Z0-9]{0,30}));/g, (_, hex, dec, named) => { if (hex) { return String.fromCharCode(parseInt(hex, 16)); } if (dec) { return String.fromCharCode(parseInt(dec, 10)); } return AwsXmlParser.ENTITIES[named] ?? ""; }); } }