@whatwg-node/node-fetch 0.7.23-alpha-20250726084802-683ac7e676415a5d08903fcace71146b2a74a837 → 0.7.23-alpha-20250727153122-fb7fb010b88fa0b07d5665b008435071f7f23bc5
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/Body.js +6 -2
- package/cjs/Request.js +0 -2
- package/cjs/fetchNodeHttp.js +14 -2
- package/cjs/utils.js +44 -0
- package/esm/Body.js +7 -3
- package/esm/Request.js +0 -2
- package/esm/fetchNodeHttp.js +15 -3
- package/esm/utils.js +43 -0
- package/package.json +1 -1
- package/typings/Body.d.cts +1 -0
- package/typings/Body.d.ts +1 -0
- package/typings/Request.d.cts +0 -1
- package/typings/Request.d.ts +0 -1
- package/typings/utils.d.cts +6 -0
- package/typings/utils.d.ts +6 -0
package/cjs/Body.js
CHANGED
|
@@ -36,11 +36,13 @@ class PonyfillBody {
|
|
|
36
36
|
this.contentLength = contentLength;
|
|
37
37
|
this.bodyType = bodyType;
|
|
38
38
|
this._buffer = buffer;
|
|
39
|
+
this._signal = options.signal;
|
|
39
40
|
}
|
|
40
41
|
bodyType;
|
|
41
42
|
_bodyFactory = () => null;
|
|
42
43
|
_generatedBody = null;
|
|
43
44
|
_buffer;
|
|
45
|
+
_signal;
|
|
44
46
|
generateBody() {
|
|
45
47
|
if (this._generatedBody?.readable?.destroyed && this._buffer) {
|
|
46
48
|
this._generatedBody.readable = node_stream_1.Readable.from(this._buffer);
|
|
@@ -131,8 +133,7 @@ class PonyfillBody {
|
|
|
131
133
|
return (0, utils_js_1.fakePromise)(this._chunks);
|
|
132
134
|
}
|
|
133
135
|
if (_body.readable.destroyed) {
|
|
134
|
-
this._chunks = [];
|
|
135
|
-
return (0, utils_js_1.fakePromise)(this._chunks);
|
|
136
|
+
return (0, utils_js_1.fakePromise)((this._chunks = []));
|
|
136
137
|
}
|
|
137
138
|
const chunks = [];
|
|
138
139
|
return new Promise((resolve, reject) => {
|
|
@@ -219,6 +220,9 @@ class PonyfillBody {
|
|
|
219
220
|
limits: formDataLimits,
|
|
220
221
|
defCharset: 'utf-8',
|
|
221
222
|
});
|
|
223
|
+
if (this._signal) {
|
|
224
|
+
(0, node_stream_1.addAbortSignal)(this._signal, bb);
|
|
225
|
+
}
|
|
222
226
|
let completed = false;
|
|
223
227
|
const complete = (err) => {
|
|
224
228
|
if (completed)
|
package/cjs/Request.js
CHANGED
|
@@ -76,7 +76,6 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
|
|
|
76
76
|
this.agent = requestInit.agent;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
this._signal = requestInit?.signal || undefined;
|
|
80
79
|
}
|
|
81
80
|
headersSerializer;
|
|
82
81
|
cache;
|
|
@@ -92,7 +91,6 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
|
|
|
92
91
|
referrer;
|
|
93
92
|
referrerPolicy;
|
|
94
93
|
_url;
|
|
95
|
-
_signal;
|
|
96
94
|
get signal() {
|
|
97
95
|
this._signal ||= new AbortController().signal;
|
|
98
96
|
return this._signal;
|
package/cjs/fetchNodeHttp.js
CHANGED
|
@@ -92,10 +92,21 @@ function fetchNodeHttp(fetchRequest) {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
outputStream ||= new node_stream_1.PassThrough(
|
|
95
|
+
outputStream ||= new node_stream_1.PassThrough();
|
|
96
|
+
(0, utils_js_1.pipeThrough)({
|
|
97
|
+
src: nodeResponse,
|
|
98
|
+
dest: outputStream,
|
|
96
99
|
signal,
|
|
100
|
+
onError: e => {
|
|
101
|
+
if (!nodeResponse.destroyed) {
|
|
102
|
+
nodeResponse.destroy(e);
|
|
103
|
+
}
|
|
104
|
+
if (!outputStream.destroyed) {
|
|
105
|
+
outputStream.destroy(e);
|
|
106
|
+
}
|
|
107
|
+
reject(e);
|
|
108
|
+
},
|
|
97
109
|
});
|
|
98
|
-
nodeResponse.pipe(outputStream);
|
|
99
110
|
const statusCode = nodeResponse.statusCode || 200;
|
|
100
111
|
let statusText = nodeResponse.statusMessage || node_http_1.STATUS_CODES[statusCode];
|
|
101
112
|
if (statusText == null) {
|
|
@@ -106,6 +117,7 @@ function fetchNodeHttp(fetchRequest) {
|
|
|
106
117
|
statusText,
|
|
107
118
|
headers: nodeResponse.headers,
|
|
108
119
|
url: fetchRequest.url,
|
|
120
|
+
signal,
|
|
109
121
|
});
|
|
110
122
|
resolve(ponyfillResponse);
|
|
111
123
|
});
|
package/cjs/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.isArrayBufferView = isArrayBufferView;
|
|
|
7
7
|
exports.isNodeReadable = isNodeReadable;
|
|
8
8
|
exports.isIterable = isIterable;
|
|
9
9
|
exports.shouldRedirect = shouldRedirect;
|
|
10
|
+
exports.pipeThrough = pipeThrough;
|
|
10
11
|
exports.endStream = endStream;
|
|
11
12
|
exports.safeWrite = safeWrite;
|
|
12
13
|
const node_events_1 = require("node:events");
|
|
@@ -48,6 +49,42 @@ function isIterable(value) {
|
|
|
48
49
|
function shouldRedirect(status) {
|
|
49
50
|
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
50
51
|
}
|
|
52
|
+
function pipeThrough({ src, dest, signal, onError, }) {
|
|
53
|
+
if (onError) {
|
|
54
|
+
// listen for errors on the destination stream if necessary. if the readable
|
|
55
|
+
// stream (src) emits an error, the writable destination (dest) will be
|
|
56
|
+
// destroyed with that error (see below)
|
|
57
|
+
dest.once('error', onError);
|
|
58
|
+
}
|
|
59
|
+
src.once('error', (e) => {
|
|
60
|
+
// if the readable stream (src) emits an error during pipe, the writable
|
|
61
|
+
// destination (dest) is not closed automatically. that needs to be
|
|
62
|
+
// done manually. the readable stream is closed when error is emitted,
|
|
63
|
+
// so only the writable destination needs to be destroyed
|
|
64
|
+
dest.destroy(e);
|
|
65
|
+
});
|
|
66
|
+
if (signal) {
|
|
67
|
+
// this is faster than `import('node:signal').addAbortSignal(signal, src)`
|
|
68
|
+
const srcRef = new WeakRef(src);
|
|
69
|
+
const signalRef = new WeakRef(signal);
|
|
70
|
+
function cleanup() {
|
|
71
|
+
signalRef.deref()?.removeEventListener('abort', onAbort);
|
|
72
|
+
srcRef.deref()?.removeListener('end', cleanup);
|
|
73
|
+
srcRef.deref()?.removeListener('error', cleanup);
|
|
74
|
+
srcRef.deref()?.removeListener('close', cleanup);
|
|
75
|
+
}
|
|
76
|
+
function onAbort() {
|
|
77
|
+
srcRef.deref()?.destroy(new AbortError());
|
|
78
|
+
cleanup();
|
|
79
|
+
}
|
|
80
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
81
|
+
// this is faster than `import('node:signal').finished(src, cleanup)`
|
|
82
|
+
src.once('end', cleanup);
|
|
83
|
+
src.once('error', cleanup);
|
|
84
|
+
src.once('close', cleanup);
|
|
85
|
+
}
|
|
86
|
+
src.pipe(dest, { end: true /* already default */ });
|
|
87
|
+
}
|
|
51
88
|
function endStream(stream) {
|
|
52
89
|
// @ts-expect-error Avoid arguments adaptor trampoline https://v8.dev/blog/adaptor-frame
|
|
53
90
|
return stream.end(null, null, null);
|
|
@@ -58,3 +95,10 @@ function safeWrite(chunk, stream) {
|
|
|
58
95
|
return (0, node_events_1.once)(stream, 'drain');
|
|
59
96
|
}
|
|
60
97
|
}
|
|
98
|
+
// https://github.com/nodejs/node/blob/f692878dec6354c0a82241f224906981861bc840/lib/internal/errors.js#L961-L973
|
|
99
|
+
class AbortError extends Error {
|
|
100
|
+
constructor(message = 'The operation was aborted', options = undefined) {
|
|
101
|
+
super(message, options);
|
|
102
|
+
this.name = 'AbortError';
|
|
103
|
+
}
|
|
104
|
+
}
|
package/esm/Body.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
2
|
import { Buffer } from 'node:buffer';
|
|
3
|
-
import { Readable } from 'node:stream';
|
|
3
|
+
import { addAbortSignal, Readable } from 'node:stream';
|
|
4
4
|
import { Busboy } from '@fastify/busboy';
|
|
5
5
|
import { handleMaybePromise } from '@whatwg-node/promise-helpers';
|
|
6
6
|
import { hasArrayBufferMethod, hasBufferMethod, hasBytesMethod, PonyfillBlob } from './Blob.js';
|
|
@@ -33,11 +33,13 @@ export class PonyfillBody {
|
|
|
33
33
|
this.contentLength = contentLength;
|
|
34
34
|
this.bodyType = bodyType;
|
|
35
35
|
this._buffer = buffer;
|
|
36
|
+
this._signal = options.signal;
|
|
36
37
|
}
|
|
37
38
|
bodyType;
|
|
38
39
|
_bodyFactory = () => null;
|
|
39
40
|
_generatedBody = null;
|
|
40
41
|
_buffer;
|
|
42
|
+
_signal;
|
|
41
43
|
generateBody() {
|
|
42
44
|
if (this._generatedBody?.readable?.destroyed && this._buffer) {
|
|
43
45
|
this._generatedBody.readable = Readable.from(this._buffer);
|
|
@@ -128,8 +130,7 @@ export class PonyfillBody {
|
|
|
128
130
|
return fakePromise(this._chunks);
|
|
129
131
|
}
|
|
130
132
|
if (_body.readable.destroyed) {
|
|
131
|
-
this._chunks = [];
|
|
132
|
-
return fakePromise(this._chunks);
|
|
133
|
+
return fakePromise((this._chunks = []));
|
|
133
134
|
}
|
|
134
135
|
const chunks = [];
|
|
135
136
|
return new Promise((resolve, reject) => {
|
|
@@ -216,6 +217,9 @@ export class PonyfillBody {
|
|
|
216
217
|
limits: formDataLimits,
|
|
217
218
|
defCharset: 'utf-8',
|
|
218
219
|
});
|
|
220
|
+
if (this._signal) {
|
|
221
|
+
addAbortSignal(this._signal, bb);
|
|
222
|
+
}
|
|
219
223
|
let completed = false;
|
|
220
224
|
const complete = (err) => {
|
|
221
225
|
if (completed)
|
package/esm/Request.js
CHANGED
|
@@ -73,7 +73,6 @@ export class PonyfillRequest extends PonyfillBody {
|
|
|
73
73
|
this.agent = requestInit.agent;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
this._signal = requestInit?.signal || undefined;
|
|
77
76
|
}
|
|
78
77
|
headersSerializer;
|
|
79
78
|
cache;
|
|
@@ -89,7 +88,6 @@ export class PonyfillRequest extends PonyfillBody {
|
|
|
89
88
|
referrer;
|
|
90
89
|
referrerPolicy;
|
|
91
90
|
_url;
|
|
92
|
-
_signal;
|
|
93
91
|
get signal() {
|
|
94
92
|
this._signal ||= new AbortController().signal;
|
|
95
93
|
return this._signal;
|
package/esm/fetchNodeHttp.js
CHANGED
|
@@ -6,7 +6,7 @@ import { handleMaybePromise } from '@whatwg-node/promise-helpers';
|
|
|
6
6
|
import { PonyfillRequest } from './Request.js';
|
|
7
7
|
import { PonyfillResponse } from './Response.js';
|
|
8
8
|
import { PonyfillURL } from './URL.js';
|
|
9
|
-
import { endStream, getHeadersObj, isNodeReadable, safeWrite, shouldRedirect } from './utils.js';
|
|
9
|
+
import { endStream, getHeadersObj, isNodeReadable, pipeThrough, safeWrite, shouldRedirect, } from './utils.js';
|
|
10
10
|
function getRequestFnForProtocol(url) {
|
|
11
11
|
if (url.startsWith('http:')) {
|
|
12
12
|
return httpRequest;
|
|
@@ -89,10 +89,21 @@ export function fetchNodeHttp(fetchRequest) {
|
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
outputStream ||= new PassThrough(
|
|
92
|
+
outputStream ||= new PassThrough();
|
|
93
|
+
pipeThrough({
|
|
94
|
+
src: nodeResponse,
|
|
95
|
+
dest: outputStream,
|
|
93
96
|
signal,
|
|
97
|
+
onError: e => {
|
|
98
|
+
if (!nodeResponse.destroyed) {
|
|
99
|
+
nodeResponse.destroy(e);
|
|
100
|
+
}
|
|
101
|
+
if (!outputStream.destroyed) {
|
|
102
|
+
outputStream.destroy(e);
|
|
103
|
+
}
|
|
104
|
+
reject(e);
|
|
105
|
+
},
|
|
94
106
|
});
|
|
95
|
-
nodeResponse.pipe(outputStream);
|
|
96
107
|
const statusCode = nodeResponse.statusCode || 200;
|
|
97
108
|
let statusText = nodeResponse.statusMessage || STATUS_CODES[statusCode];
|
|
98
109
|
if (statusText == null) {
|
|
@@ -103,6 +114,7 @@ export function fetchNodeHttp(fetchRequest) {
|
|
|
103
114
|
statusText,
|
|
104
115
|
headers: nodeResponse.headers,
|
|
105
116
|
url: fetchRequest.url,
|
|
117
|
+
signal,
|
|
106
118
|
});
|
|
107
119
|
resolve(ponyfillResponse);
|
|
108
120
|
});
|
package/esm/utils.js
CHANGED
|
@@ -36,6 +36,42 @@ export function isIterable(value) {
|
|
|
36
36
|
export function shouldRedirect(status) {
|
|
37
37
|
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
38
38
|
}
|
|
39
|
+
export function pipeThrough({ src, dest, signal, onError, }) {
|
|
40
|
+
if (onError) {
|
|
41
|
+
// listen for errors on the destination stream if necessary. if the readable
|
|
42
|
+
// stream (src) emits an error, the writable destination (dest) will be
|
|
43
|
+
// destroyed with that error (see below)
|
|
44
|
+
dest.once('error', onError);
|
|
45
|
+
}
|
|
46
|
+
src.once('error', (e) => {
|
|
47
|
+
// if the readable stream (src) emits an error during pipe, the writable
|
|
48
|
+
// destination (dest) is not closed automatically. that needs to be
|
|
49
|
+
// done manually. the readable stream is closed when error is emitted,
|
|
50
|
+
// so only the writable destination needs to be destroyed
|
|
51
|
+
dest.destroy(e);
|
|
52
|
+
});
|
|
53
|
+
if (signal) {
|
|
54
|
+
// this is faster than `import('node:signal').addAbortSignal(signal, src)`
|
|
55
|
+
const srcRef = new WeakRef(src);
|
|
56
|
+
const signalRef = new WeakRef(signal);
|
|
57
|
+
function cleanup() {
|
|
58
|
+
signalRef.deref()?.removeEventListener('abort', onAbort);
|
|
59
|
+
srcRef.deref()?.removeListener('end', cleanup);
|
|
60
|
+
srcRef.deref()?.removeListener('error', cleanup);
|
|
61
|
+
srcRef.deref()?.removeListener('close', cleanup);
|
|
62
|
+
}
|
|
63
|
+
function onAbort() {
|
|
64
|
+
srcRef.deref()?.destroy(new AbortError());
|
|
65
|
+
cleanup();
|
|
66
|
+
}
|
|
67
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
68
|
+
// this is faster than `import('node:signal').finished(src, cleanup)`
|
|
69
|
+
src.once('end', cleanup);
|
|
70
|
+
src.once('error', cleanup);
|
|
71
|
+
src.once('close', cleanup);
|
|
72
|
+
}
|
|
73
|
+
src.pipe(dest, { end: true /* already default */ });
|
|
74
|
+
}
|
|
39
75
|
export function endStream(stream) {
|
|
40
76
|
// @ts-expect-error Avoid arguments adaptor trampoline https://v8.dev/blog/adaptor-frame
|
|
41
77
|
return stream.end(null, null, null);
|
|
@@ -46,3 +82,10 @@ export function safeWrite(chunk, stream) {
|
|
|
46
82
|
return once(stream, 'drain');
|
|
47
83
|
}
|
|
48
84
|
}
|
|
85
|
+
// https://github.com/nodejs/node/blob/f692878dec6354c0a82241f224906981861bc840/lib/internal/errors.js#L961-L973
|
|
86
|
+
class AbortError extends Error {
|
|
87
|
+
constructor(message = 'The operation was aborted', options = undefined) {
|
|
88
|
+
super(message, options);
|
|
89
|
+
this.name = 'AbortError';
|
|
90
|
+
}
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whatwg-node/node-fetch",
|
|
3
|
-
"version": "0.7.23-alpha-
|
|
3
|
+
"version": "0.7.23-alpha-20250727153122-fb7fb010b88fa0b07d5665b008435071f7f23bc5",
|
|
4
4
|
"description": "Fetch API implementation for Node",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"dependencies": {
|
package/typings/Body.d.cts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class PonyfillBody<TJSON = any> implements Body {
|
|
|
29
29
|
private _bodyFactory;
|
|
30
30
|
private _generatedBody;
|
|
31
31
|
private _buffer?;
|
|
32
|
+
_signal?: AbortSignal | undefined;
|
|
32
33
|
private generateBody;
|
|
33
34
|
protected handleContentLengthHeader(this: PonyfillBody & {
|
|
34
35
|
headers: Headers;
|
package/typings/Body.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class PonyfillBody<TJSON = any> implements Body {
|
|
|
29
29
|
private _bodyFactory;
|
|
30
30
|
private _generatedBody;
|
|
31
31
|
private _buffer?;
|
|
32
|
+
_signal?: AbortSignal | undefined;
|
|
32
33
|
private generateBody;
|
|
33
34
|
protected handleContentLengthHeader(this: PonyfillBody & {
|
|
34
35
|
headers: Headers;
|
package/typings/Request.d.cts
CHANGED
|
@@ -26,7 +26,6 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
|
|
|
26
26
|
referrer: string;
|
|
27
27
|
referrerPolicy: ReferrerPolicy;
|
|
28
28
|
_url: string | undefined;
|
|
29
|
-
_signal: AbortSignal | undefined;
|
|
30
29
|
get signal(): AbortSignal;
|
|
31
30
|
get url(): string;
|
|
32
31
|
_parsedUrl: URL | undefined;
|
package/typings/Request.d.ts
CHANGED
|
@@ -26,7 +26,6 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
|
|
|
26
26
|
referrer: string;
|
|
27
27
|
referrerPolicy: ReferrerPolicy;
|
|
28
28
|
_url: string | undefined;
|
|
29
|
-
_signal: AbortSignal | undefined;
|
|
30
29
|
get signal(): AbortSignal;
|
|
31
30
|
get url(): string;
|
|
32
31
|
_parsedUrl: URL | undefined;
|
package/typings/utils.d.cts
CHANGED
|
@@ -6,6 +6,12 @@ export declare function isArrayBufferView(obj: any): obj is ArrayBufferView;
|
|
|
6
6
|
export declare function isNodeReadable(obj: any): obj is Readable;
|
|
7
7
|
export declare function isIterable(value: any): value is Iterable<unknown>;
|
|
8
8
|
export declare function shouldRedirect(status?: number): boolean;
|
|
9
|
+
export declare function pipeThrough({ src, dest, signal, onError, }: {
|
|
10
|
+
src: Readable;
|
|
11
|
+
dest: Writable;
|
|
12
|
+
signal?: AbortSignal | undefined;
|
|
13
|
+
onError?: ((e: Error) => void) | undefined;
|
|
14
|
+
}): void;
|
|
9
15
|
export declare function endStream(stream: {
|
|
10
16
|
end: () => void;
|
|
11
17
|
}): void;
|
package/typings/utils.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ export declare function isArrayBufferView(obj: any): obj is ArrayBufferView;
|
|
|
6
6
|
export declare function isNodeReadable(obj: any): obj is Readable;
|
|
7
7
|
export declare function isIterable(value: any): value is Iterable<unknown>;
|
|
8
8
|
export declare function shouldRedirect(status?: number): boolean;
|
|
9
|
+
export declare function pipeThrough({ src, dest, signal, onError, }: {
|
|
10
|
+
src: Readable;
|
|
11
|
+
dest: Writable;
|
|
12
|
+
signal?: AbortSignal | undefined;
|
|
13
|
+
onError?: ((e: Error) => void) | undefined;
|
|
14
|
+
}): void;
|
|
9
15
|
export declare function endStream(stream: {
|
|
10
16
|
end: () => void;
|
|
11
17
|
}): void;
|