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.
- package/docs/docs/api/Client.md +2 -1
- package/docs/docs/api/Dispatcher.md +135 -0
- package/docs/docs/api/RetryHandler.md +6 -0
- package/index.js +4 -0
- package/lib/dispatcher/client.js +13 -3
- package/lib/dispatcher/dispatcher.js +47 -1
- package/lib/handler/retry-handler.js +17 -10
- package/lib/interceptor/redirect.js +24 -0
- package/lib/interceptor/retry.js +19 -0
- package/lib/web/fetch/formdata-parser.js +4 -6
- package/lib/web/fetch/formdata.js +25 -0
- package/lib/web/fetch/headers.js +2 -8
- package/lib/web/fetch/request.js +29 -0
- package/lib/web/fetch/response.js +23 -0
- package/package.json +1 -1
- package/types/dispatcher.d.ts +5 -0
- package/types/fetch.d.ts +1 -1
- package/types/retry-handler.d.ts +1 -1
package/docs/docs/api/Client.md
CHANGED
|
@@ -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
|
-
|
|
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
|
package/lib/dispatcher/client.js
CHANGED
|
@@ -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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
minTimeout,
|
|
108
110
|
maxTimeout,
|
|
109
111
|
timeoutFactor,
|
|
110
112
|
statusCodes,
|
|
111
113
|
errorCodes,
|
|
112
114
|
methods
|
|
113
115
|
} = retryOptions
|
|
114
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
447
|
+
let start = position.position
|
|
448
448
|
|
|
449
|
-
while (
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
position.position++
|
|
449
|
+
while (start < input.length && condition(input[start])) {
|
|
450
|
+
++start
|
|
453
451
|
}
|
|
454
452
|
|
|
455
|
-
return
|
|
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')
|
package/lib/web/fetch/headers.js
CHANGED
|
@@ -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
|
-
|
|
576
|
+
options.depth ??= depth
|
|
583
577
|
|
|
584
|
-
return `Headers ${
|
|
578
|
+
return `Headers ${util.formatWithOptions(options, this[kHeadersList].entries)}`
|
|
585
579
|
}
|
|
586
580
|
}
|
|
587
581
|
|
package/lib/web/fetch/request.js
CHANGED
|
@@ -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
package/types/dispatcher.d.ts
CHANGED
|
@@ -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
package/types/retry-handler.d.ts
CHANGED
|
@@ -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;
|
|
15
|
+
export type RetryState = { counter: number; };
|
|
16
16
|
|
|
17
17
|
export type RetryContext = {
|
|
18
18
|
state: RetryState;
|