@depup/undici 7.24.3-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/changes.json +5 -0
  4. package/docs/docs/api/Agent.md +84 -0
  5. package/docs/docs/api/BalancedPool.md +99 -0
  6. package/docs/docs/api/CacheStorage.md +30 -0
  7. package/docs/docs/api/CacheStore.md +164 -0
  8. package/docs/docs/api/Client.md +285 -0
  9. package/docs/docs/api/ClientStats.md +27 -0
  10. package/docs/docs/api/Connector.md +115 -0
  11. package/docs/docs/api/ContentType.md +57 -0
  12. package/docs/docs/api/Cookies.md +101 -0
  13. package/docs/docs/api/Debug.md +62 -0
  14. package/docs/docs/api/DiagnosticsChannel.md +313 -0
  15. package/docs/docs/api/Dispatcher.md +1392 -0
  16. package/docs/docs/api/EnvHttpProxyAgent.md +159 -0
  17. package/docs/docs/api/Errors.md +49 -0
  18. package/docs/docs/api/EventSource.md +45 -0
  19. package/docs/docs/api/Fetch.md +52 -0
  20. package/docs/docs/api/GlobalInstallation.md +91 -0
  21. package/docs/docs/api/H2CClient.md +263 -0
  22. package/docs/docs/api/MockAgent.md +603 -0
  23. package/docs/docs/api/MockCallHistory.md +197 -0
  24. package/docs/docs/api/MockCallHistoryLog.md +43 -0
  25. package/docs/docs/api/MockClient.md +81 -0
  26. package/docs/docs/api/MockErrors.md +12 -0
  27. package/docs/docs/api/MockPool.md +555 -0
  28. package/docs/docs/api/Pool.md +84 -0
  29. package/docs/docs/api/PoolStats.md +35 -0
  30. package/docs/docs/api/ProxyAgent.md +229 -0
  31. package/docs/docs/api/RedirectHandler.md +96 -0
  32. package/docs/docs/api/RetryAgent.md +50 -0
  33. package/docs/docs/api/RetryHandler.md +118 -0
  34. package/docs/docs/api/RoundRobinPool.md +145 -0
  35. package/docs/docs/api/SnapshotAgent.md +616 -0
  36. package/docs/docs/api/Socks5ProxyAgent.md +274 -0
  37. package/docs/docs/api/Util.md +25 -0
  38. package/docs/docs/api/WebSocket.md +141 -0
  39. package/docs/docs/api/api-lifecycle.md +91 -0
  40. package/docs/docs/best-practices/client-certificate.md +64 -0
  41. package/docs/docs/best-practices/crawling.md +58 -0
  42. package/docs/docs/best-practices/mocking-request.md +190 -0
  43. package/docs/docs/best-practices/proxy.md +127 -0
  44. package/docs/docs/best-practices/undici-vs-builtin-fetch.md +137 -0
  45. package/docs/docs/best-practices/writing-tests.md +20 -0
  46. package/index-fetch.js +65 -0
  47. package/index.d.ts +3 -0
  48. package/index.js +234 -0
  49. package/lib/api/abort-signal.js +59 -0
  50. package/lib/api/api-connect.js +110 -0
  51. package/lib/api/api-pipeline.js +252 -0
  52. package/lib/api/api-request.js +214 -0
  53. package/lib/api/api-stream.js +209 -0
  54. package/lib/api/api-upgrade.js +111 -0
  55. package/lib/api/index.js +7 -0
  56. package/lib/api/readable.js +580 -0
  57. package/lib/cache/memory-cache-store.js +234 -0
  58. package/lib/cache/sqlite-cache-store.js +461 -0
  59. package/lib/core/connect.js +137 -0
  60. package/lib/core/constants.js +143 -0
  61. package/lib/core/diagnostics.js +225 -0
  62. package/lib/core/errors.js +477 -0
  63. package/lib/core/request.js +430 -0
  64. package/lib/core/socks5-client.js +407 -0
  65. package/lib/core/socks5-utils.js +203 -0
  66. package/lib/core/symbols.js +75 -0
  67. package/lib/core/tree.js +160 -0
  68. package/lib/core/util.js +972 -0
  69. package/lib/dispatcher/agent.js +158 -0
  70. package/lib/dispatcher/balanced-pool.js +219 -0
  71. package/lib/dispatcher/client-h1.js +1610 -0
  72. package/lib/dispatcher/client-h2.js +995 -0
  73. package/lib/dispatcher/client.js +654 -0
  74. package/lib/dispatcher/dispatcher-base.js +165 -0
  75. package/lib/dispatcher/dispatcher.js +48 -0
  76. package/lib/dispatcher/env-http-proxy-agent.js +146 -0
  77. package/lib/dispatcher/fixed-queue.js +135 -0
  78. package/lib/dispatcher/h2c-client.js +51 -0
  79. package/lib/dispatcher/pool-base.js +214 -0
  80. package/lib/dispatcher/pool.js +118 -0
  81. package/lib/dispatcher/proxy-agent.js +318 -0
  82. package/lib/dispatcher/retry-agent.js +35 -0
  83. package/lib/dispatcher/round-robin-pool.js +137 -0
  84. package/lib/dispatcher/socks5-proxy-agent.js +249 -0
  85. package/lib/encoding/index.js +33 -0
  86. package/lib/global.js +50 -0
  87. package/lib/handler/cache-handler.js +561 -0
  88. package/lib/handler/cache-revalidation-handler.js +124 -0
  89. package/lib/handler/decorator-handler.js +67 -0
  90. package/lib/handler/deduplication-handler.js +460 -0
  91. package/lib/handler/redirect-handler.js +238 -0
  92. package/lib/handler/retry-handler.js +394 -0
  93. package/lib/handler/unwrap-handler.js +100 -0
  94. package/lib/handler/wrap-handler.js +105 -0
  95. package/lib/interceptor/cache.js +495 -0
  96. package/lib/interceptor/decompress.js +259 -0
  97. package/lib/interceptor/deduplicate.js +117 -0
  98. package/lib/interceptor/dns.js +571 -0
  99. package/lib/interceptor/dump.js +112 -0
  100. package/lib/interceptor/redirect.js +21 -0
  101. package/lib/interceptor/response-error.js +95 -0
  102. package/lib/interceptor/retry.js +19 -0
  103. package/lib/llhttp/.gitkeep +0 -0
  104. package/lib/llhttp/constants.d.ts +195 -0
  105. package/lib/llhttp/constants.js +531 -0
  106. package/lib/llhttp/llhttp-wasm.js +15 -0
  107. package/lib/llhttp/llhttp_simd-wasm.js +15 -0
  108. package/lib/llhttp/utils.d.ts +2 -0
  109. package/lib/llhttp/utils.js +12 -0
  110. package/lib/mock/mock-agent.js +232 -0
  111. package/lib/mock/mock-call-history.js +248 -0
  112. package/lib/mock/mock-client.js +68 -0
  113. package/lib/mock/mock-errors.js +29 -0
  114. package/lib/mock/mock-interceptor.js +209 -0
  115. package/lib/mock/mock-pool.js +68 -0
  116. package/lib/mock/mock-symbols.js +31 -0
  117. package/lib/mock/mock-utils.js +480 -0
  118. package/lib/mock/pending-interceptors-formatter.js +43 -0
  119. package/lib/mock/snapshot-agent.js +353 -0
  120. package/lib/mock/snapshot-recorder.js +588 -0
  121. package/lib/mock/snapshot-utils.js +158 -0
  122. package/lib/util/cache.js +407 -0
  123. package/lib/util/date.js +653 -0
  124. package/lib/util/promise.js +28 -0
  125. package/lib/util/runtime-features.js +124 -0
  126. package/lib/util/stats.js +32 -0
  127. package/lib/util/timers.js +425 -0
  128. package/lib/web/cache/cache.js +864 -0
  129. package/lib/web/cache/cachestorage.js +152 -0
  130. package/lib/web/cache/util.js +45 -0
  131. package/lib/web/cookies/constants.js +12 -0
  132. package/lib/web/cookies/index.js +199 -0
  133. package/lib/web/cookies/parse.js +322 -0
  134. package/lib/web/cookies/util.js +282 -0
  135. package/lib/web/eventsource/eventsource-stream.js +399 -0
  136. package/lib/web/eventsource/eventsource.js +501 -0
  137. package/lib/web/eventsource/util.js +29 -0
  138. package/lib/web/fetch/LICENSE +21 -0
  139. package/lib/web/fetch/body.js +509 -0
  140. package/lib/web/fetch/constants.js +131 -0
  141. package/lib/web/fetch/data-url.js +596 -0
  142. package/lib/web/fetch/formdata-parser.js +575 -0
  143. package/lib/web/fetch/formdata.js +259 -0
  144. package/lib/web/fetch/global.js +40 -0
  145. package/lib/web/fetch/headers.js +719 -0
  146. package/lib/web/fetch/index.js +2378 -0
  147. package/lib/web/fetch/request.js +1115 -0
  148. package/lib/web/fetch/response.js +641 -0
  149. package/lib/web/fetch/util.js +1520 -0
  150. package/lib/web/infra/index.js +229 -0
  151. package/lib/web/subresource-integrity/Readme.md +9 -0
  152. package/lib/web/subresource-integrity/subresource-integrity.js +307 -0
  153. package/lib/web/webidl/index.js +1006 -0
  154. package/lib/web/websocket/connection.js +329 -0
  155. package/lib/web/websocket/constants.js +126 -0
  156. package/lib/web/websocket/events.js +331 -0
  157. package/lib/web/websocket/frame.js +133 -0
  158. package/lib/web/websocket/permessage-deflate.js +118 -0
  159. package/lib/web/websocket/receiver.js +450 -0
  160. package/lib/web/websocket/sender.js +109 -0
  161. package/lib/web/websocket/stream/websocketerror.js +104 -0
  162. package/lib/web/websocket/stream/websocketstream.js +497 -0
  163. package/lib/web/websocket/util.js +347 -0
  164. package/lib/web/websocket/websocket.js +739 -0
  165. package/package.json +163 -0
  166. package/scripts/strip-comments.js +10 -0
  167. package/types/README.md +6 -0
  168. package/types/agent.d.ts +32 -0
  169. package/types/api.d.ts +43 -0
  170. package/types/balanced-pool.d.ts +30 -0
  171. package/types/cache-interceptor.d.ts +179 -0
  172. package/types/cache.d.ts +36 -0
  173. package/types/client-stats.d.ts +15 -0
  174. package/types/client.d.ts +123 -0
  175. package/types/connector.d.ts +36 -0
  176. package/types/content-type.d.ts +21 -0
  177. package/types/cookies.d.ts +30 -0
  178. package/types/diagnostics-channel.d.ts +74 -0
  179. package/types/dispatcher.d.ts +279 -0
  180. package/types/env-http-proxy-agent.d.ts +22 -0
  181. package/types/errors.d.ts +177 -0
  182. package/types/eventsource.d.ts +66 -0
  183. package/types/fetch.d.ts +211 -0
  184. package/types/formdata.d.ts +108 -0
  185. package/types/global-dispatcher.d.ts +9 -0
  186. package/types/global-origin.d.ts +7 -0
  187. package/types/h2c-client.d.ts +73 -0
  188. package/types/handlers.d.ts +15 -0
  189. package/types/header.d.ts +160 -0
  190. package/types/index.d.ts +91 -0
  191. package/types/interceptors.d.ts +80 -0
  192. package/types/mock-agent.d.ts +68 -0
  193. package/types/mock-call-history.d.ts +111 -0
  194. package/types/mock-client.d.ts +27 -0
  195. package/types/mock-errors.d.ts +12 -0
  196. package/types/mock-interceptor.d.ts +94 -0
  197. package/types/mock-pool.d.ts +27 -0
  198. package/types/patch.d.ts +29 -0
  199. package/types/pool-stats.d.ts +19 -0
  200. package/types/pool.d.ts +41 -0
  201. package/types/proxy-agent.d.ts +29 -0
  202. package/types/readable.d.ts +68 -0
  203. package/types/retry-agent.d.ts +8 -0
  204. package/types/retry-handler.d.ts +125 -0
  205. package/types/round-robin-pool.d.ts +41 -0
  206. package/types/snapshot-agent.d.ts +109 -0
  207. package/types/socks5-proxy-agent.d.ts +25 -0
  208. package/types/util.d.ts +18 -0
  209. package/types/utility.d.ts +7 -0
  210. package/types/webidl.d.ts +347 -0
  211. package/types/websocket.d.ts +188 -0
