undici 6.6.2 → 6.7.0

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 (114) hide show
  1. package/{README.md → docs/README.md} +19 -15
  2. package/docs/{api → docs/api}/Dispatcher.md +39 -3
  3. package/docs/docs/api/Fetch.md +57 -0
  4. package/docs/{api → docs/api}/ProxyAgent.md +3 -1
  5. package/docs/docs/api/RetryAgent.md +45 -0
  6. package/docs/{api → docs/api}/RetryHandler.md +1 -1
  7. package/docs/{api → docs/api}/api-lifecycle.md +33 -4
  8. package/docs/{best-practices → docs/best-practices}/proxy.md +6 -6
  9. package/index-fetch.js +9 -8
  10. package/index.js +31 -25
  11. package/lib/core/request.js +72 -135
  12. package/lib/core/symbols.js +6 -5
  13. package/lib/core/tree.js +46 -26
  14. package/lib/core/util.js +41 -20
  15. package/lib/{agent.js → dispatcher/agent.js} +4 -4
  16. package/lib/{balanced-pool.js → dispatcher/balanced-pool.js} +3 -3
  17. package/lib/dispatcher/client-h1.js +1339 -0
  18. package/lib/dispatcher/client-h2.js +639 -0
  19. package/lib/dispatcher/client.js +611 -0
  20. package/lib/{dispatcher-base.js → dispatcher/dispatcher-base.js} +2 -2
  21. package/lib/{pool-base.js → dispatcher/pool-base.js} +3 -3
  22. package/lib/{pool-stats.js → dispatcher/pool-stats.js} +1 -1
  23. package/lib/{pool.js → dispatcher/pool.js} +4 -4
  24. package/lib/{proxy-agent.js → dispatcher/proxy-agent.js} +29 -35
  25. package/lib/dispatcher/retry-agent.js +35 -0
  26. package/lib/global.js +1 -1
  27. package/lib/handler/{RetryHandler.js → retry-handler.js} +2 -2
  28. package/lib/interceptor/{redirectInterceptor.js → redirect-interceptor.js} +1 -1
  29. package/lib/mock/mock-agent.js +2 -2
  30. package/lib/mock/mock-client.js +1 -1
  31. package/lib/mock/mock-interceptor.js +2 -2
  32. package/lib/mock/mock-pool.js +1 -1
  33. package/lib/mock/mock-utils.js +6 -4
  34. package/lib/{cache → web/cache}/cache.js +2 -4
  35. package/lib/{cache → web/cache}/cachestorage.js +1 -1
  36. package/lib/web/cache/symbols.js +5 -0
  37. package/lib/{cache → web/cache}/util.js +5 -9
  38. package/lib/{cookies → web/cookies}/parse.js +1 -1
  39. package/lib/{cookies → web/cookies}/util.js +76 -60
  40. package/lib/{eventsource → web/eventsource}/eventsource.js +2 -6
  41. package/lib/{fetch → web/fetch}/body.js +23 -52
  42. package/lib/{fetch/dataURL.js → web/fetch/data-url.js} +2 -0
  43. package/lib/{compat → web/fetch}/dispatcher-weakref.js +1 -1
  44. package/lib/{fetch → web/fetch}/file.js +2 -2
  45. package/lib/{fetch → web/fetch}/formdata.js +6 -67
  46. package/lib/{fetch → web/fetch}/headers.js +99 -71
  47. package/lib/{fetch → web/fetch}/index.js +33 -25
  48. package/lib/{fetch → web/fetch}/request.js +14 -6
  49. package/lib/{fetch → web/fetch}/response.js +3 -3
  50. package/lib/{fetch → web/fetch}/symbols.js +2 -1
  51. package/lib/{fetch → web/fetch}/util.js +142 -48
  52. package/lib/{fetch → web/fetch}/webidl.js +46 -16
  53. package/lib/{fileapi → web/fileapi}/filereader.js +1 -1
  54. package/lib/{fileapi → web/fileapi}/util.js +1 -1
  55. package/lib/{websocket → web/websocket}/connection.js +20 -10
  56. package/lib/{websocket → web/websocket}/constants.js +7 -0
  57. package/lib/{websocket → web/websocket}/events.js +1 -1
  58. package/lib/{websocket → web/websocket}/frame.js +1 -0
  59. package/lib/{websocket → web/websocket}/receiver.js +9 -16
  60. package/lib/{websocket → web/websocket}/util.js +37 -23
  61. package/lib/{websocket → web/websocket}/websocket.js +21 -9
  62. package/package.json +26 -51
  63. package/types/dispatcher.d.ts +1 -1
  64. package/types/fetch.d.ts +20 -21
  65. package/types/index.d.ts +2 -1
  66. package/types/retry-agent.d.ts +11 -0
  67. package/types/webidl.d.ts +6 -1
  68. package/docs/api/Fetch.md +0 -27
  69. package/docs/assets/lifecycle-diagram.png +0 -0
  70. package/lib/cache/symbols.js +0 -5
  71. package/lib/client.js +0 -2295
  72. package/lib/llhttp/llhttp-wasm.js +0 -3
  73. package/lib/llhttp/llhttp.wasm +0 -0
  74. package/lib/llhttp/llhttp_simd.wasm +0 -0
  75. /package/docs/{api → docs/api}/Agent.md +0 -0
  76. /package/docs/{api → docs/api}/BalancedPool.md +0 -0
  77. /package/docs/{api → docs/api}/CacheStorage.md +0 -0
  78. /package/docs/{api → docs/api}/Client.md +0 -0
  79. /package/docs/{api → docs/api}/Connector.md +0 -0
  80. /package/docs/{api → docs/api}/ContentType.md +0 -0
  81. /package/docs/{api → docs/api}/Cookies.md +0 -0
  82. /package/docs/{api → docs/api}/Debug.md +0 -0
  83. /package/docs/{api → docs/api}/DiagnosticsChannel.md +0 -0
  84. /package/docs/{api → docs/api}/DispatchInterceptor.md +0 -0
  85. /package/docs/{api → docs/api}/Errors.md +0 -0
  86. /package/docs/{api → docs/api}/EventSource.md +0 -0
  87. /package/docs/{api → docs/api}/MockAgent.md +0 -0
  88. /package/docs/{api → docs/api}/MockClient.md +0 -0
  89. /package/docs/{api → docs/api}/MockErrors.md +0 -0
  90. /package/docs/{api → docs/api}/MockPool.md +0 -0
  91. /package/docs/{api → docs/api}/Pool.md +0 -0
  92. /package/docs/{api → docs/api}/PoolStats.md +0 -0
  93. /package/docs/{api → docs/api}/RedirectHandler.md +0 -0
  94. /package/docs/{api → docs/api}/Util.md +0 -0
  95. /package/docs/{api → docs/api}/WebSocket.md +0 -0
  96. /package/docs/{best-practices → docs/best-practices}/client-certificate.md +0 -0
  97. /package/docs/{best-practices → docs/best-practices}/mocking-request.md +0 -0
  98. /package/docs/{best-practices → docs/best-practices}/writing-tests.md +0 -0
  99. /package/lib/{dispatcher.js → dispatcher/dispatcher.js} +0 -0
  100. /package/lib/{node → dispatcher}/fixed-queue.js +0 -0
  101. /package/lib/handler/{DecoratorHandler.js → decorator-handler.js} +0 -0
  102. /package/lib/handler/{RedirectHandler.js → redirect-handler.js} +0 -0
  103. /package/lib/{timers.js → util/timers.js} +0 -0
  104. /package/lib/{cookies → web/cookies}/constants.js +0 -0
  105. /package/lib/{cookies → web/cookies}/index.js +0 -0
  106. /package/lib/{eventsource → web/eventsource}/eventsource-stream.js +0 -0
  107. /package/lib/{eventsource → web/eventsource}/util.js +0 -0
  108. /package/lib/{fetch → web/fetch}/LICENSE +0 -0
  109. /package/lib/{fetch → web/fetch}/constants.js +0 -0
  110. /package/lib/{fetch → web/fetch}/global.js +0 -0
  111. /package/lib/{fileapi → web/fileapi}/encoding.js +0 -0
  112. /package/lib/{fileapi → web/fileapi}/progressevent.js +0 -0
  113. /package/lib/{fileapi → web/fileapi}/symbols.js +0 -0
  114. /package/lib/{websocket → web/websocket}/symbols.js +0 -0
