undici 6.8.0 → 6.10.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.
@@ -29,7 +29,8 @@ Returns: `Client`
29
29
  * **pipelining** `number | null` (optional) - Default: `1` - 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). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
30
30
  * **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
31
31
  * **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body.
32
- * **interceptors** `{ Client: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time.
32
+ <!-- TODO: Remove once we drop its support -->
33
+ * **interceptors** `{ Client: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time. **Note: this is deprecated in favor of [Dispatcher#compose](./Dispatcher.md#dispatcher). Support will be droped in next major.**
33
34
  * **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.
34
35
  * **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.
35
36
  * **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
@@ -817,6 +817,141 @@ try {
817
817
  }
818
818
  ```
819
819
 
820
+ ### `Dispatcher.compose(interceptors[, interceptor])`
821
+
822
+ Compose a new dispatcher from the current dispatcher and the given interceptors.
823
+
824
+ > _Notes_:
825
+ > - The order of the interceptors matters. The first interceptor will be the first to be called.
826
+ > - It is important to note that the `interceptor` function should return a function that follows the `Dispatcher.dispatch` signature.
827
+ > - Any fork of the chain of `interceptors` can lead to unexpected results.
828
+
829
+ Arguments:
830
+
831
+ * **interceptors** `Interceptor[interceptor[]]`: It is an array of `Interceptor` functions passed as only argument, or several interceptors passed as separate arguments.
832
+
833
+ Returns: `Dispatcher`.
834
+
835
+ #### Parameter: `Interceptor`
836
+
837
+ A function that takes a `dispatch` method and returns a `dispatch`-like function.
838
+
839
+ #### Example 1 - Basic Compose
840
+
841
+ ```js
842
+ const { Client, RedirectHandler } = require('undici')
843
+
844
+ const redirectInterceptor = dispatch => {
845
+ return (opts, handler) => {
846
+ const { maxRedirections } = opts
847
+
848
+ if (!maxRedirections) {
849
+ return dispatch(opts, handler)
850
+ }
851
+
852
+ const redirectHandler = new RedirectHandler(
853
+ dispatch,
854
+ maxRedirections,
855
+ opts,
856
+ handler
857
+ )
858
+ opts = { ...opts, maxRedirections: 0 } // Stop sub dispatcher from also redirecting.
859
+ return dispatch(opts, redirectHandler)
860
+ }
861
+ }
862
+
863
+ const client = new Client('http://localhost:3000')
864
+ .compose(redirectInterceptor)
865
+
866
+ await client.request({ path: '/', method: 'GET' })
867
+ ```
868
+
869
+ #### Example 2 - Chained Compose
870
+
871
+ ```js
872
+ const { Client, RedirectHandler, RetryHandler } = require('undici')
873
+
874
+ const redirectInterceptor = dispatch => {
875
+ return (opts, handler) => {
876
+ const { maxRedirections } = opts
877
+
878
+ if (!maxRedirections) {
879
+ return dispatch(opts, handler)
880
+ }
881
+
882
+ const redirectHandler = new RedirectHandler(
883
+ dispatch,
884
+ maxRedirections,
885
+ opts,
886
+ handler
887
+ )
888
+ opts = { ...opts, maxRedirections: 0 }
889
+ return dispatch(opts, redirectHandler)
890
+ }
891
+ }
892
+
893
+ const retryInterceptor = dispatch => {
894
+ return function retryInterceptor (opts, handler) {
895
+ return dispatch(
896
+ opts,
897
+ new RetryHandler(opts, {
898
+ handler,
899
+ dispatch
900
+ })
901
+ )
902
+ }
903
+ }
904
+
905
+ const client = new Client('http://localhost:3000')
906
+ .compose(redirectInterceptor)
907
+ .compose(retryInterceptor)
908
+
909
+ await client.request({ path: '/', method: 'GET' })
910
+ ```
911
+
912
+ #### Pre-built interceptors
913
+
914
+ ##### `redirect`
915
+
916
+ The `redirect` interceptor allows you to customize the way your dispatcher handles redirects.
917
+
918
+ It accepts the same arguments as the [`RedirectHandler` constructor](./RedirectHandler.md).
919
+
920
+ **Example - Basic Redirect Interceptor**
921
+
922
+ ```js
923
+ const { Client, interceptors } = require("undici");
924
+ const { redirect } = interceptors;
925
+
926
+ const client = new Client("http://example.com").compose(
927
+ redirect({ maxRedirections: 3, throwOnMaxRedirects: true })
928
+ );
929
+ client.request({ path: "/" })
930
+ ```
931
+
932
+ ##### `retry`
933
+
934
+ The `retry` interceptor allows you to customize the way your dispatcher handles retries.
935
+
936
+ It accepts the same arguments as the [`RetryHandler` constructor](./RetryHandler.md).
937
+
938
+ **Example - Basic Redirect Interceptor**
939
+
940
+ ```js
941
+ const { Client, interceptors } = require("undici");
942
+ const { retry } = interceptors;
943
+
944
+ const client = new Client("http://example.com").compose(
945
+ retry({
946
+ maxRetries: 3,
947
+ minTimeout: 1000,
948
+ maxTimeout: 10000,
949
+ timeoutFactor: 2,
950
+ retryAfter: true,
951
+ })
952
+ );
953
+ ```
954
+
820
955
  ## Instance Events
821
956
 
822
957
  ### Event: `'connect'`
@@ -35,6 +35,12 @@ Extends: [`Dispatch.DispatchOptions`](Dispatcher.md#parameter-dispatchoptions).
35
35
  - `state`: `RetryState` - Current retry state. It can be mutated.
36
36
  - `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.
37
37
 
38
+ **`RetryState`**
39
+
40
+ It represents the retry state for a given request.
41
+
42
+ - `counter`: `number` - Current retry attempt.
43
+
38
44
  ### Parameter `RetryHandlers`
39
45
 
40
46
  - **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every retry.
package/index.js CHANGED
@@ -36,6 +36,10 @@ module.exports.RetryHandler = RetryHandler
36
36
  module.exports.DecoratorHandler = DecoratorHandler
37
37
  module.exports.RedirectHandler = RedirectHandler
38
38
  module.exports.createRedirectInterceptor = createRedirectInterceptor
39
+ module.exports.interceptors = {
40
+ redirect: require('./lib/interceptor/redirect'),
41
+ retry: require('./lib/interceptor/retry')
42
+ }
39
43
 
40
44
  module.exports.buildConnector = buildConnector
41
45
  module.exports.errors = errors
@@ -59,6 +59,7 @@ const {
59
59
  } = require('../core/symbols.js')
60
60
  const connectH1 = require('./client-h1.js')
61
61
  const connectH2 = require('./client-h2.js')
62
+ let deprecatedInterceptorWarned = false
62
63
 
63
64
  const kClosedResolve = Symbol('kClosedResolve')
64
65
 
@@ -207,9 +208,18 @@ class Client extends DispatcherBase {
207
208
  })
208
209
  }
