fetch-xhr-shim 0.0.1-beta → 1.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.
Files changed (45) hide show
  1. package/README.md +845 -0
  2. package/dist/cjs/dev.js +1 -0
  3. package/dist/cjs/encoding/TextDecoderP.js +3 -1
  4. package/dist/cjs/event-system/AbortSignalP.js +20 -17
  5. package/dist/cjs/event-system/EventTargetP.js +12 -12
  6. package/dist/cjs/fetch-api/BodyImpl.js +11 -9
  7. package/dist/cjs/fetch-api/HeadersP.js +15 -13
  8. package/dist/cjs/fetch-api/RequestP.js +4 -1
  9. package/dist/cjs/fetch-api/fetchP.js +17 -17
  10. package/dist/cjs/file-system/BlobP.js +12 -10
  11. package/dist/cjs/file-system/FileP.js +2 -2
  12. package/dist/cjs/file-system/FileReaderP.js +27 -23
  13. package/dist/cjs/fixes.js +54 -21
  14. package/dist/cjs/index.js +1 -0
  15. package/dist/cjs/network/FormDataP.js +12 -10
  16. package/dist/cjs/network/URLSearchParamsP.js +9 -9
  17. package/dist/cjs/polyfill.js +18 -15
  18. package/dist/cjs/utils.js +4 -4
  19. package/dist/dev.d.ts +2 -1
  20. package/dist/esm/dev.js +1 -1
  21. package/dist/esm/encoding/TextDecoderP.js +3 -1
  22. package/dist/esm/event-system/AbortSignalP.js +20 -17
  23. package/dist/esm/event-system/EventTargetP.js +12 -12
  24. package/dist/esm/fetch-api/BodyImpl.js +11 -9
  25. package/dist/esm/fetch-api/HeadersP.js +15 -13
  26. package/dist/esm/fetch-api/RequestP.js +4 -2
  27. package/dist/esm/fetch-api/fetchP.js +17 -17
  28. package/dist/esm/file-system/BlobP.js +12 -10
  29. package/dist/esm/file-system/FileP.js +2 -2
  30. package/dist/esm/file-system/FileReaderP.js +27 -23
  31. package/dist/esm/fixes.js +55 -23
  32. package/dist/esm/index.js +1 -1
  33. package/dist/esm/network/FormDataP.js +12 -10
  34. package/dist/esm/network/URLSearchParamsP.js +9 -9
  35. package/dist/esm/polyfill.js +19 -16
  36. package/dist/esm/utils.js +4 -4
  37. package/dist/fetch-xhr-shim.cjs.js +2795 -0
  38. package/dist/fetch-xhr-shim.cjs.min.js +1 -1
  39. package/dist/fetch-xhr-shim.esm.js +2756 -0
  40. package/dist/fetch-xhr-shim.esm.min.js +1 -1
  41. package/dist/fetch-xhr-shim.polyfill.iife.js +3382 -0
  42. package/dist/fetch-xhr-shim.polyfill.iife.min.js +1 -0
  43. package/dist/index.d.ts +5 -4
  44. package/package.json +61 -59
  45. package/polyfill.js +3382 -0
