@whatwg-node/node-fetch 0.7.20-alpha-20250515210136-f3eeba6bfbd13f3930315ee75f80a9091e946e92 → 0.7.20-alpha-20250516115150-ce23c7139392555706532687427aa8535aae5852

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 CHANGED
@@ -28,11 +28,11 @@ class PonyfillBody {
28
28
  bodyUsed = false;
29
29
  contentType = null;
30
30
  contentLength = null;
31
- signal = null;
31
+ _signal = null;
32
32
  constructor(bodyInit, options = {}) {
33
33
  this.bodyInit = bodyInit;
34
34
  this.options = options;
35
- this.signal = options.signal || null;
35
+ this._signal = options.signal || null;
36
36
  const { bodyFactory, contentType, contentLength, bodyType, buffer } = processBodyInit(bodyInit, options?.signal);
37
37
  this._bodyFactory = bodyFactory;
38
38
  this.contentType = contentType;
@@ -150,6 +150,13 @@ class PonyfillBody {
150
150
  if (this._blob) {
151
151
  return (0, utils_js_1.fakePromise)(this._blob);
152
152
  }
153
+ if (this.bodyType === BodyInitType.String) {
154
+ this._text = this.bodyInit;
155
+ this._blob = new Blob_js_1.PonyfillBlob([this._text], {
156
+ type: this.contentType || 'text/plain;charset=UTF-8',
157
+ size: this.contentLength,
158
+ });
159
+ }
153
160
  if (this.bodyType === BodyInitType.Blob) {
154
161
  this._blob = this.bodyInit;
155
162
  return (0, utils_js_1.fakePromise)(this._blob);
@@ -205,8 +212,8 @@ class PonyfillBody {
205
212
  limits: formDataLimits,
206
213
  defCharset: 'utf-8',
207
214
  });
208
- if (this.signal) {
209
- (0, node_stream_1.addAbortSignal)(this.signal, bb);
215
+ if (this._signal) {
216
+ (0, node_stream_1.addAbortSignal)(this._signal, bb);
210
217
  }
211
218
  let completed = false;
212
219
  const complete = (err) => {
@@ -279,6 +286,17 @@ class PonyfillBody {
279
286
  if (this._buffer) {
280
287
  return (0, utils_js_1.fakePromise)(this._buffer);
281
288
  }
289
+ if (this._text) {
290
+ this._buffer = node_buffer_1.Buffer.from(this._text, 'utf-8');
291
+ return (0, utils_js_1.fakePromise)(this._buffer);
292
+ }
293
+ if (this.bodyType === BodyInitType.String) {
294
+ return this.text().then(text => {
295
+ this._text = text;
296
+ this._buffer = node_buffer_1.Buffer.from(text, 'utf-8');
297
+ return this._buffer;
298
+ });
299
+ }
282
300
  if (this.bodyType === BodyInitType.Blob) {
283
301
  if ((0, Blob_js_1.hasBufferMethod)(this.bodyInit)) {
284
302
  return this.bodyInit.buffer().then(buf => {
@@ -358,15 +376,13 @@ function processBodyInit(bodyInit, signal) {
358
376
  };
359
377
  }
360
378
  if (typeof bodyInit === 'string') {
361
- const buffer = node_buffer_1.Buffer.from(bodyInit);
362
- const contentLength = buffer.byteLength;
379
+ const contentLength = node_buffer_1.Buffer.byteLength(bodyInit);
363
380
  return {
364
381
  bodyType: BodyInitType.String,
365
382
  contentType: 'text/plain;charset=UTF-8',
366
383
  contentLength,
367
- buffer,
368
384
  bodyFactory() {
369
- const readable = node_stream_1.Readable.from(buffer);
385
+ const readable = node_stream_1.Readable.from(node_buffer_1.Buffer.from(bodyInit, 'utf-8'));
370
386
  return new ReadableStream_js_1.PonyfillReadableStream(readable);
371
387
  },
372
388
  };
package/cjs/Headers.js CHANGED
@@ -4,6 +4,7 @@ exports.PonyfillHeaders = void 0;
4
4
  exports.isHeadersLike = isHeadersLike;
5
5
  const node_util_1 = require("node:util");
6
6
  const IteratorObject_js_1 = require("./IteratorObject.js");
7
+ const utils_js_1 = require("./utils.js");
7
8
  function isHeadersLike(headers) {
8
9
  return headers?.get && headers?.forEach;
9
10
  }
@@ -30,7 +31,7 @@ class PonyfillHeaders {
30
31
  if (this.headersInit == null) {
31
32
  return null;
32
33
  }
33
- if (Array.isArray(this.headersInit)) {
34
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
34
35
  const found = this.headersInit.filter(([headerKey]) => headerKey.toLowerCase() === normalized);
35
36
  if (found.length === 0) {
36
37
  return null;
@@ -66,9 +67,9 @@ class PonyfillHeaders {
66
67
  // I could do a getter here, but I'm too lazy to type `getter`.
67
68
  getMap() {
68
69
  if (!this._map) {
69
- this._setCookies = [];
70
+ this._setCookies ||= [];
70
71
  if (this.headersInit != null) {
71
- if (Array.isArray(this.headersInit)) {
72
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
72
73
  this._map = new Map();
73
74
  for (const [key, value] of this.headersInit) {
74
75
  const normalizedKey = key.toLowerCase();
@@ -131,7 +132,8 @@ class PonyfillHeaders {
131
132
  return value.toString();
132
133
  }
133
134
  has(name) {
134
- if (name === 'set-cookie') {
135
+ const key = name.toLowerCase();
136
+ if (key === 'set-cookie') {
135
137
  return !!this._setCookies?.length;
136
138
  }
137
139
  return !!this._get(name); // we might need to check if header exists and not just check if it's not nullable
@@ -142,6 +144,26 @@ class PonyfillHeaders {
142
144
  this._setCookies = [value];
143
145
  return;
144
146
  }
147
+ if (!this._map && this.headersInit != null) {
148
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
149
+ const found = this.headersInit.find(([headerKey]) => headerKey.toLowerCase() === key);
150
+ if (found) {
151
+ found[1] = value;
152
+ }
153
+ else {
154
+ this.headersInit.push([key, value]);
155
+ }
156
+ return;
157
+ }
158
+ else if (isHeadersLike(this.headersInit)) {
159
+ this.headersInit.set(key, value);
160
+ return;
161
+ }
162
+ else {
163
+ this.headersInit[key] = value;
164
+ return;
165
+ }
166
+ }
145
167
  this.getMap().set(key, value);
146
168
  }
147
169
  delete(name) {
@@ -158,7 +180,7 @@ class PonyfillHeaders {
158
180
  });
159
181
  if (!this._map) {
160
182
  if (this.headersInit) {
161
- if (Array.isArray(this.headersInit)) {
183
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
162
184
  this.headersInit.forEach(([key, value]) => {
163
185
  callback(value, key, this);
164
186
  });
@@ -186,7 +208,7 @@ class PonyfillHeaders {
186
208
  }
187
209
  if (!this._map) {
188
210
  if (this.headersInit) {
189
- if (Array.isArray(this.headersInit)) {
211
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
190
212
  yield* this.headersInit.map(([key]) => key)[Symbol.iterator]();
191
213
  return;
192
214
  }
@@ -209,7 +231,7 @@ class PonyfillHeaders {
209
231
  }
210
232
  if (!this._map) {
211
233
  if (this.headersInit) {
212
- if (Array.isArray(this.headersInit)) {
234
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
213
235
  yield* this.headersInit.map(([, value]) => value)[Symbol.iterator]();
214
236
  return;
215
237
  }
@@ -232,7 +254,7 @@ class PonyfillHeaders {
232
254
  }
233
255
  if (!this._map) {
234
256
  if (this.headersInit) {
235
- if (Array.isArray(this.headersInit)) {
257
+ if ((0, utils_js_1.isArray)(this.headersInit)) {
236
258
  yield* this.headersInit;
237
259
  return;
238
260
  }
package/cjs/Request.js CHANGED
@@ -57,7 +57,6 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
57
57
  this.redirect = requestInit?.redirect || 'follow';
58
58
  this.referrer = requestInit?.referrer || 'about:client';
59
59
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
60
- this.signal = requestInit?.signal || new AbortController().signal;
61
60
  this.headersSerializer = requestInit?.headersSerializer;
62
61
  this.duplex = requestInit?.duplex || 'half';
63
62
  this.destination = 'document';
@@ -92,6 +91,10 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
92
91
  referrer;
93
92
  referrerPolicy;
94
93
  _url;
94
+ get signal() {
95
+ this._signal ||= new AbortController().signal;
96
+ return this._signal;
97
+ }
95
98
  get url() {
96
99
  if (this._url == null) {
97
100
  if (this._parsedUrl) {
@@ -117,7 +120,6 @@ class PonyfillRequest extends Body_js_1.PonyfillBody {
117
120
  }
118
121
  duplex;
119
122
  agent;
120
- signal;
121
123
  clone() {
122
124
  return this;
123
125
  }
package/cjs/Response.js CHANGED
@@ -4,6 +4,7 @@ exports.PonyfillResponse = void 0;
4
4
  const node_http_1 = require("node:http");
5
5
  const Body_js_1 = require("./Body.js");
6
6
  const Headers_js_1 = require("./Headers.js");
7
+ const utils_js_1 = require("./utils.js");
7
8
  const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
8
9
  class PonyfillResponse extends Body_js_1.PonyfillBody {
9
10
  headers;
@@ -18,7 +19,6 @@ class PonyfillResponse extends Body_js_1.PonyfillBody {
18
19
  this.url = init?.url || '';
19
20
  this.redirected = init?.redirected || false;
20
21
  this.type = init?.type || 'default';
21
- this.signal = init?.signal || null;
22
22
  this.handleContentLengthHeader();
23
23
  }
24
24
  get ok() {
@@ -49,13 +49,33 @@ class PonyfillResponse extends Body_js_1.PonyfillBody {
49
49
  status,
50
50
  });
51
51
  }
52
- static json(data, init = {}) {
53
- init.headers =
54
- init?.headers && (0, Headers_js_1.isHeadersLike)(init.headers)
55
- ? init.headers
56
- : new Headers_js_1.PonyfillHeaders(init?.headers);
57
- if (!init.headers.has('content-type')) {
58
- init.headers.set('content-type', JSON_CONTENT_TYPE);
52
+ static json(data, init) {
53
+ if (!init) {
54
+ init = {
55
+ headers: {
56
+ 'content-type': JSON_CONTENT_TYPE,
57
+ },
58
+ };
59
+ }
60
+ else if (!init.headers) {
61
+ init.headers = {
62
+ 'content-type': JSON_CONTENT_TYPE,
63
+ };
64
+ }
65
+ else if ((0, Headers_js_1.isHeadersLike)(init.headers)) {
66
+ if (!init.headers.has('content-type')) {
67
+ init.headers.set('content-type', JSON_CONTENT_TYPE);
68
+ }
69
+ }
70
+ else if ((0, utils_js_1.isArray)(init.headers)) {
71
+ if (!init.headers.some(([key]) => key.toLowerCase() === 'content-type')) {
72
+ init.headers.push(['content-type', JSON_CONTENT_TYPE]);
73
+ }
74
+ }
75
+ else if (typeof init.headers === 'object') {
76
+ if (init.headers?.['content-type'] == null) {
77
+ init.headers['content-type'] = JSON_CONTENT_TYPE;
78
+ }
59
79
  }
60
80
  return new PonyfillResponse(JSON.stringify(data), init);
61
81
  }
package/cjs/fetchCurl.js CHANGED
@@ -21,8 +21,18 @@ function fetchCurl(fetchRequest) {
21
21
  curlHandle.setOpt('CAINFO_BLOB', node_tls_1.rootCertificates.join('\n'));
22
22
  }
23
23
  curlHandle.enable(CurlFeature.StreamResponse);
24
+ let signal;
25
+ if (fetchRequest._signal === null) {
26
+ signal = undefined;
27
+ }
28
+ else if (fetchRequest._signal) {
29
+ signal = fetchRequest._signal;
30
+ }
31
+ else {
32
+ signal = fetchRequest.signal;
33
+ }
24
34
  curlHandle.setStreamProgressCallback(function () {
25
- return fetchRequest.signal.aborted ? (process.env.DEBUG ? CurlProgressFunc.Continue : 1) : 0;
35
+ return signal?.aborted ? (process.env.DEBUG ? CurlProgressFunc.Continue : 1) : 0;
26
36
  });
27
37
  if (fetchRequest['bodyType'] === 'String') {
28
38
  curlHandle.setOpt('POSTFIELDS', fetchRequest['bodyInit']);
@@ -69,9 +79,7 @@ function fetchCurl(fetchRequest) {
69
79
  }
70
80
  }
71
81
  }
72
- if (fetchRequest.signal) {
73
- fetchRequest.signal.addEventListener('abort', onAbort, { once: true });
74
- }
82
+ signal?.addEventListener('abort', onAbort, { once: true });
75
83
  curlHandle.once('end', function endListener() {
76
84
  try {
77
85
  curlHandle.close();
@@ -79,9 +87,7 @@ function fetchCurl(fetchRequest) {
79
87
  catch (e) {
80
88
  deferredPromise.reject(e);
81
89
  }
82
- if (fetchRequest.signal) {
83
- fetchRequest.signal.removeEventListener('abort', onAbort);
84
- }
90
+ signal?.removeEventListener('abort', onAbort);
85
91
  });
86
92
  curlHandle.once('error', function errorListener(error) {
87
93
  if (streamResolved && !streamResolved.closed && !streamResolved.destroyed) {
@@ -103,7 +109,7 @@ function fetchCurl(fetchRequest) {
103
109
  curlHandle.once('stream', function streamListener(stream, status, headersBuf) {
104
110
  const outputStream = (0, utils_js_1.wrapIncomingMessageWithPassthrough)({
105
111
  incomingMessage: stream,
106
- signal: fetchRequest.signal,
112
+ signal,
107
113
  onError: deferredPromise.reject,
108
114
  });
109
115
  const headersFlat = headersBuf
@@ -28,13 +28,23 @@ function fetchNodeHttp(fetchRequest) {
28
28
  if (nodeHeaders['accept-encoding'] == null) {
29
29
  nodeHeaders['accept-encoding'] = 'gzip, deflate, br';
30
30
  }
31
+ let signal;
32
+ if (fetchRequest._signal === null) {
33
+ signal = undefined;
34
+ }
35
+ else if (fetchRequest._signal) {
36
+ signal = fetchRequest._signal;
37
+ }
38
+ else {
39
+ signal = fetchRequest.signal;
40
+ }
31
41
  let nodeRequest;
32
42
  // If it is our ponyfilled Request, it should have `parsedUrl` which is a `URL` object
33
43
  if (fetchRequest.parsedUrl) {
34
44
  nodeRequest = requestFn(fetchRequest.parsedUrl, {
35
45
  method: fetchRequest.method,
36
46
  headers: nodeHeaders,
37
- signal: fetchRequest.signal,
47
+ signal,
38
48
  agent: fetchRequest.agent,
39
49
  });
40
50
  }
@@ -42,7 +52,7 @@ function fetchNodeHttp(fetchRequest) {
42
52
  nodeRequest = requestFn(fetchRequest.url, {
43
53
  method: fetchRequest.method,
44
54
  headers: nodeHeaders,
45
- signal: fetchRequest.signal,
55
+ signal,
46
56
  agent: fetchRequest.agent,
47
57
  });
48
58
  }
@@ -89,7 +99,7 @@ function fetchNodeHttp(fetchRequest) {
89
99
  outputStream = (0, utils_js_1.wrapIncomingMessageWithPassthrough)({
90
100
  incomingMessage: nodeResponse,
91
101
  passThrough: outputStream,
92
- signal: fetchRequest.signal,
102
+ signal,
93
103
  onError: reject,
94
104
  });
95
105
  }
@@ -103,12 +113,12 @@ function fetchNodeHttp(fetchRequest) {
103
113
  statusText,
104
114
  headers: nodeResponse.headers,
105
115
  url: fetchRequest.url,
106
- signal: fetchRequest.signal,
116
+ signal,
107
117
  });
108
118
  resolve(ponyfillResponse);
109
119
  });
110
120
  if (fetchRequest['_buffer'] != null) {
111
- (0, promise_helpers_1.handleMaybePromise)(() => (0, utils_js_1.safeWrite)(fetchRequest['_buffer'], nodeRequest, fetchRequest.signal), () => (0, utils_js_1.endStream)(nodeRequest), reject);
121
+ (0, promise_helpers_1.handleMaybePromise)(() => (0, utils_js_1.safeWrite)(fetchRequest['_buffer'], nodeRequest, signal), () => (0, utils_js_1.endStream)(nodeRequest), reject);
112
122
  }
113
123
  else {
114
124
  const nodeReadable = (fetchRequest.body != null
package/cjs/utils.js CHANGED
@@ -10,6 +10,7 @@ exports.shouldRedirect = shouldRedirect;
10
10
  exports.wrapIncomingMessageWithPassthrough = wrapIncomingMessageWithPassthrough;
11
11
  exports.endStream = endStream;
12
12
  exports.safeWrite = safeWrite;
13
+ exports.isArray = isArray;
13
14
  const node_events_1 = require("node:events");
14
15
  const node_stream_1 = require("node:stream");
15
16
  const promises_1 = require("node:stream/promises");
@@ -20,6 +21,11 @@ function getHeadersObj(headers) {
20
21
  if (headers == null || !isHeadersInstance(headers)) {
21
22
  return headers;
22
23
  }
24
+ // @ts-expect-error - `headersInit` is not a public property
25
+ if (headers.headersInit && !headers._map && !isHeadersInstance(headers.headersInit)) {
26
+ // @ts-expect-error - `headersInit` is not a public property
27
+ return headers.headersInit;
28
+ }
23
29
  return Object.fromEntries(headers.entries());
24
30
  }
25
31
  function defaultHeadersSerializer(headers, onContentLength) {
@@ -73,3 +79,6 @@ function safeWrite(chunk, stream, signal) {
73
79
  });
74
80
  }
75
81
  }
82
+ function isArray(value) {
83
+ return value?.splice != null;
84
+ }
package/esm/Body.js CHANGED
@@ -25,11 +25,11 @@ export class PonyfillBody {
25
25
  bodyUsed = false;
26
26
  contentType = null;
27
27
  contentLength = null;
28
- signal = null;
28
+ _signal = null;
29
29
  constructor(bodyInit, options = {}) {
30
30
  this.bodyInit = bodyInit;
31
31
  this.options = options;
32
- this.signal = options.signal || null;
32
+ this._signal = options.signal || null;
33
33
  const { bodyFactory, contentType, contentLength, bodyType, buffer } = processBodyInit(bodyInit, options?.signal);
34
34
  this._bodyFactory = bodyFactory;
35
35
  this.contentType = contentType;
@@ -147,6 +147,13 @@ export class PonyfillBody {
147
147
  if (this._blob) {
148
148
  return fakePromise(this._blob);
149
149
  }
150
+ if (this.bodyType === BodyInitType.String) {
151
+ this._text = this.bodyInit;
152
+ this._blob = new PonyfillBlob([this._text], {
153
+ type: this.contentType || 'text/plain;charset=UTF-8',
154
+ size: this.contentLength,
155
+ });
156
+ }
150
157
  if (this.bodyType === BodyInitType.Blob) {
151
158
  this._blob = this.bodyInit;
152
159
  return fakePromise(this._blob);
@@ -202,8 +209,8 @@ export class PonyfillBody {
202
209
  limits: formDataLimits,
203
210
  defCharset: 'utf-8',
204
211
  });
205
- if (this.signal) {
206
- addAbortSignal(this.signal, bb);
212
+ if (this._signal) {
213
+ addAbortSignal(this._signal, bb);
207
214
  }
208
215
  let completed = false;
209
216
  const complete = (err) => {
@@ -276,6 +283,17 @@ export class PonyfillBody {
276
283
  if (this._buffer) {
277
284
  return fakePromise(this._buffer);
278
285
  }
286
+ if (this._text) {
287
+ this._buffer = Buffer.from(this._text, 'utf-8');
288
+ return fakePromise(this._buffer);
289
+ }
290
+ if (this.bodyType === BodyInitType.String) {
291
+ return this.text().then(text => {
292
+ this._text = text;
293
+ this._buffer = Buffer.from(text, 'utf-8');
294
+ return this._buffer;
295
+ });
296
+ }
279
297
  if (this.bodyType === BodyInitType.Blob) {
280
298
  if (hasBufferMethod(this.bodyInit)) {
281
299
  return this.bodyInit.buffer().then(buf => {
@@ -354,15 +372,13 @@ function processBodyInit(bodyInit, signal) {
354
372
  };
355
373
  }
356
374
  if (typeof bodyInit === 'string') {
357
- const buffer = Buffer.from(bodyInit);
358
- const contentLength = buffer.byteLength;
375
+ const contentLength = Buffer.byteLength(bodyInit);
359
376
  return {
360
377
  bodyType: BodyInitType.String,
361
378
  contentType: 'text/plain;charset=UTF-8',
362
379
  contentLength,
363
- buffer,
364
380
  bodyFactory() {
365
- const readable = Readable.from(buffer);
381
+ const readable = Readable.from(Buffer.from(bodyInit, 'utf-8'));
366
382
  return new PonyfillReadableStream(readable);
367
383
  },
368
384
  };
package/esm/Headers.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { inspect } from 'node:util';
2
2
  import { PonyfillIteratorObject } from './IteratorObject.js';
3
+ import { isArray } from './utils.js';
3
4
  export function isHeadersLike(headers) {
4
5
  return headers?.get && headers?.forEach;
5
6
  }
@@ -26,7 +27,7 @@ export class PonyfillHeaders {
26
27
  if (this.headersInit == null) {
27
28
  return null;
28
29
  }
29
- if (Array.isArray(this.headersInit)) {
30
+ if (isArray(this.headersInit)) {
30
31
  const found = this.headersInit.filter(([headerKey]) => headerKey.toLowerCase() === normalized);
31
32
  if (found.length === 0) {
32
33
  return null;
@@ -62,9 +63,9 @@ export class PonyfillHeaders {
62
63
  // I could do a getter here, but I'm too lazy to type `getter`.
63
64
  getMap() {
64
65
  if (!this._map) {
65
- this._setCookies = [];
66
+ this._setCookies ||= [];
66
67
  if (this.headersInit != null) {
67
- if (Array.isArray(this.headersInit)) {
68
+ if (isArray(this.headersInit)) {
68
69
  this._map = new Map();
69
70
  for (const [key, value] of this.headersInit) {
70
71
  const normalizedKey = key.toLowerCase();
@@ -127,7 +128,8 @@ export class PonyfillHeaders {
127
128
  return value.toString();
128
129
  }
129
130
  has(name) {
130
- if (name === 'set-cookie') {
131
+ const key = name.toLowerCase();
132
+ if (key === 'set-cookie') {
131
133
  return !!this._setCookies?.length;
132
134
  }
133
135
  return !!this._get(name); // we might need to check if header exists and not just check if it's not nullable
@@ -138,6 +140,26 @@ export class PonyfillHeaders {
138
140
  this._setCookies = [value];
139
141
  return;
140
142
  }
143
+ if (!this._map && this.headersInit != null) {
144
+ if (isArray(this.headersInit)) {
145
+ const found = this.headersInit.find(([headerKey]) => headerKey.toLowerCase() === key);
146
+ if (found) {
147
+ found[1] = value;
148
+ }
149
+ else {
150
+ this.headersInit.push([key, value]);
151
+ }
152
+ return;
153
+ }
154
+ else if (isHeadersLike(this.headersInit)) {
155
+ this.headersInit.set(key, value);
156
+ return;
157
+ }
158
+ else {
159
+ this.headersInit[key] = value;
160
+ return;
161
+ }
162
+ }
141
163
  this.getMap().set(key, value);
142
164
  }
143
165
  delete(name) {
@@ -154,7 +176,7 @@ export class PonyfillHeaders {
154
176
  });
155
177
  if (!this._map) {
156
178
  if (this.headersInit) {
157
- if (Array.isArray(this.headersInit)) {
179
+ if (isArray(this.headersInit)) {
158
180
  this.headersInit.forEach(([key, value]) => {
159
181
  callback(value, key, this);
160
182
  });
@@ -182,7 +204,7 @@ export class PonyfillHeaders {
182
204
  }
183
205
  if (!this._map) {
184
206
  if (this.headersInit) {
185
- if (Array.isArray(this.headersInit)) {
207
+ if (isArray(this.headersInit)) {
186
208
  yield* this.headersInit.map(([key]) => key)[Symbol.iterator]();
187
209
  return;
188
210
  }
@@ -205,7 +227,7 @@ export class PonyfillHeaders {
205
227
  }
206
228
  if (!this._map) {
207
229
  if (this.headersInit) {
208
- if (Array.isArray(this.headersInit)) {
230
+ if (isArray(this.headersInit)) {
209
231
  yield* this.headersInit.map(([, value]) => value)[Symbol.iterator]();
210
232
  return;
211
233
  }
@@ -228,7 +250,7 @@ export class PonyfillHeaders {
228
250
  }
229
251
  if (!this._map) {
230
252
  if (this.headersInit) {
231
- if (Array.isArray(this.headersInit)) {
253
+ if (isArray(this.headersInit)) {
232
254
  yield* this.headersInit;
233
255
  return;
234
256
  }
package/esm/Request.js CHANGED
@@ -54,7 +54,6 @@ export class PonyfillRequest extends PonyfillBody {
54
54
  this.redirect = requestInit?.redirect || 'follow';
55
55
  this.referrer = requestInit?.referrer || 'about:client';
56
56
  this.referrerPolicy = requestInit?.referrerPolicy || 'no-referrer';
57
- this.signal = requestInit?.signal || new AbortController().signal;
58
57
  this.headersSerializer = requestInit?.headersSerializer;
59
58
  this.duplex = requestInit?.duplex || 'half';
60
59
  this.destination = 'document';
@@ -89,6 +88,10 @@ export class PonyfillRequest extends PonyfillBody {
89
88
  referrer;
90
89
  referrerPolicy;
91
90
  _url;
91
+ get signal() {
92
+ this._signal ||= new AbortController().signal;
93
+ return this._signal;
94
+ }
92
95
  get url() {
93
96
  if (this._url == null) {
94
97
  if (this._parsedUrl) {
@@ -114,7 +117,6 @@ export class PonyfillRequest extends PonyfillBody {
114
117
  }
115
118
  duplex;
116
119
  agent;
117
- signal;
118
120
  clone() {
119
121
  return this;
120
122
  }
package/esm/Response.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { STATUS_CODES } from 'node:http';
2
2
  import { PonyfillBody } from './Body.js';
3
3
  import { isHeadersLike, PonyfillHeaders } from './Headers.js';
4
+ import { isArray } from './utils.js';
4
5
  const JSON_CONTENT_TYPE = 'application/json; charset=utf-8';
5
6
  export class PonyfillResponse extends PonyfillBody {
6
7
  headers;
@@ -15,7 +16,6 @@ export class PonyfillResponse extends PonyfillBody {
15
16
  this.url = init?.url || '';
16
17
  this.redirected = init?.redirected || false;
17
18
  this.type = init?.type || 'default';
18
- this.signal = init?.signal || null;
19
19
  this.handleContentLengthHeader();
20
20
  }
21
21
  get ok() {
@@ -46,13 +46,33 @@ export class PonyfillResponse extends PonyfillBody {
46
46
  status,
47
47
  });
48
48
  }
49
- static json(data, init = {}) {
50
- init.headers =
51
- init?.headers && isHeadersLike(init.headers)
52
- ? init.headers
53
- : new PonyfillHeaders(init?.headers);
54
- if (!init.headers.has('content-type')) {
55
- init.headers.set('content-type', JSON_CONTENT_TYPE);
49
+ static json(data, init) {
50
+ if (!init) {
51
+ init = {
52
+ headers: {
53
+ 'content-type': JSON_CONTENT_TYPE,
54
+ },
55
+ };
56
+ }
57
+ else if (!init.headers) {
58
+ init.headers = {
59
+ 'content-type': JSON_CONTENT_TYPE,
60
+ };
61
+ }
62
+ else if (isHeadersLike(init.headers)) {
63
+ if (!init.headers.has('content-type')) {
64
+ init.headers.set('content-type', JSON_CONTENT_TYPE);
65
+ }
66
+ }
67
+ else if (isArray(init.headers)) {
68
+ if (!init.headers.some(([key]) => key.toLowerCase() === 'content-type')) {
69
+ init.headers.push(['content-type', JSON_CONTENT_TYPE]);
70
+ }
71
+ }
72
+ else if (typeof init.headers === 'object') {
73
+ if (init.headers?.['content-type'] == null) {
74
+ init.headers['content-type'] = JSON_CONTENT_TYPE;
75
+ }
56
76
  }
57
77
  return new PonyfillResponse(JSON.stringify(data), init);
58
78
  }
package/esm/fetchCurl.js CHANGED
@@ -18,8 +18,18 @@ export function fetchCurl(fetchRequest) {
18
18
  curlHandle.setOpt('CAINFO_BLOB', rootCertificates.join('\n'));
19
19
  }
20
20
  curlHandle.enable(CurlFeature.StreamResponse);
21
+ let signal;
22
+ if (fetchRequest._signal === null) {
23
+ signal = undefined;
24
+ }
25
+ else if (fetchRequest._signal) {
26
+ signal = fetchRequest._signal;
27
+ }
28
+ else {
29
+ signal = fetchRequest.signal;
30
+ }
21
31
  curlHandle.setStreamProgressCallback(function () {
22
- return fetchRequest.signal.aborted ? (process.env.DEBUG ? CurlProgressFunc.Continue : 1) : 0;
32
+ return signal?.aborted ? (process.env.DEBUG ? CurlProgressFunc.Continue : 1) : 0;
23
33
  });
24
34
  if (fetchRequest['bodyType'] === 'String') {
25
35
  curlHandle.setOpt('POSTFIELDS', fetchRequest['bodyInit']);
@@ -66,9 +76,7 @@ export function fetchCurl(fetchRequest) {
66
76
  }
67
77
  }
68
78
  }
69
- if (fetchRequest.signal) {
70
- fetchRequest.signal.addEventListener('abort', onAbort, { once: true });
71
- }
79
+ signal?.addEventListener('abort', onAbort, { once: true });
72
80
  curlHandle.once('end', function endListener() {
73
81
  try {
74
82
  curlHandle.close();
@@ -76,9 +84,7 @@ export function fetchCurl(fetchRequest) {
76
84
  catch (e) {
77
85
  deferredPromise.reject(e);
78
86
  }
79
- if (fetchRequest.signal) {
80
- fetchRequest.signal.removeEventListener('abort', onAbort);
81
- }
87
+ signal?.removeEventListener('abort', onAbort);
82
88
  });
83
89
  curlHandle.once('error', function errorListener(error) {
84
90
  if (streamResolved && !streamResolved.closed && !streamResolved.destroyed) {
@@ -100,7 +106,7 @@ export function fetchCurl(fetchRequest) {
100
106
  curlHandle.once('stream', function streamListener(stream, status, headersBuf) {
101
107
  const outputStream = wrapIncomingMessageWithPassthrough({
102
108
  incomingMessage: stream,
103
- signal: fetchRequest.signal,
109
+ signal,
104
110
  onError: deferredPromise.reject,
105
111
  });
106
112
  const headersFlat = headersBuf
@@ -25,13 +25,23 @@ export function fetchNodeHttp(fetchRequest) {
25
25
  if (nodeHeaders['accept-encoding'] == null) {
26
26
  nodeHeaders['accept-encoding'] = 'gzip, deflate, br';
27
27
  }
28
+ let signal;
29
+ if (fetchRequest._signal === null) {
30
+ signal = undefined;
31
+ }
32
+ else if (fetchRequest._signal) {
33
+ signal = fetchRequest._signal;
34
+ }
35
+ else {
36
+ signal = fetchRequest.signal;
37
+ }
28
38
  let nodeRequest;
29
39
  // If it is our ponyfilled Request, it should have `parsedUrl` which is a `URL` object
30
40
  if (fetchRequest.parsedUrl) {
31
41
  nodeRequest = requestFn(fetchRequest.parsedUrl, {
32
42
  method: fetchRequest.method,
33
43
  headers: nodeHeaders,
34
- signal: fetchRequest.signal,
44
+ signal,
35
45
  agent: fetchRequest.agent,
36
46
  });
37
47
  }
@@ -39,7 +49,7 @@ export function fetchNodeHttp(fetchRequest) {
39
49
  nodeRequest = requestFn(fetchRequest.url, {
40
50
  method: fetchRequest.method,
41
51
  headers: nodeHeaders,
42
- signal: fetchRequest.signal,
52
+ signal,
43
53
  agent: fetchRequest.agent,
44
54
  });
45
55
  }
@@ -86,7 +96,7 @@ export function fetchNodeHttp(fetchRequest) {
86
96
  outputStream = wrapIncomingMessageWithPassthrough({
87
97
  incomingMessage: nodeResponse,
88
98
  passThrough: outputStream,
89
- signal: fetchRequest.signal,
99
+ signal,
90
100
  onError: reject,
91
101
  });
92
102
  }
@@ -100,12 +110,12 @@ export function fetchNodeHttp(fetchRequest) {
100
110
  statusText,
101
111
  headers: nodeResponse.headers,
102
112
  url: fetchRequest.url,
103
- signal: fetchRequest.signal,
113
+ signal,
104
114
  });
105
115
  resolve(ponyfillResponse);
106
116
  });
107
117
  if (fetchRequest['_buffer'] != null) {
108
- handleMaybePromise(() => safeWrite(fetchRequest['_buffer'], nodeRequest, fetchRequest.signal), () => endStream(nodeRequest), reject);
118
+ handleMaybePromise(() => safeWrite(fetchRequest['_buffer'], nodeRequest, signal), () => endStream(nodeRequest), reject);
109
119
  }
110
120
  else {
111
121
  const nodeReadable = (fetchRequest.body != null
package/esm/utils.js CHANGED
@@ -8,6 +8,11 @@ export function getHeadersObj(headers) {
8
8
  if (headers == null || !isHeadersInstance(headers)) {
9
9
  return headers;
10
10
  }
11
+ // @ts-expect-error - `headersInit` is not a public property
12
+ if (headers.headersInit && !headers._map && !isHeadersInstance(headers.headersInit)) {
13
+ // @ts-expect-error - `headersInit` is not a public property
14
+ return headers.headersInit;
15
+ }
11
16
  return Object.fromEntries(headers.entries());
12
17
  }
13
18
  export function defaultHeadersSerializer(headers, onContentLength) {
@@ -60,3 +65,6 @@ export function safeWrite(chunk, stream, signal) {
60
65
  });
61
66
  }
62
67
  }
68
+ export function isArray(value) {
69
+ return value?.splice != null;
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/node-fetch",
3
- "version": "0.7.20-alpha-20250515210136-f3eeba6bfbd13f3930315ee75f80a9091e946e92",
3
+ "version": "0.7.20-alpha-20250516115150-ce23c7139392555706532687427aa8535aae5852",
4
4
  "description": "Fetch API implementation for Node",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
@@ -16,7 +16,7 @@ export interface FormDataLimits {
16
16
  }
17
17
  export interface PonyfillBodyOptions {
18
18
  formDataLimits?: FormDataLimits;
19
- signal?: AbortSignal;
19
+ signal?: AbortSignal | undefined;
20
20
  }
21
21
  export declare class PonyfillBody<TJSON = any> implements Body {
22
22
  private bodyInit;
@@ -24,7 +24,7 @@ export declare class PonyfillBody<TJSON = any> implements Body {
24
24
  bodyUsed: boolean;
25
25
  contentType: string | null;
26
26
  contentLength: number | null;
27
- signal?: AbortSignal | null;
27
+ _signal?: AbortSignal | null;
28
28
  constructor(bodyInit: BodyPonyfillInit | null, options?: PonyfillBodyOptions);
29
29
  private bodyType?;
30
30
  private _bodyFactory;
package/typings/Body.d.ts CHANGED
@@ -16,7 +16,7 @@ export interface FormDataLimits {
16
16
  }
17
17
  export interface PonyfillBodyOptions {
18
18
  formDataLimits?: FormDataLimits;
19
- signal?: AbortSignal;
19
+ signal?: AbortSignal | undefined;
20
20
  }
21
21
  export declare class PonyfillBody<TJSON = any> implements Body {
22
22
  private bodyInit;
@@ -24,7 +24,7 @@ export declare class PonyfillBody<TJSON = any> implements Body {
24
24
  bodyUsed: boolean;
25
25
  contentType: string | null;
26
26
  contentLength: number | null;
27
- signal?: AbortSignal | null;
27
+ _signal?: AbortSignal | null;
28
28
  constructor(bodyInit: BodyPonyfillInit | null, options?: PonyfillBodyOptions);
29
29
  private bodyType?;
30
30
  private _bodyFactory;
@@ -26,12 +26,12 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
26
26
  referrer: string;
27
27
  referrerPolicy: ReferrerPolicy;
28
28
  _url: string | undefined;
29
+ get signal(): AbortSignal;
29
30
  get url(): string;
30
31
  _parsedUrl: URL | undefined;
31
32
  get parsedUrl(): URL;
32
33
  duplex: 'half' | 'full';
33
34
  agent: HTTPAgent | HTTPSAgent | false | undefined;
34
- signal: AbortSignal;
35
35
  clone(): PonyfillRequest<TJSON>;
36
36
  [Symbol.toStringTag]: string;
37
37
  }
@@ -26,12 +26,12 @@ export declare class PonyfillRequest<TJSON = any> extends PonyfillBody<TJSON> im
26
26
  referrer: string;
27
27
  referrerPolicy: ReferrerPolicy;
28
28
  _url: string | undefined;
29
+ get signal(): AbortSignal;
29
30
  get url(): string;
30
31
  _parsedUrl: URL | undefined;
31
32
  get parsedUrl(): URL;
32
33
  duplex: 'half' | 'full';
33
34
  agent: HTTPAgent | HTTPSAgent | false | undefined;
34
- signal: AbortSignal;
35
35
  clone(): PonyfillRequest<TJSON>;
36
36
  [Symbol.toStringTag]: string;
37
37
  }
@@ -17,3 +17,4 @@ export declare function endStream(stream: {
17
17
  end: () => void;
18
18
  }): void;
19
19
  export declare function safeWrite(chunk: any, stream: Writable, signal?: AbortSignal | undefined): Promise<any> | undefined;
20
+ export declare function isArray<T>(value: any): value is T[];
@@ -17,3 +17,4 @@ export declare function endStream(stream: {
17
17
  end: () => void;
18
18
  }): void;
19
19
  export declare function safeWrite(chunk: any, stream: Writable, signal?: AbortSignal | undefined): Promise<any> | undefined;
20
+ export declare function isArray<T>(value: any): value is T[];