url-sanitizer 4.2.2 → 5.0.1
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 +47 -49
- package/dist/url-sanitizer-wo-dompurify.min.js +1 -1
- package/dist/url-sanitizer-wo-dompurify.min.js.map +3 -3
- package/dist/url-sanitizer.min.js +4 -4
- package/dist/url-sanitizer.min.js.map +3 -3
- package/package.json +20 -26
- package/src/index.js +2 -5
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/constant.js +18 -0
- package/src/mjs/regexp.js +17 -11
- package/src/mjs/sanitizer.js +559 -442
- package/src/mjs/text-chars.js +6 -6
- package/src/mjs/uri-util.js +201 -139
- package/types/index.d.ts +1 -1
- package/types/mjs/constant.d.ts +3 -0
- package/types/mjs/regexp.d.ts +3 -2
- package/types/mjs/sanitizer.d.ts +20 -11
- package/types/mjs/text-chars.d.ts +1 -1
- package/types/mjs/uri-util.d.ts +9 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../bundle_wo_dompurify/mjs/dompurify.js", "../bundle_wo_dompurify/mjs/common.js", "../bundle_wo_dompurify/lib/iana/uri-schemes.json", "../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/uri-util.js", "../bundle_wo_dompurify/mjs/sanitizer.js"],
|
|
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) {\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 target;\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\",\"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\",\"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\",\"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\",\"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 = /(?:#|%23)$/;\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#&.;-]+/;\n\n/**\n * Matches numeric character reference.\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))*)(?:\\?|%3F)$/i;\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_BLOB = /^(?:(?:java|vb)script|blob)$/i;\n\n/**\n * Matches HTML tags, quotes, and their encoded or character reference 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 an ampersand-encoded character (&foo).\n * @type {RegExp}\n */\nexport const REG_AMP_ENC = /&(?=[#a-z0-9])(?![a-z0-9]+=)/i;\n\n/**\n * Match one or more consecutive URL percent-encoded sequences (%20, %E3%81%82).\n * @type {RegExp}\n */\nexport const REG_PCT_ENC = /(?:%[\\dA-F]{2})+/gi;\n\n/**\n * Validates relative URLs and flags invalid 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 { 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 control character codes in hex.\n * @private\n * @returns {Map<number, string>} Generated control char codes.\n */\nconst generateCtrlCharCodes = () => {\n const charCodes = new Map();\n for (let i = 0; i < HEX * HEX; 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 control character codes to their hexadecimal escape strings.\n * @type {Map<number, string>}\n */\nexport const CTRL_CHAR_CODES = generateCtrlCharCodes();\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 { CHUNK_SIZE, DECI, HEX, MAX_BLOB_SIZE, MAX_NEST } from './constant.js';\nimport {\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 CTRL_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: HEX * HEX },\n (_, i) => `%${i.toString(HEX).padStart(2, '0').toUpperCase()}`\n);\nconst IS_NODE = globalThis.process?.versions?.node !== undefined;\nconst REG_BINARY = new RegExp(`[${[...CTRL_CHAR_CODES.values()].join('')}]`);\nconst REG_CTRL_CHARS = new RegExp(\n `[${[...CTRL_CHAR_CODES.values()].join('')}]`,\n 'g'\n);\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 * 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 if (!isString(scheme)) {\n return false;\n }\n return this.#schemes.has(scheme.trim().toLowerCase());\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 = this.#schemes) {\n if (!isString(uri)) {\n return false;\n }\n const normalizedUri = uri.normalize('NFKC');\n const parsedUrl = URL.parse(normalizedUri);\n if (!parsedUrl) {\n return false;\n }\n const scheme = parsedUrl.protocol.replace(/:$/, '');\n const parts = scheme.split('+');\n const isScript = parts.some(s => REG_SCRIPT.test(s));\n if (isScript) {\n return false;\n }\n return REG_SCHEME_EXT.test(scheme) || parts.every(s => schemes.has(s));\n }\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 let encoded = '';\n for (let i = 0; i < bytes.length; i++) {\n encoded += HEX_TABLE[bytes[i]];\n }\n return encoded;\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 */\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 * 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: ${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_BINARY.test(text)) {\n return cleanData;\n }\n return text;\n } catch {\n return cleanData;\n }\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[0])\n ? parseInt(value.substring(1), HEX)\n : parseInt(value, DECI);\n if (Number.isInteger(num)) {\n if (TEXT_CHAR_CODES.has(num)) {\n return String.fromCharCode(num);\n } else if (WINDOWS1252_TO_UNICODE.has(num)) {\n const codePoint = WINDOWS1252_TO_UNICODE.get(num);\n return String.fromCodePoint(codePoint);\n } else if (CTRL_CHAR_CODES.has(num)) {\n return '';\n }\n }\n return match;\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 * Parses URL-encoded numeric character references in the range 0x00 to 0xFF.\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_CTRL_CHARS, '');\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(/&/gi, '&').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 * 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', () => reject(reader.error));\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 * Converts a Blob to a data URL.\n * @param {Blob} blob - The target Blob object.\n * @param {number} [maxSize] - The maximum allowed blob size.\n * @returns {Promise<string|null>} A promise resolving to the data URL, or null.\n */\nexport const convertBlobToDataURL = async (blob, maxSize = MAX_BLOB_SIZE) => {\n if (!Number.isInteger(blob?.size)) {\n return null;\n } else if (Number.isInteger(maxSize) && blob.size > maxSize) {\n const msg = `Blob size (${blob.size} bytes) exceeds max (${maxSize} bytes).`;\n throw new DOMException(msg, 'NotReadableError');\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 * sanitizer.js\n */\n\n/* shared */\nimport { domPurify } from './dompurify.js';\nimport { getType, isString } from './common.js';\nimport {\n URISchemes,\n convertBlobToDataURL,\n escapeURLEncodedHTMLChars,\n parseBase64,\n parseURLEncodedNumCharRef,\n trimTrailingEmptyQueryAndHash\n} from './uri-util.js';\n\n/* constants */\nimport { MAX_BLOB_SIZE, MAX_NEST } from './constant.js';\nimport {\n REG_AMP_ENC,\n REG_MIME_DOM,\n REG_SCHEME,\n REG_SCRIPT,\n REG_SCRIPT_BLOB,\n REG_TAG_QUOT,\n REG_VERIFY_RELATIVE\n} 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 INTERNAL_PURIFY_CONFIG = Object.freeze({\n RETURN_DOM: false,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false\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 URI is valid.\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 {boolean} isDebug - Flag to enable or disable debug output.\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 = (isDebug, message, error) => {\n if (isDebug) {\n console.warn(`[URLSanitizer Debug] ${message}`, error);\n }\n};\n\n/**\n * URL sanitizer\n */\nclass URLSanitizer extends URISchemes {\n /* private fields */\n #allowedSchemes;\n\n /**\n * NOTE: Global Context Pattern for DOMPurify Hooks\n * - DOMPurify hooks (e.g., 'uponSanitizeAttribute') are registered globally\n * and do not accept custom arguments (like `this` or `ctx`).\n * To pass the current sanitization context into the hook, we temporarily\n * store it in static fields (#currentInstance and #currentCtx).\n * - This is entirely safe and immune to race conditions because JavaScript\n * runs on a single thread and DOMPurify's sanitize() is synchronous.\n * The context is safely restored via try/finally block in #purify() even\n * during recursive calls.\n */\n /** @type {URLSanitizer | null} */\n static #currentInstance = null;\n static #currentCtx = null;\n\n /**\n * DOMPurify 'uponSanitizeAttribute' hook callback.\n * @private\n * @static\n * @param {Node} node - The DOM node being sanitized.\n * @param {object} evt - The event object containing attribute details.\n */\n static #uponSanitizeAttribute(node, evt) {\n if (!evt.attrValue || !/^\\s*data:/i.test(evt.attrValue)) {\n return;\n }\n /** @type {URLSanitizer | null} */\n const sanitizer = URLSanitizer.#currentInstance;\n const ctx = URLSanitizer.#currentCtx;\n if (!sanitizer || !ctx) {\n return;\n }\n const urlObj = URL.parse(evt.attrValue);\n if (!urlObj) {\n return;\n }\n if (urlObj.protocol === 'data:') {\n const originalUrl = evt.attrValue;\n if (!ctx.recurse) {\n ctx.recurse = new Set();\n }\n if (ctx.recurse.has(originalUrl)) {\n const msg = `Circular Data URL detected and skipped: ${originalUrl}`;\n logDebug(ctx.debug, msg);\n evt.attrValue = '';\n return;\n }\n ctx.nest++;\n ctx.recurse.add(originalUrl);\n try {\n const sanitized = sanitizer.#process(\n originalUrl,\n {\n allow: ['data'],\n deny: [],\n only: [],\n allowRelative: false\n },\n ctx\n );\n evt.attrValue = sanitized || '';\n } finally {\n ctx.nest--;\n ctx.recurse.delete(originalUrl);\n }\n }\n }\n\n static {\n domPurify.setConfig(INTERNAL_PURIFY_CONFIG);\n domPurify.addHook(\n 'uponSanitizeAttribute',\n URLSanitizer.#uponSanitizeAttribute\n );\n }\n\n constructor() {\n super();\n this.#allowedSchemes = new Set(super.get());\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 {object} 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 if (REG_SCRIPT_BLOB.test(item)) {\n return false;\n }\n const schemeParts = item.split('+');\n const isScript = schemeParts.some(s => REG_SCRIPT.test(s));\n if (isScript || !REG_SCHEME.test(item)) {\n const msg = `Failed to add scheme '${item}' in '${listName}' list.`;\n logDebug(ctx.debug, msg);\n return false;\n }\n schemeMap.set(item, true);\n allowedSchemes.add(item);\n return true;\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 {object} 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 const prevInstance = URLSanitizer.#currentInstance;\n const prevCtx = URLSanitizer.#currentCtx;\n URLSanitizer.#currentInstance = this;\n URLSanitizer.#currentCtx = ctx;\n let purifiedDom;\n try {\n purifiedDom = ctx.domPurify.sanitize(decodedDom);\n } finally {\n URLSanitizer.#currentInstance = prevInstance;\n URLSanitizer.#currentCtx = prevCtx;\n }\n purifiedDom = trimTrailingEmptyQueryAndHash(purifiedDom);\n try {\n return encodeURI(purifiedDom);\n } catch {\n return purifiedDom;\n }\n }\n\n /**\n * Internal recursive method for sanitization.\n * @private\n * @param {string} url - The URL string to sanitize.\n * @param {object} rules - Normalized sanitization rules.\n * @param {object} ctx - Internal context for state.\n * @returns {string|null} The sanitized URL, or null.\n */\n #process(url, rules, 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(rules, 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, rules.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 {object} 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 (only.length) {\n allowedSchemes = new Set();\n for (const item of only) {\n if (isString(item)) {\n const registered = this.#registerScheme(\n item.trim(),\n 'only',\n allowedSchemes,\n schemeMap,\n ctx\n );\n if (registered && !restrictScheme) {\n restrictScheme = true;\n }\n }\n }\n } else {\n if (allow.length) {\n allowedSchemes = new Set(this.#allowedSchemes);\n for (const item of allow) {\n if (isString(item)) {\n this.#registerScheme(\n item.trim(),\n 'allow',\n allowedSchemes,\n schemeMap,\n ctx\n );\n }\n }\n }\n if (deny.length) {\n for (let item of deny) {\n if (isString(item)) {\n item = item.trim();\n if (item) {\n schemeMap.set(item, false);\n }\n }\n }\n }\n }\n return { allowedSchemes, restrictScheme, schemeMap };\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 - If relative URLs are OK.\n * @param {Set<string>} allowedSchemes - Permitted schemes.\n * @param {object} ctx - Context for logging and state.\n * @returns {object} Parsed URL properties and flags.\n */\n #parseAndVerifyURL(url, allowRelative, allowedSchemes, ctx) {\n let isVerified = super.verify(url, allowedSchemes);\n let isRelative = false;\n let relativeParsedPath = '';\n // Handle Relative URLs\n if (!isVerified && allowRelative && !REG_VERIFY_RELATIVE.test(url)) {\n const normUrl = url.normalize('NFKC');\n const dummyUrl = URL.parse(url, 'http://dummy.local');\n const dummyUrlNormalized = URL.parse(normUrl, 'http://dummy.local');\n if (dummyUrl && dummyUrlNormalized) {\n if (\n dummyUrl.protocol === 'http:' &&\n dummyUrl.hostname === 'dummy.local' &&\n dummyUrlNormalized.protocol === 'http:'\n ) {\n isVerified = true;\n isRelative = true;\n relativeParsedPath = `${dummyUrl.pathname}${dummyUrl.search}${dummyUrl.hash}`;\n }\n } else {\n logDebug(ctx.debug, 'Failed to parse relative URL.');\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: relativeParsedPath\n };\n }\n const urlObj = URL.parse(url);\n const scheme = urlObj.protocol.replace(/:$/, '').normalize('NFKC');\n const schemeParts = scheme.split('+');\n return {\n isRelative,\n isVerified,\n scheme,\n schemeParts,\n urlObj,\n isDataUrl: schemeParts.includes('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 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 {object} ctx - Context for DOMPurify sanitization.\n * @returns {string|null} Sanitized data URL or null.\n */\n #sanitizeDataURL(urlObj, scheme, ctx) {\n const [mediaType, ...dataParts] = urlObj.pathname.split(',');\n const data = `${dataParts.join(',')}${urlObj.search}${urlObj.hash}`;\n const mediaTypes = mediaType.split(';');\n const isBase64 = mediaTypes[mediaTypes.length - 1] === 'base64';\n let parsedData = data;\n if (isBase64) {\n try {\n parsedData = parseBase64(data);\n } catch (e) {\n logDebug(ctx.debug, 'Failed to parse base64 data.', e);\n return null;\n }\n }\n try {\n const decodedData = parseURLEncodedNumCharRef(parsedData).trim();\n const normalizedData = decodedData.normalize('NFKC');\n const dummy = 'http://dummy.local';\n const parsedUrl = URL.parse(normalizedData, dummy);\n if (!parsedUrl) {\n logDebug(ctx.debug, 'Failed to parse inner data URL protocol.');\n return null;\n }\n const dataScheme = parsedUrl.protocol;\n const dataSchemeParts = dataScheme.replace(/:$/, '').split('+');\n if (dataSchemeParts.some(s => REG_SCRIPT_BLOB.test(s))) {\n return null;\n }\n } catch (e) {\n logDebug(ctx.debug, 'Failed to parse inner data URL protocol.', e);\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 * 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 if (REG_TAG_QUOT.test(sanitized)) {\n const { index } = REG_TAG_QUOT.exec(sanitized);\n sanitized = sanitized.substring(0, index).replace(/[?&]$/, '');\n }\n if (REG_AMP_ENC.test(sanitized)) {\n const { index } = REG_AMP_ENC.exec(sanitized);\n sanitized = sanitized.substring(0, index).replace(/[?&]$/, '');\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] - Flag to safely 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 if (!url || !isString(url)) {\n return null;\n }\n const maxLength =\n Number.isInteger(opt?.maxLength) && opt.maxLength ? opt.maxLength : 0;\n if (maxLength && url.length > maxLength) {\n const msg = `URL length ${url.length} exceeds maxLength ${maxLength}.`;\n throw new RangeError(msg);\n }\n const hasRestrictiveRules =\n opt &&\n ((Array.isArray(opt.deny) && opt.deny.length > 0) ||\n (Array.isArray(opt.only) && opt.only.length > 0) ||\n opt.allowRelative);\n if (\n !hasRestrictiveRules &&\n (url.startsWith('https://') || url.startsWith('http://')) &&\n !REG_TAG_QUOT.test(url) &&\n !url.includes('data:')\n ) {\n const parsedUrl = URL.parse(url);\n if (parsedUrl) {\n return parsedUrl.href.replace(/%26/g, escapeURLEncodedHTMLChars);\n } else {\n logDebug(opt?.debug, `Failed to parse URL.`);\n return null;\n }\n }\n const rules = {\n allow: Array.isArray(opt?.allow) ? opt.allow : [],\n deny: Array.isArray(opt?.deny) ? opt.deny : [],\n only: Array.isArray(opt?.only) ? opt.only : [],\n allowRelative: !!opt?.allowRelative\n };\n const ctx = {\n debug: !!opt?.debug,\n domPurify,\n nest: 0,\n recurse: null,\n schemeMap: new Map([\n ['blob', false],\n ['data', false],\n ['file', false],\n ['javascript', false],\n ['vbscript', false]\n ])\n };\n return this.#process(url, rules, ctx);\n }\n\n /**\n * Inspects, parses, and sanitizes the given URL.\n * NOTE: blob URLs are simply parsed, but neither decoded nor sanitized.\n * @param {string} url - The URL string to parse.\n * @param {object} [opt] - Sanitization options.\n * @returns {InspectedURLResult} The result of an inspected URL.\n */\n inspect(url, opt) {\n if (!isString(url)) {\n throw new TypeError(`Expected String but got ${getType(url)}.`);\n }\n const inspectedURL = {\n input: url,\n valid: false\n };\n let sanitizedUrl;\n let invalidReason = null;\n const maxLength =\n Number.isInteger(opt?.maxLength) && opt.maxLength ? opt.maxLength : 0;\n const urlLength = url.length;\n if (maxLength && urlLength > maxLength) {\n invalidReason = `URL length ${urlLength} exceeds maxLength ${maxLength}.`;\n } else if (this.verify(url)) {\n const { protocol } = new URL(url);\n if (protocol === 'blob:') {\n sanitizedUrl = url;\n } else {\n try {\n sanitizedUrl = this.sanitize(url, opt ?? { allow: ['data'] });\n if (!sanitizedUrl) {\n invalidReason =\n 'Sanitization failed (blocked by allowed schemes or rules).';\n }\n } catch (e) {\n invalidReason = e.message;\n }\n }\n } else {\n invalidReason = 'Invalid URI syntax or scheme is not registered.';\n }\n if (sanitizedUrl) {\n const urlObj = new URL(sanitizedUrl);\n const { pathname, protocol } = urlObj;\n const schemeParts = protocol.replace(/:$/, '').split('+');\n const isDataUrl = schemeParts.includes('data');\n inspectedURL.valid = true;\n if (isDataUrl) {\n const [mediaType, ...dataParts] = pathname.split(',');\n const data = `${dataParts.join(',')}${urlObj.search}${urlObj.hash}`;\n const mediaTypes = mediaType.split(';');\n const isBase64 = mediaTypes[mediaTypes.length - 1] === 'base64';\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 inspectedURL.valid = false;\n if (invalidReason) {\n inspectedURL.reason = invalidReason;\n }\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 if (!isString(scheme)) {\n return false;\n }\n return this.#allowedSchemes.has(scheme.trim().toLowerCase());\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 let loweredScheme;\n if (isString(scheme)) {\n loweredScheme = scheme.trim().toLowerCase();\n } else {\n throw new TypeError(`Expected String but got ${getType(scheme)}.`);\n }\n const schemeParts = loweredScheme.split('+');\n const isScript = schemeParts.some(s => REG_SCRIPT.test(s));\n if (isScript || !REG_SCHEME.test(loweredScheme)) {\n throw new Error(`Invalid scheme: ${scheme}`);\n }\n this.#allowedSchemes.add(loweredScheme);\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 return this.#allowedSchemes.delete(scheme.trim().toLowerCase());\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 = this.#allowedSchemes) {\n return super.verify(uri, schemes);\n }\n}\nexport { URLSanitizer };\n\n/* 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 (\n url,\n opt = {\n allow: [],\n deny: [],\n only: [],\n allowRelative: false,\n debug: false,\n revokeObjectURL: false,\n maxBlobSize: MAX_BLOB_SIZE\n }\n) => {\n const isDebug = !!opt?.debug;\n const maxBlobSize =\n Number.isInteger(opt?.maxBlobSize) && opt.maxBlobSize > 0\n ? opt.maxBlobSize\n : MAX_BLOB_SIZE;\n let res;\n if (url && isString(url)) {\n let scheme;\n const parsedUrl = URL.parse(url);\n if (parsedUrl) {\n scheme = parsedUrl.protocol.replace(/:$/, '');\n } else {\n logDebug(isDebug, `Invalid URL input format: ${url}`);\n }\n if (scheme === 'blob') {\n const { allow, deny, only } = opt;\n if (\n (Array.isArray(allow) &&\n allow.includes('blob') &&\n !(Array.isArray(deny) && deny.includes('blob'))) ||\n (Array.isArray(only) && only.includes('blob'))\n ) {\n let data;\n try {\n data = await fetch(url)\n .then(r => r.blob())\n .then(b => convertBlobToDataURL(b, maxBlobSize));\n } catch (e) {\n const msg = `Failed to fetch and convert blob URL: ${url}`;\n logDebug(isDebug, msg, e);\n }\n if (data) {\n const options = { ...opt };\n if (Array.isArray(opt.only)) {\n options.only = opt.only.includes('data')\n ? opt.only\n : [...opt.only, 'data'];\n } else if (Array.isArray(opt.allow)) {\n options.allow = opt.allow.includes('data')\n ? opt.allow\n : [...opt.allow, 'data'];\n if (Array.isArray(opt.deny)) {\n options.deny = opt.deny.filter(scheme => scheme !== 'data');\n }\n }\n res = urlSanitizer.sanitize(data, options);\n }\n }\n if (opt?.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n } else if (scheme || opt.allowRelative) {\n res = urlSanitizer.sanitize(url, opt);\n }\n }\n return res || null;\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 = (\n url,\n opt = {\n allow: [],\n deny: [],\n only: [],\n allowRelative: false,\n debug: false,\n revokeObjectURL: false\n }\n) => {\n const isDebug = !!opt?.debug;\n let res;\n if (url && isString(url)) {\n let scheme;\n const parsedUrl = URL.parse(url);\n if (parsedUrl) {\n scheme = parsedUrl.protocol.replace(/:$/, '');\n } else {\n logDebug(isDebug, `Invalid URL input format: ${url}`);\n }\n if (scheme === 'blob') {\n if (opt?.revokeObjectURL) {\n URL.revokeObjectURL(url);\n }\n return null;\n } else if (scheme || opt.allowRelative) {\n res = urlSanitizer.sanitize(url, opt);\n }\n }\n return res || null;\n};\n\n/**\n * Inspects, parses, and sanitizes the given URL asynchronously.\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 const res = urlSanitizer.inspect(url);\n return res;\n};\n\n/**\n * @deprecated Use {@link inspectURL} instead.\n * @param {string} url - The URL string to inspect.\n * @returns {Promise<InspectedURLResult>} A promise resolving to the inspected URL result.\n */\nexport const parseURL = async url => inspectURL(url);\n\n/**\n * Inspects, parses, and sanitizes the given URL synchronously.\n * @param {string} url - The URL string to inspect.\n * @returns {InspectedURLResult} The inspected URL result.\n */\nexport const inspectURLSync = url => urlSanitizer.inspect(url);\n\n/**\n * @deprecated Use {@link inspectURLSync} instead.\n * @param {string} url - The URL string to inspect.\n * @returns {InspectedURLResult} The inspected URL result.\n */\nexport const parseURLSync = url => inspectURLSync(url);\n\n/**\n * Asynchronously checks if the given string is a valid URI and is registered.\n * @param {string} uri - The URI string to verify.\n * @returns {Promise<boolean>} True if valid and registered, false otherwise.\n */\nexport const isURI = async uri => {\n const res = urlSanitizer.verify(uri);\n return res;\n};\n\n/**\n * Synchronously 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 isURISync = uri => urlSanitizer.verify(uri);\n\n/* export instance */\nexport default urlSanitizer;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["PROPERTIES", "hookQueue", "domPurify", "target", "prop", "purifier", "p", "args", "value", "getType", "o", "isString", "uri_schemes_default", "REG_HASH", "REG_MIME_DOM", "REG_NUM_REF", "REG_QUERY", "REG_SCHEME", "REG_SCHEME_EXT", "REG_SCRIPT", "
|
|
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": "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",
|
|
6
|
+
"names": ["PROPERTIES", "hookQueue", "domPurify", "target", "prop", "receiver", "purifier", "p", "args", "value", "getType", "o", "isString", "uri_schemes_default", "REG_HASH", "REG_MIME_DOM", "REG_NUM_REF", "REG_QUERY", "REG_SCHEME", "REG_SCHEME_EXT", "REG_SCRIPT", "REG_SCRIPT_OR_BLOB", "REG_TAG_QUOT", "REG_URL_ENC", "REG_PCT_ENC", "REG_AMP", "REG_AMP_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", "encoder", "decoder", "URISchemes", "#schemes", "uri_schemes_default", "uri", "isString", "parsed", "scheme", "normalized", "isScheme", "base", "schemes", "parsedUrl", "allowCustom", "parts", "s", "REG_SCRIPT", "REG_SCHEME_EXT", "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", "data", "mediaTypes", "isBase64", "parseBase64", "cleanData", "binStr", "bytes", "IS_NODE", "len", "i", "text", "decoder", "REG_NON_TEXT", "DUMMY_BASE", "IS_NODE", "URL_PROPS", "DEFAULT_OPTS", "logDebug", "message", "error", "SanitizeContext", "opt", "domPurifyInstance", "url", "truncateURL", "URLSanitizer", "URISchemes", "#allowedSchemes", "#executeSanitize", "isString", "allow", "allowRelative", "deny", "maxLength", "only", "msg", "REG_TAG_QUOT", "urlObj", "escapeURLEncodedHTMLChars", "ctx", "domPurify", "#process", "allowedSchemes", "restrictScheme", "schemeMap", "#resolveSchemeRules", "isDataUrl", "isRelative", "isVerified", "scheme", "schemeParts", "urlToSanitize", "#parseAndVerifyURL", "#isSchemeAllowed", "#sanitizeDataURL", "#sanitizeStandardURL", "item", "#registerScheme", "normalized", "listName", "normalizedScheme", "#isValidScheme", "REG_SCRIPT_OR_BLOB", "s", "REG_SCRIPT", "REG_SCHEME", "relativePath", "REG_VERIFY_RELATIVE", "dummyUrl", "key", "value", "mediaType", "mediaTypes", "data", "isBase64", "extractDataUrlComponents", "parsedData", "parseBase64", "e", "decodedData", "parseURLEncodedNumCharRef", "parsedUrl", "dataScheme", "getSchemeParts", "REG_MIME_DOM", "#purify", "dom", "decodedDom", "purifiedDom", "tempHook", "node", "evt", "#handleSanitizeAttribute", "trimTrailingEmptyQueryAndHash", "originalUrl", "sanitized", "patterns", "REG_AMP_ENC", "pattern", "match", "options", "inspectedURL", "sanitizedUrl", "invalidReason", "getType", "uri", "schemes", "convertFromBuffer", "blob", "mimeStr", "buffer", "base64", "convertFromFileReader", "resolve", "reject", "reader", "convertFromBtoa", "uint8arr", "chunks", "i", "readStreamInChunks", "maxSize", "accumulatedSize", "done", "newSize", "fetchBlobAsDataURL", "maxBlobSize", "response", "contentLength", "parsedLength", "type", "urlSanitizer", "sanitizeURL", "res", "sanitizeURLSync", "inspectURL", "dataUrl", "inspectedURLResult", "isValidURI", "isURISync", "sanitizer_default"]
|
|
7
7
|
}
|