@@ -10,7 +10,7 @@ const {
10
10
  fromInnerResponse
11
11
  } = require('./response')
12
12
  const { HeadersList } = require('./headers')
13
- const { Request, makeRequest } = require('./request')
13
+ const { Request, cloneRequest } = require('./request')
14
14
  const zlib = require('node:zlib')
15
15
  const {
16
16
  bytesMatch,
@@ -47,7 +47,7 @@ const {
47
47
  createInflate,
48
48
  extractMimeType
49
49
  } = require('./util')
50
- const { kState } = require('./symbols')
50
+ const { kState, kDispatcher } = require('./symbols')
51
51
  const assert = require('node:assert')
52
52
  const { safelyExtractBody, extractBody } = require('./body')
53
53
  const {
@@ -59,13 +59,17 @@ const {
59
59
  } = require('./constants')
60
60
  const EE = require('node:events')
61
61
  const { Readable, pipeline } = require('node:stream')
62
- const { addAbortListener, isErrored, isReadable, nodeMajor, nodeMinor, bufferToLowerCasedHeaderName } = require('../core/util')
63
- const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./dataURL')
64
- const { getGlobalDispatcher } = require('../global')
62
+ const { addAbortListener, isErrored, isReadable, nodeMajor, nodeMinor, bufferToLowerCasedHeaderName } = require('../../core/util')
63
+ const { dataURLProcessor, serializeAMimeType, minimizeSupportedMimeType } = require('./data-url')
64
+ const { getGlobalDispatcher } = require('../../global')
65
65
  const { webidl } = require('./webidl')
66
66
  const { STATUS_CODES } = require('node:http')
67
67
  const GET_OR_HEAD = ['GET', 'HEAD']
68
68
 
69
+ const defaultUserAgent = typeof __UNDICI_IS_NODE__ !== 'undefined' || typeof esbuildDetection !== 'undefined'
70
+ ? 'node'
71
+ : 'undici'
72
+
69
73
  /** @type {import('buffer').resolveObjectURL} */
70
74
  let resolveObjectURL
71
75
 
@@ -77,12 +81,6 @@ class Fetch extends EE {
77
81
  this.connection = null
78
82
  this.dump = false
79
83
  this.state = 'ongoing'
80
- // 2 terminated listeners get added per request,
81
- // but only 1 gets removed. If there are 20 redirects,
82
- // 21 listeners will be added.
83
- // See https://github.com/nodejs/undici/issues/1711
84
- // TODO (fix): Find and fix root cause for leaked listener.
85
- this.setMaxListeners(21)
86
84
  }
87
85
 
88
86
  terminate (reason) {
@@ -206,7 +204,7 @@ function fetch (input, init = undefined) {
206
204
  const processResponse = (response) => {
207
205
  // 1. If locallyAborted is true, terminate these substeps.
208
206
  if (locallyAborted) {
209
- return Promise.resolve()
207
+ return
210
208
  }
211
209
 
212
210
  // 2. If response’s aborted flag is set, then:
@@ -219,14 +217,14 @@ function fetch (input, init = undefined) {
219
217
  // deserializedError.
220
218
 
221
219
  abortFetch(p, request, responseObject, controller.serializedAbortReason)
222
- return Promise.resolve()
220
+ return
223
221
  }
224
222
 
225
223
  // 3. If response is a network error, then reject p with a TypeError
226
224
  // and terminate these substeps.
227
225
  if (response.type === 'error') {
228
226
  p.reject(new TypeError('fetch failed', { cause: response.error }))
229
- return Promise.resolve()
227
+ return
230
228
  }
231
229
 
232
230
  // 4. Set responseObject to the result of creating a Response object,
@@ -241,7 +239,7 @@ function fetch (input, init = undefined) {
241
239
  request,
242
240
  processResponseEndOfBody: handleFetchDone,
243
241
  processResponse,
244
- dispatcher: init?.dispatcher ?? getGlobalDispatcher() // undici
242
+ dispatcher: requestObject[kDispatcher] // undici
245
243
  })
246
244
 
247
245
  // 14. Return p.
@@ -363,9 +361,9 @@ function fetching ({
363
361
  processResponseEndOfBody,
364
362
  processResponseConsumeBody,
365
363
  useParallelQueue = false,
366
- dispatcher // undici
364
+ dispatcher = getGlobalDispatcher() // undici
367
365
  }) {
368
- // This has bitten me in the ass more times than I'd like to admit.
366
+ // Ensure that the dispatcher is set accordingly
369
367
  assert(dispatcher)
370
368
 
371
369
  // 1. Let taskDestination be null.
@@ -1114,7 +1112,6 @@ function fetchFinale (fetchParams, response) {
1114
1112
  controller.enqueue(value)
1115
1113
  }
1116
1114
  },
1117
- queuingStrategy: new ByteLengthQueuingStrategy({ highWaterMark: 16384 }),
1118
1115
  type: 'bytes'
1119
1116
  })
1120
1117
 
@@ -1408,7 +1405,7 @@ async function httpNetworkOrCacheFetch (
1408
1405
  // Otherwise:
1409
1406
 
1410
1407
  // 1. Set httpRequest to a clone of request.
1411
- httpRequest = makeRequest(request)
1408
+ httpRequest = cloneRequest(request)
1412
1409
 
1413
1410
  // 2. Set httpFetchParams to a copy of fetchParams.
1414
1411
  httpFetchParams = { ...fetchParams }
@@ -1478,7 +1475,7 @@ async function httpNetworkOrCacheFetch (
1478
1475
  // user agents should append `User-Agent`/default `User-Agent` value to
1479
1476
  // httpRequest’s header list.
1480
1477
  if (!httpRequest.headersList.contains('user-agent', true)) {
1481
- httpRequest.headersList.append('user-agent', typeof esbuildDetection === 'undefined' ? 'undici' : 'node', true)
1478
+ httpRequest.headersList.append('user-agent', defaultUserAgent)
1482
1479
  }
1483
1480
 
1484
1481
  // 15. If httpRequest’s cache mode is "default" and httpRequest’s header
@@ -1929,7 +1926,6 @@ async function httpNetworkFetch (
1929
1926
  // cancelAlgorithm set to cancelAlgorithm.
1930
1927
  const stream = new ReadableStream(
1931
1928
  {
1932
- highWaterMark: 16384,
1933
1929
  async start (controller) {
1934
1930
  fetchParams.controller.controller = controller
1935
1931
  },
@@ -1939,15 +1935,14 @@ async function httpNetworkFetch (
1939
1935
  async cancel (reason) {
1940
1936
  await cancelAlgorithm(reason)
1941
1937
  },
1942
- type: 'bytes',
1943
- queuingStrategy: new ByteLengthQueuingStrategy({ highWaterMark: 16384 })
1938
+ type: 'bytes'
1944
1939
  }
1945
1940
  )
1946
1941
 
1947
1942
  // 17. Run these steps, but abort when the ongoing fetch is terminated:
1948
1943
 
1949
1944
  // 1. Set response’s body to a new body whose stream is stream.
1950
- response.body = { stream }
1945
+ response.body = { stream, source: null, length: null }
1951
1946
 
1952
1947
  // 2. If response is not a network error and request’s cache mode is
1953
1948
  // not "no-store", then update response in httpCache for request.
@@ -1966,6 +1961,7 @@ async function httpNetworkFetch (
1966
1961
  // 19. Run these steps in parallel:
1967
1962
 
1968
1963
  // 1. Run these steps, but abort when fetchParams is canceled:
1964
+ fetchParams.controller.onAborted = onAborted
1969
1965
  fetchParams.controller.on('terminated', onAborted)
1970
1966
  fetchParams.controller.resume = async () => {
1971
1967
  // 1. While true
@@ -2149,7 +2145,15 @@ async function httpNetworkFetch (
2149
2145
  const keys = Object.keys(rawHeaders)
2150
2146
  for (let i = 0; i < keys.length; ++i) {
2151
2147
  // The header names are already in lowercase.
2152
- headersList.append(keys[i], rawHeaders[keys[i]], true)
2148
+ const key = keys[i]
2149
+ const value = rawHeaders[key]
2150
+ if (key === 'set-cookie') {
2151
+ for (let j = 0; j < value.length; ++j) {
2152
+ headersList.append(key, value[j], true)
2153
+ }
2154
+ } else {
2155
+ headersList.append(key, value, true)
2156
+ }
2153
2157
  }
2154
2158
  // For H2, The header names are already in lowercase,
2155
2159
  // so we can avoid the `HeadersList#get` call here.
@@ -2234,6 +2238,10 @@ async function httpNetworkFetch (
2234
2238
  fetchParams.controller.off('terminated', this.abort)
2235
2239
  }
2236
2240
 
2241
+ if (fetchParams.controller.onAborted) {
2242
+ fetchParams.controller.off('terminated', fetchParams.controller.onAborted)
2243
+ }
2244
+
2237
2245
  fetchParams.controller.ended = true
2238
2246
 
2239
2247
  this.body.push(null)
@@ -4,8 +4,8 @@
4
4
 
5
5
  const { extractBody, mixinBody, cloneBody } = require('./body')
6
6
  const { Headers, fill: fillHeaders, HeadersList } = require('./headers')
7
- const { FinalizationRegistry } = require('../compat/dispatcher-weakref')()
8
- const util = require('../core/util')
7
+ const { FinalizationRegistry } = require('./dispatcher-weakref')()
8
+ const util = require('../../core/util')
9
9
  const {
10
10
  isValidHTTPToken,
11
11
  sameOrigin,
@@ -24,11 +24,11 @@ const {
24
24
  requestDuplex
25
25
  } = require('./constants')
26
26
  const { kEnumerableProperty } = util
27
- const { kHeaders, kSignal, kState, kGuard, kRealm } = require('./symbols')
27
+ const { kHeaders, kSignal, kState, kGuard, kRealm, kDispatcher } = require('./symbols')
28
28
  const { webidl } = require('./webidl')
29
29
  const { getGlobalOrigin } = require('./global')
30
- const { URLSerializer } = require('./dataURL')
31
- const { kHeadersList, kConstruct } = require('../core/symbols')
30
+ const { URLSerializer } = require('./data-url')
31
+ const { kHeadersList, kConstruct } = require('../../core/symbols')
32
32
  const assert = require('node:assert')
33
33
  const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('node:events')
34
34
 
@@ -78,6 +78,8 @@ class Request {
78
78
 
79
79
  // 5. If input is a string, then:
80
80
  if (typeof input === 'string') {
81
+ this[kDispatcher] = init.dispatcher
82
+
81
83
  // 1. Let parsedURL be the result of parsing input with baseURL.
82
84
  // 2. If parsedURL is failure, then throw a TypeError.
83
85
  let parsedURL
@@ -101,6 +103,8 @@ class Request {
101
103
  // 5. Set fallbackMode to "cors".
102
104
  fallbackMode = 'cors'
103
105
  } else {
106
+ this[kDispatcher] = input[kDispatcher]
107
+
104
108
  // 6. Otherwise:
105
109
 
106
110
  // 7. Assert: input is a Request object.
@@ -979,7 +983,11 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([
979
983
  key: 'duplex',
980
984
  converter: webidl.converters.DOMString,
981
985
  allowedValues: requestDuplex
986
+ },
987
+ {
988
+ key: 'dispatcher', // undici specific option
989
+ converter: webidl.converters.any
982
990
  }
983
991
  ])
984
992
 
985
- module.exports = { Request, makeRequest, fromInnerRequest }
993
+ module.exports = { Request, makeRequest, fromInnerRequest, cloneRequest }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { Headers, HeadersList, fill } = require('./headers')
4
4
  const { extractBody, cloneBody, mixinBody } = require('./body')
5
- const util = require('../core/util')
5
+ const util = require('../../core/util')
6
6
  const { kEnumerableProperty } = util
7
7
  const {
8
8
  isValidReasonPhrase,
@@ -21,8 +21,8 @@ const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
21
21
  const { webidl } = require('./webidl')
22
22
  const { FormData } = require('./formdata')
23
23
  const { getGlobalOrigin } = require('./global')
24
- const { URLSerializer } = require('./dataURL')
25
- const { kHeadersList, kConstruct } = require('../core/symbols')
24
+ const { URLSerializer } = require('./data-url')
25
+ const { kHeadersList, kConstruct } = require('../../core/symbols')
26
26
  const assert = require('node:assert')
27
27
  const { types } = require('node:util')
28
28
 
@@ -6,5 +6,6 @@ module.exports = {
6
6
  kSignal: Symbol('signal'),
7
7
  kState: Symbol('state'),
8
8
  kGuard: Symbol('guard'),
9
- kRealm: Symbol('realm')
9
+ kRealm: Symbol('realm'),
10
+ kDispatcher: Symbol('dispatcher')
10
11
  }
@@ -4,18 +4,19 @@ const { Transform } = require('node:stream')
4
4
  const zlib = require('node:zlib')
5
5
  const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = require('./constants')
6
6
  const { getGlobalOrigin } = require('./global')
7
- const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./dataURL')
7
+ const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url')
8
8
  const { performance } = require('node:perf_hooks')
9
- const { isBlobLike, toUSVString, ReadableStreamFrom, isValidHTTPToken } = require('../core/util')
9
+ const { isBlobLike, ReadableStreamFrom, isValidHTTPToken } = require('../../core/util')
10
10
  const assert = require('node:assert')
11
- const { isUint8Array } = require('util/types')
11
+ const { isUint8Array } = require('node:util/types')
12
+ const { webidl } = require('./webidl')
12
13
 
13
14
  // https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable
14
- /** @type {import('crypto')|undefined} */
15
+ /** @type {import('crypto')} */
15
16
  let crypto
16
-
17
17
  try {
18
18
  crypto = require('node:crypto')
19
+ /* c8 ignore next 3 */
19
20
  } catch {
20
21
 
21
22
  }
@@ -110,9 +111,7 @@ function isValidReasonPhrase (statusText) {
110
111
  * @see https://fetch.spec.whatwg.org/#header-name
111
112
  * @param {string} potentialValue
112
113
  */
113
- function isValidHeaderName (potentialValue) {
114
- return isValidHTTPToken(potentialValue)
115
- }
114
+ const isValidHeaderName = isValidHTTPToken
116
115
 
117
116
  /**
118
117
  * @see https://fetch.spec.whatwg.org/#header-value
@@ -438,6 +437,8 @@ function stripURLForReferrer (url, originOnly) {
438
437
  // 1. Assert: url is a URL.
439
438
  assert(url instanceof URL)
440
439
 
440
+ url = new URL(url)
441
+
441
442
  // 2. If url’s scheme is a local scheme, then return no referrer.
442
443
  if (url.protocol === 'file:' || url.protocol === 'about:' || url.protocol === 'blank:') {
443
444
  return 'no-referrer'
@@ -739,35 +740,40 @@ const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbo
739
740
 
740
741
  /**
741
742
  * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
742
- * @param {() => unknown} iterator
743
743
  * @param {string} name name of the instance
744
- * @param {'key'|'value'|'key+value'} kind
744
+ * @param {symbol} kInternalIterator
745
745
  * @param {string | number} [keyIndex]
746
746
  * @param {string | number} [valueIndex]
747
747
  */
748
- function makeIterator (iterator, name, kind, keyIndex = 0, valueIndex = 1) {
749
- const object = {
750
- index: 0,
751
- kind,
752
- target: iterator
753
- }
754
- // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
755
- const iteratorObject = Object.create(esIteratorPrototype)
748
+ function createIterator (name, kInternalIterator, keyIndex = 0, valueIndex = 1) {
749
+ class FastIterableIterator {
750
+ /** @type {any} */
751
+ #target
752
+ /** @type {'key' | 'value' | 'key+value'} */
753
+ #kind
754
+ /** @type {number} */
755
+ #index
756
+
757
+ /**
758
+ * @see https://webidl.spec.whatwg.org/#dfn-default-iterator-object
759
+ * @param {unknown} target
760
+ * @param {'key' | 'value' | 'key+value'} kind
761
+ */
762
+ constructor (target, kind) {
763
+ this.#target = target
764
+ this.#kind = kind
765
+ this.#index = 0
766
+ }
756
767
 
757
- Object.defineProperty(iteratorObject, 'next', {
758
- value: function next () {
768
+ next () {
759
769
  // 1. Let interface be the interface for which the iterator prototype object exists.
760
-
761
770
  // 2. Let thisValue be the this value.
762
-
763
771
  // 3. Let object be ? ToObject(thisValue).
764
-
765
772
  // 4. If object is a platform object, then perform a security
766
773
  // check, passing:
767
-
768
774
  // 5. If object is not a default iterator object for interface,
769
775
  // then throw a TypeError.
770
- if (Object.getPrototypeOf(this) !== iteratorObject) {
776
+ if (typeof this !== 'object' || this === null || !(#target in this)) {
771
777
  throw new TypeError(
772
778
  `'next' called on an object that does not implement interface ${name} Iterator.`
773
779
  )
@@ -776,8 +782,8 @@ function makeIterator (iterator, name, kind, keyIndex = 0, valueIndex = 1) {
776
782
  // 6. Let index be object’s index.
777
783
  // 7. Let kind be object’s kind.
778
784
  // 8. Let values be object’s target's value pairs to iterate over.
779
- const { index, kind, target } = object
780
- const values = target()
785
+ const index = this.#index
786
+ const values = this.#target[kInternalIterator]
781
787
 
782
788
  // 9. Let len be the length of values.
783
789
  const len = values.length
@@ -785,17 +791,25 @@ function makeIterator (iterator, name, kind, keyIndex = 0, valueIndex = 1) {
785
791
  // 10. If index is greater than or equal to len, then return
786
792
  // CreateIterResultObject(undefined, true).
787
793
  if (index >= len) {
788
- return { value: undefined, done: true }
794
+ return {
795
+ value: undefined,
796
+ done: true
797
+ }
789
798
  }
799
+
790
800
  // 11. Let pair be the entry in values at index index.
791
801
  const { [keyIndex]: key, [valueIndex]: value } = values[index]
802
+
792
803
  // 12. Set object’s index to index + 1.
793
- object.index = index + 1
804
+ this.#index = index + 1
805
+
794
806
  // 13. Return the iterator result for pair and kind.
807
+
795
808
  // https://webidl.spec.whatwg.org/#iterator-result
809
+
796
810
  // 1. Let result be a value determined by the value of kind:
797
811
  let result
798
- switch (kind) {
812
+ switch (this.#kind) {
799
813
  case 'key':
800
814
  // 1. Let idlKey be pair’s key.
801
815
  // 2. Let key be the result of converting idlKey to an
@@ -824,29 +838,108 @@ function makeIterator (iterator, name, kind, keyIndex = 0, valueIndex = 1) {
824
838
  result = [key, value]
825
839
  break
826
840
  }
841
+
827
842
  // 2. Return CreateIterResultObject(result, false).
828
843
  return {
829
844
  value: result,
830
845
  done: false
831
846
  }
847
+ }
848
+ }
849
+
850
+ // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
851
+ // @ts-ignore
852
+ delete FastIterableIterator.prototype.constructor
853
+
854
+ Object.setPrototypeOf(FastIterableIterator.prototype, esIteratorPrototype)
855
+
856
+ Object.defineProperties(FastIterableIterator.prototype, {
857
+ [Symbol.toStringTag]: {
858
+ writable: false,
859
+ enumerable: false,
860
+ configurable: true,
861
+ value: `${name} Iterator`
832
862
  },
833
- writable: true,
834
- enumerable: true,
835
- configurable: true
863
+ next: { writable: true, enumerable: true, configurable: true }
836
864
  })
837
865
 
838
- // The class string of an iterator prototype object for a given interface is the
839
- // result of concatenating the identifier of the interface and the string " Iterator".
840
- Object.defineProperty(iteratorObject, Symbol.toStringTag, {
841
- value: `${name} Iterator`,
842
- writable: false,
843
- enumerable: false,
844
- configurable: true
845
- })
866
+ /**
867
+ * @param {unknown} target
868
+ * @param {'key' | 'value' | 'key+value'} kind
869
+ * @returns {IterableIterator<any>}
870
+ */
871
+ return function (target, kind) {
872
+ return new FastIterableIterator(target, kind)
873
+ }
874
+ }
875
+
876
+ /**
877
+ * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
878
+ * @param {string} name name of the instance
879
+ * @param {any} object class
880
+ * @param {symbol} kInternalIterator
881
+ * @param {string | number} [keyIndex]
882
+ * @param {string | number} [valueIndex]
883
+ */
884
+ function iteratorMixin (name, object, kInternalIterator, keyIndex = 0, valueIndex = 1) {
885
+ const makeIterator = createIterator(name, kInternalIterator, keyIndex, valueIndex)
886
+
887
+ const properties = {
888
+ keys: {
889
+ writable: true,
890
+ enumerable: true,
891
+ configurable: true,
892
+ value: function keys () {
893
+ webidl.brandCheck(this, object)
894
+ return makeIterator(this, 'key')
895
+ }
896
+ },
897
+ values: {
898
+ writable: true,
899
+ enumerable: true,
900
+ configurable: true,
901
+ value: function values () {
902
+ webidl.brandCheck(this, object)
903
+ return makeIterator(this, 'value')
904
+ }
905
+ },
906
+ entries: {
907
+ writable: true,
908
+ enumerable: true,
909
+ configurable: true,
910
+ value: function entries () {
911
+ webidl.brandCheck(this, object)
912
+ return makeIterator(this, 'key+value')
913
+ }
914
+ },
915
+ forEach: {
916
+ writable: true,
917
+ enumerable: true,
918
+ configurable: true,
919
+ value: function forEach (callbackfn, thisArg = globalThis) {
920
+ webidl.brandCheck(this, object)
921
+ webidl.argumentLengthCheck(arguments, 1, { header: `${name}.forEach` })
922
+ if (typeof callbackfn !== 'function') {
923
+ throw new TypeError(
924
+ `Failed to execute 'forEach' on '${name}': parameter 1 is not of type 'Function'.`
925
+ )
926
+ }
927
+ for (const { 0: key, 1: value } of makeIterator(this, 'key+value')) {
928
+ callbackfn.call(thisArg, value, key, this)
929
+ }
930
+ }
931
+ }
932
+ }
846
933
 
847
- // esIteratorPrototype needs to be the prototype of iteratorObject
848
- // which is the prototype of an empty object. Yes, it's confusing.
849
- return Object.create(iteratorObject)
934
+ return Object.defineProperties(object.prototype, {
935
+ ...properties,
936
+ [Symbol.iterator]: {
937
+ writable: true,
938
+ enumerable: false,
939
+ configurable: true,
940
+ value: properties.entries.value
941
+ }
942
+ })
850
943
  }
851
944
 
852
945
  /**
@@ -1072,7 +1165,7 @@ function simpleRangeHeaderValue (value, allowWhitespace) {
1072
1165
 
1073
1166
  // 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab
1074
1167
  // or space, from data given position.
1075
- // Note from Khafra: its the same fucking step again lol
1168
+ // Note from Khafra: its the same step as in #8 again lol
1076
1169
  if (allowWhitespace) {
1077
1170
  collectASequenceOfCodePoints(
1078
1171
  (char) => char === '\t' || char === ' ',
@@ -1340,7 +1433,6 @@ module.exports = {
1340
1433
  isCancelled,
1341
1434
  createDeferredPromise,
1342
1435
  ReadableStreamFrom,
1343
- toUSVString,
1344
1436
  tryUpgradeRequestToAPotentiallyTrustworthyURL,
1345
1437
  clampAndCoarsenConnectionTimingInfo,
1346
1438
  coarsenedSharedCurrentTime,
@@ -1365,7 +1457,8 @@ module.exports = {
1365
1457
  sameOrigin,
1366
1458
  normalizeMethod,
1367
1459
  serializeJavascriptValueToJSONString,
1368
- makeIterator,
1460
+ iteratorMixin,
1461
+ createIterator,
1369
1462
  isValidHeaderName,
1370
1463
  isValidHeaderValue,
1371
1464
  isErrorLike,
@@ -1383,5 +1476,6 @@ module.exports = {
1383
1476
  buildContentRange,
1384
1477
  parseMetadata,
1385
1478
  createInflate,
1386
- extractMimeType
1479
+ extractMimeType,
1480
+ getDecodeSplit
1387
1481
  }