@mswjs/interceptors 0.31.1 → 0.32.1

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.
Files changed (69) hide show
  1. package/README.md +56 -39
  2. package/lib/node/RemoteHttpInterceptor.d.ts +1 -2
  3. package/lib/node/RemoteHttpInterceptor.js +11 -11
  4. package/lib/node/RemoteHttpInterceptor.mjs +5 -5
  5. package/lib/node/{chunk-LTEXDYJ6.js → chunk-2COJKQQB.js} +3 -3
  6. package/lib/node/chunk-3OJLYEWA.mjs +963 -0
  7. package/lib/node/chunk-3OJLYEWA.mjs.map +1 -0
  8. package/lib/node/chunk-5JMJ55U7.js +963 -0
  9. package/lib/node/chunk-5JMJ55U7.js.map +1 -0
  10. package/lib/node/{chunk-E4AC7YAC.js → chunk-BFLYGQ6D.js} +4 -2
  11. package/lib/node/{chunk-KSHIDGUL.mjs → chunk-DV4PBH4D.mjs} +3 -3
  12. package/lib/node/{chunk-OUWBQF3Z.mjs → chunk-KWV3JXSI.mjs} +14 -14
  13. package/lib/node/chunk-KWV3JXSI.mjs.map +1 -0
  14. package/lib/node/{chunk-6FRASLM3.mjs → chunk-PNWPIDEL.mjs} +2 -2
  15. package/lib/node/{chunk-APT7KA3B.js → chunk-PYD4E2EJ.js} +13 -13
  16. package/lib/node/{chunk-Q7POAM5N.mjs → chunk-TGTPXCLF.mjs} +3 -1
  17. package/lib/node/{chunk-MQJ3JOOK.js → chunk-UXCYRE4F.js} +14 -14
  18. package/lib/node/chunk-UXCYRE4F.js.map +1 -0
  19. package/lib/node/index.js +3 -3
  20. package/lib/node/index.mjs +2 -2
  21. package/lib/node/interceptors/ClientRequest/index.d.ts +83 -14
  22. package/lib/node/interceptors/ClientRequest/index.js +4 -4
  23. package/lib/node/interceptors/ClientRequest/index.mjs +3 -3
  24. package/lib/node/interceptors/XMLHttpRequest/index.js +4 -4
  25. package/lib/node/interceptors/XMLHttpRequest/index.mjs +3 -3
  26. package/lib/node/interceptors/fetch/index.js +10 -10
  27. package/lib/node/interceptors/fetch/index.mjs +2 -2
  28. package/lib/node/presets/node.d.ts +2 -3
  29. package/lib/node/presets/node.js +6 -6
  30. package/lib/node/presets/node.mjs +4 -4
  31. package/package.json +2 -2
  32. package/src/interceptors/ClientRequest/MockHttpSocket.ts +595 -0
  33. package/src/interceptors/ClientRequest/agents.ts +78 -0
  34. package/src/interceptors/ClientRequest/index.test.ts +14 -12
  35. package/src/interceptors/ClientRequest/index.ts +200 -41
  36. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +78 -98
  37. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +40 -22
  38. package/src/interceptors/Socket/MockSocket.test.ts +264 -0
  39. package/src/interceptors/Socket/MockSocket.ts +59 -0
  40. package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
  41. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
  42. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
  43. package/src/interceptors/Socket/utils/parseRawHeaders.ts +10 -0
  44. package/lib/node/chunk-IS3CIGXU.js +0 -909
  45. package/lib/node/chunk-IS3CIGXU.js.map +0 -1
  46. package/lib/node/chunk-MQJ3JOOK.js.map +0 -1
  47. package/lib/node/chunk-OMOWHUE6.mjs +0 -909
  48. package/lib/node/chunk-OMOWHUE6.mjs.map +0 -1
  49. package/lib/node/chunk-OUWBQF3Z.mjs.map +0 -1
  50. package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +0 -206
  51. package/src/interceptors/ClientRequest/NodeClientRequest.ts +0 -680
  52. package/src/interceptors/ClientRequest/http.get.ts +0 -30
  53. package/src/interceptors/ClientRequest/http.request.ts +0 -27
  54. package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.test.ts +0 -26
  55. package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.ts +0 -74
  56. package/src/interceptors/ClientRequest/utils/createRequest.test.ts +0 -144
  57. package/src/interceptors/ClientRequest/utils/createRequest.ts +0 -51
  58. package/src/interceptors/ClientRequest/utils/createResponse.test.ts +0 -53
  59. package/src/interceptors/ClientRequest/utils/createResponse.ts +0 -55
  60. package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.test.ts +0 -41
  61. package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.ts +0 -53
  62. package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.test.ts +0 -36
  63. package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.ts +0 -39
  64. /package/lib/node/{chunk-LTEXDYJ6.js.map → chunk-2COJKQQB.js.map} +0 -0
  65. /package/lib/node/{chunk-E4AC7YAC.js.map → chunk-BFLYGQ6D.js.map} +0 -0
  66. /package/lib/node/{chunk-KSHIDGUL.mjs.map → chunk-DV4PBH4D.mjs.map} +0 -0
  67. /package/lib/node/{chunk-6FRASLM3.mjs.map → chunk-PNWPIDEL.mjs.map} +0 -0
  68. /package/lib/node/{chunk-APT7KA3B.js.map → chunk-PYD4E2EJ.js.map} +0 -0
  69. /package/lib/node/{chunk-Q7POAM5N.mjs.map → chunk-TGTPXCLF.mjs.map} +0 -0
