mphttpx 1.0.6 → 1.0.7

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
@@ -1,35 +1,716 @@
1
- # A polyfill for mini-program.
1
+ # MPHTTPX
2
2
 
3
- ## install
4
- ```bash
3
+ The `mphttpx` library aims to provide a more ES6-styled [Blob.js][0],
4
+ along with a `fetch` polyfill that works seamlessly with the Blob-polyfill.
5
+ This allows web code to be reused in other environments (such as mini-programs).
6
+
7
+ ## Table of Contents
8
+
9
+ - [MPHTTPX](#mphttpx)
10
+ - [Table of Contents](#table-of-contents)
11
+ - [Features](#features)
12
+ - [Installation](#installation)
13
+ - [Mini-Program Support](#mini-program-support)
14
+ - [Usage](#usage)
15
+ - [TextEncoder](#textencoder)
16
+ - [Compatibility](#compatibility)
17
+ - [TextDecoder](#textdecoder)
18
+ - [Compatibility](#compatibility-1)
19
+ - [Blob](#blob)
20
+ - [Compatibility](#compatibility-2)
21
+ - [File](#file)
22
+ - [Compatibility](#compatibility-3)
23
+ - [FileReader](#filereader)
24
+ - [Compatibility](#compatibility-4)
25
+ - [FormData](#formdata)
26
+ - [Compatibility](#compatibility-5)
27
+ - [URLSearchParams](#urlsearchparams)
28
+ - [Compatibility](#compatibility-6)
29
+ - [fetch](#fetch)
30
+ - [Compatibility](#compatibility-7)
31
+ - [Headers](#headers)
32
+ - [Compatibility](#compatibility-8)
33
+ - [Request](#request)
34
+ - [Compatibility](#compatibility-9)
35
+ - [Response](#response)
36
+ - [Compatibility](#compatibility-10)
37
+ - [AbortController](#abortcontroller)
38
+ - [Compatibility](#compatibility-11)
39
+ - [AbortSignal](#abortsignal)
40
+ - [Compatibility](#compatibility-12)
41
+ - [EventTarget](#eventtarget)
42
+ - [Compatibility](#compatibility-13)
43
+ - [XMLHttpRequest (mini-programs)](#xmlhttprequest-mini-programs)
44
+ - [Compatibility](#compatibility-14)
45
+ - [UniApp \& Taro](#uniapp--taro)
46
+ - [Node.js](#nodejs)
47
+ - [License](#license)
48
+
49
+ ## Features
50
+
51
+ - **TextEncoder**
52
+ - **TextDecoder**
53
+ - **Blob**
54
+ - **File**
55
+ - **FileReader**
56
+ - **FormData**
57
+ - **URLSearchParams**
58
+ - **fetch**
59
+ - **Headers**
60
+ - **Request**
61
+ - **Response**
62
+ - **AbortController**
63
+ - **AbortSignal**
64
+ - **EventTarget**
65
+ - **XMLHttpRequest (mini-programs)**
66
+
67
+ ## Installation
68
+
69
+ ```
5
70
  npm install mphttpx
