undici 7.9.0 → 7.11.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 (66) hide show
  1. package/README.md +157 -0
  2. package/docs/docs/api/CacheStore.md +23 -3
  3. package/docs/docs/api/Debug.md +13 -13
  4. package/docs/docs/api/DiagnosticsChannel.md +25 -0
  5. package/docs/docs/api/Dispatcher.md +20 -1
  6. package/docs/docs/api/GlobalInstallation.md +91 -0
  7. package/docs/docs/api/MockClient.md +4 -0
  8. package/docs/docs/api/MockPool.md +6 -0
  9. package/docs/docs/api/Pool.md +1 -0
  10. package/docs/docs/api/ProxyAgent.md +3 -0
  11. package/docs/docs/api/RetryAgent.md +6 -1
  12. package/docs/docs/api/RetryHandler.md +1 -0
  13. package/index.js +15 -0
  14. package/lib/api/api-stream.js +1 -1
  15. package/lib/cache/memory-cache-store.js +42 -4
  16. package/lib/cache/sqlite-cache-store.js +1 -1
  17. package/lib/core/connect.js +21 -51
  18. package/lib/core/diagnostics.js +6 -4
  19. package/lib/core/request.js +6 -0
  20. package/lib/core/util.js +0 -45
  21. package/lib/dispatcher/agent.js +25 -15
  22. package/lib/dispatcher/client-h1.js +1 -1
  23. package/lib/dispatcher/pool.js +17 -3
  24. package/lib/dispatcher/proxy-agent.js +90 -3
  25. package/lib/handler/retry-handler.js +110 -56
  26. package/lib/mock/mock-agent.js +8 -8
  27. package/lib/mock/mock-client.js +4 -0
  28. package/lib/mock/mock-pool.js +4 -0
  29. package/lib/util/cache.js +11 -1
  30. package/lib/util/timers.js +11 -9
  31. package/lib/web/cache/cache.js +1 -1
  32. package/lib/web/cache/cachestorage.js +1 -1
  33. package/lib/web/cookies/index.js +1 -1
  34. package/lib/web/eventsource/eventsource.js +3 -6
  35. package/lib/web/eventsource/util.js +1 -1
  36. package/lib/web/fetch/body.js +2 -2
  37. package/lib/web/fetch/dispatcher-weakref.js +0 -41
  38. package/lib/web/fetch/formdata-parser.js +4 -4
  39. package/lib/web/fetch/formdata.js +1 -1
  40. package/lib/web/fetch/headers.js +1 -1
  41. package/lib/web/fetch/index.js +7 -1
  42. package/lib/web/fetch/request.js +1 -1
  43. package/lib/web/fetch/response.js +1 -1
  44. package/lib/web/fetch/util.js +2 -2
  45. package/lib/web/{fetch/webidl.js → webidl/index.js} +57 -9
  46. package/lib/web/websocket/connection.js +4 -3
  47. package/lib/web/websocket/events.js +1 -1
  48. package/lib/web/websocket/frame.js +2 -1
  49. package/lib/web/websocket/stream/websocketerror.js +1 -1
  50. package/lib/web/websocket/stream/websocketstream.js +1 -1
  51. package/lib/web/websocket/websocket.js +4 -4
  52. package/package.json +4 -4
  53. package/types/diagnostics-channel.d.ts +9 -0
  54. package/types/dispatcher.d.ts +3 -2
  55. package/types/env-http-proxy-agent.d.ts +2 -1
  56. package/types/eventsource.d.ts +3 -3
  57. package/types/fetch.d.ts +1 -0
  58. package/types/handlers.d.ts +1 -1
  59. package/types/mock-client.d.ts +2 -0
  60. package/types/mock-interceptor.d.ts +2 -0
  61. package/types/mock-pool.d.ts +2 -0
  62. package/types/pool.d.ts +2 -0
  63. package/types/proxy-agent.d.ts +1 -0
  64. package/types/retry-handler.d.ts +9 -0
  65. package/types/webidl.d.ts +19 -15
  66. package/types/websocket.d.ts +1 -1
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { types, inspect } = require('node:util')
4
4
  const { markAsUncloneable } = require('node:worker_threads')