@@ -6,11 +6,10 @@ import { getUrlByRequestOptions } from '../../../utils/getUrlByRequestOptions'
6
6
  import { normalizeClientRequestArgs } from './normalizeClientRequestArgs'
7
7
 
8
8
  it('handles [string, callback] input', () => {
9
- const [url, options, callback] = normalizeClientRequestArgs(
10
- 'https:',
9
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
11
10
  'https://mswjs.io/resource',
12
- function cb() {}
13
- )
11
+ function cb() {},
12
+ ])
14
13
 
15
14
  // URL string must be converted to a URL instance.
16
15
  expect(url.href).toEqual('https://mswjs.io/resource')
@@ -31,12 +30,11 @@ it('handles [string, RequestOptions, callback] input', () => {
31
30
  'Content-Type': 'text/plain',
32
31
  },
33
32
  }
34
- const [url, options, callback] = normalizeClientRequestArgs(
35
- 'https:',
33
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
36
34
  'https://mswjs.io/resource',
37
35
  initialOptions,
38
- function cb() {}
39
- )
36
+ function cb() {},
37
+ ])
40
38
 
41
39
  // URL must be created from the string.
42
40
  expect(url.href).toEqual('https://mswjs.io/resource')
@@ -49,11 +47,10 @@ it('handles [string, RequestOptions, callback] input', () => {
49
47
  })
50
48
 
51
49
  it('handles [URL, callback] input', () => {
52
- const [url, options, callback] = normalizeClientRequestArgs(
53
- 'https:',
50
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
54
51
  new URL('https://mswjs.io/resource'),
55
- function cb() {}
56
- )
52
+ function cb() {},
53
+ ])
57
54
 
58
55
  // URL must be preserved.
59
56
  expect(url.href).toEqual('https://mswjs.io/resource')
@@ -69,11 +66,10 @@ it('handles [URL, callback] input', () => {
69
66
  })
70
67
 
71
68
  it('handles [Absolute Legacy URL, callback] input', () => {
72
- const [url, options, callback] = normalizeClientRequestArgs(
73
- 'https:',
69
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
74
70
  parse('https://cherry:durian@mswjs.io:12345/resource?apple=banana'),
75
- function cb() {}
76
- )
71
+ function cb() {},
72
+ ])
77
73
 
78
74
  // URL must be preserved.
