undici 7.12.0 → 7.14.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 +16 -14
- package/docs/docs/api/DiagnosticsChannel.md +25 -1
- package/docs/docs/api/ProxyAgent.md +1 -1
- package/docs/docs/api/SnapshotAgent.md +616 -0
- package/index.js +2 -0
- package/lib/api/readable.js +48 -26
- package/lib/core/util.js +0 -1
- package/lib/dispatcher/proxy-agent.js +68 -73
- package/lib/handler/cache-handler.js +22 -4
- package/lib/handler/redirect-handler.js +10 -0
- package/lib/interceptor/cache.js +2 -2
- package/lib/interceptor/dump.js +2 -1
- package/lib/mock/mock-agent.js +10 -4
- package/lib/mock/snapshot-agent.js +347 -0
- package/lib/mock/snapshot-recorder.js +580 -0
- package/lib/mock/snapshot-utils.js +158 -0
- package/lib/util/cache.js +3 -3
- package/lib/web/cache/cache.js +4 -4
- package/lib/web/eventsource/eventsource.js +17 -2
- package/lib/web/fetch/body.js +0 -1
- package/lib/web/fetch/formdata-parser.js +0 -3
- package/lib/web/fetch/formdata.js +1 -5
- package/lib/web/fetch/response.js +8 -4
- package/lib/web/webidl/index.js +1 -1
- package/lib/web/websocket/stream/websocketstream.js +2 -2
- package/lib/web/websocket/websocket.js +11 -4
- package/package.json +5 -5
- package/types/agent.d.ts +0 -4
- package/types/client.d.ts +0 -2
- package/types/dispatcher.d.ts +0 -6
- package/types/eventsource.d.ts +6 -1
- package/types/h2c-client.d.ts +0 -2
- package/types/index.d.ts +6 -1
- package/types/mock-interceptor.d.ts +0 -1
- package/types/snapshot-agent.d.ts +107 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { InvalidArgumentError } = require('../core/errors')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} HeaderFilters
|
|
7
|
+
* @property {Set<string>} ignore - Set of headers to ignore for matching
|
|
8
|
+
* @property {Set<string>} exclude - Set of headers to exclude from matching
|
|
9
|
+
* @property {Set<string>} match - Set of headers to match (empty means match
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates cached header sets for performance
|
|
14
|
+
*
|
|
15
|
+
* @param {import('./snapshot-recorder').SnapshotRecorderMatchOptions} matchOptions - Matching options for headers
|
|
16
|
+
* @returns {HeaderFilters} - Cached sets for ignore, exclude, and match headers
|
|
17
|
+
*/
|
|
18
|
+
function createHeaderFilters (matchOptions = {}) {
|
|
19
|
+
const { ignoreHeaders = [], excludeHeaders = [], matchHeaders = [], caseSensitive = false } = matchOptions
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
ignore: new Set(ignoreHeaders.map(header => caseSensitive ? header : header.toLowerCase())),
|
|
23
|
+
exclude: new Set(excludeHeaders.map(header => caseSensitive ? header : header.toLowerCase())),
|
|
24
|
+
match: new Set(matchHeaders.map(header => caseSensitive ? header : header.toLowerCase()))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let crypto
|
|
29
|
+
try {
|
|
30
|
+
crypto = require('node:crypto')
|
|
31
|
+
} catch { /* Fallback if crypto is not available */ }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @callback HashIdFunction
|
|
35
|
+
* @param {string} value - The value to hash
|
|
36
|
+
* @returns {string} - The base64url encoded hash of the value
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generates a hash for a given value
|
|
41
|
+
* @type {HashIdFunction}
|
|
42
|
+
*/
|
|
43
|
+
const hashId = crypto?.hash
|
|
44
|
+
? (value) => crypto.hash('sha256', value, 'base64url')
|
|
45
|
+
: (value) => Buffer.from(value).toString('base64url')
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {(url: string) => boolean} IsUrlExcluded Checks if a URL matches any of the exclude patterns
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** @typedef {{[key: Lowercase<string>]: string}} NormalizedHeaders */
|
|
52
|
+
/** @typedef {Array<string>} UndiciHeaders */
|
|
53
|
+
/** @typedef {Record<string, string|string[]>} Headers */
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {*} headers
|
|
57
|
+
* @returns {headers is UndiciHeaders}
|
|
58
|
+
*/
|
|
59
|
+
function isUndiciHeaders (headers) {
|
|
60
|
+
return Array.isArray(headers) && (headers.length & 1) === 0
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Factory function to create a URL exclusion checker
|
|
65
|
+
* @param {Array<string| RegExp>} [excludePatterns=[]] - Array of patterns to exclude
|
|
66
|
+
* @returns {IsUrlExcluded} - A function that checks if a URL matches any of the exclude patterns
|
|
67
|
+
*/
|
|
68
|
+
function isUrlExcludedFactory (excludePatterns = []) {
|
|
69
|
+
if (excludePatterns.length === 0) {
|
|
70
|
+
return () => false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return function isUrlExcluded (url) {
|
|
74
|
+
let urlLowerCased
|
|
75
|
+
|
|
76
|
+
for (const pattern of excludePatterns) {
|
|
77
|
+
if (typeof pattern === 'string') {
|
|
78
|
+
if (!urlLowerCased) {
|
|
79
|
+
// Convert URL to lowercase only once
|
|
80
|
+
urlLowerCased = url.toLowerCase()
|
|
81
|
+
}
|
|
82
|
+
// Simple string match (case-insensitive)
|
|
83
|
+
if (urlLowerCased.includes(pattern.toLowerCase())) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
} else if (pattern instanceof RegExp) {
|
|
87
|
+
// Regex pattern match
|
|
88
|
+
if (pattern.test(url)) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Normalizes headers for consistent comparison
|
|
100
|
+
*
|
|
101
|
+
* @param {Object|UndiciHeaders} headers - Headers to normalize
|
|
102
|
+
* @returns {NormalizedHeaders} - Normalized headers as a lowercase object
|
|
103
|
+
*/
|
|
104
|
+
function normalizeHeaders (headers) {
|
|
105
|
+
/** @type {NormalizedHeaders} */
|
|
106
|
+
const normalizedHeaders = {}
|
|
107
|
+
|
|
108
|
+
if (!headers) return normalizedHeaders
|
|
109
|
+
|
|
110
|
+
// Handle array format (undici internal format: [name, value, name, value, ...])
|
|
111
|
+
if (isUndiciHeaders(headers)) {
|
|
112
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
113
|
+
const key = headers[i]
|
|
114
|
+
const value = headers[i + 1]
|
|
115
|
+
if (key && value !== undefined) {
|
|
116
|
+
// Convert Buffers to strings if needed
|
|
117
|
+
const keyStr = Buffer.isBuffer(key) ? key.toString() : key
|
|
118
|
+
const valueStr = Buffer.isBuffer(value) ? value.toString() : value
|
|
119
|
+
normalizedHeaders[keyStr.toLowerCase()] = valueStr
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return normalizedHeaders
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Handle object format
|
|
126
|
+
if (headers && typeof headers === 'object') {
|
|
127
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
128
|
+
if (key && typeof key === 'string') {
|
|
129
|
+
normalizedHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return normalizedHeaders
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const validSnapshotModes = /** @type {const} */ (['record', 'playback', 'update'])
|
|
138
|
+
|
|
139
|
+
/** @typedef {typeof validSnapshotModes[number]} SnapshotMode */
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {*} mode - The snapshot mode to validate
|
|
143
|
+
* @returns {asserts mode is SnapshotMode}
|
|
144
|
+
*/
|
|
145
|
+
function validateSnapshotMode (mode) {
|
|
146
|
+
if (!validSnapshotModes.includes(mode)) {
|
|
147
|
+
throw new InvalidArgumentError(`Invalid snapshot mode: ${mode}. Must be one of: ${validSnapshotModes.join(', ')}`)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
createHeaderFilters,
|
|
153
|
+
hashId,
|
|
154
|
+
isUndiciHeaders,
|
|
155
|
+
normalizeHeaders,
|
|
156
|
+
isUrlExcludedFactory,
|
|
157
|
+
validateSnapshotMode
|
|
158
|
+
}
|
package/lib/util/cache.js
CHANGED
|
@@ -34,7 +34,7 @@ function makeCacheKey (opts) {
|
|
|
34
34
|
* @param {Record<string, string[] | string>}
|
|
35
35
|
* @returns {Record<string, string[] | string>}
|
|
36
36
|
*/
|
|
37
|
-
function
|
|
37
|
+
function normalizeHeaders (opts) {
|
|
38
38
|
let headers
|
|
39
39
|
if (opts.headers == null) {
|
|
40
40
|
headers = {}
|
|
@@ -234,7 +234,7 @@ function parseCacheControlHeader (header) {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
} else {
|
|
237
|
-
// Something like `no-cache=some-header`
|
|
237
|
+
// Something like `no-cache="some-header"`
|
|
238
238
|
if (key in output) {
|
|
239
239
|
output[key] = output[key].concat(value)
|
|
240
240
|
} else {
|
|
@@ -367,7 +367,7 @@ function assertCacheMethods (methods, name = 'CacheMethods') {
|
|
|
367
367
|
|
|
368
368
|
module.exports = {
|
|
369
369
|
makeCacheKey,
|
|
370
|
-
|
|
370
|
+
normalizeHeaders,
|
|
371
371
|
assertCacheKey,
|
|
372
372
|
assertCacheValue,
|
|
373
373
|
parseCacheControlHeader,
|
package/lib/web/cache/cache.js
CHANGED
|
@@ -18,7 +18,7 @@ const { createDeferredPromise } = require('../../util/promise')
|
|
|
18
18
|
* @property {'delete' | 'put'} type
|
|
19
19
|
* @property {any} request
|
|
20
20
|
* @property {any} response
|
|
21
|
-
* @property {import('
|
|
21
|
+
* @property {import('../../../types/cache').CacheQueryOptions} options
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -452,7 +452,7 @@ class Cache {
|
|
|
452
452
|
/**
|
|
453
453
|
* @see https://w3c.github.io/ServiceWorker/#dom-cache-keys
|
|
454
454
|
* @param {any} request
|
|
455
|
-
* @param {import('
|
|
455
|
+
* @param {import('../../../types/cache').CacheQueryOptions} options
|
|
456
456
|
* @returns {Promise<readonly Request[]>}
|
|
457
457
|
*/
|
|
458
458
|
async keys (request = undefined, options = {}) {
|
|
@@ -670,7 +670,7 @@ class Cache {
|
|
|
670
670
|
/**
|
|
671
671
|
* @see https://w3c.github.io/ServiceWorker/#query-cache
|
|
672
672
|
* @param {any} requestQuery
|
|
673
|
-
* @param {import('
|
|
673
|
+
* @param {import('../../../types/cache').CacheQueryOptions} options
|
|
674
674
|
* @param {requestResponseList} targetStorage
|
|
675
675
|
* @returns {requestResponseList}
|
|
676
676
|
*/
|
|
@@ -695,7 +695,7 @@ class Cache {
|
|
|
695
695
|
* @param {any} requestQuery
|
|
696
696
|
* @param {any} request
|
|
697
697
|
* @param {any | null} response
|
|
698
|
-
* @param {import('
|
|
698
|
+
* @param {import('../../../types/cache').CacheQueryOptions | undefined} options
|
|
699
699
|
* @returns {boolean}
|
|
700
700
|
*/
|
|
701
701
|
#requestMatchesCachedItem (requestQuery, request, response = null, options) {
|
|
@@ -124,10 +124,10 @@ class EventSource extends EventTarget {
|
|
|
124
124
|
url = webidl.converters.USVString(url)
|
|
125
125
|
eventSourceInitDict = webidl.converters.EventSourceInitDict(eventSourceInitDict, prefix, 'eventSourceInitDict')
|
|
126
126
|
|
|
127
|
-
this.#dispatcher = eventSourceInitDict.dispatcher
|
|
127
|
+
this.#dispatcher = eventSourceInitDict.node.dispatcher || eventSourceInitDict.dispatcher
|
|
128
128
|
this.#state = {
|
|
129
129
|
lastEventId: '',
|
|
130
|
-
reconnectionTime:
|
|
130
|
+
reconnectionTime: eventSourceInitDict.node.reconnectionTime
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// 2. Let settings be ev's relevant settings object.
|
|
@@ -472,6 +472,21 @@ webidl.converters.EventSourceInitDict = webidl.dictionaryConverter([
|
|
|
472
472
|
{
|
|
473
473
|
key: 'dispatcher', // undici only
|
|
474
474
|
converter: webidl.converters.any
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
key: 'node', // undici only
|
|
478
|
+
converter: webidl.dictionaryConverter([
|
|
479
|
+
{
|
|
480
|
+
key: 'reconnectionTime',
|
|
481
|
+
converter: webidl.converters['unsigned long'],
|
|
482
|
+
defaultValue: () => defaultReconnectionTime
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
key: 'dispatcher',
|
|
486
|
+
converter: webidl.converters.any
|
|
487
|
+
}
|
|
488
|
+
]),
|
|
489
|
+
defaultValue: () => ({})
|
|
475
490
|
}
|
|
476
491
|
])
|
|
477
492
|
|
package/lib/web/fetch/body.js
CHANGED
|
@@ -10,7 +10,6 @@ const {
|
|
|
10
10
|
} = require('./util')
|
|
11
11
|
const { FormData, setFormDataState } = require('./formdata')
|
|
12
12
|
const { webidl } = require('../webidl')
|
|
13
|
-
const { Blob } = require('node:buffer')
|
|
14
13
|
const assert = require('node:assert')
|
|
15
14
|
const { isErrored, isDisturbed } = require('node:stream')
|
|
16
15
|
const { isArrayBuffer } = require('node:util/types')
|
|
@@ -6,9 +6,6 @@ const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url')
|
|
|
6
6
|
const { makeEntry } = require('./formdata')
|
|
7
7
|
const { webidl } = require('../webidl')
|
|
8
8
|
const assert = require('node:assert')
|
|
9
|
-
const { File: NodeFile } = require('node:buffer')
|
|
10
|
-
|
|
11
|
-
const File = globalThis.File ?? NodeFile
|
|
12
9
|
|
|
13
10
|
const formDataNameBuffer = Buffer.from('form-data; name="')
|
|
14
11
|
const filenameBuffer = Buffer.from('filename')
|
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
const { iteratorMixin } = require('./util')
|
|
4
4
|
const { kEnumerableProperty } = require('../../core/util')
|
|
5
5
|
const { webidl } = require('../webidl')
|
|
6
|
-
const { File: NativeFile } = require('node:buffer')
|
|
7
6
|
const nodeUtil = require('node:util')
|
|
8
7
|
|
|
9
|
-
/** @type {globalThis['File']} */
|
|
10
|
-
const File = globalThis.File ?? NativeFile
|
|
11
|
-
|
|
12
8
|
// https://xhr.spec.whatwg.org/#formdata
|
|
13
9
|
class FormData {
|
|
14
10
|
#state = []
|
|
15
11
|
|
|
16
|
-
constructor (form) {
|
|
12
|
+
constructor (form = undefined) {
|
|
17
13
|
webidl.util.markAsUncloneable(this)
|
|
18
14
|
|
|
19
15
|
if (form !== undefined) {
|
|
@@ -22,7 +22,8 @@ const { webidl } = require('../webidl')
|
|
|
22
22
|
const { URLSerializer } = require('./data-url')
|
|
23
23
|
const { kConstruct } = require('../../core/symbols')
|
|
24
24
|
const assert = require('node:assert')
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
const { isArrayBuffer } = nodeUtil.types
|
|
26
27
|
|
|
27
28
|
const textEncoder = new TextEncoder('utf-8')
|
|
28
29
|
|
|
@@ -243,6 +244,11 @@ class Response {
|
|
|
243
244
|
// 2. Let clonedResponse be the result of cloning this’s response.
|
|
244
245
|
const clonedResponse = cloneResponse(this.#state)
|
|
245
246
|
|
|
247
|
+
// Note: To re-register because of a new stream.
|
|
248
|
+
if (this.#state.body?.stream) {
|
|
249
|
+
streamRegistry.register(this, new WeakRef(this.#state.body.stream))
|
|
250
|
+
}
|
|
251
|
+
|
|
246
252
|
// 3. Return the result of creating a Response object, given
|
|
247
253
|
// clonedResponse, this’s headers’s guard, and this’s relevant Realm.
|
|
248
254
|
return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers))
|
|
@@ -353,8 +359,6 @@ function cloneResponse (response) {
|
|
|
353
359
|
// result of cloning response’s body.
|
|
354
360
|
if (response.body != null) {
|
|
355
361
|
newResponse.body = cloneBody(response.body)
|
|
356
|
-
|
|
357
|
-
streamRegistry.register(newResponse, new WeakRef(response.body.stream))
|
|
358
362
|
}
|
|
359
363
|
|
|
360
364
|
// 4. Return newResponse.
|
|
@@ -576,7 +580,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
|
|
|
576
580
|
return V
|
|
577
581
|
}
|
|
578
582
|
|
|
579
|
-
if (ArrayBuffer.isView(V) ||
|
|
583
|
+
if (ArrayBuffer.isView(V) || isArrayBuffer(V)) {
|
|
580
584
|
return V
|
|
581
585
|
}
|
|
582
586
|
|
package/lib/web/webidl/index.js
CHANGED
|
@@ -509,7 +509,7 @@ webidl.is.USVString = function (value) {
|
|
|
509
509
|
webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream)
|
|
510
510
|
webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob)
|
|
511
511
|
webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams)
|
|
512
|
-
webidl.is.File = webidl.util.MakeTypeAssertion(
|
|
512
|
+
webidl.is.File = webidl.util.MakeTypeAssertion(File)
|
|
513
513
|
webidl.is.URL = webidl.util.MakeTypeAssertion(URL)
|
|
514
514
|
webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal)
|
|
515
515
|
webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort)
|
|
@@ -6,7 +6,7 @@ const { states, opcodes, sentCloseFrameState } = require('../constants')
|
|
|
6
6
|
const { webidl } = require('../../webidl')
|
|
7
7
|
const { getURLRecord, isValidSubprotocol, isEstablished, utf8Decode } = require('../util')
|
|
8
8
|
const { establishWebSocketConnection, failWebsocketConnection, closeWebSocketConnection } = require('../connection')
|
|
9
|
-
const {
|
|
9
|
+
const { isArrayBuffer } = require('node:util/types')
|
|
10
10
|
const { channels } = require('../../../core/diagnostics')
|
|
11
11
|
const { WebsocketFrameSend } = require('../frame')
|
|
12
12
|
const { ByteParser } = require('../receiver')
|
|
@@ -210,7 +210,7 @@ class WebSocketStream {
|
|
|
210
210
|
let opcode = null
|
|
211
211
|
|
|
212
212
|
// 4. If chunk is a BufferSource ,
|
|
213
|
-
if (ArrayBuffer.isView(chunk) ||
|
|
213
|
+
if (ArrayBuffer.isView(chunk) || isArrayBuffer(chunk)) {
|
|
214
214
|
// 4.1. Set data to a copy of the bytes given chunk .
|
|
215
215
|
data = new Uint8Array(ArrayBuffer.isView(chunk) ? new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength) : chunk)
|
|
216
216
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { isArrayBuffer } = require('node:util/types')
|
|
3
4
|
const { webidl } = require('../webidl')
|
|
4
5
|
const { URLSerializer } = require('../fetch/data-url')
|
|
5
6
|
const { environmentSettingsObject } = require('../fetch/util')
|
|
@@ -19,7 +20,6 @@ const { establishWebSocketConnection, closeWebSocketConnection, failWebsocketCon
|
|
|
19
20
|
const { ByteParser } = require('./receiver')
|
|
20
21
|
const { kEnumerableProperty } = require('../../core/util')
|
|
21
22
|
const { getGlobalDispatcher } = require('../../global')
|
|
22
|
-
const { types } = require('node:util')
|
|
23
23
|
const { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events')
|
|
24
24
|
const { SendQueue } = require('./sender')
|
|
25
25
|
const { WebsocketFrameSend } = require('./frame')
|
|
@@ -257,7 +257,7 @@ class WebSocket extends EventTarget {
|
|
|
257
257
|
this.#sendQueue.add(buffer, () => {
|
|
258
258
|
this.#bufferedAmount -= buffer.byteLength
|
|
259
259
|
}, sendHints.text)
|
|
260
|
-
} else if (
|
|
260
|
+
} else if (isArrayBuffer(data)) {
|
|
261
261
|
// If the WebSocket connection is established, and the WebSocket
|
|
262
262
|
// closing handshake has not yet started, then the user agent must
|
|
263
263
|
// send a WebSocket Message comprised of data using a binary frame
|
|
@@ -482,11 +482,18 @@ class WebSocket extends EventTarget {
|
|
|
482
482
|
fireEvent('open', this)
|
|
483
483
|
|
|
484
484
|
if (channels.open.hasSubscribers) {
|
|
485
|
+
// Convert headers to a plain object for the event
|
|
486
|
+
const headers = response.headersList.entries
|
|
485
487
|
channels.open.publish({
|
|
486
488
|
address: response.socket.address(),
|
|
487
489
|
protocol: this.#protocol,
|
|
488
490
|
extensions: this.#extensions,
|
|
489
|
-
websocket: this
|
|
491
|
+
websocket: this,
|
|
492
|
+
handshakeResponse: {
|
|
493
|
+
status: response.status,
|
|
494
|
+
statusText: response.statusText,
|
|
495
|
+
headers
|
|
496
|
+
}
|
|
490
497
|
})
|
|
491
498
|
}
|
|
492
499
|
}
|
|
@@ -728,7 +735,7 @@ webidl.converters.WebSocketSendData = function (V) {
|
|
|
728
735
|
return V
|
|
729
736
|
}
|
|
730
737
|
|
|
731
|
-
if (ArrayBuffer.isView(V) ||
|
|
738
|
+
if (ArrayBuffer.isView(V) || isArrayBuffer(V)) {
|
|
732
739
|
return V
|
|
733
740
|
}
|
|
734
741
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.14.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": {
|
|
@@ -109,23 +109,23 @@
|
|
|
109
109
|
"devDependencies": {
|
|
110
110
|
"@fastify/busboy": "3.1.1",
|
|
111
111
|
"@matteo.collina/tspl": "^0.2.0",
|
|
112
|
+
"@metcoder95/https-pem": "^1.0.0",
|
|
112
113
|
"@sinonjs/fake-timers": "^12.0.0",
|
|
113
114
|
"@types/node": "^18.19.50",
|
|
114
115
|
"abort-controller": "^3.0.0",
|
|
115
116
|
"borp": "^0.20.0",
|
|
116
117
|
"c8": "^10.0.0",
|
|
117
|
-
"cross-env": "^
|
|
118
|
+
"cross-env": "^10.0.0",
|
|
118
119
|
"dns-packet": "^5.4.0",
|
|
119
120
|
"esbuild": "^0.25.2",
|
|
120
121
|
"eslint": "^9.9.0",
|
|
121
122
|
"fast-check": "^4.1.1",
|
|
122
|
-
"https-pem": "^3.0.0",
|
|
123
123
|
"husky": "^9.0.7",
|
|
124
|
-
"jest": "^
|
|
124
|
+
"jest": "^30.0.5",
|
|
125
125
|
"neostandard": "^0.12.0",
|
|
126
126
|
"node-forge": "^1.3.1",
|
|
127
127
|
"proxy": "^2.1.1",
|
|
128
|
-
"tsd": "^0.
|
|
128
|
+
"tsd": "^0.33.0",
|
|
129
129
|
"typescript": "^5.6.2",
|
|
130
130
|
"ws": "^8.11.0"
|
|
131
131
|
},
|
package/types/agent.d.ts
CHANGED
|
@@ -22,14 +22,10 @@ declare namespace Agent {
|
|
|
22
22
|
export interface Options extends Pool.Options {
|
|
23
23
|
/** Default: `(origin, opts) => new Pool(origin, opts)`. */
|
|
24
24
|
factory?(origin: string | URL, opts: Object): Dispatcher;
|
|
25
|
-
/** Integer. Default: `0` */
|
|
26
|
-
maxRedirections?: number;
|
|
27
25
|
|
|
28
26
|
interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options['interceptors']
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
export interface DispatchOptions extends Dispatcher.DispatchOptions {
|
|
32
|
-
/** Integer. */
|
|
33
|
-
maxRedirections?: number;
|
|
34
30
|
}
|
|
35
31
|
}
|
package/types/client.d.ts
CHANGED
|
@@ -71,8 +71,6 @@ export declare namespace Client {
|
|
|
71
71
|
/** TODO */
|
|
72
72
|
maxCachedSessions?: number;
|
|
73
73
|
/** TODO */
|
|
74
|
-
maxRedirections?: number;
|
|
75
|
-
/** TODO */
|
|
76
74
|
connect?: Partial<buildConnector.BuildOptions> | buildConnector.connector;
|
|
77
75
|
/** TODO */
|
|
78
76
|
maxRequestsPerClient?: number;
|
package/types/dispatcher.d.ts
CHANGED
|
@@ -135,8 +135,6 @@ declare namespace Dispatcher {
|
|
|
135
135
|
signal?: AbortSignal | EventEmitter | null;
|
|
136
136
|
/** This argument parameter is passed through to `ConnectData` */
|
|
137
137
|
opaque?: TOpaque;
|
|
138
|
-
/** Default: 0 */
|
|
139
|
-
maxRedirections?: number;
|
|
140
138
|
/** Default: false */
|
|
141
139
|
redirectionLimitReached?: boolean;
|
|
142
140
|
/** Default: `null` */
|
|
@@ -147,8 +145,6 @@ declare namespace Dispatcher {
|
|
|
147
145
|
opaque?: TOpaque;
|
|
148
146
|
/** Default: `null` */
|
|
149
147
|
signal?: AbortSignal | EventEmitter | null;
|
|
150
|
-
/** Default: 0 */
|
|
151
|
-
maxRedirections?: number;
|
|
152
148
|
/** Default: false */
|
|
153
149
|
redirectionLimitReached?: boolean;
|
|
154
150
|
/** Default: `null` */
|
|
@@ -172,8 +168,6 @@ declare namespace Dispatcher {
|
|
|
172
168
|
protocol?: string;
|
|
173
169
|
/** Default: `null` */
|
|
174
170
|
signal?: AbortSignal | EventEmitter | null;
|
|
175
|
-
/** Default: 0 */
|
|
176
|
-
maxRedirections?: number;
|
|
177
171
|
/** Default: false */
|
|
178
172
|
redirectionLimitReached?: boolean;
|
|
179
173
|
/** Default: `null` */
|
package/types/eventsource.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export declare const EventSource: {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
interface EventSourceInit {
|
|
59
|
-
withCredentials?: boolean
|
|
59
|
+
withCredentials?: boolean
|
|
60
|
+
// @deprecated use `node.dispatcher` instead
|
|
60
61
|
dispatcher?: Dispatcher
|
|
62
|
+
node?: {
|
|
63
|
+
dispatcher?: Dispatcher
|
|
64
|
+
reconnectionTime?: number
|
|
65
|
+
}
|
|
61
66
|
}
|
package/types/h2c-client.d.ts
CHANGED
|
@@ -51,8 +51,6 @@ export declare namespace H2CClient {
|
|
|
51
51
|
/** TODO */
|
|
52
52
|
maxCachedSessions?: number;
|
|
53
53
|
/** TODO */
|
|
54
|
-
maxRedirections?: number;
|
|
55
|
-
/** TODO */
|
|
56
54
|
connect?: Omit<Partial<buildConnector.BuildOptions>, 'allowH2'> | buildConnector.connector;
|
|
57
55
|
/** TODO */
|
|
58
56
|
maxRequestsPerClient?: number;
|
package/types/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import Agent from './agent'
|
|
|
13
13
|
import MockClient from './mock-client'
|
|
14
14
|
import MockPool from './mock-pool'
|
|
15
15
|
import MockAgent from './mock-agent'
|
|
16
|
+
import { SnapshotAgent } from './snapshot-agent'
|
|
16
17
|
import { MockCallHistory, MockCallHistoryLog } from './mock-call-history'
|
|
17
18
|
import mockErrors from './mock-errors'
|
|
18
19
|
import ProxyAgent from './proxy-agent'
|
|
@@ -33,7 +34,9 @@ export * from './content-type'
|
|
|
33
34
|
export * from './cache'
|
|
34
35
|
export { Interceptable } from './mock-interceptor'
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
declare function globalThisInstall (): void
|
|
38
|
+
|
|
39
|
+
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, interceptors, MockClient, MockPool, MockAgent, SnapshotAgent, MockCallHistory, MockCallHistoryLog, mockErrors, ProxyAgent, EnvHttpProxyAgent, RedirectHandler, DecoratorHandler, RetryHandler, RetryAgent, H2CClient, globalThisInstall as install }
|
|
37
40
|
export default Undici
|
|
38
41
|
|
|
39
42
|
declare namespace Undici {
|
|
@@ -58,6 +61,7 @@ declare namespace Undici {
|
|
|
58
61
|
const MockClient: typeof import('./mock-client').default
|
|
59
62
|
const MockPool: typeof import('./mock-pool').default
|
|
60
63
|
const MockAgent: typeof import('./mock-agent').default
|
|
64
|
+
const SnapshotAgent: typeof import('./snapshot-agent').SnapshotAgent
|
|
61
65
|
const MockCallHistory: typeof import('./mock-call-history').MockCallHistory
|
|
62
66
|
const MockCallHistoryLog: typeof import('./mock-call-history').MockCallHistoryLog
|
|
63
67
|
const mockErrors: typeof import('./mock-errors').default
|
|
@@ -72,4 +76,5 @@ declare namespace Undici {
|
|
|
72
76
|
MemoryCacheStore: typeof import('./cache-interceptor').default.MemoryCacheStore,
|
|
73
77
|
SqliteCacheStore: typeof import('./cache-interceptor').default.SqliteCacheStore
|
|
74
78
|
}
|
|
79
|
+
const install: typeof globalThisInstall
|
|
75
80
|
}
|
|
@@ -69,7 +69,6 @@ declare namespace MockInterceptor {
|
|
|
69
69
|
headers?: Headers | Record<string, string>;
|
|
70
70
|
origin?: string;
|
|
71
71
|
body?: BodyInit | Dispatcher.DispatchOptions['body'] | null;
|
|
72
|
-
maxRedirections?: number;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
export type MockResponseDataHandler<TData extends object = object> = (
|