5
- const { toUSVString } = require('../../core/util')
6
5
 
7
6
  const UNDEFINED = 1
8
7
  const BOOLEAN = 2
@@ -23,22 +22,48 @@ const webidl = {
23
22
  is: {}
24
23
  }
25
24
 
25
+ /**
26
+ * @description Instantiate an error.
27
+ *
28
+ * @param {Object} opts
29
+ * @param {string} opts.header
30
+ * @param {string} opts.message
31
+ * @returns {TypeError}
32
+ */
26
33
  webidl.errors.exception = function (message) {
27
34
  return new TypeError(`${message.header}: ${message.message}`)
28
35
  }
29
36
 
30
- webidl.errors.conversionFailed = function (context) {
31
- const plural = context.types.length === 1 ? '' : ' one of'
37
+ /**
38
+ * @description Instantiate an error when conversion from one type to another has failed.
39
+ *
40
+ * @param {Object} opts
41
+ * @param {string} opts.prefix
42
+ * @param {string} opts.argument
43
+ * @param {string[]} opts.types
44
+ * @returns {TypeError}
45
+ */
46
+ webidl.errors.conversionFailed = function (opts) {
47
+ const plural = opts.types.length === 1 ? '' : ' one of'
32
48
  const message =
33
- `${context.argument} could not be converted to` +
34
- `${plural}: ${context.types.join(', ')}.`
49
+ `${opts.argument} could not be converted to` +
50
+ `${plural}: ${opts.types.join(', ')}.`
35
51
 
36
52
  return webidl.errors.exception({
37
- header: context.prefix,
53
+ header: opts.prefix,
38
54
  message
39
55
  })
40
56
  }
41
57
 
58
+ /**
59
+ * @description Instantiate an error when an invalid argument is provided
60
+ *
61
+ * @param {Object} context
62
+ * @param {string} context.prefix
63
+ * @param {string} context.value
64
+ * @param {string} context.type
65
+ * @returns {TypeError}
66
+ */
42
67
  webidl.errors.invalidArgument = function (context) {
43
68
  return webidl.errors.exception({
44
69
  header: context.prefix,
@@ -278,6 +303,8 @@ webidl.util.Stringify = function (V) {
278
303
  return inspect(V)
279
304
  case STRING:
280
305
  return `"${V}"`
306
+ case BIGINT:
307
+ return `${V}n`
281
308
  default:
282
309
  return `${V}`
283
310
  }
@@ -468,6 +495,17 @@ webidl.nullableConverter = function (converter) {
468
495
  }
469
496
  }
470
497
 
498
+ /**
499
+ * @param {*} value
500
+ * @returns {boolean}
501
+ */
502
+ webidl.is.USVString = function (value) {
503
+ return (
504
+ typeof value === 'string' &&
505
+ value.isWellFormed()
506
+ )
507
+ }
508
+
471
509
  webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream)
472
510
  webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob)
473
511
  webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams)
@@ -529,13 +567,23 @@ webidl.converters.ByteString = function (V, prefix, argument) {
529
567
  return x
530
568
  }
531
569
 
532
- // https://webidl.spec.whatwg.org/#es-USVString
533
- // TODO: rewrite this so we can control the errors thrown
534
- webidl.converters.USVString = toUSVString
570
+ /**
571
+ * @param {unknown} value
572
+ * @returns {string}
573
+ * @see https://webidl.spec.whatwg.org/#es-USVString
574
+ */
575
+ webidl.converters.USVString = function (value) {
576
+ // TODO: rewrite this so we can control the errors thrown
577
+ if (typeof value === 'string') {
578
+ return value.toWellFormed()
579
+ }
580
+ return `${value}`.toWellFormed()
581
+ }
535
582
 
