{"version":3,"file":"browser.mjs","names":[],"sources":["../../src/main/browser.ts"],"sourcesContent":["/**\n * neosanitize, browser entry (resolved via the package's `browser` export\n * condition; also reachable explicitly as `neosanitize/browser`).\n *\n * Parses untrusted HTML with the PLATFORM parser (native `DOMParser`) instead of\n * the bundled WHATWG parser, then runs the EXACT same engine-core policy over the\n * resulting tree. Two wins:\n *   1. The browser bundle ships ZERO parser bytes, the tokenizer + tree-builder\n *      (the bulk of the engine) are never imported here, only the policy core.\n *   2. Parsing is, by construction, byte-for-byte what the user's own browser\n *      would do, which closes parser-differential / mutation-XSS gaps for free.\n *\n * Same `Sanitizer` class API as the default entry; the only difference is the\n * parse step (and that this build requires a DOM).\n */\nexport * from './core';\n\nimport { SanitizerCore, type ParseAdapter, type Policy } from './core';\nimport type { ElementNode, ParentNode, TreeNode, NS } from './core';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst MATHML_NS = 'http://www.w3.org/1998/Math/MathML';\n\n/**\n * The browser default parse adapter, native `DOMParser`. Ships zero parser bytes\n * (the platform already has one) and parses byte-for-byte what the user's browser\n * would. Used automatically by the browser `Sanitizer`; also exported so you can\n * pass it to any `Sanitizer` via `.parser(domParserAdapter)` where a DOM exists.\n */\nexport const domParserAdapter: ParseAdapter = (html) => {\n  if (typeof DOMParser === 'undefined') {\n    throw new Error('neosanitize/browser: no DOM available (DOMParser is undefined). In Node, import the default \"neosanitize\" entry, which bundles the parser.');\n  }\n  const doc = new DOMParser().parseFromString(html, 'text/html');\n  const root: ParentNode = { type: 'document', children: [] };\n  if (doc.documentElement) root.children.push(domToNode(doc.documentElement));\n  return root;\n};\n\n/**\n * Browser `Sanitizer`, defaults to native `DOMParser`. Build one with\n * `Sanitizer.builder()`. Override the parser with `.parser(adapter)`, e.g.\n * `whatwgAdapter` to force the bundled parser, or `parse5Adapter`.\n */\nexport class Sanitizer extends SanitizerCore {\n  constructor(policy?: Policy, parser: ParseAdapter | null = null) {\n    super(policy, domParserAdapter, parser);\n  }\n}\n\n/**\n * Convert a native DOM element subtree into the engine-core node shape, the same\n * plain `{type,name,attrs,children}` tree the custom parser produces, so the\n * policy + serializer are reused verbatim (zero security-logic fork per env).\n */\nfunction domToNode(el: Element): ElementNode {\n  let ns: NS = 'html';\n  /* v8 ignore start -- happy-dom (the test DOM) leaves <math> in the HTML namespace, so the MathML branch can't be exercised here; real browsers do namespace it */\n  if (el.namespaceURI === SVG_NS) ns = 'svg';\n  else if (el.namespaceURI === MATHML_NS) ns = 'mathml';\n  /* v8 ignore stop */\n  const attrs: Array<[string, string]> = [];\n  const a = el.attributes;\n  for (let i = 0; i < a.length; i++) {\n    let name = a[i].name;\n    // foreign namespaced attrs (xlink:href, xml:lang) -> space-form, matching the\n    // custom parser's storage so URL_ATTRS / allow-list checks line up.\n    if (ns !== 'html' && name.indexOf(':') !== -1) name = name.replace(':', ' ');\n    attrs.push([name, a[i].value]);\n  }\n  // <template> content lives in a separate fragment, not in childNodes.\n  const kids: NodeListOf<ChildNode> =\n    ns === 'html' && el.localName === 'template' && (el as HTMLTemplateElement).content\n      ? (el as HTMLTemplateElement).content.childNodes\n      : el.childNodes;\n  const children: TreeNode[] = [];\n  for (let i = 0; i < kids.length; i++) {\n    const c = kids[i];\n    const t = c.nodeType;\n    if (t === 1) children.push(domToNode(c as Element));\n    else if (t === 3) children.push({ type: 'text', value: (c as CharacterData).data, parent: null });\n    // comments (8) and others are dropped, the serializer drops them anyway.\n    // (CDATA sections / nodeType 4 never occur via parseFromString(html,'text/html').)\n  }\n  return { type: 'element', name: el.localName, namespace: ns, attrs, children, parent: null };\n}\n"],"mappings":";;AAoBA,MAAM,SAAS;AACf,MAAM,YAAY;;;;;;;AAQlB,MAAa,oBAAkC,SAAS;CACtD,IAAI,OAAO,cAAc,aACvB,MAAM,IAAI,MAAM,8IAA4I;CAE9J,MAAM,MAAM,IAAI,UAAU,EAAE,gBAAgB,MAAM,WAAW;CAC7D,MAAM,OAAmB;EAAE,MAAM;EAAY,UAAU,CAAC;CAAE;CAC1D,IAAI,IAAI,iBAAiB,KAAK,SAAS,KAAK,UAAU,IAAI,eAAe,CAAC;CAC1E,OAAO;AACT;;;;;;AAOA,IAAa,YAAb,cAA+B,cAAc;CAC3C,YAAY,QAAiB,SAA8B,MAAM;EAC/D,MAAM,QAAQ,kBAAkB,MAAM;CACxC;AACF;;;;;;AAOA,SAAS,UAAU,IAA0B;CAC3C,IAAI,KAAS;;CAEb,IAAI,GAAG,iBAAiB,QAAQ,KAAK;MAChC,IAAI,GAAG,iBAAiB,WAAW,KAAK;;CAE7C,MAAM,QAAiC,CAAC;CACxC,MAAM,IAAI,GAAG;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;EACjC,IAAI,OAAO,EAAE,GAAG;EAGhB,IAAI,OAAO,UAAU,KAAK,QAAQ,GAAG,MAAM,IAAI,OAAO,KAAK,QAAQ,KAAK,GAAG;EAC3E,MAAM,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC;CAC/B;CAEA,MAAM,OACJ,OAAO,UAAU,GAAG,cAAc,cAAe,GAA2B,UACvE,GAA2B,QAAQ,aACpC,GAAG;CACT,MAAM,WAAuB,CAAC;CAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,EAAE;EACZ,IAAI,MAAM,GAAG,SAAS,KAAK,UAAU,CAAY,CAAC;OAC7C,IAAI,MAAM,GAAG,SAAS,KAAK;GAAE,MAAM;GAAQ,OAAQ,EAAoB;GAAM,QAAQ;EAAK,CAAC;CAGlG;CACA,OAAO;EAAE,MAAM;EAAW,MAAM,GAAG;EAAW,WAAW;EAAI;EAAO;EAAU,QAAQ;CAAK;AAC7F"}