undici 6.19.7 → 7.0.0-alpha.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 (115) hide show
  1. package/README.md +5 -9
  2. package/docs/docs/api/Agent.md +0 -3
  3. package/docs/docs/api/Client.md +0 -2
  4. package/docs/docs/api/Dispatcher.md +204 -6
  5. package/docs/docs/api/EnvHttpProxyAgent.md +0 -1
  6. package/docs/docs/api/Fetch.md +1 -0
  7. package/docs/docs/api/Pool.md +0 -1
  8. package/docs/docs/api/RetryHandler.md +1 -1
  9. package/index.js +0 -4
  10. package/lib/api/api-connect.js +3 -1
  11. package/lib/api/api-pipeline.js +3 -4
  12. package/lib/api/api-request.js +29 -46
  13. package/lib/api/api-stream.js +36 -49
  14. package/lib/api/api-upgrade.js +5 -3
  15. package/lib/api/readable.js +71 -27
  16. package/lib/core/connect.js +39 -24
  17. package/lib/core/errors.js +17 -4
  18. package/lib/core/request.js +7 -5
  19. package/lib/core/symbols.js +0 -1
  20. package/lib/core/tree.js +6 -0
  21. package/lib/core/util.js +1 -11
  22. package/lib/dispatcher/agent.js +3 -17
  23. package/lib/dispatcher/balanced-pool.js +27 -11
  24. package/lib/dispatcher/client-h1.js +44 -39
  25. package/lib/dispatcher/client.js +3 -27
  26. package/lib/dispatcher/dispatcher-base.js +2 -34
  27. package/lib/dispatcher/dispatcher.js +3 -24
  28. package/lib/dispatcher/pool.js +3 -6
  29. package/lib/dispatcher/proxy-agent.js +3 -6
  30. package/lib/handler/decorator-handler.js +24 -0
  31. package/lib/handler/redirect-handler.js +9 -0
  32. package/lib/handler/retry-handler.js +22 -3
  33. package/lib/interceptor/dump.js +2 -2
  34. package/lib/interceptor/redirect.js +11 -14
  35. package/lib/interceptor/response-error.js +89 -0
  36. package/lib/llhttp/constants.d.ts +97 -0
  37. package/lib/llhttp/constants.js +412 -192
  38. package/lib/llhttp/constants.js.map +1 -0
  39. package/lib/llhttp/llhttp-wasm.js +11 -1
  40. package/lib/llhttp/llhttp_simd-wasm.js +11 -1
  41. package/lib/llhttp/utils.d.ts +2 -0
  42. package/lib/llhttp/utils.js +9 -9
  43. package/lib/llhttp/utils.js.map +1 -0
  44. package/lib/mock/mock-client.js +2 -2
  45. package/lib/mock/mock-pool.js +2 -2
  46. package/lib/mock/mock-symbols.js +1 -0
  47. package/lib/util/timers.js +324 -44
  48. package/lib/web/cookies/index.js +15 -13
  49. package/lib/web/cookies/parse.js +2 -2
  50. package/lib/web/eventsource/eventsource-stream.js +9 -8
  51. package/lib/web/eventsource/eventsource.js +10 -6
  52. package/lib/web/fetch/body.js +31 -11
  53. package/lib/web/fetch/data-url.js +1 -1
  54. package/lib/web/fetch/formdata-parser.js +1 -2
  55. package/lib/web/fetch/formdata.js +28 -37
  56. package/lib/web/fetch/headers.js +1 -1
  57. package/lib/web/fetch/index.js +7 -8
  58. package/lib/web/fetch/request.js +11 -28
  59. package/lib/web/fetch/response.js +13 -41
  60. package/lib/web/fetch/symbols.js +0 -1
  61. package/lib/web/fetch/util.js +3 -12
  62. package/lib/web/fetch/webidl.js +73 -62
  63. package/lib/web/websocket/connection.js +26 -174
  64. package/lib/web/websocket/constants.js +1 -1
  65. package/lib/web/websocket/frame.js +45 -3
  66. package/lib/web/websocket/receiver.js +28 -26
  67. package/lib/web/websocket/sender.js +18 -13
  68. package/lib/web/websocket/util.js +20 -74
  69. package/lib/web/websocket/websocket.js +294 -70
  70. package/package.json +16 -29
  71. package/scripts/strip-comments.js +3 -1
  72. package/types/agent.d.ts +7 -7
  73. package/types/api.d.ts +24 -24
  74. package/types/balanced-pool.d.ts +11 -11
  75. package/types/client.d.ts +11 -12
  76. package/types/diagnostics-channel.d.ts +10 -10
  77. package/types/dispatcher.d.ts +96 -97
  78. package/types/env-http-proxy-agent.d.ts +2 -2
  79. package/types/errors.d.ts +53 -47
  80. package/types/eventsource.d.ts +0 -2
  81. package/types/fetch.d.ts +8 -8
  82. package/types/formdata.d.ts +7 -7
  83. package/types/global-dispatcher.d.ts +4 -4
  84. package/types/global-origin.d.ts +5 -5
  85. package/types/handlers.d.ts +4 -4
  86. package/types/header.d.ts +157 -1
  87. package/types/index.d.ts +42 -46
  88. package/types/interceptors.d.ts +10 -8
  89. package/types/mock-agent.d.ts +18 -18
  90. package/types/mock-client.d.ts +4 -4
  91. package/types/mock-errors.d.ts +3 -3
  92. package/types/mock-interceptor.d.ts +19 -19
  93. package/types/mock-pool.d.ts +4 -4
  94. package/types/patch.d.ts +0 -42
  95. package/types/pool-stats.d.ts +8 -8
  96. package/types/pool.d.ts +12 -12
  97. package/types/proxy-agent.d.ts +4 -4
  98. package/types/readable.d.ts +14 -9
  99. package/types/retry-agent.d.ts +1 -1
  100. package/types/retry-handler.d.ts +8 -8
  101. package/types/util.d.ts +3 -3
  102. package/types/utility.d.ts +7 -0
  103. package/types/webidl.d.ts +22 -4
  104. package/types/websocket.d.ts +1 -3
  105. package/docs/docs/api/DispatchInterceptor.md +0 -60
  106. package/lib/interceptor/redirect-interceptor.js +0 -21
  107. package/lib/web/fetch/file.js +0 -126
  108. package/lib/web/fileapi/encoding.js +0 -290
  109. package/lib/web/fileapi/filereader.js +0 -344
  110. package/lib/web/fileapi/progressevent.js +0 -78
  111. package/lib/web/fileapi/symbols.js +0 -10
  112. package/lib/web/fileapi/util.js +0 -391
  113. package/lib/web/websocket/symbols.js +0 -12
  114. package/types/file.d.ts +0 -39
  115. package/types/filereader.d.ts +0 -54