package/README.md ADDED
@@ -0,0 +1,845 @@
1
+ # fetch-xhr-shim <!-- omit in toc -->
2
+
3
+ A comprehensive polyfill for the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
4
+ and its entire ecosystem — `fetch`, `Blob`, `FormData`, `Headers`, `Request`, `Response`, `AbortController`,
5
+ and more. All network requests go through `XMLHttpRequest` under the hood,
6
+ so it works anywhere XHR is available: browsers, Node.js, mini-programs, you name it.
7
+
8
+ When a native implementation exists, it's used directly. When it doesn't, the polyfill kicks in.
9
+
10
+ ## Table of Contents <!-- omit in toc -->
11
+
12
+ - [Features](#features)
13
+ - [Installation](#installation)
14
+ - [Usage](#usage)
15
+ - [fetch](#fetch)
16
+ - [Request](#request)
17
+ - [Response](#response)
18
+ - [Headers](#headers)
19
+ - [Blob](#blob)
20
+ - [File](#file)
21
+ - [FileReader](#filereader)
22
+ - [URLSearchParams](#urlsearchparams)
23
+ - [FormData](#formdata)
24
+ - [AbortController](#abortcontroller)
25
+ - [EventTarget](#eventtarget)
26
+ - [TextEncoder](#textencoder)
27
+ - [TextDecoder](#textdecoder)
28
+ - [Fix Functions](#fix-functions)
29
+ - [Auto Import](#auto-import)
30
+ - [Node.js](#nodejs)
31
+ - [License](#license)
32
+
33
+ ## Features
34
+
35
+ - **fetch** — Promise-based HTTP client built on `XMLHttpRequest`
36
+ - **Request** / **Response** — Full `Request` and `Response` objects
37
+ - **Headers** — `Headers` with all standard methods
38
+ - **Blob** / **File** — Binary data handling with `Blob` and `File`
39
+ - **FileReader** — Read blob/file contents as text, data URL, array buffer, etc.
40
+ - **URLSearchParams** — Query string parsing and serialization
41
+ - **FormData** — Multipart form construction and parsing
42
+ - **AbortController** / **AbortSignal** — Request cancellation
43
+ - **EventTarget** / **Event** / **CustomEvent** — DOM event system
44
+ - **TextEncoder** / **TextDecoder** — UTF-8 encoding and decoding
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ npm install fetch-xhr-shim
50
+ ```
51
+
52
+ ### Importing <!-- omit in toc -->
53
+
54
+ To polyfill `globalThis.fetch` and all related APIs automatically:
55
+
56
+ ```js
57
+ import "fetch-xhr-shim/polyfill";
58
+
59
+ fetch("http://example.com/movies.json")
60
+ .then((response) => response.json())
61
+ .then((data) => console.log(data));
62
+ ```
63
+
64
+ If you prefer explicit imports, every module has two export variants:
65
+
66
+ - The **default export** (e.g. `fetch`) returns the native implementation if available, falling back to the polyfill.
67
+ - The **`P`-suffixed export** (e.g. `fetchP`) returns the polyfill implementation directly, bypassing the native check.
68
+
69
+ ```js
70
+ import { fetch } from "fetch-xhr-shim"; // native fetch if available, otherwise polyfill
71
+ import { fetchP } from "fetch-xhr-shim"; // always the polyfill version
72
+ ```
73
+
74
+ This pattern applies to every API: `Blob` / `BlobP`, `FormData` / `FormDataP`, `Headers` / `HeadersP`, and so on.
75
+
76
+ ## Usage
77
+
78
+ ### fetch
79
+
80
+ #### Example <!-- omit in toc -->
81
+
82
+ ```javascript
83
+ import { fetch } from "fetch-xhr-shim";
84
+
85
+ fetch("http://example.com/movies.json")
86
+ .then((response) => response.json())
87
+ .then((data) => console.log(data));
88
+ ```
89
+
90
+ ##### POST JSON <!-- omit in toc -->
91
+
92
+ ```javascript
93
+ import { fetch } from "fetch-xhr-shim";
94
+
95
+ const data = { username: "example" };
96
+
97
+ fetch("https://example.com/profile", {
98
+ method: "POST", // or 'PUT'
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ },
102
+ body: JSON.stringify(data),
103
+ })
104
+ .then((response) => response.json())
105
+ .then((data) => {
106
+ console.log("Success:", data);
107
+ })
108
+ .catch((error) => {
109
+ console.error("Error:", error);
110
+ });
111
+ ```
112
+
113
+ ##### Upload files <!-- omit in toc -->
114
+
115
+ ```javascript
116
+ import { fetch, File, FormData } from "fetch-xhr-shim";
117
+
118
+ const formData = new FormData();
119
+
120
+ formData.append("username", "abc123");
121
+ formData.append("file", new File(["foo"], "foo.txt", { type: "text/plain" }));
122
+
123
+ fetch("https://example.com/profile/avatar", {
124
+ method: "PUT",
125
+ body: formData,
126
+ })
127
+ .then((response) => response.json())
128
+ .then((result) => {
129
+ console.log("Success:", result);
130
+ })
131
+ .catch((error) => {
132
+ console.error("Error:", error);
133
+ });
134
+ ```
135
+
136
+ > **Under the hood:** `fetch` is built on `XMLHttpRequest`. You can swap in your own XHR implementation — handy for Node.js or custom runtimes.
137
+
138
+ ```javascript
139
+ import { setXMLHttpRequestClass } from "fetch-xhr-shim";
140
+
141
+ setXMLHttpRequestClass(another_XMLHttpRequest_Class);
142
+ ```
143
+
144
+ | Syntax | Supported |
145
+ | -------------------------- | --------- |
146
+ | `fetch(resource)` | ✔ |
147
+ | `fetch(resource, options)` | ✔ |
148
+
149
+ > See [Request](#request) for option compatibility.
150
+
151
+ ### Request
152
+
153
+ #### Example <!-- omit in toc -->
154
+
155
+ ```javascript
156
+ import { fetch, Request } from "fetch-xhr-shim";
157
+
158
+ const request = new Request("https://example.com/favicon.ico");
159
+
160
+ console.log(request.url); // "https://example.com/favicon.ico"
161
+ console.log(request.method); // "GET"
162
+ console.log(request.credentials); // "same-origin"
163
+
164
+ fetch(request)
165
+ .then((response) => response.blob())
166
+ .then((blob) => {
167
+ console.log(blob);
168
+ });
169
+ ```
170
+
171
+ ```javascript
172
+ import { fetch, Request } from "fetch-xhr-shim";
173
+
174
+ const request = new Request("https://example.com", {
175
+ method: "POST",
176
+ body: '{"foo": "bar"}',
177
+ });
178
+
179
+ console.log(request.url); // "https://example.com"
180
+ console.log(request.method); // "POST"
181
+ console.log(request.credentials); // "same-origin"
182
+ console.log(request.bodyUsed); // false
183
+
184
+ fetch(request)
185
+ .then((response) => {
186
+ if (response.status === 200) {
187
+ return response.json();
188
+ } else {
189
+ throw new Error("Something went wrong on API server!");
190
+ }
191
+ })
192
+ .then((data) => {
193
+ console.debug(data);
194
+ })
195
+ .catch((error) => {
196
+ console.error(error);
197
+ });
198
+ ```
199
+
200
+ #### Compatibility <!-- omit in toc -->
201
+
202
+ **Properties**
203
+
204
+ | Property | Supported |
205
+ | ---------------- | --------- |
206
+ | `body` | ✖ |
207
+ | `bodyUsed` | ✔ |
208
+ | `cache` | ✔ |
209
+ | `credentials` | ✔ |
210
+ | `destination` | ✖ |
211
+ | `headers` | ✔ |
212
+ | `integrity` | ✖ |
213
+ | `keepalive` | ✖ |
214
+ | `method` | ✔ |
215
+ | `mode` | ✖ |
216
+ | `redirect` | ✖ |
217
+ | `referrer` | ✖ |
218
+ | `referrerPolicy` | ✖ |
219
+ | `signal` | ✔ |
220
+ | `url` | ✔ |
221
+
222
+ **Methods**
223
+
224
+ | Method | Supported |
225
+ | --------------- | --------- |
226
+ | `arrayBuffer()` | ✔ |
227
+ | `blob()` | ✔ |
228
+ | `bytes()` | ✔ |
229
+ | `clone()` | ✔ |
230
+ | `formData()` | ✔ |
231
+ | `json()` | ✔ |
232
+ | `text()` | ✔ |
233
+
234
+ ### Response
235
+
236
+ #### Example <!-- omit in toc -->
237
+
238
+ ```javascript
239
+ import { Response, Blob, fetch } from "fetch-xhr-shim";
240
+
241
+ const myBlob = new Blob();
242
+ const myOptions = { status: 200, statusText: "SuperSmashingGreat!" };
243
+ const myResponse = new Response(myBlob, myOptions);
244
+ ```
245
+
246
+ #### Compatibility <!-- omit in toc -->
247
+
248
+ **Properties**
249
+
250
+ | Property | Supported |
251
+ | ------------ | --------- |
252
+ | `body` | ✖ |
253
+ | `bodyUsed` | ✔ |
254
+ | `headers` | ✔ |
255
+ | `ok` | ✔ |
256
+ | `redirected` | ✖ |
257
+ | `status` | ✔ |
258
+ | `statusText` | ✔ |
259
+ | `type` | ✖ |
260
+ | `url` | ✔ |
261
+
262
+ **Methods**
263
+
264
+ | Method | Supported |
265
+ | --------------- | --------- |
266
+ | `arrayBuffer()` | ✔ |
267
+ | `blob()` | ✔ |
268
+ | `bytes()` | ✔ |
269
+ | `clone()` | ✔ |
270
+ | `formData()` | ✔ |
271
+ | `json()` | ✔ |
272
+ | `text()` | ✔ |
273
+
274
+ ### Headers
275
+
276
+ #### Example <!-- omit in toc -->
277
+
278
+ ```javascript
279
+ import { Headers, fetch } from "fetch-xhr-shim";
280
+
281
+ const myHeaders = new Headers();
282
+
283
+ myHeaders.append("Content-Type", "text/plain");
284
+ myHeaders.get("Content-Type"); // 'text/plain'
285
+
286
+ fetch("https://example.com/headers", {
287
+ headers: myHeaders,
288
+ });
289
+ ```
290
+
291
+ You can also pass an object or an array of pairs to the constructor:
292
+
293
+ ```javascript
294
+ import { Headers } from "fetch-xhr-shim";
295
+
296
+ let myHeaders = new Headers({
297
+ "Content-Type": "text/plain",
298
+ });
299
+
300
+ // or, using an array of arrays:
301
+ myHeaders = new Headers([["Content-Type", "text/plain"]]);
302
+
303
+ myHeaders.get("Content-Type"); // 'text/plain'
304
+ ```
305
+
306
+ #### Compatibility <!-- omit in toc -->
307
+
308
+ | Method | Supported |
309
+ | ------------------------------ | --------- |
310
+ | `append(name, value)` | ✔ |
311
+ | `delete(name)` | ✔ |
312
+ | `entries()` | ✔ |
313
+ | `forEach(callbackFn)` | ✔ |
314
+ | `forEach(callbackFn, thisArg)` | ✔ |
315
+ | `get(name)` | ✔ |
316
+ | `getSetCookie()` | ✖ |
317
+ | `has(name)` | ✔ |
318
+ | `keys()` | ✔ |
319
+ | `set(name, value)` | ✔ |
320
+ | `values()` | ✔ |
321
+
322
+ ### Blob
323
+
324
+ #### Example <!-- omit in toc -->
325
+
326
+ ##### Create a blob <!-- omit in toc -->
327
+
328
+ ```javascript
329
+ import { Blob, fetch } from "fetch-xhr-shim";
330
+
331
+ const obj = { hello: "world" };
332
+ const blob = new Blob([JSON.stringify(obj, null, 2)], {
333
+ type: "application/json",
334
+ });
335
+
336
+ const another_blob = new Blob(["Hello, World!"], {
337
+ type: "text/plain"
338
+ });
339
+
340
+ fetch("https://example.com/blob", {
341
+ method: "POST",
342
+ body: another_blob,
343
+ });
344
+ ```
345
+
346
+ ##### Extract data from a blob <!-- omit in toc -->
347
+
348
+ ```javascript
349
+ import { Blob, FileReader, fetch } from "fetch-xhr-shim";
350
+
351
+ const blob = new Blob([JSON.stringify({ foo: "bar" })], {
352
+ type: "application/json",
353
+ });
354
+
355
+ const reader = new FileReader();
356
+ reader.addEventListener("loadend", () => {
357
+ // reader.result contains the contents of blob as a typed array
358
+ });
359
+ reader.readAsArrayBuffer(blob);
360
+
361
+ fetch("https://example.com/blob", {
362
+ method: "POST",
363
+ body: blob,
364
+ })
365
+ .then(r => r.blob())
366
+ .then(r => {
367
+ const reader2 = new FileReader();
368
+ reader2.onload = () => {
369
+ // reader2.result
370
+ }
371
+ reader2.readAsDataURL(r); // base64
372
+ });
373
+ ```
374
+
375
+ > If you call `blob.stream()` in an environment without `ReadableStream`, you can inject your own implementation:
376
+
377
+ ```javascript
378
+ import { setReadableStreamClass } from "fetch-xhr-shim";
379
+
380
+ setReadableStreamClass(another_ReadableStream_Class);
381
+ ```
382
+
383
+ #### Compatibility <!-- omit in toc -->
384
+
385
+ **Properties**
386
+
387
+ | Property | Supported |
388
+ | -------- | --------- |
389
+ | `size` | ✔ |
390
+ | `type` | ✔ |
391
+
392
+ **Methods**
393
+
394
+ | Method | Supported |
395
+ | -------------------------------- | --------- |
396
+ | `arrayBuffer()` | ✔ |
397
+ | `bytes()` | ✔ |
398
+ | `slice()` | ✔ |
399
+ | `slice(start)` | ✔ |
400
+ | `slice(start, end)` | ✔ |
401
+ | `slice(start, end, contentType)` | ✔ |
402
+ | `stream()` | ✔ |
403
+ | `text()` | ✔ |
404
+
405
+ ### File
406
+
407
+ #### Example <!-- omit in toc -->
408
+
409
+ ```javascript
410
+ import { File } from "fetch-xhr-shim";
411
+
412
+ const file = new File(["foo"], "foo.txt", {
413
+ type: "text/plain",
414
+ });
415
+ ```
416
+
417
+ #### Compatibility <!-- omit in toc -->
418
+
419
+ | Property | Supported |
420
+ | -------------------- | --------- |
421
+ | `lastModified` | ✔ |
422
+ | `name` | ✔ |
423
+ | `webkitRelativePath` | ✖ |
424
+
425
+ ### FileReader
426
+
427
+ #### Example <!-- omit in toc -->
428
+
429
+ ```javascript
430
+ import { File, FileReader } from "fetch-xhr-shim";
431
+
432
+ const file = new File([JSON.stringify({ foo: "bar" })], "test.json", {
433
+ type: "application/json",
434
+ });
435
+
436
+ // Read the file
437
+ const reader = new FileReader();
438
+ reader.onload = () => {
439
+ console.log(reader.result);
440
+ };
441
+ reader.readAsText(file);
442
+ ```
443
+
444
+ #### Compatibility <!-- omit in toc -->
445
+
446
+ **Properties**
447
+
448
+ | Property | Supported |
449
+ | ------------ | --------- |
450
+ | `error` | ✔ |
451
+ | `readyState` | ✔ |
452
+ | `result` | ✔ |
453
+
454
+ **Methods**
455
+
456
+ | Method | Supported | Notes |
457
+ | ---------------------- | --------- | ---------- |
458
+ | `abort()` | ✔ | simulated |
459
+ | `readAsArrayBuffer()` | ✔ | |
460
+ | `readAsBinaryString()` | ✔ | |
461
+ | `readAsDataURL()` | ✔ | |
462
+ | `readAsText()` | ✔ | UTF-8 only |
463
+
464
+ ### URLSearchParams
465
+
466
+ #### Example <!-- omit in toc -->
467
+
468
+ ```javascript
469
+ import { URLSearchParams, fetch } from "fetch-xhr-shim";
470
+
471
+ const paramsString = "q=URLUtils.searchParams&topic=api";
472
+ const searchParams = new URLSearchParams(paramsString);
473
+
474
+ // Iterating the search parameters
475
+ for (const p of searchParams) {
476
+ console.log(p);
477
+ }
478
+
479
+ console.log(searchParams.has("topic")); // true
480
+ console.log(searchParams.has("topic", "fish")); // false
481
+ console.log(searchParams.get("topic") === "api"); // true
482
+ console.log(searchParams.getAll("topic")); // ["api"]
483
+ console.log(searchParams.get("foo") === null); // true
484
+ searchParams.append("topic", "webdev");
485
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams&topic=api&topic=webdev"
486
+ searchParams.set("topic", "More webdev");
487
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams&topic=More+webdev"
488
+ searchParams.delete("topic");
489
+ console.log(searchParams.toString()); // "q=URLUtils.searchParams"
490
+
491
+ // GET
492
+ fetch("https://example.com/get" + `?${searchParams.toString()}`);
493
+
494
+ // POST
495
+ fetch("https://example.com/post", {
496
+ method: "POST",
497
+ body: searchParams,
498
+ });
499
+ ```
500
+
501
+ ##### From an object <!-- omit in toc -->
502
+
503
+ Search params can also be initialized from a plain object.
504
+
505
+ ```javascript
506
+ import { URLSearchParams } from "fetch-xhr-shim";
507
+
508
+ const paramsObj = { foo: "bar", baz: "bar" };
509
+ const searchParams = new URLSearchParams(paramsObj);
510
+
511
+ console.log(searchParams.toString()); // "foo=bar&baz=bar"
512
+ console.log(searchParams.has("foo")); // true
513
+ console.log(searchParams.get("foo")); // "bar"
514
+ ```
515
+
516
+ #### Compatibility <!-- omit in toc -->
517
+
518
+ **Properties**
519
+
520
+ | Property | Supported |
521
+ | -------- | --------- |
522
+ | `size` | ✔ |
523
+
524
+ **Methods**
525
+
526
+ | Method | Supported |
527
+ | ---------------------------- | --------- |
528
+ | `append(name, value)` | ✔ |
529
+ | `delete(name)` | ✔ |
530
+ | `delete(name, value)` | ✔ |
531
+ | `entries()` | ✔ |
532
+ | `forEach(callback)` | ✔ |
533
+ | `forEach(callback, thisArg)` | ✔ |
534
+ | `get(name)` | ✔ |
535
+ | `getAll(name)` | ✔ |
536
+ | `has(name)` | ✔ |
537
+ | `has(name, value)` | ✔ |
538
+ | `keys()` | ✔ |
539
+ | `set(name, value)` | ✔ |
540
+ | `sort()` | ✔ |
541
+ | `toString()` | ✔ |
542
+ | `values()` | ✔ |
543
+
544
+ ### FormData
545
+
546
+ #### Example <!-- omit in toc -->
547
+
548
+ ```javascript
549
+ import { FormData, fetch } from "fetch-xhr-shim";
550
+
551
+ const formData = new FormData();
552
+ formData.append("username", "Chris");
553
+
554
+ const file = new File(["Hello, World!"], "file.txt", {
555
+ type: "text/plain",
556
+ });
557
+ formData.append("file", file);
558
+
559
+ fetch("https://example.com/formdata", {
560
+ method: "POST",
561
+ body: formData,
562
+ });
563
+ ```
564
+
565
+ #### Compatibility <!-- omit in toc -->
566
+
567
+ **Constructors**
568
+
569
+ | Constructor | Supported |
570
+ | ------------------------------- | --------- |
571
+ | `new FormData()` | ✔ |
572
+ | `new FormData(form)` | ✔ |
573
+ | `new FormData(form, submitter)` | ✖ |
574
+
575
+ **Methods**
576
+
577
+ | Method | Supported |
578
+ | ------------------------------- | --------- |
579
+ | `append(name, value)` | ✔ |
580
+ | `append(name, value, filename)` | ✔ |
581
+ | `delete(name)` | ✔ |
582
+ | `entries()` | ✔ |
583
+ | `get(name)` | ✔ |
584
+ | `getAll(name)` | ✔ |
585
+ | `has(name)` | ✔ |
586
+ | `keys()` | ✔ |
587
+ | `set(name, value)` | ✔ |
588
+ | `set(name, value, filename)` | ✔ |
589
+ | `values()` | ✔ |
590
+
591
+ ### AbortController
592
+
593
+ #### Example <!-- omit in toc -->
594
+
595
+ ```javascript
596
+ import { AbortController, fetch } from "fetch-xhr-shim";
597
+
598
+ const controller = new AbortController();
599
+
600
+ fetch("https://example.com/abort", {
601
+ signal: controller.signal,
602
+ });
603
+ ```
604
+
605
+ ```javascript
606
+ import { AbortController, AbortSignal, Request, fetch } from "fetch-xhr-shim";
607
+
608
+ async function get() {
609
+ const controller = new AbortController();
610
+ const request = new Request("https://example.org/get", {
611
+ signal: controller.signal,
612
+ });
613
+
614
+ const response = await fetch(request);
615
+ controller.abort();
616
+ // The next line will throw `AbortError`
617
+ const text = await response.text();
618
+ console.log(text);
619
+ }
620
+ ```
621
+
622
+ #### Compatibility <!-- omit in toc -->
623
+
624
+ **AbortController**
625
+
626
+ | Member | Supported |
627
+ | ------------------- | --------- |
628
+ | `signal` (property) | ✔ |
629
+ | `abort()` | ✔ |
630
+ | `abort(reason)` | ✔ |
631
+
632
+ **AbortSignal**
633
+
634
+ | Member | Supported |
635
+ | -------------------- | --------- |
636
+ | `aborted` (property) | ✔ |
637
+ | `reason` (property) | ✔ |
638
+ | `throwIfAborted()` | ✔ |
639
+
640
+ ### EventTarget
641
+
642
+ #### Example <!-- omit in toc -->
643
+
644
+ ```javascript
645
+ import { EventTarget, Event, CustomEvent } from "fetch-xhr-shim";
646
+
647
+ const target = new EventTarget();
648
+
649
+ target.addEventListener("foo", function (evt) {
650
+ console.log(evt);
651
+ });
652
+
653
+ const evt = new Event("foo");
654
+ target.dispatchEvent(evt);
655
+
656
+ target.addEventListener("animalfound", function (evt) {
657
+ console.log(evt.detail.name);
658
+ });
659
+
660
+ const catFound = new CustomEvent("animalfound", {
661
+ detail: {
662
+ name: "cat",
663
+ },
664
+ });
665
+ target.dispatchEvent(catFound);
666
+ ```
667
+
668
+ #### Compatibility <!-- omit in toc -->
669
+
670
+ | Method | Supported |
671
+ | ------------------------------------------------- | --------- |
672
+ | `addEventListener(type, listener)` | ✔ |
673
+ | `addEventListener(type, listener, options)` | ✔ |
674
+ | `addEventListener(type, listener, useCapture)` | ✔ |
675
+ | `dispatchEvent(event)` | ✔ |
676
+ | `removeEventListener(type, listener)` | ✔ |
677
+ | `removeEventListener(type, listener, options)` | ✔ |
678
+ | `removeEventListener(type, listener, useCapture)` | ✔ |
679
+
680
+ ### TextEncoder
681
+
682
+ #### Example <!-- omit in toc -->
683
+
684
+ ```javascript
685
+ import { TextEncoder } from "fetch-xhr-shim";
686
+
687
+ const encoder = new TextEncoder();
688
+ const encoded = encoder.encode("€");
689
+
690
+ console.log(encoded); // Uint8Array(3) [226, 130, 172]
691
+ ```
692
+
693
+ #### Compatibility <!-- omit in toc -->
694
+
695
+ | Member | Supported | Notes |
696
+ | -------------------------------- | --------- | --------- |
697
+ | `encoding` (property) | ✔ | `"utf-8"` |
698
+ | `encode(string)` | ✔ | |
699
+ | `encodeInto(string, uint8Array)` | ✔ | |
700
+
701
+ ### TextDecoder
702
+
703
+ #### Example <!-- omit in toc -->
704
+
705
+ ```javascript
706
+ import { TextDecoder } from "fetch-xhr-shim";
707
+
708
+ const utf8decoder = new TextDecoder(); // default 'utf-8'
709
+ const encodedText = new Uint8Array([240, 160, 174, 183]);
710
+
711
+ console.log(utf8decoder.decode(encodedText)); // 𠮷
712
+ ```
713
+
714
+ #### Compatibility <!-- omit in toc -->
715
+
716
+ | Member | Supported | Notes |
717
+ | ------------------------- | --------- | ---------- |
718
+ | `encoding` (property) | ✔ | UTF-8 only |
719
+ | `fatal` (property) | ✔ | |
720
+ | `ignoreBOM` (property) | ✔ | |
721
+ | `decode()` | ✔ | |
722
+ | `decode(buffer)` | ✔ | |
723
+ | `decode(buffer, options)` | ✔ | |
724
+
725
+ ## Fix Functions
726
+
727
+ ### fixFetch <!-- omit in toc -->
728
+
729
+ Wraps a native `fetch` so it can accept polyfill `Blob`, `FormData`, `Request`, and `Headers` objects as input.
730
+ Useful when you have a native `fetch` but want to pass polyfill types to it.
731
+
732
+ ```javascript
733
+ import { fixFetch } from "fetch-xhr-shim";
734
+
735
+ const _fetch = fixFetch(); // You can also pass a specific fetch function to fix.
736
+
737
+ _fetch("https://example.com/flowers.jpg")
738
+ .then((response) => response.blob());
739
+ ```
740
+
741
+ ### fixXMLHttpRequest <!-- omit in toc -->
742
+
743
+ Patches `XMLHttpRequest.prototype` so the native XHR can send polyfill `Blob` and `FormData` bodies.
744
+
745
+ ```javascript
746
+ import { fixXMLHttpRequest } from "fetch-xhr-shim";
747
+
748
+ fixXMLHttpRequest(); // You can also pass a specific XHR class to fix.
749
+ ```
750
+
751
+ ### fixWebSocket <!-- omit in toc -->
752
+
753
+ Patches `WebSocket.prototype` so the native WebSocket can send polyfill `Blob` data.
754
+
755
+ ```javascript
756
+ import { fixWebSocket } from "fetch-xhr-shim";
757
+
758
+ fixWebSocket(); // You can also pass a specific WebSocket class to fix.
759
+ ```
760
+
761
+ ## Auto Import
762
+
763
+ See [unplugin-auto-import](https://www.npmjs.com/package/unplugin-auto-import) for more details.
764
+
765
+ ```javascript
766
+ // for reference only
767
+ AutoImport({
768
+ // other configs
769
+
770
+ imports: [
771
+ // other imports
772
+
773
+ {
774
+ "fetch-xhr-shim": [
775
+ "fetch",
776
+ "Headers",
777
+ "Request",
778
+ "Response",
779
+
780
+ "Blob",
781
+ "File",
782
+ "FileReader",
783
+
784
+ "URLSearchParams",
785
+ "FormData",
786
+
787
+ "AbortController",
788
+ "AbortSignal",
789
+
790
+ "EventTarget",
791
+ "Event",
792
+ "CustomEvent",
793
+
794
+ "TextEncoder",
795
+ "TextDecoder",
796
+ ],
797
+ },
798
+
799
+ // other imports
800
+ ],
801
+
802
+ // other configs
803
+ });
804
+ ```
805
+
806
+ ## Node.js
807
+
808
+ By providing an `XMLHttpRequest` implementation, the polyfill `fetch` works in any environment — not just browsers.
809
+
810
+ For example, using the `xhr2` package:
811
+
812
+ ```bash
813
+ npm install xhr2
814
+ ```
815
+
816
+ ```javascript
817
+ import XMLHttpRequest from "xhr2";
818
+ import { setXMLHttpRequestClass } from "fetch-xhr-shim";
819
+
820
+ setXMLHttpRequestClass(XMLHttpRequest);
821
+ ```
822
+
823
+ ## License
824
+
825
+ MIT License
826
+
827
+ Copyright (c) 2026
828
+
829
+ Permission is hereby granted, free of charge, to any person obtaining a copy
830
+ of this software and associated documentation files (the "Software"), to deal
831
+ in the Software without restriction, including without limitation the rights
832
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
833
+ copies of the Software, and to permit persons to whom the Software is
834
+ furnished to do so, subject to the following conditions:
835
+
836
+ The above copyright notice and this permission notice shall be included in all
837
+ copies or substantial portions of the Software.
838
+
839
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
840
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
841
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
842
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
843
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
844
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
845
+ SOFTWARE.