209
210
 
210
- this[kInterceptors] = interceptors?.Client && Array.isArray(interceptors.Client)
211
- ? interceptors.Client
212
- : [createRedirectInterceptor({ maxRedirections })]
211
+ if (interceptors?.Client && Array.isArray(interceptors.Client)) {
212
+ this[kInterceptors] = interceptors.Client
213
+ if (!deprecatedInterceptorWarned) {
214
+ deprecatedInterceptorWarned = true
215
+ process.emitWarning('Client.Options#interceptor is deprecated. Use Dispatcher#compose instead.', {
216
+ code: 'UNDICI-CLIENT-INTERCEPTOR-DEPRECATED'
217
+ })
218
+ }
219
+ } else {
220
+ this[kInterceptors] = [createRedirectInterceptor({ maxRedirections })]
221
+ }
222
+
213
223
  this[kUrl] = util.parseOrigin(url)
214
224
  this[kConnector] = connect
215
225
  this[kPipelining] = pipelining != null ? pipelining : 1
@@ -1,5 +1,4 @@
1
1
  'use strict'
2
-
3
2
  const EventEmitter = require('node:events')
4
3
 
5
4
  class Dispatcher extends EventEmitter {
@@ -14,6 +13,53 @@ class Dispatcher extends EventEmitter {
14
13
  destroy () {
15
14
  throw new Error('not implemented')
16
15
  }
16
+
17
+ compose (...args) {
18
+ // So we handle [interceptor1, interceptor2] or interceptor1, interceptor2, ...
19
+ const interceptors = Array.isArray(args[0]) ? args[0] : args
20
+ let dispatch = this.dispatch.bind(this)
21
+
22
+ for (const interceptor of interceptors) {
23
+ if (interceptor == null) {
24
+ continue
25
+ }
26
+
27
+ if (typeof interceptor !== 'function') {
28
+ throw new TypeError(`invalid interceptor, expected function received ${typeof interceptor}`)
29
+ }
30
+
31
+ dispatch = interceptor(dispatch)
32
+
33
+ if (dispatch == null || typeof dispatch !== 'function' || dispatch.length !== 2) {
34
+ throw new TypeError('invalid interceptor')
35
+ }
36
+ }
37
+
38
+ return new ComposedDispatcher(this, dispatch)
39
+ }
40
+ }
41
+
42
+ class ComposedDispatcher extends Dispatcher {
43
+ #dispatcher = null
44
+ #dispatch = null
45
+
46
+ constructor (dispatcher, dispatch) {
47
+ super()
48
+ this.#dispatcher = dispatcher
49
+ this.#dispatch = dispatch
50
+ }
51
+
52
+ dispatch (...args) {
53
+ this.#dispatch(...args)
54
+ }
55
+
56
+ close (...args) {
57
+ return this.#dispatcher.close(...args)
58
+ }
59
+
60
+ destroy (...args) {
61
+ return this.#dispatcher.destroy(...args)
62
+ }
17
63
  }
18
64
 
19
65
  module.exports = Dispatcher
@@ -1,3 +1,4 @@
1
+ 'use strict'
1
2
  const assert = require('node:assert')
2
3
 
3
4
  const { kRetryHandlerDefaultRetry } = require('../core/symbols')
@@ -37,7 +38,7 @@ class RetryHandler {
37
38
  retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
38
39
  retryAfter: retryAfter ?? true,
39
40
  maxTimeout: maxTimeout ?? 30 * 1000, // 30s,
40
- timeout: minTimeout ?? 500, // .5s
41
+ minTimeout: minTimeout ?? 500, // .5s
41
42
  timeoutFactor: timeoutFactor ?? 2,
42
43
  maxRetries: maxRetries ?? 5,
43
44
  // What errors we should retry
@@ -59,6 +60,7 @@ class RetryHandler {
59
60
  }
60
61
 
61
62
  this.retryCount = 0
63
+ this.retryCountCheckpoint = 0
62
64
  this.start = 0
63
65
  this.end = null
64
66
  this.etag = null
@@ -104,17 +106,14 @@ class RetryHandler {
104
106
  const { method, retryOptions } = opts
105
107
  const {
106
108
  maxRetries,
107
- timeout,
109
+ minTimeout,
108
110
  maxTimeout,
109
111
  timeoutFactor,
110
112
  statusCodes,
111
113
  errorCodes,
112
114
  methods
113
115
  } = retryOptions
114
- let { counter, currentTimeout } = state
115
-
116
- currentTimeout =
117
- currentTimeout != null && currentTimeout > 0 ? currentTimeout : timeout
116
+ const { counter } = state
118
117
 
119
118
  // Any code that is not a Undici's originated and allowed to retry
120
119
  if (
@@ -159,9 +158,7 @@ class RetryHandler {
159
158
  const retryTimeout =
160
159
  retryAfterHeader > 0
161
160
  ? Math.min(retryAfterHeader, maxTimeout)
162
- : Math.min(currentTimeout * timeoutFactor ** counter, maxTimeout)
163
-
164
- state.currentTimeout = retryTimeout
161
+ : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
165
162
 
166
163
  setTimeout(() => cb(null), retryTimeout)
167
164
  }
@@ -309,10 +306,19 @@ class RetryHandler {
309
306
  return this.handler.onError(err)
310
307
  }
311
308
 
309
+ // We reconcile in case of a mix between network errors
310
+ // and server error response
311
+ if (this.retryCount - this.retryCountCheckpoint > 0) {
312
+ // We count the difference between the last checkpoint and the current retry count
313
+ this.retryCount = this.retryCountCheckpoint + (this.retryCount - this.retryCountCheckpoint)
314
+ } else {
315
+ this.retryCount += 1
316
+ }
317
+
312
318
  this.retryOpts.retry(
313
319
  err,
314
320
  {
315
- state: { counter: this.retryCount++, currentTimeout: this.retryAfter },
321
+ state: { counter: this.retryCount },
316
322
  opts: { retryOptions: this.retryOpts, ...this.opts }
317
323
  },
318
324
  onRetry.bind(this)
@@ -334,6 +340,7 @@ class RetryHandler {
334
340
  }
335
341
 
336
342
  try {
343
+ this.retryCountCheckpoint = this.retryCount
337
344
  this.dispatch(this.opts, this)
338
345
  } catch (err) {
339
346
  this.handler.onError(err)
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+ const RedirectHandler = require('../handler/redirect-handler')
3
+
4
+ module.exports = opts => {
5
+ const globalMaxRedirections = opts?.maxRedirections
6
+ return dispatch => {
7
+ return function redirectInterceptor (opts, handler) {
8
+ const { maxRedirections = globalMaxRedirections, ...baseOpts } = opts
9
+
10
+ if (!maxRedirections) {
11
+ return dispatch(opts, handler)
12
+ }
13
+
14
+ const redirectHandler = new RedirectHandler(
15
+ dispatch,
16
+ maxRedirections,
17
+ opts,
18
+ handler
19
+ )
20
+
21
+ return dispatch(baseOpts, redirectHandler)
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ const RetryHandler = require('../handler/retry-handler')
3
+
4
+ module.exports = globalOpts => {
5
+ return dispatch => {
6
+ return function retryInterceptor (opts, handler) {
7
+ return dispatch(
8
+ opts,
9
+ new RetryHandler(
10
+ { ...opts, retryOptions: { ...globalOpts, ...opts.retryOptions } },
11
+ {
12
+ handler,
13
+ dispatch
14
+ }
15
+ )
16
+ )
17
+ }
18
+ }
19
+ }
@@ -444,15 +444,13 @@ function parseMultipartFormDataName (input, position) {
444
444
  * @param {{ position: number }} position
445
445
  */
446
446
  function collectASequenceOfBytes (condition, input, position) {
447
- const result = []
447
+ let start = position.position
448
448
 
449
- while (position.position < input.length && condition(input[position.position])) {
450
- result.push(input[position.position])
451
-
452
- position.position++
449
+ while (start < input.length && condition(input[start])) {
450
+ ++start
453
451
  }
454
452
 
455
- return Buffer.from(result, result.length)
453
+ return input.subarray(position.position, (position.position = start))
456
454
  }
457
455
 
458
456
  /**
@@ -6,6 +6,7 @@ const { kEnumerableProperty } = require('../../core/util')
6
6
  const { File: UndiciFile, FileLike, isFileLike } = require('./file')
7
7
  const { webidl } = require('./webidl')
8
8
  const { File: NativeFile } = require('node:buffer')
9
+ const nodeUtil = require('node:util')
9
10
 
10
11
  /** @type {globalThis['File']} */
11
12
  const File = NativeFile ?? UndiciFile
@@ -154,6 +155,30 @@ class FormData {
154
155
  this[kState].push(entry)
155
156
  }
156
157
  }
158
+
159
+ [nodeUtil.inspect.custom] (depth, options) {
160
+ const state = this[kState].reduce((a, b) => {
161
+ if (a[b.name]) {
162
+ if (Array.isArray(a[b.name])) {
163
+ a[b.name].push(b.value)
164
+ } else {
165
+ a[b.name] = [a[b.name], b.value]
166
+ }
167
+ } else {
168
+ a[b.name] = b.value
169
+ }
170
+
171
+ return a
172
+ }, { __proto__: null })
173
+
174
+ options.depth ??= depth
175
+ options.colors ??= true
176
+
177
+ const output = nodeUtil.formatWithOptions(options, state)
178
+
179
+ // remove [Object null prototype]
180
+ return `FormData ${output.slice(output.indexOf(']') + 2)}`
181
+ }
157
182
  }
158
183
 
159
184
  iteratorMixin('FormData', FormData, kState, 'name', 'value')
@@ -572,16 +572,10 @@ class Headers {
572
572
  return (this[kHeadersList][kHeadersSortedMap] = headers)
573
573
  }
574
574
 
575
- [Symbol.for('nodejs.util.inspect.custom')] () {
576
- webidl.brandCheck(this, Headers)
577
-
578
- return this[kHeadersList]
579
- }
580
-
581
575
  [util.inspect.custom] (depth, options) {
582
- const inspected = util.inspect(this[kHeadersList].entries)
576
+ options.depth ??= depth
583
577
 
584
- return `Headers ${inspected}`
578
+ return `Headers ${util.formatWithOptions(options, this[kHeadersList].entries)}`
585
579
  }
586
580
  }
587
581
 
@@ -6,6 +6,7 @@ const { extractBody, mixinBody, cloneBody } = require('./body')
6
6
  const { Headers, fill: fillHeaders, HeadersList } = require('./headers')
7
7
  const { FinalizationRegistry } = require('./dispatcher-weakref')()
8
8
  const util = require('../../core/util')
9
+ const nodeUtil = require('node:util')
9
10
  const {
10
11
  isValidHTTPToken,
11
12
  sameOrigin,
@@ -771,6 +772,34 @@ class Request {
771
772
  // 4. Return clonedRequestObject.
772
773
  return fromInnerRequest(clonedRequest, ac.signal, this[kHeaders][kGuard], this[kRealm])
773
774
  }
775
+
776
+ [nodeUtil.inspect.custom] (depth, options) {
777
+ if (options.depth === null) {
778
+ options.depth = 2
779
+ }
780
+
781
+ options.colors ??= true
782
+
783
+ const properties = {
784
+ method: this.method,
785
+ url: this.url,
786
+ headers: this.headers,
787
+ destination: this.destination,
788
+ referrer: this.referrer,
789
+ referrerPolicy: this.referrerPolicy,
790
+ mode: this.mode,
791
+ credentials: this.credentials,
792
+ cache: this.cache,
793
+ redirect: this.redirect,
794
+ integrity: this.integrity,
795
+ keepalive: this.keepalive,
796
+ isReloadNavigation: this.isReloadNavigation,
797
+ isHistoryNavigation: this.isHistoryNavigation,
798
+ signal: this.signal
799
+ }
800
+
801
+ return `Request ${nodeUtil.formatWithOptions(options, properties)}`
802
+ }
774
803
  }
775
804
 
776
805
  mixinBody(Request)
@@ -3,6 +3,7 @@
3
3
  const { Headers, HeadersList, fill } = require('./headers')
4
4
  const { extractBody, cloneBody, mixinBody } = require('./body')
5
5
  const util = require('../../core/util')
6
+ const nodeUtil = require('node:util')
6
7
  const { kEnumerableProperty } = util
7
8
  const {
8
9
  isValidReasonPhrase,
@@ -252,6 +253,28 @@ class Response {
252
253
  // clonedResponse, this’s headers’s guard, and this’s relevant Realm.
253
254
  return fromInnerResponse(clonedResponse, this[kHeaders][kGuard], this[kRealm])
254
255
  }
256
+
257
+ [nodeUtil.inspect.custom] (depth, options) {
258
+ if (options.depth === null) {
259
+ options.depth = 2
260
+ }
261
+
262
+ options.colors ??= true
263
+
264
+ const properties = {
265
+ status: this.status,
266
+ statusText: this.statusText,
267
+ headers: this.headers,
268
+ body: this.body,
269
+ bodyUsed: this.bodyUsed,
270
+ ok: this.ok,
271
+ redirected: this.redirected,
272
+ type: this.type,
273
+ url: this.url
274
+ }
275
+
276
+ return `Response ${nodeUtil.formatWithOptions(options, properties)}`
277
+ }
255
278
  }
256
279
 
257
280
  mixinBody(Response)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "6.8.0",
3
+ "version": "6.10.0",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -18,6 +18,9 @@ declare class Dispatcher extends EventEmitter {
18
18
  /** Starts two-way communications with the requested resource. */
19
19
  connect(options: Dispatcher.ConnectOptions): Promise<Dispatcher.ConnectData>;
20
20
  connect(options: Dispatcher.ConnectOptions, callback: (err: Error | null, data: Dispatcher.ConnectData) => void): void;
21
+ /** Compose a chain of dispatchers */
22
+ compose(dispatchers: Dispatcher['dispatch'][]): Dispatcher.ComposedDispatcher;
23
+ compose(...dispatchers: Dispatcher['dispatch'][]): Dispatcher.ComposedDispatcher;
21
24
  /** Performs an HTTP request. */
22
25
  request(options: Dispatcher.RequestOptions): Promise<Dispatcher.ResponseData>;
23
26
  request(options: Dispatcher.RequestOptions, callback: (err: Error | null, data: Dispatcher.ResponseData) => void): void;
@@ -93,6 +96,8 @@ declare class Dispatcher extends EventEmitter {
93
96
  }
94
97
 
95
98
  declare namespace Dispatcher {
99
+ export interface ComposedDispatcher extends Dispatcher {}
100
+ export type DispatcherInterceptor = (dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch'];
96
101
  export interface DispatchOptions {
97
102
  origin?: string | URL;
98
103
  path: string;
package/types/fetch.d.ts CHANGED
@@ -122,7 +122,7 @@ export interface RequestInit {
122
122
  method?: string
123
123
  keepalive?: boolean
124
124
  headers?: HeadersInit
125
- body?: BodyInit
125
+ body?: BodyInit | null
126
126
  redirect?: RequestRedirect
127
127
  integrity?: string
128
128
  signal?: AbortSignal | null
@@ -12,7 +12,7 @@ declare class RetryHandler implements Dispatcher.DispatchHandlers {
12
12
  }
13
13
 
14
14
  declare namespace RetryHandler {
15
- export type RetryState = { counter: number; currentTimeout: number };
15
+ export type RetryState = { counter: number; };
16
16
 
17
17
  export type RetryContext = {
18
18
  state: RetryState;