Mirror: A Node.js fetch shim using built-in Request, Response, and Headers (but without native fetch)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add web standard type/global re-exports and set `File` global (#1)

authored by

Phil Pluckthun and committed by
GitHub
4405475d bb7dd7ce

+97 -12
+5
.changeset/pink-flowers-wait.md
··· 1 + --- 2 + 'fetch-nodeshim': minor 3 + --- 4 + 5 + Add web standard type/globals re-exports and polyfill `File` from `node:buffer`.
+1 -1
package.json
··· 64 64 "rollup-plugin-cjs-check": "^1.0.3", 65 65 "rollup-plugin-dts": "^6.1.1", 66 66 "typescript": "^5.7.3", 67 - "undici-types": "^7.3.0", 67 + "undici-types": "^6.20.0", 68 68 "vitest": "^3.0.4" 69 69 } 70 70 }
+2 -7
pnpm-lock.yaml
··· 69 69 specifier: ^5.7.3 70 70 version: 5.7.3 71 71 undici-types: 72 - specifier: ^7.3.0 73 - version: 7.3.0 72 + specifier: ^6.20.0 73 + version: 6.20.0 74 74 vitest: 75 75 specifier: ^3.0.4 76 76 version: 3.0.4(@types/node@22.12.0)(terser@5.37.0)(yaml@2.7.0) ··· 1910 1910 1911 1911 undici-types@6.20.0: 1912 1912 resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1913 - 1914 - undici-types@7.3.0: 1915 - resolution: {integrity: sha512-z2pHpkN2BEJl3QlQo0GtfGCyuhuBbWX60vzGwyn7ex/seM2UkvyGEfEV0Qb9pXc5StNfcJpsstgaf2YTEJa63Q==} 1916 1913 1917 1914 universalify@0.1.2: 1918 1915 resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} ··· 4031 4028 which-boxed-primitive: 1.1.1 4032 4029 4033 4030 undici-types@6.20.0: {} 4034 - 4035 - undici-types@7.3.0: {} 4036 4031 4037 4032 universalify@0.1.2: {} 4038 4033
+7 -4
src/fetch.ts
··· 5 5 6 6 import { extractBody } from './body'; 7 7 import { createContentDecoder } from './encoding'; 8 + import { URL, Request, RequestInit, Response } from './webstd'; 8 9 9 10 /** Maximum allowed redirects (matching Chromium's limit) */ 10 11 const MAX_REDIRECTS = 20; ··· 102 103 return response; 103 104 } 104 105 105 - export async function fetch( 106 + async function _fetch( 106 107 input: string | URL | Request, 107 108 requestInit?: RequestInit 108 109 ): Promise<Response> { ··· 131 132 signal, 132 133 } satisfies http.RequestOptions; 133 134 134 - function _fetch( 135 + function _call( 135 136 resolve: (response: Response | Promise<Response>) => void, 136 137 reject: (reason?: any) => void 137 138 ) { ··· 196 197 requestOptions, 197 198 urlToHttpOptions((requestUrl = locationURL)) 198 199 ); 199 - return _fetch(resolve, reject); 200 + return _call(resolve, reject); 200 201 } 201 202 } 202 203 ··· 258 259 } 259 260 } 260 261 261 - return await new Promise(_fetch); 262 + return await new Promise(_call); 262 263 } 264 + 265 + export { _fetch as fetch };
+1
src/index.ts
··· 1 1 export { fetch, fetch as default } from './fetch'; 2 + export * from './webstd';
+81
src/webstd.ts
··· 1 + /// <reference types="@types/node" /> 2 + 3 + import * as buffer from 'node:buffer'; 4 + 5 + type Or<T, U> = void extends T ? U : T; 6 + 7 + export type BodyInit = 8 + | ArrayBuffer 9 + | AsyncIterable<Uint8Array> 10 + | Blob 11 + | FormData 12 + | Iterable<Uint8Array> 13 + | NodeJS.ArrayBufferView 14 + | URLSearchParams 15 + | null 16 + | string; 17 + 18 + // See: https://nodejs.org/docs/latest-v20.x/api/globals.html#class-file 19 + // The `File` global was only added in Node.js 20 20 + interface _File extends Or<File, globalThis.File> {} 21 + const _File: Or<typeof File, typeof buffer.File> = buffer.File; 22 + if (typeof globalThis.File === 'undefined') { 23 + globalThis.File = _File; 24 + } 25 + 26 + declare global { 27 + var File: typeof _File; 28 + 29 + // NOTE: In case undici was used, but its types aren't applied, this needs to be added 30 + interface RequestInit { 31 + duplex?: 'half'; 32 + } 33 + } 34 + 35 + // There be dragons here. 36 + // This is complex because of overlapping definitions in lib.dom, @types/node, and undici-types 37 + // Some types define and overload constructor interfaces with type interfaces 38 + // Here, we have to account for global differences and split the overloads apart 39 + 40 + interface _RequestInit extends Or<RequestInit, globalThis.RequestInit> {} 41 + interface _ResponseInit extends Or<ResponseInit, globalThis.ResponseInit> {} 42 + 43 + interface _URLSearchParams 44 + extends Or<URLSearchParams, globalThis.URLSearchParams> {} 45 + interface URLSearchParamsClass 46 + extends Or<typeof URLSearchParams, typeof globalThis.URLSearchParams> {} 47 + const _URLSearchParams: URLSearchParamsClass = URLSearchParams as any; 48 + 49 + interface _URL extends Or<URL, globalThis.URL> {} 50 + interface URLClass extends Or<typeof URL, typeof globalThis.URL> {} 51 + const _URL: URLClass = URL; 52 + 53 + interface _Request extends Or<Request, globalThis.Request> {} 54 + interface RequestClass extends Or<typeof Request, typeof globalThis.Request> {} 55 + const _Request: RequestClass = Request; 56 + 57 + interface _Response extends Or<Response, globalThis.Response> {} 58 + interface ResponseClass 59 + extends Or<typeof Response, typeof globalThis.Response> {} 60 + const _Response: ResponseClass = Response; 61 + 62 + interface _Headers extends Or<Headers, globalThis.Headers> {} 63 + interface HeadersClass extends Or<typeof Headers, typeof globalThis.Headers> {} 64 + const _Headers: HeadersClass = Headers; 65 + 66 + interface _FormData extends Or<FormData, globalThis.FormData> {} 67 + interface FormDataClass 68 + extends Or<typeof FormData, typeof globalThis.FormData> {} 69 + const _FormData: FormDataClass = FormData; 70 + 71 + export { 72 + type _RequestInit as RequestInit, 73 + type _ResponseInit as ResponseInit, 74 + _File as File, 75 + _URL as URL, 76 + _URLSearchParams as URLSearchParams, 77 + _Request as Request, 78 + _Response as Response, 79 + _Headers as Headers, 80 + _FormData as FormData, 81 + };