url-sanitizer 2.0.13 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,77 +5,121 @@
5
5
  [![npm](https://img.shields.io/npm/v/url-sanitizer)](https://www.npmjs.com/package/url-sanitizer)
6
6
  [![release](https://img.shields.io/github/v/release/asamuzaK/urlSanitizer)](https://github.com/asamuzaK/urlSanitizer/releases)
7
7
 
8
- URL sanitizer for Node.js, browsers and web sites.
9
- Sanitize not only regular URLs, but also data URLs and blob URLs.
10
- It also has the ability to parse URLs and verify URIs.
11
-
8
+ A robust URL sanitizer for Node.js, browsers, and websites.
9
+ It sanitizes not only regular URLs but also deeply inspects `data` URLs and `blob` URLs.
10
+ It also provides built-in utilities to parse URLs and verify URI schemes.
11
+
12
+ ## Features
13
+
14
+ * **Secure by Default**: Automatically denies `javascript:` and `vbscript:` schemes.
15
+ * **Deep Data URL Inspection**: Parses, decodes (including base64), and sanitizes nested data URLs.
16
+ * **Blob URL Support**: Converts and sanitizes blob URLs into safe data URLs (async).
17
+ * **DOMPurify Integration**: Safely purifies HTML/SVG content embedded within data URLs.
18
+ * **Pure ESM with TypeScript Support**: Works seamlessly across modern environments like Node.js, Deno, websites, and browsers (including WebExtensions).
19
+
20
+ ## Table of Contents
21
+ * [Install](#install)
22
+ * [Usage](#usage)
23
+ * [API Reference](#api-reference)
24
+ * [sanitizeURL(url, opt)](#sanitizeurlurl-opt)
25
+ * [sanitizeURLSync(url, opt)](#sanitizeurlsyncurl-opt)
26
+ * [parseURL(url)](#parseurlurl)
27
+ * [parseURLSync(url)](#parseurlsyncurl)
28
+ * [isURI(uri)](#isuriuri)
29
+ * [isURISync(uri)](#isurisyncuri)
30
+ * [urlSanitizer Instance](#urlsanitizer)
31
+ * [Acknowledgments](#acknowledgments)
12
32
 
13
33
  ## Install
14
34
 
15
- ```console
35
+ ### Node.js
36
+
37
+ ``` console
16
38
  npm i url-sanitizer
17
39
  ```
18
40
 
19
- For browsers and web sites, standalone ESM builds are available in `dist/` directory.
20
- * node_modules/url-sanitizer/dist/url-sanitizer.min.js
21
- * node_modules/url-sanitizer/dist/url-sanitizer-wo-dompurify.min.js
41
+ ### Deno
42
+
43
+ You can import directly in your code without installing:
44
+ ``` javascript
45
+ import urlSanitizer from "npm:url-sanitizer";
46
+ ```
22
47
 
23
- Or, download them from [Releases](https://github.com/asamuzaK/urlSanitizer/releases).
48
+ ### Browsers and Websites
24
49
 
25
- NOTE: `url-sanitizer-wo-dompurify.min.js` is built without [DOMPurify](https://www.npmjs.com/package/dompurify).
26
- If you use it, make sure DOMPurify is exposed globally, e.g. `window.DOMPurify`.
50
+ Standalone ESM builds are available in the `dist/` directory:
27
51
 
52
+ * node_modules/url-sanitizer/dist/url-sanitizer.min.js
53
+ * node_modules/url-sanitizer/dist/url-sanitizer-wo-dompurify.min.js
54
+
55
+ Alternatively, download them from [Releases](https://github.com/asamuzaK/urlSanitizer/releases).
56
+ **NOTE:** url-sanitizer-wo-dompurify.min.js is a lightweight build without [DOMPurify](https://www.npmjs.com/package/dompurify) bundled.
57
+ If you use this build, ensure DOMPurify is exposed globally (e.g., `window.DOMPurify`), otherwise the sanitizer will throw an error at runtime.
28
58
 
29
59
  ## Usage
30
60
 
31
- ```javascript
61
+ ``` javascript
32
62
  import urlSanitizer, {
33
63
  isURI, isURISync, parseURL, parseURLSync, sanitizeURL, sanitizeURLSync
34
64
  } from 'url-sanitizer';
35
65
  ```
36
66
 
67
+ ## API Reference
68
+
69
+ ### sanitizeURL(url, opt)
37
70
 
38
- ## sanitizeURL(url, opt)
71
+ Sanitizes the given URL asynchronously.
39
72
 
40
- Sanitize the given URL.
41
- * `blob`, `data` and `file` schemes must be explicitly allowed.
42
- * Given a `blob` URL, returns a sanitized `data` URL.
73
+ * `blob`, `data`, and `file` schemes must be explicitly allowed.
74
+ * Given a blob URL, it returns a sanitized **data** URL.
43
75
 
44
- ### Parameters
76
+ #### Parameters
77
+
78
+ * url **string** URL input.
79
+ * opt **object?** Options.
80
+ * opt.allow **Array<string>?** Array of allowed schemes, e.g., ['data'].
81
+ * opt.deny **Array<string>?** Array of denied schemes, e.g., ['web+foo'].
82
+ * opt.only **Array<string>?** Array of specific schemes to allow, e.g., ['git', 'https'].
83
+ `only` takes precedence over `allow` and `deny`.
84
+ #### Parameters
45
85
 
46
- * `url` **[string][1]** URL input.
47
- * `opt` **[object][3]?** Options.
48
- * `opt.allow` **[Array][4]<[string][1]>?** Array of allowed schemes, e.g. `['data']`.
49
- * `opt.deny` **[Array][4]<[string][1]>?** Array of denied schemes, e.g. `['web+foo']`.
50
- * `opt.only` **[Array][4]<[string][1]>?** Array of specific schemes to allow, e.g. `['git', 'https']`.
86
+ * url **string** URL input.
87
+ * opt **object?** Options.
88
+ * opt.allow **Array<string>?** Array of allowed schemes, e.g., ['data'].
89
+ * opt.deny **Array<string>?** Array of denied schemes, e.g., ['web+foo'].
90
+ * opt.only **Array<string>?** Array of specific schemes to allow, e.g., ['git', 'https'].
51
91
  `only` takes precedence over `allow` and `deny`.
92
+ * opt.debug **boolean?** If `true`, outputs internal error/warning logs to the console. Default is `false`.
93
+ * opt.maxBlobSize **number?** Maximum allowed blob size in bytes. Default is `33554432` (32MB). Exceeding this limit will result in parsing failure to prevent memory exhaustion.
52
94
 
53
- Returns **[Promise][5]<[string][1]?>** Sanitized URL, `null`able.
95
+ **Returns** **Promise<string?>** Sanitized URL, nullable.
54
96
 
55
- ```javascript
56
- // Sanitize tags and quotes
97
+ #### Samples
98
+
99
+ Sanitize tags and quotes:
100
+ ``` javascript
57
101
  const res1 = await sanitizeURL('https://example.com/?<script>alert(1)</script>');
58
102
  // => 'https://example.com/'
59
103
 
60
104
  const res1_2 = await sanitizeURL('https://example.com/" onclick="alert(1)"');
61
105
  // => 'https://example.com/'
106
+ ```
62
107
 
63
-
64
- // Can parse and sanitize data URL
108
+ Parse and sanitize data URLs:
109
+ ``` javascript
65
110
  const res2 = await sanitizeURL('data:text/html,<div><script>alert(1);</script></div><p onclick="alert(2)"></p>', {
66
111
  allow: ['data']
67
- })
112
+ });
68
113
  // => 'data:text/html,%3Cdiv%3E%3C/div%3E%3Cp%3E%3C/p%3E'
69
114
 
70
115
  console.log(decodeURIComponent(res2));
71
116
  // => 'data:text/html,<div></div><p></p>'
72
117
 
73
-
74
- // Also can parse and sanitize base64 encoded data
118
+ // Also can parse and sanitize base64 encoded data:
75
119
  const base64data3 = btoa('<div><script>alert(1);</script></div>');
76
120
  const res3 = await sanitizeURL(`data:text/html;base64,${base64data3}`, {
77
121
  allow: ['data']
78
- })
122
+ });
79
123
  // => 'data:text/html,%3Cdiv%3E%3C/div%3E'
80
124
 
81
125
  console.log(decodeURIComponent(res3));
@@ -87,9 +131,10 @@ const res3_2 = await sanitizeURL(`data:text/html;base64,${base64data3_2}`);
87
131
 
88
132
  console.log(decodeURIComponent(res3_2));
89
133
  // => 'data:text/html,<div><img></div>'
134
+ ```
90
135
 
91
-
92
- // Can parse and sanitize blob URL
136
+ Parse and sanitize blob URLs:
137
+ ``` javascript
93
138
  const blob4 = new Blob(['<svg><g onload="alert(1)"/></svg>'], {
94
139
  type: 'image/svg+xml'
95
140
  });
@@ -101,16 +146,18 @@ const res4 = await sanitizeURL(url4, {
101
146
 
102
147
  console.log(decodeURIComponent(res4));
103
148
  // => 'data:image/svg+xml,<svg><g></g></svg>'
149
+ ```
104
150
 
105
-
106
- // Deny if the scheme matches the `deny` list
151
+ Denies if the scheme matches the `deny` list:
152
+ ``` javascript
107
153
  const res5 = await sanitizeURL('web+foo://example.com', {
108
154
  deny: ['web+foo']
109
155
  });
110
156
  // => null
157
+ ```
111
158
 
112
-
113
- // Allow only if the scheme matches the `only` list
159
+ Allows only if the scheme matches the `only` list:
160
+ ``` javascript
114
161
  const res6 = await sanitizeURL('http://example.com', {
115
162
  only: ['data', 'git', 'https']
116
163
  });
@@ -121,7 +168,6 @@ const res6_2 = await sanitizeURL('https://example.com/"onmouseover="alert(1)"',
121
168
  });
122
169
  // => 'https://example.com/'
123
170
 
124
-
125
171
  // `only` also allows combination of the schemes in the list
126
172
  const res7 = await sanitizeURL('git+https://example.com/foo.git?<script>alert(1)</script>', {
127
173
  only: ['data', 'git', 'https']
@@ -129,53 +175,38 @@ const res7 = await sanitizeURL('git+https://example.com/foo.git?<script>alert(1)
129
175
  // => 'git+https://example.com/foo.git'
130
176
  ```
131
177
 
178
+ ### sanitizeURLSync(url, opt)
132
179
 
133
- ## sanitizeURLSync(url, opt)
180
+ Synchronous version of `sanitizeURL()`.
134
181
 
135
- Synchronous version of the [sanitizeURL()](#sanitizeurlurl-opt).
136
182
  * `data` and `file` schemes must be explicitly allowed.
137
- * `blob` scheme is not supported, returns `null`.
138
- Use async [sanitizeURL()](#sanitizeurlurl-opt) for `blob`.
139
-
140
-
141
- ## parseURL(url)
183
+ * `blob` scheme is not supported and will return `null`. Use the async version for `blob`.
142
184
 
143
- Parse the given URL.
144
- * Blob URLs are simply parsed and not yet sanitized.
185
+ ### parseURL(url)
145
186
 
146
- ### Parameters
187
+ Parses the given URL asynchronously.
147
188
 
148
- * `url` **[string][1]** URL input.
189
+ * Blob URLs are simply parsed and not yet sanitized at this stage.
149
190
 
150
- Returns **[Promise][5]<[ParsedURL](#parsedurl)>** Result.
191
+ #### Parameters
151
192
 
152
- ### ParsedURL
193
+ * url **string** URL input.
153
194
 
154
- Object with additional properties based on [URL](https://developer.mozilla.org/ja/docs/Web/API/URL) API.
195
+ **Returns** **Promise<ParsedURL>** Result.
155
196
 
156
- Type: [object][3]
197
+ #### ParsedURL
157
198
 
158
- #### Properties
199
+ An object with additional properties based on the standard [URL](https://developer.mozilla.org/ja/docs/Web/API/URL) API.
159
200
 
160
- * `input` **[string][1]** URL input.
161
- * `valid` **[boolean][2]** Is valid URI.
162
- * `data` **[object][3]?** Parsed result of data URL, `null`able.
163
- * `data.mime` **[string][1]?** MIME type.
164
- * `data.base64` **[boolean][2]?** Is base64 encoded.
165
- * `data.data` **[string][1]?** Data part of the data URL.
166
- * `href` **[string][1]?** Sanitized URL input.
167
- * `origin` **[string][1]?** Scheme, domain and port of the sanitized URL.
168
- * `protocol` **[string][1]?** Protocol scheme of the sanitized URL.
169
- * `username` **[string][1]?** Username specified before the domain name.
170
- * `password` **[string][1]?** Password specified before the domain name.
171
- * `host` **[string][1]?** Domain and port of the sanitized URL.
172
- * `hostname` **[string][1]?** Domain of the sanitized URL.
173
- * `port` **[string][1]?** Port number of the sanitized URL.
174
- * `pathname` **[string][1]?** Path of the sanitized URL.
175
- * `search` **[string][1]?** Query string of the sanitized URL.
176
- * `hash` **[string][1]?** Fragment identifier of the sanitized URL.
201
+ * `input` **string** URL input.
202
+ * `valid` **boolean** Is valid URI.
203
+ * `data` **object?** Parsed result of data URL, nullable.
204
+ * `data.mime` **string?** MIME type.
205
+ * `data.base64` **boolean?** Is base64 encoded.
206
+ * `data.data` **string?** Data part of the data URL.
207
+ * `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, `pathname`, `search`, `hash` — **string?** Properties same as the standard URL API.
177
208
 
178
- ```javascript
209
+ ``` javascript
179
210
  const res1 = await parseURL('javascript:alert(1)');
180
211
  /* => {
181
212
  input: 'javascript:alert(1)',
@@ -249,25 +280,24 @@ const res5 = await parseURL(url5);
249
280
  } */
250
281
  ```
251
282
 
283
+ ### parseURLSync(url)
252
284
 
253
- ## parseURLSync(url)
254
-
255
- Synchronous version of the [parseURL()](#parseURL).
285
+ Synchronous version of `parseURL()`.
256
286
 
287
+ ### isURI(uri)
257
288
 
258
- ## isURI(uri)
289
+ Verifies if the given URI is valid and registered.
259
290
 
260
- Verify if the given URI is valid and registered.
291
+ #### Parameters
261
292
 
262
- ### Parameters
293
+ * uri **string** URI input.
263
294
 
264
- * `uri` **[string][1]** URI input.
295
+ **Returns** **Promise<boolean>** Result.
265
296
 
266
- Returns **[Promise][5]<[boolean][2]>** Result.
267
- * Always `true` for `web+*` and `ext+*` schemes, except `web+javascript`, `web+vbscript`, `ext+javascript`, `ext+vbscript`.
297
+ * Always `true` for `web+*` and `ext+*` schemes (except `web+javascript`, `web+vbscript`, `ext+javascript`, and `ext+vbscript`).
268
298
  * `false` for `javascript` and `vbscript` schemes.
269
299
 
270
- ```javascript
300
+ ``` javascript
271
301
  const res1 = await isURI('https://example.com/foo');
272
302
  // => true
273
303
 
@@ -287,122 +317,45 @@ const res6 = await isURI('web+javascript:alert(1)');
287
317
  // => false
288
318
  ```
289
319
 
320
+ ### isURISync(uri)
290
321
 
291
- ## isURISync(uri)
292
-
293
- Synchronous version of the [isURI()](#isuriuri).
322
+ Synchronous version of `isURI()`.
294
323
 
295
- ---
324
+ ### urlSanitizer
296
325
 
297
- ## urlSanitizer
326
+ The instance of the sanitizer.
298
327
 
299
- Instance of the sanitizer.
328
+ #### urlSanitizer.get()
300
329
 
301
- ### urlSanitizer.get()
330
+ Get a list of registered URI schemes (includes [IANA registered schemes](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml) by default).
302
331
 
303
- Get a list of registered URI schemes.
332
+ * Historical schemes are omitted.
333
+ * `moz-extension` scheme is added.
304
334
 
305
- Returns **[Array][4]<[string][1]>** Array of registered URI schemes.
306
- * Includes schemes registered at [iana.org](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml "Uniform Resource Identifier (URI) Schemes") by default.
307
- * Historical schemes omitted.
308
- * `moz-extension` scheme added.
309
- * Also includes custom schemes added via [urlSanitizer.add()](#urlsanitizeraddscheme).
310
-
311
- ```javascript
312
- const schemes = urlSanitizer.get();
313
- // => ['aaa', 'aaas', 'about', 'acap', 'acct', ...]
314
- ```
315
-
316
- ### urlSanitizer.has(scheme)
335
+ #### urlSanitizer.has(scheme)
317
336
 
318
337
  Check if the given scheme is registered.
319
338
 
320
- #### Parameters
321
-
322
- * `scheme` **[string][1]** Scheme.
339
+ #### urlSanitizer.add(scheme)
323
340
 
324
- Returns **[boolean][2]** Result.
341
+ Add a scheme to the list of registered URI schemes (throws if `javascript` or `vbscript`).
325
342
 
326
- ```javascript
327
- const res1 = urlSanitizer.has('https');
328
- // => true
329
-
330
- const res2 = urlSanitizer.has('foo');
331
- // => false
332
- ```
333
-
334
- ### urlSanitizer.add(scheme)
335
-
336
- Add a scheme to the list of registered URI schemes.
337
- * `javascript` and `vbscript` schemes can not be registered. It throws.
338
-
339
- #### Parameters
340
-
341
- * `scheme` **[string][1]** Scheme.
342
-
343
- Returns **[Array][4]<[string][1]>** Array of registered URI schemes.
344
-
345
- ```javascript
346
- console.log(urlSanitizer.has('foo'));
347
- // => false
348
-
349
- const res = urlSanitizer.add('foo');
350
- // => ['aaa', 'aaas', 'about', 'acap', ... 'foo', ...]
351
-
352
- console.log(urlSanitizer.has('foo'));
353
- // => true
354
- ```
355
-
356
- ### urlSanitizer.remove(scheme)
343
+ #### urlSanitizer.remove(scheme)
357
344
 
358
345
  Remove a scheme from the list of registered URI schemes.
359
346
 
360
- #### Parameters
361
-
362
- * `scheme` **[string][1]** Scheme.
363
-
364
- Returns **[boolean][2]** Result.
365
- * `true` if the scheme is successfully removed, `false` otherwise.
347
+ #### urlSanitizer.reset()
366
348
 
367
- ```javascript
368
- console.log(urlSanitizer.has('aaa'));
369
- // => true
370
-
371
- const res1 = urlSanitizer.remove('aaa');
372
- // => true
373
-
374
- console.log(urlSanitizer.has('aaa'));
375
- // => false
376
-
377
- const res2 = urlSanitizer.remove('foo');
378
- // => false
379
- ```
380
-
381
- ### urlSanitizer.reset()
382
-
383
- Reset sanitizer.
384
-
385
- Returns **void**
386
-
387
- ---
349
+ Reset registered schemes to the default list and clear internal cache.
388
350
 
389
351
  ## Acknowledgments
390
352
 
391
- The following resources have been of great help in the development of the URL Sanitizer.
353
+ The following resources have been of great help in the development of the URL Sanitizer:
392
354
 
393
355
  * [DOMPurify](https://www.npmjs.com/package/dompurify)
394
356
  * [Uniform Resource Identifier (URI) Schemes - IANA](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml)
395
357
  * [Encoding -- determine the character encoding of a text file. - file/file](https://github.com/file/file/blob/master/src/encoding.c)
396
358
 
397
359
  ---
398
- Copyright (c) 2023 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
399
-
400
- [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
401
360
 
402
- [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
403
-
404
- [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
405
-
406
- [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
407
-
408
- [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
361
+ Copyright (c) 2023 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
@@ -1,4 +1,4 @@
1
- var{DOMPurify:N}=globalThis;var H=/^[\w+/=-]+$/;var S=/data:[\w#&+./;=-]*,/,D=/data:[\w#&+./;=^]*base64,[\w+/=-]+/i,q=/data:[\w#&+./;=-]*,[^\x22]+/g,X=/&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/gi,Y=/^(?:application\/(?:[\w#&.;-]+\+)?x|image\/svg\+x|text\/(?:ht|x))ml;?/;var Q=/^[a-z][\da-z+.-]*$/,J=/^(?:ext|web)\+[a-z]+$/,j=/(?:java|vb)script/,A=/(?:java|vb)script|blob/,M=/%(?:22|27|3C|3E)|[\x22\x27<>]/,K=/^%[\dA-F]{2}$/i;var g=t=>Object.prototype.toString.call(t).slice(8,-1),d=t=>typeof t=="string"||t instanceof String;var{FileReader:V}=globalThis;var L=[7,8,9,10,11,12,13,27,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,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,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];var C=["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","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","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","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","mvn","mvrp","mvrps","news","nfs","ni","nih","nntp","notes","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","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","turn","turns","tv","udp","unreal","upn","urn","ut2004","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"];var W=t=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);let e=[];for(let s of t)e.push(`%${s.charCodeAt(0).toString(16).toUpperCase()}`);return e.join("")},Z=t=>{d(t)&&K.test(t)&&(t=t.toUpperCase());let[e,s,r,i,o,a]=["&","#","<",">",'"',"'"].map(W),c;return t===e?c=`${e}amp;`:t===r?c=`${e}lt;`:t===i?c=`${e}gt;`:t===o?c=`${e}quot;`:t===a?c=`${e}${s}39;`:c=t,c},ee=t=>{if(d(t)){if(!H.test(t))throw new Error(`Invalid base64 data: ${t}`)}else throw new TypeError(`Expected String but got ${g(t)}.`);let e=atob(t),s=Uint8Array.from([...e].map(o=>o.charCodeAt(0))),r=new Set(L),i;return s.every(o=>r.has(o))?i=e.replace(/\s/g,W):i=t,i},P=(t,e=0)=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);if(Number.isInteger(e)){if(e>16)throw new Error("Character references nested too deeply.")}else throw new TypeError(`Expected Number but got ${g(e)}.`);let s=decodeURIComponent(t);if(/&#/.test(s)){let r=new Set(L),i=[...s.matchAll(X)].reverse();for(let o of i){let[a,c]=o,l;if(/^x[\dA-F]+/i.test(c)?l=parseInt(`0${c}`,16):/^\d+/.test(c)&&(l=parseInt(c)),Number.isInteger(l)){let{index:u}=o,[p,n]=[s.substring(0,u),s.substring(u+a.length)];r.has(l)?(s=`${p}${String.fromCharCode(l)}${n}`,(/#x?$/.test(p)||/^#(?:x(?:00)?[2-7]|\d)/.test(n))&&(s=P(s,++e))):l<256&&(s=`${p}${n}`)}}}return s},te=t=>new Promise((e,s)=>{let r=new V;r.addEventListener("error",()=>s(r.error)),r.addEventListener("abort",()=>e(r.result)),r.addEventListener("load",()=>e(r.result)),r.readAsDataURL(t)}),k=class{#e;constructor(){this.#e=new Set(C)}get(){return[...this.#e]}has(e){return this.#e.has(e)}add(e){if(d(e)){if(j.test(e)||!Q.test(e))throw new Error(`Invalid scheme: ${e}`)}else throw new TypeError(`Expected String but got ${g(e)}.`);return this.#e.add(e),[...this.#e]}remove(e){return this.#e.delete(e)}verify(e){let s;if(d(e))try{let{protocol:r}=new URL(e),i=r.replace(/:$/,""),o=i.split("+");s=!j.test(i)&&J.test(i)||o.every(a=>this.#e.has(a))}catch{s=!1}return!!s}reset(){this.#e.clear();for(let e of C)this.#e.add(e)}};var O=class extends k{#e;#t=new Set;constructor(){super(),this.#e=0}replace(e){if(!d(e))throw new TypeError(`Expected String but got ${g(e)}.`);let s=e;if(S.test(s)){let i=[...s.matchAll(q)].reverse();for(let o of i){let[a]=o;D.test(a)&&([a]=D.exec(a)),this.#e++,this.#t.add(a);let c=this.sanitize(a,{allow:["data"]}),{index:l}=o,[u,p]=[s.substring(0,l),s.substring(l+a.length)];c?s=`${u}${c}${p}`:s=`${u}${p}`}}return s}purify(e){if(!d(e))throw new TypeError(`Expected String but got ${g(e)}.`);let s=N.sanitize(decodeURIComponent(e));return s&&S.test(s)&&(s=this.replace(s)),s=s.replace(/(?:#|%23)$/,"").replace(/(?<!(?:#|%23).*)(?:\?|%3F)$/,""),encodeURI(s)}sanitize(e,s){if(this.#e>16)throw this.#e=0,new Error("Data URLs nested too deeply.");let{allow:r,deny:i,only:o}=s??{},a=new Map([["blob",!1],["data",!1],["file",!1],["javascrpt",!1],["vbscript",!1]]),c=new Set,l=!1;if(Array.isArray(o)&&o.length){let p=super.get();for(let m of p)a.set(m,!1);let n=Object.values(o);for(let m of n)if(d(m)&&(m=m.trim(),!A.test(m))){if(super.has(m))a.set(m,!0);else{try{super.add(m)}catch{}super.has(m)&&(a.set(m,!0),c.add(m))}!l&&a.has(m)&&(l=a.get(m))}}else{if(Array.isArray(r)&&r.length){let p=Object.values(r);for(let n of p)if(d(n)&&(n=n.trim(),!A.test(n)))if(super.has(n))a.set(n,!0);else{try{super.add(n)}catch{}super.has(n)&&(a.set(n,!0),c.add(n))}}if(Array.isArray(i)&&i.length){let p=Object.values(i);for(let n of p)d(n)&&(n=n.trim(),n&&a.set(n,!1))}}let u;if(super.verify(e)){let{hash:p,href:n,pathname:m,protocol:U,search:x}=new URL(e),E=U.replace(/:$/,""),G=E.split("+"),_;if(l)_=G.every(y=>a.get(y));else for(let[y,R]of a.entries())if(_=R||E!==y&&G.every(f=>f!==y),!_)break;if(_){let y=G.includes("data"),R,f=n;if(y){let[v,...z]=m.split(","),$=`${z.join(",")}${x}${p}`,T=v.split(";"),I=T[T.length-1]==="base64",h=$;I&&(h=ee($));try{let F=P(h).trim(),{protocol:se}=new URL(F);se.replace(/:$/,"").split("+").some(re=>A.test(re))&&(f="")}catch{}let B=S.test(h);h!==$||B?B?h=this.replace(h):this.#t.has(e)?this.#t.delete(e):R=!0:this.#t.has(e)?this.#t.delete(e):R=!0,(!v||Y.test(v))&&(h=this.purify(h)),f&&h?(I&&h!==$&&T.pop(),f=`${E}:${T.join(";")},${h}`):f=""}else R=!0;if(!y&&M.test(f)){let v=M.exec(f),{index:z}=v;f=f.substring(0,z).replace(/[?&]$/,"")}f?(u=f.replace(/%26/g,Z),R&&(this.#e=0)):this.#e=0}c.size&&c.forEach(y=>{super.remove(y)})}return u||null}parse(e,s){if(!d(e))throw new TypeError(`Expected String but got ${g(e)}.`);let r=new Map([["input",e]]),i;if(this.verify(e)){let{protocol:o}=new URL(e);o==="blob:"?i=e:i=this.sanitize(e,s??{allow:["data","file"]})}if(i){let o=new URL(i),{pathname:a,protocol:c}=o,u=c.replace(/:$/,"").split("+").includes("data");if(r.set("valid",!0),u){let p=new Map,[n,...m]=a.split(","),U=`${m.join(",")}`,x=n.split(";"),E=x[x.length-1]==="base64";E&&x.pop(),p.set("mime",x.join(";")),p.set("base64",E),p.set("data",U),r.set("data",Object.fromEntries(p))}else r.set("data",null);for(let p in o){let n=o[p];d(n)&&r.set(p,n)}}else r.set("valid",!1);return Object.fromEntries(r)}reset(){super.reset(),this.#e=0,this.#t.clear()}};var b=new O,ce=async(t,e={allow:[],deny:[],only:[]})=>{let s;if(t&&d(t)){let r;try{let{protocol:i}=new URL(t);r=i.replace(/:$/,"")}catch{}if(r==="blob"){let{allow:i,deny:o,only:a}=e;if(Array.isArray(i)&&i.includes("blob")&&!(Array.isArray(o)&&o.includes("blob"))||Array.isArray(a)&&a.includes("blob")){let c;try{c=await fetch(t).then(l=>l.blob()).then(te)}catch{}if(c){if(Array.isArray(a))a.includes("data")||a.push("data");else if(Array.isArray(i)&&(i.includes("data")||i.push("data"),Array.isArray(o)&&o.includes("data"))){let l=o.indexOf("data");o.splice(l,1)}s=b.sanitize(c,e)}}URL.revokeObjectURL(t)}else r&&(s=b.sanitize(t,e))}return s||null},pe=(t,e)=>{let s;if(t&&d(t)){let r;try{let{protocol:i}=new URL(t);r=i.replace(/:$/,"")}catch{}r==="blob"?URL.revokeObjectURL(t):r&&(s=b.sanitize(t,e))}return s||null},le=async t=>b.parse(t),me=t=>b.parse(t),de=async t=>b.verify(t),fe=t=>b.verify(t),he=b;export{he as default,de as isURI,fe as isURISync,le as parseURL,me as parseURLSync,ce as sanitizeURL,pe as sanitizeURLSync};
1
+ var Y={sanitize:(...t)=>{if(globalThis?.DOMPurify)return globalThis.DOMPurify.sanitize(...t);let e=["DOMPurify is not available.","Ensure DOMPurify is exposed globally (e.g., window.DOMPurify)."];throw new Error(e.join(" "))}};var Z=/^[\w+/=-]+$/;var T=/data:[\w#&+./;=-]*,/,P=/data:[\w#&+./;=^]*base64,[\w+/=-]+/i,Q=/data:[\w#&+./;=-]*,[^\x22]+/g,K=/&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/gi,J=/^(?:application\/(?:[\w#&.;-]+\+)?x|image\/svg\+x|text\/(?:ht|x))ml;?/;var V=/^[a-z][\da-z+.-]*$/,W=/^(?:ext|web)\+[a-z]+$/,j=/(?:java|vb)script/,A=/(?:java|vb)script|blob/,B=/%(?:22|27|3C|3E)|[\x22\x27<>]/,ee=/^%[\dA-F]{2}$/i;var y=t=>Object.prototype.toString.call(t).slice(8,-1),f=t=>typeof t=="string"||t instanceof String;var{FileReader:te}=globalThis;var z=[7,8,9,10,11,12,13,27,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,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,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];var C=["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","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","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","mvn","mvrp","mvrps","news","nfs","ni","nih","nntp","notes","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","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","turn","turns","tv","udp","unreal","upn","urn","ut2004","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"];var se=t=>{if(!f(t))throw new TypeError(`Expected String but got ${y(t)}.`);let e=[];for(let s of t)e.push(`%${s.charCodeAt(0).toString(16).toUpperCase()}`);return e.join("")},re=t=>{f(t)&&ee.test(t)&&(t=t.toUpperCase());let[e,s,a,r,o,n]=["&","#","<",">",'"',"'"].map(se),i;return t===e?i=`${e}amp;`:t===a?i=`${e}lt;`:t===r?i=`${e}gt;`:t===o?i=`${e}quot;`:t===n?i=`${e}${s}39;`:i=t,i},ie=t=>{if(f(t)){if(!Z.test(t))throw new Error(`Invalid base64 data: ${t}`)}else throw new TypeError(`Expected String but got ${y(t)}.`);let e=atob(t),s=Uint8Array.from([...e].map(o=>o.charCodeAt(0))),a=new Set(z),r;return s.every(o=>a.has(o))?r=e.replace(/\s/g,se):r=t,r},F=(t,e=0)=>{if(!f(t))throw new TypeError(`Expected String but got ${y(t)}.`);if(Number.isInteger(e)){if(e>16)throw new Error("Character references nested too deeply.")}else throw new TypeError(`Expected Number but got ${y(e)}.`);let s=decodeURIComponent(t);if(/&#/.test(s)){let a=new Set(z),r=[...s.matchAll(K)].reverse();for(let o of r){let[n,i]=o,l;if(/^x[\dA-F]+/i.test(i)?l=parseInt(`0${i}`,16):/^\d+/.test(i)&&(l=parseInt(i)),Number.isInteger(l)){let{index:h}=o,[c,d]=[s.substring(0,h),s.substring(h+n.length)];a.has(l)?(s=`${c}${String.fromCharCode(l)}${d}`,(/#x?$/.test(c)||/^#(?:x(?:00)?[2-7]|\d)/.test(d))&&(s=F(s,++e))):l<256&&(s=`${c}${d}`)}}}return s},ae=(t,e=33554432)=>new Promise((s,a)=>{if(Number.isInteger(t?.size)){if(Number.isInteger(e)&&t.size>e)return a(new DOMException(`Blob size (${t.size} bytes) exceeds the maximum allowed size of ${e} bytes.`,"NotReadableError"))}else return s(null);let r=new te;r.addEventListener("error",()=>a(r.error)),r.addEventListener("abort",()=>s(r.result)),r.addEventListener("load",()=>s(r.result)),r.readAsDataURL(t)}),D=class{#e;constructor(){this.#e=new Set(C)}get(){return[...this.#e]}has(e){return this.#e.has(e)}add(e){if(f(e)){if(j.test(e)||!V.test(e))throw new Error(`Invalid scheme: ${e}`)}else throw new TypeError(`Expected String but got ${y(e)}.`);return this.#e.add(e),[...this.#e]}remove(e){return this.#e.delete(e)}verify(e){let s;if(f(e))try{let{protocol:a}=new URL(e),r=a.replace(/:$/,""),o=r.split("+");s=!j.test(r)&&W.test(r)||o.every(n=>this.#e.has(n))}catch{s=!1}return!!s}reset(){this.#e.clear();for(let e of C)this.#e.add(e)}};var U=(t,e,s)=>{t&&console.warn(`[URLSanitizer Debug] ${e}`,s?s.message:"")},N=class extends D{#e;#t=new Set;constructor(){super(),this.#e=0}replace(e){if(!f(e))throw new TypeError(`Expected String but got ${y(e)}.`);let s=e;if(T.test(s)){let r=[...s.matchAll(Q)].reverse();for(let o of r){let[n]=o;P.test(n)&&([n]=P.exec(n)),this.#e++,this.#t.add(n);let i=this.sanitize(n,{allow:["data"]}),{index:l}=o,[h,c]=[s.substring(0,l),s.substring(l+n.length)];i?s=`${h}${i}${c}`:s=`${h}${c}`}}return s}purify(e){if(!f(e))throw new TypeError(`Expected String but got ${y(e)}.`);let s=Y.sanitize(decodeURIComponent(e));return s&&T.test(s)&&(s=this.replace(s)),s=s.replace(/(?:#|%23)$/,"").replace(/(?<!(?:#|%23).*)(?:\?|%3F)$/,""),encodeURI(s)}sanitize(e,s){if(this.#e>16)throw this.#e=0,new Error("Data URLs nested too deeply.");let{allow:a,deny:r,only:o,debug:n=!1}=s??{},i=new Map([["blob",!1],["data",!1],["file",!1],["javascrpt",!1],["vbscript",!1]]),l=new Set,h=!1;if(Array.isArray(o)&&o.length){let d=super.get();for(let m of d)i.set(m,!1);let p=Object.values(o);for(let m of p)if(f(m)&&(m=m.trim(),!A.test(m))){if(super.has(m))i.set(m,!0);else{try{super.add(m)}catch(b){let R=`Failed to add scheme '${m}' in 'only' list.`;U(n,R,b)}super.has(m)&&(i.set(m,!0),l.add(m))}!h&&i.has(m)&&(h=i.get(m))}}else{if(Array.isArray(a)&&a.length){let d=Object.values(a);for(let p of d)if(f(p)&&(p=p.trim(),!A.test(p)))if(super.has(p))i.set(p,!0);else{try{super.add(p)}catch(m){let b=`Failed to add scheme '${p}' in 'allow' list.`;U(n,b,m)}super.has(p)&&(i.set(p,!0),l.add(p))}}if(Array.isArray(r)&&r.length){let d=Object.values(r);for(let p of d)f(p)&&(p=p.trim(),p&&i.set(p,!1))}}let c;if(super.verify(e)){let{hash:d,href:p,pathname:m,protocol:b,search:R}=new URL(e),M=b.replace(/:$/,""),G=M.split("+"),$;if(h)$=G.every(x=>i.get(x));else for(let[x,v]of i.entries())if($=v||M!==x&&G.every(u=>u!==x),!$)break;if($){let x=G.includes("data"),v,u=p;if(x){let[_,...O]=m.split(","),S=`${O.join(",")}${R}${d}`,L=_.split(";"),H=L[L.length-1]==="base64",g=S;H&&(g=ie(S));try{let I=F(g).trim(),{protocol:X}=new URL(I);X.replace(/:$/,"").split("+").some(oe=>A.test(oe))&&(u="")}catch(I){U(n,"Failed to parse inner data URL protocol.",I)}let q=T.test(g);g!==S||q?q?g=this.replace(g):this.#t.has(e)?this.#t.delete(e):v=!0:this.#t.has(e)?this.#t.delete(e):v=!0,(!_||J.test(_))&&(g=this.purify(g)),u&&g?(H&&g!==S&&L.pop(),u=`${M}:${L.join(";")},${g}`):u=""}else v=!0;if(!x&&B.test(u)){let _=B.exec(u),{index:O}=_;u=u.substring(0,O).replace(/[?&]$/,"")}u?(c=u.replace(/%26/g,re),v&&(this.#e=0)):this.#e=0}l.size&&l.forEach(x=>{super.remove(x)})}return c||null}parse(e,s){if(!f(e))throw new TypeError(`Expected String but got ${y(e)}.`);let a=new Map([["input",e]]),r;if(this.verify(e)){let{protocol:o}=new URL(e);o==="blob:"?r=e:r=this.sanitize(e,s??{allow:["data","file"]})}if(r){let o=new URL(r),{pathname:n,protocol:i}=o,h=i.replace(/:$/,"").split("+").includes("data");if(a.set("valid",!0),h){let c=new Map,[d,...p]=n.split(","),m=`${p.join(",")}`,b=d.split(";"),R=b[b.length-1]==="base64";R&&b.pop(),c.set("mime",b.join(";")),c.set("base64",R),c.set("data",m),a.set("data",Object.fromEntries(c))}else a.set("data",null);for(let c in o){let d=o[c];f(d)&&a.set(c,d)}}else a.set("valid",!1);return Object.fromEntries(a)}reset(){super.reset(),this.#e=0,this.#t.clear()}};var w=new N,me=async(t,e={allow:[],deny:[],only:[],debug:!1,maxBlobSize:33554432})=>{let s=!!e?.debug,a=Number.isInteger(e?.maxBlobSize)&&e.maxBlobSize>0?e.maxBlobSize:33554432,r;if(t&&f(t)){let o;try{let{protocol:n}=new URL(t);o=n.replace(/:$/,"")}catch(n){U(s,`Invalid URL input format: ${t}`,n)}if(o==="blob"){let{allow:n,deny:i,only:l}=e;if(Array.isArray(n)&&n.includes("blob")&&!(Array.isArray(i)&&i.includes("blob"))||Array.isArray(l)&&l.includes("blob")){let h;try{h=await fetch(t).then(c=>c.blob()).then(c=>ae(c,a))}catch(c){let d=`Failed to fetch and convert blob URL: ${t}`;U(s,d,c)}if(h){if(Array.isArray(l))l.includes("data")||l.push("data");else if(Array.isArray(n)&&(n.includes("data")||n.push("data"),Array.isArray(i)&&i.includes("data"))){let c=i.indexOf("data");i.splice(c,1)}r=w.sanitize(h,e)}}URL.revokeObjectURL(t)}else o&&(r=w.sanitize(t,e))}return r||null},de=(t,e)=>{let s;if(t&&f(t)){let a;try{let{protocol:r}=new URL(t);a=r.replace(/:$/,"")}catch(r){let o=`Failed to parse URL: ${t}`;U(e?.debug,o,r)}a==="blob"?URL.revokeObjectURL(t):a&&(s=w.sanitize(t,e))}return s||null},fe=async t=>w.parse(t),he=t=>w.parse(t),ue=async t=>w.verify(t),ge=t=>w.verify(t),be=w;export{be as default,ue as isURI,ge as isURISync,fe as parseURL,he as parseURLSync,me as sanitizeURL,de as sanitizeURLSync};
2
2
  /*!
3
3
  * URL Sanitizer
4
4
  *