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.

fix: Add missing constructor overloads and `Blob` re-export (#2)

authored by kitten.sh and committed by

GitHub ad700fa8 4405475d

+131 -28
+5
.changeset/pink-papayas-doubt.md
··· 1 + --- 2 + 'fetch-nodeshim': patch 3 + --- 4 + 5 + Add missing constructor type overloads and add missing `Blob` re-export
+3 -3
.github/workflows/ci.yml
··· 66 66 - name: Install Dependencies 67 67 run: pnpm install --frozen-lockfile --prefer-offline 68 68 69 - - name: TypeScript 70 - run: pnpm run check 71 - 72 69 - name: Unit Tests 73 70 run: pnpm run test 71 + 72 + - name: Type checks 73 + run: pnpm run check:all 74 74 75 75 - name: Build 76 76 run: pnpm run build
+8 -2
package.json
··· 16 16 "scripts": { 17 17 "test": "vitest test", 18 18 "test:run": "vitest test --run", 19 - "check": "tsc --noEmit", 20 19 "build": "rollup -c ./scripts/rollup.config.mjs", 20 + "postbuild": "tsc --noEmit ./dist/minifetch.d.ts", 21 + "check": "tsc --noEmit", 22 + "check:node18": "tsc --noEmit -p ./tsconfig-node18.json", 23 + "check:node20": "tsc --noEmit -p ./tsconfig-node20.json", 24 + "check:all": "run-s check check:node18 check:node20", 21 25 "clean": "rimraf dist node_modules/.cache", 22 - "prepublishOnly": "run-s clean build check test:run", 26 + "prepublishOnly": "run-s clean build check:all test:run", 23 27 "prepare": "node ./scripts/prepare.js || true", 24 28 "changeset:version": "changeset version && pnpm install --lockfile-only", 25 29 "changeset:publish": "changeset publish" ··· 52 56 "@rollup/plugin-commonjs": "^28.0.2", 53 57 "@rollup/plugin-node-resolve": "^16.0.0", 54 58 "@rollup/plugin-terser": "^0.4.4", 59 + "@types-internal/node-18": "npm:@types/node@^18.19.0", 60 + "@types-internal/node-20": "npm:@types/node@^20.17.0", 55 61 "@types/node": "^22.12.0", 56 62 "busboy": "^0.3.1", 57 63 "dotenv": "^16.4.7",
+30
pnpm-lock.yaml
··· 32 32 '@rollup/plugin-terser': 33 33 specifier: ^0.4.4 34 34 version: 0.4.4(rollup@4.32.1) 35 + '@types-internal/node-18': 36 + specifier: npm:@types/node@^18.19.0 37 + version: '@types/node@18.19.74' 38 + '@types-internal/node-20': 39 + specifier: npm:@types/node@^20.17.0 40 + version: '@types/node@20.17.16' 35 41 '@types/node': 36 42 specifier: ^22.12.0 37 43 version: 22.12.0 ··· 598 604 599 605 '@types/node@12.20.55': 600 606 resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} 607 + 608 + '@types/node@18.19.74': 609 + resolution: {integrity: sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==} 610 + 611 + '@types/node@20.17.16': 612 + resolution: {integrity: sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==} 601 613 602 614 '@types/node@22.12.0': 603 615 resolution: {integrity: sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==} ··· 1908 1920 resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} 1909 1921 engines: {node: '>= 0.4'} 1910 1922 1923 + undici-types@5.26.5: 1924 + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 1925 + 1926 + undici-types@6.19.8: 1927 + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 1928 + 1911 1929 undici-types@6.20.0: 1912 1930 resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1913 1931 ··· 2620 2638 '@types/estree@1.0.6': {} 2621 2639 2622 2640 '@types/node@12.20.55': {} 2641 + 2642 + '@types/node@18.19.74': 2643 + dependencies: 2644 + undici-types: 5.26.5 2645 + 2646 + '@types/node@20.17.16': 2647 + dependencies: 2648 + undici-types: 6.19.8 2623 2649 2624 2650 '@types/node@22.12.0': 2625 2651 dependencies: ··· 4026 4052 has-bigints: 1.1.0 4027 4053 has-symbols: 1.1.0 4028 4054 which-boxed-primitive: 1.1.1 4055 + 4056 + undici-types@5.26.5: {} 4057 + 4058 + undici-types@6.19.8: {} 4029 4059 4030 4060 undici-types@6.20.0: {} 4031 4061
+1
src/body.ts
··· 1 1 import { Readable } from 'node:stream'; 2 2 import { isAnyArrayBuffer } from 'node:util/types'; 3 3 import { randomBytes } from 'node:crypto'; 4 + import { Blob, FormData, URLSearchParams } from './webstd'; 4 5 5 6 export type BodyInit = 6 7 | Exclude<RequestInit['body'], undefined | null>
+61 -23
src/webstd.ts
··· 1 - /// <reference types="@types/node" /> 2 - 3 1 import * as buffer from 'node:buffer'; 4 2 5 3 type Or<T, U> = void extends T ? U : T; 6 4 5 + export type HeadersInit = 6 + | string[][] 7 + | Record<string, string | ReadonlyArray<string>> 8 + | _Headers; 9 + 10 + export type FormDataEntryValue = string | _File; 11 + 12 + export type RequestInfo = string | _URL | _Request; 13 + 14 + interface _Iterable<T, TReturn = any, TNext = any> 15 + extends Or< 16 + Iterable<T, TReturn, TNext>, 17 + globalThis.Iterable<T, TReturn, TNext> 18 + > {} 19 + interface _AsyncIterable<T, TReturn = any, TNext = any> 20 + extends Or< 21 + AsyncIterable<T, TReturn, TNext>, 22 + globalThis.AsyncIterable<T, TReturn, TNext> 23 + > {} 24 + interface _ReadableStream<T = any> 25 + extends Or<ReadableStream<T>, globalThis.ReadableStream<T>> {} 26 + 27 + // NOTE: AsyncIterable<Uint8Array> is left out 7 28 export type BodyInit = 8 29 | ArrayBuffer 9 - | AsyncIterable<Uint8Array> 10 - | Blob 11 - | FormData 12 - | Iterable<Uint8Array> 30 + | _Blob 13 31 | NodeJS.ArrayBufferView 14 - | URLSearchParams 32 + | _URLSearchParams 33 + | _ReadableStream 34 + | _AsyncIterable<Uint8Array> 35 + | _FormData 36 + | _Iterable<Uint8Array> 15 37 | null 16 38 | string; 17 39 18 40 // See: https://nodejs.org/docs/latest-v20.x/api/globals.html#class-file 19 41 // 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; 42 + interface _File extends _Blob, Or<File, globalThis.File> { 43 + readonly name: string; 44 + readonly lastModified: number; 45 + } 46 + interface _File extends Or<globalThis.File, buffer.File> {} 47 + interface FileClass extends Or<typeof globalThis.File, typeof buffer.File> {} 48 + const _File: FileClass = globalThis.File || buffer.File; 22 49 if (typeof globalThis.File === 'undefined') { 23 50 globalThis.File = _File; 24 51 } 25 52 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 53 // There be dragons here. 36 54 // This is complex because of overlapping definitions in lib.dom, @types/node, and undici-types 37 55 // Some types define and overload constructor interfaces with type interfaces 38 56 // Here, we have to account for global differences and split the overloads apart 39 57 40 - interface _RequestInit extends Or<RequestInit, globalThis.RequestInit> {} 58 + interface _RequestInit extends Or<RequestInit, globalThis.RequestInit> { 59 + duplex?: 'half'; 60 + } 41 61 interface _ResponseInit extends Or<ResponseInit, globalThis.ResponseInit> {} 42 62 63 + interface _Blob extends Or<Blob, globalThis.Blob> {} 64 + interface BlobClass extends Or<typeof Blob, typeof globalThis.Blob> {} 65 + const _Blob: BlobClass = Blob; 66 + 43 67 interface _URLSearchParams 44 68 extends Or<URLSearchParams, globalThis.URLSearchParams> {} 45 69 interface URLSearchParamsClass ··· 51 75 const _URL: URLClass = URL; 52 76 53 77 interface _Request extends Or<Request, globalThis.Request> {} 54 - interface RequestClass extends Or<typeof Request, typeof globalThis.Request> {} 78 + interface RequestClass extends Or<typeof Request, typeof globalThis.Request> { 79 + new ( 80 + input: RequestInfo, 81 + init?: _RequestInit | Or<RequestInit, globalThis.RequestInit> 82 + ): _Request; 83 + } 55 84 const _Request: RequestClass = Request; 56 85 57 86 interface _Response extends Or<Response, globalThis.Response> {} 58 87 interface ResponseClass 59 - extends Or<typeof Response, typeof globalThis.Response> {} 88 + extends Or<typeof Response, typeof globalThis.Response> { 89 + new (body?: BodyInit, init?: _ResponseInit): _Response; 90 + } 60 91 const _Response: ResponseClass = Response; 61 92 62 93 interface _Headers extends Or<Headers, globalThis.Headers> {} 63 - interface HeadersClass extends Or<typeof Headers, typeof globalThis.Headers> {} 94 + interface HeadersClass extends Or<typeof Headers, typeof globalThis.Headers> { 95 + new (init?: HeadersInit): _Headers; 96 + } 64 97 const _Headers: HeadersClass = Headers; 65 98 66 - interface _FormData extends Or<FormData, globalThis.FormData> {} 99 + interface _FormData 100 + extends Or< 101 + FormData & _Iterable<[string, FormDataEntryValue]>, 102 + globalThis.FormData 103 + > {} 67 104 interface FormDataClass 68 105 extends Or<typeof FormData, typeof globalThis.FormData> {} 69 106 const _FormData: FormDataClass = FormData; ··· 71 108 export { 72 109 type _RequestInit as RequestInit, 73 110 type _ResponseInit as ResponseInit, 111 + _Blob as Blob, 74 112 _File as File, 75 113 _URL as URL, 76 114 _URLSearchParams as URLSearchParams,
+11
tsconfig-node18.json
··· 1 + { 2 + "extends": "./tsconfig.json", 3 + "compilerOptions": { 4 + "types": ["@types-internal/node-18"], 5 + "paths": { 6 + "@types/node": ["node_modules/@types-internal/node-18"], 7 + "@types/node/*": ["node_modules/@types-internal/node-18/*"] 8 + } 9 + }, 10 + "include": ["src", "packages"] 11 + }
+11
tsconfig-node20.json
··· 1 + { 2 + "extends": "./tsconfig.json", 3 + "compilerOptions": { 4 + "types": ["@types-internal/node-20"], 5 + "paths": { 6 + "@types/node": ["node_modules/@types-internal/node-20"], 7 + "@types/node/*": ["node_modules/@types-internal/node-20/*"] 8 + } 9 + }, 10 + "include": ["src", "packages"] 11 + }
+1
tsconfig.json
··· 1 1 { 2 2 "compilerOptions": { 3 + "types": ["@types/node"], 3 4 "baseUrl": "./", 4 5 "rootDir": ".", 5 6 "forceConsistentCasingInFileNames": true,