undici 7.0.0-alpha.1 → 7.0.0-alpha.2
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 +2 -2
- package/docs/docs/api/Client.md +1 -1
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +53 -2
- package/docs/docs/api/MockAgent.md +2 -0
- package/docs/docs/api/MockPool.md +2 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +1 -1
- package/docs/docs/api/WebSocket.md +45 -3
- package/index.js +6 -2
- package/lib/api/abort-signal.js +2 -0
- package/lib/api/api-pipeline.js +4 -2
- package/lib/api/api-request.js +4 -2
- package/lib/api/api-stream.js +3 -1
- package/lib/api/api-upgrade.js +2 -2
- package/lib/api/readable.js +194 -41
- package/lib/api/util.js +2 -0
- package/lib/core/connect.js +49 -22
- package/lib/core/constants.js +11 -9
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/request.js +4 -4
- package/lib/core/symbols.js +2 -0
- package/lib/core/tree.js +4 -2
- package/lib/core/util.js +220 -39
- package/lib/dispatcher/client-h1.js +299 -60
- package/lib/dispatcher/client-h2.js +1 -1
- package/lib/dispatcher/client.js +24 -7
- package/lib/dispatcher/fixed-queue.js +91 -49
- package/lib/dispatcher/pool-stats.js +2 -0
- package/lib/dispatcher/proxy-agent.js +3 -1
- package/lib/handler/redirect-handler.js +2 -2
- package/lib/handler/retry-handler.js +2 -2
- package/lib/interceptor/dns.js +346 -0
- package/lib/mock/mock-agent.js +5 -8
- package/lib/mock/mock-client.js +7 -2
- package/lib/mock/mock-errors.js +3 -1
- package/lib/mock/mock-interceptor.js +8 -6
- package/lib/mock/mock-pool.js +7 -2
- package/lib/mock/mock-symbols.js +2 -1
- package/lib/mock/mock-utils.js +33 -5
- package/lib/util/timers.js +50 -6
- package/lib/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +6 -4
- package/lib/web/fetch/body.js +42 -34
- package/lib/web/fetch/constants.js +35 -26
- package/lib/web/fetch/formdata-parser.js +14 -3
- package/lib/web/fetch/formdata.js +40 -20
- package/lib/web/fetch/headers.js +116 -84
- package/lib/web/fetch/index.js +65 -59
- package/lib/web/fetch/request.js +130 -55
- package/lib/web/fetch/response.js +79 -36
- package/lib/web/fetch/util.js +104 -57
- package/lib/web/fetch/webidl.js +38 -14
- package/lib/web/websocket/connection.js +92 -15
- package/lib/web/websocket/constants.js +2 -3
- package/lib/web/websocket/events.js +4 -2
- package/lib/web/websocket/receiver.js +20 -26
- package/lib/web/websocket/stream/websocketerror.js +83 -0
- package/lib/web/websocket/stream/websocketstream.js +485 -0
- package/lib/web/websocket/util.js +115 -10
- package/lib/web/websocket/websocket.js +45 -170
- package/package.json +6 -6
- package/types/interceptors.d.ts +14 -0
- package/types/mock-agent.d.ts +3 -0
- package/types/readable.d.ts +10 -7
- package/types/webidl.d.ts +24 -4
- package/types/websocket.d.ts +33 -0
- package/lib/mock/pluralizer.js +0 -29
- package/lib/web/cache/symbols.js +0 -5
- package/lib/web/fetch/symbols.js +0 -8
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { webidl } = require('../fetch/webidl')
|
|
4
4
|
const { URLSerializer } = require('../fetch/data-url')
|
|
5
5
|
const { environmentSettingsObject } = require('../fetch/util')
|
|
6
|
-
const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes
|
|
6
|
+
const { staticPropertyDescriptors, states, sentCloseFrameState, sendHints, opcodes } = require('./constants')
|
|
7
7
|
const {
|
|
8
8
|
isConnecting,
|
|
9
9
|
isEstablished,
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
failWebsocketConnection,
|
|
14
14
|
utf8Decode,
|
|
15
15
|
toArrayBuffer,
|
|
16
|
-
|
|
16
|
+
getURLRecord
|
|
17
17
|
} = require('./util')
|
|
18
18
|
const { establishWebSocketConnection, closeWebSocketConnection } = require('./connection')
|
|
19
19
|
const { ByteParser } = require('./receiver')
|
|
@@ -22,15 +22,13 @@ const { getGlobalDispatcher } = require('../../global')
|
|
|
22
22
|
const { types } = require('node:util')
|
|
23
23
|
const { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events')
|
|
24
24
|
const { SendQueue } = require('./sender')
|
|
25
|
-
const { WebsocketFrameSend } = require('./frame')
|
|
26
25
|
const { channels } = require('../../core/diagnostics')
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
28
|
* @typedef {object} Handler
|
|
30
29
|
* @property {(response: any, extensions?: string[]) => void} onConnectionEstablished
|
|
31
|
-
* @property {(reason: any) => void} onFail
|
|
30
|
+
* @property {(code: number, reason: any) => void} onFail
|
|
32
31
|
* @property {(opcode: number, data: Buffer) => void} onMessage
|
|
33
|
-
* @property {(code: number, reason: any, reasonByteLength: number) => void} onClose
|
|
34
32
|
* @property {(error: Error) => void} onParserError
|
|
35
33
|
* @property {() => void} onParserDrain
|
|
36
34
|
* @property {(chunk: Buffer) => void} onSocketData
|
|
@@ -39,8 +37,9 @@ const { channels } = require('../../core/diagnostics')
|
|
|
39
37
|
*
|
|
40
38
|
* @property {number} readyState
|
|
41
39
|
* @property {import('stream').Duplex} socket
|
|
42
|
-
* @property {number} closeState
|
|
43
|
-
* @property {
|
|
40
|
+
* @property {Set<number>} closeState
|
|
41
|
+
* @property {import('../fetch/index').Fetch} controller
|
|
42
|
+
* @property {boolean} [wasEverConnected=false]
|
|
44
43
|
*/
|
|
45
44
|
|
|
46
45
|
// https://websockets.spec.whatwg.org/#interface-definition
|
|
@@ -62,10 +61,9 @@ class WebSocket extends EventTarget {
|
|
|
62
61
|
/** @type {Handler} */
|
|
63
62
|
#handler = {
|
|
64
63
|
onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions),
|
|
65
|
-
onFail: (reason) => this.#onFail(reason),
|
|
64
|
+
onFail: (code, reason) => this.#onFail(code, reason),
|
|
66
65
|
onMessage: (opcode, data) => this.#onMessage(opcode, data),
|
|
67
|
-
|
|
68
|
-
onParserError: (err) => this.#onParserError(err),
|
|
66
|
+
onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message),
|
|
69
67
|
onParserDrain: () => this.#onParserDrain(),
|
|
70
68
|
onSocketData: (chunk) => {
|
|
71
69
|
if (!this.#parser.write(chunk)) {
|
|
@@ -85,12 +83,12 @@ class WebSocket extends EventTarget {
|
|
|
85
83
|
|
|
86
84
|
readyState: states.CONNECTING,
|
|
87
85
|
socket: null,
|
|
88
|
-
closeState:
|
|
89
|
-
|
|
86
|
+
closeState: new Set(),
|
|
87
|
+
controller: null,
|
|
88
|
+
wasEverConnected: false
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
#url
|
|
93
|
-
#controller
|
|
94
92
|
#binaryType
|
|
95
93
|
/** @type {import('./receiver').ByteParser} */
|
|
96
94
|
#parser
|
|
@@ -113,45 +111,16 @@ class WebSocket extends EventTarget {
|
|
|
113
111
|
// 1. Let baseURL be this's relevant settings object's API base URL.
|
|
114
112
|
const baseURL = environmentSettingsObject.settingsObject.baseUrl
|
|
115
113
|
|
|
116
|
-
//
|
|
117
|
-
|
|
114
|
+
// 2. Let urlRecord be the result of getting a URL record given url and baseURL.
|
|
115
|
+
const urlRecord = getURLRecord(url, baseURL)
|
|
118
116
|
|
|
119
|
-
|
|
120
|
-
urlRecord = new URL(url, baseURL)
|
|
121
|
-
} catch (e) {
|
|
122
|
-
// 3. If urlRecord is failure, then throw a "SyntaxError" DOMException.
|
|
123
|
-
throw new DOMException(e, 'SyntaxError')
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 4. If urlRecord’s scheme is "http", then set urlRecord’s scheme to "ws".
|
|
127
|
-
if (urlRecord.protocol === 'http:') {
|
|
128
|
-
urlRecord.protocol = 'ws:'
|
|
129
|
-
} else if (urlRecord.protocol === 'https:') {
|
|
130
|
-
// 5. Otherwise, if urlRecord’s scheme is "https", set urlRecord’s scheme to "wss".
|
|
131
|
-
urlRecord.protocol = 'wss:'
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 6. If urlRecord’s scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException.
|
|
135
|
-
if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {
|
|
136
|
-
throw new DOMException(
|
|
137
|
-
`Expected a ws: or wss: protocol, got ${urlRecord.protocol}`,
|
|
138
|
-
'SyntaxError'
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 7. If urlRecord’s fragment is non-null, then throw a "SyntaxError"
|
|
143
|
-
// DOMException.
|
|
144
|
-
if (urlRecord.hash || urlRecord.href.endsWith('#')) {
|
|
145
|
-
throw new DOMException('Got fragment', 'SyntaxError')
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 8. If protocols is a string, set protocols to a sequence consisting
|
|
117
|
+
// 3. If protocols is a string, set protocols to a sequence consisting
|
|
149
118
|
// of just that string.
|
|
150
119
|
if (typeof protocols === 'string') {
|
|
151
120
|
protocols = [protocols]
|
|
152
121
|
}
|
|
153
122
|
|
|
154
|
-
//
|
|
123
|
+
// 4. If any of the values in protocols occur more than once or otherwise
|
|
155
124
|
// fail to match the requirements for elements that comprise the value
|
|
156
125
|
// of `Sec-WebSocket-Protocol` fields as defined by The WebSocket
|
|
157
126
|
// protocol, then throw a "SyntaxError" DOMException.
|
|
@@ -163,17 +132,16 @@ class WebSocket extends EventTarget {
|
|
|
163
132
|
throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')
|
|
164
133
|
}
|
|
165
134
|
|
|
166
|
-
//
|
|
135
|
+
// 5. Set this's url to urlRecord.
|
|
167
136
|
this.#url = new URL(urlRecord.href)
|
|
168
137
|
|
|
169
|
-
//
|
|
138
|
+
// 6. Let client be this's relevant settings object.
|
|
170
139
|
const client = environmentSettingsObject.settingsObject
|
|
171
140
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
this.#controller = establishWebSocketConnection(
|
|
141
|
+
// 7. Run this step in parallel:
|
|
142
|
+
// 7.1. Establish a WebSocket connection given urlRecord, protocols,
|
|
143
|
+
// and client.
|
|
144
|
+
this.#handler.controller = establishWebSocketConnection(
|
|
177
145
|
urlRecord,
|
|
178
146
|
protocols,
|
|
179
147
|
client,
|
|
@@ -186,8 +154,6 @@ class WebSocket extends EventTarget {
|
|
|
186
154
|
// be CONNECTING (0).
|
|
187
155
|
this.#handler.readyState = WebSocket.CONNECTING
|
|
188
156
|
|
|
189
|
-
this.#handler.closeState = sentCloseFrameState.NOT_SENT
|
|
190
|
-
|
|
191
157
|
// The extensions attribute must initially return the empty string.
|
|
192
158
|
|
|
193
159
|
// The protocol attribute must initially return the empty string.
|
|
@@ -215,34 +181,14 @@ class WebSocket extends EventTarget {
|
|
|
215
181
|
reason = webidl.converters.USVString(reason)
|
|
216
182
|
}
|
|
217
183
|
|
|
218
|
-
// 1. If code is
|
|
219
|
-
|
|
220
|
-
// "InvalidAccessError" DOMException.
|
|
221
|
-
if (code !== undefined) {
|
|
222
|
-
if (code !== 1000 && (code < 3000 || code > 4999)) {
|
|
223
|
-
throw new DOMException('invalid code', 'InvalidAccessError')
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
let reasonByteLength = 0
|
|
184
|
+
// 1. If code is the special value "missing", then set code to null.
|
|
185
|
+
code ??= null
|
|
228
186
|
|
|
229
|
-
// 2. If reason is
|
|
230
|
-
|
|
231
|
-
// 1. Let reasonBytes be the result of encoding reason.
|
|
232
|
-
// 2. If reasonBytes is longer than 123 bytes, then throw a
|
|
233
|
-
// "SyntaxError" DOMException.
|
|
234
|
-
reasonByteLength = Buffer.byteLength(reason)
|
|
235
|
-
|
|
236
|
-
if (reasonByteLength > 123) {
|
|
237
|
-
throw new DOMException(
|
|
238
|
-
`Reason must be less than 123 bytes; received ${reasonByteLength}`,
|
|
239
|
-
'SyntaxError'
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
187
|
+
// 2. If reason is the special value "missing", then set reason to the empty string.
|
|
188
|
+
reason ??= ''
|
|
243
189
|
|
|
244
|
-
// 3.
|
|
245
|
-
closeWebSocketConnection(this.#handler, code, reason,
|
|
190
|
+
// 3. Close the WebSocket with this, code, and reason.
|
|
191
|
+
closeWebSocketConnection(this.#handler, code, reason, true)
|
|
246
192
|
}
|
|
247
193
|
|
|
248
194
|
/**
|
|
@@ -324,7 +270,7 @@ class WebSocket extends EventTarget {
|
|
|
324
270
|
this.#sendQueue.add(data, () => {
|
|
325
271
|
this.#bufferedAmount -= data.byteLength
|
|
326
272
|
}, sendHints.typedArray)
|
|
327
|
-
} else if (data
|
|
273
|
+
} else if (webidl.is.Blob(data)) {
|
|
328
274
|
// If the WebSocket connection is established, and the WebSocket
|
|
329
275
|
// closing handshake has not yet started, then the user agent must
|
|
330
276
|
// send a WebSocket Message comprised of data using a binary frame
|
|
@@ -515,15 +461,7 @@ class WebSocket extends EventTarget {
|
|
|
515
461
|
fireEvent('open', this)
|
|
516
462
|
}
|
|
517
463
|
|
|
518
|
-
#onFail (reason) {
|
|
519
|
-
this.#controller.abort()
|
|
520
|
-
|
|
521
|
-
if (this.#handler.socket && !this.#handler.socket.destroyed) {
|
|
522
|
-
this.#handler.socket.destroy()
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
this.#handler.readyState = states.CLOSED
|
|
526
|
-
|
|
464
|
+
#onFail (code, reason) {
|
|
527
465
|
if (reason) {
|
|
528
466
|
// TODO: process.nextTick
|
|
529
467
|
fireEvent('error', this, (type, init) => new ErrorEvent(type, init), {
|
|
@@ -531,6 +469,16 @@ class WebSocket extends EventTarget {
|
|
|
531
469
|
message: reason
|
|
532
470
|
})
|
|
533
471
|
}
|
|
472
|
+
|
|
473
|
+
if (!this.#handler.wasEverConnected) {
|
|
474
|
+
this.#handler.readyState = states.CLOSED
|
|
475
|
+
|
|
476
|
+
// If the WebSocket connection could not be established, it is also said
|
|
477
|
+
// that _The WebSocket Connection is Closed_, but not _cleanly_.
|
|
478
|
+
fireEvent('close', this, (type, init) => new CloseEvent(type, init), {
|
|
479
|
+
wasClean: false, code, reason
|
|
480
|
+
})
|
|
481
|
+
}
|
|
534
482
|
}
|
|
535
483
|
|
|
536
484
|
#onMessage (type, data) {
|
|
@@ -548,7 +496,7 @@ class WebSocket extends EventTarget {
|
|
|
548
496
|
try {
|
|
549
497
|
dataForEvent = utf8Decode(data)
|
|
550
498
|
} catch {
|
|
551
|
-
failWebsocketConnection(this.#handler, 'Received invalid UTF-8 in text frame.')
|
|
499
|
+
failWebsocketConnection(this.#handler, 1007, 'Received invalid UTF-8 in text frame.')
|
|
552
500
|
return
|
|
553
501
|
}
|
|
554
502
|
} else if (type === opcodes.BINARY) {
|
|
@@ -574,81 +522,6 @@ class WebSocket extends EventTarget {
|
|
|
574
522
|
})
|
|
575
523
|
}
|
|
576
524
|
|
|
577
|
-
#onClose (code, reason, reasonByteLength) {
|
|
578
|
-
if (isClosing(this.#handler.readyState) || isClosed(this.#handler.readyState)) {
|
|
579
|
-
// If this's ready state is CLOSING (2) or CLOSED (3)
|
|
580
|
-
// Do nothing.
|
|
581
|
-
} else if (!isEstablished(this.#handler.readyState)) {
|
|
582
|
-
// If the WebSocket connection is not yet established
|
|
583
|
-
// Fail the WebSocket connection and set this's ready state
|
|
584
|
-
// to CLOSING (2).
|
|
585
|
-
failWebsocketConnection(this.#handler, 'Connection was closed before it was established.')
|
|
586
|
-
this.#handler.readyState = states.CLOSING
|
|
587
|
-
} else if (this.#handler.closeState === sentCloseFrameState.NOT_SENT) {
|
|
588
|
-
// If the WebSocket closing handshake has not yet been started
|
|
589
|
-
// Start the WebSocket closing handshake and set this's ready
|
|
590
|
-
// state to CLOSING (2).
|
|
591
|
-
// - If neither code nor reason is present, the WebSocket Close
|
|
592
|
-
// message must not have a body.
|
|
593
|
-
// - If code is present, then the status code to use in the
|
|
594
|
-
// WebSocket Close message must be the integer given by code.
|
|
595
|
-
// - If reason is also present, then reasonBytes must be
|
|
596
|
-
// provided in the Close message after the status code.
|
|
597
|
-
|
|
598
|
-
this.#handler.closeState = sentCloseFrameState.PROCESSING
|
|
599
|
-
|
|
600
|
-
const frame = new WebsocketFrameSend()
|
|
601
|
-
|
|
602
|
-
// If neither code nor reason is present, the WebSocket Close
|
|
603
|
-
// message must not have a body.
|
|
604
|
-
|
|
605
|
-
// If code is present, then the status code to use in the
|
|
606
|
-
// WebSocket Close message must be the integer given by code.
|
|
607
|
-
if (code !== undefined && reason === undefined) {
|
|
608
|
-
frame.frameData = Buffer.allocUnsafe(2)
|
|
609
|
-
frame.frameData.writeUInt16BE(code, 0)
|
|
610
|
-
} else if (code !== undefined && reason !== undefined) {
|
|
611
|
-
// If reason is also present, then reasonBytes must be
|
|
612
|
-
// provided in the Close message after the status code.
|
|
613
|
-
frame.frameData = Buffer.allocUnsafe(2 + reasonByteLength)
|
|
614
|
-
frame.frameData.writeUInt16BE(code, 0)
|
|
615
|
-
// the body MAY contain UTF-8-encoded data with value /reason/
|
|
616
|
-
frame.frameData.write(reason, 2, 'utf-8')
|
|
617
|
-
} else {
|
|
618
|
-
frame.frameData = emptyBuffer
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
this.#handler.socket.write(frame.createFrame(opcodes.CLOSE))
|
|
622
|
-
|
|
623
|
-
this.#handler.closeState = sentCloseFrameState.SENT
|
|
624
|
-
|
|
625
|
-
// Upon either sending or receiving a Close control frame, it is said
|
|
626
|
-
// that _The WebSocket Closing Handshake is Started_ and that the
|
|
627
|
-
// WebSocket connection is in the CLOSING state.
|
|
628
|
-
this.#handler.readyState = states.CLOSING
|
|
629
|
-
} else {
|
|
630
|
-
// Otherwise
|
|
631
|
-
// Set this's ready state to CLOSING (2).
|
|
632
|
-
this.#handler.readyState = states.CLOSING
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
#onParserError (err) {
|
|
637
|
-
let message
|
|
638
|
-
let code
|
|
639
|
-
|
|
640
|
-
if (err instanceof CloseEvent) {
|
|
641
|
-
message = err.reason
|
|
642
|
-
code = err.code
|
|
643
|
-
} else {
|
|
644
|
-
message = err.message
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
fireEvent('error', this, () => new ErrorEvent('error', { error: err, message }))
|
|
648
|
-
|
|
649
|
-
closeWebSocketConnection(this.#handler, code)
|
|
650
|
-
}
|
|
651
|
-
|
|
652
525
|
#onParserDrain () {
|
|
653
526
|
this.#handler.socket.resume()
|
|
654
527
|
}
|
|
@@ -661,7 +534,9 @@ class WebSocket extends EventTarget {
|
|
|
661
534
|
// If the TCP connection was closed after the
|
|
662
535
|
// WebSocket closing handshake was completed, the WebSocket connection
|
|
663
536
|
// is said to have been closed _cleanly_.
|
|
664
|
-
const wasClean =
|
|
537
|
+
const wasClean =
|
|
538
|
+
this.#handler.closeState.has(sentCloseFrameState.SENT) &&
|
|
539
|
+
this.#handler.closeState.has(sentCloseFrameState.RECEIVED)
|
|
665
540
|
|
|
666
541
|
let code = 1005
|
|
667
542
|
let reason = ''
|
|
@@ -671,7 +546,7 @@ class WebSocket extends EventTarget {
|
|
|
671
546
|
if (result && !result.error) {
|
|
672
547
|
code = result.code ?? 1005
|
|
673
548
|
reason = result.reason
|
|
674
|
-
} else if (!this.#handler.
|
|
549
|
+
} else if (!this.#handler.closeState.has(sentCloseFrameState.RECEIVED)) {
|
|
675
550
|
// If _The WebSocket
|
|
676
551
|
// Connection is Closed_ and no Close control frame was received by the
|
|
677
552
|
// endpoint (such as could occur if the underlying transport connection
|
|
@@ -793,7 +668,7 @@ webidl.converters['DOMString or sequence<DOMString> or WebSocketInit'] = functio
|
|
|
793
668
|
|
|
794
669
|
webidl.converters.WebSocketSendData = function (V) {
|
|
795
670
|
if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
|
|
796
|
-
if (V
|
|
671
|
+
if (webidl.is.Blob(V)) {
|
|
797
672
|
return V
|
|
798
673
|
}
|
|
799
674
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "undici",
|
|
3
|
-
"version": "7.0.0-alpha.
|
|
3
|
+
"version": "7.0.0-alpha.2",
|
|
4
4
|
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
|
5
5
|
"homepage": "https://undici.nodejs.org",
|
|
6
6
|
"bugs": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"main": "index.js",
|
|
63
63
|
"types": "index.d.ts",
|
|
64
64
|
"scripts": {
|
|
65
|
-
"build:node": "
|
|
65
|
+
"build:node": "esbuild index-fetch.js --bundle --platform=node --outfile=undici-fetch.js --define:esbuildDetection=1 --keep-names && node scripts/strip-comments.js",
|
|
66
66
|
"prebuild:wasm": "node build/wasm.js --prebuild",
|
|
67
67
|
"build:wasm": "node build/wasm.js --docker",
|
|
68
68
|
"generate-pem": "node scripts/generate-pem.js",
|
|
@@ -105,12 +105,13 @@
|
|
|
105
105
|
"@fastify/busboy": "3.0.0",
|
|
106
106
|
"@matteo.collina/tspl": "^0.1.1",
|
|
107
107
|
"@sinonjs/fake-timers": "^11.1.0",
|
|
108
|
-
"@types/node": "
|
|
108
|
+
"@types/node": "^18.19.50",
|
|
109
109
|
"abort-controller": "^3.0.0",
|
|
110
110
|
"borp": "^0.17.0",
|
|
111
111
|
"c8": "^10.0.0",
|
|
112
112
|
"cross-env": "^7.0.3",
|
|
113
113
|
"dns-packet": "^5.4.0",
|
|
114
|
+
"esbuild": "^0.24.0",
|
|
114
115
|
"eslint": "^9.9.0",
|
|
115
116
|
"fast-check": "^3.17.1",
|
|
116
117
|
"https-pem": "^3.0.0",
|
|
@@ -118,10 +119,9 @@
|
|
|
118
119
|
"jest": "^29.0.2",
|
|
119
120
|
"neostandard": "^0.11.2",
|
|
120
121
|
"node-forge": "^1.3.1",
|
|
121
|
-
"pre-commit": "^1.2.2",
|
|
122
122
|
"proxy": "^2.1.1",
|
|
123
|
-
"tsd": "^0.31.
|
|
124
|
-
"typescript": "^5.
|
|
123
|
+
"tsd": "^0.31.2",
|
|
124
|
+
"typescript": "^5.6.2",
|
|
125
125
|
"ws": "^8.11.0"
|
|
126
126
|
},
|
|
127
127
|
"engines": {
|
package/types/interceptors.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Dispatcher from './dispatcher'
|
|
2
2
|
import RetryHandler from './retry-handler'
|
|
3
|
+
import { LookupOptions } from 'node:dns'
|
|
3
4
|
|
|
4
5
|
export default Interceptors
|
|
5
6
|
|
|
@@ -7,11 +8,24 @@ declare namespace Interceptors {
|
|
|
7
8
|
export type DumpInterceptorOpts = { maxSize?: number }
|
|
8
9
|
export type RetryInterceptorOpts = RetryHandler.RetryOptions
|
|
9
10
|
export type RedirectInterceptorOpts = { maxRedirections?: number }
|
|
11
|
+
|
|
10
12
|
export type ResponseErrorInterceptorOpts = { throwOnError: boolean }
|
|
13
|
+
// DNS interceptor
|
|
14
|
+
export type DNSInterceptorRecord = { address: string, ttl: number, family: 4 | 6 }
|
|
15
|
+
export type DNSInterceptorOriginRecords = { 4: { ips: DNSInterceptorRecord[] } | null, 6: { ips: DNSInterceptorRecord[] } | null }
|
|
16
|
+
export type DNSInterceptorOpts = {
|
|
17
|
+
maxTTL?: number
|
|
18
|
+
maxItems?: number
|
|
19
|
+
lookup?: (hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, addresses: DNSInterceptorRecord[]) => void) => void
|
|
20
|
+
pick?: (origin: URL, records: DNSInterceptorOriginRecords, affinity: 4 | 6) => DNSInterceptorRecord
|
|
21
|
+
dualStack?: boolean
|
|
22
|
+
affinity?: 4 | 6
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
export function createRedirectInterceptor (opts: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
13
26
|
export function dump (opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
14
27
|
export function retry (opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
15
28
|
export function redirect (opts?: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
16
29
|
export function responseError (opts?: ResponseErrorInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
30
|
+
export function dns (opts?: DNSInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
|
17
31
|
}
|
package/types/mock-agent.d.ts
CHANGED
package/types/readable.d.ts
CHANGED
|
@@ -4,11 +4,13 @@ import { Blob } from 'buffer'
|
|
|
4
4
|
export default BodyReadable
|
|
5
5
|
|
|
6
6
|
declare class BodyReadable extends Readable {
|
|
7
|
-
constructor (
|
|
8
|
-
resume
|
|
9
|
-
abort
|
|
10
|
-
contentType?: string
|
|
11
|
-
|
|
7
|
+
constructor (opts: {
|
|
8
|
+
resume: (this: Readable, size: number) => void | null;
|
|
9
|
+
abort: () => void | null;
|
|
10
|
+
contentType?: string;
|
|
11
|
+
contentLength?: number;
|
|
12
|
+
highWaterMark?: number;
|
|
13
|
+
})
|
|
12
14
|
|
|
13
15
|
/** Consumes and returns the body as a string
|
|
14
16
|
* https://fetch.spec.whatwg.org/#dom-body-text
|
|
@@ -59,7 +61,8 @@ declare class BodyReadable extends Readable {
|
|
|
59
61
|
readonly body: never | undefined
|
|
60
62
|
|
|
61
63
|
/** Dumps the response body by reading `limit` number of bytes.
|
|
62
|
-
* @param opts.limit Number of bytes to read (optional) - Default:
|
|
64
|
+
* @param opts.limit Number of bytes to read (optional) - Default: 131072
|
|
65
|
+
* @param opts.signal AbortSignal to cancel the operation (optional)
|
|
63
66
|
*/
|
|
64
|
-
dump (opts?: { limit: number }): Promise<void>
|
|
67
|
+
dump (opts?: { limit: number; signal?: AbortSignal }): Promise<void>
|
|
65
68
|
}
|
package/types/webidl.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// These types are not exported, and are only used internally
|
|
2
|
+
import * as undici from './index'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Take in an unknown value and return one that is of type T
|
|
@@ -49,7 +50,7 @@ interface WebidlUtil {
|
|
|
49
50
|
/**
|
|
50
51
|
* @see https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
|
|
51
52
|
*/
|
|
52
|
-
Type (object: unknown): WebIDLTypes
|
|
53
|
+
Type (object: unknown): WebIDLTypes[keyof WebIDLTypes]
|
|
53
54
|
|
|
54
55
|
TypeValueToString (o: unknown):
|
|
55
56
|
| 'Undefined'
|
|
@@ -82,6 +83,8 @@ interface WebidlUtil {
|
|
|
82
83
|
* Stringifies {@param V}
|
|
83
84
|
*/
|
|
84
85
|
Stringify (V: any): string
|
|
86
|
+
|
|
87
|
+
MakeTypeAssertion <T extends { prototype: T }>(Prototype: T['prototype']): (arg: any) => arg is T
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
interface WebidlConverters {
|
|
@@ -173,10 +176,27 @@ interface WebidlConverters {
|
|
|
173
176
|
[Key: string]: (...args: any[]) => unknown
|
|
174
177
|
}
|
|
175
178
|
|
|
179
|
+
type IsAssertion<T> = (arg: any) => arg is T
|
|
180
|
+
|
|
181
|
+
interface WebidlIs {
|
|
182
|
+
Request: IsAssertion<undici.Request>
|
|
183
|
+
Response: IsAssertion<undici.Response>
|
|
184
|
+
ReadableStream: IsAssertion<ReadableStream>
|
|
185
|
+
Blob: IsAssertion<Blob>
|
|
186
|
+
URLSearchParams: IsAssertion<URLSearchParams>
|
|
187
|
+
File: IsAssertion<File>
|
|
188
|
+
FormData: IsAssertion<undici.FormData>
|
|
189
|
+
URL: IsAssertion<URL>
|
|
190
|
+
WebSocketError: IsAssertion<undici.WebSocketError>
|
|
191
|
+
AbortSignal: IsAssertion<AbortSignal>
|
|
192
|
+
MessagePort: IsAssertion<MessagePort>
|
|
193
|
+
}
|
|
194
|
+
|
|
176
195
|
export interface Webidl {
|
|
177
196
|
errors: WebidlErrors
|
|
178
197
|
util: WebidlUtil
|
|
179
198
|
converters: WebidlConverters
|
|
199
|
+
is: WebidlIs
|
|
180
200
|
|
|
181
201
|
/**
|
|
182
202
|
* @description Performs a brand-check on {@param V} to ensure it is a
|
|
@@ -184,7 +204,7 @@ export interface Webidl {
|
|
|
184
204
|
*/
|
|
185
205
|
brandCheck <Interface extends new () => unknown>(V: unknown, cls: Interface): asserts V is Interface
|
|
186
206
|
|
|
187
|
-
brandCheckMultiple <Interfaces extends (new () => unknown)[]> (
|
|
207
|
+
brandCheckMultiple <Interfaces extends (new () => unknown)[]> (list: Interfaces): (V: any) => asserts V is Interfaces[number]
|
|
188
208
|
|
|
189
209
|
/**
|
|
190
210
|
* @see https://webidl.spec.whatwg.org/#es-sequence
|
|
@@ -207,11 +227,11 @@ export interface Webidl {
|
|
|
207
227
|
* Similar to {@link Webidl.brandCheck} but allows skipping the check if third party
|
|
208
228
|
* interfaces are allowed.
|
|
209
229
|
*/
|
|
210
|
-
interfaceConverter <Interface>(
|
|
230
|
+
interfaceConverter <Interface>(typeCheck: IsAssertion<Interface>, name: string): (
|
|
211
231
|
V: unknown,
|
|
212
232
|
prefix: string,
|
|
213
233
|
argument: string
|
|
214
|
-
) => asserts V is
|
|
234
|
+
) => asserts V is Interface
|
|
215
235
|
|
|
216
236
|
// TODO(@KhafraDev): a type could likely be implemented that can infer the return type
|
|
217
237
|
// from the converters given?
|
package/types/websocket.d.ts
CHANGED
|
@@ -148,3 +148,36 @@ interface WebSocketInit {
|
|
|
148
148
|
dispatcher?: Dispatcher,
|
|
149
149
|
headers?: HeadersInit
|
|
150
150
|
}
|
|
151
|
+
|
|
152
|
+
interface WebSocketStreamOptions {
|
|
153
|
+
protocols?: string | string[]
|
|
154
|
+
signal?: AbortSignal
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
interface WebSocketCloseInfo {
|
|
158
|
+
closeCode: number
|
|
159
|
+
reason: string
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface WebSocketStream {
|
|
163
|
+
closed: Promise<WebSocketCloseInfo>
|
|
164
|
+
opened: Promise<{
|
|
165
|
+
extensions: string
|
|
166
|
+
protocol: string
|
|
167
|
+
readable: ReadableStream
|
|
168
|
+
writable: WritableStream
|
|
169
|
+
}>
|
|
170
|
+
url: string
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export declare const WebSocketStream: {
|
|
174
|
+
prototype: WebSocketStream
|
|
175
|
+
new (url: string | URL, options?: WebSocketStreamOptions): WebSocketStream
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface WebSocketError extends Event, WebSocketCloseInfo {}
|
|
179
|
+
|
|
180
|
+
export declare const WebSocketError: {
|
|
181
|
+
prototype: WebSocketError
|
|
182
|
+
new (type: string, init?: WebSocketCloseInfo): WebSocketError
|
|
183
|
+
}
|
package/lib/mock/pluralizer.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const singulars = {
|
|
4
|
-
pronoun: 'it',
|
|
5
|
-
is: 'is',
|
|
6
|
-
was: 'was',
|
|
7
|
-
this: 'this'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const plurals = {
|
|
11
|
-
pronoun: 'they',
|
|
12
|
-
is: 'are',
|
|
13
|
-
was: 'were',
|
|
14
|
-
this: 'these'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = class Pluralizer {
|
|
18
|
-
constructor (singular, plural) {
|
|
19
|
-
this.singular = singular
|
|
20
|
-
this.plural = plural
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pluralize (count) {
|
|
24
|
-
const one = count === 1
|
|
25
|
-
const keys = one ? singulars : plurals
|
|
26
|
-
const noun = one ? this.singular : this.plural
|
|
27
|
-
return { ...keys, count, noun }
|
|
28
|
-
}
|
|
29
|
-
}
|
package/lib/web/cache/symbols.js
DELETED