6
71
  ```
7
72
 
8
- ## usage
73
+ ## Mini-Program Support
74
+
75
+ | WeChat | Alipay | Baidu | ByteDance | QQ | Kwai | JD | RedNote |
76
+ |:------:|:------:|:-----:|:---------:|:--:|:----:|:--:|:-------:|
77
+ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ |
78
+
79
+ Note: Modern browsers such as Chrome, Firefox, Edge, and Safari do not need these polyfills;
80
+ the relevant implementations imported from the mphttpx library will directly return the native functions of the browser.
81
+
82
+ ## Usage
83
+
84
+ ### TextEncoder
85
+
86
+ ```javascript
87
+ import { TextEncoder } from "mphttpx";
88
+
89
+ const encoder = new TextEncoder();
90
+ const view = encoder.encode("€");
91
+ console.log(view); // Uint8Array(3) [226, 130, 172]
92
+ ```
93
+
94
+ #### Compatibility
95
+
96
+ | Property | Available | Description |
97
+ | --------- | --------- | -------------|
98
+ | encoding | ✔ | utf-8 |
99
+
100
+ | Method | Available | Description |
101
+ | ------- | --------- | -------------|
102
+ | encode(string) | ✔ |
103
+ | encodeInto(string, uint8Array) | ✔ |
104
+
105
+ ### TextDecoder
106
+
107
+ ```javascript
108
+ import { TextDecoder } from "mphttpx";
109
+
110
+ const utf8decoder = new TextDecoder(); // default 'utf-8'
111
+ const encodedText = new Uint8Array([240, 160, 174, 183]);
112
+
113
+ console.log(utf8decoder.decode(encodedText));
114
+ ```
115
+
116
+ #### Compatibility
117
+
118
+ | Property | Available | Description |
119
+ | --------- | --------- | -------------|
120
+ | encoding | ✔ | utf-8 |
121
+ | fatal | ✔ |
122
+ | ignoreBOM | ✔ |
123
+
124
+ | Method | Available | Description |
125
+ | ------- | --------- | -------------|
126
+ | decode() | ✔ |
127
+ | decode(buffer) | ✔ |
128
+ | decode(buffer, options) | ✔ |
129
+
130
+ ### Blob
131
+
132
+ Creating a blob
133
+
134
+ ```javascript
135
+ import { Blob } from "mphttpx";
136
+
137
+ const obj = { hello: "world" };
138
+ const blob = new Blob([JSON.stringify(obj, null, 2)], {
139
+ type: "application/json",
140
+ });
141
+ ```
142
+
143
+ Extracting data from a blob
144
+
145
+ ```javascript
146
+ import { Blob, FileReader } from "mphttpx";
147
+
148
+ const reader = new FileReader();
149
+ reader.addEventListener("loadend", () => {
150
+ // reader.result contains the contents of blob as a typed array
151
+ });
152
+ reader.readAsArrayBuffer(blob);
153
+ ```
154
+
155
+ #### Compatibility
156
+
157
+ | Property | Available | Description |
158
+ | --------- | --------- | -------------|
159
+ | size | ✔ |
160
+ | type | ✔ |
161
+
162
+ | Method | Available | Description |
163
+ | ------- | --------- | -------------|
164
+ | arrayBuffer() | ✔ |
165
+ | bytes() | ✔ |
166
+ | slice() | ✔ |
167
+ | slice(start) | ✔ |
168
+ | slice(start, end) | ✔ |
169
+ | slice(start, end, contentType) | ✔ |
170
+ | stream() | ✖ |
171
+ | text() | ✔ |
172
+
173
+ ### File
174
+
175
+ ```javascript
176
+ import { File } from "mphttpx";
177
+
178
+ const file = new File(["foo"], "foo.txt", {
179
+ type: "text/plain",
180
+ });
181
+ ```
182
+
183
+ #### Compatibility
184
+
185
+ | Property | Available | Description |
186
+ | --------- | --------- | -------------|
187
+ | lastModified | ✔ |
188
+ | name | ✔ |
189
+ | webkitRelativePath | ✖ |
190
+
191
+ ### FileReader
192
+
193
+ ```javascript
194
+ import { File, FileReader } from "mphttpx";
195
+
196
+ // Read the file
197
+ const reader = new FileReader();
198
+ reader.onload = () => {
199
+ console.log(reader.result);
200
+ };
201
+ reader.readAsText(file);
202
+ ```
203
+
204
+ #### Compatibility
205
+
206
+ | Property | Available | Description |
207
+ | --------- | --------- | -------------|
208
+ | error | ✔ |
209
+ | readyState | ✔ |
210
+ | result | ✔ |
211
+
212
+ | Method | Available | Description |
213
+ | ------- | --------- | -------------|
214
+ | abort() | ✔ | simulated |
215
+ | readAsArrayBuffer() | ✔ |
216
+ | readAsBinaryString() | ✔ |
217
+ | readAsDataURL() | ✔ |
218
+ | readAsText() | ✔ |
219
+
220
+ ### FormData
221
+
222
+ ```javascript
223
+ import { FormData } from "mphttpx";
224
+
225
+ const formData = new FormData();
226
+ formData.append("username", "Chris");
227
+ ```
228
+
229
+ #### Compatibility
230
+
231
+ | Constructor | Available | Description |
232
+ | ----------- | --------- | -------------|
233
+ | new FormData() | ✔ |
234
+ | new FormData(form) | ✖ |
235
+ | new FormData(form, submitter) | ✖ |
236
+
237
+ | Method | Available | Description |
238
+ | ------- | --------- | -------------|
239
+ | append(name, value) | ✔ |
240
+ | append(name, value, filename) | ✔ |
241
+ | delete(name) | ✔ |
242
+ | entries() | ✔ |
243
+ | get(name) | ✔ |
244
+ | getAll(name) | ✔ |
245
+ | has(name) | ✔ |
246
+ | keys() | ✔ |
247
+ | set(name, value) | ✔ |
248
+ | set(name, value, filename) | ✔ |
249
+ | values() | ✔ |
250
+
251
+ ### URLSearchParams
252
+
253
+ ```javascript
254
+ import { URLSearchParams } from "mphttpx";
255
+
256
+ const paramsString = "q=URLUtils.searchParams&topic=api";
257
+ const searchParams = new URLSearchParams(paramsString);
258
+
259
+ // Iterating the search parameters
260
+ for (const p of searchParams) {
261
+ console.log(p);
262
+ }
263
+
264
+ console.log(searchParams.has("topic")); // true
265
+ console.log(searchParams.has("topic", "fish")); // false
266
+ console.log(searchParams.get("topic") === "api"); // true
267
+ console.log(searchParams.getAll("topic")); // ["api"]
268
+ console.log(searchParams.get("foo") === null); // true
269
+ console.log(searchParams.append("topic", "webdev"));
270
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams&topic=api&topic=webdev"
271
+ console.log(searchParams.set("topic", "More webdev"));
272
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams&topic=More+webdev"
273
+ console.log(searchParams.delete("topic"));
274
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams"
275
+ ```
276
+
277
+ Search parameters can also be an object.
278
+
279
+ ```javascript
280
+ import { URLSearchParams } from "mphttpx";
281
+
282
+ const paramsObj = { foo: "bar", baz: "bar" };
283
+ const searchParams = new URLSearchParams(paramsObj);
284
+
285
+ console.log(searchParams.toString()); // "foo=bar&baz=bar"
286
+ console.log(searchParams.has("foo")); // true
287
+ console.log(searchParams.get("foo")); // "bar"
288
+ ```
289
+
290
+ #### Compatibility
291
+
292
+ | Property | Available | Description |
293
+ | --------- | --------- | -------------|
294
+ | size | ✔ |
295
+
296
+ | Method | Available | Description |
297
+ | ------- | --------- | -------------|
298
+ | append(name, value) | ✔ |
299
+ | delete(name) | ✔ |
300
+ | delete(name, value) | ✔ |
301
+ | entries() | ✔ |
302
+ | forEach(callback) | ✔ |
303
+ | forEach(callback, thisArg) | ✔ |
304
+ | get(name) | ✔ |
305
+ | getAll(name) | ✔ |
306
+ | has(name) | ✔ |
307
+ | has(name, value) | ✔ |
308
+ | keys() | ✔ |
309
+ | set(name, value) | ✔ |
310
+ | sort() | ✔ |
311
+ | toString() | ✔ |
312
+ | values() | ✔ |
313
+
314
+ ### fetch
315
+
316
+ ```javascript
317
+ import { fetch } from "mphttpx";
318
+
319
+ fetch("http://example.com/movies.json")
320
+ .then((response) => response.json())
321
+ .then((data) => console.log(data));
322
+ ```
323
+
324
+ Using fetch() to POST JSON data
325
+
326
+ ```javascript
327
+ import { fetch } from "mphttpx";
328
+
329
+ const data = { username: "example" };
330
+
331
+ fetch("https://example.com/profile", {
332
+ method: "POST", // or 'PUT'
333
+ headers: {
334
+ "Content-Type": "application/json",
335
+ },
336
+ body: JSON.stringify(data),
337
+ })
338
+ .then((response) => response.json())
339
+ .then((data) => {
340
+ console.log("Success:", data);
341
+ })
342
+ .catch((error) => {
343
+ console.error("Error:", error);
344
+ });
345
+ ```
346
+
347
+ Uploading files
348
+
349
+ ```javascript
350
+ import { fetch, File } from "mphttpx";
351
+
352
+ const formData = new FormData();
353
+
354
+ formData.append("username", "abc123");
355
+ formData.append("file", new File(["foo"], "foo.txt", { type: "text/plain" }));
356
+
357
+ fetch("https://example.com/profile/avatar", {
358
+ method: "PUT",
359
+ body: formData,
360
+ })
361
+ .then((response) => response.json())
362
+ .then((result) => {
363
+ console.log("Success:", result);
364
+ })
365
+ .catch((error) => {
366
+ console.error("Error:", error);
367
+ });
368
+ ```
369
+
370
+ | Syntax | Available | Description |
371
+ | ------- | --------- | -------------|
372
+ | fetch(resource) | ✔ |
373
+ | fetch(resource, options) | ✔ |
374
+
375
+ #### Compatibility
376
+
377
+ Refer to Request below.
378
+
379
+ ### Headers
380
+
381
+ ```javascript
382
+ import { Headers } from "mphttpx";
383
+
384
+ const myHeaders = new Headers();
385
+
386
+ myHeaders.append("Content-Type", "text/plain");
387
+ myHeaders.get("Content-Type"); // should return 'text/plain'
388
+ ```
389
+
390
+ The same can be achieved by passing an array of arrays or an object literal to the constructor:
391
+
392
+ ```javascript
393
+ import { Headers } from "mphttpx";
394
+
395
+ let myHeaders = new Headers({
396
+ "Content-Type": "text/plain",
397
+ });
398
+
399
+ // or, using an array of arrays:
400
+ myHeaders = new Headers([["Content-Type", "text/plain"]]);
401
+
402
+ myHeaders.get("Content-Type"); // should return 'text/plain'
403
+ ```
404
+
405
+ #### Compatibility
406
+
407
+ | Method | Available | Description |
408
+ | ------- | --------- | -------------|
409
+ | append(name, value) | ✔ |
410
+ | delete(name) | ✔ |
411
+ | entries() | ✔ |
412
+ | forEach(callbackFn) | ✔ |
413
+ | forEach(callbackFn, thisArg) | ✔ |
414
+ | get(name) | ✔ |
415
+ | getSetCookie() | ✔ |
416
+ | has(name) | ✔ |
417
+ | keys() | ✔ |
418
+ | set(name, value) | ✔ |
419
+ | values() | ✔ |
420
+
421
+ ### Request
422
+
423
+ ```javascript
424
+ import { fetch, Request } from "mphttpx";
425
+
426
+ const request = new Request("https://www.mozilla.org/favicon.ico");
427
+
428
+ const url = request.url;
429
+ const method = request.method;
430
+ const credentials = request.credentials;
431
+
432
+ fetch(request)
433
+ .then((response) => response.blob())
434
+ .then((blob) => {
435
+ console.log(blob);
436
+ });
437
+ ```
438
+
9
439
  ```javascript
