undici 5.4.0 → 5.5.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 CHANGED
@@ -185,13 +185,12 @@ Help us improve the test coverage by following instructions at [nodejs/undici/#9
185
185
  Basic usage example:
186
186
 
187
187
  ```js
188
- import {fetch} from 'undici';
188
+ import { fetch } from 'undici';
189
189
 
190
- async function fetchJson() {
191
- const res = await fetch('https://example.com')
192
- const json = await res.json()
193
- console.log(json);
194
- }
190
+
191
+ const res = await fetch('https://example.com')
192
+ const json = await res.json()
193
+ console.log(json);
195
194
  ```
196
195
 
197
196
  You can pass an optional dispatcher to `fetch` as:
@@ -235,9 +234,7 @@ const data = {
235
234
  },
236
235
  };
237
236
 
238
- (async () => {
239
- await fetch("https://example.com", { body: data, method: 'POST' });
240
- })();
237
+ await fetch("https://example.com", { body: data, method: 'POST' });
241
238
  ```
242
239
 
243
240
  #### `response.body`
@@ -245,14 +242,12 @@ const data = {
245
242
  Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.
246
243
 
247
244
  ```js
248
- import {fetch} from 'undici';
249
- import {Readable} from 'node:stream';
245
+ import { fetch } from 'undici';
246
+ import { Readable } from 'node:stream';
250
247
 
