undici 8.0.2 → 8.0.3

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.
@@ -533,7 +533,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.
533
533
 
534
534
  `body` contains the following additional extensions:
535
535
 
536
- - `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
536
+ - `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 131072.
537
537
 
538
538
  Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.
539
539
 
@@ -1031,7 +1031,7 @@ const client = new Client("http://service.example").compose(
1031
1031
  The `dump` interceptor enables you to dump the response body from a request upon a given limit.
1032
1032
 
1033
1033
  **Options**
1034
- - `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the request's body exceeds this value then the connection will be closed. Default: `1048576`.
1034
+ - `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the response's body exceeds this value then the connection will be closed. Default: `1048576`.
1035
1035
 
1036
1036
  > The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis.
1037
1037
 
@@ -0,0 +1,231 @@
1
+ # Migrating from Undici 7 to 8
2
+
3
+ This guide covers the changes you are most likely to hit when upgrading an
4
+ application or library from Undici v7 to v8.
5
+
6
+ ## Before you upgrade
7
+
8
+ - Make sure your runtime is Node.js `>= 22.19.0`.
9
+ - If you have custom dispatchers, interceptors, or handlers, review the
10
+ handler API changes before updating.
11
+ - If you rely on HTTP/1.1-only behavior, plan to set `allowH2: false`
12
+ explicitly.
13
+
14
+ ## 1. Update your Node.js version
15
+
16
+ Undici v8 requires Node.js `>= 22.19.0`.
17
+
18
+ If you are still on Node.js 20 or an older Node.js 22 release, upgrade Node.js
19
+ first:
20
+
21
+ ```bash
22
+ node -v
23
+ ```
24
+
25
+ If that command prints a version lower than `v22.19.0`, upgrade Node.js before
26
+ installing Undici v8.
27
+
28
+ ## 2. Migrate custom dispatcher handlers to the v2 API
29
+
30
+ Undici v8 uses the newer dispatcher handler API consistently.
31
+
32
+ If you implemented custom dispatchers, interceptors, or wrappers around
33
+ `dispatch()`, update legacy callbacks such as `onConnect`, `onHeaders`, and
34
+ `onComplete` to the newer callback names.
35
+
36
+ ### Old handler callbacks vs. v8 callbacks
37
+
38
+ | Undici 7 style | Undici 8 style |
39
+ |---|---|
40
+ | `onConnect(abort, context)` | `onRequestStart(controller, context)` |
41
+ | `onHeaders(statusCode, rawHeaders, resume, statusText)` | `onResponseStart(controller, statusCode, headers, statusText)` |
42
+ | `onData(chunk)` | `onResponseData(controller, chunk)` |
43
+ | `onComplete(trailers)` | `onResponseEnd(controller, trailers)` |
44
+ | `onError(err)` | `onResponseError(controller, err)` |
45
+ | `onUpgrade(statusCode, rawHeaders, socket)` | `onRequestUpgrade(controller, statusCode, headers, socket)` |
46
+
47
+ ### Example
48
+
49
+ Before:
50
+
51
+ ```js
52
+ client.dispatch(options, {
53
+ onConnect (abort) {
54
+ this.abort = abort
55
+ },
56
+ onHeaders (statusCode, headers, resume) {
57
+ this.resume = resume
58
+ return true
59
+ },
60
+ onData (chunk) {
61
+ chunks.push(chunk)
62
+ return true
63
+ },
64
+ onComplete (trailers) {
65
+ console.log(trailers)
66
+ },
67
+ onError (err) {
68
+ console.error(err)
69
+ }
70
+ })
71
+ ```
72
+
73
+ After:
74
+
75
+ ```js
76
+ client.dispatch(options, {
77
+ onRequestStart (controller) {
78
+ this.controller = controller
79
+ },
80
+ onResponseStart (controller, statusCode, headers, statusText) {
81
+ console.log(statusCode, statusText, headers)
82
+ },
83
+ onResponseData (controller, chunk) {
84
+ chunks.push(chunk)
85
+ },
86
+ onResponseEnd (controller, trailers) {
87
+ console.log(trailers)
88
+ },
89
+ onResponseError (controller, err) {
90
+ console.error(err)
91
+ }
92
+ })
93
+ ```
94
+
95
+ ### Pause, resume, and abort now go through the controller
96
+
97
+ In Undici v7, legacy handlers could return `false` or keep references to
98
+ `abort()` and `resume()` callbacks. In Undici v8, use the controller instead:
99
+
100
+ ```js
101
+ onRequestStart (controller) {
102
+ this.controller = controller
103
+ }
104
+
105
+ onResponseData (controller, chunk) {
106
+ controller.pause()
107
+ setImmediate(() => controller.resume())
108
+ }
109
+
110
+ onResponseError (controller, err) {
111
+ controller.abort(err)
112
+ }
113
+ ```
114
+
115
+ ### Raw headers and trailers moved to the controller
116
+
117
+ If you need the raw header arrays, read them from the controller:
118
+
119
+ - `controller.rawHeaders`
120
+ - `controller.rawTrailers`
121
+
122
+ ## 3. Update `onBodySent()` handlers
123
+
124
+ If you implemented `onBodySent()`, note that its signature changed.
125
+
126
+ Before, handlers received counters:
127
+
128
+ ```js
129
+ onBodySent (chunkSize, totalBytesSent) {}
130
+ ```
131
+
132
+ In Undici v8, handlers receive the actual chunk:
133
+
134
+ ```js
135
+ onBodySent (chunk) {}
136
+ ```
137
+
138
+ If you need a notification that the whole body has been sent, use
139
+ `onRequestSent()`:
140
+
141
+ ```js
142
+ onRequestSent () {
143
+ console.log('request body fully sent')
144
+ }
145
+ ```
146
+
147
+ ## 4. If you need HTTP/1.1 only, disable HTTP/2 explicitly
148
+
149
+ Undici v8 enables HTTP/2 by default when a TLS server negotiates it via ALPN.
150
+
151
+ If your application depends on HTTP/1.1-specific behavior, set `allowH2: false`
152
+ explicitly.
153
+
154
+ Before:
155
+
156
+ ```js
157
+ const client = new Client('https://example.com')
158
+ ```
159
+
160
+ After, to keep HTTP/1.1 only:
161
+
162
+ ```js
163
+ const client = new Client('https://example.com', {
164
+ allowH2: false
165
+ })
166
+ ```
167
+
168
+ The same applies when you configure an `Agent`:
169
+
170
+ ```js
171
+ const agent = new Agent({
172
+ allowH2: false
173
+ })
174
+ ```
175
+
176
+ ## 5. Use real `Blob` and `File` instances
177
+
178
+ Undici v8 no longer accepts fake Blob-like values that only imitate `Blob` or
179
+ `File` via properties such as `Symbol.toStringTag`.
180
+
181
+ If you were passing custom objects that looked like `Blob`s, replace them with
182
+ actual `Blob` or `File` instances:
183
+
184
+ ```js
185
+ const body = new Blob(['hello'])
186
+ ```
187
+
188
+ ## 6. Avoid depending on the internal global dispatcher symbol
189
+
190
+ `setGlobalDispatcher()` and `getGlobalDispatcher()` remain the public APIs and
191
+ should continue to be used.
192
+
193
+ Internally, Undici v8 stores its dispatcher under
194
+ `Symbol.for('undici.globalDispatcher.2')` and mirrors a v1-compatible wrapper
195
+ for legacy consumers such as Node.js built-in `fetch`.
196
+
197
+ If your code was reading or writing `Symbol.for('undici.globalDispatcher.1')`
198
+ directly, migrate to the public APIs instead:
199
+
200
+ ```js
201
+ import { setGlobalDispatcher, getGlobalDispatcher, Agent } from 'undici'
202
+
203
+ setGlobalDispatcher(new Agent())
204
+ const dispatcher = getGlobalDispatcher()
205
+ ```
206
+
207
+ If you must expose a dispatcher to legacy v1 handler consumers, wrap it with
208
+ `Dispatcher1Wrapper`:
209
+
210
+ ```js
211
+ import { Agent, Dispatcher1Wrapper } from 'undici'
212
+
213
+ const legacyCompatibleDispatcher = new Dispatcher1Wrapper(new Agent())
214
+ ```
215
+
216
+ ## 7. Verify the upgrade
217
+
218
+ After moving to Undici v8, it is worth checking these paths in your test suite:
219
+
220
+ - requests that use a custom `dispatcher`
221
+ - `setGlobalDispatcher()` behavior
222
+ - any custom interceptor or retry handler
223
+ - uploads that use `Blob`, `File`, or `FormData`
224
+ - integrations that depend on HTTP/1.1-only behavior
225
+
226
+ ## Related documentation
227
+
228
+ - [Dispatcher](/docs/api/Dispatcher.md)
229
+ - [Client](/docs/api/Client.md)
230
+ - [Global Installation](/docs/api/GlobalInstallation.md)
231
+ - [Undici Module vs. Node.js Built-in Fetch](/docs/best-practices/undici-vs-builtin-fetch.md)
package/index.js CHANGED
@@ -105,14 +105,14 @@ function makeDispatcher (fn) {
105
105
  url = util.parseURL(url)
106
106
  }
107
107
 
108
- const { agent, dispatcher = getGlobalDispatcher() } = opts
108
+ const { agent, dispatcher = getGlobalDispatcher(), ...restOpts } = opts
109
109
 
110
110
  if (agent) {
111
111
  throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')
112
112
  }
113
113
 
114
114
  return fn.call(dispatcher, {
115
- ...opts,
115
+ ...restOpts,
116
116
  origin: url.origin,
117
117
  path: url.search ? `${url.pathname}${url.search}` : url.pathname,
118
118
  method: opts.method || (opts.body ? 'PUT' : 'GET')
package/lib/core/util.js CHANGED
@@ -12,8 +12,6 @@ const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
12
12
  const { headerNameLowerCasedRecord } = require('./constants')
13
13
  const { tree } = require('./tree')
14
14
 
15
- const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(v => Number(v))
16
-
17
15
  class BodyAsyncIterable {
18
16
  constructor (body) {
19
17
  this[kBody] = body
@@ -323,7 +321,7 @@ function isIterable (obj) {
323
321
  */
324
322
  function hasSafeIterator (obj) {
325
323
  const prototype = Object.getPrototypeOf(obj)
326
- const ownIterator = Object.prototype.hasOwnProperty.call(obj, Symbol.iterator)
324
+ const ownIterator = Object.hasOwn(obj, Symbol.iterator)
327
325
  return ownIterator || (prototype != null && prototype !== Object.prototype && typeof obj[Symbol.iterator] === 'function')
328
326
  }
329
327
 
@@ -989,8 +987,6 @@ module.exports = {
989
987
  normalizedMethodRecords,
990
988
  isValidPort,
991
989
  isHttpOrHttpsPrefixed,
992
- nodeMajor,
993
- nodeMinor,
994
990
  safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
995
991
  wrapRequestBody,
996
992
  setupConnectTimeout,
@@ -57,9 +57,6 @@ class BalancedPool extends PoolBase {
57
57
  super()
58
58
 
59
59
  this[kOptions] = { ...util.deepClone(opts) }
60
- this[kOptions].interceptors = opts.interceptors
61
- ? { ...opts.interceptors }
62
- : undefined
63
60
  this[kIndex] = -1
64
61
  this[kCurrentWeight] = 0
65
62
 
@@ -138,6 +138,10 @@ class DispatcherBase extends Dispatcher {
138
138
  throw new InvalidArgumentError('opts must be an object.')
139
139
  }
140
140
 
141
+ if (opts.dispatcher) {
142
+ throw new InvalidArgumentError('opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly.')
143
+ }
144
+
141
145
  if (this[kDestroyed] || this[kOnDestroyed]) {
142
146
  throw new ClientDestroyedError()
143
147
  }
@@ -86,6 +86,12 @@ class Dispatcher1Wrapper extends Dispatcher {
86
86
  }
87
87
 
88
88
  dispatch (opts, handler) {
89
+ // Legacy (v1) consumers do not support HTTP/2, so force HTTP/1.1.
90
+ // See https://github.com/nodejs/undici/issues/4989
91
+ if (opts.allowH2 !== false) {
92
+ opts = { ...opts, allowH2: false }
93
+ }
94
+
89
95
  return this.#dispatcher.dispatch(opts, Dispatcher1Wrapper.wrapHandler(handler))
90
96
  }
91
97
 
@@ -15,7 +15,7 @@ class H2CClient extends Client {
15
15
  )
16
16
  }
17
17
 
18
- const { connect, maxConcurrentStreams, pipelining, ...opts } =
18
+ const { maxConcurrentStreams, pipelining, ...opts } =
19
19
  clientOpts ?? {}
20
20
  let defaultMaxConcurrentStreams = 100
21
21
  let defaultPipelining = 100
@@ -68,9 +68,6 @@ class Pool extends PoolBase {
68
68
  this[kConnections] = connections || null
69
69
  this[kUrl] = util.parseOrigin(origin)
70
70
  this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }
71
- this[kOptions].interceptors = options.interceptors
72
- ? { ...options.interceptors }
73
- : undefined
74
71
  this[kFactory] = factory
75
72
 
76
73
  this.on('connect', (origin, targets) => {
@@ -104,7 +104,7 @@ class ProxyAgent extends DispatcherBase {
104
104
  throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
105
105
  }
106
106
 
107
- const { proxyTunnel = true } = opts
107
+ const { proxyTunnel = true, connectTimeout } = opts
108
108
 
109
109
  super()
110
110
 
@@ -128,9 +128,9 @@ class ProxyAgent extends DispatcherBase {
128
128
  this[kProxyHeaders]['proxy-authorization'] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString('base64')}`
129
129
  }
130
130
 
131
- const connect = buildConnector({ ...opts.proxyTls })
132
- this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
133
- this[kConnectEndpointHTTP1] = buildConnector({ ...opts.requestTls, allowH2: false })
131
+ const connect = buildConnector({ timeout: connectTimeout, ...opts.proxyTls })
132
+ this[kConnectEndpoint] = buildConnector({ timeout: connectTimeout, ...opts.requestTls })
133
+ this[kConnectEndpointHTTP1] = buildConnector({ timeout: connectTimeout, ...opts.requestTls, allowH2: false })
134
134
 
135
135
  const agentFactory = opts.factory || defaultAgentFactory
136
136
  const factory = (origin, options) => {
@@ -69,9 +69,6 @@ class RoundRobinPool extends PoolBase {
69
69
  this[kConnections] = connections || null
70
70
  this[kUrl] = util.parseOrigin(origin)
71
71
  this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }
72
- this[kOptions].interceptors = options.interceptors
73
- ? { ...options.interceptors }
74
- : undefined
75
72
  this[kFactory] = factory
76
73
  this[kIndex] = -1
77
74
 
@@ -79,26 +79,28 @@ class Socks5ProxyAgent extends DispatcherBase {
79
79
  debug('creating SOCKS5 connection to', proxyHost, proxyPort)
80
80
 
81
81
  // Connect to the SOCKS5 proxy
82
- const socket = await new Promise((resolve, reject) => {
83
- const onConnect = () => {
84
- socket.removeListener('error', onError)
85
- resolve(socket)
86
- }
82
+ const socketReady = Promise.withResolvers()
87
83
 
88
- const onError = (err) => {
89
- socket.removeListener('connect', onConnect)
90
- reject(err)
91
- }
84
+ const onSocketConnect = () => {
85
+ socket.removeListener('error', onSocketError)
86
+ socketReady.resolve(socket)
87
+ }
92
88
 
93
- const socket = net.connect({
94
- host: proxyHost,
95
- port: proxyPort
96
- })
89
+ const onSocketError = (err) => {
90
+ socket.removeListener('connect', onSocketConnect)
91
+ socketReady.reject(err)
92
+ }
97
93
 
98
- socket.once('connect', onConnect)
99
- socket.once('error', onError)
94
+ const socket = net.connect({
95
+ host: proxyHost,
96
+ port: proxyPort
100
97
  })
101
98
 
99
+ socket.once('connect', onSocketConnect)
100
+ socket.once('error', onSocketError)
101
+
102
+ await socketReady.promise
103
+
102
104
  // Create SOCKS5 client
103
105
  const socks5Client = new Socks5Client(socket, this[kProxyAuth])
104
106
 
@@ -112,58 +114,62 @@ class Socks5ProxyAgent extends DispatcherBase {
112
114
  await socks5Client.handshake()
113
115
 
114
116
  // Wait for authentication (if required)
115
- await new Promise((resolve, reject) => {
116
- const timeout = setTimeout(() => {
117
- reject(new Error('SOCKS5 authentication timeout'))
118
- }, 5000)
119
-
120
- const onAuthenticated = () => {
121
- clearTimeout(timeout)
122
- socks5Client.removeListener('error', onError)
123
- resolve()
124
- }
117
+ const authenticationReady = Promise.withResolvers()
125
118
 
126
- const onError = (err) => {
127
- clearTimeout(timeout)
128
- socks5Client.removeListener('authenticated', onAuthenticated)
129
- reject(err)
130
- }
119
+ const authenticationTimeout = setTimeout(() => {
120
+ authenticationReady.reject(new Error('SOCKS5 authentication timeout'))
121
+ }, 5000)
131
122
 
132
- // Check if already authenticated (for NO_AUTH method)
133
- if (socks5Client.state === 'authenticated') {
134
- clearTimeout(timeout)
135
- resolve()
136
- } else {
137
- socks5Client.once('authenticated', onAuthenticated)
138
- socks5Client.once('error', onError)
139
- }
140
- })
123
+ const onAuthenticated = () => {
124
+ clearTimeout(authenticationTimeout)
125
+ socks5Client.removeListener('error', onAuthenticationError)
126
+ authenticationReady.resolve()
127
+ }
128
+
129
+ const onAuthenticationError = (err) => {
130
+ clearTimeout(authenticationTimeout)
131
+ socks5Client.removeListener('authenticated', onAuthenticated)
132
+ authenticationReady.reject(err)
133
+ }
134
+
135
+ // Check if already authenticated (for NO_AUTH method)
136
+ if (socks5Client.state === 'authenticated') {
137
+ clearTimeout(authenticationTimeout)
138
+ authenticationReady.resolve()
139
+ } else {
140
+ socks5Client.once('authenticated', onAuthenticated)
141
+ socks5Client.once('error', onAuthenticationError)
142
+ }
143
+
144
+ await authenticationReady.promise
141
145
 
142
146
  // Send CONNECT command
143
147
  await socks5Client.connect(targetHost, targetPort)
144
148
 
145
149
  // Wait for connection
146
- await new Promise((resolve, reject) => {
147
- const timeout = setTimeout(() => {
148
- reject(new Error('SOCKS5 connection timeout'))
149
- }, 5000)
150
-
151
- const onConnected = (info) => {
152
- debug('SOCKS5 tunnel established to', targetHost, targetPort, 'via', info)
153
- clearTimeout(timeout)
154
- socks5Client.removeListener('error', onError)
155
- resolve()
156
- }
150
+ const connectionReady = Promise.withResolvers()
157
151
 
158
- const onError = (err) => {
159
- clearTimeout(timeout)
160
- socks5Client.removeListener('connected', onConnected)
161
- reject(err)
162
- }
152
+ const connectionTimeout = setTimeout(() => {
153
+ connectionReady.reject(new Error('SOCKS5 connection timeout'))
154
+ }, 5000)
163
155
 
164
- socks5Client.once('connected', onConnected)
165
- socks5Client.once('error', onError)
166
- })
156
+ const onConnected = (info) => {
157
+ debug('SOCKS5 tunnel established to', targetHost, targetPort, 'via', info)
158
+ clearTimeout(connectionTimeout)
159
+ socks5Client.removeListener('error', onConnectionError)
160
+ connectionReady.resolve()
161
+ }
162
+
163
+ const onConnectionError = (err) => {
164
+ clearTimeout(connectionTimeout)
165
+ socks5Client.removeListener('connected', onConnected)
166
+ connectionReady.reject(err)
167
+ }
168
+
169
+ socks5Client.once('connected', onConnected)
170
+ socks5Client.once('error', onConnectionError)
171
+
172
+ await connectionReady.promise
167
173
 
168
174
  return socket
169
175
  }
@@ -206,10 +212,10 @@ class Socks5ProxyAgent extends DispatcherBase {
206
212
  ...connectOpts.tls || {}
207
213
  })
208
214
 
209
- await new Promise((resolve, reject) => {
210
- finalSocket.once('secureConnect', resolve)
211
- finalSocket.once('error', reject)
212
- })
215
+ const tlsReady = Promise.withResolvers()
216
+ finalSocket.once('secureConnect', tlsReady.resolve)
217
+ finalSocket.once('error', tlsReady.reject)
218
+ await tlsReady.promise
213
219
  }
214
220
 
215
221
  callback(null, finalSocket)
@@ -1,30 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const util = require('../core/util')
4
- const { kBodyUsed } = require('../core/symbols')
5
4
  const assert = require('node:assert')
6
5
  const { InvalidArgumentError } = require('../core/errors')
7
- const EE = require('node:events')
8
6
 
9
7
  const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
10
8
 
11
- const kBody = Symbol('body')
12
-
13
9
  const noop = () => {}
14
10
 
15
- class BodyAsyncIterable {
16
- constructor (body) {
17
- this[kBody] = body
18
- this[kBodyUsed] = false
19
- }
20
-
21
- async * [Symbol.asyncIterator] () {
22
- assert(!this[kBodyUsed], 'disturbed')
23
- this[kBodyUsed] = true
24
- yield * this[kBody]
25
- }
26
- }
27
-
28
11
  class RedirectHandler {
29
12
  static buildDispatch (dispatcher, maxRedirections) {
30
13
  if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
@@ -44,43 +27,10 @@ class RedirectHandler {
44
27
  this.location = null
45
28
  const { maxRedirections: _, ...cleanOpts } = opts
46
29
  this.opts = cleanOpts // opts must be a copy, exclude maxRedirections
30
+ this.opts.body = util.wrapRequestBody(this.opts.body)
47
31
  this.maxRedirections = maxRedirections
48
32
  this.handler = handler
49
33
  this.history = []
50
-
51
- if (util.isStream(this.opts.body)) {
52
- // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
53
- // so that it can be dispatched again?
54
- // TODO (fix): Do we need 100-expect support to provide a way to do this properly?
55
- if (util.bodyLength(this.opts.body) === 0) {
56
- this.opts.body
57
- .on('data', function () {
58
- assert(false)
59
- })
60
- }
61
-
62
- if (typeof this.opts.body.readableDidRead !== 'boolean') {
63
- this.opts.body[kBodyUsed] = false
64
- EE.prototype.on.call(this.opts.body, 'data', function () {
65
- this[kBodyUsed] = true
66
- })
67
- }
68
- } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
69
- // TODO (fix): We can't access ReadableStream internal state
70
- // to determine whether or not it has been disturbed. This is just
71
- // a workaround.
72
- this.opts.body = new BodyAsyncIterable(this.opts.body)
73
- } else if (
74
- this.opts.body &&
75
- typeof this.opts.body !== 'string' &&
76
- !ArrayBuffer.isView(this.opts.body) &&
77
- util.isIterable(this.opts.body) &&
78
- !util.isFormDataLike(this.opts.body)
79
- ) {
80
- // TODO: Should we allow re-using iterable if !this.opts.idempotent
81
- // or through some other flag?
82
- this.opts.body = new BodyAsyncIterable(this.opts.body)
83
- }
84
34
  }
85
35
 
86
36
  onRequestStart (controller, context) {
@@ -3,7 +3,6 @@
3
3
  const { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = require('node:zlib')
4
4
  const { pipeline } = require('node:stream')
5
5
  const DecoratorHandler = require('../handler/decorator-handler')
6
- const { runtimeFeatures } = require('../util/runtime-features')
7
6
 
8
7
  /** @typedef {import('node:stream').Transform} Transform */
9
8
  /** @typedef {import('node:stream').Transform} Controller */
@@ -17,7 +16,7 @@ const supportedEncodings = {
17
16
  deflate: createInflate,
18
17
  compress: createInflate,
19
18
  'x-compress': createInflate,
20
- ...(runtimeFeatures.has('zstd') ? { zstd: createZstdDecompress } : {})
19
+ zstd: createZstdDecompress
21
20
  }
22
21
 
23
22
  const defaultSkipStatusCodes = /** @type {const} */ ([204, 304])
@@ -7,7 +7,7 @@ const maxInt = Math.pow(2, 31) - 1
7
7
 
8
8
  function hasSafeIterator (headers) {
9
9
  const prototype = Object.getPrototypeOf(headers)
10
- const ownIterator = Object.prototype.hasOwnProperty.call(headers, Symbol.iterator)
10
+ const ownIterator = Object.hasOwn(headers, Symbol.iterator)
11
11
  return ownIterator || (prototype != null && prototype !== Object.prototype && typeof headers[Symbol.iterator] === 'function')
12
12
  }
13
13
 
package/lib/util/cache.js CHANGED
@@ -374,9 +374,11 @@ function assertCacheMethods (methods, name = 'CacheMethods') {
374
374
  * @returns {string}
375
375
  */
376
376
  function makeDeduplicationKey (cacheKey, excludeHeaders) {
377
- // Create a deterministic string key from the cache key
378
- // Include origin, method, path, and sorted headers
379
- let key = `${cacheKey.origin}:${cacheKey.method}:${cacheKey.path}`
377
+ // Use JSON.stringify to produce a collision-resistant key.
378
+ // Previous format used `:` and `=` delimiters without escaping, which
379
+ // allowed different header sets to produce identical keys (e.g.
380
+ // {a:"x:b=y"} vs {a:"x", b:"y"}). See: https://github.com/nodejs/undici/issues/5012
381
+ const headers = {}
380
382
 
381
383
  if (cacheKey.headers) {
382
384
  const sortedHeaders = Object.keys(cacheKey.headers).sort()
@@ -385,12 +387,11 @@ function makeDeduplicationKey (cacheKey, excludeHeaders) {
385
387
  if (excludeHeaders?.has(header.toLowerCase())) {
386
388
  continue
387
389
  }
388
- const value = cacheKey.headers[header]
389
- key += `:${header}=${Array.isArray(value) ? value.join(',') : value}`
390
+ headers[header] = cacheKey.headers[header]
390
391
  }
391
392
  }
392
393
 
393
- return key
394
+ return JSON.stringify([cacheKey.origin, cacheKey.method, cacheKey.path, headers])
394
395
  }
395
396
 
396
397
  module.exports = {
@@ -6,9 +6,7 @@
6
6
  const lazyLoaders = {
7
7
  __proto__: null,
8
8
  'node:crypto': () => require('node:crypto'),
9
- 'node:sqlite': () => require('node:sqlite'),
10
- 'node:worker_threads': () => require('node:worker_threads'),
11
- 'node:zlib': () => require('node:zlib')
9
+ 'node:sqlite': () => require('node:sqlite')
12
10
  }
13
11
 
14
12
  /**
@@ -27,35 +25,9 @@ function detectRuntimeFeatureByNodeModule (moduleName) {
27
25
  }
28
26
  }
29
27
 
30
- /**
31
- * @param {NodeModuleName} moduleName
32
- * @param {string} property
33
- * @returns {boolean}
34
- */
35
- function detectRuntimeFeatureByExportedProperty (moduleName, property) {
36
- const module = lazyLoaders[moduleName]()
37
- return typeof module[property] !== 'undefined'
38
- }
39
-
40
- const runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd'])
41
-
42
- /** @type {Record<RuntimeFeatureByExportedProperty, [NodeModuleName, string]>} */
43
- const exportedPropertyLookup = {
44
- markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'],
45
- zstd: ['node:zlib', 'createZstdDecompress']
46
- }
47
-
48
- /** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */
49
-
50
28
  const runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite'])
51
29
  /** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */
52
-
53
- const features = /** @type {const} */ ([
54
- ...runtimeFeaturesAsNodeModule,
55
- ...runtimeFeaturesByExportedProperty
56
- ])
57
-
58
- /** @typedef {typeof features[number]} Feature */
30
+ /** @typedef {RuntimeFeatureByNodeModule} Feature */
59
31
 
60
32
  /**
61
33
  * @param {Feature} feature
@@ -64,9 +36,6 @@ const features = /** @type {const} */ ([
64
36
  function detectRuntimeFeature (feature) {
65
37
  if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) {
66
38
  return detectRuntimeFeatureByNodeModule(`node:${feature}`)
67
- } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) {
68
- const [moduleName, property] = exportedPropertyLookup[feature]
69
- return detectRuntimeFeatureByExportedProperty(moduleName, property)
70
39
  }
71
40
  throw new TypeError(`unknown feature: ${feature}`)
72
41
  }
@@ -101,7 +70,7 @@ class RuntimeFeatures {
101
70
  * @param {boolean} value
102
71
  */
103
72
  set (feature, value) {
104
- if (features.includes(feature) === false) {
73
+ if (runtimeFeaturesAsNodeModule.includes(feature) === false) {
105
74
  throw new TypeError(`unknown feature: ${feature}`)
106
75
  }
107
76
  this.#map.set(feature, value)
@@ -10,8 +10,6 @@ const { cloneResponse, fromInnerResponse, getResponseState } = require('../fetch
10
10
  const { Request, fromInnerRequest, getRequestState } = require('../fetch/request')
11
11
  const { fetching } = require('../fetch/index')
12
12
  const { urlIsHttpHttpsScheme, readAllBytes } = require('../fetch/util')
13
- const { createDeferredPromise } = require('../../util/promise')
14
-
15
13
  /**
16
14
  * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation
17
15
  * @typedef {Object} CacheBatchOperation
@@ -153,7 +151,7 @@ class Cache {
153
151
  requestList.push(r)
154
152
 
155
153
  // 5.6
156
- const responsePromise = createDeferredPromise()
154
+ const responsePromise = Promise.withResolvers()
157
155
 
158
156
  // 5.7
159
157
  fetchControllers.push(fetching({
@@ -231,7 +229,7 @@ class Cache {
231
229
  }
232
230
 
233
231
  // 7.5
234
- const cacheJobPromise = createDeferredPromise()
232
+ const cacheJobPromise = Promise.withResolvers()
235
233
 
236
234
  // 7.6.1
237
235
  let errorData = null
@@ -325,7 +323,7 @@ class Cache {
325
323
  const clonedResponse = cloneResponse(innerResponse)
326
324
 
327
325
  // 10.
328
- const bodyReadPromise = createDeferredPromise()
326
+ const bodyReadPromise = Promise.withResolvers()
329
327
 
330
328
  // 11.
331
329
  if (innerResponse.body != null) {
@@ -364,7 +362,7 @@ class Cache {
364
362
  }
365
363
 
366
364
  // 19.1
367
- const cacheJobPromise = createDeferredPromise()
365
+ const cacheJobPromise = Promise.withResolvers()
368
366
 
369
367
  // 19.2.1
370
368
  let errorData = null
@@ -427,7 +425,7 @@ class Cache {
427
425
 
428
426
  operations.push(operation)
429
427
 
430
- const cacheJobPromise = createDeferredPromise()
428
+ const cacheJobPromise = Promise.withResolvers()
431
429
 
432
430
  let errorData = null
433
431
  let requestResponses
@@ -483,7 +481,7 @@ class Cache {
483
481
  }
484
482
 
485
483
  // 4.
486
- const promise = createDeferredPromise()
484
+ const promise = Promise.withResolvers()
487
485
 
488
486
  // 5.
489
487
  // 5.1
@@ -14,7 +14,6 @@ const { isErrored, isDisturbed } = require('node:stream')
14
14
  const { isUint8Array } = require('node:util/types')
15
15
  const { serializeAMimeType } = require('./data-url')
16
16
  const { multipartFormDataParser } = require('./formdata-parser')
17
- const { createDeferredPromise } = require('../../util/promise')
18
17
  const { parseJSONFromBytes } = require('../infra')
19
18
  const { utf8DecodeBytes } = require('../../encoding')
20
19
  const { runtimeFeatures } = require('../../util/runtime-features.js')
@@ -431,7 +430,7 @@ function consumeBody (object, convertBytesToJSValue, instance, getInternalState)
431
430
  }
432
431
 
433
432
  // 2. Let promise be a new promise.
434
- const promise = createDeferredPromise()
433
+ const promise = Promise.withResolvers()
435
434
 
436
435
  // 3. Let errorSteps given error be to reject promise with error.
437
436
  const errorSteps = promise.reject
@@ -64,12 +64,7 @@ const { getGlobalDispatcher } = require('../../global')
64
64
  const { webidl } = require('../webidl')
65
65
  const { STATUS_CODES } = require('node:http')
66
66
  const { bytesMatch } = require('../subresource-integrity/subresource-integrity')
67
- const { createDeferredPromise } = require('../../util/promise')
68
67
  const { isomorphicEncode } = require('../infra')
69
- const { runtimeFeatures } = require('../../util/runtime-features')
70
-
71
- // Node.js v23.8.0+ and v22.15.0+ supports Zstandard
72
- const hasZstd = runtimeFeatures.has('zstd')
73
68
 
74
69
  const GET_OR_HEAD = ['GET', 'HEAD']
75
70
 
@@ -136,7 +131,7 @@ function fetch (input, init = undefined) {
136
131
  webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch')
137
132
 
138
133
  // 1. Let p be a new promise.
139
- let p = createDeferredPromise()
134
+ let p = Promise.withResolvers()
140
135
 
141
136
  // 2. Let requestObject be the result of invoking the initial value of
142
137
  // Request as constructor with input and init as arguments. If this throws
@@ -1644,12 +1639,25 @@ async function httpNetworkOrCacheFetch (
1644
1639
  // 14. If response’s status is 401, httpRequest’s response tainting is not "cors",
1645
1640
  // includeCredentials is true, and request’s traversable for user prompts is
1646
1641
  // a traversable navigable:
1647
- if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) {
1642
+ //
1643
+ // In Node.js there is no traversable navigable to prompt the user, but we
1644
+ // still need to handle URL-embedded credentials so authentication retries
1645
+ // for WebSocket handshakes continue to work.
1646
+ if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && (
1647
+ request.useURLCredentials !== undefined ||
1648
+ isTraversableNavigable(request.traversableForUserPrompts)
1649
+ )) {
1648
1650
  // 2. If request’s body is non-null, then:
1649
1651
  if (request.body != null) {
1650
1652
  // 1. If request’s body’s source is null, then return a network error.
1651
1653
  if (request.body.source == null) {
1652
- return makeNetworkError('expected non-null body source')
1654
+ // Note: In Node.js, this code path should not be reached because
1655
+ // isTraversableNavigable() returns false for non-navigable contexts.
1656
+ // However, we handle it gracefully by returning the response instead of
1657
+ // a network error, as we won't actually retry the request.
1658
+ // This aligns with the Fetch spec discussion in whatwg/fetch#1132,
1659
+ // which allows implementations flexibility when credentials can't be obtained.
1660
+ return response
1653
1661
  }
1654
1662
 
1655
1663
  // 2. Set request’s body to the body of the result of safely extracting
@@ -2251,7 +2259,7 @@ async function httpNetworkFetch (
2251
2259
  flush: zlib.constants.BROTLI_OPERATION_FLUSH,
2252
2260
  finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
2253
2261
  }))
2254
- } else if (coding === 'zstd' && hasZstd) {
2262
+ } else if (coding === 'zstd') {
2255
2263
  decoders.push(zlib.createZstdDecompress({
2256
2264
  flush: zlib.constants.ZSTD_e_continue,
2257
2265
  finishFlush: zlib.constants.ZSTD_e_end
@@ -1447,8 +1447,10 @@ function includesCredentials (url) {
1447
1447
  * @param {object|string} navigable
1448
1448
  */
1449
1449
  function isTraversableNavigable (navigable) {
1450
- // TODO
1451
- return true
1450
+ // Returns true only if we have an actual traversable navigable object
1451
+ // that can prompt the user for credentials. In Node.js, this will always
1452
+ // be false since there's no Window object or navigable.
1453
+ return navigable != null && navigable !== 'client' && navigable !== 'no-traversable'
1452
1454
  }
1453
1455
 
1454
1456
  class EnvironmentSettingsObjectBase {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const assert = require('node:assert')
4
4
  const { types, inspect } = require('node:util')
5
- const { runtimeFeatures } = require('../../util/runtime-features')
5
+ const { markAsUncloneable } = require('node:worker_threads')
6
6
 
7
7
  const UNDEFINED = 1
8
8
  const BOOLEAN = 2
@@ -158,9 +158,7 @@ webidl.util.TypeValueToString = function (o) {
158
158
  }
159
159
  }
160
160
 
161
- webidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable')
162
- ? require('node:worker_threads').markAsUncloneable
163
- : () => {}
161
+ webidl.util.markAsUncloneable = markAsUncloneable
164
162
 
165
163
  // https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
166
164
  webidl.util.ConvertToInt = function (V, bitLength, signedness, flags) {
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { createDeferredPromise } = require('../../../util/promise')
4
3
  const { environmentSettingsObject } = require('../../fetch/util')
5
4
  const { states, opcodes, sentCloseFrameState } = require('../constants')
6
5
  const { webidl } = require('../../webidl')
@@ -21,11 +20,11 @@ class WebSocketStream {
21
20
  #url
22
21
 
23
22
  // Each WebSocketStream object has an associated opened promise , which is a promise.
24
- /** @type {import('../../../util/promise').DeferredPromise} */
23
+ /** @type {ReturnType<typeof Promise.withResolvers>} */
25
24
  #openedPromise
26
25
 
27
26
  // Each WebSocketStream object has an associated closed promise , which is a promise.
28
- /** @type {import('../../../util/promise').DeferredPromise} */
27
+ /** @type {ReturnType<typeof Promise.withResolvers>} */
29
28
  #closedPromise
30
29
 
31
30
  // Each WebSocketStream object has an associated readable stream , which is a ReadableStream .
@@ -113,8 +112,8 @@ class WebSocketStream {
113
112
  this.#url = urlRecord.toString()
114
113
 
115
114
  // 6. Set this 's opened promise and closed promise to new promises.
116
- this.#openedPromise = createDeferredPromise()
117
- this.#closedPromise = createDeferredPromise()
115
+ this.#openedPromise = Promise.withResolvers()
116
+ this.#closedPromise = Promise.withResolvers()
118
117
 
119
118
  // 7. Apply backpressure to the WebSocket.
120
119
  // TODO
@@ -202,7 +201,7 @@ class WebSocketStream {
202
201
  chunk = webidl.converters.WebSocketStreamWrite(chunk)
203
202
 
204
203
  // 1. Let promise be a new promise created in stream ’s relevant realm .
205
- const promise = createDeferredPromise()
204
+ const promise = Promise.withResolvers()
206
205
 
207
206
  // 2. Let data be null.
208
207
  let data = null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "8.0.2",
3
+ "version": "8.0.3",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -119,7 +119,7 @@
119
119
  "c8": "^10.0.0",
120
120
  "cross-env": "^10.0.0",
121
121
  "dns-packet": "^5.4.0",
122
- "esbuild": "^0.27.3",
122
+ "esbuild": "^0.28.0",
123
123
  "eslint": "^9.9.0",
124
124
  "fast-check": "^4.1.1",
125
125
  "husky": "^9.0.7",
@@ -127,7 +127,7 @@
127
127
  "jsondiffpatch": "^0.7.3",
128
128
  "neostandard": "^0.12.0",
129
129
  "node-forge": "^1.3.1",
130
- "proxy": "^2.1.1",
130
+ "proxy": "^4.0.0",
131
131
  "tsd": "^0.33.0",
132
132
  "typescript": "^6.0.2",
133
133
  "ws": "^8.11.0"
package/types/agent.d.ts CHANGED
@@ -22,8 +22,6 @@ declare namespace Agent {
22
22
  export interface Options extends Pool.Options {
23
23
  /** Default: `(origin, opts) => new Pool(origin, opts)`. */
24
24
  factory?(origin: string | URL, opts: Object): Dispatcher;
25
-
26
- interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options['interceptors']
27
25
  maxOrigins?: number
28
26
  }
29
27
 
package/types/client.d.ts CHANGED
@@ -30,12 +30,7 @@ export class Client extends Dispatcher {
30
30
  }
31
31
 
32
32
  export declare namespace Client {
33
- export interface OptionsInterceptors {
34
- Client: readonly Dispatcher.DispatchInterceptor[];
35
- }
36
33
  export interface Options {
37
- /** TODO */
38
- interceptors?: OptionsInterceptors;
39
34
  /** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */
40
35
  maxHeaderSize?: number;
41
36
  /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
@@ -44,7 +39,7 @@ export declare namespace Client {
44
39
  socketTimeout?: never;
45
40
  /** @deprecated unsupported requestTimeout, use headersTimeout & bodyTimeout instead */
46
41
  requestTimeout?: never;
47
- /** TODO */
42
+ /** The timeout for establishing a socket connection, in milliseconds. Use `0` to disable it entirely. Default: `10e3` milliseconds (10s). */
48
43
  connectTimeout?: number;
49
44
  /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */
50
45
  bodyTimeout?: number;
@@ -60,7 +55,7 @@ export declare namespace Client {
60
55
  keepAliveMaxTimeout?: number;
61
56
  /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */
62
57
  keepAliveTimeoutThreshold?: number;
63
- /** TODO */
58
+ /** An IPC endpoint, either a Unix domain socket or Windows named pipe. Default: `null`. */
64
59
  socketPath?: string;
65
60
  /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */
66
61
  pipelining?: number;
@@ -68,13 +63,13 @@ export declare namespace Client {
68
63
  tls?: never;
69
64
  /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */
70
65
  strictContentLength?: boolean;
71
- /** TODO */
66
+ /** Maximum number of TLS cached sessions used by the built-in connector. Use `0` to disable TLS session caching. Default: `100`. */
72
67
  maxCachedSessions?: number;
73
- /** TODO */
68
+ /** Connector options passed to `buildConnector`, or a custom connector function. Default: `null`. */
74
69
  connect?: Partial<buildConnector.BuildOptions> | buildConnector.connector;
75
- /** TODO */
70
+ /** The maximum number of requests to send over a single connection before it is reset. Use `0` to disable this limit. Default: `null`. */
76
71
  maxRequestsPerClient?: number;
77
- /** TODO */
72
+ /** Local IP address the socket should connect from. */
78
73
  localAddress?: string;
79
74
  /** Max response body size in bytes, -1 is disabled */
80
75
  maxResponseSize?: number;
@@ -84,7 +79,7 @@ export declare namespace Client {
84
79
  autoSelectFamilyAttemptTimeout?: number;
85
80
  /**
86
81
  * @description Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
87
- * @default false
82
+ * @default true
88
83
  */
89
84
  allowH2?: boolean;
90
85
  /**
@@ -8,8 +8,6 @@ import { FormData } from './formdata'
8
8
  import Errors from './errors'
9
9
  import { Autocomplete } from './utility'
10
10
 
11
- type AbortSignal = unknown
12
-
13
11
  export default Dispatcher
14
12
 
15
13
  export type UndiciHeaders = Record<string, string | string[]> | IncomingHttpHeaders | string[] | Iterable<[string, string | string[] | undefined]> | null
@@ -32,7 +32,7 @@ export declare namespace H2CClient {
32
32
  maxHeaderSize?: number;
33
33
  /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
34
34
  headersTimeout?: number;
35
- /** TODO */
35
+ /** The timeout for establishing a socket connection, in milliseconds. Use `0` to disable it entirely. Default: `10e3` milliseconds (10s). */
36
36
  connectTimeout?: number;
37
37
  /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */
38
38
  bodyTimeout?: number;
@@ -42,19 +42,19 @@ export declare namespace H2CClient {
42
42
  keepAliveMaxTimeout?: number;
43
43
  /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */
44
44
  keepAliveTimeoutThreshold?: number;
45
- /** TODO */
45
+ /** An IPC endpoint, either a Unix domain socket or Windows named pipe. Default: `null`. */
46
46
  socketPath?: string;
47
47
  /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */
48
48
  pipelining?: number;
49
49
  /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */
50
50
  strictContentLength?: boolean;
51
- /** TODO */
51
+ /** Maximum number of TLS cached sessions used by the built-in connector. Use `0` to disable TLS session caching. Default: `100`. */
52
52
  maxCachedSessions?: number;
53
- /** TODO */
53
+ /** Connector options passed to `buildConnector`, or a custom connector function. Default: `null`. */
54
54
  connect?: Omit<Partial<buildConnector.BuildOptions>, 'allowH2'> | buildConnector.connector;
55
- /** TODO */
55
+ /** The maximum number of requests to send over a single connection before it is reset. Use `0` to disable this limit. Default: `null`. */
56
56
  maxRequestsPerClient?: number;
57
- /** TODO */
57
+ /** Local IP address the socket should connect from. */
58
58
  localAddress?: string;
59
59
  /** Max response body size in bytes, -1 is disabled */
60
60
  maxResponseSize?: number;
package/types/pool.d.ts CHANGED
@@ -35,7 +35,5 @@ declare namespace Pool {
35
35
  connections?: number | null;
36
36
  /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */
37
37
  clientTtl?: number | null;
38
-
39
- interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']
40
38
  }
41
39
  }
@@ -35,7 +35,5 @@ declare namespace RoundRobinPool {
35
35
  connections?: number | null;
36
36
  /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */
37
37
  clientTtl?: number | null;
38
-
39
- interceptors?: { RoundRobinPool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']
40
38
  }
41
39
  }
package/types/webidl.d.ts CHANGED
@@ -87,7 +87,6 @@ interface WebidlUtil {
87
87
 
88
88
  /**
89
89
  * Mark a value as uncloneable for Node.js.
90
- * This is only effective in some newer Node.js versions.
91
90
  */
92
91
  markAsUncloneable (V: any): void
93
92
 
@@ -1,28 +0,0 @@
1
- 'use strict'
2
-
3
- /**
4
- * @template {*} T
5
- * @typedef {Object} DeferredPromise
6
- * @property {Promise<T>} promise
7
- * @property {(value?: T) => void} resolve
8
- * @property {(reason?: any) => void} reject
9
- */
10
-
11
- /**
12
- * @template {*} T
13
- * @returns {DeferredPromise<T>} An object containing a promise and its resolve/reject methods.
14
- */
15
- function createDeferredPromise () {
16
- let res
17
- let rej
18
- const promise = new Promise((resolve, reject) => {
19
- res = resolve
20
- rej = reject
21
- })
22
-
23
- return { promise, resolve: res, reject: rej }
24
- }
25
-
26
- module.exports = {
27
- createDeferredPromise
28
- }