@whatwg-node/node-fetch 0.5.0-alpha-20230710180612-32e574f → 0.5.0-alpha-20231025080723-49677d8
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/cjs/Blob.js +78 -58
- package/cjs/Body.js +83 -116
- package/cjs/FormData.js +3 -12
- package/cjs/Headers.js +1 -1
- package/cjs/ReadableStream.js +10 -4
- package/cjs/Request.js +4 -1
- package/cjs/TextEncoderDecoder.js +7 -0
- package/cjs/URLSearchParams.js +4 -1
- package/cjs/fetch.js +13 -8
- package/cjs/fetchCurl.js +89 -62
- package/cjs/fetchNodeHttp.js +2 -13
- package/cjs/utils.js +50 -6
- package/esm/Blob.js +79 -59
- package/esm/Body.js +84 -117
- package/esm/FormData.js +3 -12
- package/esm/Headers.js +1 -1
- package/esm/ReadableStream.js +10 -4
- package/esm/Request.js +4 -1
- package/esm/TextEncoderDecoder.js +7 -0
- package/esm/URLSearchParams.js +4 -1
- package/esm/fetch.js +13 -8
- package/esm/fetchCurl.js +90 -63
- package/esm/fetchNodeHttp.js +3 -14
- package/esm/utils.js +46 -4
- package/package.json +1 -1
- package/typings/Blob.d.cts +6 -2
- package/typings/Blob.d.ts +6 -2
- package/typings/Body.d.cts +2 -2
- package/typings/Body.d.ts +2 -2
- package/typings/FormData.d.cts +4 -2
- package/typings/FormData.d.ts +4 -2
- package/typings/TextEncoderDecoder.d.cts +2 -2
- package/typings/TextEncoderDecoder.d.ts +2 -2
- package/typings/utils.d.cts +5 -1
- package/typings/utils.d.ts +5 -1
package/esm/fetchCurl.js
CHANGED
@@ -1,75 +1,102 @@
|
|
1
|
-
|
2
|
-
import {
|
3
|
-
import { PonyfillHeaders } from './Headers.js';
|
1
|
+
import { Readable } from 'node:stream';
|
2
|
+
import { PonyfillAbortError } from './AbortError.js';
|
4
3
|
import { PonyfillResponse } from './Response.js';
|
5
|
-
import { defaultHeadersSerializer } from './utils.js';
|
6
|
-
export
|
7
|
-
const
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
import { defaultHeadersSerializer, isNodeReadable } from './utils.js';
|
5
|
+
export function fetchCurl(fetchRequest) {
|
6
|
+
const { Curl, CurlCode, CurlFeature, CurlPause, CurlProgressFunc } = globalThis['libcurl'];
|
7
|
+
const curlHandle = new Curl();
|
8
|
+
curlHandle.enable(CurlFeature.NoDataParsing);
|
9
|
+
curlHandle.setOpt('URL', fetchRequest.url);
|
10
|
+
curlHandle.setOpt('SSL_VERIFYPEER', false);
|
11
|
+
curlHandle.enable(CurlFeature.StreamResponse);
|
12
|
+
curlHandle.setStreamProgressCallback(function () {
|
13
|
+
return fetchRequest['_signal']?.aborted
|
14
|
+
? process.env.DEBUG
|
15
|
+
? CurlProgressFunc.Continue
|
16
|
+
: 1
|
17
|
+
: 0;
|
18
|
+
});
|
19
|
+
if (fetchRequest['bodyType'] === 'String') {
|
20
|
+
curlHandle.setOpt('POSTFIELDS', fetchRequest['bodyInit']);
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
const nodeReadable = (fetchRequest.body != null
|
24
|
+
? isNodeReadable(fetchRequest.body)
|
25
|
+
? fetchRequest.body
|
26
|
+
: Readable.from(fetchRequest.body)
|
27
|
+
: null);
|
28
|
+
if (nodeReadable) {
|
29
|
+
curlHandle.setOpt('UPLOAD', true);
|
30
|
+
curlHandle.setUploadStream(nodeReadable);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
if (process.env.DEBUG) {
|
34
|
+
curlHandle.setOpt('VERBOSE', true);
|
35
|
+
}
|
36
|
+
curlHandle.setOpt('TRANSFER_ENCODING', false);
|
37
|
+
curlHandle.setOpt('HTTP_TRANSFER_DECODING', true);
|
38
|
+
curlHandle.setOpt('FOLLOWLOCATION', fetchRequest.redirect === 'follow');
|
39
|
+
curlHandle.setOpt('MAXREDIRS', 20);
|
40
|
+
curlHandle.setOpt('ACCEPT_ENCODING', '');
|
41
|
+
curlHandle.setOpt('CUSTOMREQUEST', fetchRequest.method);
|
12
42
|
const headersSerializer = fetchRequest.headersSerializer || defaultHeadersSerializer;
|
13
43
|
let size;
|
14
|
-
const
|
44
|
+
const curlHeaders = headersSerializer(fetchRequest.headers, value => {
|
15
45
|
size = Number(value);
|
16
46
|
});
|
17
|
-
let easyNativeBinding;
|
18
|
-
const curlyOptions = {
|
19
|
-
// we want the unparsed binary response to be returned as a stream to us
|
20
|
-
curlyStreamResponse: true,
|
21
|
-
curlyResponseBodyParser: false,
|
22
|
-
curlyProgressCallback() {
|
23
|
-
if (easyNativeBinding == null) {
|
24
|
-
easyNativeBinding = this;
|
25
|
-
}
|
26
|
-
return fetchRequest.signal.aborted ? 1 : 0;
|
27
|
-
},
|
28
|
-
upload: nodeReadable != null,
|
29
|
-
transferEncoding: false,
|
30
|
-
httpTransferDecoding: true,
|
31
|
-
followLocation: fetchRequest.redirect === 'follow',
|
32
|
-
maxRedirs: 20,
|
33
|
-
acceptEncoding: '',
|
34
|
-
curlyStreamUpload: nodeReadable,
|
35
|
-
// this will just make libcurl use their own progress function (which is pretty neat)
|
36
|
-
// curlyProgressCallback() { return CurlProgressFunc.Continue },
|
37
|
-
// verbose: true,
|
38
|
-
httpHeader: curlyHeaders,
|
39
|
-
customRequest: fetchRequest.method,
|
40
|
-
};
|
41
47
|
if (size != null) {
|
42
|
-
|
48
|
+
curlHandle.setOpt('INFILESIZE', size);
|
43
49
|
}
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
curlHandle.setOpt('HTTPHEADER', curlHeaders);
|
51
|
+
curlHandle.enable(CurlFeature.NoHeaderParsing);
|
52
|
+
return new Promise(function promiseResolver(resolve, reject) {
|
53
|
+
let streamResolved = false;
|
54
|
+
if (fetchRequest['_signal']) {
|
55
|
+
fetchRequest['_signal'].onabort = () => {
|
56
|
+
if (streamResolved) {
|
57
|
+
curlHandle.pause(CurlPause.Recv);
|
58
|
+
}
|
59
|
+
else {
|
60
|
+
reject(new PonyfillAbortError());
|
61
|
+
curlHandle.close();
|
62
|
+
}
|
63
|
+
};
|
48
64
|
}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
throw new Error('redirects are not allowed');
|
65
|
+
curlHandle.once('end', function endListener() {
|
66
|
+
curlHandle.close();
|
67
|
+
});
|
68
|
+
curlHandle.once('error', function errorListener(error) {
|
69
|
+
if (error.isCurlError && error.code === CurlCode.CURLE_ABORTED_BY_CALLBACK) {
|
70
|
+
// this is expected
|
56
71
|
}
|
57
|
-
|
58
|
-
|
72
|
+
else {
|
73
|
+
// this is unexpected
|
74
|
+
reject(error);
|
59
75
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
76
|
+
curlHandle.close();
|
77
|
+
});
|
78
|
+
curlHandle.once('stream', function streamListener(stream, status, headersBuf) {
|
79
|
+
const headersFlat = headersBuf
|
80
|
+
.toString('utf8')
|
81
|
+
.split(/\r?\n|\r/g)
|
82
|
+
.filter(headerFilter => {
|
83
|
+
if (headerFilter && !headerFilter.startsWith('HTTP/')) {
|
84
|
+
if (fetchRequest.redirect === 'error' &&
|
85
|
+
(headerFilter.includes('location') || headerFilter.includes('Location'))) {
|
86
|
+
reject(new Error('redirect is not allowed'));
|
87
|
+
}
|
88
|
+
return true;
|
89
|
+
}
|
90
|
+
return false;
|
91
|
+
});
|
92
|
+
const headersInit = headersFlat.map(headerFlat => headerFlat.split(/:\s(.+)/).slice(0, 2));
|
93
|
+
resolve(new PonyfillResponse(stream, {
|
94
|
+
status,
|
95
|
+
headers: headersInit,
|
96
|
+
url: fetchRequest.url,
|
97
|
+
}));
|
98
|
+
streamResolved = true;
|
99
|
+
});
|
100
|
+
curlHandle.perform();
|
74
101
|
});
|
75
102
|
}
|
package/esm/fetchNodeHttp.js
CHANGED
@@ -2,11 +2,10 @@ import { request as httpRequest } from 'http';
|
|
2
2
|
import { request as httpsRequest } from 'https';
|
3
3
|
import { Readable } from 'stream';
|
4
4
|
import { createBrotliDecompress, createGunzip, createInflate } from 'zlib';
|
5
|
-
import { PonyfillAbortError } from './AbortError.js';
|
6
5
|
import { PonyfillRequest } from './Request.js';
|
7
6
|
import { PonyfillResponse } from './Response.js';
|
8
7
|
import { PonyfillURL } from './URL.js';
|
9
|
-
import { getHeadersObj } from './utils.js';
|
8
|
+
import { getHeadersObj, isNodeReadable } from './utils.js';
|
10
9
|
function getRequestFnForProtocol(url) {
|
11
10
|
if (url.startsWith('http:')) {
|
12
11
|
return httpRequest;
|
@@ -21,7 +20,7 @@ export function fetchNodeHttp(fetchRequest) {
|
|
21
20
|
try {
|
22
21
|
const requestFn = getRequestFnForProtocol(fetchRequest.url);
|
23
22
|
const nodeReadable = (fetchRequest.body != null
|
24
|
-
?
|
23
|
+
? isNodeReadable(fetchRequest.body)
|
25
24
|
? fetchRequest.body
|
26
25
|
: Readable.from(fetchRequest.body)
|
27
26
|
: null);
|
@@ -30,19 +29,9 @@ export function fetchNodeHttp(fetchRequest) {
|
|
30
29
|
const nodeRequest = requestFn(fetchRequest.url, {
|
31
30
|
method: fetchRequest.method,
|
32
31
|
headers: nodeHeaders,
|
33
|
-
signal: fetchRequest
|
32
|
+
signal: fetchRequest['_signal'] ?? undefined,
|
34
33
|
agent: fetchRequest.agent,
|
35
34
|
});
|
36
|
-
// TODO: will be removed after v16 reaches EOL
|
37
|
-
fetchRequest.signal?.addEventListener('abort', () => {
|
38
|
-
if (!nodeRequest.aborted) {
|
39
|
-
nodeRequest.abort();
|
40
|
-
}
|
41
|
-
});
|
42
|
-
// TODO: will be removed after v16 reaches EOL
|
43
|
-
nodeRequest.once('abort', (reason) => {
|
44
|
-
reject(new PonyfillAbortError(reason));
|
45
|
-
});
|
46
35
|
nodeRequest.once('response', nodeResponse => {
|
47
36
|
let responseBody = nodeResponse;
|
48
37
|
const contentEncoding = nodeResponse.headers['content-encoding'];
|
package/esm/utils.js
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
function isHeadersInstance(obj) {
|
2
|
+
return obj?.forEach != null;
|
3
|
+
}
|
1
4
|
export function getHeadersObj(headers) {
|
2
|
-
if (headers == null || !(
|
5
|
+
if (headers == null || !isHeadersInstance(headers)) {
|
3
6
|
return headers;
|
4
7
|
}
|
5
8
|
const obj = {};
|
@@ -8,9 +11,6 @@ export function getHeadersObj(headers) {
|
|
8
11
|
});
|
9
12
|
return obj;
|
10
13
|
}
|
11
|
-
export function uint8ArrayToArrayBuffer(uint8array) {
|
12
|
-
return uint8array.buffer.slice(uint8array.byteOffset, uint8array.byteOffset + uint8array.byteLength);
|
13
|
-
}
|
14
14
|
export function defaultHeadersSerializer(headers, onContentLength) {
|
15
15
|
const headerArray = [];
|
16
16
|
headers.forEach((value, key) => {
|
@@ -21,3 +21,45 @@ export function defaultHeadersSerializer(headers, onContentLength) {
|
|
21
21
|
});
|
22
22
|
return headerArray;
|
23
23
|
}
|
24
|
+
function isPromise(val) {
|
25
|
+
return val?.then != null;
|
26
|
+
}
|
27
|
+
export function fakePromise(value) {
|
28
|
+
if (isPromise(value)) {
|
29
|
+
return value;
|
30
|
+
}
|
31
|
+
// Write a fake promise to avoid the promise constructor
|
32
|
+
// being called with `new Promise` in the browser.
|
33
|
+
return {
|
34
|
+
then(resolve) {
|
35
|
+
if (resolve) {
|
36
|
+
const callbackResult = resolve(value);
|
37
|
+
if (isPromise(callbackResult)) {
|
38
|
+
return callbackResult;
|
39
|
+
}
|
40
|
+
return fakePromise(callbackResult);
|
41
|
+
}
|
42
|
+
return this;
|
43
|
+
},
|
44
|
+
catch() {
|
45
|
+
return this;
|
46
|
+
},
|
47
|
+
finally(cb) {
|
48
|
+
if (cb) {
|
49
|
+
const callbackResult = cb();
|
50
|
+
if (isPromise(callbackResult)) {
|
51
|
+
return callbackResult.then(() => value);
|
52
|
+
}
|
53
|
+
return fakePromise(value);
|
54
|
+
}
|
55
|
+
return this;
|
56
|
+
},
|
57
|
+
[Symbol.toStringTag]: 'Promise',
|
58
|
+
};
|
59
|
+
}
|
60
|
+
export function isArrayBufferView(obj) {
|
61
|
+
return obj != null && obj.buffer != null && obj.byteLength != null && obj.byteOffset != null;
|
62
|
+
}
|
63
|
+
export function isNodeReadable(obj) {
|
64
|
+
return obj != null && obj.pipe != null;
|
65
|
+
}
|
package/package.json
CHANGED
package/typings/Blob.d.cts
CHANGED
@@ -10,14 +10,18 @@ interface BlobOptions {
|
|
10
10
|
* is performed.
|
11
11
|
*/
|
12
12
|
type?: string | undefined;
|
13
|
+
/**
|
14
|
+
* The size of the Blob object in bytes.
|
15
|
+
*/
|
16
|
+
size?: number | null;
|
13
17
|
}
|
14
18
|
export declare class PonyfillBlob implements Blob {
|
15
19
|
private blobParts;
|
16
20
|
type: string;
|
17
21
|
private encoding;
|
22
|
+
private _size;
|
18
23
|
constructor(blobParts: BlobPart[], options?: BlobOptions);
|
19
|
-
|
20
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
24
|
+
arrayBuffer(): Promise<Buffer>;
|
21
25
|
text(): Promise<string>;
|
22
26
|
get size(): number;
|
23
27
|
stream(): any;
|
package/typings/Blob.d.ts
CHANGED
@@ -10,14 +10,18 @@ interface BlobOptions {
|
|
10
10
|
* is performed.
|
11
11
|
*/
|
12
12
|
type?: string | undefined;
|
13
|
+
/**
|
14
|
+
* The size of the Blob object in bytes.
|
15
|
+
*/
|
16
|
+
size?: number | null;
|
13
17
|
}
|
14
18
|
export declare class PonyfillBlob implements Blob {
|
15
19
|
private blobParts;
|
16
20
|
type: string;
|
17
21
|
private encoding;
|
22
|
+
private _size;
|
18
23
|
constructor(blobParts: BlobPart[], options?: BlobOptions);
|
19
|
-
|
20
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
24
|
+
arrayBuffer(): Promise<Buffer>;
|
21
25
|
text(): Promise<string>;
|
22
26
|
get size(): number;
|
23
27
|
stream(): any;
|
package/typings/Body.d.cts
CHANGED
@@ -27,15 +27,15 @@ export declare class PonyfillBody<TJSON = any> implements Body {
|
|
27
27
|
private bodyType?;
|
28
28
|
private _bodyFactory;
|
29
29
|
private _generatedBody;
|
30
|
+
private _buffer?;
|
30
31
|
private generateBody;
|
31
32
|
get body(): PonyfillReadableStream<Uint8Array> | null;
|
32
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
33
33
|
_collectChunksFromReadable(): Promise<Uint8Array[]>;
|
34
34
|
blob(): Promise<PonyfillBlob>;
|
35
35
|
formData(opts?: {
|
36
36
|
formDataLimits: FormDataLimits;
|
37
37
|
}): Promise<PonyfillFormData>;
|
38
|
-
|
38
|
+
arrayBuffer(): Promise<Buffer>;
|
39
39
|
json(): Promise<TJSON>;
|
40
40
|
text(): Promise<string>;
|
41
41
|
}
|
package/typings/Body.d.ts
CHANGED
@@ -27,15 +27,15 @@ export declare class PonyfillBody<TJSON = any> implements Body {
|
|
27
27
|
private bodyType?;
|
28
28
|
private _bodyFactory;
|
29
29
|
private _generatedBody;
|
30
|
+
private _buffer?;
|
30
31
|
private generateBody;
|
31
32
|
get body(): PonyfillReadableStream<Uint8Array> | null;
|
32
|
-
arrayBuffer(): Promise<ArrayBuffer>;
|
33
33
|
_collectChunksFromReadable(): Promise<Uint8Array[]>;
|
34
34
|
blob(): Promise<PonyfillBlob>;
|
35
35
|
formData(opts?: {
|
36
36
|
formDataLimits: FormDataLimits;
|
37
37
|
}): Promise<PonyfillFormData>;
|
38
|
-
|
38
|
+
arrayBuffer(): Promise<Buffer>;
|
39
39
|
json(): Promise<TJSON>;
|
40
40
|
text(): Promise<string>;
|
41
41
|
}
|
package/typings/FormData.d.cts
CHANGED
@@ -2,12 +2,14 @@ import { PonyfillBlob } from './Blob.cjs';
|
|
2
2
|
import { PonyfillReadableStream } from './ReadableStream.cjs';
|
3
3
|
export declare class PonyfillFormData implements FormData {
|
4
4
|
private map;
|
5
|
-
append(name: string, value:
|
5
|
+
append(name: string, value: string): void;
|
6
|
+
append(name: string, value: PonyfillBlob, fileName?: string): void;
|
6
7
|
delete(name: string): void;
|
7
8
|
get(name: string): FormDataEntryValue | null;
|
8
9
|
getAll(name: string): FormDataEntryValue[];
|
9
10
|
has(name: string): boolean;
|
10
|
-
set(name: string, value:
|
11
|
+
set(name: string, value: string): void;
|
12
|
+
set(name: string, value: PonyfillBlob, fileName?: string): void;
|
11
13
|
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
|
12
14
|
entries(): IterableIterator<[string, FormDataEntryValue]>;
|
13
15
|
keys(): IterableIterator<string>;
|
package/typings/FormData.d.ts
CHANGED
@@ -2,12 +2,14 @@ import { PonyfillBlob } from './Blob.js';
|
|
2
2
|
import { PonyfillReadableStream } from './ReadableStream.js';
|
3
3
|
export declare class PonyfillFormData implements FormData {
|
4
4
|
private map;
|
5
|
-
append(name: string, value:
|
5
|
+
append(name: string, value: string): void;
|
6
|
+
append(name: string, value: PonyfillBlob, fileName?: string): void;
|
6
7
|
delete(name: string): void;
|
7
8
|
get(name: string): FormDataEntryValue | null;
|
8
9
|
getAll(name: string): FormDataEntryValue[];
|
9
10
|
has(name: string): boolean;
|
10
|
-
set(name: string, value:
|
11
|
+
set(name: string, value: string): void;
|
12
|
+
set(name: string, value: PonyfillBlob, fileName?: string): void;
|
11
13
|
[Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
|
12
14
|
entries(): IterableIterator<[string, FormDataEntryValue]>;
|
13
15
|
keys(): IterableIterator<string>;
|
@@ -9,7 +9,7 @@ export declare class PonyfillTextDecoder implements TextDecoder {
|
|
9
9
|
encoding: BufferEncoding;
|
10
10
|
fatal: boolean;
|
11
11
|
ignoreBOM: boolean;
|
12
|
-
constructor(encoding
|
13
|
-
decode(input:
|
12
|
+
constructor(encoding?: BufferEncoding, options?: TextDecoderOptions);
|
13
|
+
decode(input: BufferSource): string;
|
14
14
|
}
|
15
15
|
export declare function PonyfillBtoa(input: string): string;
|
@@ -9,7 +9,7 @@ export declare class PonyfillTextDecoder implements TextDecoder {
|
|
9
9
|
encoding: BufferEncoding;
|
10
10
|
fatal: boolean;
|
11
11
|
ignoreBOM: boolean;
|
12
|
-
constructor(encoding
|
13
|
-
decode(input:
|
12
|
+
constructor(encoding?: BufferEncoding, options?: TextDecoderOptions);
|
13
|
+
decode(input: BufferSource): string;
|
14
14
|
}
|
15
15
|
export declare function PonyfillBtoa(input: string): string;
|
package/typings/utils.d.cts
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
import { Readable } from 'node:stream';
|
1
3
|
export declare function getHeadersObj(headers: Headers): Record<string, string>;
|
2
|
-
export declare function uint8ArrayToArrayBuffer(uint8array: Uint8Array): ArrayBuffer;
|
3
4
|
export declare function defaultHeadersSerializer(headers: Headers, onContentLength?: (value: string) => void): string[];
|
5
|
+
export declare function fakePromise<T>(value: T): Promise<T>;
|
6
|
+
export declare function isArrayBufferView(obj: any): obj is ArrayBufferView;
|
7
|
+
export declare function isNodeReadable(obj: any): obj is Readable;
|
package/typings/utils.d.ts
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
import { Readable } from 'node:stream';
|
1
3
|
export declare function getHeadersObj(headers: Headers): Record<string, string>;
|
2
|
-
export declare function uint8ArrayToArrayBuffer(uint8array: Uint8Array): ArrayBuffer;
|
3
4
|
export declare function defaultHeadersSerializer(headers: Headers, onContentLength?: (value: string) => void): string[];
|
5
|
+
export declare function fakePromise<T>(value: T): Promise<T>;
|
6
|
+
export declare function isArrayBufferView(obj: any): obj is ArrayBufferView;
|
7
|
+
export declare function isNodeReadable(obj: any): obj is Readable;
|