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/lib/util/cache.js
CHANGED
|
@@ -4,6 +4,8 @@ const {
|
|
|
4
4
|
safeHTTPMethods
|
|
5
5
|
} = require('../core/util')
|
|
6
6
|
|
|
7
|
+
const { serializePathWithQuery } = require('../core/util')
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts
|
|
9
11
|
*/
|
|
@@ -12,17 +14,25 @@ function makeCacheKey (opts) {
|
|
|
12
14
|
throw new Error('opts.origin is undefined')
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
let fullPath
|
|
18
|
+
try {
|
|
19
|
+
fullPath = serializePathWithQuery(opts.path || '/', opts.query)
|
|
20
|
+
} catch (error) {
|
|
21
|
+
// If fails (path already has query params), use as-is
|
|
22
|
+
fullPath = opts.path || '/'
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
return {
|
|
16
26
|
origin: opts.origin.toString(),
|
|
17
27
|
method: opts.method,
|
|
18
|
-
path:
|
|
28
|
+
path: fullPath,
|
|
19
29
|
headers: opts.headers
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/**
|
|
24
34
|
* @param {Record<string, string[] | string>}
|
|
25
|
-
* @
|
|
35
|
+
* @returns {Record<string, string[] | string>}
|
|
26
36
|
*/
|
|
27
37
|
function normaliseHeaders (opts) {
|
|
28
38
|
let headers
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @template {*} T
|
|
5
|
+
* @typedef {Object} DeferredPromise
|
|
6
|
+
* @property {Promise<T>} promise
|
|
7
|
+
* @property {(value?: T) => void} resolve
|
|
8
|
+
* @property {(reason?: any) => void} reject
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @template {*} T
|
|
13
|
+
* @returns {DeferredPromise<T>} An object containing a promise and its resolve/reject methods.
|
|
14
|
+
*/
|
|
15
|
+
function createDeferredPromise () {
|
|
16
|
+
let res
|
|
17
|
+
let rej
|
|
18
|
+
const promise = new Promise((resolve, reject) => {
|
|
19
|
+
res = resolve
|
|
20
|
+
rej = reject
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return { promise, resolve: res, reject: rej }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
createDeferredPromise
|
|
28
|
+
}
|
package/lib/util/timers.js
CHANGED
|
@@ -188,19 +188,21 @@ function onTick () {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function refreshTimeout () {
|
|
191
|
-
// If the fastNowTimeout is already set
|
|
192
|
-
|
|
191
|
+
// If the fastNowTimeout is already set and the Timer has the refresh()-
|
|
192
|
+
// method available, call it to refresh the timer.
|
|
193
|
+
// Some timer objects returned by setTimeout may not have a .refresh()
|
|
194
|
+
// method (e.g. mocked timers in tests).
|
|
195
|
+
if (fastNowTimeout?.refresh) {
|
|
193
196
|
fastNowTimeout.refresh()
|
|
194
|
-
|
|
197
|
+
// fastNowTimeout is not instantiated yet or refresh is not availabe,
|
|
198
|
+
// create a new Timer.
|
|
195
199
|
} else {
|
|
196
200
|
clearTimeout(fastNowTimeout)
|
|
197
201
|
fastNowTimeout = setTimeout(onTick, TICK_MS)
|
|
198
|
-
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
fastNowTimeout.unref()
|
|
203
|
-
}
|
|
202
|
+
// If the Timer has an unref method, call it to allow the process to exit,
|
|
203
|
+
// if there are no other active handles. When using fake timers or mocked
|
|
204
|
+
// environments (like Jest), .unref() may not be defined,
|
|
205
|
+
fastNowTimeout?.unref()
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
|
package/lib/web/cache/cache.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const assert = require('node:assert')
|
|
4
|
+
|
|
3
5
|
const { kConstruct } = require('../../core/symbols')
|
|
4
6
|
const { urlEquals, getFieldValues } = require('./util')
|
|
5
7
|
const { kEnumerableProperty, isDisturbed } = require('../../core/util')
|
|
6
|
-
const { webidl } = require('../
|
|
8
|
+
const { webidl } = require('../webidl')
|
|
7
9
|
const { cloneResponse, fromInnerResponse, getResponseState } = require('../fetch/response')
|
|
8
10
|
const { Request, fromInnerRequest, getRequestState } = require('../fetch/request')
|
|
9
11
|
const { fetching } = require('../fetch/index')
|
|
10
|
-
const { urlIsHttpHttpsScheme,
|
|
11
|
-
const
|
|
12
|
+
const { urlIsHttpHttpsScheme, readAllBytes } = require('../fetch/util')
|
|
13
|
+
const { createDeferredPromise } = require('../../util/promise')
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation
|
|
@@ -46,7 +48,7 @@ class Cache {
|
|
|
46
48
|
const prefix = 'Cache.match'
|
|
47
49
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
48
50
|
|
|
49
|
-
request = webidl.converters.RequestInfo(request
|
|
51
|
+
request = webidl.converters.RequestInfo(request)
|
|
50
52
|
options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
|
|
51
53
|
|
|
52
54
|
const p = this.#internalMatchAll(request, options, 1)
|
|
@@ -62,7 +64,7 @@ class Cache {
|
|
|
62
64
|
webidl.brandCheck(this, Cache)
|
|
63
65
|
|
|
64
66
|
const prefix = 'Cache.matchAll'
|
|
65
|
-
if (request !== undefined) request = webidl.converters.RequestInfo(request
|
|
67
|
+
if (request !== undefined) request = webidl.converters.RequestInfo(request)
|
|
66
68
|
options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
|
|
67
69
|
|
|
68
70
|
return this.#internalMatchAll(request, options)
|
|
@@ -74,7 +76,7 @@ class Cache {
|
|
|
74
76
|
const prefix = 'Cache.add'
|
|
75
77
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
76
78
|
|
|
77
|
-
request = webidl.converters.RequestInfo(request
|
|
79
|
+
request = webidl.converters.RequestInfo(request)
|
|
78
80
|
|
|
79
81
|
// 1.
|
|
80
82
|
const requests = [request]
|
|
@@ -262,7 +264,7 @@ class Cache {
|
|
|
262
264
|
const prefix = 'Cache.put'
|
|
263
265
|
webidl.argumentLengthCheck(arguments, 2, prefix)
|
|
264
266
|
|
|
265
|
-
request = webidl.converters.RequestInfo(request
|
|
267
|
+
request = webidl.converters.RequestInfo(request)
|
|
266
268
|
response = webidl.converters.Response(response, prefix, 'response')
|
|
267
269
|
|
|
268
270
|
// 1.
|
|
@@ -393,7 +395,7 @@ class Cache {
|
|
|
393
395
|
const prefix = 'Cache.delete'
|
|
394
396
|
webidl.argumentLengthCheck(arguments, 1, prefix)
|
|
395
397
|
|
|
396
|
-
request = webidl.converters.RequestInfo(request
|
|
398
|
+
request = webidl.converters.RequestInfo(request)
|
|
397
399
|
options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
|
|
398
400
|
|
|
399
401
|
/**
|
|
@@ -458,7 +460,7 @@ class Cache {
|
|
|
458
460
|
|
|
459
461
|
const prefix = 'Cache.keys'
|
|
460
462
|
|
|
461
|
-
if (request !== undefined) request = webidl.converters.RequestInfo(request
|
|
463
|
+
if (request !== undefined) request = webidl.converters.RequestInfo(request)
|
|
462
464
|
options = webidl.converters.CacheQueryOptions(options, prefix, 'options')
|
|
463
465
|
|
|
464
466
|
// 1.
|
package/lib/web/cookies/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { parseSetCookie } = require('./parse')
|
|
4
4
|
const { stringify } = require('./util')
|
|
5
|
-
const { webidl } = require('../
|
|
5
|
+
const { webidl } = require('../webidl')
|
|
6
6
|
const { Headers } = require('../fetch/headers')
|
|
7
7
|
|
|
8
8
|
const brandChecks = webidl.brandCheckMultiple([Headers, globalThis.Headers].filter(Boolean))
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { pipeline } = require('node:stream')
|
|
4
4
|
const { fetching } = require('../fetch')
|
|
5
5
|
const { makeRequest } = require('../fetch/request')
|
|
6
|
-
const { webidl } = require('../
|
|
6
|
+
const { webidl } = require('../webidl')
|
|
7
7
|
const { EventSourceStream } = require('./eventsource-stream')
|
|
8
8
|
const { parseMIMEType } = require('../fetch/data-url')
|
|
9
9
|
const { createFastMessageEvent } = require('../websocket/events')
|
|
@@ -231,12 +231,9 @@ class EventSource extends EventTarget {
|
|
|
231
231
|
|
|
232
232
|
// 14. Let processEventSourceEndOfBody given response res be the following step: if res is not a network error, then reestablish the connection.
|
|
233
233
|
const processEventSourceEndOfBody = (response) => {
|
|
234
|
-
if (isNetworkError(response)) {
|
|
235
|
-
this
|
|
236
|
-
this.close()
|
|
234
|
+
if (!isNetworkError(response)) {
|
|
235
|
+
return this.#reconnect()
|
|
237
236
|
}
|
|
238
|
-
|
|
239
|
-
this.#reconnect()
|
|
240
237
|
}
|
|
241
238
|
|
|
242
239
|
// 15. Fetch request, with processResponseEndOfBody set to processEventSourceEndOfBody...
|
package/lib/web/fetch/body.js
CHANGED
|
@@ -4,19 +4,20 @@ const util = require('../../core/util')
|
|
|
4
4
|
const {
|
|
5
5
|
ReadableStreamFrom,
|
|
6
6
|
readableStreamClose,
|
|
7
|
-
createDeferredPromise,
|
|
8
7
|
fullyReadBody,
|
|
9
8
|
extractMimeType,
|
|
10
9
|
utf8DecodeBytes
|
|
11
10
|
} = require('./util')
|
|
12
11
|
const { FormData, setFormDataState } = require('./formdata')
|
|
13
|
-
const { webidl } = require('
|
|
12
|
+
const { webidl } = require('../webidl')
|
|
14
13
|
const { Blob } = require('node:buffer')
|
|
15
14
|
const assert = require('node:assert')
|
|
16
15
|
const { isErrored, isDisturbed } = require('node:stream')
|
|
17
16
|
const { isArrayBuffer } = require('node:util/types')
|
|
18
17
|
const { serializeAMimeType } = require('./data-url')
|
|
19
18
|
const { multipartFormDataParser } = require('./formdata-parser')
|
|
19
|
+
const { createDeferredPromise } = require('../../util/promise')
|
|
20
|
+
|
|
20
21
|
let random
|
|
21
22
|
|
|
22
23
|
try {
|
|
@@ -29,19 +30,22 @@ try {
|
|
|
29
30
|
const textEncoder = new TextEncoder()
|
|
30
31
|
function noop () {}
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
|
|
39
|
-
stream.cancel('Response object has been garbage collected').catch(noop)
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
}
|
|
33
|
+
const streamRegistry = new FinalizationRegistry((weakRef) => {
|
|
34
|
+
const stream = weakRef.deref()
|
|
35
|
+
if (stream && !stream.locked && !isDisturbed(stream) && !isErrored(stream)) {
|
|
36
|
+
stream.cancel('Response object has been garbage collected').catch(noop)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Extract a body with type from a byte sequence or BodyInit object
|
|
42
|
+
*
|
|
43
|
+
* @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
|
|
44
|
+
* @param {boolean} [keepalive=false] - If true, indicates that the body
|
|
45
|
+
* @returns {[{stream: ReadableStream, source: any, length: number | null}, string | null]} - Returns a tuple containing the body and its type
|
|
46
|
+
*
|
|
47
|
+
* @see https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
48
|
+
*/
|
|
45
49
|
function extractBody (object, keepalive = false) {
|
|
46
50
|
// 1. Let stream be null.
|
|
47
51
|
let stream = null
|
|
@@ -267,7 +271,22 @@ function extractBody (object, keepalive = false) {
|
|
|
267
271
|
return [body, type]
|
|
268
272
|
}
|
|
269
273
|
|
|
270
|
-
|
|
274
|
+
/**
|
|
275
|
+
* @typedef {object} ExtractBodyResult
|
|
276
|
+
* @property {ReadableStream<Uint8Array<ArrayBuffer>>} stream - The ReadableStream containing the body data
|
|
277
|
+
* @property {any} source - The original source of the body data
|
|
278
|
+
* @property {number | null} length - The length of the body data, or null
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Safely extract a body with type from a byte sequence or BodyInit object.
|
|
283
|
+
*
|
|
284
|
+
* @param {import('../../../types').BodyInit} object - The BodyInit object to extract from
|
|
285
|
+
* @param {boolean} [keepalive=false] - If true, indicates that the body
|
|
286
|
+
* @returns {[ExtractBodyResult, string | null]} - Returns a tuple containing the body and its type
|
|
287
|
+
*
|
|
288
|
+
* @see https://fetch.spec.whatwg.org/#bodyinit-safely-extract
|
|
289
|
+
*/
|
|
271
290
|
function safelyExtractBody (object, keepalive = false) {
|
|
272
291
|
// To safely extract a body and a `Content-Type` value from
|
|
273
292
|
// a byte sequence or BodyInit object object, run these steps:
|
|
@@ -275,9 +294,7 @@ function safelyExtractBody (object, keepalive = false) {
|
|
|
275
294
|
// 1. If object is a ReadableStream object, then:
|
|
276
295
|
if (webidl.is.ReadableStream(object)) {
|
|
277
296
|
// Assert: object is neither disturbed nor locked.
|
|
278
|
-
// istanbul ignore next
|
|
279
297
|
assert(!util.isDisturbed(object), 'The body has already been consumed.')
|
|
280
|
-
// istanbul ignore next
|
|
281
298
|
assert(!object.locked, 'The stream is locked.')
|
|
282
299
|
}
|
|
283
300
|
|
|
@@ -285,17 +302,13 @@ function safelyExtractBody (object, keepalive = false) {
|
|
|
285
302
|
return extractBody(object, keepalive)
|
|
286
303
|
}
|
|
287
304
|
|
|
288
|
-
function cloneBody (
|
|
305
|
+
function cloneBody (body) {
|
|
289
306
|
// To clone a body body, run these steps:
|
|
290
307
|
|
|
291
308
|
// https://fetch.spec.whatwg.org/#concept-body-clone
|
|
292
309
|
|
|
293
310
|
// 1. Let « out1, out2 » be the result of teeing body’s stream.
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
if (hasFinalizationRegistry) {
|
|
297
|
-
streamRegistry.register(instance, new WeakRef(out1))
|
|
298
|
-
}
|
|
311
|
+
const { 0: out1, 1: out2 } = body.stream.tee()
|
|
299
312
|
|
|
300
313
|
// 2. Set body’s stream to out1.
|
|
301
314
|
body.stream = out1
|
|
@@ -527,6 +540,5 @@ module.exports = {
|
|
|
527
540
|
cloneBody,
|
|
528
541
|
mixinBody,
|
|
529
542
|
streamRegistry,
|
|
530
|
-
hasFinalizationRegistry,
|
|
531
543
|
bodyUnusable
|
|
532
544
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { bufferToLowerCasedHeaderName } = require('../../core/util')
|
|
4
4
|
const { utf8DecodeBytes } = require('./util')
|
|
5
5
|
const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url')
|
|
6
6
|
const { makeEntry } = require('./formdata')
|
|
7
|
-
const { webidl } = require('
|
|
7
|
+
const { webidl } = require('../webidl')
|
|
8
8
|
const assert = require('node:assert')
|
|
9
9
|
const { File: NodeFile } = require('node:buffer')
|
|
10
10
|
|
|
@@ -200,8 +200,8 @@ function multipartFormDataParser (input, mimeType) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.
|
|
203
|
-
assert(
|
|
204
|
-
assert((typeof value === 'string' &&
|
|
203
|
+
assert(webidl.is.USVString(name))
|
|
204
|
+
assert((typeof value === 'string' && webidl.is.USVString(value)) || webidl.is.File(value))
|
|
205
205
|
|
|
206
206
|
// 5.13. Create an entry with name and value, and append it to entry list.
|
|
207
207
|
entryList.push(makeEntry(name, value, filename))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { iteratorMixin } = require('./util')
|
|
4
4
|
const { kEnumerableProperty } = require('../../core/util')
|
|
5
|
-
const { webidl } = require('
|
|
5
|
+
const { webidl } = require('../webidl')
|
|
6
6
|
const { File: NativeFile } = require('node:buffer')
|
|
7
7
|
const nodeUtil = require('node:util')
|
|
8
8
|
|
package/lib/web/fetch/headers.js
CHANGED