{"version":3,"file":"parser-By6RWZVW.mjs","names":[],"sources":["../src/xml/parser.ts"],"sourcesContent":["// DOM-style XML parser. fast-xml-parser does the lexing, then we walk its\n// preserveOrder tree to:\n//   1. resolve `prefix:local` element + attribute names to Clark notation\n//      (`{ns}local`) using a namespace-declaration stack;\n//   2. fold text segments into XmlNode.text for text-only elements;\n//   3. drop XML declarations and processing instructions.\n//\n// DOCTYPE / external entity declarations are rejected outright via a byte-level\n// prescan before the parser ever sees the input — fast-xml-parser does not\n// expand external entities, but we still want the offending document to fail\n// loudly.\n\nimport { XMLParser } from 'fast-xml-parser';\nimport { OpenXmlSchemaError } from '../utils/exceptions.js';\nimport { qname } from './namespaces.js';\nimport { el, type XmlNode } from './tree.js';\n\n// ---- DOCTYPE / DTD prescan --------------------------------------------------\n\nconst decoder = new TextDecoder('utf-8', { fatal: false });\n\nconst decodeForPrescan = (input: Uint8Array | string): string => {\n  if (typeof input === 'string') return input;\n  return decoder.decode(input);\n};\n\nconst checkForDoctype = (text: string): void => {\n  // Strip XML declaration so any subsequent `<!DOCTYPE` is the real thing. The\n  // declaration is always the first non-BOM token in well-formed XML.\n  const stripped = text.replace(/^﻿/, '');\n  if (/<!DOCTYPE\\b/.test(stripped)) {\n    throw new OpenXmlSchemaError('DTD declarations are not permitted in OOXML payloads');\n  }\n  if (/<!ENTITY\\b/.test(stripped)) {\n    throw new OpenXmlSchemaError('Entity declarations are not permitted in OOXML payloads');\n  }\n};\n\n// ---- fast-xml-parser configuration ------------------------------------------\n\nconst TEXT_KEY = '#text';\nconst CDATA_KEY = '#cdata';\n\nconst parser = new XMLParser({\n  preserveOrder: true,\n  ignoreAttributes: false,\n  attributeNamePrefix: '',\n  attributesGroupName: ':@',\n  trimValues: false,\n  parseTagValue: false,\n  parseAttributeValue: false,\n  // Decode entities ourselves below. fast-xml-parser expands the five named\n  // XML entities but leaves numeric character references untouched. Keeping\n  // its processing disabled also lets us preserve XML's single-pass semantics\n  // (`&amp;#65;` is the literal text \"&#65;\", not \"A\").\n  processEntities: false,\n  htmlEntities: false,\n  // Without this, CDATA content is merged into `#text` and would go through\n  // entity decoding, but CDATA is literal: `<![CDATA[&amp;]]>` is \"&amp;\".\n  cdataPropName: CDATA_KEY,\n});\n\nconst XML_ENTITY_RE = /&(amp|lt|gt|quot|apos|#(?:[0-9]+|x[0-9A-Fa-f]+));/g;\n\n/** Decode the five predefined XML entities and decimal/hex character refs once. */\nconst decodeXmlEntities = (value: string): string =>\n  value.replace(XML_ENTITY_RE, (reference, body: string) => {\n    switch (body) {\n      case 'amp':\n        return '&';\n      case 'lt':\n        return '<';\n      case 'gt':\n        return '>';\n      case 'quot':\n        return '\"';\n      case 'apos':\n        return \"'\";\n      default: {\n        const radix = body[1] === 'x' ? 16 : 10;\n        const digits = radix === 16 ? body.slice(2) : body.slice(1);\n        const codePoint = Number.parseInt(digits, radix);\n        if (!isXmlCodePoint(codePoint)) {\n          throw new OpenXmlSchemaError(`parseXml: invalid XML character reference \"${reference}\"`);\n        }\n        return String.fromCodePoint(codePoint);\n      }\n    }\n  });\n\nconst isXmlCodePoint = (codePoint: number): boolean =>\n  codePoint === 0x09 ||\n  codePoint === 0x0a ||\n  codePoint === 0x0d ||\n  (codePoint >= 0x20 && codePoint <= 0xd7ff) ||\n  (codePoint >= 0xe000 && codePoint <= 0xfffd) ||\n  (codePoint >= 0x10000 && codePoint <= 0x10ffff);\n\n// ---- preserveOrder shape ----------------------------------------------------\n\ntype FxpAttrs = Record<string, string>;\ntype FxpEntry = { ':@'?: FxpAttrs } & { [tagOrText: string]: FxpEntry[] | string | FxpAttrs | undefined };\ntype FxpTree = FxpEntry[];\n\nconst ATTR_KEY = ':@';\n\n// ---- public API -------------------------------------------------------------\n\nexport interface ParsedDocument {\n  root: XmlNode;\n  /**\n   * The root element's own `xmlns` declarations, in source order (`prefix` is\n   * `''` for the default one). Clark notation carries the namespace but not\n   * the prefix, so these are only needed by parts that must be written back\n   * with the prefixes the producer chose — `xl/workbook.xml`, whose\n   * `mc:Ignorable` names them by prefix.\n   */\n  rootNamespaces: Array<{ prefix: string; ns: string }>;\n}\n\n/**\n * Parse a UTF-8 XML payload into an {@link XmlNode} tree. Element and attribute\n * names are returned in Clark notation. Throws {@link OpenXmlSchemaError} on\n * DTD/entity declarations or on multi-root documents.\n *\n * Shorthand for {@link parseXmlDocument} when the root's namespace prefixes\n * don't matter — which is everywhere except the handful of parts that carry an\n * `mc:Ignorable`.\n */\nexport function parseXml(input: Uint8Array | string): XmlNode {\n  return parseXmlDocument(input).root;\n}\n\n/** {@link parseXml} plus the root element's namespace declarations. */\nexport function parseXmlDocument(input: Uint8Array | string): ParsedDocument {\n  const text = decodeForPrescan(input);\n  checkForDoctype(text);\n\n  let raw: FxpTree;\n  try {\n    raw = parser.parse(text) as FxpTree;\n  } catch (cause) {\n    throw new OpenXmlSchemaError('parseXml: failed to parse XML payload', { cause });\n  }\n\n  // Skip XML declaration, processing instructions and any leading whitespace\n  // text nodes.\n  const roots: FxpEntry[] = [];\n  for (const entry of raw) {\n    const tag = elementTag(entry);\n    if (tag === undefined) continue; // text-only entry\n    if (isProcessingInstruction(tag)) continue; // <?xml …?>, other PIs\n    roots.push(entry);\n  }\n  if (roots.length === 0) {\n    throw new OpenXmlSchemaError('parseXml: document has no root element');\n  }\n  if (roots.length > 1) {\n    throw new OpenXmlSchemaError(`parseXml: document has ${roots.length} root elements; expected exactly one`);\n  }\n\n  const initial: NamespaceStack = { default: '', byPrefix: {} };\n  const [root] = roots;\n  if (root === undefined) {\n    throw new OpenXmlSchemaError('parseXml: no root element');\n  }\n  return { root: convertElement(root, initial), rootNamespaces: declarationsOf(root[ATTR_KEY] as FxpAttrs | undefined) };\n}\n\nconst declarationsOf = (attrs: FxpAttrs | undefined): Array<{ prefix: string; ns: string }> => {\n  const out: Array<{ prefix: string; ns: string }> = [];\n  for (const [k, v] of Object.entries(attrs ?? {})) {\n    if (k === 'xmlns') out.push({ prefix: '', ns: decodeXmlEntities(v) });\n    else if (k.startsWith('xmlns:')) out.push({ prefix: k.slice('xmlns:'.length), ns: decodeXmlEntities(v) });\n  }\n  return out;\n};\n\n// ---- conversion -------------------------------------------------------------\n\ninterface NamespaceStack {\n  /** Default namespace URI (xmlns=\"…\"); empty string means no default. */\n  readonly default: string;\n  /** Map of prefix → namespace URI declared in this scope or any ancestor. */\n  readonly byPrefix: Readonly<Record<string, string>>;\n}\n\nconst elementTag = (entry: FxpEntry): string | undefined => {\n  for (const k of Object.keys(entry)) {\n    if (k === ATTR_KEY) continue;\n    return k;\n  }\n  return undefined;\n};\n\nconst isProcessingInstruction = (tag: string): boolean => tag.startsWith('?');\n\nconst splitPrefixed = (qname0: string): { prefix: string; local: string } => {\n  const idx = qname0.indexOf(':');\n  if (idx < 0) return { prefix: '', local: qname0 };\n  return { prefix: qname0.slice(0, idx), local: qname0.slice(idx + 1) };\n};\n\nconst extendStack = (parent: NamespaceStack, attrs: FxpAttrs | undefined): NamespaceStack => {\n  if (attrs === undefined) return parent;\n  let nextDefault = parent.default;\n  let nextByPrefix: Record<string, string> | undefined;\n  for (const [k, v] of Object.entries(attrs)) {\n    if (k === 'xmlns') {\n      nextDefault = decodeXmlEntities(v);\n      continue;\n    }\n    if (k.startsWith('xmlns:')) {\n      const prefix = k.slice('xmlns:'.length);\n      nextByPrefix ??= { ...parent.byPrefix };\n      nextByPrefix[prefix] = decodeXmlEntities(v);\n    }\n  }\n  if (nextDefault === parent.default && nextByPrefix === undefined) return parent;\n  return {\n    default: nextDefault,\n    byPrefix: nextByPrefix ?? parent.byPrefix,\n  };\n};\n\nconst resolveElementName = (raw: string, stack: NamespaceStack): string => {\n  const { prefix, local } = splitPrefixed(raw);\n  if (prefix === '') return qname(stack.default, local);\n  const ns = stack.byPrefix[prefix];\n  if (ns === undefined) {\n    throw new OpenXmlSchemaError(`parseXml: undeclared namespace prefix \"${prefix}\" on element <${raw}>`);\n  }\n  return qname(ns, local);\n};\n\nconst resolveAttrName = (raw: string, stack: NamespaceStack): string => {\n  const { prefix, local } = splitPrefixed(raw);\n  // Unprefixed attributes do NOT inherit the default namespace (XMLNS spec).\n  if (prefix === '') return local;\n  if (prefix === 'xml') return qname('http://www.w3.org/XML/1998/namespace', local);\n  const ns = stack.byPrefix[prefix];\n  if (ns === undefined) {\n    throw new OpenXmlSchemaError(`parseXml: undeclared namespace prefix \"${prefix}\" on attribute \"${raw}\"`);\n  }\n  return qname(ns, local);\n};\n\nconst filterAttrs = (rawAttrs: FxpAttrs | undefined, stack: NamespaceStack): { resolved: Record<string, string> } => {\n  const resolved: Record<string, string> = {};\n  if (rawAttrs === undefined) return { resolved };\n  for (const [k, v] of Object.entries(rawAttrs)) {\n    // xmlns / xmlns:* declarations: dropped from the XmlNode attribute table.\n    // The serializer rebuilds them from the Clark-notation namespaces it walks,\n    // so round-tripping does not require preserving the declarations.\n    if (k === 'xmlns' || k.startsWith('xmlns:')) continue;\n    resolved[resolveAttrName(k, stack)] = decodeXmlEntities(v);\n  }\n  return { resolved };\n};\n\nconst isWhitespaceOnly = (s: string): boolean => /^\\s*$/.test(s);\n\nconst convertElement = (entry: FxpEntry, parentStack: NamespaceStack): XmlNode => {\n  const rawTag = elementTag(entry);\n  if (rawTag === undefined) {\n    throw new OpenXmlSchemaError('parseXml: encountered an entry with no element tag');\n  }\n  if (isProcessingInstruction(rawTag)) {\n    throw new OpenXmlSchemaError(`parseXml: processing instructions are not supported (saw \"<${rawTag}>\")`);\n  }\n\n  const rawAttrs = entry[ATTR_KEY] as FxpAttrs | undefined;\n  const stack = extendStack(parentStack, rawAttrs);\n\n  const { resolved } = filterAttrs(rawAttrs, stack);\n  const node = el(resolveElementName(rawTag, stack), resolved);\n\n  const childEntries = entry[rawTag] as FxpEntry[] | undefined;\n  if (childEntries === undefined || childEntries.length === 0) return node;\n\n  const textParts: string[] = [];\n  for (const child of childEntries) {\n    if (Object.hasOwn(child, TEXT_KEY)) {\n      const t = child[TEXT_KEY];\n      if (typeof t === 'string') textParts.push(decodeXmlEntities(t));\n      continue;\n    }\n    if (Object.hasOwn(child, CDATA_KEY)) {\n      const section = child[CDATA_KEY] as FxpEntry[];\n      for (const part of section) {\n        const t = part[TEXT_KEY];\n        if (typeof t === 'string') textParts.push(t);\n      }\n      continue;\n    }\n    if (textParts.length > 0 && node.children.length === 0) {\n      // text accumulated *before* any child element — keep collecting.\n    } else if (textParts.length > 0 && node.children.length > 0) {\n      const acc = textParts.join('');\n      if (!isWhitespaceOnly(acc)) {\n        throw new OpenXmlSchemaError(`parseXml: mixed content not supported (text between elements under <${rawTag}>)`);\n      }\n      // whitespace-only inter-element text: drop.\n      textParts.length = 0;\n    }\n    node.children.push(convertElement(child, stack));\n  }\n  // Trailing text after the last child element.\n  if (textParts.length > 0) {\n    const acc = textParts.join('');\n    if (node.children.length === 0) {\n      // text-only element (the common case): keep as the element's text.\n      node.text = acc;\n    } else if (!isWhitespaceOnly(acc)) {\n      throw new OpenXmlSchemaError(`parseXml: mixed content not supported (trailing text under <${rawTag}>)`);\n    }\n  }\n  return node;\n};\n"],"mappings":"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"}