url-sanitizer 5.0.1 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -15
- package/dist/url-sanitizer-wo-dompurify.min.js +1 -1
- package/dist/url-sanitizer-wo-dompurify.min.js.map +4 -4
- package/dist/url-sanitizer.min.js +4 -4
- package/dist/url-sanitizer.min.js.map +4 -4
- package/package.json +7 -7
- package/src/index.js +72 -8
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/common.js +10 -0
- package/src/mjs/constant.js +6 -0
- package/src/mjs/filter.js +444 -0
- package/src/mjs/regexp.js +24 -18
- package/src/mjs/sanitize.js +509 -0
- package/src/mjs/scheme.js +150 -0
- package/src/mjs/text-chars.js +4 -4
- package/src/mjs/{uri-util.js → utility.js} +98 -132
- package/types/index.d.ts +27 -1
- package/types/mjs/common.d.ts +1 -0
- package/types/mjs/constant.d.ts +1 -0
- package/types/mjs/filter.d.ts +28 -0
- package/types/mjs/regexp.d.ts +4 -3
- package/types/mjs/sanitize.d.ts +57 -0
- package/types/mjs/scheme.d.ts +11 -0
- package/types/mjs/{uri-util.d.ts → utility.d.ts} +10 -11
- package/src/mjs/sanitizer.js +0 -1019
- package/types/mjs/sanitizer.d.ts +0 -90
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../bundle_wo_dompurify/mjs/
|
|
4
|
-
"sourcesContent": ["/**\n * dompurify.js (for wo-dompurify build)\n */\n\n/* constants */\nconst PROPERTIES = Object.freeze([\n 'addHook',\n 'removeHook',\n 'removeAllHooks',\n 'setConfig'\n]);\n\nconst hookQueue = [];\nexport const domPurify = new Proxy({}, {\n get(target, prop, receiver) {\n const purifier = globalThis.DOMPurify || globalThis.window?.DOMPurify;\n if (purifier) {\n if (hookQueue.length) {\n for (const { prop: p, args } of hookQueue) {\n if (typeof purifier[p] === 'function') {\n purifier[p](...args);\n }\n }\n hookQueue.length = 0;\n }\n const value = purifier[prop];\n return typeof value === 'function' ? value.bind(purifier) : value;\n }\n return (...args) => {\n if (PROPERTIES.includes(prop)) {\n hookQueue.push({ prop, args });\n return receiver;\n }\n throw new Error('DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).');\n };\n }\n});\n", "/**\n * common.js\n */\n\n/* shared */\nimport { TYPE_FROM, TYPE_TO } from './constant.js';\n\n/**\n * Gets the type of the given object.\n * Returns the object class name (e.g., \"String\", \"Array\", \"Null\", \"Undefined\").\n * @param {unknown} o - The object to check.\n * @returns {string} The specific type of the object.\n */\nexport const getType = o =>\n Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);\n\n/**\n * Checks if the given value is a string.\n * @param {unknown} o - The value to check.\n * @returns {boolean} True if the value is a string, false otherwise.\n */\nexport const isString = o => typeof o === 'string' || o instanceof String;\n", "[\"aaa\",\"aaas\",\"about\",\"acap\",\"acct\",\"acd\",\"acr\",\"adiumxtra\",\"adt\",\"aet\",\"afp\",\"afs\",\"agtp\",\"aim\",\"amss\",\"android\",\"appdata\",\"apt\",\"ar\",\"ari\",\"ark\",\"at\",\"attachment\",\"aw\",\"barion\",\"beshare\",\"bitcoin\",\"bitcoincash\",\"bl\",\"blob\",\"bluetooth\",\"bolo\",\"brid\",\"browserext\",\"cabal\",\"caip\",\"calculator\",\"callto\",\"cap\",\"cast\",\"casts\",\"chrome\",\"chrome-extension\",\"cid\",\"cm\",\"coap\",\"coaps\",\"com-eventbrite-attendee\",\"content\",\"content-type\",\"crid\",\"cstr\",\"cttps\",\"cvs\",\"dab\",\"dat\",\"data\",\"dav\",\"dhttp\",\"diaspora\",\"dict\",\"did\",\"dilithium3\",\"dis\",\"dlna-playcontainer\",\"dlna-playsingle\",\"dnp\",\"dns\",\"dntp\",\"doi\",\"donau\",\"dpp\",\"drm\",\"dtmi\",\"dtn\",\"dvb\",\"dvx\",\"dweb\",\"ed2k\",\"eid\",\"elsi\",\"embedded\",\"ens\",\"esim\",\"ethereum\",\"example\",\"ez\",\"facetime\",\"feed\",\"feedready\",\"fido\",\"file\",\"finger\",\"first-run-pen-experience\",\"fish\",\"fm\",\"ftp\",\"fuchsia-pkg\",\"gcx\",\"gdsi\",\"geo\",\"gg\",\"git\",\"gitoid\",\"gizmoproject\",\"go\",\"gopher\",\"graph\",\"gtalk\",\"h323\",\"ham\",\"hcap\",\"hcp\",\"hs20\",\"http\",\"https\",\"hxxp\",\"hxxps\",\"hydrazone\",\"hyper\",\"i0\",\"iax\",\"ibi\",\"ibi-\",\"icap\",\"icon\",\"ilstring\",\"im\",\"imap\",\"info\",\"iotdisco\",\"ipfs\",\"ipn\",\"ipns\",\"ipp\",\"ipps\",\"irc\",\"irc6\",\"ircs\",\"iris\",\"iris.beep\",\"iris.lwz\",\"iris.xpc\",\"iris.xpcs\",\"isostore\",\"itms\",\"jabber\",\"jar\",\"jms\",\"keyparc\",\"lastfm\",\"lbry\",\"ldap\",\"ldaps\",\"leaptofrogans\",\"lid\",\"linkid\",\"lorawan\",\"lpa\",\"lvlt\",\"machineProvisioningProgressReporter\",\"magnet\",\"mailto\",\"maps\",\"market\",\"matrix\",\"mdoc\",\"mdoc-openid4vp\",\"message\",\"microsoft.windows.camera\",\"microsoft.windows.camera.multipicker\",\"microsoft.windows.camera.picker\",\"mid\",\"mms\",\"mongodb\",\"moz\",\"moz-extension\",\"mqtt\",\"mqtts\",\"ms-access\",\"ms-appinstaller\",\"ms-browser-extension\",\"ms-calculator\",\"ms-drive-to\",\"ms-enrollment\",\"ms-excel\",\"ms-eyecontrolspeech\",\"ms-gamebarservices\",\"ms-gamingoverlay\",\"ms-getoffice\",\"ms-help\",\"ms-infopath\",\"ms-inputapp\",\"ms-launchremotedesktop\",\"ms-lockscreencomponent-config\",\"ms-media-stream-id\",\"ms-meetnow\",\"ms-mixedrealitycapture\",\"ms-mobileplans\",\"ms-newsandinterests\",\"ms-officeapp\",\"ms-people\",\"ms-personacard\",\"ms-powerpoint\",\"ms-project\",\"ms-publisher\",\"ms-recall\",\"ms-remotedesktop\",\"ms-remotedesktop-launch\",\"ms-restoretabcompanion\",\"ms-screenclip\",\"ms-screensketch\",\"ms-search\",\"ms-search-repair\",\"ms-secondary-screen-controller\",\"ms-secondary-screen-setup\",\"ms-settings\",\"ms-settings-airplanemode\",\"ms-settings-bluetooth\",\"ms-settings-camera\",\"ms-settings-cellular\",\"ms-settings-cloudstorage\",\"ms-settings-connectabledevices\",\"ms-settings-displays-topology\",\"ms-settings-emailandaccounts\",\"ms-settings-language\",\"ms-settings-location\",\"ms-settings-lock\",\"ms-settings-nfctransactions\",\"ms-settings-notifications\",\"ms-settings-power\",\"ms-settings-privacy\",\"ms-settings-proximity\",\"ms-settings-screenrotation\",\"ms-settings-wifi\",\"ms-settings-workplace\",\"ms-spd\",\"ms-stickers\",\"ms-sttoverlay\",\"ms-transit-to\",\"ms-useractivityset\",\"ms-uup\",\"ms-virtualtouchpad\",\"ms-visio\",\"ms-walk-to\",\"ms-whiteboard\",\"ms-whiteboard-cmd\",\"ms-widgetboard\",\"ms-widgets\",\"ms-word\",\"msnim\",\"msrp\",\"msrps\",\"mss\",\"mt\",\"mtqp\",\"mtrust\",\"mumble\",\"mupdate\",\"musik\",\"mvn\",\"mvrp\",\"mvrps\",\"news\",\"nfs\",\"ni\",\"nih\",\"nntp\",\"nostr\",\"notes\",\"npamp\",\"num\",\"ocf\",\"oid\",\"onenote\",\"onenote-cmd\",\"opaquelocktoken\",\"openid\",\"openpgp4fpr\",\"otpauth\",\"palm\",\"paparazzi\",\"payto\",\"pkcs11\",\"pkg\",\"platform\",\"pop\",\"pres\",\"proxy\",\"psyc\",\"pttp\",\"pwid\",\"qb\",\"query\",\"quic-transport\",\"redis\",\"rediss\",\"reload\",\"res\",\"resource\",\"rmi\",\"rsync\",\"rtmfp\",\"rtmp\",\"rtsp\",\"rtsps\",\"rtspu\",\"sarif\",\"secondlife\",\"secret-token\",\"service\",\"session\",\"sftp\",\"sgn\",\"shc\",\"shelter\",\"sieve\",\"simpleledger\",\"simplex\",\"sip\",\"sips\",\"skype\",\"smb\",\"smp\",\"sms\",\"smtp\",\"snmp\",\"soap.beep\",\"soap.beeps\",\"soldat\",\"spacify\",\"spiffe\",\"spotify\",\"ssb\",\"ssh\",\"sss\",\"starknet\",\"steam\",\"stun\",\"stuns\",\"submit\",\"svn\",\"swh\",\"swid\",\"swidpath\",\"tag\",\"taler\",\"teamspeak\",\"teapot\",\"teapots\",\"tel\",\"teliaeid\",\"telnet\",\"tftp\",\"things\",\"thismessage\",\"tip\",\"tn3270\",\"tool\",\"tttps\",\"turn\",\"turns\",\"tv\",\"udp\",\"unreal\",\"upn\",\"urn\",\"ust\",\"ut2004\",\"uuaid\",\"uuid-in-package\",\"v-event\",\"vemmi\",\"ventrilo\",\"ves\",\"view-source\",\"vnc\",\"vscode\",\"vscode-insiders\",\"vsls\",\"w3\",\"wasm\",\"wasm-js\",\"wcr\",\"web3\",\"webcal\",\"wifi\",\"ws\",\"wss\",\"wtai\",\"wyciwyg\",\"xcompute\",\"xcon\",\"xcon-userid\",\"xfire\",\"xftp\",\"xmlrpc.beep\",\"xmlrpc.beeps\",\"xmpp\",\"xrcp\",\"xri\",\"ymsgr\",\"z39.50r\",\"z39.50s\",\"ztdnaid\"]\n", "/**\n * regexp.js\n */\n\n/**\n * Matches and captures the charset parameter in a media type string.\n * @type {RegExp}\n */\nexport const REG_CHARSET = /^charset\\s*=\\s*([\"']?)(?<name>[\\w:-]+)\\1$/;\n\n/**\n * Matches a trailing empty fragment identifier (#).\n * @type {RegExp}\n */\nexport const REG_HASH = /#$/;\n\n/**\n * Matches MIME types that contain DOM-parsable content (HTML, XML, SVG).\n * @type {RegExp}\n */\nexport const REG_MIME_DOM =\n /^(?:application\\/(?:[\\w.-]+\\+)?xml|image\\/svg\\+xml|text\\/(?:html|xml))(?:\\s*;\\s*|$)/i;\n\n/**\n * Matches general text MIME types.\n * @type {RegExp}\n */\nexport const REG_MIME_TEXT = /^text\\/[\\w#&.;+-]+/i;\n\n/**\n * Matches numeric character references (hex or decimal).\n * @type {RegExp}\n */\nexport const REG_NUM_REF = /&#(x0*[\\dA-F]{1,2}|0*\\d{1,3});?/gi;\n\n/**\n * Matches a trailing empty query string (?), ensuring it is not part of a hash.\n * @type {RegExp}\n */\nexport const REG_QUERY = /^((?:[^#%]|%(?!23))*)\\?$/;\n\n/**\n * Validates a string against standard IANA URI scheme syntax.\n * @type {RegExp}\n */\nexport const REG_SCHEME = /^[a-z][\\da-z+.-]*$/;\n\n/**\n * Matches web custom schemes (`web+`) or application-specific schemes (`ext+`).\n * - `web+` schemes comply with the HTML Standard.\n * - `ext+` schemes comply with RFC 3986 but disallow `+` in the suffix.\n * @type {RegExp}\n */\nexport const REG_SCHEME_EXT = /^(?:web\\+[a-z]+|ext\\+[a-z][\\da-z.-]*)$/;\n\n/**\n * Matches executable script schemes (javascript or vbscript).\n * @type {RegExp}\n */\nexport const REG_SCRIPT = /^(?:java|vb)script$/i;\n\n/**\n * Matches script-executing or data-fetching schemes.\n * @type {RegExp}\n */\nexport const REG_SCRIPT_OR_BLOB = /^(?:(?:java|vb)script|blob)$/i;\n\n/**\n * Matches HTML-sensitive characters and their encoded variants.\n * @type {RegExp}\n */\nexport const REG_TAG_QUOT =\n /[\"'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i;\n\n/**\n * Matches a percent-encoded character (%XX).\n * @type {RegExp}\n */\nexport const REG_URL_ENC = /^%[\\dA-F]{2}$/i;\n\n/**\n * Matches one or more consecutive percent-encoded sequences (%20, %E3%81%82).\n * @type {RegExp}\n */\nexport const REG_PCT_ENC = /(?:%[\\dA-F]{2})+/gi;\n\n/**\n * Matches HTML-encoded ampersands.\n * @type {RegExp}\n */\nexport const REG_AMP = /&/gi;\n\n/**\n * Matches ampersand that may begin an HTML entity reference.\n * @type {RegExp}\n */\nexport const REG_AMP_ENC = /&(?=[#a-z0-9])(?![a-z0-9]+=)/i;\n\n/**\n * Matches invalid relative URL patterns.\n * @type {RegExp}\n */\nexport const REG_VERIFY_RELATIVE = /^(?:\\/{2,}|\\\\|[a-z][a-z\\d+\\-.]*:[^/])/i;\n", "[9,10,13,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]\n", "/**\n * text-chars.js\n */\n\n/* shared */\nimport textChars from '../lib/file/text-chars.json' with { type: 'json' };\nimport { BYTE_RANGE, HEX } from './constant.js';\n\n/**\n * A set of valid text character codes.\n * @type {Set<number>}\n */\nexport const TEXT_CHAR_CODES = new Set(textChars);\n\n/**\n * A mapping of Windows-1252 specific character codes to their corresponding Unicode code points.\n * @type {Map<number, number>}\n */\nexport const WINDOWS1252_TO_UNICODE = new Map([\n [128, 0x20ac],\n [130, 0x201a],\n [131, 0x0192],\n [132, 0x201e],\n [133, 0x2026],\n [134, 0x2020],\n [135, 0x2021],\n [136, 0x02c6],\n [137, 0x2030],\n [138, 0x0160],\n [139, 0x2039],\n [140, 0x0152],\n [142, 0x017d],\n [145, 0x2018],\n [146, 0x2019],\n [147, 0x201c],\n [148, 0x201d],\n [149, 0x2022],\n [150, 0x2013],\n [151, 0x2014],\n [152, 0x02dc],\n [153, 0x2122],\n [154, 0x0161],\n [155, 0x203a],\n [156, 0x0153],\n [158, 0x017e],\n [159, 0x0178]\n]);\n\n/**\n * Generates hexadecimal escape strings for non-text characters.\n * @private\n * @returns {Map<number, string>} Generated control char codes.\n */\nconst generateNonTextCharCodes = () => {\n const charCodes = new Map();\n for (let i = 0; i < BYTE_RANGE; i++) {\n if (!TEXT_CHAR_CODES.has(i) && !WINDOWS1252_TO_UNICODE.has(i)) {\n charCodes.set(i, `\\\\x${i.toString(HEX).padStart(2, '0').toUpperCase()}`);\n }\n }\n return charCodes;\n};\n\n/**\n * A mapping of non-text character codes to their hexadecimal escape strings.\n * @type {Map<number, string>}\n */\nexport const NON_TEXT_CHAR_CODES = generateNonTextCharCodes();\n", "/**\n * uri-util.js\n */\n\n/* shared */\nimport uriSchemes from '../lib/iana/uri-schemes.json' with { type: 'json' };\nimport { getType, isString } from './common.js';\n\n/* constants */\nimport {\n BYTE_RANGE,\n DECI,\n HEX,\n MAX_NEST,\n TRUNCATE_LENGTH\n} from './constant.js';\nimport {\n REG_AMP,\n REG_HASH,\n REG_NUM_REF,\n REG_PCT_ENC,\n REG_QUERY,\n REG_SCHEME_EXT,\n REG_SCRIPT,\n REG_URL_ENC\n} from './regexp.js';\nimport {\n NON_TEXT_CHAR_CODES,\n TEXT_CHAR_CODES,\n WINDOWS1252_TO_UNICODE\n} from './text-chars.js';\nconst [ENC_AMP, ENC_NUM, ENC_LT, ENC_GT, ENC_QUOT, ENC_APOS] = [\n '&',\n '#',\n '<',\n '>',\n '\"',\n \"'\"\n].map(ch => `%${ch.charCodeAt(0).toString(HEX).toUpperCase()}`);\nconst ESCAPE_MAP = {\n [ENC_AMP]: `${ENC_AMP}amp;`,\n [ENC_LT]: `${ENC_AMP}lt;`,\n [ENC_GT]: `${ENC_AMP}gt;`,\n [ENC_QUOT]: `${ENC_AMP}quot;`,\n [ENC_APOS]: `${ENC_AMP}${ENC_NUM}39;`\n};\nconst HEX_TABLE = Array.from(\n { length: BYTE_RANGE },\n (_, i) => `%${i.toString(HEX).padStart(2, '0').toUpperCase()}`\n);\nconst IS_NODE = globalThis.process?.versions?.node !== undefined;\nconst NON_TEXT_PATTERN = `[${[...NON_TEXT_CHAR_CODES.values()].join('')}]`;\nconst REG_NON_TEXT = new RegExp(NON_TEXT_PATTERN);\nconst REG_NON_TEXT_G = new RegExp(NON_TEXT_PATTERN, 'g');\n\n/* encoder / decoder */\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder('utf-8', { fatal: true });\n\n/**\n * URI schemes\n */\nexport class URISchemes {\n /* private fields */\n #schemes = new Set(uriSchemes);\n\n /**\n * Gets the list of registered URI schemes.\n * @see {@link https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml}\n * - Historical schemes are omitted.\n * - The 'moz-extension' scheme is added by default.\n * @returns {string[]} An array of registered schemes.\n */\n get() {\n return [...this.#schemes];\n }\n\n /**\n * Extracts the scheme from URI.\n * @param {string} uri - The URI string.\n * @returns {string|null} The extracted scheme, or null.\n */\n getScheme(uri) {\n if (!isString(uri)) {\n return null;\n }\n const parsed = this.parse(uri);\n if (parsed) {\n return parsed.protocol.replace(/:$/, '');\n }\n return null;\n }\n\n /**\n * Checks if the specified scheme is currently registered.\n * @param {string} scheme - The target scheme.\n * @returns {boolean} True if the scheme is registered.\n */\n has(scheme) {\n const normalized = this.normalize(scheme, true);\n if (normalized) {\n return this.#schemes.has(normalized);\n }\n return false;\n }\n\n /**\n * Normalizes the URI string using NFKC.\n * @param {string} uri - The URL to normalize.\n * @param {boolean} [isScheme] - True if uri is a scheme.\n * @returns {string|null} The normalized URI string, or null.\n */\n normalize(uri, isScheme = false) {\n if (!isString(uri)) {\n return null;\n }\n const normalized = uri.normalize('NFKC');\n if (isScheme) {\n return normalized.trim().toLowerCase();\n }\n return normalized;\n }\n\n /**\n * Parse the URI string.\n * @param {string} uri - The URI string.\n * @param {string} [base] - The base URI string.\n * @returns {URL|null} The parsed URL object, or null.\n */\n parse(uri, base) {\n if (!isString(uri)) {\n return null;\n }\n const normalized = this.normalize(uri);\n if (base && isString(base)) {\n return URL.parse(normalized, this.normalize(base));\n }\n return URL.parse(normalized);\n }\n\n /**\n * Verifies if the given URI is valid and its scheme is allowed.\n * @param {string} uri - The URI string to verify.\n * @param {Set<string>} [schemes] - The set of allowed schemes.\n * @returns {boolean} True if the URI is valid and permitted.\n */\n verify(uri, schemes) {\n if (!isString(uri)) {\n return false;\n }\n const parsedUrl = this.parse(uri);\n return this.verifyParsed(parsedUrl, schemes, true);\n }\n\n /**\n * Verifies if the pre-parsed URL object is valid and its scheme is allowed.\n * @param {URL} parsedUrl - The parsed URL object to verify.\n * @param {Set<string>} [schemes] - The set of allowed schemes.\n * @param {boolean} [allowCustom] - True if web+*, ext+* schemes are allowed.\n * @returns {boolean} True if the parsed URL is valid and permitted.\n */\n verifyParsed(parsedUrl, schemes, allowCustom = false) {\n if (!parsedUrl || !parsedUrl.protocol) {\n return false;\n }\n const scheme = parsedUrl.protocol.toLowerCase().replace(/:$/, '');\n const parts = scheme.split('+');\n const isScript = parts.some(s => REG_SCRIPT.test(s));\n if (isScript) {\n return false;\n }\n if (!schemes) {\n schemes = this.#schemes;\n }\n if (schemes.has(scheme)) {\n return true;\n }\n if (parts.length > 1) {\n return (\n (allowCustom && REG_SCHEME_EXT.test(scheme)) ||\n parts.every(s => schemes.has(s))\n );\n }\n return false;\n }\n}\n\n/**\n * Extracts an array of schemes.\n * @param {string} protocol - The URL protocol (e.g., \"data:\", \"git+https:\").\n * @returns {string[]} An array of scheme parts.\n */\nexport const getSchemeParts = protocol => {\n if (!isString(protocol)) {\n return [];\n }\n return protocol.replace(/:$/, '').split('+');\n};\n\n/**\n * Gets the URL-encoded representation of a given string.\n * @param {string} str - The target string.\n * @returns {string} The URL-encoded string.\n */\nexport const getURLEncodedString = str => {\n if (!isString(str)) {\n throw new TypeError(`Expected String but got ${getType(str)}.`);\n }\n const bytes = encoder.encode(str);\n const encoded = new Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) {\n encoded[i] = HEX_TABLE[bytes[i]];\n }\n return encoded.join('');\n};\n\n/**\n * Escapes URL-encoded HTML special characters.\n * @param {string} ch - A URL-encoded (percent-encoded) character.\n * @returns {string} The escaped HTML special character, or the given character.\n */\nexport const escapeURLEncodedHTMLChars = ch => {\n if (!isString(ch)) {\n return ch;\n }\n const target = REG_URL_ENC.test(ch) ? ch.toUpperCase() : ch;\n return ESCAPE_MAP[target] ?? ch;\n};\n\n/**\n * Truncate URL string.\n * @param {string|unknown} url - A URL string\n * @returns {string} - The truncated string\n */\nexport const truncateURL = url => {\n const str = isString(url) ? url : String(url);\n const truncated =\n str.length > TRUNCATE_LENGTH ? `${str.slice(0, TRUNCATE_LENGTH)}...` : str;\n return truncated;\n};\n\n/**\n * Removes a trailing empty hash and an empty query string from a URL.\n * @param {string} url - The target URL string to be cleaned.\n * @returns {string} The cleaned URL string or the original input.\n */\nexport const trimTrailingEmptyQueryAndHash = url => {\n if (!isString(url)) {\n return url;\n }\n return url.replace(REG_HASH, '').replace(REG_QUERY, '$1');\n};\n\n/**\n * Replaces URL-encoded numeric character references.\n * @param {string} match - The matched substring.\n * @param {string} value - The captured numeric value.\n * @returns {string} The resolved character or the original match.\n */\nexport const replaceNumCharRef = (match, value) => {\n const num = /^x/i.test(value)\n ? Number.parseInt(value.slice(1), HEX)\n : Number.parseInt(value, DECI);\n if (!Number.isInteger(num) || num < 0 || num >= BYTE_RANGE) {\n return match;\n }\n if (NON_TEXT_CHAR_CODES.has(num)) {\n return '';\n }\n if (TEXT_CHAR_CODES.has(num)) {\n return String.fromCharCode(num);\n }\n const codePoint = WINDOWS1252_TO_UNICODE.get(num);\n return String.fromCodePoint(codePoint);\n};\n\n/**\n * Parses URL-encoded numeric character references.\n * @param {string} str - The target string to parse.\n * @param {number} [nest] - The current nesting depth for recursive parsing.\n * @returns {string} The decoded and parsed string.\n */\nexport const parseURLEncodedNumCharRef = (str, nest = 0) => {\n if (!isString(str)) {\n throw new TypeError(`Expected String but got ${getType(str)}.`);\n }\n if (!Number.isInteger(nest)) {\n throw new TypeError(`Expected Number but got ${getType(nest)}.`);\n }\n let res;\n try {\n res = decodeURIComponent(str);\n } catch {\n // Fallback: decode only valid multi-byte %XX sequences.\n res = str.replace(REG_PCT_ENC, match => {\n try {\n return decodeURIComponent(match);\n } catch {\n return match;\n }\n });\n }\n res = res.replace(REG_NON_TEXT_G, '');\n let depth = 0;\n for (; depth + nest < MAX_NEST; depth++) {\n const previousRes = res;\n // Decode '&' before decoding numeric references.\n res = res.replace(REG_AMP, '&').replace(REG_NUM_REF, replaceNumCharRef);\n if (res === previousRes) {\n break;\n }\n }\n if (depth + nest >= MAX_NEST && /&#/.test(res)) {\n throw new Error('Character references nested too deeply.');\n }\n return res;\n};\n\n/**\n * Extracts the components of a parsed Data URL.\n * @param {string} pathname - The pathname of the URL.\n * @param {string} [search] - The search (query) string of the URL.\n * @param {string} [hash] - The hash (fragment) of the URL.\n * @returns {object} An object containing the media type, parsed media types array, data string, and a boolean indicating if it is base64-encoded.\n */\nexport const extractDataUrlComponents = (pathname, search = '', hash = '') => {\n if (!isString(pathname)) {\n return { mediaType: '', mediaTypes: [], data: '', isBase64: false };\n }\n const comma = pathname.indexOf(',');\n if (comma === -1) {\n return {\n mediaType: pathname,\n mediaTypes: pathname.split(';'),\n data: '',\n isBase64: false\n };\n }\n const mediaType = pathname.slice(0, comma);\n const data = `${pathname.slice(comma + 1)}${search}${hash}`;\n const mediaTypes = mediaType.split(';');\n const isBase64 = /^base64$/i.test(mediaTypes.at(-1));\n return { mediaType, mediaTypes, data, isBase64 };\n};\n\n/**\n * Parses base64-encoded data.\n * @param {string} data - The base64-encoded string.\n * @returns {string} The parsed text, or the original base64 if binary.\n */\nexport const parseBase64 = data => {\n if (!isString(data)) {\n throw new TypeError(`Expected String but got ${getType(data)}.`);\n }\n const cleanData = data.replace(/\\s/g, '');\n let binStr;\n try {\n binStr = atob(cleanData);\n } catch {\n throw new Error(`Invalid base64 data: ${truncateURL(data)}`);\n }\n let bytes;\n if (IS_NODE && globalThis.Buffer) {\n // Use 'latin1' to correctly map the binary string directly to bytes.\n // @see https://nodejs.org/docs/latest/api/buffer.html#buffers-and-character-encodings\n bytes = globalThis.Buffer.from(binStr, 'latin1');\n } else {\n const len = binStr.length;\n bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binStr.charCodeAt(i);\n }\n }\n try {\n const text = decoder.decode(bytes);\n if (REG_NON_TEXT.test(text)) {\n return cleanData;\n }\n return text;\n } catch {\n return cleanData;\n }\n};\n", "/**\n * sanitizer.js\n */\n\n/* shared */\nimport { domPurify } from './dompurify.js';\nimport { getType, isString } from './common.js';\nimport {\n URISchemes,\n escapeURLEncodedHTMLChars,\n extractDataUrlComponents,\n getSchemeParts,\n parseBase64,\n parseURLEncodedNumCharRef,\n trimTrailingEmptyQueryAndHash,\n truncateURL\n} from './uri-util.js';\n\n/* constants */\nimport {\n CHUNK_SIZE,\n DECI,\n MAX_BLOB_SIZE,\n MAX_NEST,\n MAX_URL_LENGTH\n} from './constant.js';\nimport {\n REG_AMP_ENC,\n REG_MIME_DOM,\n REG_SCHEME,\n REG_SCRIPT,\n REG_SCRIPT_OR_BLOB,\n REG_TAG_QUOT,\n REG_VERIFY_RELATIVE\n} from './regexp.js';\nconst DUMMY_BASE = 'http://dummy.local';\nconst IS_NODE = globalThis.process?.versions?.node !== undefined;\nconst URL_PROPS = Object.freeze([\n 'href',\n 'origin',\n 'protocol',\n 'username',\n 'password',\n 'host',\n 'hostname',\n 'port',\n 'pathname',\n 'search',\n 'hash'\n]);\nconst DEFAULT_OPTS = Object.freeze({\n allow: Object.freeze([]),\n deny: Object.freeze([]),\n only: Object.freeze([]),\n allowRelative: false,\n debug: false,\n revokeObjectURL: false,\n maxBlobSize: MAX_BLOB_SIZE,\n maxLength: MAX_URL_LENGTH\n});\n\n/* typedef */\n/**\n * The parsed result of a data URL.\n * @typedef {object} InspectedDataURL\n * @property {string} mime - The MIME type of the data.\n * @property {boolean} base64 - Indicates whether the data is base64-encoded.\n * @property {string} data - The actual data part of the data URL.\n */\n\n/**\n * The result of an inspected URL, extending the standard URL API.\n * The properties except for input and valid are omitted for invalid URLs.\n * @typedef {object} InspectedURLResult\n * @property {string} input - The original URL input.\n * @property {boolean} valid - Indicates whether the URL passed sanitization rules.\n * @property {boolean} [relative] - Indicates whether the input is a valid relative URL.\n * @property {string} [reason] - The reason why the URL is invalid.\n * @property {InspectedDataURL | null} [data] - The parsed result of a data URL. Null if not a data URL.\n * @property {string} [href] - The sanitized URL input.\n * @property {string} [origin] - The scheme, domain, and port.\n * @property {string} [protocol] - The protocol scheme.\n * @property {string} [username] - The specified username.\n * @property {string} [password] - The specified password.\n * @property {string} [host] - The domain and port.\n * @property {string} [hostname] - The domain.\n * @property {string} [port] - The port number.\n * @property {string} [pathname] - The path.\n * @property {string} [search] - The query string.\n * @property {string} [hash] - The fragment identifier.\n */\n\n/**\n * Internal debug logger.\n * @param {string} message - The message to output.\n * @param {Error} [error] - The caught error object, if any.\n * @returns {void}\n */\nexport const logDebug = (message, error) => {\n console.warn(`[URLSanitizer Debug] ${message}`, error);\n};\n\n/**\n * Context manager for the sanitization process.\n */\nexport class SanitizeContext {\n /**\n * @param {object} opt - Sanitization options.\n * @param {object} domPurifyInstance - The DOMPurify instance.\n */\n constructor(opt, domPurifyInstance) {\n this.debug = !!opt?.debug;\n this.domPurify = domPurifyInstance;\n this.nest = 0;\n this.recurse = new Set();\n this.schemeMap = new Map([\n ['blob', false],\n ['data', false],\n ['file', false],\n ['javascript', false],\n ['vbscript', false]\n ]);\n }\n\n /**\n * Enters a new level of nested URL sanitization.\n * Tracks the URL to prevent circular references.\n * @param {string} url - The URL being processed.\n * @returns {boolean} True if it is safe to proceed, false if circular reference detected.\n */\n enter(url) {\n if (this.recurse.has(url)) {\n if (this.debug) {\n logDebug(`Circular Data URL detected and skipped: ${truncateURL(url)}`);\n }\n return false;\n }\n this.nest++;\n this.recurse.add(url);\n return true;\n }\n\n /**\n * Leaves the current level of nested URL sanitization.\n * Cleans up the tracking state after processing the URL.\n * @param {string} url - The URL that finished processing.\n */\n leave(url) {\n this.nest--;\n this.recurse.delete(url);\n }\n}\n\n/**\n * URL sanitizer\n */\nexport class URLSanitizer extends URISchemes {\n /* private fields */\n #allowedSchemes;\n\n constructor() {\n super();\n this.#allowedSchemes = new Set(super.get());\n }\n\n /**\n * Executes the core sanitization logic.\n * @private\n * @param {string} url - The URL string to sanitize.\n * @param {object} opt - Sanitization options.\n * @returns {string|null} The sanitized URL, or null.\n */\n #executeSanitize(url, opt) {\n if (!url || !isString(url)) {\n return null;\n }\n const { allow, allowRelative, deny, maxLength, only } = opt;\n if (Number.isInteger(maxLength) && url.length > maxLength) {\n const msg = `URL length ${url.length} exceeds max length ${maxLength}.`;\n throw new RangeError(msg);\n }\n const hasRestrictiveRules =\n (Array.isArray(deny) && deny.length) ||\n (Array.isArray(only) && only.length) ||\n allowRelative;\n // Early return for standard HTTP/HTTPS URLs without restrictive rules.\n if (\n !hasRestrictiveRules &&\n (url.startsWith('https://') || url.startsWith('http://')) &&\n !REG_TAG_QUOT.test(url) &&\n !url.includes('data:')\n ) {\n const urlObj = this.parse(url);\n if (urlObj) {\n return urlObj.href.replace(/%26/g, escapeURLEncodedHTMLChars);\n }\n return null;\n }\n const ctx = new SanitizeContext(opt, domPurify);\n return this.#process(url, { allow, allowRelative, deny, only }, ctx);\n }\n\n /**\n * Internal recursive method for sanitization.\n * @private\n * @param {string} url - The URL string to sanitize.\n * @param {object} rules - The sanitization rules.\n * @param {string[]} rules.allow - Allowed schemes.\n * @param {boolean} rules.allowRelative - Allow relative URLs.\n * @param {string[]} rules.deny - Denied schemes.\n * @param {string[]} rules.only - Exclusively allowed schemes.\n * @param {SanitizeContext} ctx - Internal context for state.\n * @returns {string|null} The sanitized URL, or null.\n */\n #process(url, { allow, allowRelative, deny, only }, ctx) {\n if (ctx.nest > MAX_NEST) {\n throw new Error('Data URLs nested too deeply.');\n }\n // Resolve allowed/denied schemes\n const { allowedSchemes, restrictScheme, schemeMap } =\n this.#resolveSchemeRules({ allow, deny, only }, ctx);\n // Parse and verify the URL\n const {\n isDataUrl,\n isRelative,\n isVerified,\n scheme,\n schemeParts,\n urlObj,\n urlToSanitize\n } = this.#parseAndVerifyURL(url, allowRelative, allowedSchemes, ctx);\n if (!isVerified) {\n return null;\n }\n // Check if the scheme is allowed\n if (\n !this.#isSchemeAllowed(\n scheme,\n schemeParts,\n restrictScheme,\n schemeMap,\n isRelative\n )\n ) {\n return null;\n }\n // Sanitize based on URL type\n if (isDataUrl) {\n return this.#sanitizeDataURL(urlObj, scheme, ctx);\n }\n return this.#sanitizeStandardURL(urlToSanitize);\n }\n\n /**\n * Resolves allow, deny, and only rules into mappings.\n * @private\n * @param {object} rules - The sanitization rules.\n * @param {string[]} rules.allow - Allowed schemes.\n * @param {string[]} rules.deny - Denied schemes.\n * @param {string[]} rules.only - Exclusively allowed schemes.\n * @param {SanitizeContext} ctx - Context for state management.\n * @returns {object} Resolved scheme objects and flags.\n */\n #resolveSchemeRules({ allow, deny, only }, ctx) {\n let allowedSchemes = this.#allowedSchemes;\n let restrictScheme = false;\n const schemeMap = new Map(ctx.schemeMap);\n if (Array.isArray(only) && only.length) {\n allowedSchemes = new Set();\n restrictScheme = true;\n for (const item of only) {\n if (isString(item)) {\n this.#registerScheme(item, 'only', allowedSchemes, schemeMap, ctx);\n }\n }\n } else {\n if (Array.isArray(allow) && allow.length) {\n allowedSchemes = new Set(this.#allowedSchemes);\n for (const item of allow) {\n if (isString(item)) {\n this.#registerScheme(item, 'allow', allowedSchemes, schemeMap, ctx);\n }\n }\n }\n if (Array.isArray(deny) && deny.length) {\n for (const item of deny) {\n if (isString(item)) {\n const normalized = this.normalize(item, true);\n if (normalized) {\n schemeMap.set(normalized, false);\n }\n }\n }\n }\n }\n return { allowedSchemes, restrictScheme, schemeMap };\n }\n\n /**\n * Helper method to register schemes for the 'allow' or 'only' options.\n * @private\n * @param {string} item - The scheme to register.\n * @param {string} listName - The name of the target option list.\n * @param {Set<string>} allowedSchemes - The local set of allowed schemes.\n * @param {Map<string, boolean>} schemeMap - The local map of schemes.\n * @param {SanitizeContext} ctx - The context for state management.\n * @returns {boolean} True if the scheme is successfully registered.\n */\n #registerScheme(item, listName, allowedSchemes, schemeMap, ctx) {\n const normalizedScheme = this.normalize(item, true);\n if (!this.#isValidScheme(normalizedScheme)) {\n return false;\n }\n schemeMap.set(normalizedScheme, true);\n allowedSchemes.add(normalizedScheme);\n return true;\n }\n\n /**\n * Validates if a normalized URI scheme is syntactically correct.\n * @private\n * @param {string} normalizedScheme - The normalized URI scheme to validate.\n * @returns {boolean} True if the scheme satisfies the syntax and security requirements.\n */\n #isValidScheme(normalizedScheme) {\n if (REG_SCRIPT_OR_BLOB.test(normalizedScheme)) {\n return false;\n }\n const schemeParts = normalizedScheme.split('+');\n const isScript = schemeParts.some(s => REG_SCRIPT.test(s));\n return !isScript && REG_SCHEME.test(normalizedScheme);\n }\n\n /**\n * Parses and verifies absolute and relative URLs.\n * @private\n * @param {string} url - The URL to parse.\n * @param {boolean} allowRelative - Allow relative URLs.\n * @param {Set<string>} allowedSchemes - Permitted schemes.\n * @param {SanitizeContext} ctx - Context for logging and state.\n * @returns {object} Parsed URL properties and flags.\n */\n #parseAndVerifyURL(url, allowRelative, allowedSchemes, ctx) {\n const urlObj = this.parse(url);\n let isVerified = this.verifyParsed(urlObj, allowedSchemes);\n let isRelative = false;\n let relativePath = '';\n // Handle Relative URLs\n if (!isVerified && allowRelative && !REG_VERIFY_RELATIVE.test(url)) {\n const dummyUrl = this.parse(url, DUMMY_BASE);\n if (dummyUrl) {\n if (\n dummyUrl.protocol === 'http:' &&\n dummyUrl.hostname === 'dummy.local'\n ) {\n isVerified = true;\n isRelative = true;\n relativePath = `${dummyUrl.pathname}${dummyUrl.search}${dummyUrl.hash}`;\n }\n }\n }\n if (!isVerified) {\n return { isVerified: false };\n }\n // Extract parts if verified\n if (isRelative) {\n return {\n isRelative,\n isVerified,\n isDataUrl: false,\n scheme: '',\n schemeParts: [],\n urlToSanitize: relativePath\n };\n }\n const scheme = urlObj.protocol.replace(/:$/, '');\n const schemeParts = scheme.split('+');\n return {\n isRelative,\n isVerified,\n scheme,\n schemeParts,\n urlObj,\n isDataUrl: scheme === 'data',\n urlToSanitize: urlObj.href\n };\n }\n\n /**\n * Evaluates if the extracted scheme is permitted.\n * @private\n * @param {string} scheme - The normalized URL scheme.\n * @param {string[]} schemeParts - Parts of the split scheme.\n * @param {boolean} restrictScheme - If rules strictly limit.\n * @param {Map<string, boolean>} schemeMap - Scheme rules map.\n * @param {boolean} isRelative - If the URL is relative.\n * @returns {boolean} True if the scheme is allowed.\n */\n #isSchemeAllowed(scheme, schemeParts, restrictScheme, schemeMap, isRelative) {\n if (isRelative) {\n return true;\n }\n if (restrictScheme) {\n return schemeMap.has(scheme) || schemeParts.every(s => schemeMap.get(s));\n }\n for (const [key, value] of schemeMap.entries()) {\n const bool =\n value || (scheme !== key && schemeParts.every(s => s !== key));\n if (!bool) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Decodes, verifies inner protocols, and purifies data URLs.\n * @private\n * @param {object} urlObj - The URL object.\n * @param {string} scheme - The URL scheme.\n * @param {SanitizeContext} ctx - Context for DOMPurify sanitization.\n * @returns {string|null} Sanitized data URL or null.\n */\n #sanitizeDataURL(urlObj, scheme, ctx) {\n const { mediaType, mediaTypes, data, isBase64 } = extractDataUrlComponents(\n urlObj.pathname,\n urlObj.search,\n urlObj.hash\n );\n let parsedData = data;\n if (isBase64) {\n try {\n parsedData = parseBase64(data);\n } catch (e) {\n if (ctx.debug) {\n logDebug('Failed to parse base64 data.', e);\n }\n return null;\n }\n }\n try {\n const decodedData = parseURLEncodedNumCharRef(parsedData).trim();\n const parsedUrl = this.parse(decodedData, DUMMY_BASE);\n if (!parsedUrl) {\n return null;\n }\n const dataScheme = parsedUrl.protocol;\n const dataSchemeParts = getSchemeParts(dataScheme);\n if (dataSchemeParts.some(s => REG_SCRIPT_OR_BLOB.test(s))) {\n return null;\n }\n } catch (e) {\n if (ctx.debug) {\n logDebug('Failed to parse inner data URL protocol.', e);\n }\n return null;\n }\n if (!mediaType || REG_MIME_DOM.test(mediaType)) {\n parsedData = this.#purify(parsedData, ctx);\n }\n if (parsedData) {\n if (isBase64 && parsedData !== data) {\n mediaTypes.pop();\n }\n return `${scheme}:${mediaTypes.join(';')},${parsedData}`;\n }\n return null;\n }\n\n /**\n * Purifies a URL-encoded DOM string to prevent XSS.\n * @private\n * @param {string} dom - The URL-encoded DOM string.\n * @param {SanitizeContext} ctx - The context for state management.\n * @returns {string} The purified DOM string.\n */\n #purify(dom, ctx) {\n let decodedDom = dom;\n try {\n decodedDom = decodeURIComponent(dom);\n } catch {\n // fall through\n }\n let purifiedDom;\n const tempHook = (node, evt) =>\n this.#handleSanitizeAttribute(node, evt, ctx);\n ctx.domPurify.addHook('uponSanitizeAttribute', tempHook);\n try {\n purifiedDom = ctx.domPurify.sanitize(decodedDom);\n } finally {\n ctx.domPurify.removeHook('uponSanitizeAttribute');\n }\n purifiedDom = trimTrailingEmptyQueryAndHash(purifiedDom);\n try {\n return encodeURI(purifiedDom);\n } catch {\n return purifiedDom;\n }\n }\n\n /**\n * Internal handler for the DOMPurify hook.\n * @private\n * @param {Node} node - The DOM node.\n * @param {object} evt - The event object.\n * @param {SanitizeContext} ctx - The sanitization context.\n */\n #handleSanitizeAttribute(node, evt, ctx) {\n if (!evt.attrValue || !/^\\s*data:/i.test(evt.attrValue)) {\n return;\n }\n const urlObj = this.parse(evt.attrValue);\n if (!urlObj || urlObj.protocol !== 'data:') {\n return;\n }\n const originalUrl = evt.attrValue;\n if (!ctx.enter(originalUrl)) {\n evt.attrValue = '';\n return;\n }\n try {\n const sanitized = this.#process(\n originalUrl,\n { allow: ['data'], deny: [], only: [], allowRelative: false },\n ctx\n );\n evt.attrValue = sanitized || '';\n } finally {\n ctx.leave(originalUrl);\n }\n }\n\n /**\n * Applies regex cleanups to standard non-data URLs.\n * Strips out trailing queries or problematic characters.\n * @private\n * @param {string} urlToSanitize - The absolute/relative URL.\n * @returns {string|null} Cleaned URL string or null.\n */\n #sanitizeStandardURL(urlToSanitize) {\n let sanitized = urlToSanitize;\n const patterns = [REG_TAG_QUOT, REG_AMP_ENC];\n for (const pattern of patterns) {\n const match = pattern.exec(sanitized);\n if (match) {\n sanitized = sanitized.substring(0, match.index).replace(/[?&]$/, '');\n }\n }\n return sanitized;\n }\n\n /**\n * Sanitizes the given URL.\n * @param {string} url - The URL string to sanitize.\n * @param {object} [opt] - Sanitization options.\n * @param {string[]} [opt.allow] - An array of schemes to allow.\n * @param {string[]} [opt.deny] - An array of schemes to deny.\n * @param {string[]} [opt.only] - An array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Flag to enable debug mode.\n * @param {number} [opt.maxBlobSize] - The maximum allowed blob size in bytes.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {string|null} The sanitized URL, or null.\n */\n sanitize(url, opt = {}) {\n const options = {\n ...DEFAULT_OPTS,\n ...opt\n };\n return this.#executeSanitize(url, options);\n }\n\n /**\n * Inspects, parses, and sanitizes the given URL.\n * @param {string} url - The URL string to parse.\n * @returns {InspectedURLResult} The result of an inspected URL.\n */\n inspect(url) {\n const inspectedURL = {\n input: url,\n valid: false\n };\n if (!isString(url)) {\n inspectedURL.reason = `Invalid URL input: ${truncateURL(url)}`;\n return inspectedURL;\n }\n if (url === '') {\n inspectedURL.reason = 'Invalid URL input: (empty string)';\n return inspectedURL;\n }\n let sanitizedUrl;\n let invalidReason = null;\n try {\n sanitizedUrl = this.#executeSanitize(url, {\n ...DEFAULT_OPTS,\n allow: ['data'],\n allowRelative: true\n });\n if (!sanitizedUrl) {\n invalidReason =\n 'Sanitization failed (blocked by allowed schemes or rules).';\n }\n } catch (e) {\n invalidReason = e.message;\n }\n if (sanitizedUrl) {\n // Reparse the sanitized string to safely extract updated properties.\n const urlObj = this.parse(sanitizedUrl);\n inspectedURL.valid = true;\n if (urlObj) {\n const schemeParts = getSchemeParts(urlObj.protocol);\n const isDataUrl = schemeParts.includes('data');\n if (isDataUrl) {\n const { mediaTypes, data, isBase64 } = extractDataUrlComponents(\n urlObj.pathname,\n urlObj.search,\n urlObj.hash\n );\n if (isBase64) {\n mediaTypes.pop();\n }\n inspectedURL.data = {\n mime: mediaTypes.join(';'),\n base64: isBase64,\n data\n };\n } else {\n inspectedURL.data = null;\n }\n for (const key of URL_PROPS) {\n const value = urlObj[key];\n if (isString(value)) {\n inspectedURL[key] = value;\n }\n }\n } else {\n // Fallback for the valid relative URL without a base URL.\n inspectedURL.valid = false;\n inspectedURL.relative = true;\n inspectedURL.href = sanitizedUrl;\n inspectedURL.reason =\n 'Invalid as an absolute URL, but valid as a relative URL.';\n }\n } else {\n inspectedURL.valid = false;\n inspectedURL.reason = invalidReason;\n }\n return inspectedURL;\n }\n\n /**\n * Gets the list of registered URI schemes.\n * @returns {string[]} An array of registered schemes.\n */\n get() {\n return [...this.#allowedSchemes];\n }\n\n /**\n * Checks if the specified scheme is currently registered.\n * @param {string} scheme - The target scheme.\n * @returns {boolean} True if the scheme is registered.\n */\n has(scheme) {\n const normalizedScheme = this.normalize(scheme, true);\n if (normalizedScheme) {\n return this.#allowedSchemes.has(normalizedScheme);\n }\n return false;\n }\n\n /**\n * Adds a new scheme to the allowed list.\n * @param {string} scheme - The scheme to add.\n * @returns {string[]} The updated array of registered schemes.\n */\n add(scheme) {\n if (!isString(scheme)) {\n throw new TypeError(`Expected String but got ${getType(scheme)}.`);\n }\n const normalizedScheme = this.normalize(scheme, true);\n if (!this.#isValidScheme(normalizedScheme)) {\n throw new Error(`Invalid scheme: ${scheme}`);\n }\n this.#allowedSchemes.add(normalizedScheme);\n return [...this.#allowedSchemes];\n }\n\n /**\n * Removes a scheme from the allowed list.\n * @param {string} scheme - The scheme to remove.\n * @returns {boolean} True if the scheme was successfully removed.\n */\n remove(scheme) {\n if (!isString(scheme)) {\n return false;\n }\n const normalizedScheme = this.normalize(scheme, true);\n return this.#allowedSchemes.delete(normalizedScheme);\n }\n\n /**\n * Resets the registered schemes back to the default initial list.\n * @returns {void}\n */\n reset() {\n this.#allowedSchemes = new Set(super.get());\n }\n\n /**\n * Verifies if the given URI is valid and its scheme is allowed.\n * @param {string} uri - The URI string to verify.\n * @param {Set<string>} [schemes] - The set of allowed schemes.\n * @returns {boolean} True if the URI is syntactically valid and permitted.\n */\n verify(uri, schemes) {\n return super.verify(uri, schemes ?? this.#allowedSchemes);\n }\n}\n\n/* blob handlers */\n/**\n * Converts Blob to data URL from Buffer.\n * @private\n * @param {Blob} blob - The target Blob object.\n * @returns {Promise<string|null>} A promise resolving to the data URL, or null.\n */\nconst convertFromBuffer = async blob => {\n const mimeStr = blob.type ? `${blob.type};base64` : 'base64';\n const buffer = await blob.arrayBuffer();\n const base64 = globalThis.Buffer.from(buffer).toString('base64');\n return `data:${mimeStr},${base64}`;\n};\n\n/**\n * Converts Blob to data URL from FileReader.\n * @private\n * @param {Blob} blob - The target Blob object.\n * @returns {Promise<string|null>} A promise resolving to the data URL, or null.\n */\nconst convertFromFileReader = blob =>\n new Promise((resolve, reject) => {\n const reader = new globalThis.FileReader();\n reader.addEventListener('error', () => {\n const error =\n reader.error ||\n new DOMException(\n 'Failed to read Blob via FileReader.',\n 'NotReadableError'\n );\n reject(error);\n });\n reader.addEventListener('abort', () => resolve(null));\n reader.addEventListener('load', () => resolve(reader.result));\n reader.readAsDataURL(blob);\n });\n\n/**\n * Converts Blob to data URL from btoa.\n * @private\n * @param {Blob} blob - The target Blob object.\n * @returns {Promise<string|null>} A promise resolving to the data URL, or null.\n */\nconst convertFromBtoa = async blob => {\n const mimeStr = blob.type ? `${blob.type};base64` : 'base64';\n const buffer = await blob.arrayBuffer();\n const uint8arr = new Uint8Array(buffer);\n const chunks = [];\n for (let i = 0; i < uint8arr.length; i += CHUNK_SIZE) {\n chunks.push(String.fromCharCode(...uint8arr.subarray(i, i + CHUNK_SIZE)));\n }\n return `data:${mimeStr},${btoa(chunks.join(''))}`;\n};\n\n/**\n * Reads a stream in chunks and validates its size.\n * @private\n * @param {ReadableStreamDefaultReader} reader - The stream reader.\n * @param {number} maxSize - The maximum allowed size in bytes.\n * @param {Uint8Array[]} chunks - The array to store chunks.\n * @param {number} [accumulatedSize] - The currently accumulated size.\n * @returns {Promise<void>}\n */\nconst readStreamInChunks = async (\n reader,\n maxSize,\n chunks,\n accumulatedSize = 0\n) => {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n const newSize = accumulatedSize + value.byteLength;\n if (newSize > maxSize) {\n await reader.cancel('Size limit exceeded');\n const msg = `Payload (${newSize} bytes) exceeds max (${maxSize} bytes).`;\n throw new DOMException(msg, 'NotReadableError');\n }\n chunks.push(value);\n return readStreamInChunks(reader, maxSize, chunks, newSize);\n};\n\n/**\n * Fetches a blob URL and converts it to a data URL.\n * @param {string} url - The blob URL to fetch.\n * @param {number} [maxBlobSize] - The maximum allowed blob size in bytes.\n * @returns {Promise<string>} A promise resolving to the data URL.\n */\nconst fetchBlobAsDataURL = async (url, maxBlobSize) => {\n let maxSize = MAX_BLOB_SIZE;\n if (Number.isInteger(maxBlobSize) && maxBlobSize > 0) {\n maxSize = maxBlobSize;\n }\n const response = await fetch(url);\n if (!response.ok) {\n const truncatedUrl = truncateURL(url);\n let msg = `Failed to fetch ${truncatedUrl}`;\n if (Number.isInteger(response.status)) {\n if (response.statusText) {\n msg += `: ${response.status} ${response.statusText}`;\n } else {\n msg += `: ${response.status}`;\n }\n }\n throw new Error(msg);\n }\n // Check content length if available.\n const contentLength = response.headers.get('content-length');\n if (contentLength) {\n const parsedLength = Number.parseInt(contentLength, DECI);\n if (Number.isInteger(parsedLength) && parsedLength > maxSize) {\n const msg = `Payload (${parsedLength} bytes) exceeds max (${maxSize} bytes).`;\n throw new DOMException(msg, 'NotReadableError');\n }\n }\n let blob;\n // Use ReadableStream.\n if (response.body) {\n const reader = response.body.getReader();\n const chunks = [];\n try {\n await readStreamInChunks(reader, maxSize, chunks, 0);\n } finally {\n reader.releaseLock();\n }\n const type = response.headers.get('content-type');\n if (type) {\n blob = new Blob(chunks, { type });\n } else {\n blob = new Blob(chunks);\n }\n } else {\n blob = await response.blob();\n if (blob.size > maxSize) {\n const msg = `Payload (${blob.size} bytes) exceeds max (${maxSize} bytes).`;\n throw new DOMException(msg, 'NotReadableError');\n }\n }\n if (IS_NODE && globalThis.Buffer) {\n return convertFromBuffer(blob);\n } else if (globalThis.FileReader) {\n return convertFromFileReader(blob);\n }\n return convertFromBtoa(blob);\n};\n\n/* URLSanitizer instance */\nconst urlSanitizer = new URLSanitizer();\n\n/**\n * Asynchronously sanitizes the given URL.\n * NOTE: `blob`, `data`, and `file` schemes must be explicitly allowed.\n * Given a `blob` URL, it securely converts and returns a sanitized `data` URL.\n * @param {string} url - URL\n * @param {object} [opt] - options\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the blob URL after sanitization.\n * @param {number} [opt.maxBlobSize] - The maximum allowed blob size in bytes.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {Promise<string|null>} A promise resolving to the sanitized URL, or null.\n */\nexport const sanitizeURL = async (url, opt = {}) => {\n if (!url || !isString(url)) {\n return null;\n }\n const options = {\n ...DEFAULT_OPTS,\n ...opt\n };\n const scheme = urlSanitizer.getScheme(url);\n if (scheme === null) {\n if (options.allowRelative) {\n return urlSanitizer.sanitize(url, options);\n }\n return null;\n }\n if (scheme === 'blob') {\n const { allow, deny, only } = options;\n let res = null;\n if (\n (allow.includes('blob') && !deny.includes('blob')) ||\n only.includes('blob')\n ) {\n let data;\n try {\n data = await fetchBlobAsDataURL(url, options.maxBlobSize);\n } catch (e) {\n if (options.debug) {\n logDebug(\n `Failed to fetch and convert blob URL: ${truncateURL(url)}`,\n e\n );\n }\n }\n if (data) {\n if (only.length) {\n if (!only.includes('data')) {\n options.only = [...only, 'data'];\n }\n } else {\n if (!allow.includes('data')) {\n options.allow = [...allow, 'data'];\n }\n options.deny = options.deny.filter(s => s !== 'data');\n }\n res = urlSanitizer.sanitize(data, options);\n }\n }\n if (options.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n return res;\n }\n return urlSanitizer.sanitize(url, options);\n};\n\n/**\n * Synchronously sanitizes the given URL.\n * NOTE: `data` and `file` schemes must be explicitly allowed.\n * The `blob` scheme is not supported and will return `null`.\n * @param {string} url - URL\n * @param {object} [opt] - options\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the blob URL.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {string|null} The sanitized URL, or null if denied.\n */\nexport const sanitizeURLSync = (url, opt = {}) => {\n if (!url || !isString(url)) {\n return null;\n }\n const options = {\n ...DEFAULT_OPTS,\n ...opt\n };\n const scheme = urlSanitizer.getScheme(url);\n if (scheme === null) {\n if (options.allowRelative) {\n return urlSanitizer.sanitize(url, options);\n }\n return null;\n }\n if (scheme === 'blob') {\n if (options.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n return null;\n }\n return urlSanitizer.sanitize(url, options);\n};\n\n/**\n * Sanitizes the given URL and returns its parsed components.\n * NOTE: blob URLs are not revoked after inspection.\n * @param {string} url - The URL string to inspect.\n * @returns {Promise<InspectedURLResult>} A promise resolving to the inspected URL result.\n */\nexport const inspectURL = async url => {\n if (isString(url)) {\n const parsedUrl = urlSanitizer.parse(url);\n if (parsedUrl?.protocol === 'blob:') {\n try {\n const dataUrl = await fetchBlobAsDataURL(parsedUrl.href);\n const inspectedURLResult = urlSanitizer.inspect(dataUrl);\n inspectedURLResult.input = url;\n return inspectedURLResult;\n } catch (e) {\n return { input: url, valid: false, reason: e.message };\n }\n }\n }\n return urlSanitizer.inspect(url);\n};\n\n/**\n * Checks if the given string is a valid URI and is registered.\n * @param {string} uri - The URI string to verify.\n * @returns {boolean} True if valid and registered, false otherwise.\n */\nexport const isValidURI = uri => urlSanitizer.verify(uri);\n\n/**\n * @deprecated Use {@link isValidURI} instead.\n * @param {string} uri - The URI string to verify.\n * @returns {boolean} True if valid and registered, false otherwise.\n */\nexport const isURISync = uri => isValidURI(uri);\n\n/* export instance */\nexport default urlSanitizer;\n"],
|
|
5
|
-
"mappings": "AAKA,IAAMA,GAAa,OAAO,OAAO,CAC/B,UACA,aACA,iBACA,WACF,CAAC,EAEKC,EAAY,CAAC,EACNC,EAAY,IAAI,MAAM,CAAC,EAAG,CACrC,IAAIC,EAAQC,EAAMC,EAAU,CAC1B,IAAMC,EAAW,WAAW,WAAa,WAAW,QAAQ,UAC5D,GAAIA,EAAU,CACZ,GAAIL,EAAU,OAAQ,CACpB,OAAW,CAAE,KAAMM,EAAG,KAAAC,CAAK,IAAKP,EAC1B,OAAOK,EAASC,CAAC,GAAM,YACzBD,EAASC,CAAC,EAAE,GAAGC,CAAI,EAGvBP,EAAU,OAAS,CACrB,CACA,IAAMQ,EAAQH,EAASF,CAAI,EAC3B,OAAO,OAAOK,GAAU,WAAaA,EAAM,KAAKH,CAAQ,EAAIG,CAC9D,CACA,MAAO,IAAID,IAAS,CAClB,GAAIR,GAAW,SAASI,CAAI,EAC1B,OAAAH,EAAU,KAAK,CAAE,KAAAG,EAAM,KAAAI,CAAK,CAAC,EACtBH,EAET,MAAM,IAAI,MAAM,4FAA4F,CAC9G,CACF,CACF,CAAC,ECvBM,IAAMK,EAAUC,GACrB,OAAO,UAAU,SAAS,KAAKA,CAAC,EAAE,MAAM,EAAW,EAAO,EAO/CC,EAAWD,GAAK,OAAOA,GAAM,UAAYA,aAAa,OCrBnE,IAAAE,EAAA,CAAC,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM,MAAM,YAAY,MAAM,MAAM,MAAM,MAAM,OAAO,MAAM,OAAO,UAAU,UAAU,MAAM,KAAK,MAAM,MAAM,KAAK,aAAa,KAAK,SAAS,UAAU,UAAU,cAAc,KAAK,OAAO,YAAY,OAAO,OAAO,aAAa,QAAQ,OAAO,aAAa,SAAS,MAAM,OAAO,QAAQ,SAAS,mBAAmB,MAAM,KAAK,OAAO,QAAQ,0BAA0B,UAAU,eAAe,OAAO,OAAO,QAAQ,MAAM,MAAM,MAAM,OAAO,MAAM,QAAQ,WAAW,OAAO,MAAM,aAAa,MAAM,qBAAqB,kBAAkB,MAAM,MAAM,OAAO,MAAM,QAAQ,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,OAAO,WAAW,UAAU,KAAK,WAAW,OAAO,YAAY,OAAO,OAAO,SAAS,2BAA2B,OAAO,KAAK,MAAM,cAAc,MAAM,OAAO,MAAM,KAAK,MAAM,SAAS,eAAe,KAAK,SAAS,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,OAAO,QAAQ,OAAO,QAAQ,YAAY,QAAQ,KAAK,MAAM,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,OAAO,OAAO,WAAW,OAAO,MAAM,OAAO,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,YAAY,WAAW,WAAW,YAAY,WAAW,OAAO,SAAS,MAAM,MAAM,UAAU,SAAS,OAAO,OAAO,QAAQ,gBAAgB,MAAM,SAAS,UAAU,MAAM,OAAO,sCAAsC,SAAS,SAAS,OAAO,SAAS,SAAS,OAAO,iBAAiB,UAAU,2BAA2B,uCAAuC,kCAAkC,MAAM,MAAM,UAAU,MAAM,gBAAgB,OAAO,QAAQ,YAAY,kBAAkB,uBAAuB,gBAAgB,cAAc,gBAAgB,WAAW,sBAAsB,qBAAqB,mBAAmB,eAAe,UAAU,cAAc,cAAc,yBAAyB,gCAAgC,qBAAqB,aAAa,yBAAyB,iBAAiB,sBAAsB,eAAe,YAAY,iBAAiB,gBAAgB,aAAa,eAAe,YAAY,mBAAmB,0BAA0B,yBAAyB,gBAAgB,kBAAkB,YAAY,mBAAmB,iCAAiC,4BAA4B,cAAc,2BAA2B,wBAAwB,qBAAqB,uBAAuB,2BAA2B,iCAAiC,gCAAgC,+BAA+B,uBAAuB,uBAAuB,mBAAmB,8BAA8B,4BAA4B,oBAAoB,sBAAsB,wBAAwB,6BAA6B,mBAAmB,wBAAwB,SAAS,cAAc,gBAAgB,gBAAgB,qBAAqB,SAAS,qBAAqB,WAAW,aAAa,gBAAgB,oBAAoB,iBAAiB,aAAa,UAAU,QAAQ,OAAO,QAAQ,MAAM,KAAK,OAAO,SAAS,SAAS,UAAU,QAAQ,MAAM,OAAO,QAAQ,OAAO,MAAM,KAAK,MAAM,OAAO,QAAQ,QAAQ,QAAQ,MAAM,MAAM,MAAM,UAAU,cAAc,kBAAkB,SAAS,cAAc,UAAU,OAAO,YAAY,QAAQ,SAAS,MAAM,WAAW,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,KAAK,QAAQ,iBAAiB,QAAQ,SAAS,SAAS,MAAM,WAAW,MAAM,QAAQ,QAAQ,OAAO,OAAO,QAAQ,QAAQ,QAAQ,aAAa,eAAe,UAAU,UAAU,OAAO,MAAM,MAAM,UAAU,QAAQ,eAAe,UAAU,MAAM,OAAO,QAAQ,MAAM,MAAM,MAAM,OAAO,OAAO,YAAY,aAAa,SAAS,UAAU,SAAS,UAAU,MAAM,MAAM,MAAM,WAAW,QAAQ,OAAO,QAAQ,SAAS,MAAM,MAAM,OAAO,WAAW,MAAM,QAAQ,YAAY,SAAS,UAAU,MAAM,WAAW,SAAS,OAAO,SAAS,cAAc,MAAM,SAAS,OAAO,QAAQ,OAAO,QAAQ,KAAK,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,QAAQ,kBAAkB,UAAU,QAAQ,WAAW,MAAM,cAAc,MAAM,SAAS,kBAAkB,OAAO,KAAK,OAAO,UAAU,MAAM,OAAO,SAAS,OAAO,KAAK,MAAM,OAAO,UAAU,WAAW,OAAO,cAAc,QAAQ,OAAO,cAAc,eAAe,OAAO,OAAO,MAAM,QAAQ,UAAU,UAAU,SAAS,ECc5qI,IAAMC,EAAW,KAMXC,EACX,uFAYK,IAAMC,EAAc,oCAMdC,EAAY,2BAMZC,EAAa,qBAQbC,EAAiB,yCAMjBC,EAAa,uBAMbC,EAAqB,gCAMrBC,EACX,wFAMWC,EAAc,iBAMdC,EAAc,qBAMdC,EAAU,UAMVC,EAAc,gCAMdC,EAAsB,yCCtGnC,IAAAC,EAAA,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,ECYzrB,IAAMC,EAAkB,IAAI,IAAIC,CAAS,EAMnCC,EAAyB,IAAI,IAAI,CAC5C,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,IAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,GAAM,EACZ,CAAC,IAAK,GAAM,CACd,CAAC,EAOKC,GAA2B,IAAM,CACrC,IAAMC,EAAY,IAAI,IACtB,QAASC,EAAI,EAAGA,EAAI,IAAYA,IAC1B,CAACL,EAAgB,IAAIK,CAAC,GAAK,CAACH,EAAuB,IAAIG,CAAC,GAC1DD,EAAU,IAAIC,EAAG,MAAMA,EAAE,SAAS,EAAG,EAAE,SAAS,EAAG,GAAG,EAAE,YAAY,CAAC,EAAE,EAG3E,OAAOD,CACT,EAMaE,EAAsBH,GAAyB,ECpC5D,GAAM,CAACI,EAASC,GAASC,GAAQC,GAAQC,GAAUC,EAAQ,EAAI,CAC7D,IACA,IACA,IACA,IACA,IACA,GACF,EAAE,IAAIC,GAAM,IAAIA,EAAG,WAAW,CAAC,EAAE,SAAS,EAAG,EAAE,YAAY,CAAC,EAAE,EACxDC,GAAa,CACjB,CAACP,CAAO,EAAG,GAAGA,CAAO,OACrB,CAACE,EAAM,EAAG,GAAGF,CAAO,MACpB,CAACG,EAAM,EAAG,GAAGH,CAAO,MACpB,CAACI,EAAQ,EAAG,GAAGJ,CAAO,QACtB,CAACK,EAAQ,EAAG,GAAGL,CAAO,GAAGC,EAAO,KAClC,EACMO,GAAY,MAAM,KACtB,CAAE,OAAQ,GAAW,EACrB,CAACC,EAAGC,IAAM,IAAIA,EAAE,SAAS,EAAG,EAAE,SAAS,EAAG,GAAG,EAAE,YAAY,CAAC,EAC9D,EACMC,GAAU,WAAW,SAAS,UAAU,OAAS,OACjDC,EAAmB,IAAI,CAAC,GAAGC,EAAoB,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,IACjEC,GAAe,IAAI,OAAOF,CAAgB,EAC1CG,GAAiB,IAAI,OAAOH,EAAkB,GAAG,EAGjDI,GAAU,IAAI,YACdC,GAAU,IAAI,YAAY,QAAS,CAAE,MAAO,EAAK,CAAC,EAK3CC,EAAN,KAAiB,CAEtBC,GAAW,IAAI,IAAIC,CAAU,EAS7B,KAAM,CACJ,MAAO,CAAC,GAAG,KAAKD,EAAQ,CAC1B,CAOA,UAAUE,EAAK,CACb,GAAI,CAACC,EAASD,CAAG,EACf,OAAO,KAET,IAAME,EAAS,KAAK,MAAMF,CAAG,EAC7B,OAAIE,EACKA,EAAO,SAAS,QAAQ,KAAM,EAAE,EAElC,IACT,CAOA,IAAIC,EAAQ,CACV,IAAMC,EAAa,KAAK,UAAUD,EAAQ,EAAI,EAC9C,OAAIC,EACK,KAAKN,GAAS,IAAIM,CAAU,EAE9B,EACT,CAQA,UAAUJ,EAAKK,EAAW,GAAO,CAC/B,GAAI,CAACJ,EAASD,CAAG,EACf,OAAO,KAET,IAAMI,EAAaJ,EAAI,UAAU,MAAM,EACvC,OAAIK,EACKD,EAAW,KAAK,EAAE,YAAY,EAEhCA,CACT,CAQA,MAAMJ,EAAKM,EAAM,CACf,GAAI,CAACL,EAASD,CAAG,EACf,OAAO,KAET,IAAMI,EAAa,KAAK,UAAUJ,CAAG,EACrC,OAAIM,GAAQL,EAASK,CAAI,EAChB,IAAI,MAAMF,EAAY,KAAK,UAAUE,CAAI,CAAC,EAE5C,IAAI,MAAMF,CAAU,CAC7B,CAQA,OAAOJ,EAAKO,EAAS,CACnB,GAAI,CAACN,EAASD,CAAG,EACf,MAAO,GAET,IAAMQ,EAAY,KAAK,MAAMR,CAAG,EAChC,OAAO,KAAK,aAAaQ,EAAWD,EAAS,EAAI,CACnD,CASA,aAAaC,EAAWD,EAASE,EAAc,GAAO,CACpD,GAAI,CAACD,GAAa,CAACA,EAAU,SAC3B,MAAO,GAET,IAAML,EAASK,EAAU,SAAS,YAAY,EAAE,QAAQ,KAAM,EAAE,EAC1DE,EAAQP,EAAO,MAAM,GAAG,EAE9B,OADiBO,EAAM,KAAKC,GAAKC,EAAW,KAAKD,CAAC,CAAC,EAE1C,IAEJJ,IACHA,EAAU,KAAKT,IAEbS,EAAQ,IAAIJ,CAAM,EACb,GAELO,EAAM,OAAS,EAEdD,GAAeI,EAAe,KAAKV,CAAM,GAC1CO,EAAM,MAAMC,GAAKJ,EAAQ,IAAII,CAAC,CAAC,EAG5B,GACT,CACF,EAOaG,EAAiBC,GACvBd,EAASc,CAAQ,EAGfA,EAAS,QAAQ,KAAM,EAAE,EAAE,MAAM,GAAG,EAFlC,CAAC,EA2BL,IAAMC,EAA4BC,GAAM,CAC7C,GAAI,CAACC,EAASD,CAAE,EACd,OAAOA,EAET,IAAME,EAASC,EAAY,KAAKH,CAAE,EAAIA,EAAG,YAAY,EAAIA,EACzD,OAAOI,GAAWF,CAAM,GAAKF,CAC/B,EAOaK,EAAcC,GAAO,CAChC,IAAMC,EAAMN,EAASK,CAAG,EAAIA,EAAM,OAAOA,CAAG,EAG5C,OADEC,EAAI,OAAS,GAAkB,GAAGA,EAAI,MAAM,EAAG,EAAe,CAAC,MAAQA,CAE3E,EAOaC,GAAgCF,GACtCL,EAASK,CAAG,EAGVA,EAAI,QAAQG,EAAU,EAAE,EAAE,QAAQC,EAAW,IAAI,EAF/CJ,EAWEK,GAAoB,CAACC,EAAOC,IAAU,CACjD,IAAMC,EAAM,MAAM,KAAKD,CAAK,EACxB,OAAO,SAASA,EAAM,MAAM,CAAC,EAAG,EAAG,EACnC,OAAO,SAASA,EAAO,EAAI,EAC/B,GAAI,CAAC,OAAO,UAAUC,CAAG,GAAKA,EAAM,GAAKA,GAAO,IAC9C,OAAOF,EAET,GAAIG,EAAoB,IAAID,CAAG,EAC7B,MAAO,GAET,GAAIE,EAAgB,IAAIF,CAAG,EACzB,OAAO,OAAO,aAAaA,CAAG,EAEhC,IAAMG,EAAYC,EAAuB,IAAIJ,CAAG,EAChD,OAAO,OAAO,cAAcG,CAAS,CACvC,EAQaE,GAA4B,CAACZ,EAAKa,EAAO,IAAM,CAC1D,GAAI,CAACnB,EAASM,CAAG,EACf,MAAM,IAAI,UAAU,2BAA2Bc,EAAQd,CAAG,CAAC,GAAG,EAEhE,GAAI,CAAC,OAAO,UAAUa,CAAI,EACxB,MAAM,IAAI,UAAU,2BAA2BC,EAAQD,CAAI,CAAC,GAAG,EAEjE,IAAIE,EACJ,GAAI,CACFA,EAAM,mBAAmBf,CAAG,CAC9B,MAAQ,CAENe,EAAMf,EAAI,QAAQgB,EAAaX,GAAS,CACtC,GAAI,CACF,OAAO,mBAAmBA,CAAK,CACjC,MAAQ,CACN,OAAOA,CACT,CACF,CAAC,CACH,CACAU,EAAMA,EAAI,QAAQE,GAAgB,EAAE,EACpC,IAAIC,EAAQ,EACZ,KAAOA,EAAQL,EAAO,GAAUK,IAAS,CACvC,IAAMC,EAAcJ,EAGpB,GADAA,EAAMA,EAAI,QAAQK,EAAS,GAAG,EAAE,QAAQC,EAAajB,EAAiB,EAClEW,IAAQI,EACV,KAEJ,CACA,GAAID,EAAQL,GAAQ,IAAY,KAAK,KAAKE,CAAG,EAC3C,MAAM,IAAI,MAAM,yCAAyC,EAE3D,OAAOA,CACT,EASaO,EAA2B,CAACC,EAAUC,EAAS,GAAIC,EAAO,KAAO,CAC5E,GAAI,CAAC/B,EAAS6B,CAAQ,EACpB,MAAO,CAAE,UAAW,GAAI,WAAY,CAAC,EAAG,KAAM,GAAI,SAAU,EAAM,EAEpE,IAAMG,EAAQH,EAAS,QAAQ,GAAG,EAClC,GAAIG,IAAU,GACZ,MAAO,CACL,UAAWH,EACX,WAAYA,EAAS,MAAM,GAAG,EAC9B,KAAM,GACN,SAAU,EACZ,EAEF,IAAMI,EAAYJ,EAAS,MAAM,EAAGG,CAAK,EACnCE,EAAO,GAAGL,EAAS,MAAMG,EAAQ,CAAC,CAAC,GAAGF,CAAM,GAAGC,CAAI,GACnDI,EAAaF,EAAU,MAAM,GAAG,EAChCG,EAAW,YAAY,KAAKD,EAAW,GAAG,EAAE,CAAC,EACnD,MAAO,CAAE,UAAAF,EAAW,WAAAE,EAAY,KAAAD,EAAM,SAAAE,CAAS,CACjD,EAOaC,GAAcH,GAAQ,CACjC,GAAI,CAAClC,EAASkC,CAAI,EAChB,MAAM,IAAI,UAAU,2BAA2Bd,EAAQc,CAAI,CAAC,GAAG,EAEjE,IAAMI,EAAYJ,EAAK,QAAQ,MAAO,EAAE,EACpCK,EACJ,GAAI,CACFA,EAAS,KAAKD,CAAS,CACzB,MAAQ,CACN,MAAM,IAAI,MAAM,wBAAwBlC,EAAY8B,CAAI,CAAC,EAAE,CAC7D,CACA,IAAIM,EACJ,GAAIC,IAAW,WAAW,OAGxBD,EAAQ,WAAW,OAAO,KAAKD,EAAQ,QAAQ,MAC1C,CACL,IAAMG,EAAMH,EAAO,OACnBC,EAAQ,IAAI,WAAWE,CAAG,EAC1B,QAASC,EAAI,EAAGA,EAAID,EAAKC,IACvBH,EAAMG,CAAC,EAAIJ,EAAO,WAAWI,CAAC,CAElC,CACA,GAAI,CACF,IAAMC,EAAOC,GAAQ,OAAOL,CAAK,EACjC,OAAIM,GAAa,KAAKF,CAAI,EACjBN,EAEFM,CACT,MAAQ,CACN,OAAON,CACT,CACF,EC3VA,IAAMS,GAAa,qBACbC,GAAU,WAAW,SAAS,UAAU,OAAS,OACjDC,GAAY,OAAO,OAAO,CAC9B,OACA,SACA,WACA,WACA,WACA,OACA,WACA,OACA,WACA,SACA,MACF,CAAC,EACKC,EAAe,OAAO,OAAO,CACjC,MAAO,OAAO,OAAO,CAAC,CAAC,EACvB,KAAM,OAAO,OAAO,CAAC,CAAC,EACtB,KAAM,OAAO,OAAO,CAAC,CAAC,EACtB,cAAe,GACf,MAAO,GACP,gBAAiB,GACjB,YAAa,SACb,UAAW,KACb,CAAC,EAuCYC,EAAW,CAACC,EAASC,IAAU,CAC1C,QAAQ,KAAK,wBAAwBD,CAAO,GAAIC,CAAK,CACvD,EAKaC,EAAN,KAAsB,CAK3B,YAAYC,EAAKC,EAAmB,CAClC,KAAK,MAAQ,CAAC,CAACD,GAAK,MACpB,KAAK,UAAYC,EACjB,KAAK,KAAO,EACZ,KAAK,QAAU,IAAI,IACnB,KAAK,UAAY,IAAI,IAAI,CACvB,CAAC,OAAQ,EAAK,EACd,CAAC,OAAQ,EAAK,EACd,CAAC,OAAQ,EAAK,EACd,CAAC,aAAc,EAAK,EACpB,CAAC,WAAY,EAAK,CACpB,CAAC,CACH,CAQA,MAAMC,EAAK,CACT,OAAI,KAAK,QAAQ,IAAIA,CAAG,GAClB,KAAK,OACPN,EAAS,2CAA2CO,EAAYD,CAAG,CAAC,EAAE,EAEjE,KAET,KAAK,OACL,KAAK,QAAQ,IAAIA,CAAG,EACb,GACT,CAOA,MAAMA,EAAK,CACT,KAAK,OACL,KAAK,QAAQ,OAAOA,CAAG,CACzB,CACF,EAKaE,EAAN,cAA2BC,CAAW,CAE3CC,GAEA,aAAc,CACZ,MAAM,EACN,KAAKA,GAAkB,IAAI,IAAI,MAAM,IAAI,CAAC,CAC5C,CASAC,GAAiBL,EAAKF,EAAK,CACzB,GAAI,CAACE,GAAO,CAACM,EAASN,CAAG,EACvB,OAAO,KAET,GAAM,CAAE,MAAAO,EAAO,cAAAC,EAAe,KAAAC,EAAM,UAAAC,EAAW,KAAAC,CAAK,EAAIb,EACxD,GAAI,OAAO,UAAUY,CAAS,GAAKV,EAAI,OAASU,EAAW,CACzD,IAAME,EAAM,cAAcZ,EAAI,MAAM,uBAAuBU,CAAS,IACpE,MAAM,IAAI,WAAWE,CAAG,CAC1B,CAMA,GACE,EALC,MAAM,QAAQH,CAAI,GAAKA,EAAK,QAC5B,MAAM,QAAQE,CAAI,GAAKA,EAAK,QAC7BH,KAICR,EAAI,WAAW,UAAU,GAAKA,EAAI,WAAW,SAAS,IACvD,CAACa,EAAa,KAAKb,CAAG,GACtB,CAACA,EAAI,SAAS,OAAO,EACrB,CACA,IAAMc,EAAS,KAAK,MAAMd,CAAG,EAC7B,OAAIc,EACKA,EAAO,KAAK,QAAQ,OAAQC,CAAyB,EAEvD,IACT,CACA,IAAMC,EAAM,IAAInB,EAAgBC,EAAKmB,CAAS,EAC9C,OAAO,KAAKC,GAASlB,EAAK,CAAE,MAAAO,EAAO,cAAAC,EAAe,KAAAC,EAAM,KAAAE,CAAK,EAAGK,CAAG,CACrE,CAcAE,GAASlB,EAAK,CAAE,MAAAO,EAAO,cAAAC,EAAe,KAAAC,EAAM,KAAAE,CAAK,EAAGK,EAAK,CACvD,GAAIA,EAAI,KAAO,GACb,MAAM,IAAI,MAAM,8BAA8B,EAGhD,GAAM,CAAE,eAAAG,EAAgB,eAAAC,EAAgB,UAAAC,CAAU,EAChD,KAAKC,GAAoB,CAAE,MAAAf,EAAO,KAAAE,EAAM,KAAAE,CAAK,EAAGK,CAAG,EAE/C,CACJ,UAAAO,EACA,WAAAC,EACA,WAAAC,EACA,OAAAC,EACA,YAAAC,GACA,OAAAb,GACA,cAAAc,EACF,EAAI,KAAKC,GAAmB7B,EAAKQ,EAAeW,EAAgBH,CAAG,EAKnE,MAJI,CAACS,GAKH,CAAC,KAAKK,GACJJ,EACAC,GACAP,EACAC,EACAG,CACF,EAEO,KAGLD,EACK,KAAKQ,GAAiBjB,GAAQY,EAAQV,CAAG,EAE3C,KAAKgB,GAAqBJ,EAAa,CAChD,CAYAN,GAAoB,CAAE,MAAAf,EAAO,KAAAE,EAAM,KAAAE,CAAK,EAAGK,EAAK,CAC9C,IAAIG,EAAiB,KAAKf,GACtBgB,EAAiB,GACfC,EAAY,IAAI,IAAIL,EAAI,SAAS,EACvC,GAAI,MAAM,QAAQL,CAAI,GAAKA,EAAK,OAAQ,CACtCQ,EAAiB,IAAI,IACrBC,EAAiB,GACjB,QAAWa,KAAQtB,EACbL,EAAS2B,CAAI,GACf,KAAKC,GAAgBD,EAAM,OAAQd,EAAgBE,EAAWL,CAAG,CAGvE,KAAO,CACL,GAAI,MAAM,QAAQT,CAAK,GAAKA,EAAM,OAAQ,CACxCY,EAAiB,IAAI,IAAI,KAAKf,EAAe,EAC7C,QAAW6B,KAAQ1B,EACbD,EAAS2B,CAAI,GACf,KAAKC,GAAgBD,EAAM,QAASd,EAAgBE,EAAWL,CAAG,CAGxE,CACA,GAAI,MAAM,QAAQP,CAAI,GAAKA,EAAK,QAC9B,QAAWwB,KAAQxB,EACjB,GAAIH,EAAS2B,CAAI,EAAG,CAClB,IAAME,EAAa,KAAK,UAAUF,EAAM,EAAI,EACxCE,GACFd,EAAU,IAAIc,EAAY,EAAK,CAEnC,EAGN,CACA,MAAO,CAAE,eAAAhB,EAAgB,eAAAC,EAAgB,UAAAC,CAAU,CACrD,CAYAa,GAAgBD,EAAMG,EAAUjB,EAAgBE,EAAWL,EAAK,CAC9D,IAAMqB,EAAmB,KAAK,UAAUJ,EAAM,EAAI,EAClD,OAAK,KAAKK,GAAeD,CAAgB,GAGzChB,EAAU,IAAIgB,EAAkB,EAAI,EACpClB,EAAe,IAAIkB,CAAgB,EAC5B,IAJE,EAKX,CAQAC,GAAeD,EAAkB,CAC/B,OAAIE,EAAmB,KAAKF,CAAgB,EACnC,GAIF,CAFaA,EAAiB,MAAM,GAAG,EACjB,KAAKG,GAAKC,EAAW,KAAKD,CAAC,CAAC,GACrCE,EAAW,KAAKL,CAAgB,CACtD,CAWAR,GAAmB7B,EAAKQ,EAAeW,EAAgBH,EAAK,CAC1D,IAAMF,EAAS,KAAK,MAAMd,CAAG,EACzByB,EAAa,KAAK,aAAaX,EAAQK,CAAc,EACrDK,EAAa,GACbmB,EAAe,GAEnB,GAAI,CAAClB,GAAcjB,GAAiB,CAACoC,EAAoB,KAAK5C,CAAG,EAAG,CAClE,IAAM6C,EAAW,KAAK,MAAM7C,EAAKV,EAAU,EACvCuD,GAEAA,EAAS,WAAa,SACtBA,EAAS,WAAa,gBAEtBpB,EAAa,GACbD,EAAa,GACbmB,EAAe,GAAGE,EAAS,QAAQ,GAAGA,EAAS,MAAM,GAAGA,EAAS,IAAI,GAG3E,CACA,GAAI,CAACpB,EACH,MAAO,CAAE,WAAY,EAAM,EAG7B,GAAID,EACF,MAAO,CACL,WAAAA,EACA,WAAAC,EACA,UAAW,GACX,OAAQ,GACR,YAAa,CAAC,EACd,cAAekB,CACjB,EAEF,IAAMjB,EAASZ,EAAO,SAAS,QAAQ,KAAM,EAAE,EACzCa,EAAcD,EAAO,MAAM,GAAG,EACpC,MAAO,CACL,WAAAF,EACA,WAAAC,EACA,OAAAC,EACA,YAAAC,EACA,OAAAb,EACA,UAAWY,IAAW,OACtB,cAAeZ,EAAO,IACxB,CACF,CAYAgB,GAAiBJ,EAAQC,EAAaP,EAAgBC,EAAWG,EAAY,CAC3E,GAAIA,EACF,MAAO,GAET,GAAIJ,EACF,OAAOC,EAAU,IAAIK,CAAM,GAAKC,EAAY,MAAMa,GAAKnB,EAAU,IAAImB,CAAC,CAAC,EAEzE,OAAW,CAACM,EAAKC,CAAK,IAAK1B,EAAU,QAAQ,EAG3C,GAAI,EADF0B,GAAUrB,IAAWoB,GAAOnB,EAAY,MAAMa,GAAKA,IAAMM,CAAG,GAE5D,MAAO,GAGX,MAAO,EACT,CAUAf,GAAiBjB,EAAQY,EAAQV,EAAK,CACpC,GAAM,CAAE,UAAAgC,EAAW,WAAAC,EAAY,KAAAC,EAAM,SAAAC,CAAS,EAAIC,EAChDtC,EAAO,SACPA,EAAO,OACPA,EAAO,IACT,EACIuC,EAAaH,EACjB,GAAIC,EACF,GAAI,CACFE,EAAaC,GAAYJ,CAAI,CAC/B,OAASK,EAAG,CACV,OAAIvC,EAAI,OACNtB,EAAS,+BAAgC6D,CAAC,EAErC,IACT,CAEF,GAAI,CACF,IAAMC,EAAcC,GAA0BJ,CAAU,EAAE,KAAK,EACzDK,EAAY,KAAK,MAAMF,EAAalE,EAAU,EACpD,GAAI,CAACoE,EACH,OAAO,KAET,IAAMC,EAAaD,EAAU,SAE7B,GADwBE,EAAeD,CAAU,EAC7B,KAAKnB,GAAKD,EAAmB,KAAKC,CAAC,CAAC,EACtD,OAAO,IAEX,OAASe,EAAG,CACV,OAAIvC,EAAI,OACNtB,EAAS,2CAA4C6D,CAAC,EAEjD,IACT,CAIA,OAHI,CAACP,GAAaa,EAAa,KAAKb,CAAS,KAC3CK,EAAa,KAAKS,GAAQT,EAAYrC,CAAG,GAEvCqC,GACEF,GAAYE,IAAeH,GAC7BD,EAAW,IAAI,EAEV,GAAGvB,CAAM,IAAIuB,EAAW,KAAK,GAAG,CAAC,IAAII,CAAU,IAEjD,IACT,CASAS,GAAQC,EAAK/C,EAAK,CAChB,IAAIgD,EAAaD,EACjB,GAAI,CACFC,EAAa,mBAAmBD,CAAG,CACrC,MAAQ,CAER,CACA,IAAIE,EACEC,EAAW,CAACC,EAAMC,IACtB,KAAKC,GAAyBF,EAAMC,EAAKpD,CAAG,EAC9CA,EAAI,UAAU,QAAQ,wBAAyBkD,CAAQ,EACvD,GAAI,CACFD,EAAcjD,EAAI,UAAU,SAASgD,CAAU,CACjD,QAAE,CACAhD,EAAI,UAAU,WAAW,uBAAuB,CAClD,CACAiD,EAAcK,GAA8BL,CAAW,EACvD,GAAI,CACF,OAAO,UAAUA,CAAW,CAC9B,MAAQ,CACN,OAAOA,CACT,CACF,CASAI,GAAyBF,EAAMC,EAAKpD,EAAK,CACvC,GAAI,CAACoD,EAAI,WAAa,CAAC,aAAa,KAAKA,EAAI,SAAS,EACpD,OAEF,IAAMtD,EAAS,KAAK,MAAMsD,EAAI,SAAS,EACvC,GAAI,CAACtD,GAAUA,EAAO,WAAa,QACjC,OAEF,IAAMyD,EAAcH,EAAI,UACxB,GAAI,CAACpD,EAAI,MAAMuD,CAAW,EAAG,CAC3BH,EAAI,UAAY,GAChB,MACF,CACA,GAAI,CACF,IAAMI,EAAY,KAAKtD,GACrBqD,EACA,CAAE,MAAO,CAAC,MAAM,EAAG,KAAM,CAAC,EAAG,KAAM,CAAC,EAAG,cAAe,EAAM,EAC5DvD,CACF,EACAoD,EAAI,UAAYI,GAAa,EAC/B,QAAE,CACAxD,EAAI,MAAMuD,CAAW,CACvB,CACF,CASAvC,GAAqBJ,EAAe,CAClC,IAAI4C,EAAY5C,EACV6C,EAAW,CAAC5D,EAAc6D,CAAW,EAC3C,QAAWC,KAAWF,EAAU,CAC9B,IAAMG,EAAQD,EAAQ,KAAKH,CAAS,EAChCI,IACFJ,EAAYA,EAAU,UAAU,EAAGI,EAAM,KAAK,EAAE,QAAQ,QAAS,EAAE,EAEvE,CACA,OAAOJ,CACT,CAeA,SAASxE,EAAKF,EAAM,CAAC,EAAG,CACtB,IAAM+E,EAAU,CACd,GAAGpF,EACH,GAAGK,CACL,EACA,OAAO,KAAKO,GAAiBL,EAAK6E,CAAO,CAC3C,CAOA,QAAQ7E,EAAK,CACX,IAAM8E,EAAe,CACnB,MAAO9E,EACP,MAAO,EACT,EACA,GAAI,CAACM,EAASN,CAAG,EACf,OAAA8E,EAAa,OAAS,sBAAsB7E,EAAYD,CAAG,CAAC,GACrD8E,EAET,GAAI9E,IAAQ,GACV,OAAA8E,EAAa,OAAS,oCACfA,EAET,IAAIC,EACAC,EAAgB,KACpB,GAAI,CACFD,EAAe,KAAK1E,GAAiBL,EAAK,CACxC,GAAGP,EACH,MAAO,CAAC,MAAM,EACd,cAAe,EACjB,CAAC,EACIsF,IACHC,EACE,6DAEN,OAASzB,EAAG,CACVyB,EAAgBzB,EAAE,OACpB,CACA,GAAIwB,EAAc,CAEhB,IAAMjE,EAAS,KAAK,MAAMiE,CAAY,EAEtC,GADAD,EAAa,MAAQ,GACjBhE,EAAQ,CAGV,GAFoB8C,EAAe9C,EAAO,QAAQ,EACpB,SAAS,MAAM,EAC9B,CACb,GAAM,CAAE,WAAAmC,EAAY,KAAAC,EAAM,SAAAC,CAAS,EAAIC,EACrCtC,EAAO,SACPA,EAAO,OACPA,EAAO,IACT,EACIqC,GACFF,EAAW,IAAI,EAEjB6B,EAAa,KAAO,CAClB,KAAM7B,EAAW,KAAK,GAAG,EACzB,OAAQE,EACR,KAAAD,CACF,CACF,MACE4B,EAAa,KAAO,KAEtB,QAAWhC,KAAOtD,GAAW,CAC3B,IAAMuD,EAAQjC,EAAOgC,CAAG,EACpBxC,EAASyC,CAAK,IAChB+B,EAAahC,CAAG,EAAIC,EAExB,CACF,MAEE+B,EAAa,MAAQ,GACrBA,EAAa,SAAW,GACxBA,EAAa,KAAOC,EACpBD,EAAa,OACX,0DAEN,MACEA,EAAa,MAAQ,GACrBA,EAAa,OAASE,EAExB,OAAOF,CACT,CAMA,KAAM,CACJ,MAAO,CAAC,GAAG,KAAK1E,EAAe,CACjC,CAOA,IAAIsB,EAAQ,CACV,IAAMW,EAAmB,KAAK,UAAUX,EAAQ,EAAI,EACpD,OAAIW,EACK,KAAKjC,GAAgB,IAAIiC,CAAgB,EAE3C,EACT,CAOA,IAAIX,EAAQ,CACV,GAAI,CAACpB,EAASoB,CAAM,EAClB,MAAM,IAAI,UAAU,2BAA2BuD,EAAQvD,CAAM,CAAC,GAAG,EAEnE,IAAMW,EAAmB,KAAK,UAAUX,EAAQ,EAAI,EACpD,GAAI,CAAC,KAAKY,GAAeD,CAAgB,EACvC,MAAM,IAAI,MAAM,mBAAmBX,CAAM,EAAE,EAE7C,YAAKtB,GAAgB,IAAIiC,CAAgB,EAClC,CAAC,GAAG,KAAKjC,EAAe,CACjC,CAOA,OAAOsB,EAAQ,CACb,GAAI,CAACpB,EAASoB,CAAM,EAClB,MAAO,GAET,IAAMW,EAAmB,KAAK,UAAUX,EAAQ,EAAI,EACpD,OAAO,KAAKtB,GAAgB,OAAOiC,CAAgB,CACrD,CAMA,OAAQ,CACN,KAAKjC,GAAkB,IAAI,IAAI,MAAM,IAAI,CAAC,CAC5C,CAQA,OAAO8E,EAAKC,EAAS,CACnB,OAAO,MAAM,OAAOD,EAAKC,GAAW,KAAK/E,EAAe,CAC1D,CACF,EASMgF,GAAoB,MAAMC,GAAQ,CACtC,IAAMC,EAAUD,EAAK,KAAO,GAAGA,EAAK,IAAI,UAAY,SAC9CE,EAAS,MAAMF,EAAK,YAAY,EAChCG,EAAS,WAAW,OAAO,KAAKD,CAAM,EAAE,SAAS,QAAQ,EAC/D,MAAO,QAAQD,CAAO,IAAIE,CAAM,EAClC,EAQMC,GAAwBJ,GAC5B,IAAI,QAAQ,CAACK,EAASC,IAAW,CAC/B,IAAMC,EAAS,IAAI,WAAW,WAC9BA,EAAO,iBAAiB,QAAS,IAAM,CACrC,IAAMhG,EACJgG,EAAO,OACP,IAAI,aACF,sCACA,kBACF,EACFD,EAAO/F,CAAK,CACd,CAAC,EACDgG,EAAO,iBAAiB,QAAS,IAAMF,EAAQ,IAAI,CAAC,EACpDE,EAAO,iBAAiB,OAAQ,IAAMF,EAAQE,EAAO,MAAM,CAAC,EAC5DA,EAAO,cAAcP,CAAI,CAC3B,CAAC,EAQGQ,GAAkB,MAAMR,GAAQ,CACpC,IAAMC,EAAUD,EAAK,KAAO,GAAGA,EAAK,IAAI,UAAY,SAC9CE,EAAS,MAAMF,EAAK,YAAY,EAChCS,EAAW,IAAI,WAAWP,CAAM,EAChCQ,EAAS,CAAC,EAChB,QAASC,EAAI,EAAGA,EAAIF,EAAS,OAAQE,GAAK,KACxCD,EAAO,KAAK,OAAO,aAAa,GAAGD,EAAS,SAASE,EAAGA,EAAI,IAAU,CAAC,CAAC,EAE1E,MAAO,QAAQV,CAAO,IAAI,KAAKS,EAAO,KAAK,EAAE,CAAC,CAAC,EACjD,EAWME,GAAqB,MACzBL,EACAM,EACAH,EACAI,EAAkB,IACf,CACH,GAAM,CAAE,KAAAC,EAAM,MAAArD,CAAM,EAAI,MAAM6C,EAAO,KAAK,EAC1C,GAAIQ,EACF,OAEF,IAAMC,EAAUF,EAAkBpD,EAAM,WACxC,GAAIsD,EAAUH,EAAS,CACrB,MAAMN,EAAO,OAAO,qBAAqB,EACzC,IAAMhF,EAAM,YAAYyF,CAAO,wBAAwBH,CAAO,WAC9D,MAAM,IAAI,aAAatF,EAAK,kBAAkB,CAChD,CACA,OAAAmF,EAAO,KAAKhD,CAAK,EACVkD,GAAmBL,EAAQM,EAASH,EAAQM,CAAO,CAC5D,EAQMC,GAAqB,MAAOtG,EAAKuG,IAAgB,CACrD,IAAIL,EAAU,SACV,OAAO,UAAUK,CAAW,GAAKA,EAAc,IACjDL,EAAUK,GAEZ,IAAMC,EAAW,MAAM,MAAMxG,CAAG,EAChC,GAAI,CAACwG,EAAS,GAAI,CAEhB,IAAI5F,EAAM,mBADWX,EAAYD,CAAG,CACK,GACzC,MAAI,OAAO,UAAUwG,EAAS,MAAM,IAC9BA,EAAS,WACX5F,GAAO,KAAK4F,EAAS,MAAM,IAAIA,EAAS,UAAU,GAElD5F,GAAO,KAAK4F,EAAS,MAAM,IAGzB,IAAI,MAAM5F,CAAG,CACrB,CAEA,IAAM6F,EAAgBD,EAAS,QAAQ,IAAI,gBAAgB,EAC3D,GAAIC,EAAe,CACjB,IAAMC,EAAe,OAAO,SAASD,EAAe,EAAI,EACxD,GAAI,OAAO,UAAUC,CAAY,GAAKA,EAAeR,EAAS,CAC5D,IAAMtF,EAAM,YAAY8F,CAAY,wBAAwBR,CAAO,WACnE,MAAM,IAAI,aAAatF,EAAK,kBAAkB,CAChD,CACF,CACA,IAAIyE,EAEJ,GAAImB,EAAS,KAAM,CACjB,IAAMZ,EAASY,EAAS,KAAK,UAAU,EACjCT,EAAS,CAAC,EAChB,GAAI,CACF,MAAME,GAAmBL,EAAQM,EAASH,EAAQ,CAAC,CACrD,QAAE,CACAH,EAAO,YAAY,CACrB,CACA,IAAMe,EAAOH,EAAS,QAAQ,IAAI,cAAc,EAC5CG,EACFtB,EAAO,IAAI,KAAKU,EAAQ,CAAE,KAAAY,CAAK,CAAC,EAEhCtB,EAAO,IAAI,KAAKU,CAAM,CAE1B,SACEV,EAAO,MAAMmB,EAAS,KAAK,EACvBnB,EAAK,KAAOa,EAAS,CACvB,IAAMtF,EAAM,YAAYyE,EAAK,IAAI,wBAAwBa,CAAO,WAChE,MAAM,IAAI,aAAatF,EAAK,kBAAkB,CAChD,CAEF,OAAIrB,IAAW,WAAW,OACjB6F,GAAkBC,CAAI,EACpB,WAAW,WACbI,GAAsBJ,CAAI,EAE5BQ,GAAgBR,CAAI,CAC7B,EAGMuB,EAAe,IAAI1G,EAkBZ2G,GAAc,MAAO7G,EAAKF,EAAM,CAAC,IAAM,CAClD,GAAI,CAACE,GAAO,CAACM,EAASN,CAAG,EACvB,OAAO,KAET,IAAM6E,EAAU,CACd,GAAGpF,EACH,GAAGK,CACL,EACM4B,EAASkF,EAAa,UAAU5G,CAAG,EACzC,GAAI0B,IAAW,KACb,OAAImD,EAAQ,cACH+B,EAAa,SAAS5G,EAAK6E,CAAO,EAEpC,KAET,GAAInD,IAAW,OAAQ,CACrB,GAAM,CAAE,MAAAnB,EAAO,KAAAE,EAAM,KAAAE,CAAK,EAAIkE,EAC1BiC,EAAM,KACV,GACGvG,EAAM,SAAS,MAAM,GAAK,CAACE,EAAK,SAAS,MAAM,GAChDE,EAAK,SAAS,MAAM,EACpB,CACA,IAAIuC,EACJ,GAAI,CACFA,EAAO,MAAMoD,GAAmBtG,EAAK6E,EAAQ,WAAW,CAC1D,OAAStB,EAAG,CACNsB,EAAQ,OACVnF,EACE,yCAAyCO,EAAYD,CAAG,CAAC,GACzDuD,CACF,CAEJ,CACIL,IACEvC,EAAK,OACFA,EAAK,SAAS,MAAM,IACvBkE,EAAQ,KAAO,CAAC,GAAGlE,EAAM,MAAM,IAG5BJ,EAAM,SAAS,MAAM,IACxBsE,EAAQ,MAAQ,CAAC,GAAGtE,EAAO,MAAM,GAEnCsE,EAAQ,KAAOA,EAAQ,KAAK,OAAOrC,GAAKA,IAAM,MAAM,GAEtDsE,EAAMF,EAAa,SAAS1D,EAAM2B,CAAO,EAE7C,CACA,OAAIA,EAAQ,iBACV,IAAI,gBAAgB7E,CAAG,EAElB8G,CACT,CACA,OAAOF,EAAa,SAAS5G,EAAK6E,CAAO,CAC3C,EAiBakC,GAAkB,CAAC/G,EAAKF,EAAM,CAAC,IAAM,CAChD,GAAI,CAACE,GAAO,CAACM,EAASN,CAAG,EACvB,OAAO,KAET,IAAM6E,EAAU,CACd,GAAGpF,EACH,GAAGK,CACL,EACM4B,EAASkF,EAAa,UAAU5G,CAAG,EACzC,OAAI0B,IAAW,KACTmD,EAAQ,cACH+B,EAAa,SAAS5G,EAAK6E,CAAO,EAEpC,KAELnD,IAAW,QACTmD,EAAQ,iBACV,IAAI,gBAAgB7E,CAAG,EAElB,MAEF4G,EAAa,SAAS5G,EAAK6E,CAAO,CAC3C,EAQamC,GAAa,MAAMhH,GAAO,CACrC,GAAIM,EAASN,CAAG,EAAG,CACjB,IAAM0D,EAAYkD,EAAa,MAAM5G,CAAG,EACxC,GAAI0D,GAAW,WAAa,QAC1B,GAAI,CACF,IAAMuD,EAAU,MAAMX,GAAmB5C,EAAU,IAAI,EACjDwD,EAAqBN,EAAa,QAAQK,CAAO,EACvD,OAAAC,EAAmB,MAAQlH,EACpBkH,CACT,OAAS,EAAG,CACV,MAAO,CAAE,MAAOlH,EAAK,MAAO,GAAO,OAAQ,EAAE,OAAQ,CACvD,CAEJ,CACA,OAAO4G,EAAa,QAAQ5G,CAAG,CACjC,EAOamH,GAAajC,GAAO0B,EAAa,OAAO1B,CAAG,EAO3CkC,GAAYlC,GAAOiC,GAAWjC,CAAG,EAGvCmC,GAAQT",
|
|
6
|
-
"names": ["PROPERTIES", "hookQueue", "domPurify", "target", "prop", "receiver", "purifier", "p", "args", "value", "
|
|
3
|
+
"sources": ["../bundle_wo_dompurify/mjs/constant.js", "../bundle_wo_dompurify/mjs/common.js", "../bundle_wo_dompurify/mjs/dompurify.js", "../bundle_wo_dompurify/mjs/regexp.js", "../bundle_wo_dompurify/lib/file/text-chars.json", "../bundle_wo_dompurify/mjs/text-chars.js", "../bundle_wo_dompurify/mjs/utility.js", "../bundle_wo_dompurify/mjs/filter.js", "../bundle_wo_dompurify/lib/iana/uri-schemes.json", "../bundle_wo_dompurify/mjs/scheme.js", "../bundle_wo_dompurify/mjs/sanitize.js", "../bundle_wo_dompurify/index.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * constant.js\n */\n\n/**\n * The total number of 8-bit byte values (0x00\u20130xFF).\n * @type {number}\n */\nexport const BYTE_RANGE = 256;\n\n/**\n * Chunk size (8KB) used when processing byte arrays to avoid stack overflow.\n * @type {number}\n */\nexport const CHUNK_SIZE = 8 * 1024;\n\n/**\n * Decimal radix used for numeric string conversion.\n * @type {number}\n */\nexport const DECI = 10;\n\n/**\n * Hexadecimal radix used for numeric string conversion.\n * @type {number}\n */\nexport const HEX = 16;\n\n/**\n * Maximum allowed Blob size (16MB).\n * @type {number}\n */\nexport const MAX_BLOB_SIZE = 16 * 1024 * 1024;\n\n/**\n * Maximum recursion depth allowed for nested parsing.\n * @type {number}\n */\nexport const MAX_NEST = 16;\n\n/**\n * Maximum URL length (64KB).\n * @type {number}\n */\nexport const MAX_URL_LENGTH = 64 * 1024;\n\n/**\n * Length used for truncating string.\n * @type {number}\n */\nexport const TRUNCATE_LENGTH = 64;\n\n/**\n * Start index used to extract the type name from Object.prototype.toString().\n * (e.g. removes \"[object \" prefix)\n * @type {number}\n */\nexport const TYPE_FROM = 8;\n\n/**\n * End index used to extract the type name from Object.prototype.toString().\n * (e.g. removes trailing \"]\")\n * @type {number}\n */\nexport const TYPE_TO = -1;\n\n/**\n * The dummy base URL.\n * @type {string}\n */\nexport const DUMMY_BASE = 'http://dummy.local';\n", "/**\n * common.js\n */\n\n/* shared */\nimport { TYPE_FROM, TYPE_TO } from './constant.js';\n\n/**\n * Gets the type of the given object.\n * Returns the object class name (e.g., \"String\", \"Array\", \"Null\", \"Undefined\").\n * @param {unknown} o - The object to check.\n * @returns {string} The specific type of the object.\n */\nexport const getType = o =>\n Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);\n\n/**\n * Checks if the given value is a string.\n * @param {unknown} o - The value to check.\n * @returns {boolean} True if the value is a string, false otherwise.\n */\nexport const isString = o => typeof o === 'string' || o instanceof String;\n\n/**\n * Debug logger.\n * @param {string} message - The message to output.\n * @param {Error} [error] - The caught error object, if any.\n * @returns {void}\n */\nexport const logDebug = (message, error) => {\n console.warn(`[URLSanitizer Debug] ${message}`, error);\n};\n", "/**\n * dompurify.js (for wo-dompurify build)\n */\n\n/* constants */\nconst PROPERTIES = Object.freeze([\n 'addHook',\n 'removeHook',\n 'removeAllHooks',\n 'setConfig'\n]);\n\nconst hookQueue = [];\nexport const domPurify = new Proxy({}, {\n get(target, prop, receiver) {\n const purifier = globalThis.DOMPurify || globalThis.window?.DOMPurify;\n if (purifier) {\n if (hookQueue.length) {\n for (const { prop: p, args } of hookQueue) {\n if (typeof purifier[p] === 'function') {\n purifier[p](...args);\n }\n }\n hookQueue.length = 0;\n }\n const value = purifier[prop];\n return typeof value === 'function' ? value.bind(purifier) : value;\n }\n return (...args) => {\n if (PROPERTIES.includes(prop)) {\n hookQueue.push({ prop, args });\n return receiver;\n }\n throw new Error('DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).');\n };\n }\n});\n", "/**\n * regexp.js\n */\n\n/**\n * Matches HTML-encoded ampersands.\n * @type {RegExp}\n */\nexport const REG_AMP = /&/gi;\n\n/**\n * Matches ampersand that may begin an HTML entity reference.\n * @type {RegExp}\n */\nexport const REG_AMP_ENC = /&(?=[#a-z0-9])(?![a-z0-9]+=)/i;\n\n/**\n * Matches and captures the charset parameter in a media type string.\n * @type {RegExp}\n */\nexport const REG_CHARSET = /^charset\\s*=\\s*([\"']?)(?<name>[\\w:-]+)\\1$/;\n\n/**\n * Matches a trailing empty fragment identifier (#).\n * @type {RegExp}\n */\nexport const REG_HASH = /#$/;\n\n/**\n * Matches MIME types that contain DOM-parsable content (HTML, XML, SVG).\n * @type {RegExp}\n */\nexport const REG_MIME_DOM =\n /^(?:application\\/(?:[\\w.-]+\\+)?xml|image\\/svg\\+xml|text\\/(?:html|xml))(?:\\s*;\\s*|$)/i;\n\n/**\n * Matches general text MIME types.\n * @type {RegExp}\n */\nexport const REG_MIME_TEXT = /^text\\/[\\w#&.;+-]+/i;\n\n/**\n * Matches non-ASCII character.\n * @type {RegExp}\n */\nexport const REG_NON_ASCII = /\\P{ASCII}/u;\n\n/**\n * Matches numeric character references (hex or decimal).\n * @type {RegExp}\n */\nexport const REG_NUM_REF = /&#(x0*[\\dA-F]{1,2}|0*\\d{1,3});?/gi;\n\n/**\n * Matches one or more consecutive percent-encoded sequences (%20, %E3%81%82).\n * @type {RegExp}\n */\nexport const REG_PCT_ENC = /(?:%[\\dA-F]{2})+/gi;\n\n/**\n * Matches a trailing empty query string (?), ensuring it is not part of a hash.\n * @type {RegExp}\n */\nexport const REG_QUERY = /^((?:[^#%]|%(?!23))*)\\?$/;\n\n/**\n * Validates a string against standard IANA URI scheme syntax.\n * @type {RegExp}\n */\nexport const REG_SCHEME = /^[a-z][\\da-z+.-]*$/;\n\n/**\n * Matches web custom schemes (`web+`) or application-specific schemes (`ext+`).\n * - `web+` schemes comply with the HTML Standard.\n * - `ext+` schemes comply with RFC 3986 but disallow `+` in the suffix.\n * @type {RegExp}\n */\nexport const REG_SCHEME_EXT = /^(?:web\\+[a-z]+|ext\\+[a-z][\\da-z.-]*)$/;\n\n/**\n * Matches executable script schemes (javascript or vbscript).\n * @type {RegExp}\n */\nexport const REG_SCRIPT = /^(?:java|vb)script$/i;\n\n/**\n * Matches script-executing or data-fetching schemes.\n * @type {RegExp}\n */\nexport const REG_SCRIPT_OR_BLOB = /^(?:(?:java|vb)script|blob)$/i;\n\n/**\n * Matches HTML-sensitive characters and their encoded variants.\n * @type {RegExp}\n */\nexport const REG_TAG_QUOT =\n /[\"'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i;\n\n/**\n * Matches a percent-encoded character (%XX).\n * @type {RegExp}\n */\nexport const REG_URL_ENC = /^%[\\dA-F]{2}$/i;\n\n/**\n * Matches invalid relative URL patterns.\n * @type {RegExp}\n */\nexport const REG_VERIFY_RELATIVE = /^(?:\\/{2,}|\\\\|[a-z][a-z\\d+\\-.]*:[^/])/i;\n", "[9,10,13,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]\n", "/**\n * text-chars.js\n */\n\n/* shared */\nimport textChars from '../lib/file/text-chars.json' with { type: 'json' };\nimport { BYTE_RANGE, HEX } from './constant.js';\n\n/**\n * A set of valid text character codes.\n * @type {Set<number>}\n */\nexport const TEXT_CHAR_CODES = new Set(textChars);\n\n/**\n * A map of Windows-1252 codes and their corresponding Unicode code points.\n * @type {Map<number, number>}\n */\nexport const WINDOWS1252_TO_UNICODE = new Map([\n [128, 0x20ac],\n [130, 0x201a],\n [131, 0x0192],\n [132, 0x201e],\n [133, 0x2026],\n [134, 0x2020],\n [135, 0x2021],\n [136, 0x02c6],\n [137, 0x2030],\n [138, 0x0160],\n [139, 0x2039],\n [140, 0x0152],\n [142, 0x017d],\n [145, 0x2018],\n [146, 0x2019],\n [147, 0x201c],\n [148, 0x201d],\n [149, 0x2022],\n [150, 0x2013],\n [151, 0x2014],\n [152, 0x02dc],\n [153, 0x2122],\n [154, 0x0161],\n [155, 0x203a],\n [156, 0x0153],\n [158, 0x017e],\n [159, 0x0178]\n]);\n\n/**\n * Generates hexadecimal escape strings for non-text codes.\n * @private\n * @returns {Map<number, string>} The map of non-text codes and hexadicimal escape strings.\n */\nconst generateNonTextCharCodes = () => {\n const charCodes = new Map();\n for (let i = 0; i < BYTE_RANGE; i++) {\n if (!TEXT_CHAR_CODES.has(i) && !WINDOWS1252_TO_UNICODE.has(i)) {\n charCodes.set(i, `\\\\x${i.toString(HEX).padStart(2, '0').toUpperCase()}`);\n }\n }\n return charCodes;\n};\n\n/**\n * A map of non-text codes and their hexadecimal escape strings.\n * @type {Map<number, string>}\n */\nexport const NON_TEXT_CHAR_CODES = generateNonTextCharCodes();\n", "/**\n * utility.js\n */\n\n/* shared */\nimport { getType, isString } from './common.js';\n\n/* constants */\nimport {\n BYTE_RANGE,\n CHUNK_SIZE,\n DECI,\n HEX,\n MAX_NEST,\n MAX_URL_LENGTH,\n TRUNCATE_LENGTH\n} from './constant.js';\nimport {\n REG_AMP,\n REG_HASH,\n REG_NON_ASCII,\n REG_NUM_REF,\n REG_PCT_ENC,\n REG_QUERY,\n REG_URL_ENC\n} from './regexp.js';\nimport {\n NON_TEXT_CHAR_CODES,\n TEXT_CHAR_CODES,\n WINDOWS1252_TO_UNICODE\n} from './text-chars.js';\nconst [ENC_AMP, ENC_NUM, ENC_LT, ENC_GT, ENC_QUOT, ENC_APOS] = [\n '&',\n '#',\n '<',\n '>',\n '\"',\n \"'\"\n].map(ch => `%${ch.charCodeAt(0).toString(HEX).toUpperCase()}`);\nconst ESCAPE_MAP = {\n [ENC_AMP]: `${ENC_AMP}amp;`,\n [ENC_LT]: `${ENC_AMP}lt;`,\n [ENC_GT]: `${ENC_AMP}gt;`,\n [ENC_QUOT]: `${ENC_AMP}quot;`,\n [ENC_APOS]: `${ENC_AMP}${ENC_NUM}39;`\n};\nconst HEX_TABLE = Array.from(\n { length: BYTE_RANGE },\n (_, i) => `%${i.toString(HEX).padStart(2, '0').toUpperCase()}`\n);\nconst IS_NODE = globalThis.process?.versions?.node !== undefined;\nconst NON_TEXT_PATTERN = `[${[...NON_TEXT_CHAR_CODES.values()].join('')}]`;\nconst REG_NON_TEXT = new RegExp(NON_TEXT_PATTERN);\nconst REG_NON_TEXT_G = new RegExp(NON_TEXT_PATTERN, 'g');\nconst SHARED_BUFFER = new Uint8Array(MAX_URL_LENGTH);\n\n/* typedef */\n/**\n * The extracted Data URL components.\n * @typedef {object} DataURLComponents\n * @property {string} data - The actual data part of the Data URL.\n * @property {boolean} isBase64 - Indicates whether the data is base64-encoded.\n * @property {string} mediaType - The MIME type of the data.\n * @property {string[]} mediaTypes - The array of parsed media types.\n */\n\n/* encoder / decoder */\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder('utf-8', { fatal: true });\n\n/**\n * Normalizes the URL string using NFKC.\n * @param {string} url - The URL string to normalize.\n * @param {boolean} [isScheme] - True if the given `url` is a scheme.\n * @returns {string|null} The normalized URL string, or null.\n */\nexport const normalizeURL = (url, isScheme = false) => {\n if (!isString(url)) {\n return null;\n }\n const normalized = REG_NON_ASCII.test(url) ? url.normalize('NFKC') : url;\n if (isScheme) {\n return normalized.trim().toLowerCase().replace(/:$/, '');\n }\n return normalized;\n};\n\n/**\n * Parses a URL string with a fallback.\n * @param {string} url - The URL string to parse.\n * @param {string} [base] - The base URL string.\n * @param {boolean} [normalize] - Indicates whether to normalize the URI.\n * @returns {URL|null} The parsed URL object, or null.\n */\nexport const parseURL = (url, base, normalize = false) => {\n if (!isString(url) || url === '') {\n return null;\n }\n let urlStr = url;\n let baseStr = base && isString(base) ? base : '';\n if (normalize) {\n urlStr = normalizeURL(urlStr);\n if (baseStr) {\n baseStr = normalizeURL(baseStr);\n }\n }\n if (typeof URL.parse === 'function') {\n return baseStr ? URL.parse(urlStr, baseStr) : URL.parse(urlStr);\n }\n try {\n return baseStr ? new URL(urlStr, baseStr) : new URL(urlStr);\n } catch {\n return null;\n }\n};\n\n/**\n * Extracts an array of schemes.\n * @param {string} protocol - The URL protocol (e.g., \"data:\", \"git+https:\").\n * @returns {string[]} An array of scheme parts.\n */\nexport const getSchemeParts = protocol => {\n if (!isString(protocol)) {\n return [];\n }\n return protocol.replace(/:$/, '').split('+');\n};\n\n/**\n * Gets the URL-encoded representation of a given string.\n * @param {string} str - The target string.\n * @returns {string} The URL-encoded string.\n */\nexport const getURLEncodedString = str => {\n if (!isString(str)) {\n throw new TypeError(`Expected String but got ${getType(str)}.`);\n }\n const bytes = encoder.encode(str);\n const encoded = new Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) {\n encoded[i] = HEX_TABLE[bytes[i]];\n }\n return encoded.join('');\n};\n\n/**\n * Escapes URL-encoded HTML special characters.\n * @param {string} ch - A URL-encoded (percent-encoded) character.\n * @returns {string} The escaped HTML special character, or the given character.\n */\nexport const escapeURLEncodedHTMLChars = ch => {\n if (!isString(ch)) {\n return ch;\n }\n const target = REG_URL_ENC.test(ch) ? ch.toUpperCase() : ch;\n return ESCAPE_MAP[target] ?? ch;\n};\n\n/**\n * Truncates the URL string.\n * @param {string|unknown} url - A URL string.\n * @returns {string} The truncated string.\n */\nexport const truncateURL = url => {\n const str = isString(url) ? url : String(url);\n const truncated =\n str.length > TRUNCATE_LENGTH ? `${str.slice(0, TRUNCATE_LENGTH)}...` : str;\n return truncated;\n};\n\n/**\n * Removes a trailing empty hash and an empty query string from a URL.\n * @param {string} url - The target URL string to be cleaned.\n * @returns {string} The cleaned URL string or the original input.\n */\nexport const trimTrailingEmptyQueryAndHash = url => {\n if (!isString(url)) {\n return url;\n }\n return url.replace(REG_HASH, '').replace(REG_QUERY, '$1');\n};\n\n/**\n * Replaces URL-encoded numeric character references.\n * @param {string} match - The matched substring.\n * @param {string} value - The captured numeric value.\n * @returns {string} The resolved character or the original match.\n */\nexport const replaceNumCharRef = (match, value) => {\n const num = /^x/i.test(value)\n ? Number.parseInt(value.slice(1), HEX)\n : Number.parseInt(value, DECI);\n if (!Number.isInteger(num) || num < 0 || num >= BYTE_RANGE) {\n return match;\n }\n if (NON_TEXT_CHAR_CODES.has(num)) {\n return '';\n }\n if (TEXT_CHAR_CODES.has(num)) {\n return String.fromCharCode(num);\n }\n const codePoint = WINDOWS1252_TO_UNICODE.get(num);\n return String.fromCodePoint(codePoint);\n};\n\n/**\n * Parses URL-encoded numeric character references.\n * @param {string} str - The target string to parse.\n * @param {number} [nest] - The current nesting depth for recursive parsing.\n * @returns {string} The decoded and parsed string.\n */\nexport const parseURLEncodedNumCharRef = (str, nest = 0) => {\n if (!isString(str)) {\n throw new TypeError(`Expected String but got ${getType(str)}.`);\n }\n if (!Number.isInteger(nest)) {\n throw new TypeError(`Expected Number but got ${getType(nest)}.`);\n }\n let res;\n try {\n res = decodeURIComponent(str);\n } catch {\n // Fallback: decode only valid multi-byte %XX sequences.\n res = str.replace(REG_PCT_ENC, match => {\n try {\n return decodeURIComponent(match);\n } catch {\n return match;\n }\n });\n }\n res = res.replace(REG_NON_TEXT_G, '');\n let depth = 0;\n for (; depth + nest < MAX_NEST; depth++) {\n const previousRes = res;\n // Decode '&' before decoding numeric references.\n res = res.replace(REG_AMP, '&').replace(REG_NUM_REF, replaceNumCharRef);\n if (res === previousRes) {\n break;\n }\n }\n if (depth + nest >= MAX_NEST && /&#/.test(res)) {\n throw new Error('Character references nested too deeply.');\n }\n return res;\n};\n\n/**\n * Extracts the components of a parsed Data URL.\n * @param {string} pathname - The pathname of the URL.\n * @param {string} [search] - The search (query) string of the URL.\n * @param {string} [hash] - The hash (fragment) of the URL.\n * @returns {DataURLComponents} The DataURLComponents object.\n */\nexport const extractDataURLComponents = (pathname, search = '', hash = '') => {\n if (!isString(pathname)) {\n return { mediaType: '', mediaTypes: [], data: '', isBase64: false };\n }\n const comma = pathname.indexOf(',');\n if (comma === -1) {\n return {\n mediaType: pathname,\n mediaTypes: pathname.split(';'),\n data: '',\n isBase64: false\n };\n }\n const mediaType = pathname.substring(0, comma);\n const dataPart = pathname.substring(comma + 1);\n const data =\n search === '' && hash === '' ? dataPart : dataPart + search + hash;\n const mediaTypes = mediaType.split(';');\n const lastType = mediaTypes.at(-1);\n const isBase64 =\n lastType !== undefined &&\n lastType.length === 6 &&\n lastType.toLowerCase() === 'base64';\n\n return { mediaType, mediaTypes, data, isBase64 };\n};\n\n/**\n * Parses base64-encoded data.\n * @param {string} data - The base64-encoded string.\n * @returns {string} The parsed text, or the original base64 if binary.\n */\nexport const parseBase64 = data => {\n if (!isString(data)) {\n throw new TypeError(`Expected String but got ${getType(data)}.`);\n }\n let binStr;\n let cleanData = data;\n try {\n binStr = atob(data);\n } catch {\n cleanData = data.replace(/\\s/g, '');\n try {\n binStr = atob(cleanData);\n } catch {\n throw new Error(`Invalid base64 data: ${truncateURL(data)}`);\n }\n }\n let bytes;\n if (IS_NODE && globalThis.Buffer) {\n // Use 'latin1' to correctly map the binary string directly to bytes.\n // @see https://nodejs.org/docs/latest/api/buffer.html#buffers-and-character-encodings\n bytes = globalThis.Buffer.from(binStr, 'latin1');\n } else {\n const len = binStr.length;\n if (len > MAX_URL_LENGTH) {\n bytes = new Uint8Array(len);\n } else {\n bytes = SHARED_BUFFER.subarray(0, len);\n }\n for (let i = 0; i < len; i++) {\n bytes[i] = binStr.charCodeAt(i);\n }\n }\n try {\n const text = decoder.decode(bytes);\n if (REG_NON_TEXT.test(text)) {\n return cleanData;\n }\n return text;\n } catch {\n return cleanData;\n }\n};\n\n/**\n * Encodes an ArrayBuffer to a Base64 string.\n * @private\n * @param {ArrayBuffer} buffer - The buffer to encode.\n * @returns {string} The Base64 string.\n */\nexport const encodeBufferToBase64 = buffer => {\n if (IS_NODE && globalThis.Buffer) {\n return globalThis.Buffer.from(buffer).toString('base64');\n }\n const uint8arr = new Uint8Array(buffer);\n if (typeof uint8arr.toBase64 === 'function') {\n return uint8arr.toBase64();\n }\n const chunks = [];\n for (let i = 0; i < uint8arr.length; i += CHUNK_SIZE) {\n chunks.push(String.fromCharCode(...uint8arr.subarray(i, i + CHUNK_SIZE)));\n }\n return btoa(chunks.join(''));\n};\n", "/**\n * filter.js\n */\n\n/* shared */\nimport { logDebug, isString } from './common.js';\nimport { domPurify } from './dompurify.js';\nimport {\n encodeBufferToBase64,\n escapeURLEncodedHTMLChars,\n extractDataURLComponents,\n getSchemeParts,\n normalizeURL,\n parseBase64,\n parseURL,\n parseURLEncodedNumCharRef,\n trimTrailingEmptyQueryAndHash,\n truncateURL\n} from './utility.js';\n\n/* constants */\nimport { DUMMY_BASE, MAX_NEST } from './constant.js';\nimport {\n REG_AMP_ENC,\n REG_MIME_DOM,\n REG_SCHEME,\n REG_SCRIPT,\n REG_SCRIPT_OR_BLOB,\n REG_TAG_QUOT,\n REG_VERIFY_RELATIVE\n} from './regexp.js';\n\n/**\n * Context manager for the sanitization process.\n */\nexport class SanitizeContext {\n /**\n * @param {object} opt - Sanitization options.\n * @param {object} domPurifyInstance - The DOMPurify instance.\n */\n constructor(opt, domPurifyInstance) {\n this.domPurify = domPurifyInstance;\n this.nest = 0;\n this.recurse = new Set();\n this.restrictScheme = false;\n this.schemeMap = new Map([\n ['blob', false],\n ['data', false],\n ['file', false],\n ['javascript', false],\n ['vbscript', false]\n ]);\n this.#compileRules(opt);\n }\n\n /**\n * Compiles allow, deny, and only rules into the context.\n * @private\n * @param {object} opt - Sanitization options.\n */\n #compileRules(opt = {}) {\n const { allowRelative, debug, schemes, allow, deny, only } = opt;\n this.allowRelative = !!allowRelative;\n this.debug = !!debug;\n if (Array.isArray(only) && only.length) {\n this.schemes = new Set();\n this.restrictScheme = true;\n for (const scheme of only) {\n this.#registerScheme(scheme);\n }\n } else {\n this.schemes = new Set(schemes || []);\n if (Array.isArray(allow) && allow.length) {\n for (const scheme of allow) {\n this.#registerScheme(scheme);\n }\n }\n if (Array.isArray(deny) && deny.length) {\n for (const scheme of deny) {\n const normalized = normalizeURL(scheme, true);\n if (normalized) {\n this.schemeMap.set(normalized, false);\n }\n }\n }\n }\n }\n\n /**\n * Register schemes for the 'allow' or 'only' options.\n * @private\n * @param {string} scheme - The scheme to register.\n * @returns {boolean} True if the scheme is successfully registered.\n */\n #registerScheme(scheme) {\n const normalizedScheme = normalizeURL(scheme, true);\n if (\n !normalizedScheme ||\n REG_SCRIPT_OR_BLOB.test(normalizedScheme) ||\n !REG_SCHEME.test(normalizedScheme)\n ) {\n return false;\n }\n if (normalizedScheme.includes('+')) {\n const isScript = normalizedScheme\n .split('+')\n .some(s => REG_SCRIPT.test(s));\n if (isScript) {\n return false;\n }\n }\n this.schemeMap.set(normalizedScheme, true);\n this.schemes.add(normalizedScheme);\n return true;\n }\n\n /**\n * Tracks the URL to prevent circular references.\n * @param {string} url - The URL being processed.\n * @returns {boolean} True if safe to proceed, false otherwise.\n */\n enter(url) {\n if (this.recurse.has(url)) {\n if (this.debug) {\n logDebug(`Circular Data URL detected and skipped: ${truncateURL(url)}`);\n }\n return false;\n }\n this.nest++;\n this.recurse.add(url);\n return true;\n }\n\n /**\n * Cleans up the tracking state after processing the URL.\n * @param {string} url - The URL that finished processing.\n */\n leave(url) {\n this.nest--;\n this.recurse.delete(url);\n }\n}\n\n/**\n * An executor for sanitization filters.\n */\nexport class SanitizeFilter {\n /* private fields */\n #defaultOpts;\n\n /**\n * @param {object} options - The default sanitization options.\n */\n constructor(options) {\n this.#defaultOpts = options;\n }\n\n /**\n * Process recursive method for sanitization.\n * @private\n * @param {string} url - The URL string to sanitize.\n * @param {SanitizeContext} ctx - The sanitization context.\n * @returns {string|null} The sanitized URL, or null.\n */\n #process(url, ctx) {\n if (ctx.nest > MAX_NEST) {\n throw new Error('Data URLs nested too deeply.');\n }\n const urlObj = parseURL(url, null, true);\n if (!urlObj) {\n if (ctx.allowRelative && !REG_VERIFY_RELATIVE.test(url)) {\n const dummyURL = parseURL(url, DUMMY_BASE);\n if (dummyURL) {\n const { protocol, hostname, pathname, search, hash } = dummyURL;\n if (protocol === 'http:' && hostname === 'dummy.local') {\n return this.#sanitizeStandardURL(`${pathname}${search}${hash}`);\n }\n }\n }\n return null;\n }\n const scheme = normalizeURL(urlObj.protocol, true);\n if (ctx.restrictScheme && !ctx.schemeMap.has(scheme)) {\n return null;\n }\n if (scheme.includes('+')) {\n const schemeParts = scheme.split('+');\n if (\n ctx.schemeMap.get(scheme) === false ||\n schemeParts.some(part => ctx.schemeMap.get(part) === false)\n ) {\n return null;\n }\n } else if (ctx.schemeMap.get(scheme) === false) {\n return null;\n }\n if (scheme === 'data') {\n return this.#sanitizeDataURL(urlObj, ctx);\n }\n return this.#sanitizeStandardURL(urlObj.href);\n }\n\n /**\n * Decodes, verifies inner protocols, and purifies Data URLs.\n * @private\n * @param {URL} urlObj - The URL object.\n * @param {SanitizeContext} ctx - The sanitization context.\n * @returns {string|null} Sanitized Data URL or null.\n */\n #sanitizeDataURL(urlObj, ctx) {\n const { mediaType, mediaTypes, data, isBase64 } = extractDataURLComponents(\n urlObj.pathname,\n urlObj.search,\n urlObj.hash\n );\n let parsedData = data;\n if (isBase64) {\n try {\n parsedData = parseBase64(data);\n } catch (e) {\n if (ctx.debug) {\n logDebug('Failed to parse base64 data.', e);\n }\n return null;\n }\n }\n try {\n const decodedData = parseURLEncodedNumCharRef(parsedData).trim();\n const parsedURL = parseURL(decodedData, DUMMY_BASE);\n if (!parsedURL) {\n return null;\n }\n const dataSchemeParts = getSchemeParts(parsedURL.protocol);\n if (dataSchemeParts.some(s => REG_SCRIPT_OR_BLOB.test(s))) {\n return null;\n }\n } catch (e) {\n if (ctx.debug) {\n logDebug('Failed to parse inner data URL protocol.', e);\n }\n return null;\n }\n if (!mediaType || REG_MIME_DOM.test(mediaType)) {\n parsedData = this.#purify(parsedData, ctx);\n }\n if (parsedData) {\n if (isBase64 && parsedData !== data) {\n mediaTypes.pop();\n }\n return `data:${mediaTypes.join(';')},${parsedData}`;\n }\n return null;\n }\n\n /**\n * Purifies a URL-encoded DOM string to prevent XSS.\n * @private\n * @param {string} dom - The URL-encoded DOM string.\n * @param {SanitizeContext} ctx - The sanitization context.\n * @returns {string} The purified DOM string.\n */\n #purify(dom, ctx) {\n let decodedDom = dom;\n try {\n decodedDom = decodeURIComponent(dom);\n } catch {\n // fall through\n }\n let purifiedDom;\n try {\n ctx.domPurify.addHook('uponSanitizeAttribute', (node, evt) =>\n this.#handleSanitizeAttribute(node, evt, ctx)\n );\n purifiedDom = ctx.domPurify.sanitize(decodedDom);\n } finally {\n ctx.domPurify.removeHook('uponSanitizeAttribute');\n }\n purifiedDom = trimTrailingEmptyQueryAndHash(purifiedDom);\n try {\n return encodeURI(purifiedDom);\n } catch {\n return purifiedDom;\n }\n }\n\n /**\n * Internal handler for the DOMPurify hook.\n * @private\n * @param {Node} node - The DOM node.\n * @param {object} evt - The event object.\n * @param {SanitizeContext} ctx - The sanitization context.\n */\n #handleSanitizeAttribute(node, evt, ctx) {\n if (!evt.attrValue || !/^\\s*data:/i.test(evt.attrValue)) {\n return;\n }\n const urlObj = parseURL(evt.attrValue, null, true);\n if (!urlObj || urlObj.protocol !== 'data:') {\n return;\n }\n const originalURL = evt.attrValue;\n if (!ctx.enter(originalURL)) {\n evt.attrValue = '';\n return;\n }\n try {\n const sanitized = this.#process(originalURL, ctx);\n evt.attrValue = sanitized || '';\n } finally {\n ctx.leave(originalURL);\n }\n }\n\n /**\n * Sanitizes URLs by stripping out problematic characters.\n * @private\n * @param {string} urlToSanitize - The absolute/relative URL.\n * @returns {string} The sanitized URL.\n */\n #sanitizeStandardURL(urlToSanitize) {\n const len = urlToSanitize.length;\n let truncateIndex = len;\n const matchTagQuot = REG_TAG_QUOT.exec(urlToSanitize);\n if (matchTagQuot) {\n truncateIndex = matchTagQuot.index;\n }\n if (urlToSanitize.indexOf('&') > -1) {\n const matchAmp = REG_AMP_ENC.exec(urlToSanitize);\n if (matchAmp) {\n const matchAmpIndex = matchAmp.index;\n if (matchAmpIndex < truncateIndex) {\n truncateIndex = matchAmpIndex;\n }\n }\n }\n if (truncateIndex === len) {\n return urlToSanitize;\n }\n const lastChar = urlToSanitize.charCodeAt(truncateIndex - 1);\n if (lastChar === 63 /* ? */ || lastChar === 38 /* & */) {\n truncateIndex--;\n }\n return urlToSanitize.substring(0, truncateIndex);\n }\n\n /**\n * Sanitizes the URL.\n * @param {string} url - The URL string to sanitize.\n * @param {object} options - Sanitization options.\n * @returns {string|null} The sanitized URL, or null.\n */\n sanitize(url, options = this.#defaultOpts) {\n if (!url || !isString(url)) {\n return null;\n }\n const { deny, maxLength, only, allowRelative } = options;\n if (Number.isInteger(maxLength) && url.length > maxLength) {\n const msg = `URL length ${url.length} exceeds max length ${maxLength}.`;\n throw new RangeError(msg);\n }\n const hasRestrictiveRules =\n (Array.isArray(deny) && deny.length > 0) ||\n (Array.isArray(only) && only.length > 0) ||\n allowRelative;\n // Early return for standard HTTP/HTTPS URLs without restrictive rules.\n if (\n !hasRestrictiveRules &&\n (url.startsWith('https://') || url.startsWith('http://')) &&\n !REG_TAG_QUOT.test(url) &&\n !url.includes('data:')\n ) {\n const urlObj = parseURL(url, null, true);\n if (urlObj && options.schemes.has(urlObj.protocol.replace(/:$/, ''))) {\n return urlObj.href.replace(/%26/g, escapeURLEncodedHTMLChars);\n }\n return null;\n }\n const ctx = new SanitizeContext(options, domPurify);\n return this.#process(url, ctx);\n }\n\n /**\n * Asynchronously sanitizes an ArrayBuffer and converts it to a Data URL.\n * @param {ArrayBuffer} buffer - The target buffer.\n * @param {string} mimeType - The MIME type of the buffer.\n * @param {object} options - Sanitization options.\n * @returns {Promise<string|null>} The sanitized Data URL, or null.\n */\n async sanitizeBuffer(buffer, mimeType, options = this.#defaultOpts) {\n if (!(buffer instanceof ArrayBuffer)) {\n return null;\n }\n const base64Data = encodeBufferToBase64(buffer);\n const dataUrl = `data:${mimeType ? `${mimeType};base64` : 'base64'},${base64Data}`;\n if (\n Number.isInteger(options.maxLength) &&\n dataUrl.length > options.maxLength\n ) {\n throw new RangeError(\n `URL length ${dataUrl.length} exceeds max length ${options.maxLength}.`\n );\n }\n const ctx = new SanitizeContext(options, domPurify);\n if (!ctx.schemeMap.get('data')) {\n return null;\n }\n const urlObj = parseURL(dataUrl);\n if (!urlObj) {\n return null;\n }\n return this.#sanitizeDataURL(urlObj, ctx);\n }\n\n /**\n * Asynchronously sanitizes the Data URL.\n * @param {string} url - The URL string to sanitize.\n * @param {object} options - Sanitization options.\n * @returns {Promise<string|null>} The sanitized Data URL, or null.\n */\n async sanitizeDataURL(url, options = this.#defaultOpts) {\n if (!url || !isString(url)) {\n return null;\n }\n const { maxLength } = options;\n if (Number.isInteger(maxLength) && url.length > maxLength) {\n throw new RangeError(\n `URL length ${url.length} exceeds max length ${maxLength}.`\n );\n }\n const urlObj = parseURL(url);\n if (!urlObj) {\n return null;\n }\n const scheme = normalizeURL(urlObj.protocol, true);\n if (scheme !== 'data') {\n return null;\n }\n const ctx = new SanitizeContext(options, domPurify);\n if (!ctx.schemeMap.get(scheme)) {\n return null;\n }\n return this.#sanitizeDataURL(urlObj, ctx);\n }\n}\n", "[\"aaa\",\"aaas\",\"about\",\"acap\",\"acct\",\"acd\",\"acr\",\"adiumxtra\",\"adt\",\"aet\",\"afp\",\"afs\",\"agtp\",\"aim\",\"amss\",\"android\",\"appdata\",\"apt\",\"ar\",\"ari\",\"ark\",\"ars\",\"at\",\"attachment\",\"aw\",\"barion\",\"beshare\",\"bitcoin\",\"bitcoincash\",\"bl\",\"blob\",\"bluetooth\",\"bolo\",\"brid\",\"browserext\",\"cabal\",\"caip\",\"calculator\",\"callto\",\"cap\",\"cast\",\"casts\",\"chrome\",\"chrome-extension\",\"cid\",\"cm\",\"coap\",\"coaps\",\"com-eventbrite-attendee\",\"content\",\"content-type\",\"crid\",\"cstr\",\"cttps\",\"cvs\",\"dab\",\"dat\",\"data\",\"dav\",\"dhttp\",\"diaspora\",\"dict\",\"did\",\"dilithium3\",\"dis\",\"dlna-playcontainer\",\"dlna-playsingle\",\"dnp\",\"dns\",\"dntp\",\"doi\",\"donau\",\"dpp\",\"drm\",\"dtmi\",\"dtn\",\"dvb\",\"dvx\",\"dweb\",\"ed2k\",\"eid\",\"elsi\",\"embedded\",\"ens\",\"esim\",\"ethereum\",\"example\",\"ez\",\"facetime\",\"feed\",\"feedready\",\"fido\",\"file\",\"finger\",\"first-run-pen-experience\",\"fish\",\"fm\",\"ftp\",\"fuchsia-pkg\",\"gcx\",\"gdsi\",\"geo\",\"gg\",\"git\",\"gitoid\",\"gizmoproject\",\"go\",\"gopher\",\"graph\",\"gtalk\",\"h323\",\"ham\",\"hcap\",\"hcp\",\"hs20\",\"http\",\"https\",\"hxxp\",\"hxxps\",\"hydrazone\",\"hyper\",\"i0\",\"iax\",\"ibi\",\"ibi-\",\"icap\",\"icon\",\"ilstring\",\"im\",\"imap\",\"info\",\"interaction\",\"iotdisco\",\"ipfs\",\"ipn\",\"ipns\",\"ipp\",\"ipps\",\"irc\",\"irc6\",\"ircs\",\"iris\",\"iris.beep\",\"iris.lwz\",\"iris.xpc\",\"iris.xpcs\",\"isostore\",\"itms\",\"jabber\",\"jar\",\"jms\",\"keyparc\",\"lastfm\",\"lbry\",\"ldap\",\"ldaps\",\"leaptofrogans\",\"lid\",\"linkid\",\"lorawan\",\"lpa\",\"lvlt\",\"machineProvisioningProgressReporter\",\"magnet\",\"mailto\",\"maps\",\"market\",\"matrix\",\"mdoc\",\"mdoc-openid4vp\",\"message\",\"microsoft.windows.camera\",\"microsoft.windows.camera.multipicker\",\"microsoft.windows.camera.picker\",\"mid\",\"mms\",\"mongodb\",\"moz\",\"moz-extension\",\"mqtt\",\"mqtts\",\"ms-access\",\"ms-appinstaller\",\"ms-browser-extension\",\"ms-calculator\",\"ms-drive-to\",\"ms-enrollment\",\"ms-excel\",\"ms-eyecontrolspeech\",\"ms-gamebarservices\",\"ms-gamingoverlay\",\"ms-getoffice\",\"ms-help\",\"ms-infopath\",\"ms-inputapp\",\"ms-launchremotedesktop\",\"ms-lockscreencomponent-config\",\"ms-media-stream-id\",\"ms-meetnow\",\"ms-mixedrealitycapture\",\"ms-mobileplans\",\"ms-newsandinterests\",\"ms-officeapp\",\"ms-people\",\"ms-personacard\",\"ms-powerpoint\",\"ms-project\",\"ms-publisher\",\"ms-recall\",\"ms-remotedesktop\",\"ms-remotedesktop-launch\",\"ms-restoretabcompanion\",\"ms-screenclip\",\"ms-screensketch\",\"ms-search\",\"ms-search-repair\",\"ms-secondary-screen-controller\",\"ms-secondary-screen-setup\",\"ms-settings\",\"ms-settings-airplanemode\",\"ms-settings-bluetooth\",\"ms-settings-camera\",\"ms-settings-cellular\",\"ms-settings-cloudstorage\",\"ms-settings-connectabledevices\",\"ms-settings-displays-topology\",\"ms-settings-emailandaccounts\",\"ms-settings-language\",\"ms-settings-location\",\"ms-settings-lock\",\"ms-settings-nfctransactions\",\"ms-settings-notifications\",\"ms-settings-power\",\"ms-settings-privacy\",\"ms-settings-proximity\",\"ms-settings-screenrotation\",\"ms-settings-wifi\",\"ms-settings-workplace\",\"ms-spd\",\"ms-stickers\",\"ms-sttoverlay\",\"ms-transit-to\",\"ms-useractivityset\",\"ms-uup\",\"ms-virtualtouchpad\",\"ms-visio\",\"ms-walk-to\",\"ms-whiteboard\",\"ms-whiteboard-cmd\",\"ms-widgetboard\",\"ms-widgets\",\"ms-word\",\"msnim\",\"msrp\",\"msrps\",\"mss\",\"mt\",\"mtqp\",\"mtrust\",\"mumble\",\"mupdate\",\"musik\",\"mvn\",\"mvrp\",\"mvrps\",\"news\",\"nfs\",\"ni\",\"nih\",\"nntp\",\"nostr\",\"notes\",\"npamp\",\"num\",\"ocf\",\"oid\",\"onenote\",\"onenote-cmd\",\"opaquelocktoken\",\"openid\",\"openpgp4fpr\",\"otpauth\",\"palm\",\"paparazzi\",\"payto\",\"pkcs11\",\"pkg\",\"platform\",\"pop\",\"pres\",\"proxy\",\"psyc\",\"pttp\",\"pwid\",\"qb\",\"query\",\"quic-transport\",\"redis\",\"rediss\",\"reload\",\"res\",\"resource\",\"rmi\",\"rsync\",\"rtmfp\",\"rtmp\",\"rtsp\",\"rtsps\",\"rtspu\",\"sarif\",\"secondlife\",\"secret-token\",\"service\",\"session\",\"sftp\",\"sgn\",\"shc\",\"shelter\",\"sieve\",\"simpleledger\",\"simplex\",\"sip\",\"sips\",\"skype\",\"smb\",\"smp\",\"sms\",\"smtp\",\"snmp\",\"soap.beep\",\"soap.beeps\",\"soldat\",\"spacify\",\"spiffe\",\"spotify\",\"ssb\",\"ssh\",\"sss\",\"starknet\",\"steam\",\"stun\",\"stuns\",\"submit\",\"svn\",\"swh\",\"swid\",\"swidpath\",\"tag\",\"taler\",\"teamspeak\",\"teapot\",\"teapots\",\"tel\",\"teliaeid\",\"telnet\",\"tftp\",\"things\",\"thismessage\",\"tip\",\"tn3270\",\"tool\",\"tttps\",\"turn\",\"turns\",\"tv\",\"udp\",\"unreal\",\"upn\",\"urn\",\"ust\",\"ut2004\",\"uuaid\",\"uuid-in-package\",\"v-event\",\"vemmi\",\"ventrilo\",\"ves\",\"view-source\",\"vnc\",\"vscode\",\"vscode-insiders\",\"vsls\",\"w3\",\"wasm\",\"wasm-js\",\"wcr\",\"web3\",\"webcal\",\"wifi\",\"ws\",\"wss\",\"wtai\",\"wyciwyg\",\"xcompute\",\"xcon\",\"xcon-userid\",\"xfire\",\"xftp\",\"xmlrpc.beep\",\"xmlrpc.beeps\",\"xmpp\",\"xrcp\",\"xri\",\"ymsgr\",\"z39.50r\",\"z39.50s\",\"ztdnaid\"]\n", "/**\n * scheme.js\n */\n\n/* shared */\nimport uriSchemes from '../lib/iana/uri-schemes.json' with { type: 'json' };\nimport { isString } from './common.js';\nimport { normalizeURL, parseURL } from './utility.js';\n\n/* constants */\nimport { REG_SCHEME_EXT, REG_SCRIPT } from './regexp.js';\n\n/**\n * URI schemes\n */\nexport class URISchemes {\n /* private fields */\n #schemes = new Set(uriSchemes);\n\n /**\n * Gets the list of registered URI schemes.\n * @see {@link https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml}\n * - Historical schemes are omitted.\n * - The 'moz-extension' scheme is added by default.\n * @returns {string[]} An array of registered schemes.\n */\n get() {\n return [...this.#schemes];\n }\n\n /**\n * Extracts the scheme from URI.\n * @param {string} uri - The URI string.\n * @returns {string|null} The extracted scheme, or null.\n */\n getScheme(uri) {\n if (!isString(uri)) {\n return null;\n }\n const parsed = this.parse(uri);\n if (parsed) {\n return this.normalize(parsed.protocol, true);\n }\n return null;\n }\n\n /**\n * Checks if the specified scheme is currently registered.\n * @param {string} scheme - The target scheme.\n * @returns {boolean} True if the scheme is registered.\n */\n has(scheme) {\n const normalized = this.normalize(scheme, true);\n if (normalized) {\n return this.#schemes.has(normalized);\n }\n return false;\n }\n\n /**\n * Normalizes the URI string using NFKC.\n * @param {string} uri - The URI string to normalize.\n * @param {boolean} [isScheme] - True if the URI is a scheme.\n * @returns {string|null} The normalized URI string, or null.\n */\n normalize(uri, isScheme = false) {\n return normalizeURL(uri, isScheme);\n }\n\n /**\n * Normalizes an array of scheme strings.\n * Filters out non-string items and trims/lowercases valid schemes.\n * @param {string[]} schemes - An array of scheme strings to normalize.\n * @returns {string[]} An array of normalized scheme strings, or an empty array if invalid.\n */\n normalizeSchemes(schemes) {\n if (Array.isArray(schemes)) {\n const normalizedSchemes = [];\n for (const scheme of schemes) {\n const normalized = normalizeURL(scheme, true);\n if (normalized) {\n normalizedSchemes.push(normalized);\n }\n }\n return normalizedSchemes;\n }\n return [];\n }\n\n /**\n * Parses the URI/URL string.\n * @param {string} uri - The URI/URL string.\n * @param {string} [base] - The base URL string.\n * @param {boolean} [normalize] - Indicates whether to normalize the URI.\n * @returns {URL|null} The parsed URL object, or null.\n */\n parse(uri, base, normalize = false) {\n return parseURL(uri, base, normalize);\n }\n\n /**\n * Verifies if the given URI is valid and its scheme is allowed.\n * @param {string} uri - The URI string to verify.\n * @param {Set<string>} [schemes] - The set of allowed schemes.\n * @returns {boolean} True if the URI is valid and permitted.\n */\n verifyURI(uri, schemes) {\n if (!isString(uri)) {\n return false;\n }\n const parsedUrl = this.parse(uri);\n if (!parsedUrl) {\n return false;\n }\n return this.verifyScheme(parsedUrl.protocol, schemes, true);\n }\n\n /**\n * Verifies if the scheme is valid and allowed.\n * @param {string} scheme - A scheme or URL.protocol to verify.\n * @param {Set<string>} [schemes] - The set of allowed schemes.\n * @param {boolean} [allowCustom] - True if web+*, ext+* schemes are allowed.\n * @returns {boolean} True if the parsed URL is valid and permitted.\n */\n verifyScheme(scheme, schemes, allowCustom = false) {\n if (!isString(scheme)) {\n return false;\n }\n const normalized = this.normalize(scheme, true);\n const targetSchemes = schemes || this.#schemes;\n if (!normalized.includes('+')) {\n if (REG_SCRIPT.test(normalized)) {\n return false;\n }\n return targetSchemes.has(normalized);\n }\n const parts = normalized.split('+');\n const isScript = parts.some(s => REG_SCRIPT.test(s));\n if (isScript) {\n return false;\n }\n if (targetSchemes.has(normalized)) {\n return true;\n }\n return (\n (allowCustom && REG_SCHEME_EXT.test(normalized)) ||\n parts.every(s => targetSchemes.has(s))\n );\n }\n}\n", "/**\n * sanitize.js\n */\n\n/* shared */\nimport { getType, logDebug, isString } from './common.js';\nimport { SanitizeFilter } from './filter.js';\nimport { URISchemes } from './scheme.js';\nimport {\n encodeBufferToBase64,\n extractDataURLComponents,\n normalizeURL,\n parseURL,\n truncateURL\n} from './utility.js';\n\n/* constants */\nimport { DECI, MAX_BLOB_SIZE, MAX_URL_LENGTH } from './constant.js';\nimport { REG_SCHEME, REG_SCRIPT, REG_SCRIPT_OR_BLOB } from './regexp.js';\nconst URL_PROPS = Object.freeze([\n 'href',\n 'origin',\n 'protocol',\n 'username',\n 'password',\n 'host',\n 'hostname',\n 'port',\n 'pathname',\n 'search',\n 'hash'\n]);\nconst DEFAULT_OPTS = Object.freeze({\n allow: Object.freeze([]),\n deny: Object.freeze([]),\n only: Object.freeze([]),\n allowRelative: false,\n debug: false,\n revokeObjectURL: false,\n maxBlobSize: MAX_BLOB_SIZE,\n maxLength: MAX_URL_LENGTH\n});\n\n/* typedef */\n/**\n * The parsed result of a Data URL.\n * @typedef {object} InspectedDataURL\n * @property {boolean} base64 - Indicates whether the data is base64-encoded.\n * @property {string} data - The actual data part of the Data URL.\n * @property {string} mime - The MIME type of the data.\n */\n\n/**\n * The result of an inspected URL, extending the standard URL API.\n * The properties except for input and valid are omitted for invalid URLs.\n * @typedef {object} InspectedURLResult\n * @property {string} input - The original URL input.\n * @property {boolean} valid - Indicates whether the sanitized URL (href) is a valid absolute URL.\n * @property {string|null} href - The sanitized URL, or null.\n * @property {boolean} [relative] - Indicates whether the sanitized URL (href) is a valid relative URL.\n * @property {string} [reason] - The reason why the URL is invalid.\n * @property {InspectedDataURL|null} [data] - The parsed result of a Data URL, or null.\n * @property {string} [origin] - The scheme, domain, and port.\n * @property {string} [protocol] - The protocol scheme.\n * @property {string} [username] - The specified username.\n * @property {string} [password] - The specified password.\n * @property {string} [host] - The domain and port.\n * @property {string} [hostname] - The domain.\n * @property {string} [port] - The port number.\n * @property {string} [pathname] - The path.\n * @property {string} [search] - The query string.\n * @property {string} [hash] - The fragment identifier.\n */\n\n/* blob handlers */\n/**\n * Reads a stream in chunks and generates an ArrayBuffer.\n * @private\n * @param {Response} response - The Response instance.\n * @param {number} maxSize - The maximum allowed size in bytes.\n * @returns {Promise<ArrayBuffer>} A promise resolving to the generated ArrayBuffer.\n */\nconst readStreamInChunksAsArrayBuffer = async (response, maxSize) => {\n const reader = response.body.getReader();\n const chunks = [];\n try {\n let accumulatedSize = 0;\n while (accumulatedSize <= maxSize) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n accumulatedSize += value.byteLength;\n if (accumulatedSize > maxSize) {\n await reader.cancel('Size limit exceeded');\n throw new DOMException(\n `Payload (${accumulatedSize} bytes) exceeds max (${maxSize} bytes).`,\n 'NotReadableError'\n );\n }\n chunks.push(value);\n }\n const combined = new Uint8Array(accumulatedSize);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return combined.buffer;\n } finally {\n reader.releaseLock();\n }\n};\n\n/**\n * Fetches a Blob URL and extracts its ArrayBuffer and MIME type.\n * @param {string} url - The Blob URL to fetch.\n * @param {number} [maxBlobSize] - The maximum allowed Blob size in bytes.\n * @returns {Promise<{ buffer: ArrayBuffer, mimeType: string }>} A promise resolving to the buffer and MIME type.\n */\nconst fetchBlobAsArrayBuffer = async (url, maxBlobSize) => {\n let maxSize = MAX_BLOB_SIZE;\n if (Number.isInteger(maxBlobSize) && maxBlobSize > 0) {\n maxSize = maxBlobSize;\n }\n const response = await fetch(url);\n if (!response.ok) {\n const truncatedURL = truncateURL(url);\n let msg = `Failed to fetch ${truncatedURL}`;\n if (Number.isInteger(response.status)) {\n if (response.statusText) {\n msg += `: ${response.status} ${response.statusText}`;\n } else {\n msg += `: ${response.status}`;\n }\n }\n throw new Error(msg);\n }\n const contentLength = response.headers.get('content-length');\n if (contentLength) {\n const parsedLength = Number.parseInt(contentLength, DECI);\n if (Number.isInteger(parsedLength) && parsedLength > maxSize) {\n throw new DOMException(\n `Payload (${parsedLength} bytes) exceeds max (${maxSize} bytes).`,\n 'NotReadableError'\n );\n }\n }\n const mimeType = response.headers.get('content-type') || '';\n let buffer;\n if (response.body) {\n buffer = await readStreamInChunksAsArrayBuffer(response, maxSize);\n } else {\n const blob = await response.blob();\n if (blob.size > maxSize) {\n throw new DOMException(\n `Payload (${blob.size} bytes) exceeds max (${maxSize} bytes).`,\n 'NotReadableError'\n );\n }\n buffer = await blob.arrayBuffer();\n }\n return { buffer, mimeType };\n};\n\n/**\n * URL sanitizer\n */\nexport class URLSanitizer extends URISchemes {\n /* private fields */\n #allowedSchemes;\n #defaultOpts;\n #filter;\n\n constructor() {\n super();\n this.#allowedSchemes = new Set(super.get());\n this.#defaultOpts = {\n ...DEFAULT_OPTS,\n schemes: this.#allowedSchemes\n };\n this.#filter = new SanitizeFilter(this.#defaultOpts);\n }\n\n /**\n * Validates if a normalized URI scheme is syntactically correct.\n * @private\n * @param {string} normalizedScheme - The normalized URI scheme to validate.\n * @returns {boolean} True if the scheme satisfies the syntax and requirements.\n */\n #isWellFormedScheme(normalizedScheme) {\n if (\n !isString(normalizedScheme) ||\n REG_SCRIPT_OR_BLOB.test(normalizedScheme)\n ) {\n return false;\n }\n if (normalizedScheme.includes('+')) {\n const isScript = normalizedScheme\n .split('+')\n .some(s => REG_SCRIPT.test(s));\n if (isScript) {\n return false;\n }\n }\n return REG_SCHEME.test(normalizedScheme);\n }\n\n /**\n * Normalizes options and evaluates preliminary scheme routing.\n * @private\n * @param {string} url - The URL string.\n * @param {object} opt - User options.\n * @returns {{ isValid: boolean|undefined, options: object, scheme: string|null }} Nomalized options, scheme, and isValid.\n */\n #normalizeOptions(url, opt = {}) {\n if (!url || !isString(url)) {\n return { isValid: false };\n }\n const options = {\n ...this.#defaultOpts,\n ...opt,\n allow: this.normalizeSchemes(opt.allow),\n deny: this.normalizeSchemes(opt.deny),\n only: this.normalizeSchemes(opt.only),\n schemes: this.#allowedSchemes\n };\n const scheme = this.getScheme(url);\n if (scheme === null && !options.allowRelative) {\n return { isValid: false };\n }\n return { options, scheme };\n }\n\n /**\n * Inspects, parses, and sanitizes the given URL.\n * @private\n * @param {string} url - The URL string to parse.\n * @returns {InspectedURLResult} The result of an inspected URL.\n */\n #inspect(url) {\n const inspectedURL = {\n input: url,\n href: null,\n valid: false\n };\n if (!isString(url)) {\n inspectedURL.reason = `Invalid URL input: ${truncateURL(url)}`;\n return inspectedURL;\n }\n if (url === '') {\n inspectedURL.reason = 'Invalid URL input: (empty string)';\n return inspectedURL;\n }\n let sanitizedURL;\n let invalidReason = null;\n try {\n sanitizedURL = this.#filter.sanitize(url, {\n ...this.#defaultOpts,\n allow: ['data'],\n allowRelative: true\n });\n if (!sanitizedURL) {\n invalidReason =\n 'Sanitization failed (blocked by allowed schemes or rules).';\n }\n } catch (e) {\n invalidReason = e.message;\n }\n if (sanitizedURL) {\n // Reparse the sanitized string to safely extract updated properties.\n const urlObj = parseURL(sanitizedURL);\n inspectedURL.valid = true;\n if (urlObj) {\n const scheme = normalizeURL(urlObj.protocol, true);\n if (scheme === 'data') {\n const { mediaTypes, data, isBase64 } = extractDataURLComponents(\n urlObj.pathname,\n urlObj.search,\n urlObj.hash\n );\n if (isBase64) {\n mediaTypes.pop();\n }\n inspectedURL.data = {\n mime: mediaTypes.join(';'),\n base64: isBase64,\n data\n };\n } else {\n inspectedURL.data = null;\n }\n for (const key of URL_PROPS) {\n const value = urlObj[key];\n if (isString(value)) {\n inspectedURL[key] = value;\n }\n }\n } else {\n // URL which is invalid as an absolute URL, but valid as a relative URL.\n inspectedURL.valid = false;\n inspectedURL.relative = true;\n inspectedURL.href = sanitizedURL;\n inspectedURL.reason =\n 'Invalid as an absolute URL, but valid as a relative URL.';\n }\n } else {\n inspectedURL.reason = invalidReason;\n }\n return inspectedURL;\n }\n\n /**\n * Gets the list of registered URI schemes.\n * @returns {string[]} An array of registered schemes.\n */\n get() {\n return [...this.#allowedSchemes];\n }\n\n /**\n * Checks if the specified scheme is currently registered.\n * @param {string} scheme - The target scheme.\n * @returns {boolean} True if the scheme is registered.\n */\n has(scheme) {\n const normalizedScheme = normalizeURL(scheme, true);\n if (normalizedScheme) {\n return this.#allowedSchemes.has(normalizedScheme);\n }\n return false;\n }\n\n /**\n * Adds a new scheme to the allowed list.\n * @param {string} scheme - The scheme to add.\n * @returns {string[]} The updated array of registered schemes.\n */\n add(scheme) {\n if (!isString(scheme)) {\n throw new TypeError(`Expected String but got ${getType(scheme)}.`);\n }\n const normalizedScheme = normalizeURL(scheme, true);\n if (!this.#isWellFormedScheme(normalizedScheme)) {\n throw new Error(`Invalid scheme: ${scheme}`);\n }\n this.#allowedSchemes.add(normalizedScheme);\n return [...this.#allowedSchemes];\n }\n\n /**\n * Removes a scheme from the allowed list.\n * @param {string} scheme - The scheme to remove.\n * @returns {boolean} True if the scheme was successfully removed.\n */\n remove(scheme) {\n if (!isString(scheme)) {\n return false;\n }\n const normalizedScheme = normalizeURL(scheme, true);\n return this.#allowedSchemes.delete(normalizedScheme);\n }\n\n /**\n * Resets the registered schemes back to the default initial list.\n * @returns {void}\n */\n reset() {\n this.#allowedSchemes = new Set(super.get());\n }\n\n /**\n * Asynchronously sanitizes the given URL.\n * NOTE: `blob`, `data`, and `file` schemes must be explicitly allowed.\n * Given a `blob` URL, it securely converts and returns a sanitized `data` URL.\n * @param {string} url - URL.\n * @param {object} [opt] - options.\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the Blob URL after sanitization.\n * @param {number} [opt.maxBlobSize] - The maximum allowed Blob size in bytes.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {Promise<string|null>} A promise resolving to the sanitized URL, or null.\n */\n async sanitizeURL(url, opt) {\n const { isValid, options, scheme } = this.#normalizeOptions(url, opt);\n if (isValid === false) {\n return null;\n }\n if (scheme === 'blob') {\n const { allow, deny, only } = options;\n let sanitizedData = null;\n if (\n (allow.includes('blob') && !deny.includes('blob')) ||\n only.includes('blob')\n ) {\n let fetchedBuffer = null;\n let fetchedMimeType = '';\n try {\n const fetchResult = await fetchBlobAsArrayBuffer(\n url,\n options.maxBlobSize\n );\n fetchedBuffer = fetchResult.buffer;\n fetchedMimeType = fetchResult.mimeType;\n } catch (e) {\n if (options.debug) {\n logDebug(\n `Failed to fetch and convert blob URL: ${truncateURL(url)}`,\n e\n );\n }\n }\n if (fetchedBuffer) {\n if (only.length) {\n if (!only.includes('data')) {\n options.only = [...only, 'data'];\n }\n } else {\n if (!allow.includes('data')) {\n options.allow = [...allow, 'data'];\n }\n options.deny = deny.filter(s => s !== 'data');\n }\n sanitizedData = await this.#filter.sanitizeBuffer(\n fetchedBuffer,\n fetchedMimeType,\n options\n );\n }\n }\n if (options.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n return sanitizedData;\n } else if (scheme === 'data') {\n return this.#filter.sanitizeDataURL(url, options);\n }\n return this.#filter.sanitize(url, options);\n }\n\n /**\n * Synchronously sanitizes the given URL.\n * NOTE: `data` and `file` schemes must be explicitly allowed.\n * The `blob` scheme is not supported and will return `null`.\n * @param {string} url - URL.\n * @param {object} [opt] - options.\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the Blob URL.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {string|null} The sanitized URL, or null if denied.\n */\n sanitizeURLSync(url, opt) {\n const { isValid, options, scheme } = this.#normalizeOptions(url, opt);\n if (isValid === false) {\n return null;\n }\n if (scheme === 'blob') {\n if (options.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n return null;\n }\n return this.#filter.sanitize(url, options);\n }\n\n /**\n * Sanitizes the given URL and returns its parsed components.\n * NOTE: Blob URLs are not revoked after inspection.\n * @param {string} url - The URL string to inspect.\n * @returns {Promise<InspectedURLResult>} A promise resolving to the inspected URL result.\n */\n async inspectURL(url) {\n if (isString(url)) {\n const parsedURL = parseURL(url, null, true);\n if (parsedURL?.protocol === 'blob:') {\n try {\n const { buffer, mimeType } = await fetchBlobAsArrayBuffer(\n parsedURL.href\n );\n const base64Data = encodeBufferToBase64(buffer);\n const dataURL = `data:${mimeType ? `${mimeType};base64` : 'base64'},${base64Data}`;\n const inspectedURLResult = this.#inspect(dataURL);\n inspectedURLResult.input = url;\n return inspectedURLResult;\n } catch (e) {\n return { input: url, valid: false, reason: e.message };\n }\n }\n }\n return this.#inspect(url);\n }\n\n /**\n * Verifies if the given URI is valid and its scheme is allowed.\n * @param {string} uri - The URI string to verify.\n * @returns {boolean} True if the URI is syntactically valid and permitted.\n */\n isValidURI(uri) {\n return super.verifyURI(uri, this.#allowedSchemes);\n }\n}\n", "/*!\n * URL Sanitizer\n *\n * @license MIT\n * @copyright asamuzaK (Kazz)\n * @see {@link https://github.com/asamuzaK/urlSanitizer/blob/main/LICENSE}\n */\n\nimport { URLSanitizer } from './mjs/sanitize.js';\n\n/**\n * @typedef {import('./mjs/sanitize.js').InspectedURLResult} InspectedURLResult\n */\n\nexport { URLSanitizer };\n\n/* URLSanitizer instance */\nconst sanitizer = new URLSanitizer();\n\n/* aliases */\n/**\n * Asynchronously sanitizes the given URL.\n * NOTE: `blob`, `data`, and `file` schemes must be explicitly allowed.\n * Given a `blob` URL, it securely converts and returns a sanitized `data` URL.\n * @param {string} url - URL.\n * @param {object} [opt] - options.\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the Blob URL after sanitization.\n * @param {number} [opt.maxBlobSize] - The maximum allowed Blob size in bytes.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {Promise<string|null>} A promise resolving to the sanitized URL, or null.\n */\nexport const sanitizeURL = async (url, opt) => sanitizer.sanitizeURL(url, opt);\n\n/**\n * Synchronously sanitizes the given URL.\n * NOTE: `data` and `file` schemes must be explicitly allowed.\n * The `blob` scheme is not supported and will return `null`.\n * @param {string} url - URL.\n * @param {object} [opt] - options.\n * @param {string[]} [opt.allow] - The array of schemes to allow.\n * @param {string[]} [opt.deny] - The array of schemes to deny.\n * @param {string[]} [opt.only] - The array of specific schemes to allow.\n * @param {boolean} [opt.allowRelative] - Allow relative URLs.\n * @param {boolean} [opt.debug] - Enable debug mode.\n * @param {boolean} [opt.revokeObjectURL] - Revokes the Blob URL.\n * @param {number} [opt.maxLength] - The maximum allowed URL length.\n * @returns {string|null} The sanitized URL, or null if denied.\n */\nexport const sanitizeURLSync = (url, opt) =>\n sanitizer.sanitizeURLSync(url, opt);\n\n/**\n * Sanitizes the given URL and returns its parsed components.\n * NOTE: Blob URLs are not revoked after inspection.\n * @param {string} url - The URL string to inspect.\n * @returns {Promise<InspectedURLResult>} A promise resolving to the inspected URL result.\n */\nexport const inspectURL = async url => sanitizer.inspectURL(url);\n\n/**\n * Checks if the given string is a valid URI and is registered.\n * @param {string} uri - The URI string to verify.\n * @returns {boolean} True if valid and registered, false otherwise.\n */\nexport const isValidURI = uri => sanitizer.isValidURI(uri);\n\n/**\n * @deprecated Use {@link isValidURI} instead.\n * @param {string} uri - The URI string to verify.\n * @returns {boolean} True if valid and registered, false otherwise.\n */\nexport const isURISync = uri => sanitizer.isValidURI(uri);\n\n/* export instance */\nexport default sanitizer;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["DUMMY_BASE", "getType", "o", "isString", "logDebug", "message", "error", "PROPERTIES", "hookQueue", "domPurify", "target", "prop", "receiver", "purifier", "p", "args", "value", "REG_AMP", "REG_AMP_ENC", "REG_HASH", "REG_MIME_DOM", "REG_NON_ASCII", "REG_NUM_REF", "REG_PCT_ENC", "REG_QUERY", "REG_SCHEME", "REG_SCHEME_EXT", "REG_SCRIPT", "REG_SCRIPT_OR_BLOB", "REG_TAG_QUOT", "REG_URL_ENC", "REG_VERIFY_RELATIVE", "text_chars_default", "TEXT_CHAR_CODES", "text_chars_default", "WINDOWS1252_TO_UNICODE", "generateNonTextCharCodes", "charCodes", "i", "NON_TEXT_CHAR_CODES", "ENC_AMP", "ENC_NUM", "ENC_LT", "ENC_GT", "ENC_QUOT", "ENC_APOS", "ch", "ESCAPE_MAP", "HEX_TABLE", "_", "i", "IS_NODE", "NON_TEXT_PATTERN", "NON_TEXT_CHAR_CODES", "REG_NON_TEXT", "REG_NON_TEXT_G", "SHARED_BUFFER", "encoder", "decoder", "normalizeURL", "url", "isScheme", "isString", "normalized", "REG_NON_ASCII", "parseURL", "base", "normalize", "urlStr", "baseStr", "getSchemeParts", "protocol", "escapeURLEncodedHTMLChars", "ch", "isString", "target", "REG_URL_ENC", "ESCAPE_MAP", "truncateURL", "url", "str", "trimTrailingEmptyQueryAndHash", "REG_HASH", "REG_QUERY", "replaceNumCharRef", "match", "value", "num", "NON_TEXT_CHAR_CODES", "TEXT_CHAR_CODES", "codePoint", "WINDOWS1252_TO_UNICODE", "parseURLEncodedNumCharRef", "nest", "getType", "res", "REG_PCT_ENC", "REG_NON_TEXT_G", "depth", "previousRes", "REG_AMP", "REG_NUM_REF", "extractDataURLComponents", "pathname", "search", "hash", "comma", "mediaType", "dataPart", "data", "mediaTypes", "lastType", "isBase64", "parseBase64", "binStr", "cleanData", "bytes", "IS_NODE", "len", "SHARED_BUFFER", "text", "decoder", "REG_NON_TEXT", "encodeBufferToBase64", "buffer", "uint8arr", "chunks", "i", "SanitizeContext", "opt", "domPurifyInstance", "#compileRules", "allowRelative", "debug", "schemes", "allow", "deny", "only", "scheme", "#registerScheme", "normalized", "normalizeURL", "normalizedScheme", "REG_SCRIPT_OR_BLOB", "REG_SCHEME", "s", "REG_SCRIPT", "url", "logDebug", "truncateURL", "SanitizeFilter", "#defaultOpts", "options", "#process", "ctx", "urlObj", "parseURL", "REG_VERIFY_RELATIVE", "dummyURL", "DUMMY_BASE", "protocol", "hostname", "pathname", "search", "hash", "#sanitizeStandardURL", "schemeParts", "part", "#sanitizeDataURL", "mediaType", "mediaTypes", "data", "isBase64", "extractDataURLComponents", "parsedData", "parseBase64", "e", "decodedData", "parseURLEncodedNumCharRef", "parsedURL", "getSchemeParts", "REG_MIME_DOM", "#purify", "dom", "decodedDom", "purifiedDom", "node", "evt", "#handleSanitizeAttribute", "trimTrailingEmptyQueryAndHash", "originalURL", "sanitized", "urlToSanitize", "len", "truncateIndex", "matchTagQuot", "REG_TAG_QUOT", "matchAmp", "REG_AMP_ENC", "matchAmpIndex", "lastChar", "isString", "maxLength", "msg", "escapeURLEncodedHTMLChars", "domPurify", "buffer", "mimeType", "base64Data", "encodeBufferToBase64", "dataUrl", "uri_schemes_default", "URISchemes", "#schemes", "uri_schemes_default", "uri", "isString", "parsed", "scheme", "normalized", "isScheme", "normalizeURL", "schemes", "normalizedSchemes", "base", "normalize", "parseURL", "parsedUrl", "allowCustom", "targetSchemes", "REG_SCRIPT", "parts", "s", "REG_SCHEME_EXT", "URL_PROPS", "DEFAULT_OPTS", "readStreamInChunksAsArrayBuffer", "response", "maxSize", "reader", "chunks", "accumulatedSize", "done", "value", "combined", "offset", "chunk", "fetchBlobAsArrayBuffer", "url", "maxBlobSize", "msg", "truncateURL", "contentLength", "parsedLength", "mimeType", "buffer", "blob", "URLSanitizer", "URISchemes", "#allowedSchemes", "#defaultOpts", "#filter", "SanitizeFilter", "#isWellFormedScheme", "normalizedScheme", "isString", "REG_SCRIPT_OR_BLOB", "REG_SCRIPT", "REG_SCHEME", "#normalizeOptions", "opt", "options", "scheme", "#inspect", "inspectedURL", "sanitizedURL", "invalidReason", "e", "urlObj", "parseURL", "normalizeURL", "mediaTypes", "data", "isBase64", "extractDataURLComponents", "key", "getType", "isValid", "allow", "deny", "only", "sanitizedData", "fetchedBuffer", "fetchedMimeType", "fetchResult", "logDebug", "s", "parsedURL", "base64Data", "encodeBufferToBase64", "dataURL", "inspectedURLResult", "uri", "sanitizer", "URLSanitizer", "sanitizeURL", "url", "opt", "sanitizeURLSync", "inspectURL", "isValidURI", "uri", "isURISync", "index_default"]
|
|
7
7
|
}
|