79
75
  expect(url.toJSON()).toEqual(
@@ -95,12 +91,11 @@ it('handles [Absolute Legacy URL, callback] input', () => {
95
91
  })
96
92
 
97
93
  it('handles [Relative Legacy URL, RequestOptions without path set, callback] input', () => {
98
- const [url, options, callback] = normalizeClientRequestArgs(
99
- 'http:',
94
+ const [url, options, callback] = normalizeClientRequestArgs('http:', [
100
95
  parse('/resource?apple=banana'),
101
96
  { host: 'mswjs.io' },
102
- function cb() {}
103
- )
97
+ function cb() {},
98
+ ])
104
99
 
105
100
  // Correct WHATWG URL generated.
106
101
  expect(url.toJSON()).toEqual(
@@ -117,12 +112,11 @@ it('handles [Relative Legacy URL, RequestOptions without path set, callback] inp
117
112
  })
118
113
 
119
114
  it('handles [Relative Legacy URL, RequestOptions with path set, callback] input', () => {
120
- const [url, options, callback] = normalizeClientRequestArgs(
121
- 'http:',
115
+ const [url, options, callback] = normalizeClientRequestArgs('http:', [
122
116
  parse('/resource?apple=banana'),
123
117
  { host: 'mswjs.io', path: '/other?cherry=durian' },
124
- function cb() {}
125
- )
118
+ function cb() {},
119
+ ])
126
120
 
127
121
  // Correct WHATWG URL generated.
128
122
  expect(url.toJSON()).toEqual(
@@ -139,11 +133,10 @@ it('handles [Relative Legacy URL, RequestOptions with path set, callback] input'
139
133
  })
140
134
 
141
135
  it('handles [Relative Legacy URL, callback] input', () => {
142
- const [url, options, callback] = normalizeClientRequestArgs(
143
- 'http:',
136
+ const [url, options, callback] = normalizeClientRequestArgs('http:', [
144
137
  parse('/resource?apple=banana'),
145
- function cb() {}
146
- )
138
+ function cb() {},
139
+ ])
147
140
 
148
141
  // Correct WHATWG URL generated.
149
142
  expect(url.toJSON()).toMatch(
@@ -155,14 +148,14 @@ it('handles [Relative Legacy URL, callback] input', () => {
155
148
  expect(options.path).toEqual('/resource?apple=banana')
156
149
 
157
150
  // Callback must be preserved.
151
+ expect(callback).toBeTypeOf('function')
158
152
  expect(callback?.name).toEqual('cb')
159
153
  })
160
154
 
161
155
  it('handles [Relative Legacy URL] input', () => {
162
- const [url, options, callback] = normalizeClientRequestArgs(
163
- 'http:',
164
- parse('/resource?apple=banana')
165
- )
156
+ const [url, options, callback] = normalizeClientRequestArgs('http:', [
157
+ parse('/resource?apple=banana'),
158
+ ])
166
159
 
167
160
  // Correct WHATWG URL generated.
168
161
  expect(url.toJSON()).toMatch(
@@ -178,8 +171,7 @@ it('handles [Relative Legacy URL] input', () => {
178
171
  })
179
172
 
180
173
  it('handles [URL, RequestOptions, callback] input', () => {
181
- const [url, options, callback] = normalizeClientRequestArgs(
182
- 'https:',
174
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
183
175
  new URL('https://mswjs.io/resource'),
184
176
  {
185
177
  agent: false,
@@ -187,8 +179,8 @@ it('handles [URL, RequestOptions, callback] input', () => {
187
179
  'Content-Type': 'text/plain',
188
180
  },
189
181
  },
190
- function cb() {}
191
- )
182
+ function cb() {},
183
+ ])
192
184
 
193
185
  // URL must be preserved.
194
186
  expect(url.href).toEqual('https://mswjs.io/resource')
@@ -209,17 +201,17 @@ it('handles [URL, RequestOptions, callback] input', () => {
209
201
  })
210
202
 
211
203
  // Callback must be preserved.
204
+ expect(callback).toBeTypeOf('function')
212
205
  expect(callback?.name).toEqual('cb')
213
206
  })
214
207
 
215
208
  it('handles [URL, RequestOptions] where options have custom "hostname"', () => {
216
- const [url, options] = normalizeClientRequestArgs(
217
- 'http:',
209
+ const [url, options] = normalizeClientRequestArgs('http:', [
218
210
  new URL('http://example.com/path-from-url'),
219
211
  {
220
212
  hostname: 'host-from-options.com',
221
- }
222
- )
213
+ },
214
+ ])
223
215
  expect(url.href).toBe('http://host-from-options.com/path-from-url')
224
216
  expect(options).toMatchObject({
225
217
  host: 'host-from-options.com',
@@ -228,15 +220,14 @@ it('handles [URL, RequestOptions] where options have custom "hostname"', () => {
228
220
  })
229
221
 
230
222
  it('handles [URL, RequestOptions] where options contain "host" and "path" and "port"', () => {
231
- const [url, options] = normalizeClientRequestArgs(
232
- 'http:',
223
+ const [url, options] = normalizeClientRequestArgs('http:', [
233
224
  new URL('http://example.com/path-from-url?a=b&c=d'),
234
225
  {
235
226
  hostname: 'host-from-options.com',
236
227
  path: '/path-from-options',
237
228
  port: 1234,
238
- }
239
- )
229
+ },
230
+ ])
240
231
  // Must remove the query string since it's not specified in "options.path"
241
232
  expect(url.href).toBe('http://host-from-options.com:1234/path-from-options')
242
233
  expect(options).toMatchObject({
@@ -247,13 +238,12 @@ it('handles [URL, RequestOptions] where options contain "host" and "path" and "p
247
238
  })
248
239
 
249
240
  it('handles [URL, RequestOptions] where options contain "path" with query string', () => {
250
- const [url, options] = normalizeClientRequestArgs(
251
- 'http:',
241
+ const [url, options] = normalizeClientRequestArgs('http:', [
252
242
  new URL('http://example.com/path-from-url?a=b&c=d'),
253
243
  {
254
244
  path: '/path-from-options?foo=bar&baz=xyz',
255
- }
256
- )
245
+ },
246
+ ])
257
247
  expect(url.href).toBe('http://example.com/path-from-options?foo=bar&baz=xyz')
258
248
  expect(options).toMatchObject({
259
249
  host: 'example.com',
@@ -275,28 +265,27 @@ it('handles [RequestOptions, callback] input', () => {
275
265
  'Content-Type': 'text/plain',
276
266
  },
277
267
  }
278
- const [url, options, callback] = normalizeClientRequestArgs(
279
- 'https:',
268
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
280
269
  initialOptions,
281
- function cb() {}
282
- )
270
+ function cb() {},
271
+ ])
283
272
 
284
273
  // URL must be derived from request options.
285
274
  expect(url.href).toEqual('https://mswjs.io/resource')
286
275
 
287
276
  // Request options must be preserved.
288
- expect(options).toEqual(initialOptions)
277
+ expect(options).toMatchObject(initialOptions)
289
278
 
290
279
  // Callback must be preserved.
280
+ expect(callback).toBeTypeOf('function')
291
281
  expect(callback?.name).toEqual('cb')
292
282
  })
293
283
 
294
284
  it('handles [Empty RequestOptions, callback] input', () => {
295
- const [_, options, callback] = normalizeClientRequestArgs(
296
- 'https:',
285
+ const [_, options, callback] = normalizeClientRequestArgs('https:', [
297
286
  {},
298
- function cb() {}
299
- )
287
+ function cb() {},
288
+ ])
300
289
 
301
290
  expect(options.protocol).toEqual('https:')
302
291
 
@@ -320,11 +309,10 @@ it('handles [PartialRequestOptions, callback] input', () => {
320
309
  passphrase: undefined,
321
310
  agent: false,
322
311
  }
323
- const [url, options, callback] = normalizeClientRequestArgs(
324
- 'https:',
312
+ const [url, options, callback] = normalizeClientRequestArgs('https:', [
325
313
  initialOptions,
326
- function cb() {}
327
- )
314
+ function cb() {},
315
+ ])
328
316
 
329
317
  // URL must be derived from request options.
330
318
  expect(url.toJSON()).toEqual(
@@ -332,20 +320,20 @@ it('handles [PartialRequestOptions, callback] input', () => {
332
320
  )
333
321
 
334
322
  // Request options must be preserved.
335
- expect(options).toEqual(initialOptions)
323
+ expect(options).toMatchObject(initialOptions)
336
324
 
337
325
  // Options protocol must be inferred from the request issuing module.
338
326
  expect(options.protocol).toEqual('https:')
339
327
 
340
328
  // Callback must be preserved.
329
+ expect(callback).toBeTypeOf('function')
341
330
  expect(callback?.name).toEqual('cb')
342
331
  })
343
332
 
344
333
  it('sets fallback Agent based on the URL protocol', () => {
345
- const [url, options] = normalizeClientRequestArgs(
346
- 'https:',
347
- 'https://github.com'
348
- )
334
+ const [url, options] = normalizeClientRequestArgs('https:', [
335
+ 'https://github.com',
336
+ ])
349
337
  const agent = options.agent as HttpsAgent
350
338
 
351
339
  expect(agent).toBeInstanceOf(HttpsAgent)
@@ -354,59 +342,54 @@ it('sets fallback Agent based on the URL protocol', () => {
354
342
  })
355
343
 
356
344
  it('does not set any fallback Agent given "agent: false" option', () => {
357
- const [, options] = normalizeClientRequestArgs(
358
- 'https:',
345
+ const [, options] = normalizeClientRequestArgs('https:', [
359
346
  'https://github.com',
360
- { agent: false }
361
- )
347
+ { agent: false },
348
+ ])
362
349
 
363
350
  expect(options.agent).toEqual(false)
364
351
  })
365
352
 
366
353
  it('sets the default Agent for HTTP request', () => {
367
- const [, options] = normalizeClientRequestArgs(
368
- 'http:',
354
+ const [, options] = normalizeClientRequestArgs('http:', [
369
355
  'http://github.com',
370
- {}
371
- )
356
+ {},
357
+ ])
372
358
 
373
359
  expect(options._defaultAgent).toEqual(httpGlobalAgent)
374
360
  })
375
361
 
376
362
  it('sets the default Agent for HTTPS request', () => {
377
- const [, options] = normalizeClientRequestArgs(
378
- 'https:',
363
+ const [, options] = normalizeClientRequestArgs('https:', [
379
364
  'https://github.com',
380
- {}
381
- )
365
+ {},
366
+ ])
382
367
 
383
368
  expect(options._defaultAgent).toEqual(httpsGlobalAgent)
384
369
  })
385
370
 
386
371
  it('preserves a custom default Agent when set', () => {
387
- const [, options] = normalizeClientRequestArgs(
388
- 'https:',
372
+ const [, options] = normalizeClientRequestArgs('https:', [
389
373
  'https://github.com',
390
374
  {
391
375
  /**
392
376
  * @note Intentionally incorrect Agent for HTTPS request.
393
377
  */
394
378
  _defaultAgent: httpGlobalAgent,
395
- }
396
- )
379
+ },
380
+ ])
397
381
 
398
382
  expect(options._defaultAgent).toEqual(httpGlobalAgent)
399
383
  })
400
384
 
401
385
  it('merges URL-based RequestOptions with the custom RequestOptions', () => {
402
- const [url, options] = normalizeClientRequestArgs(
403
- 'https:',
386
+ const [url, options] = normalizeClientRequestArgs('https:', [
404
387
  'https://github.com/graphql',
405
388
  {
406
389
  method: 'GET',
407
390
  pfx: 'PFX_KEY',
408
- }
409
- )
391
+ },
392
+ ])
410
393
 
411
394
  expect(url.href).toEqual('https://github.com/graphql')
412
395
 
@@ -422,13 +405,12 @@ it('merges URL-based RequestOptions with the custom RequestOptions', () => {
422
405
  })
423
406
 
424
407
  it('respects custom "options.path" over URL path', () => {
425
- const [url, options] = normalizeClientRequestArgs(
426
- 'http:',
408
+ const [url, options] = normalizeClientRequestArgs('http:', [
427
409
  new URL('http://example.com/path-from-url'),
428
410
  {
429
411
  path: '/path-from-options',
430
- }
431
- )
412
+ },
413
+ ])
432
414
 
433
415
  expect(url.href).toBe('http://example.com/path-from-options')
434
416
  expect(options.protocol).toBe('http:')
@@ -438,13 +420,12 @@ it('respects custom "options.path" over URL path', () => {
438
420
  })
439
421
 
440
422
  it('respects custom "options.path" over URL path with query string', () => {
441
- const [url, options] = normalizeClientRequestArgs(
442
- 'http:',
423
+ const [url, options] = normalizeClientRequestArgs('http:', [
443
424
  new URL('http://example.com/path-from-url?a=b&c=d'),
444
425
  {
445
426
  path: '/path-from-options',
446
- }
447
- )
427
+ },
428
+ ])
448
429
 
449
430
  // Must replace both the path and the query string.
450
431
  expect(url.href).toBe('http://example.com/path-from-options')
@@ -455,10 +436,9 @@ it('respects custom "options.path" over URL path with query string', () => {
455
436
  })
456
437
 
457
438
  it('preserves URL query string', () => {
458
- const [url, options] = normalizeClientRequestArgs(
459
- 'http:',
460
- new URL('http://example.com/resource?a=b&c=d')
461
- )
439
+ const [url, options] = normalizeClientRequestArgs('http:', [
440
+ new URL('http://example.com/resource?a=b&c=d'),
441
+ ])
462
442
 
463
443
  expect(url.href).toBe('http://example.com/resource?a=b&c=d')
464
444
  expect(options.protocol).toBe('http:')
@@ -2,13 +2,23 @@ import {
2
2
  Agent as HttpAgent,
3
3
  globalAgent as httpGlobalAgent,
4
4
  IncomingMessage,
5
- } from 'http'
5
+ } from 'node:http'
6
6
  import {
7
7
  RequestOptions,
8
8
  Agent as HttpsAgent,
9
9
  globalAgent as httpsGlobalAgent,
10
- } from 'https'
11
- import { Url as LegacyURL, parse as parseUrl } from 'url'
10
+ } from 'node:https'
11
+ import {
12
+ /**
13
+ * @note Use the Node.js URL instead of the global URL
14
+ * because environments like JSDOM may override the global,
15
+ * breaking the compatibility with Node.js.
16
+ * @see https://github.com/node-fetch/node-fetch/issues/1376#issuecomment-966435555
17
+ */
18
+ URL,
19
+ Url as LegacyURL,
20
+ parse as parseUrl,
21
+ } from 'node:url'
12
22
  import { Logger } from '@open-draft/logger'
13
23
  import { getRequestOptionsByUrl } from '../../../utils/getRequestOptionsByUrl'
14
24
  import {
@@ -93,7 +103,7 @@ function resolveCallback(
93
103
  export type NormalizedClientRequestArgs = [
94
104
  url: URL,
95
105
  options: ResolvedRequestOptions,
96
- callback?: HttpRequestCallback
106
+ callback?: HttpRequestCallback,
97
107
  ]
98
108
 
99
109
  /**
@@ -102,7 +112,7 @@ export type NormalizedClientRequestArgs = [
102
112
  */
103
113
  export function normalizeClientRequestArgs(
104
114
  defaultProtocol: string,
105
- ...args: ClientRequestArgs
115
+ args: ClientRequestArgs
106
116
  ): NormalizedClientRequestArgs {
107
117
  let url: URL
108
118
  let options: ResolvedRequestOptions
@@ -172,16 +182,14 @@ export function normalizeClientRequestArgs(
172
182
  logger.info('given legacy URL is relative (no hostname)')
173
183
 
174
184
  return isObject(args[1])
175
- ? normalizeClientRequestArgs(
176
- defaultProtocol,
185
+ ? normalizeClientRequestArgs(defaultProtocol, [
177
186
  { path: legacyUrl.path, ...args[1] },
178
- args[2]
179
- )
180
- : normalizeClientRequestArgs(
181
- defaultProtocol,
187
+ args[2],
188
+ ])
189
+ : normalizeClientRequestArgs(defaultProtocol, [
182
190
  { path: legacyUrl.path },
183
- args[1] as HttpRequestCallback
184
- )
191
+ args[1] as HttpRequestCallback,
192
+ ])
185
193
  }
186
194
 
187
195
  logger.info('given legacy url is absolute')
@@ -190,20 +198,19 @@ export function normalizeClientRequestArgs(
190
198
  const resolvedUrl = new URL(legacyUrl.href)
191
199
 
192
200
  return args[1] === undefined
193
- ? normalizeClientRequestArgs(defaultProtocol, resolvedUrl)
201
+ ? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl])
194
202
  : typeof args[1] === 'function'
195
- ? normalizeClientRequestArgs(defaultProtocol, resolvedUrl, args[1])
196
- : normalizeClientRequestArgs(
197
- defaultProtocol,
198
- resolvedUrl,
199
- args[1],
200
- args[2]
201
- )
203
+ ? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl, args[1]])
204
+ : normalizeClientRequestArgs(defaultProtocol, [
205
+ resolvedUrl,
206
+ args[1],
207
+ args[2],
208
+ ])
202
209
  }
203
210
  // Handle a given "RequestOptions" object as-is
204
211
  // and derive the URL instance from it.
205
212
  else if (isObject(args[0])) {
206
- options = args[0] as any
213
+ options = { ... args[0] as any }
207
214
  logger.info('first argument is RequestOptions:', options)
208
215
 
209
216
  // When handling a "RequestOptions" object without an explicit "protocol",
@@ -266,5 +273,16 @@ export function normalizeClientRequestArgs(
266
273
  logger.info('successfully resolved options:', options)
267
274
  logger.info('successfully resolved callback:', callback)
268
275
 
276
+ /**
277
+ * @note If the user-provided URL is not a valid URL in Node.js,
278
+ * (e.g. the one provided by the JSDOM polyfills), case it to
279
+ * string. Otherwise, this throws on Node.js incompatibility
280
+ * (`ERR_INVALID_ARG_TYPE` on the connection listener)
281
+ * @see https://github.com/node-fetch/node-fetch/issues/1376#issuecomment-966435555
282
+ */
283
+ if (!(url instanceof URL)) {
284
+ url = (url as any).toString()
285
+ }
286
+
269
287
  return [url, options, callback]
270
288
  }