undici 7.10.0 → 7.12.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/README.md +159 -0
- package/docs/docs/api/CacheStore.md +3 -3
- package/docs/docs/api/Debug.md +13 -13
- package/docs/docs/api/DiagnosticsChannel.md +32 -4
- package/docs/docs/api/Dispatcher.md +22 -3
- package/docs/docs/api/GlobalInstallation.md +91 -0
- package/docs/docs/api/MockClient.md +4 -0
- package/docs/docs/api/MockPool.md +6 -0
- package/docs/docs/api/ProxyAgent.md +2 -0
- package/docs/docs/api/RetryAgent.md +6 -1
- package/docs/docs/api/RetryHandler.md +1 -0
- package/docs/docs/api/WebSocket.md +27 -0
- package/index.js +18 -1
- package/lib/api/api-stream.js +1 -1
- package/lib/api/readable.js +1 -3
- package/lib/cache/memory-cache-store.js +3 -3
- package/lib/cache/sqlite-cache-store.js +1 -1
- package/lib/core/connect.js +21 -51
- package/lib/core/diagnostics.js +6 -4
- package/lib/core/request.js +12 -1
- package/lib/core/tree.js +1 -1
- package/lib/core/util.js +0 -45
- package/lib/dispatcher/client-h1.js +9 -18
- package/lib/dispatcher/proxy-agent.js +2 -1
- package/lib/handler/cache-handler.js +4 -1
- package/lib/handler/redirect-handler.js +2 -2
- package/lib/handler/retry-handler.js +110 -56
- package/lib/interceptor/cache.js +2 -2
- package/lib/interceptor/redirect.js +1 -1
- package/lib/mock/mock-client.js +4 -0
- package/lib/mock/mock-pool.js +4 -0
- package/lib/util/cache.js +12 -2
- package/lib/util/promise.js +28 -0
- package/lib/util/timers.js +11 -9
- package/lib/web/cache/cache.js +11 -9
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +1 -1
- package/lib/web/eventsource/eventsource.js +3 -6
- package/lib/web/eventsource/util.js +1 -1
- package/lib/web/fetch/body.js +36 -24
- package/lib/web/fetch/formdata-parser.js +4 -4
- package/lib/web/fetch/formdata.js +1 -1
- package/lib/web/fetch/headers.js +1 -1
- package/lib/web/fetch/index.js +228 -226
- package/lib/web/fetch/request.js +16 -8
- package/lib/web/fetch/response.js +6 -4
- package/lib/web/fetch/util.js +23 -25
- package/lib/web/{fetch/webidl.js → webidl/index.js} +57 -9
- package/lib/web/websocket/connection.js +4 -12
- package/lib/web/websocket/events.js +1 -1
- package/lib/web/websocket/frame.js +2 -1
- package/lib/web/websocket/receiver.js +2 -12
- package/lib/web/websocket/stream/websocketerror.js +1 -1
- package/lib/web/websocket/stream/websocketstream.js +8 -5
- package/lib/web/websocket/websocket.js +61 -5
- package/package.json +5 -5
- package/types/diagnostics-channel.d.ts +9 -0
- package/types/dispatcher.d.ts +3 -2
- package/types/env-http-proxy-agent.d.ts +2 -1
- package/types/eventsource.d.ts +3 -3
- package/types/fetch.d.ts +1 -0
- package/types/handlers.d.ts +1 -1
- package/types/mock-client.d.ts +2 -0
- package/types/mock-interceptor.d.ts +2 -0
- package/types/mock-pool.d.ts +2 -0
- package/types/retry-handler.d.ts +9 -0
- package/types/webidl.d.ts +29 -15
- package/types/websocket.d.ts +3 -1
- package/lib/web/fetch/dispatcher-weakref.js +0 -46
package/README.md
CHANGED
|
@@ -43,6 +43,127 @@ The benchmark is a simple getting data [example](https://github.com/nodejs/undic
|
|
|
43
43
|
└────────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
## Undici vs. Fetch
|
|
47
|
+
|
|
48
|
+
### Overview
|
|
49
|
+
|
|
50
|
+
Node.js includes a built-in `fetch()` implementation powered by undici starting from Node.js v18. However, there are important differences between using the built-in fetch and installing undici as a separate module.
|
|
51
|
+
|
|
52
|
+
### Built-in Fetch (Node.js v18+)
|
|
53
|
+
|
|
54
|
+
Node.js's built-in fetch is powered by a bundled version of undici:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// Available globally in Node.js v18+
|
|
58
|
+
const response = await fetch('https://api.example.com/data');
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
|
|
61
|
+
// Check the bundled undici version
|
|
62
|
+
console.log(process.versions.undici); // e.g., "5.28.4"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Pros:**
|
|
66
|
+
- No additional dependencies required
|
|
67
|
+
- Works across different JavaScript runtimes
|
|
68
|
+
- Automatic compression handling (gzip, deflate, br)
|
|
69
|
+
- Built-in caching support (in development)
|
|
70
|
+
|
|
71
|
+
**Cons:**
|
|
72
|
+
- Limited to the undici version bundled with your Node.js version
|
|
73
|
+
- Less control over connection pooling and advanced features
|
|
74
|
+
- Error handling follows Web API standards (errors wrapped in `TypeError`)
|
|
75
|
+
- Performance overhead due to Web Streams implementation
|
|
76
|
+
|
|
77
|
+
### Undici Module
|
|
78
|
+
|
|
79
|
+
Installing undici as a separate module gives you access to the latest features and APIs:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install undici
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import { request, fetch, Agent, setGlobalDispatcher } from 'undici';
|
|
87
|
+
|
|
88
|
+
// Use undici.request for maximum performance
|
|
89
|
+
const { statusCode, headers, body } = await request('https://api.example.com/data');
|
|
90
|
+
const data = await body.json();
|
|
91
|
+
|
|
92
|
+
// Or use undici.fetch with custom configuration
|
|
93
|
+
const agent = new Agent({ keepAliveTimeout: 10000 });
|
|
94
|
+
setGlobalDispatcher(agent);
|
|
95
|
+
const response = await fetch('https://api.example.com/data');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Pros:**
|
|
99
|
+
- Latest undici features and bug fixes
|
|
100
|
+
- Access to advanced APIs (`request`, `stream`, `pipeline`)
|
|
101
|
+
- Fine-grained control over connection pooling
|
|
102
|
+
- Better error handling with clearer error messages
|
|
103
|
+
- Superior performance, especially with `undici.request`
|
|
104
|
+
- HTTP/1.1 pipelining support
|
|
105
|
+
- Custom interceptors and middleware
|
|
106
|
+
- Advanced features like `ProxyAgent`, `MockAgent`
|
|
107
|
+
|
|
108
|
+
**Cons:**
|
|
109
|
+
- Additional dependency to manage
|
|
110
|
+
- Larger bundle size
|
|
111
|
+
|
|
112
|
+
### When to Use Each
|
|
113
|
+
|
|
114
|
+
#### Use Built-in Fetch When:
|
|
115
|
+
- You want zero dependencies
|
|
116
|
+
- Building isomorphic code that runs in browsers and Node.js
|
|
117
|
+
- Publishing to npm and want to maximize compatibility with JS runtimes
|
|
118
|
+
- Simple HTTP requests without advanced configuration
|
|
119
|
+
- You're publishing to npm and you want to maximize compatiblity
|
|
120
|
+
- You don't depend on features from a specific version of undici
|
|
121
|
+
|
|
122
|
+
#### Use Undici Module When:
|
|
123
|
+
- You need the latest undici features and performance improvements
|
|
124
|
+
- You require advanced connection pooling configuration
|
|
125
|
+
- You need APIs not available in the built-in fetch (`ProxyAgent`, `MockAgent`, etc.)
|
|
126
|
+
- Performance is critical (use `undici.request` for maximum speed)
|
|
127
|
+
- You want better error handling and debugging capabilities
|
|
128
|
+
- You need HTTP/1.1 pipelining or advanced interceptors
|
|
129
|
+
- You prefer decoupled protocol and API interfaces
|
|
130
|
+
|
|
131
|
+
### Performance Comparison
|
|
132
|
+
|
|
133
|
+
Based on benchmarks, here's the typical performance hierarchy:
|
|
134
|
+
|
|
135
|
+
1. **`undici.request()`** - Fastest, most efficient
|
|
136
|
+
2. **`undici.fetch()`** - Good performance, standard compliance
|
|
137
|
+
3. **Node.js `http`/`https`** - Baseline performance
|
|
138
|
+
|
|
139
|
+
### Migration Guide
|
|
140
|
+
|
|
141
|
+
If you're currently using built-in fetch and want to migrate to undici:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// Before: Built-in fetch
|
|
145
|
+
const response = await fetch('https://api.example.com/data');
|
|
146
|
+
|
|
147
|
+
// After: Undici fetch (drop-in replacement)
|
|
148
|
+
import { fetch } from 'undici';
|
|
149
|
+
const response = await fetch('https://api.example.com/data');
|
|
150
|
+
|
|
151
|
+
// Or: Undici request (better performance)
|
|
152
|
+
import { request } from 'undici';
|
|
153
|
+
const { statusCode, body } = await request('https://api.example.com/data');
|
|
154
|
+
const data = await body.json();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Version Compatibility
|
|
158
|
+
|
|
159
|
+
You can check which version of undici is bundled with your Node.js version:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
console.log(process.versions.undici);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Installing undici as a module allows you to use a newer version than what's bundled with Node.js, giving you access to the latest features and performance improvements.
|
|
166
|
+
|
|
46
167
|
## Quick Start
|
|
47
168
|
|
|
48
169
|
```js
|
|
@@ -63,6 +184,44 @@ for await (const data of body) { console.log('data', data) }
|
|
|
63
184
|
console.log('trailers', trailers)
|
|
64
185
|
```
|
|
65
186
|
|
|
187
|
+
## Global Installation
|
|
188
|
+
|
|
189
|
+
Undici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally:
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
import { install } from 'undici'
|
|
193
|
+
|
|
194
|
+
// Install all WHATWG fetch classes globally
|
|
195
|
+
install()
|
|
196
|
+
|
|
197
|
+
// Now you can use fetch classes globally without importing
|
|
198
|
+
const response = await fetch('https://api.example.com/data')
|
|
199
|
+
const data = await response.json()
|
|
200
|
+
|
|
201
|
+
// All classes are available globally:
|
|
202
|
+
const headers = new Headers([['content-type', 'application/json']])
|
|
203
|
+
const request = new Request('https://example.com')
|
|
204
|
+
const formData = new FormData()
|
|
205
|
+
const ws = new WebSocket('wss://example.com')
|
|
206
|
+
const eventSource = new EventSource('https://example.com/events')
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The `install()` function adds the following classes to `globalThis`:
|
|
210
|
+
|
|
211
|
+
- `fetch` - The fetch function
|
|
212
|
+
- `Headers` - HTTP headers management
|
|
213
|
+
- `Response` - HTTP response representation
|
|
214
|
+
- `Request` - HTTP request representation
|
|
215
|
+
- `FormData` - Form data handling
|
|
216
|
+
- `WebSocket` - WebSocket client
|
|
217
|
+
- `CloseEvent`, `ErrorEvent`, `MessageEvent` - WebSocket events
|
|
218
|
+
- `EventSource` - Server-sent events client
|
|
219
|
+
|
|
220
|
+
This is useful for:
|
|
221
|
+
- Polyfilling environments that don't have fetch
|
|
222
|
+
- Ensuring consistent fetch behavior across different Node.js versions
|
|
223
|
+
- Making undici's implementations available globally for libraries that expect them
|
|
224
|
+
|
|
66
225
|
## Body Mixins
|
|
67
226
|
|
|
68
227
|
The `body` mixins are the most common way to format the request/response body. Mixins include:
|
|
@@ -13,9 +13,9 @@ The `MemoryCacheStore` stores the responses in-memory.
|
|
|
13
13
|
|
|
14
14
|
**Options**
|
|
15
15
|
|
|
16
|
-
- `maxSize` - The maximum total size in bytes of all stored responses. Default `
|
|
17
|
-
- `maxCount` - The maximum amount of responses to store. Default `
|
|
18
|
-
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `
|
|
16
|
+
- `maxSize` - The maximum total size in bytes of all stored responses. Default `104857600` (100MB).
|
|
17
|
+
- `maxCount` - The maximum amount of responses to store. Default `1024`.
|
|
18
|
+
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `5242880` (5MB).
|
|
19
19
|
|
|
20
20
|
### Getters
|
|
21
21
|
|
package/docs/docs/api/Debug.md
CHANGED
|
@@ -14,14 +14,14 @@ NODE_DEBUG=undici node script.js
|
|
|
14
14
|
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
15
15
|
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
16
16
|
UNDICI 16241: connected to nodejs.org using https:h1
|
|
17
|
-
UNDICI 16241: sending request to GET https://nodejs.org
|
|
18
|
-
UNDICI 16241: received response to GET https://nodejs.org
|
|
17
|
+
UNDICI 16241: sending request to GET https://nodejs.org/
|
|
18
|
+
UNDICI 16241: received response to GET https://nodejs.org/ - HTTP 307
|
|
19
19
|
UNDICI 16241: connecting to nodejs.org using https:h1
|
|
20
|
-
UNDICI 16241: trailers received from GET https://nodejs.org
|
|
20
|
+
UNDICI 16241: trailers received from GET https://nodejs.org/
|
|
21
21
|
UNDICI 16241: connected to nodejs.org using https:h1
|
|
22
|
-
UNDICI 16241: sending request to GET https://nodejs.org
|
|
23
|
-
UNDICI 16241: received response to GET https://nodejs.org
|
|
24
|
-
UNDICI 16241: trailers received from GET https://nodejs.org
|
|
22
|
+
UNDICI 16241: sending request to GET https://nodejs.org/en
|
|
23
|
+
UNDICI 16241: received response to GET https://nodejs.org/en - HTTP 200
|
|
24
|
+
UNDICI 16241: trailers received from GET https://nodejs.org/en
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## `fetch`
|
|
@@ -36,14 +36,14 @@ NODE_DEBUG=fetch node script.js
|
|
|
36
36
|
FETCH 16241: connecting to nodejs.org using https:h1
|
|
37
37
|
FETCH 16241: connecting to nodejs.org using https:h1
|
|
38
38
|
FETCH 16241: connected to nodejs.org using https:h1
|
|
39
|
-
FETCH 16241: sending request to GET https://nodejs.org
|
|
40
|
-
FETCH 16241: received response to GET https://nodejs.org
|
|
39
|
+
FETCH 16241: sending request to GET https://nodejs.org/
|
|
40
|
+
FETCH 16241: received response to GET https://nodejs.org/ - HTTP 307
|
|
41
41
|
FETCH 16241: connecting to nodejs.org using https:h1
|
|
42
|
-
FETCH 16241: trailers received from GET https://nodejs.org
|
|
42
|
+
FETCH 16241: trailers received from GET https://nodejs.org/
|
|
43
43
|
FETCH 16241: connected to nodejs.org using https:h1
|
|
44
|
-
FETCH 16241: sending request to GET https://nodejs.org
|
|
45
|
-
FETCH 16241: received response to GET https://nodejs.org
|
|
46
|
-
FETCH 16241: trailers received from GET https://nodejs.org
|
|
44
|
+
FETCH 16241: sending request to GET https://nodejs.org/en
|
|
45
|
+
FETCH 16241: received response to GET https://nodejs.org/en - HTTP 200
|
|
46
|
+
FETCH 16241: trailers received from GET https://nodejs.org/en
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
## `websocket`
|
|
@@ -57,6 +57,6 @@ NODE_DEBUG=websocket node script.js
|
|
|
57
57
|
|
|
58
58
|
WEBSOCKET 18309: connecting to echo.websocket.org using https:h1
|
|
59
59
|
WEBSOCKET 18309: connected to echo.websocket.org using https:h1
|
|
60
|
-
WEBSOCKET 18309: sending request to GET https://echo.websocket.org
|
|
60
|
+
WEBSOCKET 18309: sending request to GET https://echo.websocket.org/
|
|
61
61
|
WEBSOCKET 18309: connection opened <ip_address>
|
|
62
62
|
```
|
|
@@ -27,9 +27,22 @@ diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
|
|
|
27
27
|
|
|
28
28
|
Note: a request is only loosely completed to a given socket.
|
|
29
29
|
|
|
30
|
+
## `undici:request:bodyChunkSent`
|
|
31
|
+
|
|
32
|
+
This message is published when a chunk of the request body is being sent.
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
36
|
+
|
|
37
|
+
diagnosticsChannel.channel('undici:request:bodyChunkSent').subscribe(({ request, chunk }) => {
|
|
38
|
+
// request is the same object undici:request:create
|
|
39
|
+
})
|
|
40
|
+
```
|
|
30
41
|
|
|
31
42
|
## `undici:request:bodySent`
|
|
32
43
|
|
|
44
|
+
This message is published after the request body has been fully sent.
|
|
45
|
+
|
|
33
46
|
```js
|
|
34
47
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
35
48
|
|
|
@@ -54,6 +67,18 @@ diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, respo
|
|
|
54
67
|
})
|
|
55
68
|
```
|
|
56
69
|
|
|
70
|
+
## `undici:request:bodyChunkReceived`
|
|
71
|
+
|
|
72
|
+
This message is published after a chunk of the response body has been received.
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
76
|
+
|
|
77
|
+
diagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ request, chunk }) => {
|
|
78
|
+
// request is the same object undici:request:create
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
57
82
|
## `undici:request:trailers`
|
|
58
83
|
|
|
59
84
|
This message is published after the response body and trailers have been received, i.e. the response has been completed.
|
|
@@ -144,10 +169,11 @@ This message is published after the client has successfully connected to a serve
|
|
|
144
169
|
```js
|
|
145
170
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
146
171
|
|
|
147
|
-
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions }) => {
|
|
172
|
+
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions, websocket }) => {
|
|
148
173
|
console.log(address) // address, family, and port
|
|
149
174
|
console.log(protocol) // negotiated subprotocols
|
|
150
175
|
console.log(extensions) // negotiated extensions
|
|
176
|
+
console.log(websocket) // the WebSocket instance
|
|
151
177
|
})
|
|
152
178
|
```
|
|
153
179
|
|
|
@@ -159,7 +185,7 @@ This message is published after the connection has closed.
|
|
|
159
185
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
160
186
|
|
|
161
187
|
diagnosticsChannel.channel('undici:websocket:close').subscribe(({ websocket, code, reason }) => {
|
|
162
|
-
console.log(websocket) // the WebSocket
|
|
188
|
+
console.log(websocket) // the WebSocket instance
|
|
163
189
|
console.log(code) // the closing status code
|
|
164
190
|
console.log(reason) // the closing reason
|
|
165
191
|
})
|
|
@@ -184,9 +210,10 @@ This message is published after the client receives a ping frame, if the connect
|
|
|
184
210
|
```js
|
|
185
211
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
186
212
|
|
|
187
|
-
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload }) => {
|
|
213
|
+
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload, websocket }) => {
|
|
188
214
|
// a Buffer or undefined, containing the optional application data of the frame
|
|
189
215
|
console.log(payload)
|
|
216
|
+
console.log(websocket) // the WebSocket instance
|
|
190
217
|
})
|
|
191
218
|
```
|
|
192
219
|
|
|
@@ -197,8 +224,9 @@ This message is published after the client receives a pong frame.
|
|
|
197
224
|
```js
|
|
198
225
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
199
226
|
|
|
200
|
-
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {
|
|
227
|
+
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload, websocket }) => {
|
|
201
228
|
// a Buffer or undefined, containing the optional application data of the frame
|
|
202
229
|
console.log(payload)
|
|
230
|
+
console.log(websocket) // the WebSocket instance
|
|
203
231
|
})
|
|
204
232
|
```
|
|
@@ -841,9 +841,28 @@ try {
|
|
|
841
841
|
Compose a new dispatcher from the current dispatcher and the given interceptors.
|
|
842
842
|
|
|
843
843
|
> _Notes_:
|
|
844
|
-
> - The order of the interceptors matters. The
|
|
844
|
+
> - The order of the interceptors matters. The last interceptor will be the first to be called.
|
|
845
845
|
> - It is important to note that the `interceptor` function should return a function that follows the `Dispatcher.dispatch` signature.
|
|
846
846
|
> - Any fork of the chain of `interceptors` can lead to unexpected results.
|
|
847
|
+
>
|
|
848
|
+
> **Interceptor Stack Visualization:**
|
|
849
|
+
> ```
|
|
850
|
+
> compose([interceptor1, interceptor2, interceptor3])
|
|
851
|
+
>
|
|
852
|
+
> Request Flow:
|
|
853
|
+
> ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
854
|
+
> │ Request │───▶│interceptor3 │───▶│interceptor2 │───▶│interceptor1 │───▶│ dispatcher │
|
|
855
|
+
> └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ .dispatch │
|
|
856
|
+
> ▲ ▲ ▲ └─────────────┘
|
|
857
|
+
> │ │ │ ▲
|
|
858
|
+
> (called first) (called second) (called last) │
|
|
859
|
+
> │
|
|
860
|
+
> ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
861
|
+
> │ Response │◀───│interceptor3 │◀───│interceptor2 │◀───│interceptor1 │◀─────────┘
|
|
862
|
+
> └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
|
863
|
+
>
|
|
864
|
+
> The interceptors are composed in reverse order due to function composition.
|
|
865
|
+
> ```
|
|
847
866
|
|
|
848
867
|
Arguments:
|
|
849
868
|
|
|
@@ -1084,8 +1103,8 @@ The `cache` interceptor implements client-side response caching as described in
|
|
|
1084
1103
|
|
|
1085
1104
|
- `store` - The [`CacheStore`](/docs/docs/api/CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](/docs/docs/api/CacheStore.md#memorycachestore).
|
|
1086
1105
|
- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.
|
|
1087
|
-
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration. If this isn't present, responses
|
|
1088
|
-
- `type` - The type of cache for Undici to act as. Can be `shared` or `private`. Default `shared`.
|
|
1106
|
+
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration and cannot have an heuristic expiry computed. If this isn't present, responses neither with an explicit expiration nor heuristically cacheable will not be cached. Default `undefined`.
|
|
1107
|
+
- `type` - The [type of cache](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching#types_of_caches) for Undici to act as. Can be `shared` or `private`. Default `shared`. `private` implies privately cacheable responses will be cached and potentially shared with other users of your application.
|
|
1089
1108
|
|
|
1090
1109
|
## Instance Events
|
|
1091
1110
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Global Installation
|
|
2
|
+
|
|
3
|
+
Undici provides an `install()` function to add all WHATWG fetch classes to `globalThis`, making them available globally without requiring imports.
|
|
4
|
+
|
|
5
|
+
## `install()`
|
|
6
|
+
|
|
7
|
+
Install all WHATWG fetch classes globally on `globalThis`.
|
|
8
|
+
|
|
9
|
+
**Example:**
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { install } from 'undici'
|
|
13
|
+
|
|
14
|
+
// Install all WHATWG fetch classes globally
|
|
15
|
+
install()
|
|
16
|
+
|
|
17
|
+
// Now you can use fetch classes globally without importing
|
|
18
|
+
const response = await fetch('https://api.example.com/data')
|
|
19
|
+
const data = await response.json()
|
|
20
|
+
|
|
21
|
+
// All classes are available globally:
|
|
22
|
+
const headers = new Headers([['content-type', 'application/json']])
|
|
23
|
+
const request = new Request('https://example.com')
|
|
24
|
+
const formData = new FormData()
|
|
25
|
+
const ws = new WebSocket('wss://example.com')
|
|
26
|
+
const eventSource = new EventSource('https://example.com/events')
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Installed Classes
|
|
30
|
+
|
|
31
|
+
The `install()` function adds the following classes to `globalThis`:
|
|
32
|
+
|
|
33
|
+
| Class | Description |
|
|
34
|
+
|-------|-------------|
|
|
35
|
+
| `fetch` | The fetch function for making HTTP requests |
|
|
36
|
+
| `Headers` | HTTP headers management |
|
|
37
|
+
| `Response` | HTTP response representation |
|
|
38
|
+
| `Request` | HTTP request representation |
|
|
39
|
+
| `FormData` | Form data handling |
|
|
40
|
+
| `WebSocket` | WebSocket client |
|
|
41
|
+
| `CloseEvent` | WebSocket close event |
|
|
42
|
+
| `ErrorEvent` | WebSocket error event |
|
|
43
|
+
| `MessageEvent` | WebSocket message event |
|
|
44
|
+
| `EventSource` | Server-sent events client |
|
|
45
|
+
|
|
46
|
+
## Use Cases
|
|
47
|
+
|
|
48
|
+
Global installation is useful for:
|
|
49
|
+
|
|
50
|
+
- **Polyfilling environments** that don't have native fetch support
|
|
51
|
+
- **Ensuring consistent behavior** across different Node.js versions
|
|
52
|
+
- **Library compatibility** when third-party libraries expect global fetch
|
|
53
|
+
- **Migration scenarios** where you want to replace built-in implementations
|
|
54
|
+
- **Testing environments** where you need predictable fetch behavior
|
|
55
|
+
|
|
56
|
+
## Example: Polyfilling an Environment
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { install } from 'undici'
|
|
60
|
+
|
|
61
|
+
// Check if fetch is available and install if needed
|
|
62
|
+
if (typeof globalThis.fetch === 'undefined') {
|
|
63
|
+
install()
|
|
64
|
+
console.log('Undici fetch installed globally')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Now fetch is guaranteed to be available
|
|
68
|
+
const response = await fetch('https://api.example.com')
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Example: Testing Environment
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import { install } from 'undici'
|
|
75
|
+
|
|
76
|
+
// In test setup, ensure consistent fetch behavior
|
|
77
|
+
install()
|
|
78
|
+
|
|
79
|
+
// Now all tests use undici's implementations
|
|
80
|
+
test('fetch API test', async () => {
|
|
81
|
+
const response = await fetch('https://example.com')
|
|
82
|
+
expect(response).toBeInstanceOf(Response)
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
- The `install()` function overwrites any existing global implementations
|
|
89
|
+
- Classes installed are undici's implementations, not Node.js built-ins
|
|
90
|
+
- This provides access to undici's latest features and performance improvements
|
|
91
|
+
- The global installation persists for the lifetime of the process
|
|
@@ -38,6 +38,10 @@ const mockClient = mockAgent.get('http://localhost:3000')
|
|
|
38
38
|
|
|
39
39
|
Implements: [`MockPool.intercept(options)`](/docs/docs/api/MockPool.md#mockpoolinterceptoptions)
|
|
40
40
|
|
|
41
|
+
### `MockClient.cleanMocks()`
|
|
42
|
+
|
|
43
|
+
Implements: [`MockPool.cleanMocks()`](/docs/docs/api/MockPool.md#mockpoolcleanmocks)
|
|
44
|
+
|
|
41
45
|
### `MockClient.close()`
|
|
42
46
|
|
|
43
47
|
Implements: [`MockPool.close()`](/docs/docs/api/MockPool.md#mockpoolclose)
|
|
@@ -17,6 +17,8 @@ Returns: `ProxyAgent`
|
|
|
17
17
|
Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
|
|
18
18
|
> It ommits `AgentOptions#connect`.
|
|
19
19
|
|
|
20
|
+
> **Note:** When `AgentOptions#connections` is set, and different from `0`, the non-standard [`proxy-connection` header](https://udger.com/resources/http-request-headers-detail?header=Proxy-Connection) will be set to `keep-alive` in the request.
|
|
21
|
+
|
|
20
22
|
* **uri** `string | URL` (required) - The URI of the proxy server. This can be provided as a string, as an instance of the URL class, or as an object with a `uri` property of type string.
|
|
21
23
|
If the `uri` is provided as a string or `uri` is an object with an `uri` property of type string, then it will be parsed into a `URL` object according to the [WHATWG URL Specification](https://url.spec.whatwg.org).
|
|
22
24
|
For detailed information on the parsing process and potential validation errors, please refer to the ["Writing" section](https://url.spec.whatwg.org/#writing) of the WHATWG URL Specification.
|
|
@@ -16,6 +16,7 @@ Returns: `ProxyAgent`
|
|
|
16
16
|
|
|
17
17
|
### Parameter: `RetryHandlerOptions`
|
|
18
18
|
|
|
19
|
+
- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler. Default: `true`
|
|
19
20
|
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
|
|
20
21
|
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
|
21
22
|
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
|
@@ -39,7 +40,11 @@ import { Agent, RetryAgent } from 'undici'
|
|
|
39
40
|
|
|
40
41
|
const agent = new RetryAgent(new Agent())
|
|
41
42
|
|
|
42
|
-
const res = await agent.request(
|
|
43
|
+
const res = await agent.request({
|
|
44
|
+
method: 'GET',
|
|
45
|
+
origin: 'http://example.com',
|
|
46
|
+
path: '/',
|
|
47
|
+
})
|
|
43
48
|
console.log(res.statusCode)
|
|
44
49
|
console.log(await res.body.text())
|
|
45
50
|
```
|
|
@@ -19,6 +19,7 @@ Extends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dis
|
|
|
19
19
|
|
|
20
20
|
#### `RetryOptions`
|
|
21
21
|
|
|
22
|
+
- **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler.
|
|
22
23
|
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => number | null` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
|
|
23
24
|
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
|
24
25
|
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
|
@@ -78,6 +78,33 @@ setInterval(() => write(), 5000)
|
|
|
78
78
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
## ping(websocket, payload)
|
|
82
|
+
Arguments:
|
|
83
|
+
|
|
84
|
+
* **websocket** `WebSocket` - The WebSocket instance to send the ping frame on
|
|
85
|
+
* **payload** `Buffer|undefined` (optional) - Optional payload data to include with the ping frame. Must not exceed 125 bytes.
|
|
86
|
+
|
|
87
|
+
Sends a ping frame to the WebSocket server. The server must respond with a pong frame containing the same payload data. This can be used for keepalive purposes or to verify that the connection is still active.
|
|
88
|
+
|
|
89
|
+
### Example:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { WebSocket, ping } from 'undici'
|
|
93
|
+
|
|
94
|
+
const ws = new WebSocket('wss://echo.websocket.events')
|
|
95
|
+
|
|
96
|
+
ws.addEventListener('open', () => {
|
|
97
|
+
// Send ping with no payload
|
|
98
|
+
ping(ws)
|
|
99
|
+
|
|
100
|
+
// Send ping with payload
|
|
101
|
+
const payload = Buffer.from('hello')
|
|
102
|
+
ping(ws, payload)
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Note**: A ping frame cannot have a payload larger than 125 bytes. The ping will only be sent if the WebSocket connection is in the OPEN state.
|
|
107
|
+
|
|
81
108
|
## Read More
|
|
82
109
|
|
|
83
110
|
- [MDN - WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
package/index.js
CHANGED
|
@@ -157,10 +157,12 @@ module.exports.parseMIMEType = parseMIMEType
|
|
|
157
157
|
module.exports.serializeAMimeType = serializeAMimeType
|
|
158
158
|
|
|
159
159
|
const { CloseEvent, ErrorEvent, MessageEvent } = require('./lib/web/websocket/events')
|
|
160
|
-
|
|
160
|
+
const { WebSocket, ping } = require('./lib/web/websocket/websocket')
|
|
161
|
+
module.exports.WebSocket = WebSocket
|
|
161
162
|
module.exports.CloseEvent = CloseEvent
|
|
162
163
|
module.exports.ErrorEvent = ErrorEvent
|
|
163
164
|
module.exports.MessageEvent = MessageEvent
|
|
165
|
+
module.exports.ping = ping
|
|
164
166
|
|
|
165
167
|
module.exports.WebSocketStream = require('./lib/web/websocket/stream/websocketstream').WebSocketStream
|
|
166
168
|
module.exports.WebSocketError = require('./lib/web/websocket/stream/websocketerror').WebSocketError
|
|
@@ -181,3 +183,18 @@ module.exports.mockErrors = mockErrors
|
|
|
181
183
|
const { EventSource } = require('./lib/web/eventsource/eventsource')
|
|
182
184
|
|
|
183
185
|
module.exports.EventSource = EventSource
|
|
186
|
+
|
|
187
|
+
function install () {
|
|
188
|
+
globalThis.fetch = module.exports.fetch
|
|
189
|
+
globalThis.Headers = module.exports.Headers
|
|
190
|
+
globalThis.Response = module.exports.Response
|
|
191
|
+
globalThis.Request = module.exports.Request
|
|
192
|
+
globalThis.FormData = module.exports.FormData
|
|
193
|
+
globalThis.WebSocket = module.exports.WebSocket
|
|
194
|
+
globalThis.CloseEvent = module.exports.CloseEvent
|
|
195
|
+
globalThis.ErrorEvent = module.exports.ErrorEvent
|
|
196
|
+
globalThis.MessageEvent = module.exports.MessageEvent
|
|
197
|
+
globalThis.EventSource = module.exports.EventSource
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports.install = install
|
package/lib/api/api-stream.js
CHANGED
package/lib/api/readable.js
CHANGED
|
@@ -89,9 +89,7 @@ class BodyReadable extends Readable {
|
|
|
89
89
|
// promise (i.e micro tick) for installing an 'error' listener will
|
|
90
90
|
// never get a chance and will always encounter an unhandled exception.
|
|
91
91
|
if (!this[kUsed]) {
|
|
92
|
-
setImmediate(
|
|
93
|
-
callback(err)
|
|
94
|
-
})
|
|
92
|
+
setImmediate(callback, err)
|
|
95
93
|
} else {
|
|
96
94
|
callback(err)
|
|
97
95
|
}
|
|
@@ -16,9 +16,9 @@ const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
|
|
|
16
16
|
* @extends {EventEmitter}
|
|
17
17
|
*/
|
|
18
18
|
class MemoryCacheStore extends EventEmitter {
|
|
19
|
-
#maxCount =
|
|
20
|
-
#maxSize =
|
|
21
|
-
#maxEntrySize =
|
|
19
|
+
#maxCount = 1024
|
|
20
|
+
#maxSize = 104857600 // 100MB
|
|
21
|
+
#maxEntrySize = 5242880 // 5MB
|
|
22
22
|
|
|
23
23
|
#size = 0
|
|
24
24
|
#count = 0
|