undici 7.24.6 → 7.24.8

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.
@@ -364,7 +364,7 @@ client.dispatch({
364
364
 
365
365
  ### `Dispatcher.pipeline(options, handler)`
366
366
 
367
- For easy use with [stream.pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback). The `handler` argument should return a `Readable` from which the result will be read. Usually it should just return the `body` argument unless some kind of transformation needs to be performed based on e.g. `headers` or `statusCode`. The `handler` should validate the response and save any required state. If there is an error, it should be thrown. The function returns a `Duplex` which writes to the request and reads from the response.
367
+ For easy use with [stream.pipeline](https://nodejs.org/api/stream.html#streampipelinesource-transforms-destination-options). The `handler` argument should return a `Readable` from which the result will be read. Usually it should just return the `body` argument unless some kind of transformation needs to be performed based on e.g. `headers` or `statusCode`. The `handler` should validate the response and save any required state. If there is an error, it should be thrown. The function returns a `Duplex` which writes to the request and reads from the response.
368
368
 
369
369
  Arguments:
370
370
 
@@ -963,7 +963,7 @@ const { Client, interceptors } = require("undici");
963
963
  const { redirect } = interceptors;
964
964
 
965
965
  const client = new Client("http://service.example").compose(
966
- redirect({ maxRedirections: 3, throwOnMaxRedirects: true })
966
+ redirect({ maxRedirections: 3, throwOnMaxRedirect: true })
967
967
  );
968
968
  client.request({ path: "/" })
969
969
  ```
@@ -1036,10 +1036,10 @@ The `dns` interceptor enables you to cache DNS lookups for a given duration, per
1036
1036
  - `dualStack` - Whether to resolve both IPv4 and IPv6 addresses. Default: `true`.
1037
1037
  - It will also attempt a happy-eyeballs-like approach to connect to the available addresses in case of a connection failure.
1038
1038
  - `affinity` - Whether to use IPv4 or IPv6 addresses. Default: `4`.
1039
- - It can be either `'4` or `6`.
1039
+ - It can be either `4` or `6`.
1040
1040
  - It will only take effect if `dualStack` is `false`.
1041
1041
  - `lookup: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void` - Custom lookup function. Default: `dns.lookup`.
1042
- - For more info see [dns.lookup](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback).
1042
+ - For more info see [dns.lookup](https://nodejs.org/api/dns.html#dnslookuphostname-options-callback).
1043
1043
  - `pick: (origin: URL, records: DNSInterceptorRecords, affinity: 4 | 6) => DNSInterceptorRecord` - Custom pick function. Default: `RoundRobin`.
1044
1044
  - The function should return a single record from the records array.
1045
1045
  - By default a simplified version of Round Robin is used.
@@ -2,7 +2,7 @@
2
2
 
3
3
  A class that handles redirection logic for HTTP requests.
4
4
 
5
- ## `new RedirectHandler(dispatch, maxRedirections, opts, handler, redirectionLimitReached)`
5
+ ## `new RedirectHandler(dispatch, maxRedirections, opts, handler)`
6
6
 
7
7
  Arguments:
8
8
 
@@ -10,7 +10,6 @@ Arguments:
10
10
  - **maxRedirections** `number` - Maximum number of redirections allowed.
11
11
  - **opts** `object` - Options for handling redirection.
12
12
  - **handler** `object` - An object containing handlers for different stages of the request lifecycle.
13
- - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
14
13
 
15
14
  Returns: `RedirectHandler`
16
15
 
@@ -20,7 +19,6 @@ Returns: `RedirectHandler`
20
19
  - **maxRedirections** `number` (required) - Maximum number of redirections allowed.
21
20
  - **opts** `object` (required) - Options for handling redirection.
22
21
  - **handler** `object` (required) - Handlers for different stages of the request lifecycle.
23
- - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior.
24
22
 
25
23
  ### Properties
26
24
 
@@ -30,7 +28,6 @@ Returns: `RedirectHandler`
30
28
  - **maxRedirections** `number` - Maximum number of redirections allowed.
31
29
  - **handler** `object` - Handlers for different stages of the request lifecycle.
32
30
  - **history** `Array` - An array representing the history of URLs during redirection.
33
- - **redirectionLimitReached** `boolean` - Indicates whether the redirection limit has been reached.
34
31
 
35
32
  ### Methods
36
33
 
@@ -1644,12 +1644,25 @@ async function httpNetworkOrCacheFetch (
1644
1644
  // 14. If response’s status is 401, httpRequest’s response tainting is not "cors",
1645
1645
  // includeCredentials is true, and request’s traversable for user prompts is
1646
1646
  // a traversable navigable:
1647
- if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) {
1647
+ //
1648
+ // In Node.js there is no traversable navigable to prompt the user, but we
1649
+ // still need to handle URL-embedded credentials so authentication retries
1650
+ // for WebSocket handshakes continue to work.
1651
+ if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && (
1652
+ request.useURLCredentials !== undefined ||
1653
+ isTraversableNavigable(request.traversableForUserPrompts)
1654
+ )) {
1648
1655
  // 2. If request’s body is non-null, then:
1649
1656
  if (request.body != null) {
1650
1657
  // 1. If request’s body’s source is null, then return a network error.
1651
1658
  if (request.body.source == null) {
1652
- return makeNetworkError('expected non-null body source')
1659
+ // Note: In Node.js, this code path should not be reached because
1660
+ // isTraversableNavigable() returns false for non-navigable contexts.
1661
+ // However, we handle it gracefully by returning the response instead of
1662
+ // a network error, as we won't actually retry the request.
1663
+ // This aligns with the Fetch spec discussion in whatwg/fetch#1132,
1664
+ // which allows implementations flexibility when credentials can't be obtained.
1665
+ return response
1653
1666
  }
1654
1667
 
1655
1668
  // 2. Set request’s body to the body of the result of safely extracting
@@ -2187,7 +2200,15 @@ async function httpNetworkFetch (
2187
2200
  const headersList = new HeadersList()
2188
2201
 
2189
2202
  for (let i = 0; i < rawHeaders.length; i += 2) {
2190
- headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
2203
+ const nameStr = bufferToLowerCasedHeaderName(rawHeaders[i])
2204
+ const value = rawHeaders[i + 1]
2205
+ if (Array.isArray(value) && !Buffer.isBuffer(rawHeaders[i + 1])) {
2206
+ for (const val of value) {
2207
+ headersList.append(nameStr, val.toString('latin1'), true)
2208
+ }
2209
+ } else {
2210
+ headersList.append(nameStr, value.toString('latin1'), true)
2211
+ }
2191
2212
  }
2192
2213
  const location = headersList.get('location', true)
2193
2214
 
@@ -2356,7 +2377,15 @@ async function httpNetworkFetch (
2356
2377
  const headersList = new HeadersList()
2357
2378
 
2358
2379
  for (let i = 0; i < rawHeaders.length; i += 2) {
2359
- headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true)
2380
+ const nameStr = bufferToLowerCasedHeaderName(rawHeaders[i])
2381
+ const value = rawHeaders[i + 1]
2382
+ if (Array.isArray(value) && !Buffer.isBuffer(rawHeaders[i + 1])) {
2383
+ for (const val of value) {
2384
+ headersList.append(nameStr, val.toString('latin1'), true)
2385
+ }
2386
+ } else {
2387
+ headersList.append(nameStr, value.toString('latin1'), true)
2388
+ }
2360
2389
  }
2361
2390
 
2362
2391
  resolve({
@@ -1447,8 +1447,10 @@ function includesCredentials (url) {
1447
1447
  * @param {object|string} navigable
1448
1448
  */
1449
1449
  function isTraversableNavigable (navigable) {
1450
- // TODO
1451
- return true
1450
+ // Returns true only if we have an actual traversable navigable object
1451
+ // that can prompt the user for credentials. In Node.js, this will always
1452
+ // be false since there's no Window object or navigable.
1453
+ return navigable != null && navigable !== 'client' && navigable !== 'no-traversable'
1452
1454
  }
1453
1455
 
1454
1456
  class EnvironmentSettingsObjectBase {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "7.24.6",
3
+ "version": "7.24.8",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
@@ -137,8 +137,6 @@ declare namespace Dispatcher {
137
137
  signal?: AbortSignal | EventEmitter | null;
138
138
  /** This argument parameter is passed through to `ConnectData` */
139
139
  opaque?: TOpaque;
140
- /** Default: false */
141
- redirectionLimitReached?: boolean;
142
140
  /** Default: `null` */
143
141
  responseHeaders?: 'raw' | null;
144
142
  }
@@ -147,8 +145,6 @@ declare namespace Dispatcher {
147
145
  opaque?: TOpaque;
148
146
  /** Default: `null` */
149
147
  signal?: AbortSignal | EventEmitter | null;
150
- /** Default: false */
151
- redirectionLimitReached?: boolean;
152
148
  /** Default: `null` */
153
149
  onInfo?: (info: { statusCode: number, headers: Record<string, string | string[]> }) => void;
154
150
  /** Default: `null` */
@@ -170,8 +166,6 @@ declare namespace Dispatcher {
170
166
  protocol?: string;
171
167
  /** Default: `null` */
172
168
  signal?: AbortSignal | EventEmitter | null;
173
- /** Default: false */
174
- redirectionLimitReached?: boolean;
175
169
  /** Default: `null` */
176
170
  responseHeaders?: 'raw' | null;
177
171
  }
package/types/fetch.d.ts CHANGED
@@ -60,12 +60,32 @@ export interface SpecIterator<T, TReturn = any, TNext = undefined> {
60
60
  next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
61
61
  }
62
62
 
63
- export interface SpecIterableIterator<T> extends SpecIterator<T> {
63
+ export interface SpecIteratorObject<T, TReturn = undefined, TNext = unknown> extends SpecIterator<T, TReturn, TNext> {
64
+ [Symbol.iterator](): SpecIteratorObject<T, TReturn, TNext>;
65
+ map<U>(callbackfn: (value: T, index: number) => U): SpecIteratorObject<U>;
66
+ filter<S extends T>(predicate: (value: T, index: number) => value is S): SpecIteratorObject<S>;
67
+ filter(predicate: (value: T, index: number) => unknown): SpecIteratorObject<T>;
68
+ take(limit: number): SpecIteratorObject<T>;
69
+ drop(count: number): SpecIteratorObject<T>;
70
+ flatMap<U>(callbackfn: (value: T, index: number) => Iterator<U> | Iterable<U>): SpecIteratorObject<U>;
71
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T): T;
72
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T, initialValue: T): T;
73
+ reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U;
74
+ toArray(): T[];
75
+ forEach(callbackfn: (value: T, index: number) => void): void;
76
+ some(predicate: (value: T, index: number) => unknown): boolean;
77
+ every(predicate: (value: T, index: number) => unknown): boolean;
78
+ find<S extends T>(predicate: (value: T, index: number) => value is S): S | undefined;
79
+ find(predicate: (value: T, index: number) => unknown): T | undefined;
80
+ readonly [Symbol.toStringTag]: string;
81
+ }
82
+
83
+ export interface SpecIterableIterator<T> extends SpecIteratorObject<T> {
64
84
  [Symbol.iterator](): SpecIterableIterator<T>;
65
85
  }
66
86
 
67
87
  export interface SpecIterable<T> {
68
- [Symbol.iterator](): SpecIterator<T>;
88
+ [Symbol.iterator](): SpecIterableIterator<T>;
69
89
  }
70
90
 
71
91
  export type HeadersInit = [string, string][] | HeaderRecord | Headers
@@ -173,7 +193,7 @@ export declare class Request extends BodyMixin {
173
193
  readonly signal: AbortSignal
174
194
  readonly duplex: RequestDuplex
175
195
 
176
- readonly clone: () => Request
196
+ public clone (): Request
177
197
  }
178
198
 
179
199
  export interface ResponseInit {
@@ -203,7 +223,7 @@ export declare class Response extends BodyMixin {
203
223
  readonly url: string
204
224
  readonly redirected: boolean
205
225
 
206
- readonly clone: () => Response
226
+ public clone (): Response
207
227
 
208
228
  static error (): Response
209
229
  static json (data: any, init?: ResponseInit): Response
@@ -4,6 +4,12 @@
4
4
  import { File } from 'node:buffer'
5
5
  import { SpecIterableIterator } from './fetch'
6
6
 
7
+ declare module 'node:buffer' {
8
+ interface File {
9
+ readonly [Symbol.toStringTag]: string
10
+ }
11
+ }
12
+
7
13
  /**
8
14
  * A `string` or `File` that represents a single value from a set of `FormData` key-value pairs.
9
15
  */
@@ -5,8 +5,7 @@ export declare class RedirectHandler implements Dispatcher.DispatchHandler {
5
5
  dispatch: Dispatcher.Dispatch,
6
6
  maxRedirections: number,
7
7
  opts: Dispatcher.DispatchOptions,
8
- handler: Dispatcher.DispatchHandler,
9
- redirectionLimitReached: boolean
8
+ handler: Dispatcher.DispatchHandler
10
9
  )
11
10
  }
12
11