@@ -3,8 +3,6 @@
3
3
  const util = require('../../core/util')
4
4
  const {
5
5
  ReadableStreamFrom,
6
- isBlobLike,
7
- isReadableStreamLike,
8
6
  readableStreamClose,
9
7
  createDeferredPromise,
10
8
  fullyReadBody,
@@ -16,12 +14,25 @@ const { kState } = require('./symbols')
16
14
  const { webidl } = require('./webidl')
17
15
  const { Blob } = require('node:buffer')
18
16
  const assert = require('node:assert')
19
- const { isErrored } = require('../../core/util')
17
+ const { isErrored, isDisturbed } = require('node:stream')
20
18
  const { isArrayBuffer } = require('node:util/types')
21
19
  const { serializeAMimeType } = require('./data-url')
22
20
  const { multipartFormDataParser } = require('./formdata-parser')
23
21
 
24
22
  const textEncoder = new TextEncoder()
23
+ function noop () {}
24
+
25
+ const hasFinalizationRegistry = globalThis.FinalizationRegistry && process.version.indexOf('v18') !== 0
26
+ let streamRegistry
27
+
28
+ if (hasFinalizationRegistry) {
29
+ streamRegistry = new FinalizationRegistry((weakRef) => {
30
+ const stream = weakRef.deref()
31
+ if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
32
+ stream.cancel('Response object has been garbage collected').catch(noop)
33
+ }
34
+ })
35
+ }
25
36
 
26
37
  // https://fetch.spec.whatwg.org/#concept-bodyinit-extract
