tldraw
Version:
A tiny little drawing editor.
8 lines (7 loc) • 26 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../src/lib/utils/svg/sanitizeSvg.ts"],
"sourcesContent": ["/*!\n * SVG/attribute allowlists and URI sanitization approach derived from DOMPurify.\n * DOMPurify \u2014 MIT License, Copyright (c) 2015 Mario Heiderich\n * https://github.com/cure53/DOMPurify/blob/main/LICENSE\n */\n\n// --- Allowed SVG elements (lowercase) ---\n// Includes foreignObject, style, and animation elements.\nconst ALLOWED_SVG_TAGS = new Set([\n\t'svg',\n\t'a',\n\t'altglyph',\n\t'altglyphdef',\n\t'altglyphitem',\n\t'animate',\n\t'animatecolor',\n\t'animatemotion',\n\t'animatetransform',\n\t'circle',\n\t'clippath',\n\t'defs',\n\t'desc',\n\t'ellipse',\n\t'feblend',\n\t'fecolormatrix',\n\t'fecomponenttransfer',\n\t'fecomposite',\n\t'feconvolvematrix',\n\t'fediffuselighting',\n\t'fedisplacementmap',\n\t'fedistantlight',\n\t'fedropshadow',\n\t'feflood',\n\t'fefunca',\n\t'fefuncb',\n\t'fefuncg',\n\t'fefuncr',\n\t'fegaussianblur',\n\t'feimage',\n\t'femerge',\n\t'femergenode',\n\t'femorphology',\n\t'feoffset',\n\t'fepointlight',\n\t'fespecularlighting',\n\t'fespotlight',\n\t'fetile',\n\t'feturbulence',\n\t'filter',\n\t'font',\n\t'foreignobject',\n\t'g',\n\t'glyph',\n\t'glyphref',\n\t'hkern',\n\t'image',\n\t'line',\n\t'lineargradient',\n\t'marker',\n\t'mask',\n\t'metadata',\n\t'mpath',\n\t'path',\n\t'pattern',\n\t'polygon',\n\t'polyline',\n\t'radialgradient',\n\t'rect',\n\t'set',\n\t'stop',\n\t'style',\n\t'switch',\n\t'symbol',\n\t'text',\n\t'textpath',\n\t'title',\n\t'tref',\n\t'tspan',\n\t'use',\n\t'view',\n\t'vkern',\n])\n\n// --- Allowed HTML elements inside foreignObject ---\nconst ALLOWED_HTML_TAGS = new Set([\n\t'a',\n\t'b',\n\t'blockquote',\n\t'body',\n\t'br',\n\t'code',\n\t'del',\n\t'div',\n\t'em',\n\t'h1',\n\t'h2',\n\t'h3',\n\t'h4',\n\t'h5',\n\t'h6',\n\t'i',\n\t'li',\n\t'mark',\n\t'ol',\n\t'p',\n\t'pre',\n\t'span',\n\t'strong',\n\t's',\n\t'sub',\n\t'sup',\n\t'table',\n\t'tbody',\n\t'td',\n\t'th',\n\t'thead',\n\t'tr',\n\t'u',\n\t'ul',\n])\n\n// --- Blocked HTML elements inside foreignObject ---\n// These are explicitly dangerous even if not in the allow list (defense in depth).\nconst BLOCKED_HTML_TAGS = new Set([\n\t'script',\n\t'iframe',\n\t'object',\n\t'embed',\n\t'form',\n\t'input',\n\t'textarea',\n\t'select',\n\t'button',\n\t'link',\n\t'meta',\n\t'base',\n\t'img', // onerror vector\n\t'video',\n\t'audio',\n\t'source',\n\t'picture',\n\t'svg', // no nested SVG inside foreignObject\n])\n\n// --- Allowed SVG attributes (lowercase) ---\nconst ALLOWED_SVG_ATTRS = new Set([\n\t'accent-height',\n\t'accumulate',\n\t'additive',\n\t'alignment-baseline',\n\t'amplitude',\n\t'ascent',\n\t'attributename',\n\t'attributetype',\n\t'azimuth',\n\t'basefrequency',\n\t'baseline-shift',\n\t'begin',\n\t'bias',\n\t'by',\n\t'class',\n\t'clip',\n\t'clip-path',\n\t'clip-rule',\n\t'clippathunits',\n\t'color',\n\t'color-interpolation',\n\t'color-interpolation-filters',\n\t'color-profile',\n\t'color-rendering',\n\t'cx',\n\t'cy',\n\t'd',\n\t'diffuseconstant',\n\t'direction',\n\t'display',\n\t'divisor',\n\t'dominant-baseline',\n\t'dur',\n\t'dx',\n\t'dy',\n\t'edgemode',\n\t'elevation',\n\t'end',\n\t'exponent',\n\t'fill',\n\t'fill-opacity',\n\t'fill-rule',\n\t'filter',\n\t'filterunits',\n\t'flood-color',\n\t'flood-opacity',\n\t'font-family',\n\t'font-size',\n\t'font-size-adjust',\n\t'font-stretch',\n\t'font-style',\n\t'font-variant',\n\t'font-weight',\n\t'from',\n\t'fx',\n\t'fy',\n\t'g1',\n\t'g2',\n\t'glyph-name',\n\t'glyphref',\n\t'gradienttransform',\n\t'gradientunits',\n\t'height',\n\t'href',\n\t'id',\n\t'image-rendering',\n\t'in',\n\t'in2',\n\t'intercept',\n\t'k',\n\t'k1',\n\t'k2',\n\t'k3',\n\t'k4',\n\t'kerning',\n\t'kernelmatrix',\n\t'kernelunitlength',\n\t'keypoints',\n\t'keysplines',\n\t'keytimes',\n\t'lang',\n\t'lengthadjust',\n\t'letter-spacing',\n\t'lighting-color',\n\t'local',\n\t'marker-end',\n\t'marker-mid',\n\t'marker-start',\n\t'markerheight',\n\t'markerunits',\n\t'markerwidth',\n\t'mask',\n\t'mask-type',\n\t'maskcontentunits',\n\t'maskunits',\n\t'max',\n\t'media',\n\t'method',\n\t'min',\n\t'mode',\n\t'name',\n\t'numoctaves',\n\t'offset',\n\t'opacity',\n\t'operator',\n\t'order',\n\t'orient',\n\t'orientation',\n\t'origin',\n\t'overflow',\n\t'paint-order',\n\t'path',\n\t'pathlength',\n\t'patterncontentunits',\n\t'patterntransform',\n\t'patternunits',\n\t'pointer-events',\n\t'points',\n\t'preservealpha',\n\t'preserveaspectratio',\n\t'primitiveunits',\n\t'r',\n\t'radius',\n\t'refx',\n\t'refy',\n\t'repeatcount',\n\t'repeatdur',\n\t'requiredfeatures',\n\t'restart',\n\t'result',\n\t'role',\n\t'rotate',\n\t'rx',\n\t'ry',\n\t'scale',\n\t'seed',\n\t'shape-rendering',\n\t'slope',\n\t'specularconstant',\n\t'specularexponent',\n\t'spreadmethod',\n\t'startoffset',\n\t'stddeviation',\n\t'stitchtiles',\n\t'stop-color',\n\t'stop-opacity',\n\t'stroke',\n\t'stroke-dasharray',\n\t'stroke-dashoffset',\n\t'stroke-linecap',\n\t'stroke-linejoin',\n\t'stroke-miterlimit',\n\t'stroke-opacity',\n\t'stroke-width',\n\t'style',\n\t'surfacescale',\n\t'systemlanguage',\n\t'tabindex',\n\t'tablevalues',\n\t'targetx',\n\t'targety',\n\t'text-anchor',\n\t'text-decoration',\n\t'text-rendering',\n\t'textlength',\n\t'to',\n\t'transform',\n\t'transform-origin',\n\t'type',\n\t'u1',\n\t'u2',\n\t'unicode',\n\t'values',\n\t'version',\n\t'vert-adv-y',\n\t'vert-origin-x',\n\t'vert-origin-y',\n\t'viewbox',\n\t'visibility',\n\t'width',\n\t'word-spacing',\n\t'wrap',\n\t'writing-mode',\n\t'x',\n\t'x1',\n\t'x2',\n\t'xchannelselector',\n\t'xlink:href',\n\t'xml:id',\n\t'xml:space',\n\t'xlink:title',\n\t'xmlns',\n\t'xmlns:xlink',\n\t'y',\n\t'y1',\n\t'y2',\n\t'z',\n\t'zoomandpan',\n])\n\n// --- Allowed HTML attributes inside foreignObject ---\nconst ALLOWED_HTML_ATTRS = new Set([\n\t'class',\n\t'dir',\n\t'href', // only on <a>\n\t'id',\n\t'lang',\n\t'role',\n\t'style',\n\t'tabindex',\n\t'title',\n])\n\n// Patterns for data-* and aria-* attributes\nconst DATA_ATTR_PATTERN = /^data-/\nconst ARIA_ATTR_PATTERN = /^aria-/\n\n// --- URI sanitization ---\n// Strip invisible whitespace that can bypass protocol checks\n// eslint-disable-next-line no-control-regex\nconst INVISIBLE_WHITESPACE = /[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g\n\n// Safe link protocols\nconst SAFE_LINK_PROTOCOLS = /^(?:https?:|mailto:)/i\n\n// data: URI (for images, fonts)\nconst DATA_URI = /^data:/i\n\n// data: URI for raster image formats (svg+xml handled separately below)\nconst RASTER_DATA_URI =\n\t/^data:image\\/(?:png|jpeg|jpg|gif|webp|avif|bmp|tiff|x-icon|vnd\\.microsoft\\.icon)[;,]/i\n\n// data: URI for SVG images \u2014 allowed on <image>/<feimage> only after recursive sanitization\nconst SVG_DATA_URI = /^data:image\\/svg\\+xml[;,]/i\n\n// Fragment-only ref (#id)\nconst FRAGMENT_REF = /^#/\n\n// --- CSS sanitization ---\n\nfunction decodeCssEscapes(css: string): string {\n\treturn css.replace(/\\\\([0-9a-fA-F]{1,6})\\s?|\\\\([^\\n])/g, (_, hex, literal) => {\n\t\tif (hex) {\n\t\t\tconst codePoint = parseInt(hex, 16)\n\t\t\tif (codePoint > 0x10ffff || codePoint === 0) return '\\uFFFD'\n\t\t\treturn String.fromCodePoint(codePoint)\n\t\t}\n\t\treturn literal\n\t})\n}\n\n// Allowed data: MIME types in CSS url()\nconst SAFE_CSS_DATA_MIME =\n\t/^data:(?:image\\/(?:png|jpeg|jpg|gif|webp|avif)|font\\/(?:woff2?|opentype|truetype|sfnt)|application\\/(?:x-font-woff|font-woff2?|x-font-ttf|x-font-opentype|font-sfnt))[;,]/i\n\nfunction sanitizeCssValue(css: string): string {\n\tlet decoded = decodeCssEscapes(css)\n\t// Strip @import \u2014 handle url() with semicolons inside quotes\n\tdecoded = decoded.replace(\n\t\t/@import\\s+(?:url\\s*\\([^)]*\\)|\"[^\"]*\"|'[^']*')[^;]*;?|@import\\b[^;]*;?/gi,\n\t\t''\n\t)\n\t// Strip expression(), -moz-binding, behavior:\n\tdecoded = decoded.replace(/expression\\s*\\([^)]*\\)/gi, '')\n\tdecoded = decoded.replace(/-moz-binding\\s*:[^;]*/gi, '')\n\tdecoded = decoded.replace(/behavior\\s*:[^;]*/gi, '')\n\t// Sanitize url() \u2014 allow only data: with safe MIME types\n\tdecoded = decoded.replace(/url\\s*\\(\\s*(['\"]?)(.*?)\\1\\s*\\)/gis, (match, _quote, uri) => {\n\t\tconst stripped = uri.replace(INVISIBLE_WHITESPACE, '')\n\t\tif (SAFE_CSS_DATA_MIME.test(stripped) || FRAGMENT_REF.test(stripped)) {\n\t\t\treturn match\n\t\t}\n\t\treturn ''\n\t})\n\treturn decoded\n}\n\nfunction sanitizeStyleElement(textContent: string): string {\n\treturn sanitizeCssValue(textContent)\n}\n\n// --- Animation safety ---\n// Animation elements (<animate>, <set>) can overwrite attributes at runtime.\n// If attributeName targets a URI attr (href) or event handler (on*), the animation\n// can inject javascript: URIs or re-add stripped handlers, bypassing static sanitization.\nconst ANIMATION_TAGS = new Set(['animate', 'set', 'animatecolor', 'animatetransform'])\nconst DANGEROUS_ANIMATION_TARGETS = /^(?:href|xlink:href|on)/i\n\nfunction isAnimationDangerous(el: Element): boolean {\n\tconst attrName = el.getAttribute('attributeName')\n\tif (!attrName) return false\n\treturn DANGEROUS_ANIMATION_TARGETS.test(attrName.replace(INVISIBLE_WHITESPACE, ''))\n}\n\n// --- Event handler detection ---\n// Matches on* after normalizing invisible chars \u2014 catches all current and future event handlers\nconst EVENT_HANDLER_PATTERN = /^on/i\n\n// --- SVG presentation attributes that accept url() references ---\n// These can exfiltrate data via external URL loads if not sanitized.\nconst URL_BEARING_SVG_ATTRS = new Set([\n\t'clip-path',\n\t'cursor',\n\t'fill',\n\t'filter',\n\t'marker-end',\n\t'marker-mid',\n\t'marker-start',\n\t'mask',\n\t'stroke',\n])\n\n// --- Node sanitization ---\n\ntype SanitizeMode = 'svg' | 'html'\n\n// Guard against deep recursion: sanitizeSvgInner \u2192 ... \u2192 sanitizeUri \u2192 sanitizeEmbeddedSvgDataUri \u2192 sanitizeSvgInner\nconst MAX_EMBED_DEPTH = 10\n\nfunction decodeDataUri(value: string): string | null {\n\tconst base64Idx = value.search(/;base64,/i)\n\tif (base64Idx >= 0) {\n\t\tconst base64 = value.slice(base64Idx + 8) // 8 = \";base64,\".length\n\t\tconst bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0))\n\t\treturn new TextDecoder().decode(bytes)\n\t}\n\tconst commaIdx = value.indexOf(',')\n\tif (commaIdx < 0) return null\n\treturn decodeURIComponent(value.slice(commaIdx + 1))\n}\n\nfunction encodeAsSvgDataUri(svgText: string): string {\n\tconst bytes = new TextEncoder().encode(svgText)\n\tconst binaryStr = Array.from(bytes, (b) => String.fromCharCode(b)).join('')\n\treturn 'data:image/svg+xml;base64,' + btoa(binaryStr)\n}\n\nfunction sanitizeEmbeddedSvgDataUri(value: string, depth: number): string | null {\n\tif (depth >= MAX_EMBED_DEPTH) {\n\t\tconsole.warn(`Embedded SVG data URI recursion depth limit (${MAX_EMBED_DEPTH}) reached`)\n\t\treturn null\n\t}\n\tlet svgText: string\n\ttry {\n\t\tconst decoded = decodeDataUri(value)\n\t\tif (decoded === null) return null\n\t\tsvgText = decoded\n\t} catch {\n\t\t// Invalid base64 or malformed percent-encoding \u2014 treat as unsafe\n\t\treturn null\n\t}\n\tconst sanitized = sanitizeSvgInner(svgText, depth + 1)\n\tif (!sanitized) return null\n\treturn encodeAsSvgDataUri(sanitized)\n}\n\nfunction sanitizeUri(el: Element, attrName: string, value: string, depth: number): string | null {\n\tconst stripped = value.replace(INVISIBLE_WHITESPACE, '')\n\tconst tagName = el.tagName.toLowerCase()\n\n\t// <image> and <feImage>: raster data: or recursively-sanitized svg+xml data:\n\tif (tagName === 'image' || tagName === 'feimage') {\n\t\tif (RASTER_DATA_URI.test(stripped)) return value\n\t\tif (SVG_DATA_URI.test(stripped)) return sanitizeEmbeddedSvgDataUri(stripped, depth)\n\t\treturn null\n\t}\n\n\t// <use>: fragment-only (#id)\n\tif (tagName === 'use') {\n\t\tif (FRAGMENT_REF.test(stripped)) return value\n\t\treturn null\n\t}\n\n\t// <a>: http, https, mailto only\n\tif (tagName === 'a') {\n\t\tif (SAFE_LINK_PROTOCOLS.test(stripped)) return value\n\t\treturn null\n\t}\n\n\t// All other elements with href/xlink:href: data: or fragment\n\tif (DATA_URI.test(stripped) || FRAGMENT_REF.test(stripped)) return value\n\treturn null\n}\n\nfunction sanitizeSvgAttributes(el: Element, depth: number): void {\n\tfor (let i = el.attributes.length - 1; i >= 0; i--) {\n\t\tconst attr = el.attributes[i]\n\t\tconst name = attr.name.toLowerCase()\n\n\t\t// Strip ALL event handlers (on*)\n\t\tconst normalized = name.replace(INVISIBLE_WHITESPACE, '')\n\t\tif (EVENT_HANDLER_PATTERN.test(normalized)) {\n\t\t\tel.removeAttribute(attr.name)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Allow data-* and aria-*\n\t\tif (DATA_ATTR_PATTERN.test(name) || ARIA_ATTR_PATTERN.test(name)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif (!ALLOWED_SVG_ATTRS.has(name)) {\n\t\t\tel.removeAttribute(attr.name)\n\t\t\tcontinue\n\t\t}\n\n\t\t// URI attributes need context-dependent sanitization\n\t\tif (name === 'href' || name === 'xlink:href') {\n\t\t\tconst sanitized = sanitizeUri(el, name, attr.value, depth)\n\t\t\tif (sanitized === null) {\n\t\t\t\tel.removeAttribute(attr.name)\n\t\t\t} else if (sanitized !== attr.value) {\n\t\t\t\tattr.value = sanitized\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// style attribute: sanitize CSS\n\t\tif (name === 'style') {\n\t\t\tattr.value = sanitizeCssValue(attr.value)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Presentation attributes that accept url() references \u2014 sanitize to allow\n\t\t// only data: (safe MIME) and fragment (#id) refs, strip external URLs\n\t\tif (URL_BEARING_SVG_ATTRS.has(name) && /url\\s*\\(/i.test(attr.value)) {\n\t\t\tattr.value = sanitizeCssValue(attr.value)\n\t\t}\n\t}\n}\n\nfunction sanitizeHtmlAttributes(el: Element): void {\n\tconst tagName = el.tagName.toLowerCase()\n\n\tfor (let i = el.attributes.length - 1; i >= 0; i--) {\n\t\tconst attr = el.attributes[i]\n\t\tconst name = attr.name.toLowerCase()\n\n\t\t// Strip ALL event handlers\n\t\tconst normalized = name.replace(INVISIBLE_WHITESPACE, '')\n\t\tif (EVENT_HANDLER_PATTERN.test(normalized)) {\n\t\t\tel.removeAttribute(attr.name)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Allow data-* and aria-*\n\t\tif (DATA_ATTR_PATTERN.test(name) || ARIA_ATTR_PATTERN.test(name)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif (!ALLOWED_HTML_ATTRS.has(name)) {\n\t\t\tel.removeAttribute(attr.name)\n\t\t\tcontinue\n\t\t}\n\n\t\t// href only allowed on <a>, with safe protocols only\n\t\tif (name === 'href') {\n\t\t\tif (tagName !== 'a') {\n\t\t\t\tel.removeAttribute(attr.name)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst stripped = attr.value.replace(INVISIBLE_WHITESPACE, '')\n\t\t\tif (!SAFE_LINK_PROTOCOLS.test(stripped)) {\n\t\t\t\tel.removeAttribute(attr.name)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// style attribute: sanitize CSS\n\t\tif (name === 'style') {\n\t\t\tattr.value = sanitizeCssValue(attr.value)\n\t\t}\n\t}\n}\n\nfunction sanitizeNode(node: Element, mode: SanitizeMode, depth: number): void {\n\t// Walk children in reverse so removals don't shift indices\n\tfor (let i = node.children.length - 1; i >= 0; i--) {\n\t\tconst child = node.children[i]\n\t\tconst tag = child.tagName.toLowerCase()\n\n\t\tif (mode === 'svg') {\n\t\t\tif (tag === 'foreignobject') {\n\t\t\t\t// foreignObject: sanitize attrs as SVG, recurse children as HTML\n\t\t\t\tsanitizeSvgAttributes(child, depth)\n\t\t\t\tsanitizeNode(child, 'html', depth)\n\t\t\t} else if (tag === 'style') {\n\t\t\t\t// <style>: sanitize attrs, sanitize text content as CSS\n\t\t\t\tsanitizeSvgAttributes(child, depth)\n\t\t\t\tif (child.textContent) {\n\t\t\t\t\tchild.textContent = sanitizeStyleElement(child.textContent)\n\t\t\t\t}\n\t\t\t} else if (ANIMATION_TAGS.has(tag) && isAnimationDangerous(child)) {\n\t\t\t\t// Animation targeting href/on* can inject javascript: URIs at runtime\n\t\t\t\tchild.remove()\n\t\t\t} else if (ALLOWED_SVG_TAGS.has(tag)) {\n\t\t\t\tsanitizeSvgAttributes(child, depth)\n\t\t\t\tsanitizeNode(child, 'svg', depth)\n\t\t\t} else {\n\t\t\t\tchild.remove()\n\t\t\t}\n\t\t} else {\n\t\t\t// HTML mode (inside foreignObject)\n\t\t\tif (BLOCKED_HTML_TAGS.has(tag)) {\n\t\t\t\tchild.remove()\n\t\t\t} else if (ALLOWED_HTML_TAGS.has(tag)) {\n\t\t\t\tsanitizeHtmlAttributes(child)\n\t\t\t\tsanitizeNode(child, 'html', depth)\n\t\t\t} else {\n\t\t\t\tchild.remove()\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction sanitizeSvgInner(svgText: string, depth: number): string {\n\tconst doc = new DOMParser().parseFromString(svgText, 'image/svg+xml')\n\n\tconst parseError = doc.querySelector('parsererror')\n\tif (parseError) return ''\n\n\tconst svg = doc.documentElement\n\tif (svg.tagName.toLowerCase() !== 'svg') return ''\n\n\tsanitizeSvgAttributes(svg, depth)\n\tsanitizeNode(svg, 'svg', depth)\n\n\tif (svg.children.length === 0) return ''\n\n\treturn new XMLSerializer().serializeToString(svg)\n}\n\n/**\n * Sanitizes an SVG string by removing dangerous elements, attributes, and URIs\n * while preserving safe content including foreignObject (for text rendering),\n * style elements (for fonts with data: URLs), and animation elements.\n * Embedded SVG data URIs on `<image>`/`<feImage>` are recursively sanitized.\n *\n * Returns the sanitized SVG string, or an empty string if the input was\n * malformed (parse error) or contained no safe content after sanitization.\n *\n * @public\n */\nexport function sanitizeSvg(svgText: string): string {\n\treturn sanitizeSvgInner(svgText, 0)\n}\n"],
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,MAAM,mBAAmB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAGD,MAAM,oBAAoB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAID,MAAM,oBAAoB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AACD,CAAC;AAGD,MAAM,oBAAoB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAGD,MAAM,qBAAqB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAGD,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAK1B,MAAM,uBAAuB;AAG7B,MAAM,sBAAsB;AAG5B,MAAM,WAAW;AAGjB,MAAM,kBACL;AAGD,MAAM,eAAe;AAGrB,MAAM,eAAe;AAIrB,SAAS,iBAAiB,KAAqB;AAC9C,SAAO,IAAI,QAAQ,sCAAsC,CAAC,GAAG,KAAK,YAAY;AAC7E,QAAI,KAAK;AACR,YAAM,YAAY,SAAS,KAAK,EAAE;AAClC,UAAI,YAAY,WAAY,cAAc,EAAG,QAAO;AACpD,aAAO,OAAO,cAAc,SAAS;AAAA,IACtC;AACA,WAAO;AAAA,EACR,CAAC;AACF;AAGA,MAAM,qBACL;AAED,SAAS,iBAAiB,KAAqB;AAC9C,MAAI,UAAU,iBAAiB,GAAG;AAElC,YAAU,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,EACD;AAEA,YAAU,QAAQ,QAAQ,4BAA4B,EAAE;AACxD,YAAU,QAAQ,QAAQ,2BAA2B,EAAE;AACvD,YAAU,QAAQ,QAAQ,uBAAuB,EAAE;AAEnD,YAAU,QAAQ,QAAQ,qCAAqC,CAAC,OAAO,QAAQ,QAAQ;AACtF,UAAM,WAAW,IAAI,QAAQ,sBAAsB,EAAE;AACrD,QAAI,mBAAmB,KAAK,QAAQ,KAAK,aAAa,KAAK,QAAQ,GAAG;AACrE,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR,CAAC;AACD,SAAO;AACR;AAEA,SAAS,qBAAqB,aAA6B;AAC1D,SAAO,iBAAiB,WAAW;AACpC;AAMA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,WAAW,OAAO,gBAAgB,kBAAkB,CAAC;AACrF,MAAM,8BAA8B;AAEpC,SAAS,qBAAqB,IAAsB;AACnD,QAAM,WAAW,GAAG,aAAa,eAAe;AAChD,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,4BAA4B,KAAK,SAAS,QAAQ,sBAAsB,EAAE,CAAC;AACnF;AAIA,MAAM,wBAAwB;AAI9B,MAAM,wBAAwB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAOD,MAAM,kBAAkB;AAExB,SAAS,cAAc,OAA8B;AACpD,QAAM,YAAY,MAAM,OAAO,WAAW;AAC1C,MAAI,aAAa,GAAG;AACnB,UAAM,SAAS,MAAM,MAAM,YAAY,CAAC;AACxC,UAAM,QAAQ,WAAW,KAAK,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAClE,WAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,EACtC;AACA,QAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO,mBAAmB,MAAM,MAAM,WAAW,CAAC,CAAC;AACpD;AAEA,SAAS,mBAAmB,SAAyB;AACpD,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,OAAO;AAC9C,QAAM,YAAY,MAAM,KAAK,OAAO,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE;AAC1E,SAAO,+BAA+B,KAAK,SAAS;AACrD;AAEA,SAAS,2BAA2B,OAAe,OAA8B;AAChF,MAAI,SAAS,iBAAiB;AAC7B,YAAQ,KAAK,gDAAgD,eAAe,WAAW;AACvF,WAAO;AAAA,EACR;AACA,MAAI;AACJ,MAAI;AACH,UAAM,UAAU,cAAc,KAAK;AACnC,QAAI,YAAY,KAAM,QAAO;AAC7B,cAAU;AAAA,EACX,QAAQ;AAEP,WAAO;AAAA,EACR;AACA,QAAM,YAAY,iBAAiB,SAAS,QAAQ,CAAC;AACrD,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,mBAAmB,SAAS;AACpC;AAEA,SAAS,YAAY,IAAa,UAAkB,OAAe,OAA8B;AAChG,QAAM,WAAW,MAAM,QAAQ,sBAAsB,EAAE;AACvD,QAAM,UAAU,GAAG,QAAQ,YAAY;AAGvC,MAAI,YAAY,WAAW,YAAY,WAAW;AACjD,QAAI,gBAAgB,KAAK,QAAQ,EAAG,QAAO;AAC3C,QAAI,aAAa,KAAK,QAAQ,EAAG,QAAO,2BAA2B,UAAU,KAAK;AAClF,WAAO;AAAA,EACR;AAGA,MAAI,YAAY,OAAO;AACtB,QAAI,aAAa,KAAK,QAAQ,EAAG,QAAO;AACxC,WAAO;AAAA,EACR;AAGA,MAAI,YAAY,KAAK;AACpB,QAAI,oBAAoB,KAAK,QAAQ,EAAG,QAAO;AAC/C,WAAO;AAAA,EACR;AAGA,MAAI,SAAS,KAAK,QAAQ,KAAK,aAAa,KAAK,QAAQ,EAAG,QAAO;AACnE,SAAO;AACR;AAEA,SAAS,sBAAsB,IAAa,OAAqB;AAChE,WAAS,IAAI,GAAG,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,UAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,UAAM,OAAO,KAAK,KAAK,YAAY;AAGnC,UAAM,aAAa,KAAK,QAAQ,sBAAsB,EAAE;AACxD,QAAI,sBAAsB,KAAK,UAAU,GAAG;AAC3C,SAAG,gBAAgB,KAAK,IAAI;AAC5B;AAAA,IACD;AAGA,QAAI,kBAAkB,KAAK,IAAI,KAAK,kBAAkB,KAAK,IAAI,GAAG;AACjE;AAAA,IACD;AAEA,QAAI,CAAC,kBAAkB,IAAI,IAAI,GAAG;AACjC,SAAG,gBAAgB,KAAK,IAAI;AAC5B;AAAA,IACD;AAGA,QAAI,SAAS,UAAU,SAAS,cAAc;AAC7C,YAAM,YAAY,YAAY,IAAI,MAAM,KAAK,OAAO,KAAK;AACzD,UAAI,cAAc,MAAM;AACvB,WAAG,gBAAgB,KAAK,IAAI;AAAA,MAC7B,WAAW,cAAc,KAAK,OAAO;AACpC,aAAK,QAAQ;AAAA,MACd;AACA;AAAA,IACD;AAGA,QAAI,SAAS,SAAS;AACrB,WAAK,QAAQ,iBAAiB,KAAK,KAAK;AACxC;AAAA,IACD;AAIA,QAAI,sBAAsB,IAAI,IAAI,KAAK,YAAY,KAAK,KAAK,KAAK,GAAG;AACpE,WAAK,QAAQ,iBAAiB,KAAK,KAAK;AAAA,IACzC;AAAA,EACD;AACD;AAEA,SAAS,uBAAuB,IAAmB;AAClD,QAAM,UAAU,GAAG,QAAQ,YAAY;AAEvC,WAAS,IAAI,GAAG,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,UAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,UAAM,OAAO,KAAK,KAAK,YAAY;AAGnC,UAAM,aAAa,KAAK,QAAQ,sBAAsB,EAAE;AACxD,QAAI,sBAAsB,KAAK,UAAU,GAAG;AAC3C,SAAG,gBAAgB,KAAK,IAAI;AAC5B;AAAA,IACD;AAGA,QAAI,kBAAkB,KAAK,IAAI,KAAK,kBAAkB,KAAK,IAAI,GAAG;AACjE;AAAA,IACD;AAEA,QAAI,CAAC,mBAAmB,IAAI,IAAI,GAAG;AAClC,SAAG,gBAAgB,KAAK,IAAI;AAC5B;AAAA,IACD;AAGA,QAAI,SAAS,QAAQ;AACpB,UAAI,YAAY,KAAK;AACpB,WAAG,gBAAgB,KAAK,IAAI;AAC5B;AAAA,MACD;AACA,YAAM,WAAW,KAAK,MAAM,QAAQ,sBAAsB,EAAE;AAC5D,UAAI,CAAC,oBAAoB,KAAK,QAAQ,GAAG;AACxC,WAAG,gBAAgB,KAAK,IAAI;AAAA,MAC7B;AACA;AAAA,IACD;AAGA,QAAI,SAAS,SAAS;AACrB,WAAK,QAAQ,iBAAiB,KAAK,KAAK;AAAA,IACzC;AAAA,EACD;AACD;AAEA,SAAS,aAAa,MAAe,MAAoB,OAAqB;AAE7E,WAAS,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AACnD,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,UAAM,MAAM,MAAM,QAAQ,YAAY;AAEtC,QAAI,SAAS,OAAO;AACnB,UAAI,QAAQ,iBAAiB;AAE5B,8BAAsB,OAAO,KAAK;AAClC,qBAAa,OAAO,QAAQ,KAAK;AAAA,MAClC,WAAW,QAAQ,SAAS;AAE3B,8BAAsB,OAAO,KAAK;AAClC,YAAI,MAAM,aAAa;AACtB,gBAAM,cAAc,qBAAqB,MAAM,WAAW;AAAA,QAC3D;AAAA,MACD,WAAW,eAAe,IAAI,GAAG,KAAK,qBAAqB,KAAK,GAAG;AAElE,cAAM,OAAO;AAAA,MACd,WAAW,iBAAiB,IAAI,GAAG,GAAG;AACrC,8BAAsB,OAAO,KAAK;AAClC,qBAAa,OAAO,OAAO,KAAK;AAAA,MACjC,OAAO;AACN,cAAM,OAAO;AAAA,MACd;AAAA,IACD,OAAO;AAEN,UAAI,kBAAkB,IAAI,GAAG,GAAG;AAC/B,cAAM,OAAO;AAAA,MACd,WAAW,kBAAkB,IAAI,GAAG,GAAG;AACtC,+BAAuB,KAAK;AAC5B,qBAAa,OAAO,QAAQ,KAAK;AAAA,MAClC,OAAO;AACN,cAAM,OAAO;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACD;AAEA,SAAS,iBAAiB,SAAiB,OAAuB;AACjE,QAAM,MAAM,IAAI,UAAU,EAAE,gBAAgB,SAAS,eAAe;AAEpE,QAAM,aAAa,IAAI,cAAc,aAAa;AAClD,MAAI,WAAY,QAAO;AAEvB,QAAM,MAAM,IAAI;AAChB,MAAI,IAAI,QAAQ,YAAY,MAAM,MAAO,QAAO;AAEhD,wBAAsB,KAAK,KAAK;AAChC,eAAa,KAAK,OAAO,KAAK;AAE9B,MAAI,IAAI,SAAS,WAAW,EAAG,QAAO;AAEtC,SAAO,IAAI,cAAc,EAAE,kBAAkB,GAAG;AACjD;AAaO,SAAS,YAAY,SAAyB;AACpD,SAAO,iBAAiB,SAAS,CAAC;AACnC;",
"names": []
}