10
- import {
11
- TextEncoder,
12
- TextDecoder,
440
+ import { fetch, Request } from "mphttpx";
441
+
442
+ const request = new Request("https://example.com", {
443
+ method: "POST",
444
+ body: '{"foo": "bar"}',
445
+ });
13
446
 
14
- Event,
15
- EventTarget,
16
- CustomEvent,
17
- ProgressEvent,
447
+ const url = request.url;
448
+ const method = request.method;
449
+ const credentials = request.credentials;
450
+ const bodyUsed = request.bodyUsed;
451
+
452
+ fetch(request)
453
+ .then((response) => {
454
+ if (response.status === 200) {
455
+ return response.json();
456
+ } else {
457
+ throw new Error("Something went wrong on API server!");
458
+ }
459
+ })
460
+ .then((response) => {
461
+ console.debug(response);
462
+ // …
463
+ })
464
+ .catch((error) => {
465
+ console.error(error);
466
+ });
467
+ ```
18
468
 
19
- Blob,
20
- File,
21
- FileReader,
469
+ #### Compatibility
22
470
 
23
- FormData,
24
- URLSearchParams,
471
+ | Property | Available | Description |
472
+ | --------- | --------- | -------------|
473
+ | body | ✖ |
474
+ | bodyUsed | ✔ |
475
+ | cache | ✔ |
476
+ | credentials | ✔ |
477
+ | destination | ✖ |
478
+ | headers | ✔ |
479
+ | integrity | ✖ |
480
+ | keepalive | ✖ |
481
+ | method | ✔ |
482
+ | mode | ✖ |
483
+ | redirect | ✖ |
484
+ | referrer | ✖ |
485
+ | referrerPolicy | ✖ |
486
+ | signal | ✔ |
487
+ | url | ✔ |
25
488
 
26
- AbortSignal,
27
- AbortController,
489
+ | Method | Available | Description |
490
+ | ------- | --------- | -------------|
491
+ | arrayBuffer() | ✔ |
492
+ | blob() | ✔ |
493
+ | bytes() | ✔ |
494
+ | clone() | ✔ |
495
+ | formData() | ✔ |
496
+ | json() | ✔ |
497
+ | text() | ✔ |
498
+
499
+ ### Response
500
+
501
+ ```javascript
502
+ import { Response, Blob, fetch } from "mphttpx";
28
503
 
29
- XMLHttpRequest,
30
- fetch,
31
- Headers,
32
- Request,
33
- Response
34
- } from "mphttpx";
504
+ const myBlob = new Blob();
505
+ const myOptions = { status: 200, statusText: "SuperSmashingGreat!" };
506
+ const myResponse = new Response(myBlob, myOptions);
35
507
  ```
508
+
509
+ #### Compatibility
510
+
511
+ | Property | Available | Description |
512
+ | --------- | --------- | -------------|
513
+ | body | ✖ |
514
+ | bodyUsed | ✔ |
515
+ | headers | ✔ |
516
+ | ok | ✔ |
517
+ | redirected | ✖ |
518
+ | status | ✔ |
519
+ | statusText | ✔ |
520
+ | type | ✖ |
521
+ | url | ✔ |
522
+
523
+ | Method | Available | Description |
524
+ | ------- | --------- | -------------|
525
+ | arrayBuffer() | ✔ |
526
+ | blob() | ✔ |
527
+ | bytes() | ✔ |
528
+ | clone() | ✔ |
529
+ | formData() | ✔ |
530
+ | json() | ✔ |
531
+ | text() | ✔ |
532
+
533
+ ### AbortController
534
+
535
+ ```javascript
536
+ import { AbortController } from "mphttpx";
537
+
538
+ const controller = new AbortController();
539
+ ```
540
+
541
+ #### Compatibility
542
+
543
+ | Property | Available | Description |
544
+ | --------- | --------- | -------------|
545
+ | signal | ✔ |
546
+
547
+ | Method | Available | Description |
548
+ | ------- | --------- | -------------|
549
+ | abort() | ✔ |
550
+ | abort(reason) | ✔ |
551
+
552
+ ### AbortSignal
553
+
554
+ ```javascript
555
+ import { AbortController, AbortSignal, Request, fetch } from "mphttpx";
556
+
557
+ async function get() {
558
+ const controller = new AbortController();
559
+ const request = new Request("https://example.org/get", {
560
+ signal: controller.signal,
561
+ });
562
+
563
+ const response = await fetch(request);
564
+ controller.abort();
565
+ // The next line will throw `AbortError`
566
+ const text = await response.text();
567
+ console.log(text);
568
+ }
569
+ ```
570
+
571
+ #### Compatibility
572
+
573
+ | Property | Available | Description |
574
+ | --------- | --------- | -------------|
575
+ | aborted | ✔ |
576
+ | reason | ✔ |
577
+
578
+ | Method | Available | Description |
579
+ | ------- | --------- | -------------|
580
+ | throwIfAborted() | ✔ |
581
+
582
+ ### EventTarget
583
+
584
+ ```javascript
585
+ import { EventTarget, Event, CustomEvent } from "mphttpx";
586
+
587
+ const target = new EventTarget();
588
+
589
+ target.addEventListener("foo", function (evt) {
590
+ console.log(evt);
591
+ });
592
+
593
+ const evt = new Event("foo");
594
+ target.dispatchEvent(evt);
595
+
596
+ target.addEventListener("animalfound", function (evt) {
597
+ console.log(evt.detail.name);
598
+ });
599
+
600
+ const catFound = new CustomEvent("animalfound", {
601
+ detail: {
602
+ name: "cat",
603
+ },
604
+ });
605
+ target.dispatchEvent(catFound);
606
+ ```
607
+
608
+ #### Compatibility
609
+
610
+ | Method | Available | Description |
611
+ | ------- | --------- | -------------|
612
+ | addEventListener(type, listener) | ✔ |
613
+ | addEventListener(type, listener, options) | ✔ |
614
+ | addEventListener(type, listener, useCapture) | ✔ |
615
+ | dispatchEvent(event) | ✔ |
616
+ | removeEventListener(type, listener) | ✔ |
617
+ | removeEventListener(type, listener, options) | ✔ |
618
+ | removeEventListener(type, listener, useCapture) | ✔ |
619
+
620
+ ### XMLHttpRequest (mini-programs)
621
+
622
+ GET
623
+
624
+ ```javascript
625
+ import { XMLHttpRequest } from "mphttpx";
626
+
627
+ const xhr = new XMLHttpRequest();
628
+ xhr.open("GET", "https://example.com/server?foo=bar&lorem=ipsum");
629
+
630
+ xhr.onload = () => {
631
+ // Request finished. Do processing here.
632
+ };
633
+
634
+ xhr.send();
635
+ ```
636
+
637
+ POST
638
+
639
+ ```javascript
640
+ import { XMLHttpRequest } from "mphttpx";
641
+
642
+ const xhr = new XMLHttpRequest();
643
+ xhr.open("POST", "https://example.com/server");
644
+
645
+ // Send the proper header information along with the request
646
+ xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
647
+
648
+ xhr.onreadystatechange = () => {
649
+ // Call a function when the state changes.
650
+ if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
651
+ // Request finished. Do processing here.
652
+ }
653
+ };
654
+
655
+ xhr.send(JSON.stringify({ foo: "bar", lorem: "ipsum" }));
656
+ ```
657
+
658
+ #### Compatibility
659
+
660
+ | Property | Available | Description |
661
+ | -------- | --------- | -------------|
662
+ | readyState | ✔ | 2, 3: simulated |
663
+ | response | ✔ |
664
+ | responseText | ✔ |
665
+ | responseType | ✔ | The `"document"` is not supported |
666
+ | responseURL | ✔ | The `responseURL` returns the URL used in the original request. |
667
+ | responseXML | ✖ |
668
+ | status | ✔ |
669
+ | statusText | ✔ |
670
+ | timeout | ✔ | This value must be less than the default timeout of mini-programs. |
671
+ | upload | ✔ | simulated |
672
+ | withCredentials | ✖ |
673
+
674
+ | Method | Available | Description |
675
+ | ------- | --------- | -------------|
676
+ | abort() | ✔ |
677
+ | getAllResponseHeaders() | ✔ |
678
+ | getResponseHeader(headerName) | ✔ |
679
+ | open(method, url) | ✔ |
680
+ | open(method, url, async) | ✔ |
681
+ | open(method, url, async, user) | ✔ |
682
+ | open(method, url, async, user, password) | ✔ |
683
+ | overrideMimeType(mimeType) | ✖ |
684
+ | send() | ✔ |
685
+ | send(body) | ✔ |
686
+ | setRequestHeader(header, value) | ✔ |
687
+
688
+ ## UniApp & Taro
689
+
690
+ ```javascript
691
+ import { setRequest } from "mphttpx";
692
+
693
+ setRequest(uni.request);
694
+ // setRequest(Taro.request);
695
+ ```
696
+
697
+ Note: When using in UniApp or Taro, if `fetch` or `XMLHttpRequest` fails to work, try explicitly setting the request function.
698
+
699
+ ## Node.js
700
+
701
+ ```bash
702
+ npm install xhr2
703
+ ```
704
+
705
+ ```javascript
706
+ import XMLHttpRequest from "xhr2";
707
+ import { setXMLHttpRequest } from "mphttpx";
708
+
709
+ setXMLHttpRequest(XMLHttpRequest);
710
+ ```
711
+
712
+ ## License
713
+
714
+ MIT
715
+
716
+ [0]: https://github.com/eligrey/Blob.js