url-sanitizer 2.3.1 → 4.0.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/package.json CHANGED
@@ -19,35 +19,34 @@
19
19
  "exports": {
20
20
  ".": {
21
21
  "types": "./types/index.d.ts",
22
- "browser": "./dist/url-sanitizer-wo-dompurify.min.js",
22
+ "browser": "./dist/url-sanitizer.min.js",
23
23
  "default": "./src/index.js"
24
24
  },
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "dompurify": "^3.4.3",
28
+ "dompurify": "^3.4.7",
29
29
  "jsdom": "^29.1.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@braintree/sanitize-url": "^7.1.2",
33
33
  "@types/jsdom": "^28.0.3",
34
- "@types/node": "^25.8.0",
34
+ "@types/node": "^25.9.1",
35
35
  "c8": "^11.0.0",
36
36
  "chai": "^6.2.2",
37
- "commander": "^14.0.3",
37
+ "commander": "^15.0.0",
38
38
  "copyfiles-ng": "^2.4.7",
39
39
  "esbuild": "^0.28.0",
40
40
  "eslint": "^9.39.4",
41
- "eslint-plugin-jsdoc": "^62.9.0",
41
+ "eslint-plugin-jsdoc": "^63.0.0",
42
42
  "eslint-plugin-regexp": "^3.1.0",
43
43
  "eslint-plugin-unicorn": "^64.0.0",
44
44
  "fast-check": "^4.8.0",
45
45
  "globals": "^17.6.0",
46
46
  "http-server": "^14.1.1",
47
47
  "mitata": "^1.0.34",
48
- "mocha": "^11.7.5",
48
+ "mocha": "^11.7.6",
49
49
  "neostandard": "^0.13.0",
50
- "npm-run-all2": "^8.0.4",
51
50
  "papaparse": "^5.5.3",
52
51
  "sinon": "^22.0.0",
53
52
  "strict-url-sanitise": "^0.0.1",
@@ -69,20 +68,23 @@
69
68
  "scripts": {
70
69
  "bench": "node benchmark/benchmark.js",
71
70
  "build": "npm run include && npm run tsc && npm run lint && npm test && npm run copy && npm run bundle",
72
- "bundle": "npm-run-all -s bundle:*",
71
+ "bundle": "npm run bundle:min && npm run bundle:min-wo-dompurify",
73
72
  "bundle:min": "esbuild --format=esm --platform=browser --outfile=dist/url-sanitizer.min.js --bundle --minify --sourcemap bundle/index.js",
74
73
  "bundle:min-wo-dompurify": "esbuild --format=esm --platform=browser --outfile=dist/url-sanitizer-wo-dompurify.min.js --bundle --minify --sourcemap bundle_wo_dompurify/index.js",
75
74
  "char": "node scripts/index char -i",
76
- "copy": "npm-run-all -s copy:*",
77
- "copy:src": "copyfiles-ng src/**/* bundle --up=1 --verbose --exclude=src/mjs/dompurify.js --exclude=src/mjs/file-reader.js --exclude=src/mjs/lib-util.js",
78
- "copy:src-wo-dompurify": "copyfiles-ng src/**/* bundle_wo_dompurify --up=1 --verbose --exclude=src/mjs/dompurify.js --exclude=src/mjs/file-reader.js --exclude=src/mjs/lib-util.js",
79
- "copy:repl": "copyfiles-ng repl/dompurify.js repl/file-reader.js repl/lib-util.js bundle/mjs --up=1 --verbose",
80
- "copy:repl-wo-dompurify": "copyfiles-ng repl/dompurify-global.js repl/file-reader.js repl/lib-util.js bundle_wo_dompurify/mjs --up=1 --verbose && node scripts/index rename --oldpath=bundle_wo_dompurify/mjs/dompurify-global.js --newpath=bundle_wo_dompurify/mjs/dompurify.js -i",
75
+ "copy": "npm run copy:src && npm run copy:src-wo-dompurify && npm run copy:repl && npm run copy:repl-wo-dompurify",
76
+ "copy:src": "copyfiles-ng src/**/* bundle --up=1 --verbose --exclude=src/mjs/dompurify.js --exclude=src/mjs/file-reader.js",
77
+ "copy:src-wo-dompurify": "copyfiles-ng src/**/* bundle_wo_dompurify --up=1 --verbose --exclude=src/mjs/dompurify.js --exclude=src/mjs/file-reader.js",
78
+ "copy:repl": "copyfiles-ng repl/dompurify.js repl/file-reader.js bundle/mjs --up=1 --verbose",
79
+ "copy:repl-wo-dompurify": "copyfiles-ng repl/dompurify-global.js repl/file-reader.js bundle_wo_dompurify/mjs --up=1 --verbose && node scripts/index rename --oldpath=bundle_wo_dompurify/mjs/dompurify-global.js --newpath=bundle_wo_dompurify/mjs/dompurify.js -i",
81
80
  "include": "node scripts/index include --dir=iana -i",
82
81
  "lint": "eslint --fix .",
83
82
  "test": "c8 --reporter=text mocha --exit test/*.test.js",
84
83
  "test:bdd": "http-server ./ -c-1 -o test/bdd/url-sanitizer.test.html",
85
84
  "tsc": "node scripts/index clean --dir=types -i && npx tsc"
86
85
  },
87
- "version": "2.3.1"
86
+ "engines": {
87
+ "node": ">=22.13"
88
+ },
89
+ "version": "4.0.0"
88
90
  }