536
583
  // https://webidl.spec.whatwg.org/#es-boolean
537
584
  webidl.converters.boolean = function (V) {
538
585
  // 1. Let x be the result of computing ToBoolean(V).
586
+ // https://262.ecma-international.org/10.0/index.html#table-10
539
587
  const x = Boolean(V)
540
588
 
541
589
  // 2. Return the IDL boolean value that is the one that represents
@@ -105,7 +105,7 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
105
105
  // 1. If response is a network error or its status is not 101,
106
106
  // fail the WebSocket connection.
107
107
  if (response.type === 'error' || response.status !== 101) {
108
- failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.')
108
+ failWebsocketConnection(handler, 1002, 'Received network error or non-101 status code.', response.error)
109
109
  return
110
110
  }
111
111
 
@@ -298,9 +298,10 @@ function closeWebSocketConnection (object, code, reason, validate = false) {
298
298
  * @param {import('./websocket').Handler} handler
299
299
  * @param {number} code
300
300
  * @param {string|undefined} reason
301
+ * @param {unknown} cause
301
302
  * @returns {void}
302
303
  */
303
- function failWebsocketConnection (handler, code, reason) {
304
+ function failWebsocketConnection (handler, code, reason, cause) {
304
305
  // If _The WebSocket Connection is Established_ prior to the point where
305
306
  // the endpoint is required to _Fail the WebSocket Connection_, the
306
307
  // endpoint SHOULD send a Close frame with an appropriate status code
@@ -315,7 +316,7 @@ function failWebsocketConnection (handler, code, reason) {
315
316
  handler.socket.destroy()
316
317
  }
317
318
 
318
- handler.onFail(code, reason)
319
+ handler.onFail(code, reason, cause)
319
320
  }
320
321
 
321
322
  module.exports = {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { webidl } = require('../fetch/webidl')
3
+ const { webidl } = require('../webidl')
4
4
  const { kEnumerableProperty } = require('../../core/util')
5
5
  const { kConstruct } = require('../../core/symbols')
6
6
 
@@ -134,5 +134,6 @@ class WebsocketFrameSend {
134
134
  }
135
135
 
136
136
  module.exports = {
137
- WebsocketFrameSend
137
+ WebsocketFrameSend,
138
+ generateMask // for benchmark
138
139
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { webidl } = require('../../fetch/webidl')
3
+ const { webidl } = require('../../webidl')
4
4
  const { validateCloseCodeAndReason } = require('../util')
5
5
  const { kConstruct } = require('../../../core/symbols')
6
6
  const { kEnumerableProperty } = require('../../../core/util')
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { createDeferredPromise, environmentSettingsObject } = require('../../fetch/util')
4
4
  const { states, opcodes, sentCloseFrameState } = require('../constants')
5
- const { webidl } = require('../../fetch/webidl')
5
+ const { webidl } = require('../../webidl')
6
6
  const { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')
7
7
  const { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')
8
8
  const { types } = require('node:util')
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { webidl } = require('../fetch/webidl')
3
+ const { webidl } = require('../webidl')
4
4
  const { URLSerializer } = require('../fetch/data-url')
5
5
  const { environmentSettingsObject } = require('../fetch/util')
6
6
  const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes } = require('./constants')
@@ -60,7 +60,7 @@ class WebSocket extends EventTarget {
60
60
  /** @type {Handler} */
61
61
  #handler = {
62
62
  onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
63
- onFail: (code, reason) => this.#onFail(code, reason),
63
+ onFail: (code, reason, cause) => this.#onFail(code, reason, cause),
64
64
  onMessage: (opcode, data) => this.#onMessage(opcode, data),
65
65
  onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
66
66
  onParserDrain: () => this.#onParserDrain(),
@@ -462,11 +462,11 @@ class WebSocket extends EventTarget {
462
462
  fireEvent('open', this)
463
463
  }
464
464
 
465
- #onFail (code, reason) {
465
+ #onFail (code, reason, cause) {
466
466
  if (reason) {
467
467
  // TODO: process.nextTick
468
468
  fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {
469
- error: new Error(reason),
469
+ error: new Error(reason, cause ? { cause } : undefined),
470
470
  message: reason
471
471
  })
472
472
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "7.9.0",
3
+ "version": "7.11.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": {
@@ -112,20 +112,20 @@
112
112
  "@sinonjs/fake-timers": "^12.0.0",
113
113
  "@types/node": "^18.19.50",
114
114
  "abort-controller": "^3.0.0",
115
- "borp": "^0.19.0",
115
+ "borp": "^0.20.0",
116
116
  "c8": "^10.0.0",
117
117
  "cross-env": "^7.0.3",
118
118
  "dns-packet": "^5.4.0",
119
119
  "esbuild": "^0.25.2",
120
120
  "eslint": "^9.9.0",
121
- "fast-check": "^3.17.1",
121
+ "fast-check": "^4.1.1",
122
122
  "https-pem": "^3.0.0",
123
123
  "husky": "^9.0.7",
124
124
  "jest": "^29.0.2",
125
125
  "neostandard": "^0.12.0",
126
126
  "node-forge": "^1.3.1",
127
127
  "proxy": "^2.1.1",
128
- "tsd": "^0.31.2",
128
+ "tsd": "^0.32.0",
129
129
  "typescript": "^5.6.2",
130
130
  "ws": "^8.11.0"
131
131
  },
@@ -31,6 +31,15 @@ declare namespace DiagnosticsChannel {
31
31
  export interface RequestBodySentMessage {
32
32
  request: Request;
33
33
  }
34
+
35
+ export interface RequestBodyChunkSentMessage {
36
+ request: Request;
37
+ chunk: Uint8Array | string;
38
+ }
39
+ export interface RequestBodyChunkReceivedMessage {
40
+ request: Request;
41
+ chunk: Buffer;
42
+ }
34
43
  export interface RequestHeadersMessage {
35
44
  request: Request;
36
45
  response: Response;
@@ -97,7 +97,8 @@ declare class Dispatcher extends EventEmitter {
97
97
 
98
98
  declare namespace Dispatcher {
99
99
  export interface ComposedDispatcher extends Dispatcher {}
100
- export type DispatcherComposeInterceptor = (dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch']
100
+ export type Dispatch = Dispatcher['dispatch']
101
+ export type DispatcherComposeInterceptor = (dispatch: Dispatch) => Dispatch
101
102
  export interface DispatchOptions {
102
103
  origin?: string | URL;
103
104
  path: string;
@@ -276,6 +277,6 @@ declare namespace Dispatcher {
276
277
  }
277
278
 
278
279
  export interface DispatchInterceptor {
279
- (dispatch: Dispatcher['dispatch']): Dispatcher['dispatch']
280
+ (dispatch: Dispatch): Dispatch
280
281
  }
281
282
  }
@@ -1,4 +1,5 @@
1
1
  import Agent from './agent'
2
+ import ProxyAgent from './proxy-agent'
2
3
  import Dispatcher from './dispatcher'
3
4
 
4
5
  export default EnvHttpProxyAgent
@@ -10,7 +11,7 @@ declare class EnvHttpProxyAgent extends Dispatcher {
10
11
  }
11
12
 
12
13
  declare namespace EnvHttpProxyAgent {
13
- export interface Options extends Agent.Options {
14
+ export interface Options extends Omit<ProxyAgent.Options, 'uri'> {
14
15
  /** Overrides the value of the HTTP_PROXY environment variable */
15
16
  httpProxy?: string;
16
17
  /** Overrides the value of the HTTPS_PROXY environment variable */
@@ -18,9 +18,9 @@ interface EventSource extends EventTarget {
18
18
  readonly CLOSED: 2
19
19
  readonly CONNECTING: 0
20
20
  readonly OPEN: 1
21
- onerror: (this: EventSource, ev: ErrorEvent) => any
22
- onmessage: (this: EventSource, ev: MessageEvent) => any
23
- onopen: (this: EventSource, ev: Event) => any
21
+ onerror: ((this: EventSource, ev: ErrorEvent) => any) | null
22
+ onmessage: ((this: EventSource, ev: MessageEvent) => any) | null
23
+ onopen: ((this: EventSource, ev: Event) => any) | null
24
24
  readonly readyState: 0 | 1 | 2
25
25
  readonly url: string
26
26
  readonly withCredentials: boolean
package/types/fetch.d.ts CHANGED
@@ -33,6 +33,7 @@ export class BodyMixin {
33
33
 
34
34
  readonly arrayBuffer: () => Promise<ArrayBuffer>
35
35
  readonly blob: () => Promise<Blob>
36
+ readonly bytes: () => Promise<Uint8Array>
36
37
  /**
37
38
  * @deprecated This method is not recommended for parsing multipart/form-data bodies in server environments.
38
39
  * It is recommended to use a library such as [@fastify/busboy](https://www.npmjs.com/package/@fastify/busboy) as follows:
@@ -2,7 +2,7 @@ import Dispatcher from './dispatcher'
2
2
 
3
3
  export declare class RedirectHandler implements Dispatcher.DispatchHandler {
4
4
  constructor (
5
- dispatch: Dispatcher,
5
+ dispatch: Dispatcher.Dispatch,
6
6
  maxRedirections: number,
7
7
  opts: Dispatcher.DispatchOptions,
8
8
  handler: Dispatcher.DispatchHandler,
@@ -14,6 +14,8 @@ declare class MockClient extends Client implements Interceptable {
14
14
  dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandler): boolean
15
15
  /** Closes the mock client and gracefully waits for enqueued requests to complete. */
16
16
  close (): Promise<void>
17
+ /** Clean up all the prepared mocks. */
18
+ cleanMocks (): void
17
19
  }
18
20
 
19
21
  declare namespace MockClient {
@@ -84,6 +84,8 @@ declare namespace MockInterceptor {
84
84
  interface Interceptable extends Dispatcher {
85
85
  /** Intercepts any matching requests that use the same origin as this mock client. */
86
86
  intercept(options: MockInterceptor.Options): MockInterceptor;
87
+ /** Clean up all the prepared mocks. */
88
+ cleanMocks (): void
87
89
  }
88
90
 
89
91
  export {
@@ -14,6 +14,8 @@ declare class MockPool extends Pool implements Interceptable {
14
14
  dispatch (options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandler): boolean
15
15
  /** Closes the mock pool and gracefully waits for enqueued requests to complete. */
16
16
  close (): Promise<void>
17
+ /** Clean up all the prepared mocks. */
18
+ cleanMocks (): void
17
19
  }
18
20
 
19
21
  declare namespace MockPool {
package/types/pool.d.ts CHANGED
@@ -33,6 +33,8 @@ declare namespace Pool {
33
33
  factory?(origin: URL, opts: object): Dispatcher;
34
34
  /** The max number of clients to create. `null` if no limit. Default `null`. */
35
35
  connections?: number | null;
36
+ /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */
37
+ clientTtl?: number | null;
36
38
 
37
39
  interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors']
38
40
  }
@@ -24,5 +24,6 @@ declare namespace ProxyAgent {
24
24
  requestTls?: buildConnector.BuildOptions;
25
25
  proxyTls?: buildConnector.BuildOptions;
26
26
  clientFactory?(origin: URL, opts: object): Dispatcher;
27
+ proxyTunnel?: boolean;
27
28
  }
28
29
  }
@@ -35,6 +35,15 @@ declare namespace RetryHandler {
35
35
  ) => void
36
36
 
37
37
  export interface RetryOptions {
38
+ /**
39
+ * If true, the retry handler will throw an error if the request fails,
40
+ * this will prevent the folling handlers from being called, and will destroy the socket.
41
+ *
42
+ * @type {boolean}
43
+ * @memberof RetryOptions
44
+ * @default true
45
+ */
46
+ throwOnError?: boolean;
38
47
  /**
39
48
  * Callback to be invoked on every retry iteration.
40
49
  * It receives the error, current state of the retry object and the options object
package/types/webidl.d.ts CHANGED
@@ -16,9 +16,12 @@ interface ConvertToIntOpts {
16
16
  }
17
17
 
18
18
  interface WebidlErrors {
19
+ /**
20
+ * @description Instantiate an error
21
+ */
19
22
  exception (opts: { header: string, message: string }): TypeError
20
23
  /**
21
- * @description Throw an error when conversion from one type to another has failed
24
+ * @description Instantiate an error when conversion from one type to another has failed
22
25
  */
23
26
  conversionFailed (opts: {
24
27
  prefix: string
@@ -75,7 +78,7 @@ interface WebidlUtil {
75
78
  ): number
76
79
 
77
80
  /**
78
- * @see https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
81
+ * @see https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
79
82
  */
80
83
  IntegerPart (N: number): number
81
84
 
@@ -182,20 +185,21 @@ interface WebidlConverters {
182
185
  [Key: string]: (...args: any[]) => unknown
183
186
  }
184
187
 
185
- type IsAssertion<T> = (arg: any) => arg is T
188
+ type WebidlIsFunction<T> = (arg: any) => arg is T
186
189
 
187
190
  interface WebidlIs {
188
- Request: IsAssertion<undici.Request>
189
- Response: IsAssertion<undici.Response>
190
- ReadableStream: IsAssertion<ReadableStream>
191
- Blob: IsAssertion<Blob>
192
- URLSearchParams: IsAssertion<URLSearchParams>
193
- File: IsAssertion<File>
194
- FormData: IsAssertion<undici.FormData>
195
- URL: IsAssertion<URL>
196
- WebSocketError: IsAssertion<undici.WebSocketError>
197
- AbortSignal: IsAssertion<AbortSignal>
198
- MessagePort: IsAssertion<MessagePort>
191
+ Request: WebidlIsFunction<undici.Request>
192
+ Response: WebidlIsFunction<undici.Response>
193
+ ReadableStream: WebidlIsFunction<ReadableStream>
194
+ Blob: WebidlIsFunction<Blob>
195
+ URLSearchParams: WebidlIsFunction<URLSearchParams>
196
+ File: WebidlIsFunction<File>
197
+ FormData: WebidlIsFunction<undici.FormData>
198
+ URL: WebidlIsFunction<URL>
199
+ WebSocketError: WebidlIsFunction<undici.WebSocketError>
200
+ AbortSignal: WebidlIsFunction<AbortSignal>
201
+ MessagePort: WebidlIsFunction<MessagePort>
202
+ USVString: WebidlIsFunction<string>
199
203
  }
200
204
 
201
205
  export interface Webidl {
@@ -233,7 +237,7 @@ export interface Webidl {
233
237
  * Similar to {@link Webidl.brandCheck} but allows skipping the check if third party
234
238
  * interfaces are allowed.
235
239
  */
236
- interfaceConverter <Interface>(typeCheck: IsAssertion<Interface>, name: string): (
240
+ interfaceConverter <Interface>(typeCheck: WebidlIsFunction<Interface>, name: string): (
237
241
  V: unknown,
238
242
  prefix: string,
239
243
  argument: string
@@ -136,7 +136,7 @@ interface ErrorEvent extends Event {
136
136
  readonly filename: string
137
137
  readonly lineno: number
138
138
  readonly colno: number
139
- readonly error: any
139
+ readonly error: Error
140
140
  }
141
141
 
142
142
  export declare const ErrorEvent: {