{"version":3,"file":"core-CkfWe6p7.mjs","names":[],"sources":["../src/main/core.ts"],"sourcesContent":["/**\n * neosanitize, engine core (parser-agnostic policy + serializer).\n *\n * This module holds EVERYTHING except the parse step: the deny-by-default policy,\n * the inviolable safe baseline, the URL/CSS checks, and the string/text/DOM\n * serializers. It is the single source of truth for every security decision, so\n * the two entry points can NEVER drift:\n *\n *   • `./index`   (default / Node), `parse()` runs the custom WHATWG parser.\n *   • `./browser` (browser bundle), `parse()` runs the native `DOMParser`.\n *\n * Both subclass {@link SanitizerCore} and supply only `parse()`; the browser\n * build therefore ships ZERO parser bytes (it reuses the platform's parser) yet\n * shares 100% of this policy code. The node shape produced by either parser is\n * the same plain `{type,name,attrs,children}` tree (see ./parser/tree-builder).\n *\n * API is CLASS-ONLY by mandate: you BUILD a `Sanitizer` (via `Sanitizer.builder()`\n * or `new SanitizerBuilder(Sanitizer)`) and call `.sanitize()`. There is\n * deliberately NO one-shot functional `sanitize(html)` helper, constructing a\n * sanitizer forces an explicit policy choice (no careless implicit default), and\n * the policy compilation happens ONCE in the constructor so `.sanitize()` is cheap\n * to call repeatedly.\n */\nimport type { ElementNode, ParentNode } from './parser/tree-builder';\n\n// Re-export the (runtime-free) node-shape types so entry points and advanced\n// users can build/consume the common tree without importing the parser.\nexport type { ElementNode, TextNode, CommentNode, DoctypeNode, DocumentNode, TreeNode, ParentNode, NS } from './parser/tree-builder';\n\n/**\n * A parse adapter turns untrusted HTML into the common `{type,name,attrs,children}`\n * tree the policy engine consumes. This is the ONLY pluggable seam: every entry\n * supplies an environment-appropriate default (the bundled WHATWG parser in Node,\n * native `DOMParser` in the browser), and `Sanitizer.builder().parser(adapter)`\n * overrides it with any other, e.g. the `parse5` adapter from `neosanitize/parse5`,\n * or your own. The policy + serializer are reused verbatim regardless of parser.\n */\nexport type ParseAdapter = (html: string) => ParentNode;\n\nexport const version = '0.0.0-dev';\n\n// ---------------------------------------------------------------------------\n// Inviolable safe baseline (applied unless policy.allowUnsafe, mirrors the\n// native setHTML() safe path). These hold EVEN IF the allow-list permits them;\n// only `sanitizeUnsafe()` skips them.\n// ---------------------------------------------------------------------------\n/** Elements always dropped WITH their content under the baseline. */\nconst BASELINE_DROP = new Set(['script']);\n/** Disallowed elements whose CONTENT is also dropped (not unwrapped), raw-text\n * / metadata elements whose children aren't renderable text. */\nconst DROP_CONTENT_WHEN_DISALLOWED = new Set(['script', 'style', 'textarea', 'option', 'xmp', 'noscript', 'noembed', 'noframes', 'iframe', 'title', 'template']);\n/** Void elements, serialized with no end tag and no children. */\nconst VOID_ELEMENTS = new Set(['area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']);\n/** Raw-text elements, their text children serialize unescaped. */\nconst RAW_TEXT_ELEMENTS = new Set(['script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', 'noscript', 'plaintext']);\n/** Attributes interpreted as URLs (for the baseline scheme check). `xlink href`\n * is the space-stored form of the foreign `xlink:href`. */\nconst URL_ATTRS = new Set(['href', 'src', 'action', 'formaction', 'background', 'cite', 'longdesc', 'poster', 'data', 'srcdoc', 'manifest', 'xlink href']);\n\n// ---------------------------------------------------------------------------\n// Policy (resolved, immutable). Deliberately minimal for the scaffold, the\n// real shape firms up alongside the engine. Presets/url/css live in their own\n// tree-shakeable subpath modules and are passed INTO the builder.\n// ---------------------------------------------------------------------------\nexport interface Policy {\n  /** Allowed element names. Deny-by-default: everything else is dropped. */\n  readonly tags: ReadonlySet<string>;\n  /** Allowed attributes, per tag (`*` = any tag). */\n  readonly attrs: ReadonlyMap<string, ReadonlySet<string>>;\n  /**\n   * When false (the safe default), an inviolable baseline still strips\n   * known-unsafe constructs (script, event handlers, javascript: URLs, …) even\n   * if `tags`/`attrs` would allow them. `sanitizeUnsafe()` sets this true.\n   */\n  readonly allowUnsafe: boolean;\n}\n\n/** A partial policy or a named preset, accepted anywhere a policy is. */\nexport type PolicyInput = Partial<MutablePolicy> | Preset;\n\n/**\n * Brand symbol identifying a value as a built {@link Preset}. Exported so preset\n * modules (e.g. `neosanitize/presets/*`) and advanced users authoring their own\n * presets can stamp it, the `UNSAFE_` name signals that hand-stamping bypasses\n * normal `Sanitizer` construction and is the caller's responsibility.\n */\nexport const UNSAFE_PRESET_SYMBOL: unique symbol = Symbol('neosanitize.preset');\n\nexport interface Preset {\n  readonly [UNSAFE_PRESET_SYMBOL]: true;\n  readonly name: string;\n  readonly policy: Policy;\n}\n\ninterface MutablePolicy {\n  tags: Iterable<string>;\n  attrs: Record<string, Iterable<string>>;\n  allowUnsafe: boolean;\n}\n\nconst EMPTY_POLICY: Policy = {\n  tags: new Set(),\n  attrs: new Map(),\n  allowUnsafe: false\n};\n\n/** Fallback when `SanitizerCore` is constructed directly with no parser. Every\n * shipped entry (`neosanitize`, `neosanitize/browser`) supplies a real default,\n * so this only fires if you instantiate the base class without one. */\nconst THROW_NO_PARSER: ParseAdapter = () => {\n  throw new Error('neosanitize: no parse adapter. Use the `Sanitizer` from \"neosanitize\" (or \"neosanitize/browser\"), or pass one via `.parser(adapter)`.');\n};\n\n/** Precomputed serialize data for one allow-listed (\"keep\") tag. See `tagCache`. */\ninterface TagSer {\n  /** The open-tag prefix `<name` (sans attributes and `>`). */\n  readonly open: string;\n  /** The full end tag `</name>`. */\n  readonly close: string;\n  /** Void element, no end tag, no children. */\n  readonly isVoid: boolean;\n  /** Raw-text element, text children serialize unescaped. */\n  readonly rawText: boolean;\n  /** Resolved allow-listed attributes (tag-specific ∪ `*`); null = none allowed. */\n  readonly attrSet: ReadonlySet<string> | null;\n}\n\n// Minimal Trusted Types shapes, not in the configured DOM lib, and we stay\n// zero-dep; structurally compatible with the real browser globals.\nexport interface TrustedHTML {\n  toString(): string;\n}\ninterface TrustedTypePolicy {\n  createHTML(input: string): TrustedHTML;\n}\ninterface TrustedTypePolicyFactory {\n  createPolicy(name: string, rules: { createHTML: (s: string) => string }): TrustedTypePolicy;\n}\n\n/** One element/attribute/value removed during sanitization (report mode). */\nexport interface Removal {\n  readonly kind: 'tag' | 'attr' | 'url' | 'style';\n  readonly name: string;\n  readonly reason: string;\n}\n/** Result of {@link SanitizerCore.sanitizeWithReport}. */\nexport interface SanitizeReport {\n  readonly html: string;\n  readonly removed: Removal[];\n}\n\n/** A destination for {@link SanitizerCore.sanitizeTo}: either a chunk callback or\n * any object with a Node-style `write` (an HTTP response, `fs` write stream, …). */\nexport type SanitizeSink = ((chunk: string) => void) | { write(chunk: string): unknown };\n/** Options for {@link SanitizerCore.sanitizeTo}. */\nexport interface SanitizeToOptions {\n  /** Target write size in characters; fragments are batched up to this before a\n   * flush, so the sink isn't hit with a write per tag. Default 16384. */\n  readonly chunkSize?: number;\n}\n\n/** Minimal push target the serializer writes fragments to, satisfied by both a\n * plain `string[]` (collect-then-join) and {@link ChunkWriter} (stream). */\ninterface StringSink {\n  push(s: string): void;\n}\n\n/** Streaming sink for `sanitizeTo`: batches serializer fragments and flushes them\n * to the user's sink in ~chunkSize-character writes. */\nclass ChunkWriter implements StringSink {\n  private parts: string[] = [];\n  private pending = 0;\n  constructor(private readonly sink: (chunk: string) => void, private readonly chunkSize: number) {}\n  push(s: string): void {\n    this.parts.push(s);\n    this.pending += s.length;\n    if (this.pending >= this.chunkSize) this.flush();\n  }\n  flush(): void {\n    if (this.parts.length !== 0) {\n      this.sink(this.parts.join(''));\n      this.parts = [];\n      this.pending = 0;\n    }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// SanitizerCore, the compiled, reusable base object. Abstract over the parser:\n// a concrete subclass supplies `parse()` (custom WHATWG parser, or native DOM).\n// ---------------------------------------------------------------------------\n// Hoisted once, a regex literal in a function body allocates a new object per\n// call; these run per text node / per attribute / per URL on the hot path.\nconst RE_CSS_CTRL = /[\\u0000-\\u001f]/;\nconst RE_WS_G = /\\s+/g;\nconst RE_QUOTES_G = /['\"]/g;\nconst RE_TEXT_NEEDS = /[&<>\\u00a0]/;\nconst RE_ATTR_NEEDS = /[&\"\\u00a0]/;\nconst RE_AMP_G = /&/g;\nconst RE_LT_G = /</g;\nconst RE_GT_G = />/g;\nconst RE_QUOT_G = /\"/g;\nconst RE_NBSP_G = /\\u00a0/g;\n\nexport class SanitizerCore {\n  /** Compiled, immutable policy. */\n  readonly policy: Policy;\n\n  /** The environment default parser, supplied by the concrete entry subclass\n   * (bundled WHATWG parser in Node, native `DOMParser` in the browser). */\n  private readonly defaultParse: ParseAdapter;\n  /** An explicit per-instance override from `.parser(adapter)`, or `null` to use\n   * the environment default. Carried through `sanitizeUnsafe`'s re-parse. */\n  private readonly parserOverride: ParseAdapter | null;\n\n  /** Per-tag serialize cache, built once from the policy. Holds ONLY tags that\n   * serialize as \"keep\" (allow-listed and not baseline-dropped); a miss routes\n   * to the slow drop/unwrap path. Collapses the per-element baseline/allow-list/\n   * void/raw-text `Set.has` chain and the open/close-tag concatenations into one\n   * `Map.get` plus field reads on the serialize hot path. */\n  private readonly tagCache: Map<string, TagSer>;\n\n  constructor(policy: Policy = EMPTY_POLICY, defaultParse: ParseAdapter = THROW_NO_PARSER, parserOverride: ParseAdapter | null = null) {\n    // The constructor is where the expensive compilation happens once: resolving\n    // the policy into the fast structures `.sanitize()` reuses.\n    this.policy = policy;\n    this.defaultParse = defaultParse;\n    this.parserOverride = parserOverride;\n    const cache = new Map<string, TagSer>();\n    const star = policy.attrs.get('*');\n    for (const tag of policy.tags) {\n      // baseline-dropped tags (e.g. <script>) are NOT cached as keep → slow path.\n      if (!policy.allowUnsafe && BASELINE_DROP.has(tag)) continue;\n      const own = policy.attrs.get(tag);\n      let attrSet: ReadonlySet<string> | null;\n      if (own && star) { const m = new Set(own); for (const a of star) m.add(a); attrSet = m; }\n      else attrSet = own ?? star ?? null;\n      cache.set(tag, {\n        open: '<' + tag,\n        close: '</' + tag + '>',\n        isVoid: VOID_ELEMENTS.has(tag),\n        rawText: RAW_TEXT_ELEMENTS.has(tag),\n        attrSet,\n      });\n    }\n    this.tagCache = cache;\n  }\n\n  /**\n   * Parse untrusted HTML into the common `{type,name,attrs,children}` tree, via\n   * the active adapter: an explicit `.parser()` override if set, else the\n   * environment default. This is the ONLY pluggable seam, the policy engine and\n   * serializer downstream are identical for every parser.\n   */\n  protected parse(html: string): ParentNode {\n    return (this.parserOverride ?? this.defaultParse)(html);\n  }\n\n  /** Sanitize to a string. Always applies the inviolable safe baseline. */\n  sanitize(html: string): string {\n    const out: string[] = [];\n    this.emitChildren(this.parse(html), out, false, null);\n    return out.join('');\n  }\n\n  /** Sanitize and report what was removed and why (debug / audit / telemetry). */\n  sanitizeWithReport(html: string): SanitizeReport {\n    const out: string[] = [];\n    const removed: Removal[] = [];\n    this.emitChildren(this.parse(html), out, false, removed);\n    return { html: out.join(''), removed };\n  }\n\n  /**\n   * Stream the sanitized HTML to a sink instead of returning one string. `sink` is\n   * either a callback `(chunk) => void` or any object with a Node-style\n   * `write(chunk)` method (an HTTP response, an `fs` write stream, your own).\n   *\n   * Same parse, same inviolable baseline, same bytes as {@link sanitize}, just\n   * delivered incrementally, so no single large result string is built and large\n   * documents stay friendlier on memory. Fragments are batched into\n   * ~`chunkSize`-character writes (default 16 KB) so the sink isn't hit per tag.\n   *\n   * Synchronous: the sink is called inline while the tree is walked, and the whole\n   * input is parsed first (a faithful tree is required), so this streams *output*,\n   * not input. Backpressure is not awaited; for a slow consumer, buffer as needed.\n   */\n  sanitizeTo(html: string, sink: SanitizeSink, opts?: SanitizeToOptions): void {\n    const write = typeof sink === 'function' ? sink : (c: string) => { sink.write(c); };\n    const writer = new ChunkWriter(write, opts?.chunkSize ?? 16384);\n    this.emitChildren(this.parse(html), writer, false, null);\n    writer.flush();\n  }\n\n  /** Strip all markup to plain text (raw-text/script content excluded). */\n  sanitizeToText(html: string): string {\n    const out: string[] = [];\n    SanitizerCore.collectText(this.parse(html), out);\n    return out.join('');\n  }\n\n  /**\n   * Sanitize directly into a `DocumentFragment` (browser only), builds DOM nodes\n   * from the sanitized tree, skipping the non-idempotent serialize→reparse step\n   * (the strongest-safety path). Throws outside a DOM environment.\n   */\n  sanitizeToFragment(html: string): DocumentFragment {\n    if (typeof document === 'undefined') {\n      throw new Error('neosanitize: sanitizeToFragment requires a DOM (browser-only)');\n    }\n    const frag = document.createDocumentFragment();\n    this.buildDom(this.parse(html), frag);\n    return frag;\n  }\n\n  /**\n   * Sanitize and return a `TrustedHTML` via a Trusted Types policy when available\n   * (browser w/ CSP Trusted Types), else the sanitized string. The string is\n   * already safe; the wrapper just satisfies a TrustedHTML sink.\n   */\n  sanitizeToTrustedHTML(html: string): TrustedHTML | string {\n    const safe = this.sanitize(html);\n    const tt = (globalThis as unknown as { trustedTypes?: TrustedTypePolicyFactory }).trustedTypes;\n    if (tt) {\n      try {\n        SanitizerCore.ttPolicy ??= tt.createPolicy('neosanitize', { createHTML: (s: string) => s });\n        return SanitizerCore.ttPolicy.createHTML(safe);\n      } catch {\n        return safe;\n      }\n    }\n    return safe;\n  }\n  private static ttPolicy: TrustedTypePolicy | undefined;\n\n  // --- DOM + text builders (share elementAction/filterAttrs above) -----------\n  private buildDom(parent: ParentNode, domParent: Node): void {\n    for (const child of parent.children) {\n      if (child.type === 'text') {\n        domParent.appendChild(document.createTextNode(child.value));\n      } else if (child.type === 'element') {\n        const action = this.elementAction(child);\n        if (action === 'drop') continue;\n        if (action === 'unwrap') { this.buildDom(child, domParent); continue; }\n        const el = document.createElement(child.name);\n        for (const [name, v] of this.filterAttrs(child, null)) {\n          try { el.setAttribute(SanitizerCore.serializeAttrName(name), v); } catch { /* invalid attr name */ }\n        }\n        domParent.appendChild(el);\n        if (!VOID_ELEMENTS.has(child.name)) this.buildDom(child, el);\n      }\n    }\n  }\n  private static collectText(parent: ParentNode, out: string[]): void {\n    for (const child of parent.children) {\n      if (child.type === 'text') out.push(child.value);\n      else if (child.type === 'element' && !RAW_TEXT_ELEMENTS.has(child.name)) {\n        SanitizerCore.collectText(child, out);\n      }\n    }\n  }\n\n  // --- policy decisions (single source of truth, shared by every output path) --\n  /** What to do with an element: drop (with content), unwrap (drop the tag, keep\n   * sanitized children), or keep. */\n  private elementAction(el: ElementNode): 'drop' | 'unwrap' | 'keep' {\n    if (!this.policy.allowUnsafe && BASELINE_DROP.has(el.name)) return 'drop';\n    if (!this.policy.tags.has(el.name)) return DROP_CONTENT_WHEN_DISALLOWED.has(el.name) ? 'drop' : 'unwrap';\n    return 'keep';\n  }\n  /** Filtered, sanitized attributes for a kept element; records drops if `removed`.\n   * Lazily allocates: when nothing is dropped or rewritten (the common case) it\n   * returns `el.attrs` itself, so attribute-clean elements cost zero allocations. */\n  private filterAttrs(el: ElementNode, removed: Removal[] | null, allowed?: ReadonlySet<string> | null): Array<[string, string]> {\n    const baseline = !this.policy.allowUnsafe;\n    const src = el.attrs;\n    // `allowed` precomputed by the string serializer (set, or null = none);\n    // `undefined` means the caller (buildDom) didn't, so fall back to attrAllowed.\n    const useSet = allowed !== undefined;\n    let kept: Array<[string, string]> | null = null;\n    for (let i = 0; i < src.length; i++) {\n      const pair = src[i];\n      const name = pair[0], value = pair[1];\n      let drop = false;\n      let v = value;\n      if (useSet ? !(allowed !== null && allowed.has(name)) : !this.attrAllowed(el.name, name)) { removed?.push({ kind: 'attr', name, reason: 'not-allowed' }); drop = true; }\n      else if (baseline && SanitizerCore.attrUnsafe(name, value)) {\n        const ev = name[0] === 'o' && name[1] === 'n';\n        removed?.push({ kind: ev ? 'attr' : 'url', name, reason: ev ? 'event-handler' : 'dangerous-url' });\n        drop = true;\n      } else if (baseline && name === 'style') {\n        v = SanitizerCore.sanitizeStyle(value);\n        if (v === '') { removed?.push({ kind: 'style', name, reason: 'unsafe-css' }); drop = true; }\n        else if (v !== value) removed?.push({ kind: 'style', name, reason: 'unsafe-css-declaration' });\n      }\n      if (drop || v !== value) {\n        if (kept === null) kept = src.slice(0, i); // first divergence → copy the kept prefix\n        if (!drop) kept.push(v === value ? pair : [name, v]);\n      } else if (kept !== null) {\n        kept.push(pair);\n      }\n    }\n    return kept ?? src;\n  }\n\n  // --- string serializer ----------------------------------------------------\n  private emitChildren(parent: ParentNode, out: StringSink, rawText: boolean, removed: Removal[] | null): void {\n    // Index loop, not for-of: this runs once per parent element on the serialize\n    // hot path, and a for-of iterator object per call shows up under the profiler.\n    const children = parent.children;\n    for (let k = 0; k < children.length; k++) {\n      const child = children[k];\n      if (child.type === 'text') out.push(rawText ? child.value : SanitizerCore.escapeText(child.value));\n      else if (child.type === 'element') this.emitElement(child, out, removed);\n      // comments and doctype are dropped (output is a clean fragment)\n    }\n  }\n  private emitElement(el: ElementNode, out: StringSink, removed: Removal[] | null): void {\n    const info = this.tagCache.get(el.name);\n    if (info === undefined) {\n      // Slow path: not an allow-listed keep tag → drop (with content) or unwrap.\n      const action = this.elementAction(el);\n      if (action === 'drop') { removed?.push({ kind: 'tag', name: el.name, reason: 'unsafe-element' }); return; }\n      // html/head/body are implicit document structure, not user-content removals\n      if (removed && el.name !== 'html' && el.name !== 'head' && el.name !== 'body') {\n        removed.push({ kind: 'tag', name: el.name, reason: 'not-allowed' });\n      }\n      this.emitChildren(el, out, false, removed);\n      return;\n    }\n    out.push(info.open);\n    if (el.attrs.length !== 0) {\n      const attrs = this.filterAttrs(el, removed, info.attrSet);\n      for (let k = 0; k < attrs.length; k++) {\n        const a = attrs[k];\n        out.push(' ' + SanitizerCore.serializeAttrName(a[0]) + '=\"' + SanitizerCore.escapeAttr(a[1]) + '\"');\n      }\n    }\n    out.push('>');\n    if (info.isVoid) return;\n    this.emitChildren(el, out, info.rawText, removed);\n    out.push(info.close);\n  }\n\n  private attrAllowed(tag: string, attr: string): boolean {\n    const t = this.policy.attrs.get(tag);\n    if (t && t.has(attr)) return true;\n    const star = this.policy.attrs.get('*');\n    return star !== undefined && star.has(attr);\n  }\n\n  private static attrUnsafe(name: string, value: string): boolean {\n    if (name.length >= 2 && name[0] === 'o' && name[1] === 'n') return true; // on* event handlers\n    if (URL_ATTRS.has(name) && SanitizerCore.dangerousUrl(value)) return true;\n    return false;\n  }\n  private static dangerousUrl(value: string): boolean {\n    const colon = value.indexOf(':');\n    if (colon <= 0) return false; // no scheme (relative / fragment / leading ':') -> safe\n\n    // FAST PATH (the hot case): if every char before the ':' is a clean scheme\n    // char [A-Za-z0-9+.-], the scheme is UNAMBIGUOUS, no `new URL()` needed.\n    // This is safe because any obfuscation able to smuggle a \"javascript:\" past\n    // the browser's own URL parser MUST inject a non-scheme char (tab, newline,\n    // space, control) into the scheme, which fails this scan and drops to the\n    // authoritative parse below. Avoids a native URL allocation per URL attribute\n    // (profiling showed `new URL()` teardown dominating on URL-heavy input).\n    let clean = true;\n    for (let i = 0; i < colon; i++) {\n      const c = value.charCodeAt(i);\n      if (!((c >= 97 && c <= 122) || (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 43 || c === 45 || c === 46)) {\n        clean = false;\n        break;\n      }\n    }\n    if (clean) {\n      const scheme = value.slice(0, colon).toLowerCase();\n      if (scheme === 'javascript' || scheme === 'vbscript') return true;\n      if (scheme === 'data') return value.slice(0, 11).toLowerCase() !== 'data:image/';\n      return false; // http/https/mailto/tel/ftp/blob/... are fine\n    }\n\n    // SLOW PATH (obfuscated / weird scheme): the native URL parser matches how the\n    // browser resolves the attribute (it strips tab/newline + leading/trailing C0\n    // controls), so it sees the real scheme even through a tab inside \"javascript:\".\n    let scheme: string | null = null;\n    try {\n      scheme = new URL(value).protocol.slice(0, -1).toLowerCase();\n    } catch {\n      scheme = null;\n    }\n    if (scheme !== null) {\n      if (scheme === 'javascript' || scheme === 'vbscript') return true;\n      if (scheme === 'data') return !value.trim().toLowerCase().startsWith('data:image/');\n      return false; // http/https/mailto/tel/ftp/... are fine\n    }\n    // `new URL()` rejected the value outright → it is not a parseable absolute URL,\n    // so a browser won't execute it as a dangerous scheme either. Treat as safe.\n    // (Every real javascript:/vbscript:/data: obfuscation parses, so it was already\n    // resolved above; nothing dangerous reaches here.)\n    return false;\n  }\n  // --- CSS safe-subset for the `style` attribute --------------------------\n  private static sanitizeStyle(value: string): string {\n    const out: string[] = [];\n    for (const decl of SanitizerCore.splitDeclarations(value)) {\n      const colon = decl.indexOf(':');\n      if (colon === -1) continue;\n      const prop = decl.slice(0, colon).trim().toLowerCase();\n      const val = decl.slice(colon + 1).trim();\n      if (!prop || !val) continue;\n      if (prop === 'behavior' || prop === '-moz-binding' || prop === '-ms-behavior') continue;\n      if (SanitizerCore.unsafeCssValue(val)) continue;\n      out.push(prop + ': ' + val);\n    }\n    return out.join('; ');\n  }\n  /** Split a declaration list on top-level ';' (not inside parens or strings). */\n  private static splitDeclarations(s: string): string[] {\n    const decls: string[] = [];\n    let depth = 0, quote = '', start = 0;\n    for (let i = 0; i < s.length; i++) {\n      const c = s[i];\n      if (quote) { if (c === quote) quote = ''; continue; }\n      if (c === '\"' || c === \"'\") quote = c;\n      else if (c === '(') depth++;\n      else if (c === ')') { if (depth > 0) depth--; }\n      else if (c === ';' && depth === 0) { decls.push(s.slice(start, i)); start = i + 1; }\n    }\n    decls.push(s.slice(start));\n    return decls;\n  }\n  private static unsafeCssValue(val: string): boolean {\n    if (RE_CSS_CTRL.test(val)) return true; // control chars (obfuscation)\n    const v = val.replace(RE_WS_G, '').toLowerCase().replace(RE_QUOTES_G, '');\n    if (v.includes('expression(') || v.includes('javascript:') || v.includes('vbscript:')) return true;\n    if (v.includes('url(data:') && !v.includes('url(data:image/')) return true;\n    return false;\n  }\n  private static serializeAttrName(name: string): string {\n    // foreign namespaced attrs are stored as \"xlink href\" (space) → \"xlink:href\"\n    return name.indexOf(' ') === -1 ? name : name.replace(' ', ':');\n  }\n  private static escapeText(s: string): string {\n    if (!RE_TEXT_NEEDS.test(s)) return s;\n    return s.replace(RE_AMP_G, '&amp;').replace(RE_LT_G, '&lt;').replace(RE_GT_G, '&gt;').replace(RE_NBSP_G, '&nbsp;');\n  }\n  private static escapeAttr(s: string): string {\n    if (!RE_ATTR_NEEDS.test(s)) return s;\n    return s.replace(RE_AMP_G, '&amp;').replace(RE_QUOT_G, '&quot;').replace(RE_NBSP_G, '&nbsp;');\n  }\n\n  /** Escape hatch: skip the inviolable baseline (mirrors `setHTMLUnsafe`). */\n  sanitizeUnsafe(html: string): string {\n    // Re-parse with the SAME concrete entry (this.constructor) AND the same parser\n    // override, baseline off, so the active adapter is preserved.\n    const Ctor = this.constructor as new (policy?: Policy, parser?: ParseAdapter | null) => SanitizerCore;\n    return new Ctor({ ...this.policy, allowUnsafe: true }, this.parserOverride).sanitize(html);\n  }\n\n  /**\n   * Class-based factory, the entry point: `Sanitizer.builder().preset(…).build()`.\n   * Polymorphic over the concrete subclass: `Sanitizer.builder()` yields a builder\n   * whose `.build()` returns that same `Sanitizer` (so the correct parser is wired).\n   */\n  static builder<T extends SanitizerCore>(this: new (policy?: Policy, parser?: ParseAdapter | null) => T, base?: PolicyInput): SanitizerBuilder<T> {\n    const b = new SanitizerBuilder<T>(this);\n    return base ? b.preset(base) : b;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Builder, accumulate config cheaply; compile ONCE at build(). Generic over the\n// concrete Sanitizer so `build()` returns the right (parser-wired) class.\n// ---------------------------------------------------------------------------\nexport class SanitizerBuilder<T extends SanitizerCore = SanitizerCore> {\n  private _tags = new Set<string>();\n  private _attrs = new Map<string, Set<string>>();\n  private _allowUnsafe = false;\n  private _parser: ParseAdapter | null = null;\n\n  /** @param ctor the concrete `Sanitizer` subclass to instantiate at `build()`. */\n  constructor(private readonly ctor: new (policy?: Policy, parser?: ParseAdapter | null) => T) {}\n\n  /**\n   * Override the parser. Pass an adapter (e.g. `parse5Adapter` from\n   * `neosanitize/parse5`, or your own `(html) => ParentNode`) to parse with it\n   * instead of the environment default. Pass `null` to restore the default.\n   */\n  parser(adapter: ParseAdapter | null): this {\n    this._parser = adapter;\n    return this;\n  }\n\n  /** Start from a preset (or another policy), then refine. */\n  preset(p: PolicyInput): this {\n    const pol = SanitizerBuilder.resolve(p);\n    for (const t of pol.tags) this._tags.add(t);\n    for (const [tag, set] of pol.attrs) {\n      const into = this._attrs.get(tag) ?? new Set<string>();\n      for (const a of set) into.add(a);\n      this._attrs.set(tag, into);\n    }\n    return this;\n  }\n\n  /** Allow a tag (optionally with attributes). */\n  allow(tag: string, attrs?: Iterable<string>): this {\n    this._tags.add(tag);\n    if (attrs) {\n      const into = this._attrs.get(tag) ?? new Set<string>();\n      for (const a of attrs) into.add(a);\n      this._attrs.set(tag, into);\n    }\n    return this;\n  }\n\n  /** Remove a tag from the allow-list. */\n  deny(tag: string): this {\n    this._tags.delete(tag);\n    this._attrs.delete(tag);\n    return this;\n  }\n\n  /** Compile the accumulated config into a reusable `Sanitizer` (once). */\n  build(): T {\n    return new this.ctor({\n      tags: new Set(this._tags),\n      attrs: new Map([...this._attrs].map(([t, s]) => [t, new Set(s)])),\n      allowUnsafe: this._allowUnsafe\n    }, this._parser);\n  }\n\n  /** Resolve a preset or partial-policy input into an immutable `Policy`. */\n  private static resolve(p: PolicyInput): Policy {\n    if ((p as Preset)[UNSAFE_PRESET_SYMBOL] === true) return (p as Preset).policy;\n    const m = p as Partial<MutablePolicy>;\n    return {\n      tags: new Set(m.tags ?? []),\n      attrs: new Map(Object.entries(m.attrs ?? {}).map(([t, s]) => [t, new Set(s)])),\n      allowUnsafe: m.allowUnsafe ?? false\n    };\n  }\n}\n"],"mappings":"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"}