@@ -0,0 +1,190 @@
1
+ # Mocking Request
2
+
3
+ Undici has its own mocking [utility](/docs/docs/api/MockAgent.md). It allow us to intercept undici HTTP requests and return mocked values instead. It can be useful for testing purposes.
4
+
5
+ Example:
6
+
7
+ ```js
8
+ // bank.mjs
9
+ import { request } from 'undici'
10
+
11
+ export async function bankTransfer(recipient, amount) {
12
+ const { body } = await request('http://localhost:3000/bank-transfer',
13
+ {
14
+ method: 'POST',
15
+ headers: {
16
+ 'X-TOKEN-SECRET': 'SuperSecretToken',
17
+ },
18
+ body: JSON.stringify({
19
+ recipient,
20
+ amount
21
+ })
22
+ }
23
+ )
24
+ return await body.json()
25
+ }
26
+ ```
27
+
28
+ And this is what the test file looks like:
29
+
30
+ ```js
31
+ // index.test.mjs
32
+ import { strict as assert } from 'node:assert'
33
+ import { MockAgent, setGlobalDispatcher, } from 'undici'
34
+ import { bankTransfer } from './bank.mjs'
35
+
36
+ const mockAgent = new MockAgent();
37
+
38
+ setGlobalDispatcher(mockAgent);
39
+
40
+ // Provide the base url to the request
41
+ const mockPool = mockAgent.get('http://localhost:3000');
42
+
43
+ // intercept the request
44
+ mockPool.intercept({
45
+ path: '/bank-transfer',
46
+ method: 'POST',
47
+ headers: {
48
+ 'X-TOKEN-SECRET': 'SuperSecretToken',
49
+ },
50
+ body: JSON.stringify({
51
+ recipient: '1234567890',
52
+ amount: '100'
53
+ })
54
+ }).reply(200, {
55
+ message: 'transaction processed'
56
+ })
57
+
58
+ const success = await bankTransfer('1234567890', '100')
59
+
60
+ assert.deepEqual(success, { message: 'transaction processed' })
61
+
62
+ // if you dont want to check whether the body or the headers contain the same value
63
+ // just remove it from interceptor
64
+ mockPool.intercept({
65
+ path: '/bank-transfer',
66
+ method: 'POST',
67
+ }).reply(400, {
68
+ message: 'bank account not found'
69
+ })
70
+
71
+ const badRequest = await bankTransfer('1234567890', '100')
72
+
73
+ assert.deepEqual(badRequest, { message: 'bank account not found' })
74
+ ```
75
+
76
+ Explore other MockAgent functionality [here](/docs/docs/api/MockAgent.md)
77
+
78
+ ## Access agent call history
79
+
80
+ Using a MockAgent also allows you to make assertions on the configuration used to make your request in your application.
81
+
82
+ Here is an example :
83
+
84
+ ```js
85
+ // index.test.mjs
86
+ import { strict as assert } from 'node:assert'
87
+ import { MockAgent, setGlobalDispatcher, fetch } from 'undici'
88
+ import { app } from './app.mjs'
89
+
90
+ // given an application server running on http://localhost:3000
91
+ await app.start()
92
+
93
+ // enable call history at instantiation
94
+ const mockAgent = new MockAgent({ enableCallHistory: true })
95
+ // or after instantiation
96
+ mockAgent.enableCallHistory()
97
+
98
+ setGlobalDispatcher(mockAgent)
99
+
100
+ // this call is made (not intercepted)
101
+ await fetch(`http://localhost:3000/endpoint?query='hello'`, {
102
+ method: 'POST',
103
+ headers: { 'content-type': 'application/json' }
104
+ body: JSON.stringify({ data: '' })
105
+ })
106
+
107
+ // access to the call history of the MockAgent (which register every call made intercepted or not)
108
+ assert.ok(mockAgent.getCallHistory()?.calls().length === 1)
109
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.fullUrl, `http://localhost:3000/endpoint?query='hello'`)
110
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.body, JSON.stringify({ data: '' }))
111
+ assert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.searchParams, { query: 'hello' })
112
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.port, '3000')
113
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.host, 'localhost:3000')
114
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.method, 'POST')
115
+ assert.strictEqual(mockAgent.getCallHistory()?.firstCall()?.path, '/endpoint')
116
+ assert.deepStrictEqual(mockAgent.getCallHistory()?.firstCall()?.headers, { 'content-type': 'application/json' })
117
+
118
+ // clear all call history logs
119
+ mockAgent.clearCallHistory()
120
+
121
+ assert.ok(mockAgent.getCallHistory()?.calls().length === 0)
122
+ ```
123
+
124
+ Calling `mockAgent.close()` will automatically clear and delete every call history for you.
125
+
126
+ Explore other MockAgent functionality [here](/docs/docs/api/MockAgent.md)
127
+
128
+ Explore other MockCallHistory functionality [here](/docs/docs/api/MockCallHistory.md)
129
+
130
+ Explore other MockCallHistoryLog functionality [here](/docs/docs/api/MockCallHistoryLog.md)
131
+
132
+ ## Debug Mock Value
133
+
134
+ When the interceptor and the request options are not the same, undici will automatically make a real HTTP request. To prevent real requests from being made, use `mockAgent.disableNetConnect()`:
135
+
136
+ ```js
137
+ const mockAgent = new MockAgent();
138
+
139
+ setGlobalDispatcher(mockAgent);
140
+ mockAgent.disableNetConnect()
141
+
142
+ // Provide the base url to the request
143
+ const mockPool = mockAgent.get('http://localhost:3000');
144
+
145
+ mockPool.intercept({
146
+ path: '/bank-transfer',
147
+ method: 'POST',
148
+ }).reply(200, {
149
+ message: 'transaction processed'
150
+ })
151
+
152
+ const badRequest = await bankTransfer('1234567890', '100')
153
+ // Will throw an error
154
+ // MockNotMatchedError: Mock dispatch not matched for path '/bank-transfer':
155
+ // subsequent request to origin http://localhost:3000 was not allowed (net.connect disabled)
156
+ ```
157
+
158
+ ## Reply with data based on request
159
+
160
+ If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply`:
161
+
162
+ ```js
163
+ mockPool.intercept({
164
+ path: '/bank-transfer',
165
+ method: 'POST',
166
+ headers: {
167
+ 'X-TOKEN-SECRET': 'SuperSecretToken',
168
+ },
169
+ body: JSON.stringify({
170
+ recipient: '1234567890',
171
+ amount: '100'
172
+ })
173
+ }).reply(200, (opts) => {
174
+ // do something with opts
175
+
176
+ return { message: 'transaction processed' }
177
+ })
178
+ ```
179
+
180
+ in this case opts will be
181
+
182
+ ```
183
+ {
184
+ method: 'POST',
185
+ headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' },
186
+ body: '{"recipient":"1234567890","amount":"100"}',
187
+ origin: 'http://localhost:3000',
188
+ path: '/bank-transfer'
189
+ }
190
+ ```
@@ -0,0 +1,127 @@
1
+ # Connecting through a proxy
2
+
3
+ Connecting through a proxy is possible by:
4
+
5
+ - Using [ProxyAgent](/docs/docs/api/ProxyAgent.md).
6
+ - Configuring `Client` or `Pool` constructor.
7
+
8
+ The proxy url should be passed to the `Client` or `Pool` constructor, while the upstream server url
9
+ should be added to every request call in the `path`.
10
+ For instance, if you need to send a request to the `/hello` route of your upstream server,
11
+ the `path` should be `path: 'http://upstream.server:port/hello?foo=bar'`.
12
+
13
+ If you proxy requires basic authentication, you can send it via the `proxy-authorization` header.
14
+
15
+ ### Connect without authentication
16
+
17
+ ```js
18
+ import { Client } from 'undici'
19
+ import { createServer } from 'http'
20
+ import { createProxy } from 'proxy'
21
+
22
+ const server = await buildServer()
23
+ const proxyServer = await buildProxy()
24
+
25
+ const serverUrl = `http://localhost:${server.address().port}`
26
+ const proxyUrl = `http://localhost:${proxyServer.address().port}`
27
+
28
+ server.on('request', (req, res) => {
29
+ console.log(req.url) // '/hello?foo=bar'
30
+ res.setHeader('content-type', 'application/json')
31
+ res.end(JSON.stringify({ hello: 'world' }))
32
+ })
33
+
34
+ const client = new Client(proxyUrl)
35
+
36
+ const response = await client.request({
37
+ method: 'GET',
38
+ path: serverUrl + '/hello?foo=bar'
39
+ })
40
+
41
+ response.body.setEncoding('utf8')
42
+ let data = ''
43
+ for await (const chunk of response.body) {
44
+ data += chunk
45
+ }
46
+ console.log(response.statusCode) // 200
47
+ console.log(JSON.parse(data)) // { hello: 'world' }
48
+
49
+ server.close()
50
+ proxyServer.close()
51
+ client.close()
52
+
53
+ function buildServer () {
54
+ return new Promise((resolve, reject) => {
55
+ const server = createServer()
56
+ server.listen(0, () => resolve(server))
57
+ })
58
+ }
59
+
60
+ function buildProxy () {
61
+ return new Promise((resolve, reject) => {
62
+ const server = createProxy(createServer())
63
+ server.listen(0, () => resolve(server))
64
+ })
65
+ }
66
+ ```
67
+
68
+ ### Connect with authentication
69
+
70
+ ```js
71
+ import { Client } from 'undici'
72
+ import { createServer } from 'http'
73
+ import { createProxy } from 'proxy'
74
+
75
+ const server = await buildServer()
76
+ const proxyServer = await buildProxy()
77
+
78
+ const serverUrl = `http://localhost:${server.address().port}`
79
+ const proxyUrl = `http://localhost:${proxyServer.address().port}`
80
+
81
+ proxyServer.authenticate = function (req) {
82
+ return req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`
83
+ }
84
+
85
+ server.on('request', (req, res) => {
86
+ console.log(req.url) // '/hello?foo=bar'
87
+ res.setHeader('content-type', 'application/json')
88
+ res.end(JSON.stringify({ hello: 'world' }))
89
+ })
90
+
91
+ const client = new Client(proxyUrl)
92
+
93
+ const response = await client.request({
94
+ method: 'GET',
95
+ path: serverUrl + '/hello?foo=bar',
96
+ headers: {
97
+ 'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`
98
+ }
99
+ })
100
+
101
+ response.body.setEncoding('utf8')
102
+ let data = ''
103
+ for await (const chunk of response.body) {
104
+ data += chunk
105
+ }
106
+ console.log(response.statusCode) // 200
107
+ console.log(JSON.parse(data)) // { hello: 'world' }
108
+
109
+ server.close()
110
+ proxyServer.close()
111
+ client.close()
112
+
113
+ function buildServer () {
114
+ return new Promise((resolve, reject) => {
115
+ const server = createServer()
116
+ server.listen(0, () => resolve(server))
117
+ })
118
+ }
119
+
120
+ function buildProxy () {
121
+ return new Promise((resolve, reject) => {
122
+ const server = createProxy(createServer())
123
+ server.listen(0, () => resolve(server))
124
+ })
125
+ }
126
+ ```
127
+
@@ -0,0 +1,137 @@
1
+ # Undici Module vs. Node.js Built-in Fetch
2
+
3
+ Node.js has shipped a built-in `fetch()` implementation powered by undici since
4
+ Node.js v18. This guide explains the relationship between the `undici` npm
5
+ package and the built-in `fetch`, and when you should install one versus relying
6
+ on the other.
7
+
8
+ ## Background
9
+
10
+ The `fetch()`, `Request`, `Response`, `Headers`, and `FormData` globals in
11
+ Node.js v18+ are provided by a version of undici that is bundled into Node.js
12
+ itself. You can check which version is bundled with:
13
+
14
+ ```js
15
+ console.log(process.versions.undici); // e.g., "7.5.0"
16
+ ```
17
+
18
+ When you install undici from npm, you get the full library with all of its
19
+ additional APIs, and potentially a newer release than what your Node.js version
20
+ bundles.
21
+
22
+ ## When you do NOT need to install undici
23
+
24
+ If all of the following are true, you can rely on the built-in globals and skip
25
+ adding undici to your dependencies:
26
+
27
+ - You only need the standard Fetch API (`fetch`, `Request`, `Response`,
28
+ `Headers`, `FormData`).
29
+ - You are running Node.js v18 or later.
30
+ - You do not depend on features or bug fixes introduced in a version of undici
31
+ newer than the one bundled with your Node.js release.
32
+ - You want zero additional runtime dependencies.
33
+ - You want cross-platform interoperability with browsers and other runtimes
34
+ (Deno, Bun, Cloudflare Workers, etc.) using the same Fetch API surface.
35
+
36
+ This is common in applications that make straightforward HTTP requests or in
37
+ libraries that target multiple JavaScript runtimes.
38
+
39
+ ## When you SHOULD install undici
40
+
41
+ Install undici from npm when you need capabilities beyond the standard Fetch API:
42
+
43
+ ### Advanced HTTP APIs
44
+
45
+ undici exposes `request`, `stream`, `pipeline`, and `connect` methods that
46
+ provide lower-level control and significantly better performance than `fetch`:
47
+
48
+ ```js
49
+ import { request } from 'undici';
50
+
51
+ const { statusCode, headers, body } = await request('https://example.com');
52
+ const data = await body.json();
53
+ ```
54
+
55
+ ### Connection pooling and dispatchers
56
+
57
+ `Client`, `Pool`, `BalancedPool`, `Agent`, and their configuration options
58
+ let you manage connection lifecycle, keep-alive behavior, pipelining depth,
59
+ and concurrency limits:
60
+
61
+ ```js
62
+ import { Pool } from 'undici';
63
+
64
+ const pool = new Pool('https://example.com', { connections: 10 });
65
+ const { body } = await pool.request({ path: '/', method: 'GET' });
66
+ ```
67
+
68
+ ### Proxy support
69
+
70
+ `ProxyAgent` and `EnvHttpProxyAgent` handle HTTP(S) proxying. Note that
71
+ Node.js v22.21.0+ and v24.0.0+ support environment-variable-based proxy
72
+ configuration for the built-in `fetch` via the `--use-env-proxy` flag (or
73
+ `NODE_USE_ENV_PROXY=1`). However, undici's `ProxyAgent` still provides
74
+ programmatic control through the dispatcher API:
75
+
76
+ ```js
77
+ import { ProxyAgent, fetch } from 'undici';
78
+
79
+ const proxyAgent = new ProxyAgent('https://my-proxy.example.com:8080');
80
+ const response = await fetch('https://example.com', { dispatcher: proxyAgent });
81
+ ```
82
+
83
+ ### Testing and mocking
84
+
85
+ `MockAgent`, `MockClient`, and `MockPool` let you intercept and mock HTTP
86
+ requests without patching globals or depending on external libraries:
87
+
88
+ ```js
89
+ import { MockAgent, setGlobalDispatcher, fetch } from 'undici';
90
+
91
+ const mockAgent = new MockAgent();
92
+ setGlobalDispatcher(mockAgent);
93
+
94
+ const pool = mockAgent.get('https://example.com');
95
+ pool.intercept({ path: '/api' }).reply(200, { message: 'mocked' });
96
+ ```
97
+
98
+ ### Interceptors and middleware
99
+
100
+ Custom dispatchers and interceptors (retry, redirect, cache, DNS) give you
101
+ fine-grained control over how requests are processed.
102
+
103
+ ### Newer version than what Node.js bundles
104
+
105
+ The npm package often includes features, performance improvements, and bug fixes
106
+ that have not yet landed in a Node.js release. If you need a specific fix or
107
+ feature, you can install a newer version directly.
108
+
109
+ ## Version compatibility
110
+
111
+ | Node.js version | Bundled undici version | Notes |
112
+ |---|---|---|
113
+ | v18.x | ~5.x | `fetch` is experimental (behind `--experimental-fetch` in early v18) |
114
+ | v20.x | ~6.x | `fetch` is stable |
115
+ | v22.x | ~6.x / ~7.x | `fetch` is stable |
116
+ | v24.x | ~7.x | `fetch` is stable; env-proxy support via `--use-env-proxy` |
117
+
118
+ You can always check the exact bundled version at runtime with
119
+ `process.versions.undici`.
120
+
121
+ Installing undici from npm does not replace the built-in globals. If you want
122
+ your installed version to override the global `fetch`, use
123
+ [`setGlobalDispatcher`](/docs/api/GlobalInstallation.md) or import `fetch`
124
+ directly from `'undici'`:
125
+
126
+ ```js
127
+ import { fetch } from 'undici'; // uses your installed version, not the built-in
128
+ ```
129
+
130
+ ## Further reading
131
+
132
+ - [API Reference: Fetch](/docs/api/Fetch.md)
133
+ - [API Reference: Client](/docs/api/Client.md)
134
+ - [API Reference: Pool](/docs/api/Pool.md)
135
+ - [API Reference: ProxyAgent](/docs/api/ProxyAgent.md)
136
+ - [API Reference: MockAgent](/docs/api/MockAgent.md)
137
+ - [API Reference: Global Installation](/docs/api/GlobalInstallation.md)
@@ -0,0 +1,20 @@
1
+ # Writing tests
2
+
3
+ Undici is tuned for a production use case and its default will keep
4
+ a socket open for a few seconds after an HTTP request is completed to
5
+ remove the overhead of opening up a new socket. These settings that makes
6
+ Undici shine in production are not a good fit for using Undici in automated
7
+ tests, as it will result in longer execution times.
8
+
9
+ The following are good defaults that will keep the socket open for only 10ms:
10
+
11
+ ```js
12
+ import { request, setGlobalDispatcher, Agent } from 'undici'
13
+
14
+ const agent = new Agent({
15
+ keepAliveTimeout: 10, // milliseconds
16
+ keepAliveMaxTimeout: 10 // milliseconds
17
+ })
18
+
19
+ setGlobalDispatcher(agent)
20
+ ```
package/index-fetch.js ADDED
@@ -0,0 +1,65 @@
1
+ 'use strict'
2
+
3
+ const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
4
+ const EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')
5
+ const fetchImpl = require('./lib/web/fetch').fetch
6
+
7
+ // Capture __filename at module load time for stack trace augmentation.
8
+ // This may be undefined when bundled in environments like Node.js internals.
9
+ const currentFilename = typeof __filename !== 'undefined' ? __filename : undefined
10
+
11
+ function appendFetchStackTrace (err, filename) {
12
+ if (!err || typeof err !== 'object') {
13
+ return
14
+ }
15
+
16
+ const stack = typeof err.stack === 'string' ? err.stack : ''
17
+ const normalizedFilename = filename.replace(/\\/g, '/')
18
+
19
+ if (stack && (stack.includes(filename) || stack.includes(normalizedFilename))) {
20
+ return
21
+ }
22
+
23
+ const capture = {}
24
+ Error.captureStackTrace(capture, appendFetchStackTrace)
25
+
26
+ if (!capture.stack) {
27
+ return
28
+ }
29
+
30
+ const captureLines = capture.stack.split('\n').slice(1).join('\n')
31
+
32
+ err.stack = stack ? `${stack}\n${captureLines}` : capture.stack
33
+ }
34
+
35
+ module.exports.fetch = function fetch (init, options = undefined) {
36
+ return fetchImpl(init, options).catch(err => {
37
+ if (currentFilename) {
38
+ appendFetchStackTrace(err, currentFilename)
39
+ } else if (err && typeof err === 'object') {
40
+ Error.captureStackTrace(err, module.exports.fetch)
41
+ }
42
+ throw err
43
+ })
44
+ }
45
+ module.exports.FormData = require('./lib/web/fetch/formdata').FormData
46
+ module.exports.Headers = require('./lib/web/fetch/headers').Headers
47
+ module.exports.Response = require('./lib/web/fetch/response').Response
48
+ module.exports.Request = require('./lib/web/fetch/request').Request
49
+
50
+ const { CloseEvent, ErrorEvent, MessageEvent, createFastMessageEvent } = require('./lib/web/websocket/events')
51
+ module.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket
52
+ module.exports.CloseEvent = CloseEvent
53
+ module.exports.ErrorEvent = ErrorEvent
54
+ module.exports.MessageEvent = MessageEvent
55
+ module.exports.createFastMessageEvent = createFastMessageEvent
56
+
57
+ module.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource
58
+
59
+ const api = require('./lib/api')
60
+ const Dispatcher = require('./lib/dispatcher/dispatcher')
61
+ Object.assign(Dispatcher.prototype, api)
62
+ // Expose the fetch implementation to be enabled in Node.js core via a flag
63
+ module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent
64
+ module.exports.getGlobalDispatcher = getGlobalDispatcher
65
+ module.exports.setGlobalDispatcher = setGlobalDispatcher
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import Undici from './types/index'
2
+ export default Undici
3
+ export * from './types/index'