@@ -1 +1 @@
1
- [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]
1
+ [7,8,9,10,11,12,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]
package/src/mjs/common.js CHANGED
@@ -6,16 +6,17 @@
6
6
  import { TYPE_FROM, TYPE_TO } from './constant.js';
7
7
 
8
8
  /**
9
- * get type
10
- * @param {object} o - object to check
11
- * @returns {string} - type of object
9
+ * Gets the type of the given object.
10
+ * Returns the object class name (e.g., "String", "Array", "Null", "Undefined").
11
+ * @param {unknown} o - The object to check.
12
+ * @returns {string} The specific type of the object.
12
13
  */
13
14
  export const getType = o =>
14
15
  Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);
15
16
 
16
17
  /**
17
- * is string
18
- * @param {object} o - object to check
19
- * @returns {boolean} - result
18
+ * Checks if the given value is a string.
19
+ * @param {unknown} o - The value to check.
20
+ * @returns {boolean} True if the value is a string, false otherwise.
20
21
  */
21
22
  export const isString = o => typeof o === 'string' || o instanceof String;
@@ -2,26 +2,46 @@
2
2
  * constant.js
3
3
  */
4
4
 
5
- /* numeric */
5
+ /**
6
+ * The chunk size in bytes (8KB) used for processing data arrays to prevent stack overflow.
7
+ * @type {number}
8
+ */
6
9
  export const CHUNK_SIZE = 8 * 1024;
10
+
11
+ /**
12
+ * The radix used for decimal string conversions and calculations.
13
+ * @type {number}
14
+ */
15
+ export const DECI = 10;
16
+
17
+ /**
18
+ * The radix used for hexadecimal string conversions and calculations.
19
+ * @type {number}
20
+ */
7
21
  export const HEX = 16;
8
- export const MAX_BLOB_SIZE = 32 * 1024 * 1024;
22
+
23
+ /**
24
+ * The maximum allowed size for a Blob in bytes (16MB).
25
+ * @type {number}
26
+ */
27
+ export const MAX_BLOB_SIZE = 16 * 1024 * 1024;
28
+
29
+ /**
30
+ * The maximum allowed nesting depth for recurse.
31
+ * @type {number}
32
+ */
33
+ export const MAX_NEST = 16;
34
+
35
+ /**
36
+ * The start index for extracting the core type name from an object.
37
+ * (e.g., skips "[object " in "[object String]")
38
+ * @type {number}
39
+ */
9
40
  export const TYPE_FROM = 8;