27
38
  function extractBody (object, keepalive = false) {
@@ -31,7 +42,7 @@ function extractBody (object, keepalive = false) {
31
42
  // 2. If object is a ReadableStream object, then set stream to object.
32
43
  if (object instanceof ReadableStream) {
33
44
  stream = object
34
- } else if (isBlobLike(object)) {
45
+ } else if (object instanceof Blob) {
35
46
  // 3. Otherwise, if object is a Blob object, set stream to the
36
47
  // result of running object’s get stream.
37
48
  stream = object.stream()
@@ -54,7 +65,7 @@ function extractBody (object, keepalive = false) {
54
65
  }
55
66
 
56
67
  // 5. Assert: stream is a ReadableStream object.
57
- assert(isReadableStreamLike(stream))
68
+ assert(stream instanceof ReadableStream)
58
69
 
59
70
  // 6. Let action be null.
60
71
  let action = null
@@ -99,7 +110,7 @@ function extractBody (object, keepalive = false) {
99
110
 
100
111
  // Set source to a copy of the bytes held by object.
101
112
  source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength))
102
- } else if (util.isFormDataLike(object)) {
113
+ } else if (object instanceof FormData) {
103
114
  const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}`
104
115
  const prefix = `--${boundary}\r\nContent-Disposition: form-data`
105
116
 
@@ -165,7 +176,7 @@ function extractBody (object, keepalive = false) {
165
176
  // followed by the multipart/form-data boundary string generated
166
177
  // by the multipart/form-data encoding algorithm.
167
178
  type = `multipart/form-data; boundary=${boundary}`
168
- } else if (isBlobLike(object)) {
179
+ } else if (object instanceof Blob) {
169
180
  // Blob
170
181
 
171
182
  // Set source to object.
@@ -264,7 +275,7 @@ function safelyExtractBody (object, keepalive = false) {
264
275
  return extractBody(object, keepalive)
265
276
  }
266
277
 
267
- function cloneBody (body) {
278
+ function cloneBody (instance, body) {
268
279
  // To clone a body body, run these steps:
269
280
 
270
281
  // https://fetch.spec.whatwg.org/#concept-body-clone
@@ -272,6 +283,10 @@ function cloneBody (body) {
272
283
  // 1. Let « out1, out2 » be the result of teeing body’s stream.
273
284
  const [out1, out2] = body.stream.tee()
274
285
 
286
+ if (hasFinalizationRegistry) {
287
+ streamRegistry.register(instance, new WeakRef(out1))
288
+ }
289
+
275
290
  // 2. Set body’s stream to out1.
276
291
  body.stream = out1
277
292
 
@@ -414,7 +429,7 @@ async function consumeBody (object, convertBytesToJSValue, instance) {
414
429
 
415
430
  // 1. If object is unusable, then return a promise rejected
416
431
  // with a TypeError.
417
- if (bodyUnusable(object[kState].body)) {
432
+ if (bodyUnusable(object)) {
418
433
  throw new TypeError('Body is unusable: Body has already been read')
419
434
  }
420
435
 
@@ -454,7 +469,9 @@ async function consumeBody (object, convertBytesToJSValue, instance) {
454
469
  }
455
470
 
456
471
  // https://fetch.spec.whatwg.org/#body-unusable
457
- function bodyUnusable (body) {
472
+ function bodyUnusable (object) {
473
+ const body = object[kState].body
474
+
458
475
  // An object including the Body interface mixin is
459
476
  // said to be unusable if its body is non-null and
460
477
  // its body’s stream is disturbed or locked.
@@ -496,5 +513,8 @@ module.exports = {
496
513
  extractBody,
497
514
  safelyExtractBody,
498
515
  cloneBody,
499
- mixinBody
516
+ mixinBody,
517
+ streamRegistry,
518
+ hasFinalizationRegistry,
519
+ bodyUnusable
500
520
  }
@@ -431,7 +431,7 @@ function parseMIMEType (input) {
431
431
  /** @param {string} data */
432
432
  function forgivingBase64 (data) {
433
433
  // 1. Remove all ASCII whitespace from data.
434
- data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '') // eslint-disable-line
434
+ data = data.replace(ASCII_WHITESPACE_REPLACE_REGEX, '')
435
435
 
436
436
  let dataLength = data.length
437
437
  // 2. If data’s code point length divides by 4 leaving
@@ -3,7 +3,6 @@
3
3
  const { isUSVString, bufferToLowerCasedHeaderName } = require('../../core/util')
4
4
  const { utf8DecodeBytes } = require('./util')
5
5
  const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url')
6
- const { isFileLike } = require('./file')
7
6
  const { makeEntry } = require('./formdata')
8
7
  const assert = require('node:assert')
9
8
  const { File: NodeFile } = require('node:buffer')
@@ -195,7 +194,7 @@ function multipartFormDataParser (input, mimeType) {
195
194
 
196
195
  // 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.
197
196
  assert(isUSVString(name))
198
- assert((typeof value === 'string' && isUSVString(value)) || isFileLike(value))
197
+ assert((typeof value === 'string' && isUSVString(value)) || value instanceof File)
199
198
 
200
199
  // 5.13. Create an entry with name and value, and append it to entry list.
201
200
  entryList.push(makeEntry(name, value, filename))
@@ -1,9 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { isBlobLike, iteratorMixin } = require('./util')
3
+ const { iteratorMixin } = require('./util')
4
4
  const { kState } = require('./symbols')
5
5
  const { kEnumerableProperty } = require('../../core/util')
6
- const { FileLike, isFileLike } = require('./file')
7
6
  const { webidl } = require('./webidl')
8
7
  const { File: NativeFile } = require('node:buffer')
9
8
  const nodeUtil = require('node:util')
@@ -31,22 +30,20 @@ class FormData {
31
30
  const prefix = 'FormData.append'
32
31
  webidl.argumentLengthCheck(arguments, 2, prefix)
33
32
 
34
- if (arguments.length === 3 && !isBlobLike(value)) {
35
- throw new TypeError(
36
- "Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'"
37
- )
33
+ name = webidl.converters.USVString(name)
34
+
35
+ if (arguments.length === 3 || value instanceof Blob) {
36
+ value = webidl.converters.Blob(value, prefix, 'value')
37
+
38
+ if (filename !== undefined) {
39
+ filename = webidl.converters.USVString(filename)
40
+ }
41
+ } else {
42
+ value = webidl.converters.USVString(value)
38
43
  }
39
44
 
40
45
  // 1. Let value be value if given; otherwise blobValue.
41
46
 
42
- name = webidl.converters.USVString(name, prefix, 'name')
43
- value = isBlobLike(value)
44
- ? webidl.converters.Blob(value, prefix, 'value', { strict: false })
45
- : webidl.converters.USVString(value, prefix, 'value')
46
- filename = arguments.length === 3
47
- ? webidl.converters.USVString(filename, prefix, 'filename')
48
- : undefined
49
-
50
47
  // 2. Let entry be the result of creating an entry with
51
48
  // name, value, and filename if given.
52
49
  const entry = makeEntry(name, value, filename)
@@ -61,7 +58,7 @@ class FormData {
61
58
  const prefix = 'FormData.delete'
62
59
  webidl.argumentLengthCheck(arguments, 1, prefix)
63
60
 
64
- name = webidl.converters.USVString(name, prefix, 'name')
61
+ name = webidl.converters.USVString(name)
65
62
 
66
63
  // The delete(name) method steps are to remove all entries whose name
67
64
  // is name from this’s entry list.
@@ -74,7 +71,7 @@ class FormData {
74
71
  const prefix = 'FormData.get'
75
72
  webidl.argumentLengthCheck(arguments, 1, prefix)
76
73
 
77
- name = webidl.converters.USVString(name, prefix, 'name')
74
+ name = webidl.converters.USVString(name)
78
75
 
79
76
  // 1. If there is no entry whose name is name in this’s entry list,
80
77
  // then return null.
@@ -94,7 +91,7 @@ class FormData {
94
91
  const prefix = 'FormData.getAll'
95
92
  webidl.argumentLengthCheck(arguments, 1, prefix)
96
93
 
97
- name = webidl.converters.USVString(name, prefix, 'name')
94
+ name = webidl.converters.USVString(name)
98
95
 
99
96
  // 1. If there is no entry whose name is name in this’s entry list,
100
97
  // then return the empty list.
@@ -111,7 +108,7 @@ class FormData {
111
108
  const prefix = 'FormData.has'
112
109
  webidl.argumentLengthCheck(arguments, 1, prefix)
113
110
 
114
- name = webidl.converters.USVString(name, prefix, 'name')
111
+ name = webidl.converters.USVString(name)
115
112
 
116
113
  // The has(name) method steps are to return true if there is an entry
117
114
  // whose name is name in this’s entry list; otherwise false.
@@ -124,10 +121,16 @@ class FormData {
124
121
  const prefix = 'FormData.set'
125
122
  webidl.argumentLengthCheck(arguments, 2, prefix)
126
123
 
127
- if (arguments.length === 3 && !isBlobLike(value)) {
128
- throw new TypeError(
129
- "Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'"
130
- )
124
+ name = webidl.converters.USVString(name)
125
+
126
+ if (arguments.length === 3 || value instanceof Blob) {
127
+ value = webidl.converters.Blob(value, prefix, 'value')
128
+
129
+ if (filename !== undefined) {
130
+ filename = webidl.converters.USVString(filename)
131
+ }
132
+ } else {
133
+ value = webidl.converters.USVString(value)
131
134
  }
132
135
 
133
136
  // The set(name, value) and set(name, blobValue, filename) method steps
@@ -135,14 +138,6 @@ class FormData {
135
138
 
136
139
  // 1. Let value be value if given; otherwise blobValue.
137
140
 
138
- name = webidl.converters.USVString(name, prefix, 'name')
139
- value = isBlobLike(value)
140
- ? webidl.converters.Blob(value, prefix, 'name', { strict: false })
141
- : webidl.converters.USVString(value, prefix, 'name')
142
- filename = arguments.length === 3
143
- ? webidl.converters.USVString(filename, prefix, 'name')
144
- : undefined
145
-
146
141
  // 2. Let entry be the result of creating an entry with name, value, and
147
142
  // filename if given.
148
143
  const entry = makeEntry(name, value, filename)
@@ -222,10 +217,8 @@ function makeEntry (name, value, filename) {
222
217
 
223
218
  // 1. If value is not a File object, then set value to a new File object,
224
219
  // representing the same bytes, whose name attribute value is "blob"
225
- if (!isFileLike(value)) {
226
- value = value instanceof Blob
227
- ? new File([value], 'blob', { type: value.type })
228
- : new FileLike(value, 'blob', { type: value.type })
220
+ if (!(value instanceof File)) {
221
+ value = new File([value], 'blob', { type: value.type })
229
222
  }
230
223
 
231
224
  // 2. If filename is given, then set value to a new File object,
@@ -237,9 +230,7 @@ function makeEntry (name, value, filename) {
237
230
  lastModified: value.lastModified
238
231
  }
239
232
 
240
- value = value instanceof NativeFile
241
- ? new File([value], filename, options)
242
- : new FileLike(value, filename, options)
233
+ value = new File([value], filename, options)
243
234
  }
244
235
  }
245
236
 
@@ -645,7 +645,7 @@ Object.defineProperties(Headers.prototype, {
645
645
  })
646
646
 
647
647
  webidl.converters.HeadersInit = function (V, prefix, argument) {
648
- if (webidl.util.Type(V) === 'Object') {
648
+ if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
649
649
  const iterator = Reflect.get(V, Symbol.iterator)
650
650
 
651
651
  // A work-around to ensure we send the properly-cased Headers when V is a Headers object.
@@ -30,7 +30,6 @@ const {
30
30
  determineRequestsReferrer,
31
31
  coarsenedSharedCurrentTime,
32
32
  createDeferredPromise,
33
- isBlobLike,
34
33
  sameOrigin,
35
34
  isCancelled,
36
35
  isAborted,
@@ -58,8 +57,8 @@ const {
58
57
  subresourceSet
59
58
  } = require('./constants')
60
59
  const EE = require('node:events')
61
- const { Readable, pipeline, finished } = require('node:stream')
62
- const { addAbortListener, isErrored, isReadable, bufferToLowerCasedHeaderName } = require('../../core/util')
60
+ const { Readable, pipeline, finished, isErrored, isReadable } = require('node:stream')
61
+ const { addAbortListener, bufferToLowerCasedHeaderName } = require('../../core/util')
63
62
  const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
64
63
  const { getGlobalDispatcher } = require('../../global')
65
64
  const { webidl } = require('./webidl')
@@ -327,7 +326,7 @@ function abortFetch (p, request, responseObject, error) {
327
326
 
328
327
  // 2. If request’s body is not null and is readable, then cancel request’s
329
328
  // body with error.
330
- if (request.body != null && isReadable(request.body?.stream)) {
329
+ if (request.body?.stream != null && isReadable(request.body.stream)) {
331
330
  request.body.stream.cancel(error).catch((err) => {
332
331
  if (err.code === 'ERR_INVALID_STATE') {
333
332
  // Node bug?
@@ -347,7 +346,7 @@ function abortFetch (p, request, responseObject, error) {
347
346
 
348
347
  // 5. If response’s body is not null and is readable, then error response’s
349
348
  // body with error.
350
- if (response.body != null && isReadable(response.body?.stream)) {
349
+ if (response.body?.stream != null && isReadable(response.body.stream)) {
351
350
  response.body.stream.cancel(error).catch((err) => {
352
351
  if (err.code === 'ERR_INVALID_STATE') {
353
352
  // Node bug?
@@ -810,7 +809,7 @@ function schemeFetch (fetchParams) {
810
809
 
811
810
  // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
812
811
  // object is not a Blob object, then return a network error.
813
- if (request.method !== 'GET' || !isBlobLike(blob)) {
812
+ if (request.method !== 'GET' || !(blob instanceof Blob)) {
814
813
  return Promise.resolve(makeNetworkError('invalid method'))
815
814
  }
816
815
 
@@ -1460,7 +1459,7 @@ async function httpNetworkOrCacheFetch (
1460
1459
  // user agents should append `User-Agent`/default `User-Agent` value to
1461
1460
  // httpRequest’s header list.
1462
1461
  if (!httpRequest.headersList.contains('user-agent', true)) {
1463
- httpRequest.headersList.append('user-agent', defaultUserAgent)
1462
+ httpRequest.headersList.append('user-agent', defaultUserAgent, true)
1464
1463
  }
1465
1464
 
1466
1465
  // 15. If httpRequest’s cache mode is "default" and httpRequest’s header
@@ -2137,7 +2136,7 @@ async function httpNetworkFetch (
2137
2136
 
2138
2137
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
2139
2138
  if (codings.length !== 0 && request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) {
2140
- for (let i = 0; i < codings.length; ++i) {
2139
+ for (let i = codings.length - 1; i >= 0; --i) {
2141
2140
  const coding = codings[i]
2142
2141
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2
2143
2142
  if (coding === 'x-gzip' || coding === 'gzip') {
@@ -2,7 +2,7 @@
2
2
 
3
3
  'use strict'
4
4
 
5
- const { extractBody, mixinBody, cloneBody } = require('./body')
5
+ const { extractBody, mixinBody, cloneBody, bodyUnusable } = require('./body')
6
6
  const { Headers, fill: fillHeaders, HeadersList, setHeadersGuard, getHeadersGuard, setHeadersList, getHeadersList } = require('./headers')
7
7
  const { FinalizationRegistry } = require('./dispatcher-weakref')()
8
8
  const util = require('../../core/util')
@@ -28,7 +28,7 @@ const { webidl } = require('./webidl')
28
28
  const { URLSerializer } = require('./data-url')
29
29
  const { kConstruct } = require('../../core/symbols')
30
30
  const assert = require('node:assert')
31
- const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('node:events')
31
+ const { getMaxListeners, setMaxListeners, defaultMaxListeners } = require('node:events')
32
32
 
33
33
  const kAbortController = Symbol('abortController')
34
34
 
@@ -81,7 +81,7 @@ let patchMethodWarning = false
81
81
  // https://fetch.spec.whatwg.org/#request-class
82
82
  class Request {
83
83
  // https://fetch.spec.whatwg.org/#dom-request
84
- constructor (input, init = {}) {
84
+ constructor (input, init = undefined) {
85
85
  if (input === kConstruct) {
86
86
  return
87
87
  }
@@ -400,16 +400,6 @@ class Request {
400
400
 
401
401
  // 29. If signal is not null, then make this’s signal follow signal.
402
402
  if (signal != null) {
403
- if (
404
- !signal ||
405
- typeof signal.aborted !== 'boolean' ||
406
- typeof signal.addEventListener !== 'function'
407
- ) {
408
- throw new TypeError(
409
- "Failed to construct 'Request': member signal is not of type AbortSignal."
410
- )
411
- }
412
-
413
403
  if (signal.aborted) {
414
404
  ac.abort(signal.reason)
415
405
  } else {
@@ -429,8 +419,6 @@ class Request {
429
419
  // This is only available in node >= v19.9.0
430
420
  if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) {
431
421
  setMaxListeners(1500, signal)
432
- } else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
433
- setMaxListeners(1500, signal)
434
422
  }
435
423
  } catch {}
436
424
 
@@ -522,7 +510,7 @@ class Request {
522
510
  // not contain `Content-Type`, then append `Content-Type`/Content-Type to
523
511
  // this’s headers.
524
512
  if (contentType && !getHeadersList(this[kHeaders]).contains('content-type', true)) {
525
- this[kHeaders].append('content-type', contentType)
513
+ this[kHeaders].append('content-type', contentType, true)
526
514
  }
527
515
  }
528
516
 
@@ -557,7 +545,7 @@ class Request {
557
545
  // 40. If initBody is null and inputBody is non-null, then:
558
546
  if (initBody == null && inputBody != null) {
559
547
  // 1. If input is unusable, then throw a TypeError.
560
- if (util.isDisturbed(inputBody.stream) || inputBody.stream.locked) {
548
+ if (bodyUnusable(input)) {
561
549
  throw new TypeError(
562
550
  'Cannot construct a Request with a Request object that has already been used.'
563
551
  )
@@ -759,7 +747,7 @@ class Request {
759
747
  webidl.brandCheck(this, Request)
760
748
 
761
749
  // 1. If this is unusable, then throw a TypeError.
762
- if (this.bodyUsed || this.body?.locked) {
750
+ if (bodyUnusable(this)) {
763
751
  throw new TypeError('unusable')
764
752
  }
765
753
 
@@ -877,7 +865,7 @@ function cloneRequest (request) {
877
865
  // 2. If request’s body is non-null, set newRequest’s body to the
878
866
  // result of cloning request’s body.
879
867
  if (request.body != null) {
880
- newRequest.body = cloneBody(request.body)
868
+ newRequest.body = cloneBody(newRequest, request.body)
881
869
  }
882
870
 
883
871
  // 3. Return newRequest.
@@ -928,21 +916,17 @@ Object.defineProperties(Request.prototype, {
928
916
  }
929
917
  })
930
918
 
931
- webidl.converters.Request = webidl.interfaceConverter(
932
- Request
933
- )
934
-
935
919
  // https://fetch.spec.whatwg.org/#requestinfo
936
920
  webidl.converters.RequestInfo = function (V, prefix, argument) {
937
921
  if (typeof V === 'string') {
938
- return webidl.converters.USVString(V, prefix, argument)
922
+ return webidl.converters.USVString(V)
939
923
  }
940
924
 
941
925
  if (V instanceof Request) {
942
- return webidl.converters.Request(V, prefix, argument)
926
+ return V
943
927
  }
944
928
 
945
- return webidl.converters.USVString(V, prefix, argument)
929
+ return webidl.converters.USVString(V)
946
930
  }
947
931
 
948
932
  webidl.converters.AbortSignal = webidl.interfaceConverter(
@@ -1013,8 +997,7 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([
1013
997
  (signal) => webidl.converters.AbortSignal(
1014
998
  signal,
1015
999
  'RequestInit',
1016
- 'signal',
1017
- { strict: false }
1000
+ 'signal'
1018
1001
  )
1019
1002
  )
1020
1003
  },
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = require('./headers')
4
- const { extractBody, cloneBody, mixinBody } = require('./body')
4
+ const { extractBody, cloneBody, mixinBody, hasFinalizationRegistry, streamRegistry, bodyUnusable } = require('./body')
5
5
  const util = require('../../core/util')
6
6
  const nodeUtil = require('node:util')
7
7
  const { kEnumerableProperty } = util
@@ -9,7 +9,6 @@ const {
9
9
  isValidReasonPhrase,
10
10
  isCancelled,
11
11
  isAborted,
12
- isBlobLike,
13
12
  serializeJavascriptValueToJSONString,
14
13
  isErrorLike,
15
14
  isomorphicEncode,
@@ -26,24 +25,9 @@ const { URLSerializer } = require('./data-url')
26
25
  const { kConstruct } = require('../../core/symbols')
27
26
  const assert = require('node:assert')
28
27
  const { types } = require('node:util')
29
- const { isDisturbed, isErrored } = require('node:stream')
30
28
 
31
29
  const textEncoder = new TextEncoder('utf-8')
32
30
 
33
- const hasFinalizationRegistry = globalThis.FinalizationRegistry && process.version.indexOf('v18') !== 0
34
- let registry
35
-
36
- if (hasFinalizationRegistry) {
37
- registry = new FinalizationRegistry((weakRef) => {
38
- const stream = weakRef.deref()
39
- if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
40
- stream.cancel('Response object has been garbage collected').catch(noop)
41
- }
42
- })
43
- }
44
-
45
- function noop () {}
46
-
47
31
  // https://fetch.spec.whatwg.org/#response-class
48
32
  class Response {
49
33
  // Creates network error Response.
@@ -57,7 +41,7 @@ class Response {
57
41
  }
58
42
 
59
43
  // https://fetch.spec.whatwg.org/#dom-response-json
60
- static json (data, init = {}) {
44
+ static json (data, init = undefined) {
61
45
  webidl.argumentLengthCheck(arguments, 1, 'Response.json')
62
46
 
63
47
  if (init !== null) {
@@ -124,7 +108,7 @@ class Response {
124
108
  }
125
109
 
126
110
  // https://fetch.spec.whatwg.org/#dom-response
127
- constructor (body = null, init = {}) {
111
+ constructor (body = null, init = undefined) {
128
112
  if (body === kConstruct) {
129
113
  return
130
114
  }
@@ -244,7 +228,7 @@ class Response {
244
228
  webidl.brandCheck(this, Response)
245
229
 
246
230
  // 1. If this is unusable, then throw a TypeError.
247
- if (this.bodyUsed || this.body?.locked) {
231
+ if (bodyUnusable(this)) {
248
232
  throw webidl.errors.exception({
249
233
  header: 'Response.clone',
250
234
  message: 'Body has already been consumed.'
@@ -327,7 +311,7 @@ function cloneResponse (response) {
327
311
  // 3. If response’s body is non-null, then set newResponse’s body to the
328
312
  // result of cloning response’s body.
329
313
  if (response.body != null) {
330
- newResponse.body = cloneBody(response.body)
314
+ newResponse.body = cloneBody(newResponse, response.body)
331
315
  }
332
316
 
333
317
  // 4. Return newResponse.
@@ -532,44 +516,32 @@ function fromInnerResponse (innerResponse, guard) {
532
516
  // a primitive or an object, even undefined. If the held value is an object, the registry keeps
533
517
  // a strong reference to it (so it can pass it to the cleanup callback later). Reworded from
534
518
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
535
- registry.register(response, new WeakRef(innerResponse.body.stream))
519
+ streamRegistry.register(response, new WeakRef(innerResponse.body.stream))
536
520
  }
537
521
 
538
522
  return response
539
523
  }
540
524
 
541
- webidl.converters.ReadableStream = webidl.interfaceConverter(
542
- ReadableStream
543
- )
544
-
545
- webidl.converters.FormData = webidl.interfaceConverter(
546
- FormData
547
- )
548
-
549
- webidl.converters.URLSearchParams = webidl.interfaceConverter(
550
- URLSearchParams
551
- )
552
-
553
525
  // https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit
554
526
  webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
555
527
  if (typeof V === 'string') {
556
528
  return webidl.converters.USVString(V, prefix, name)
557
529
  }
558
530
 
559
- if (isBlobLike(V)) {
560
- return webidl.converters.Blob(V, prefix, name, { strict: false })
531
+ if (V instanceof Blob) {
532
+ return V
561
533
  }
562
534
 
563
535
  if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) {
564
- return webidl.converters.BufferSource(V, prefix, name)
536
+ return V
565
537
  }
566
538
 
567
- if (util.isFormDataLike(V)) {
568
- return webidl.converters.FormData(V, prefix, name, { strict: false })
539
+ if (V instanceof FormData) {
540
+ return V
569
541
  }
570
542
 
571
543
  if (V instanceof URLSearchParams) {
572
- return webidl.converters.URLSearchParams(V, prefix, name)
544
+ return V
573
545
  }
574
546
 
575
547
  return webidl.converters.DOMString(V, prefix, name)
@@ -578,7 +550,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
578
550
  // https://fetch.spec.whatwg.org/#bodyinit
579
551
  webidl.converters.BodyInit = function (V, prefix, argument) {
580
552
  if (V instanceof ReadableStream) {
581
- return webidl.converters.ReadableStream(V, prefix, argument)
553
+ return V
582
554
  }
583
555
 
584
556
  // Note: the spec doesn't include async iterables,
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
- kUrl: Symbol('url'),
5
4
  kHeaders: Symbol('headers'),
6
5
  kSignal: Symbol('signal'),
7
6
  kState: Symbol('state'),
@@ -6,7 +6,7 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet
6
6
  const { getGlobalOrigin } = require('./global')
7
7
  const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url')
8
8
  const { performance } = require('node:perf_hooks')
9
- const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
9
+ const { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util')
10
10
  const assert = require('node:assert')
11
11
  const { isUint8Array } = require('node:util/types')
12
12
  const { webidl } = require('./webidl')
@@ -449,7 +449,7 @@ function determineRequestsReferrer (request) {
449
449
  // 3. Return referrerOrigin.
450
450
  return referrerOrigin
451
451
  }
452
- case 'strict-origin': // eslint-disable-line
452
+ case 'strict-origin':
453
453
  /**
454
454
  * 1. If referrerURL is a potentially trustworthy URL and
455
455
  * request’s current URL is not a potentially trustworthy URL,
@@ -472,7 +472,7 @@ function determineRequestsReferrer (request) {
472
472
  /**
473
473
  * @see https://w3c.github.io/webappsec-referrer-policy/#strip-url
474
474
  * @param {URL} url
475
- * @param {boolean|undefined} originOnly
475
+ * @param {boolean} [originOnly]
476
476
  */
477
477
  function stripURLForReferrer (url, originOnly) {
478
478
  // 1. Assert: url is a URL.
@@ -1061,13 +1061,6 @@ async function fullyReadBody (body, processBody, processBodyError) {
1061
1061
  }
1062
1062
  }
1063
1063
 
1064
- function isReadableStreamLike (stream) {
1065
- return stream instanceof ReadableStream || (
1066
- stream[Symbol.toStringTag] === 'ReadableStream' &&
1067
- typeof stream.tee === 'function'
1068
- )
1069
- }
1070
-
1071
1064
  /**
1072
1065
  * @param {ReadableStreamController<Uint8Array>} controller
1073
1066
  */
@@ -1589,7 +1582,6 @@ module.exports = {
1589
1582
  requestCurrentURL,
1590
1583
  responseURL,
1591
1584
  responseLocationURL,
1592
- isBlobLike,
1593
1585
  isURLPotentiallyTrustworthy,
1594
1586
  isValidReasonPhrase,
1595
1587
  sameOrigin,
@@ -1602,7 +1594,6 @@ module.exports = {
1602
1594
  isErrorLike,
1603
1595
  fullyReadBody,
1604
1596
  bytesMatch,
1605
- isReadableStreamLike,
1606
1597
  readableStreamClose,
1607
1598
  isomorphicEncode,
1608
1599
  urlIsLocal,