Mirror: A Node.js fetch shim using built-in Request, Response, and Headers (but without native fetch)
1import * as buffer from 'node:buffer';
2
3type Or<T, U> = void extends T ? U : T;
4
5export type HeadersInit =
6 | string[][]
7 | Record<string, string | ReadonlyArray<string>>
8 | _Headers;
9
10export type FormDataEntryValue = string | _File;
11
12export type RequestInfo = string | _URL | _Request;
13
14interface _Iterable<T, TReturn = any, TNext = any>
15 extends Or<
16 Iterable<T, TReturn, TNext>,
17 globalThis.Iterable<T, TReturn, TNext>
18 > {}
19interface _AsyncIterable<T, TReturn = any, TNext = any>
20 extends Or<
21 AsyncIterable<T, TReturn, TNext>,
22 globalThis.AsyncIterable<T, TReturn, TNext>
23 > {}
24interface _ReadableStream<T = any>
25 extends Or<ReadableStream<T>, globalThis.ReadableStream<T>> {}
26
27// NOTE: AsyncIterable<Uint8Array> is left out
28export type BodyInit =
29 | ArrayBuffer
30 | _Blob
31 | NodeJS.ArrayBufferView
32 | _URLSearchParams
33 | _ReadableStream
34 | _AsyncIterable<Uint8Array>
35 | _FormData
36 | _Iterable<Uint8Array>
37 | null
38 | string;
39
40// See: https://nodejs.org/docs/latest-v20.x/api/globals.html#class-file
41// The `File` global was only added in Node.js 20
42interface _File extends _Blob, Or<File, globalThis.File> {
43 readonly name: string;
44 readonly lastModified: number;
45}
46interface _File extends Or<globalThis.File, buffer.File> {}
47interface FileClass extends Or<typeof globalThis.File, typeof buffer.File> {}
48const _File: FileClass = globalThis.File || buffer.File;
49if (typeof globalThis.File === 'undefined') {
50 globalThis.File = _File;
51}
52
53// There be dragons here.
54// This is complex because of overlapping definitions in lib.dom, @types/node, and undici-types
55// Some types define and overload constructor interfaces with type interfaces
56// Here, we have to account for global differences and split the overloads apart
57
58interface _RequestInit extends Or<RequestInit, globalThis.RequestInit> {
59 duplex?: 'half';
60}
61interface _ResponseInit extends Or<ResponseInit, globalThis.ResponseInit> {}
62
63interface _Blob extends Or<Blob, globalThis.Blob> {}
64interface BlobClass extends Or<typeof Blob, typeof globalThis.Blob> {}
65const _Blob: BlobClass = Blob;
66
67interface _URLSearchParams
68 extends Or<URLSearchParams, globalThis.URLSearchParams> {}
69interface URLSearchParamsClass
70 extends Or<typeof URLSearchParams, typeof globalThis.URLSearchParams> {}
71const _URLSearchParams: URLSearchParamsClass = URLSearchParams as any;
72
73interface _URL extends Or<URL, globalThis.URL> {}
74interface URLClass extends Or<typeof URL, typeof globalThis.URL> {}
75const _URL: URLClass = URL;
76
77interface _Request extends Or<Request, globalThis.Request> {}
78interface RequestClass extends Or<typeof Request, typeof globalThis.Request> {
79 new (
80 input: RequestInfo,
81 init?: _RequestInit | Or<RequestInit, globalThis.RequestInit>
82 ): _Request;
83}
84const _Request: RequestClass = Request;
85
86interface _Response extends Or<Response, globalThis.Response> {}
87interface ResponseClass
88 extends Or<typeof Response, typeof globalThis.Response> {
89 new (body?: BodyInit, init?: _ResponseInit): _Response;
90}
91const _Response: ResponseClass = Response;
92
93interface _Headers extends Or<Headers, globalThis.Headers> {}
94interface HeadersClass extends Or<typeof Headers, typeof globalThis.Headers> {
95 new (init?: HeadersInit): _Headers;
96}
97const _Headers: HeadersClass = Headers;
98
99interface _FormData
100 extends Or<
101 FormData & _Iterable<[string, FormDataEntryValue]>,
102 globalThis.FormData
103 > {}
104interface FormDataClass
105 extends Or<typeof FormData, typeof globalThis.FormData> {}
106const _FormData: FormDataClass = FormData;
107
108export {
109 type _RequestInit as RequestInit,
110 type _ResponseInit as ResponseInit,
111 _Blob as Blob,
112 _File as File,
113 _URL as URL,
114 _URLSearchParams as URLSearchParams,
115 _Request as Request,
116 _Response as Response,
117 _Headers as Headers,
118 _FormData as FormData,
119};