10
- export const TYPE_TO = -1;
11
41
 
12
- /* regexp */
13
- export const REG_B64 = /^[\w+/=-]+$/;
14
- export const REG_CHARSET = /^charset=([\w#&.;-]+)$/;
15
- export const REG_DATA_URL = /data:[\w#&+./;=-]*,/;
16
- export const REG_DATA_URL_B64 = /data:[\w#&+./;=^]*base64,[\w+/=-]+/i;
17
- export const REG_DATA_URL_G = /data:[\w#&+./;=-]*,[^\x22]+/g;
18
- export const REG_NUM_REF = /&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/gi;
19
- export const REG_MIME_DOM =
20
- /^(?:application\/(?:[\w#&.;-]+\+)?x|image\/svg\+x|text\/(?:ht|x))ml;?/;
21
- export const REG_MIME_TEXT = /^text\/[\w#&.;-]+/;
22
- export const REG_SCHEME = /^[a-z][\da-z+.-]*$/;
23
- export const REG_SCHEME_EXT = /^(?:ext|web)\+[a-z]+$/;
24
- export const REG_SCRIPT = /(?:java|vb)script/;
25
- export const REG_SCRIPT_BLOB = /(?:java|vb)script|blob/;
26
- export const REG_TAG_QUOT = /%(?:22|27|3C|3E)|[\x22\x27<>]/;
27
- export const REG_URL_ENC = /^%[\dA-F]{2}$/i;
42
+ /**
43
+ * The end index for extracting the core type name from an object.
44
+ * (e.g., excludes the trailing "]" in "[object String]")
45
+ * @type {number}
46
+ */
47
+ export const TYPE_TO = -1;
@@ -6,4 +6,10 @@ import DOMPurify from 'dompurify';
6
6
  import { JSDOM } from 'jsdom';
7
7
 
8
8
  const { window } = new JSDOM('');
9
+
10
+ /**
11
+ * An instance of DOMPurify initialized with a JSDOM window context.
12
+ * Used to sanitize HTML/SVG strings.
13
+ * @type {import('dompurify')}
14
+ */
9
15
  export const domPurify = DOMPurify(window);
@@ -4,18 +4,31 @@
4
4
 
5
5
  /* shared */
6
6
  import { getType, isString } from './common.js';
7
- import { textChars } from './lib-util.js';
8
7
 
9
8
  /* constants */
10
- import {
11
- CHUNK_SIZE, REG_CHARSET, REG_MIME_DOM, REG_MIME_TEXT
12
- } from './constant.js';
9
+ import { CHUNK_SIZE } from './constant.js';
10
+ import { REG_CHARSET, REG_MIME_DOM, REG_MIME_TEXT } from './regexp.js';
11
+ import { CTRL_CHAR_CODES } from './text-chars.js';
13
12
  const DONE = 2;
14
13
  const EMPTY = 0;
15
14
  const LOADING = 1;
16
15
 
17
16
  /**
18
- * progress event
17
+ * Converts a Uint8Array to a binary string in chunks to prevent stack overflow.
18
+ * @private
19
+ * @param {Uint8Array} uint8arr - The array to convert.
20
+ * @returns {string} The resulting binary string.
21
+ */
22
+ const getBinaryString = uint8arr => {
23
+ const chunks = [];
24
+ for (let i = 0; i < uint8arr.length; i += CHUNK_SIZE) {
25
+ chunks.push(String.fromCharCode(...uint8arr.subarray(i, i + CHUNK_SIZE)));
26
+ }
27
+ return chunks.join('');
28
+ };
29
+
30
+ /**
31
+ * Represents a progress event.
19
32
  * @see {@link https://xhr.spec.whatwg.org/#interface-progressevent}
20
33
  */
21
34
  export class ProgressEvent extends Event {
@@ -25,15 +38,15 @@ export class ProgressEvent extends Event {
25
38
  #total;
26
39
 
27
40
  /**
28
- * construct
29
- * @param {string} type - type
30
- * @param {object} [opt] - init options
31
- * @param {boolean} [opt.bubbles] - bubbles
32
- * @param {boolean} [opt.cancelable] - cancelable
33
- * @param {boolean} [opt.composed] - composed
34
- * @param {boolean} [opt.lengthComputable] - lengthComputable
35
- * @param {number} [opt.loaded] - loaded
36
- * @param {number} [opt.total] - total
41
+ * Creates a new ProgressEvent instance.
42
+ * @param {string} type - The event type.
43
+ * @param {object} [opt] - Initialization options.
44
+ * @param {boolean} [opt.bubbles] - Whether the event bubbles.
45
+ * @param {boolean} [opt.cancelable] - Whether the event is cancelable.
46
+ * @param {boolean} [opt.composed] - Whether the event is composed.
47
+ * @param {boolean} [opt.lengthComputable] - Whether the length is computable.
48
+ * @param {number} [opt.loaded] - The amount of work already performed.
49
+ * @param {number} [opt.total] - The total amount of work to be performed.
37
50
  */
38
51
  constructor(type, opt = {}) {
39
52
  const {
@@ -49,23 +62,37 @@ export class ProgressEvent extends Event {
49
62
  this.#total = Number.isInteger(total) && total > 0 ? total : 0;
50
63
  }
51
64
 
52
- /* getter */
65
+ /**
66
+ * Indicates whether the total size of the transfer is computable.
67
+ * @type {boolean}
68
+ */
53
69
  get lengthComputable() {
54
70
  return this.#lengthComputable;
55
71
  }
56
72
 
73
+ /**
74
+ * The number of bytes already loaded.
75
+ * @type {number}
76
+ */
57
77
  get loaded() {
58
78
  return this.#loaded;
59
79
  }
60
80
 
81
+ /**
82
+ * The total number of bytes to be loaded.
83
+ * @type {number}
84
+ */
61
85
  get total() {
62
86
  return this.#total;
63
87
  }
64
88
  }
65
89
 
66
90
  /**
67
- * file reader
91
+ * Asynchronously reads the contents of files (or raw data buffers).
68
92
  * @see {@link https://w3c.github.io/FileAPI/#APIASynch}
93
+ * @property {number} EMPTY - The read operation has not started yet.
94
+ * @property {number} LOADING - The data is currently being read.
95
+ * @property {number} DONE - The read operation has completed successfully or failed.
69
96
  */
70
97
  export class FileReader extends EventTarget {
71
98
  /* private fields */
@@ -75,7 +102,7 @@ export class FileReader extends EventTarget {
75
102
  #terminate = false;
76
103
 
77
104
  /**
78
- * construct
105
+ * Creates a new FileReader instance.
79
106
  */
80
107
  constructor() {
81
108
  super();
@@ -84,38 +111,48 @@ export class FileReader extends EventTarget {
84
111
  this.DONE = DONE;
85
112
  }
86
113
 
87
- /* getter */
114
+ /**
115
+ * The error that occurred while reading the file.
116
+ * @type {Error|DOMException|null}
117
+ */
88
118
  get error() {
89
119
  return this.#error;
90
120
  }
91
121
 
122
+ /**
123
+ * The current state of the FileReader operation.
124
+ * @type {number}
125
+ */
92
126
  get readyState() {
93
127
  return this.#state;
94
128
  }
95
129
 
130
+ /**
131
+ * The file's contents. Only valid after the read operation is complete.
132
+ * @type {string|ArrayBuffer|null}
133
+ */
96
134
  get result() {
97
135
  return this.#result;
98
136
  }
99
137
 
100
138
  /**
101
- * dispatch progress event
102
- * @param {string} type - event type
103
- * @returns {boolean} - result
139
+ * Dispatches a progress event.
140
+ * @private
141
+ * @param {string} type - The type of the event to dispatch.
142
+ * @returns {boolean} True if dispatched and not canceled, false otherwise.
104
143
  */
105
144
  _dispatchProgressEvent(type) {
106
- if (isString(type)) {
107
- type = type.trim();
108
- if (!/abort|error|load(?:end|start)?|progress/.test(type)) {
109
- this.#error = new DOMException('Invalid state.', 'InvalidStateError');
110
- this._dispatchProgressEvent('error');
111
- throw this.#error;
112
- }
113
- } else {
145
+ if (!isString(type)) {
114
146
  this.#error = new TypeError(`Expected String but got ${getType(type)}.`);
115
147
  this._dispatchProgressEvent('error');
116
148
  throw this.#error;
117
149
  }
118
- if (type === 'error' && !this.#error) {
150
+ type = type.trim();
151
+ if (!/abort|error|load(?:end|start)?|progress/.test(type)) {
152
+ this.#error = new DOMException('Invalid state.', 'InvalidStateError');
153
+ this._dispatchProgressEvent('error');
154
+ throw this.#error;
155
+ } else if (type === 'error' && !this.#error) {
119
156
  this.#error = new Error('Unknown error.');
120
157
  }
121
158
  const evt = new ProgressEvent(type, {
@@ -128,27 +165,40 @@ export class FileReader extends EventTarget {
128
165
  }
129
166
 
130
167
  /**
131
- * abort
168
+ * Aborts the ongoing read operation.
132
169
  * @returns {void}
133
170
  */
134
171
  abort() {
172
+ if (this.#state === this.EMPTY || this.#state === this.DONE) {
173
+ this.#result = null;
174
+ return;
175
+ }
135
176
  this.#state = this.DONE;
136
177
  this.#result = null;
137
178
  this.#terminate = true;
179
+ this.#error =
180
+ new DOMException('The read operation was aborted.', 'AbortError');
138
181
  this._dispatchProgressEvent('abort');
139
182
  this._dispatchProgressEvent('loadend');
140
183
  }
141
184
 
142
185
  /**
143
- * read blob
144
- * @param {object} blob - blob
145
- * @param {string} format - format to read as
146
- * @param {string} [encoding] - character encoding
147
- * @returns {Promise.<void>} - void
186
+ * Reads the given blob based on the specified format.
187
+ * @private
188
+ * @param {Blob} blob - The target Blob object.
189
+ * @param {string} format - The format in which to read the blob data.
190
+ * @param {string} [encoding] - The character encoding for text formats.
191
+ * @returns {Promise<void>}
148
192
  */
149
193
  async _read(blob, format, encoding = '') {
150
- if (!(blob instanceof Blob && isString(format) && isString(encoding))) {
151
- this.abort();
194
+ if (!(blob instanceof Blob)) {
195
+ throw new TypeError(`Expected Blob but got ${getType(blob)}.`);
196
+ }
197
+ if (!isString(format)) {
198
+ throw new TypeError(`Expected String but got ${getType(format)}.`);
199
+ }
200
+ if (!isString(encoding)) {
201
+ throw new TypeError(`Expected String but got ${getType(encoding)}.`);
152
202
  }
153
203
  if (!this.#terminate) {
154
204
  if (this.#state === this.LOADING) {
@@ -165,32 +215,39 @@ export class FileReader extends EventTarget {
165
215
  const mediaTypes = type ? type.split(';') : [];
166
216
  const buffer = await blob.arrayBuffer();
167
217
  const uint8arr = new Uint8Array(buffer);
168
- const chunkSize = CHUNK_SIZE;
169
- let binary = '';
170
- for (let i = 0; i < uint8arr.length; i += chunkSize) {
171
- binary += String.fromCharCode(...uint8arr.subarray(i, i + chunkSize));
172
- }
173
218
  this._dispatchProgressEvent('loadstart');
174
219
  switch (format) {
175
- case 'arrayBuffer':
220
+ case 'arrayBuffer': {
176
221
  res = buffer;
177
222
  this._dispatchProgressEvent('progress');
178
223
  break;
179
- case 'binaryString':
180
- res = binary;
224
+ }
225
+ case 'binaryString': {
226
+ res = getBinaryString(uint8arr);
181
227
  this._dispatchProgressEvent('progress');
182
228
  break;
183
- case 'dataURL':
184
- if (!mediaTypes.length ||
185
- mediaTypes[mediaTypes.length - 1] !== 'base64') {
186
- mediaTypes.push('base64');
229
+ }
230
+ case 'dataURL': {
231
+ const mime = mediaTypes.length > 0 ? mediaTypes.join(';') : '';
232
+ const mimeStr = mime ? `${mime};base64` : 'base64';
233
+ if (typeof globalThis.Buffer !== 'undefined') {
234
+ const base64 = globalThis.Buffer.from(buffer).toString('base64');
235
+ res = `data:${mimeStr},${base64}`;
236
+ } else {
237
+ res = `data:${mimeStr},${btoa(getBinaryString(uint8arr))}`;
187
238
  }
188
- res = `data:${mediaTypes.join(';')},${btoa(binary)}`;
189
239
  this._dispatchProgressEvent('progress');
190
240
  break;
241
+ }
191
242
  case 'text': {
192
- const textCharCodes = new Set(textChars);
193
- if (uint8arr.every(c => textCharCodes.has(c))) {
243
+ let isSafeText = true;
244
+ for (const i of uint8arr) {
245
+ if (CTRL_CHAR_CODES.has(i)) {
246
+ isSafeText = false;
247
+ break;
248
+ }
249
+ }
250
+ if (isSafeText) {
194
251
  let charset;
195
252
  for (const media of mediaTypes) {
196
253
  if (REG_CHARSET.test(media)) {
@@ -216,14 +273,14 @@ export class FileReader extends EventTarget {
216
273
  }
217
274
  if (REG_MIME_DOM.test(type)) {
218
275
  if (encoding === charset || (encoding === 'utf8' && !charset)) {
219
- res = binary;
276
+ res = getBinaryString(uint8arr);
220
277
  this._dispatchProgressEvent('progress');
221
278
  }
222
279
  } else if (REG_MIME_TEXT.test(type)) {
223
280
  if (encoding === charset ||
224
281
  (encoding === 'utf8' &&
225
282
  (!charset || charset === 'us-ascii'))) {
226
- res = binary;
283
+ res = getBinaryString(uint8arr);
227
284
  this._dispatchProgressEvent('progress');
228
285
  }
229
286
  }
@@ -254,37 +311,37 @@ export class FileReader extends EventTarget {
254
311
  }
255
312
 
256
313
  /**
257
- * read as arrayBuffer
258
- * @param {object} blob - blob
259
- * @returns {Promise.<void>} - void
314
+ * Reads the contents of the specified Blob as an ArrayBuffer.
315
+ * @param {Blob} blob - The target Blob object.
316
+ * @returns {Promise<void>}
260
317
  */
261
318
  async readAsArrayBuffer(blob) {
262
319
  await this._read(blob, 'arrayBuffer');
263
320
  }
264
321
 
265
322
  /**
266
- * read as binary string
267
- * @param {object} blob - blob
268
- * @returns {Promise.<void>} - void
323
+ * Reads the contents of the specified Blob as a binary string.
324
+ * @param {Blob} blob - The target Blob object.
325
+ * @returns {Promise<void>}
269
326
  */
270
327
  async readAsBinaryString(blob) {
271
328
  await this._read(blob, 'binaryString');
272
329
  }
273
330
 
274
331
  /**
275
- * read as data URL
276
- * @param {object} blob - blob
277
- * @returns {Promise.<void>} - void
332
+ * Reads the contents of the specified Blob as a base64-encoded data URL.
333
+ * @param {Blob} blob - The target Blob object.
334
+ * @returns {Promise<void>}
278
335
  */
279
336
  async readAsDataURL(blob) {
280
337
  await this._read(blob, 'dataURL');
281
338
  }
282
339
 
283
340
  /**
284
- * read as text
285
- * @param {object} blob - blob
286
- * @param {string} [encoding] - encoding
287
- * @returns {Promise.<void>} - void
341
+ * Reads the contents of the specified Blob as text.
342
+ * @param {Blob} blob - The target Blob object.
343
+ * @param {string} [encoding] - The character encoding to use.
344
+ * @returns {Promise<void>}
288
345
  */
289
346
  async readAsText(blob, encoding) {
290
347
  await this._read(blob, 'text', encoding);
@@ -0,0 +1,79 @@
1
+ /**
2
+ * regexp.js
3
+ */
4
+
5
+ /**
6
+ * Matches and captures the charset parameter in a media type string.
7
+ * @type {RegExp}
8
+ */
9
+ export const REG_CHARSET = /^charset=([\w#&.;-]+)$/;
10
+
11
+ /**
12
+ * Matches a trailing empty fragment identifier (#).
13
+ * @type {RegExp}
14
+ */
15
+ export const REG_HASH = /(?:#|%23)$/;
16
+
17
+ /**
18
+ * Matches MIME types that contain DOM-parsable content (HTML, XML, SVG).
19
+ * @type {RegExp}
20
+ */
21
+ export const REG_MIME_DOM =
22
+ /^(?:application\/(?:[\w#&.;-]+\+)?x|image\/svg\+x|text\/(?:ht|x))ml;?/;
23
+
24
+ /**
25
+ * Matches general text MIME types.
26
+ * @type {RegExp}
27
+ */
28
+ export const REG_MIME_TEXT = /^text\/[\w#&.;-]+/;
29
+
30
+ /**
31
+ * Matches a trailing empty query string (?), ensuring it is not part of a hash.
32
+ * @type {RegExp}
33
+ */
34
+ export const REG_QUERY = /(?<!(?:#|%23).*)(?:\?|%3F)$/;
35
+
36
+ /**
37
+ * Validates a string against standard IANA URI scheme syntax.
38
+ * @type {RegExp}
39
+ */
40
+ export const REG_SCHEME = /^[a-z][\da-z+.-]*$/;
41
+
42
+ /**
43
+ * Matches web custom schemes (`web+`) or application-specific schemes (`ext+`).
44
+ * - `web+` schemes comply with the HTML Standard.
45
+ * - `ext+` schemes comply with RFC 3986 but disallow `+` in the suffix.
46
+ * @type {RegExp}
47
+ */
48
+ export const REG_SCHEME_EXT = /^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/;
49
+
50
+ /**
51
+ * Matches executable script schemes (javascript or vbscript), case-insensitive.
52
+ * @type {RegExp}
53
+ */
54
+ export const REG_SCRIPT = /^(?:java|vb)script$/i;
55
+
56
+ /**
57
+ * Matches script-executing or data-fetching schemes.
58
+ * @type {RegExp}
59
+ */
60
+ export const REG_SCRIPT_BLOB = /^(?:(?:java|vb)script|blob)$/i;
61
+
62
+ /**
63
+ * Matches HTML tags, quotes, and their encoded or character reference variants.
64
+ * @type {RegExp}
65
+ */
66
+ export const REG_TAG_QUOT =
67
+ /["'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i;
68
+
69
+ /**
70
+ * Matches a valid percent-encoded character format (%XX).
71
+ * @type {RegExp}
72
+ */
73
+ export const REG_URL_ENC = /^%[\dA-F]{2}$/i;
74
+
75
+ /**
76
+ * Validates relative URLs and flags invalid patterns.
77
+ * @type {RegExp}
78
+ */
79
+ export const REG_VERIFY_RELATIVE = /^(?:\/{2,}|\\|[a-z][a-z\d+\-.]*:[^/])/i;