251
- async function fetchStream() {
252
- const response = await fetch('https://example.com')
253
- const readableWebStream = response.body;
254
- const readableNodeStream = Readable.fromWeb(readableWebStream);
255
- }
248
+ const response = await fetch('https://example.com')
249
+ const readableWebStream = response.body;
250
+ const readableNodeStream = Readable.fromWeb(readableWebStream);
256
251
  ```
257
252
 
258
253
  #### Specification Compliance
@@ -80,13 +80,12 @@ class Request {
80
80
  this.body = null
81
81
  } else if (util.isStream(body)) {
82
82
  this.body = body
83
- } else if (body instanceof DataView) {
84
- // TODO: Why is DataView special?
85
- this.body = body.buffer.byteLength ? Buffer.from(body.buffer) : null
86
- } else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
87
- this.body = body.byteLength ? Buffer.from(body) : null
88
83
  } else if (util.isBuffer(body)) {
89
84
  this.body = body.byteLength ? body : null
85
+ } else if (ArrayBuffer.isView(body)) {
86
+ this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null
87
+ } else if (body instanceof ArrayBuffer) {
88
+ this.body = body.byteLength ? Buffer.from(body) : null
90
89
  } else if (typeof body === 'string') {
91
90
  this.body = body.length ? Buffer.from(body) : null
92
91
  } else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) {
package/lib/fetch/body.js CHANGED
@@ -9,7 +9,7 @@ const { kBodyUsed } = require('../core/symbols')
9
9
  const assert = require('assert')
10
10
  const { NotSupportedError } = require('../core/errors')
11
11
  const { isErrored } = require('../core/util')
12
- const { isUint8Array } = require('util/types')
12
+ const { isUint8Array, isArrayBuffer } = require('util/types')
13
13
 
14
14
  let ReadableStream
15
15
 
@@ -61,7 +61,7 @@ function extractBody (object, keepalive = false) {
61
61
 
62
62
  // Set Content-Type to `application/x-www-form-urlencoded;charset=UTF-8`.
63
63
  contentType = 'application/x-www-form-urlencoded;charset=UTF-8'
64
- } else if (object instanceof ArrayBuffer || ArrayBuffer.isView(object)) {
64
+ } else if (isArrayBuffer(object) || ArrayBuffer.isView(object)) {
65
65
  // BufferSource
66
66
 
67
67
  if (object instanceof DataView) {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { isBlobLike, isFileLike, toUSVString } = require('./util')
3
+ const { isBlobLike, isFileLike, toUSVString, makeIterator } = require('./util')
4
4
  const { kState } = require('./symbols')
5
5
  const { File, FileLike } = require('./file')
6
6
  const { Blob } = require('buffer')
@@ -187,45 +187,68 @@ class FormData {
187
187
  return this.constructor.name
188
188
  }
189
189
 
190
- * entries () {
190
+ entries () {
191
191
  if (!(this instanceof FormData)) {
192
192
  throw new TypeError('Illegal invocation')
193
193
  }
194
194
 
195
- for (const pair of this) {
196
- yield pair
197
- }
195
+ return makeIterator(
196
+ makeIterable(this[kState], 'entries'),
197
+ 'FormData'
198
+ )
198
199
  }
199
200
 
200
- * keys () {
201
+ keys () {
201
202
  if (!(this instanceof FormData)) {
202
203
  throw new TypeError('Illegal invocation')
203
204
  }
204
205
 
205
- for (const [key] of this) {
206
- yield key
206
+ return makeIterator(
207
+ makeIterable(this[kState], 'keys'),
208
+ 'FormData'
209
+ )
210
+ }
211
+
212
+ values () {
213
+ if (!(this instanceof FormData)) {
214
+ throw new TypeError('Illegal invocation')
207
215
  }
216
+
217
+ return makeIterator(
218
+ makeIterable(this[kState], 'values'),
219
+ 'FormData'
220
+ )
208
221
  }
209
222
 
210
- * values () {
223
+ /**
224
+ * @param {(value: string, key: string, self: FormData) => void} callbackFn
225
+ * @param {unknown} thisArg
226
+ */
227
+ forEach (callbackFn, thisArg = globalThis) {
211
228
  if (!(this instanceof FormData)) {
212
229
  throw new TypeError('Illegal invocation')
213
230
  }
214
231
 
215
- for (const [, value] of this) {
216
- yield value
232
+ if (arguments.length < 1) {
233
+ throw new TypeError(
234
+ `Failed to execute 'forEach' on 'FormData': 1 argument required, but only ${arguments.length} present.`
235
+ )
217
236
  }
218
- }
219
237
 
220
- * [Symbol.iterator] () {
221
- // The value pairs to iterate over are this’s entry list’s entries with
222
- // the key being the name and the value being the value.
223
- for (const { name, value } of this[kState]) {
224
- yield [name, value]
238
+ if (typeof callbackFn !== 'function') {
239
+ throw new TypeError(
240
+ "Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'."
241
+ )
242
+ }
243
+
244
+ for (const [key, value] of this) {
245
+ callbackFn.apply(thisArg, [value, key, this])
225
246
  }
226
247
  }
227
248
  }
228
249
 
250
+ FormData.prototype[Symbol.iterator] = FormData.prototype.entries
251
+
229
252
  function makeEntry (name, value, filename) {
230
253
  // To create an entry for name, value, and optionally a filename, run these
231
254
  // steps:
@@ -267,4 +290,18 @@ function makeEntry (name, value, filename) {
267
290
  return entry
268
291
  }
269
292
 
293
+ function * makeIterable (entries, type) {
294
+ // The value pairs to iterate over are this’s entry list’s entries
295
+ // with the key being the name and the value being the value.
296
+ for (const { name, value } of entries) {
297
+ if (type === 'entries') {
298
+ yield [name, value]
299
+ } else if (type === 'values') {
300
+ yield value
301
+ } else {
302
+ yield name
303
+ }
304
+ }
305
+ }
306
+
270
307
  module.exports = { FormData }
@@ -6,6 +6,7 @@ const { validateHeaderName, validateHeaderValue } = require('http')
6
6
  const { kHeadersList } = require('../core/symbols')
7
7
  const { kGuard } = require('./symbols')
8
8
  const { kEnumerableProperty } = require('../core/util')
9
+ const { makeIterator } = require('./util')
9
10
 
10
11
  const kHeadersMap = Symbol('headers map')
11
12
  const kHeadersSortedMap = Symbol('headers map sorted')
@@ -73,33 +74,6 @@ function fill (headers, object) {
73
74
  }
74
75
  }
75
76
 
76
- // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
77
- const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))
78
-
79
- // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
80
- function makeHeadersIterator (iterator) {
81
- const i = {
82
- next () {
83
- if (Object.getPrototypeOf(this) !== i) {
84
- throw new TypeError(
85
- '\'next\' called on an object that does not implement interface Headers Iterator.'
86
- )
87
- }
88
-
89
- return iterator.next()
90
- },
91
- // The class string of an iterator prototype object for a given interface is the
92
- // result of concatenating the identifier of the interface and the string " Iterator".
93
- [Symbol.toStringTag]: 'Headers Iterator'
94
- }
95
-
96
- // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
97
- Object.setPrototypeOf(i, esIteratorPrototype)
98
- // esIteratorPrototype needs to be the prototype of i
99
- // which is the prototype of an empty object. Yes, it's confusing.
100
- return Object.setPrototypeOf({}, i)
101
- }
102
-
103
77
  class HeadersList {
104
78
  constructor (init) {
105
79
  if (init instanceof HeadersList) {
@@ -306,7 +280,7 @@ class Headers {
306
280
  throw new TypeError('Illegal invocation')
307
281
  }
308
282
 
309
- return makeHeadersIterator(this[kHeadersSortedMap].keys())
283
+ return makeIterator(this[kHeadersSortedMap].keys(), 'Headers')
310
284
  }
311
285
 
312
286
  values () {
@@ -314,7 +288,7 @@ class Headers {
314
288
  throw new TypeError('Illegal invocation')
315
289
  }
316
290
 
317
- return makeHeadersIterator(this[kHeadersSortedMap].values())
291
+ return makeIterator(this[kHeadersSortedMap].values(), 'Headers')
318
292
  }
319
293
 
320
294
  entries () {
@@ -322,7 +296,7 @@ class Headers {
322
296
  throw new TypeError('Illegal invocation')
323
297
  }
324
298
 
325
- return makeHeadersIterator(this[kHeadersSortedMap].entries())
299
+ return makeIterator(this[kHeadersSortedMap].entries(), 'Headers')
326
300
  }
327
301
 
328
302
  /**
@@ -1164,7 +1164,7 @@ async function httpRedirectFetch (fetchParams, response) {
1164
1164
  if (
1165
1165
  ([301, 302].includes(actualResponse.status) && request.method === 'POST') ||
1166
1166
  (actualResponse.status === 303 &&
1167
- !['GET', 'HEADER'].includes(request.method))
1167
+ !['GET', 'HEAD'].includes(request.method))
1168
1168
  ) {
1169
1169
  // then:
1170
1170
  // 1. Set request’s method to `GET` and request’s body to null.
package/lib/fetch/util.js CHANGED
@@ -361,6 +361,33 @@ function serializeJavascriptValueToJSONString (value) {
361
361
  return result
362
362
  }
363
363
 
364
+ // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
365
+ const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))
366
+
367
+ // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
368
+ function makeIterator (iterator, name) {
369
+ const i = {
370
+ next () {
371
+ if (Object.getPrototypeOf(this) !== i) {
372
+ throw new TypeError(
373
+ `'next' called on an object that does not implement interface ${name} Iterator.`
374
+ )
375
+ }
376
+
377
+ return iterator.next()
378
+ },
379
+ // The class string of an iterator prototype object for a given interface is the
380
+ // result of concatenating the identifier of the interface and the string " Iterator".
381
+ [Symbol.toStringTag]: `${name} Iterator`
382
+ }
383
+
384
+ // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
385
+ Object.setPrototypeOf(i, esIteratorPrototype)
386
+ // esIteratorPrototype needs to be the prototype of i
387
+ // which is the prototype of an empty object. Yes, it's confusing.
388
+ return Object.setPrototypeOf({}, i)
389
+ }
390
+
364
391
  module.exports = {
365
392
  isAborted,
366
393
  isCancelled,
@@ -390,5 +417,6 @@ module.exports = {
390
417
  isValidReasonPhrase,
391
418
  sameOrigin,
392
419
  normalizeMethod,
393
- serializeJavascriptValueToJSONString
420
+ serializeJavascriptValueToJSONString,
421
+ makeIterator
394
422
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "5.4.0",
3
+ "version": "5.5.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": {
@@ -1,16 +1,15 @@
1
- import { URL } from 'url'
2
- import { TLSSocket, TlsOptions } from 'tls'
3
- import { Socket } from 'net'
1
+ import {TLSSocket, ConnectionOptions} from 'tls'
2
+ import {IpcNetConnectOpts, Socket, TcpNetConnectOpts} from 'net'
4
3
 
5
4
  export = buildConnector
6
5
  declare function buildConnector (options?: buildConnector.BuildOptions): typeof buildConnector.connector
7
6
 
8
7
  declare namespace buildConnector {
9
- export interface BuildOptions extends TlsOptions {
8
+ export type BuildOptions = (ConnectionOptions | TcpNetConnectOpts | IpcNetConnectOpts) & {
10
9
  maxCachedSessions?: number | null;
11
10
  socketPath?: string | null;
12
11
  timeout?: number | null;
13
- servername?: string | null;
12
+ port?: number;
14
13
  }
15
14
 
16
15
  export interface Options {
package/types/fetch.d.ts CHANGED
@@ -38,21 +38,21 @@ export interface BodyMixin {
38
38
  readonly text: () => Promise<string>
39
39
  }
40
40
 
41
- export interface HeadersIterator<T, TReturn = any, TNext = undefined> {
41
+ export interface SpecIterator<T, TReturn = any, TNext = undefined> {
42
42
  next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
43
43
  }
44
44
 
45
- export interface HeadersIterableIterator<T> extends HeadersIterator<T> {
46
- [Symbol.iterator](): HeadersIterableIterator<T>;
45
+ export interface SpecIterableIterator<T> extends SpecIterator<T> {
46
+ [Symbol.iterator](): SpecIterableIterator<T>;
47
47
  }
48
48
 
49
- export interface HeadersIterable<T> {
50
- [Symbol.iterator](): HeadersIterator<T>;
49
+ export interface SpecIterable<T> {
50
+ [Symbol.iterator](): SpecIterator<T>;
51
51
  }
52
52
 
53
53
  export type HeadersInit = string[][] | Record<string, string | ReadonlyArray<string>> | Headers
54
54
 
55
- export declare class Headers implements HeadersIterable<[string, string]> {
55
+ export declare class Headers implements SpecIterable<[string, string]> {
56
56
  constructor (init?: HeadersInit)
57
57
  readonly append: (name: string, value: string) => void
58
58
  readonly delete: (name: string) => void
@@ -64,10 +64,10 @@ export declare class Headers implements HeadersIterable<[string, string]> {
64
64
  thisArg?: unknown
65
65
  ) => void
66
66
 
67
- readonly keys: () => HeadersIterableIterator<string>
68
- readonly values: () => HeadersIterableIterator<string>
69
- readonly entries: () => HeadersIterableIterator<[string, string]>
70
- readonly [Symbol.iterator]: () => HeadersIterator<[string, string]>
67
+ readonly keys: () => SpecIterableIterator<string>
68
+ readonly values: () => SpecIterableIterator<string>
69
+ readonly entries: () => SpecIterableIterator<[string, string]>
70
+ readonly [Symbol.iterator]: () => SpecIterator<[string, string]>
71
71
  }
72
72
 
73
73
  export type RequestCache =
@@ -2,6 +2,7 @@
2
2
  /// <reference types="node" />
3
3
 
4
4
  import { File } from './file'
5
+ import { SpecIterator, SpecIterableIterator } from './fetch'
5
6
 
6
7
  /**
7
8
  * A `string` or `File` that represents a single value from a set of `FormData` key-value pairs.
@@ -73,32 +74,35 @@ export declare class FormData {
73
74
  delete(name: string): void
74
75
 
75
76
  /**
76
- * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object.
77
- * Each key is a `string`.
77
+ * Executes given callback function for each field of the FormData instance
78
78
  */
79
- keys(): Generator<string>
79
+ forEach: (
80
+ callbackfn: (value: FormDataEntryValue, key: string, iterable: FormData) => void,
81
+ thisArg?: unknown
82
+ ) => void
80
83
 
81
84
  /**
82
- * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs.
83
- * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).
85
+ * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object.
86
+ * Each key is a `string`.
84
87
  */
85
- entries(): Generator<[string, FormDataEntryValue]>
88
+ keys: () => SpecIterableIterator<string>
86
89
 
87
90
  /**
88
91
  * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all values contained in this object `FormData` object.
89
92
  * Each value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).
90
93
  */
91
- values(): Generator<FormDataEntryValue>
94
+ values: () => SpecIterableIterator<FormDataEntryValue>
92
95
 
93
96
  /**
94
- * An alias for FormData#entries()
97
+ * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs.
98
+ * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue).
95
99
  */
96
- [Symbol.iterator](): Generator<[string, FormDataEntryValue], void>
100
+ entries: () => SpecIterableIterator<[string, FormDataEntryValue]>
97
101
 
98
102
  /**
99
- * Executes given callback function for each field of the FormData instance
103
+ * An alias for FormData#entries()
100
104
  */
101
- forEach(callback: (value: FormDataEntryValue, key: string, formData: FormData) => void, thisArg?: unknown): void
105
+ [Symbol.iterator]: () => SpecIterableIterator<[string, FormDataEntryValue]>
102
106
 
103
107
  readonly [Symbol.toStringTag]: string
104
108
  }