undici 7.11.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 +4 -2
- package/docs/docs/api/DiagnosticsChannel.md +7 -4
- package/docs/docs/api/Dispatcher.md +2 -2
- package/docs/docs/api/WebSocket.md +27 -0
- package/index.js +3 -1
- package/lib/api/readable.js +1 -3
- package/lib/core/request.js +6 -1
- package/lib/core/tree.js +1 -1
- package/lib/dispatcher/client-h1.js +8 -17
- package/lib/handler/cache-handler.js +4 -1
- package/lib/handler/redirect-handler.js +2 -2
- package/lib/interceptor/cache.js +2 -2
- package/lib/interceptor/redirect.js +1 -1
- package/lib/util/cache.js +1 -1
- package/lib/util/promise.js +28 -0
- package/lib/web/cache/cache.js +10 -8
- package/lib/web/fetch/body.js +35 -23
- package/lib/web/fetch/index.js +221 -225
- package/lib/web/fetch/request.js +15 -7
- package/lib/web/fetch/response.js +5 -3
- package/lib/web/fetch/util.js +21 -23
- package/lib/web/websocket/connection.js +0 -9
- package/lib/web/websocket/receiver.js +2 -12
- package/lib/web/websocket/stream/websocketstream.js +7 -4
- package/lib/web/websocket/websocket.js +57 -1
- package/package.json +2 -2
- package/types/webidl.d.ts +10 -0
- package/types/websocket.d.ts +2 -0
- package/lib/web/fetch/dispatcher-weakref.js +0 -5
package/README.md
CHANGED
|
@@ -114,8 +114,10 @@ const response = await fetch('https://api.example.com/data');
|
|
|
114
114
|
#### Use Built-in Fetch When:
|
|
115
115
|
- You want zero dependencies
|
|
116
116
|
- Building isomorphic code that runs in browsers and Node.js
|
|
117
|
+
- Publishing to npm and want to maximize compatibility with JS runtimes
|
|
117
118
|
- Simple HTTP requests without advanced configuration
|
|
118
|
-
- You're
|
|
119
|
+
- You're publishing to npm and you want to maximize compatiblity
|
|
120
|
+
- You don't depend on features from a specific version of undici
|
|
119
121
|
|
|
120
122
|
#### Use Undici Module When:
|
|
121
123
|
- You need the latest undici features and performance improvements
|
|
@@ -209,7 +211,7 @@ The `install()` function adds the following classes to `globalThis`:
|
|
|
209
211
|
- `fetch` - The fetch function
|
|
210
212
|
- `Headers` - HTTP headers management
|
|
211
213
|
- `Response` - HTTP response representation
|
|
212
|
-
- `Request` - HTTP request representation
|
|
214
|
+
- `Request` - HTTP request representation
|
|
213
215
|
- `FormData` - Form data handling
|
|
214
216
|
- `WebSocket` - WebSocket client
|
|
215
217
|
- `CloseEvent`, `ErrorEvent`, `MessageEvent` - WebSocket events
|
|
@@ -169,10 +169,11 @@ This message is published after the client has successfully connected to a serve
|
|
|
169
169
|
```js
|
|
170
170
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
171
171
|
|
|
172
|
-
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions }) => {
|
|
172
|
+
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions, websocket }) => {
|
|
173
173
|
console.log(address) // address, family, and port
|
|
174
174
|
console.log(protocol) // negotiated subprotocols
|
|
175
175
|
console.log(extensions) // negotiated extensions
|
|
176
|
+
console.log(websocket) // the WebSocket instance
|
|
176
177
|
})
|
|
177
178
|
```
|
|
178
179
|
|
|
@@ -184,7 +185,7 @@ This message is published after the connection has closed.
|
|
|
184
185
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
185
186
|
|
|
186
187
|
diagnosticsChannel.channel('undici:websocket:close').subscribe(({ websocket, code, reason }) => {
|
|
187
|
-
console.log(websocket) // the WebSocket
|
|
188
|
+
console.log(websocket) // the WebSocket instance
|
|
188
189
|
console.log(code) // the closing status code
|
|
189
190
|
console.log(reason) // the closing reason
|
|
190
191
|
})
|
|
@@ -209,9 +210,10 @@ This message is published after the client receives a ping frame, if the connect
|
|
|
209
210
|
```js
|
|
210
211
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
211
212
|
|
|
212
|
-
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload }) => {
|
|
213
|
+
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload, websocket }) => {
|
|
213
214
|
// a Buffer or undefined, containing the optional application data of the frame
|
|
214
215
|
console.log(payload)
|
|
216
|
+
console.log(websocket) // the WebSocket instance
|
|
215
217
|
})
|
|
216
218
|
```
|
|
217
219
|
|
|
@@ -222,8 +224,9 @@ This message is published after the client receives a pong frame.
|
|
|
222
224
|
```js
|
|
223
225
|
import diagnosticsChannel from 'diagnostics_channel'
|
|
224
226
|
|
|
225
|
-
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {
|
|
227
|
+
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload, websocket }) => {
|
|
226
228
|
// a Buffer or undefined, containing the optional application data of the frame
|
|
227
229
|
console.log(payload)
|
|
230
|
+
console.log(websocket) // the WebSocket instance
|
|
228
231
|
})
|
|
229
232
|
```
|
|
@@ -1103,8 +1103,8 @@ The `cache` interceptor implements client-side response caching as described in
|
|
|
1103
1103
|
|
|
1104
1104
|
- `store` - The [`CacheStore`](/docs/docs/api/CacheStore.md) to store and retrieve responses from. Default is [`MemoryCacheStore`](/docs/docs/api/CacheStore.md#memorycachestore).
|
|
1105
1105
|
- `methods` - The [**safe** HTTP methods](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1) to cache the response of.
|
|
1106
|
-
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration. If this isn't present, responses
|
|
1107
|
-
- `type` - The type of cache for Undici to act as. Can be `shared` or `private`. Default `shared`.
|
|
1106
|
+
- `cacheByDefault` - The default expiration time to cache responses by if they don't have an explicit expiration and cannot have an heuristic expiry computed. If this isn't present, responses neither with an explicit expiration nor heuristically cacheable will not be cached. Default `undefined`.
|
|
1107
|
+
- `type` - The [type of cache](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching#types_of_caches) for Undici to act as. Can be `shared` or `private`. Default `shared`. `private` implies privately cacheable responses will be cached and potentially shared with other users of your application.
|
|
1108
1108
|
|
|
1109
1109
|
## Instance Events
|
|
1110
1110
|
|
|
@@ -78,6 +78,33 @@ setInterval(() => write(), 5000)
|
|
|
78
78
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
## ping(websocket, payload)
|
|
82
|
+
Arguments:
|
|
83
|
+
|
|
84
|
+
* **websocket** `WebSocket` - The WebSocket instance to send the ping frame on
|
|
85
|
+
* **payload** `Buffer|undefined` (optional) - Optional payload data to include with the ping frame. Must not exceed 125 bytes.
|
|
86
|
+
|
|
87
|
+
Sends a ping frame to the WebSocket server. The server must respond with a pong frame containing the same payload data. This can be used for keepalive purposes or to verify that the connection is still active.
|
|
88
|
+
|
|
89
|
+
### Example:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { WebSocket, ping } from 'undici'
|
|
93
|
+
|
|
94
|
+
const ws = new WebSocket('wss://echo.websocket.events')
|
|
95
|
+
|
|
96
|
+
ws.addEventListener('open', () => {
|
|
97
|
+
// Send ping with no payload
|
|
98
|
+
ping(ws)
|
|
99
|
+
|
|
100
|
+
// Send ping with payload
|
|
101
|
+
const payload = Buffer.from('hello')
|
|
102
|
+
ping(ws, payload)
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Note**: A ping frame cannot have a payload larger than 125 bytes. The ping will only be sent if the WebSocket connection is in the OPEN state.
|
|
107
|
+
|
|
81
108
|
## Read More
|
|
82
109
|
|
|
83
110
|
- [MDN - WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
package/index.js
CHANGED
|
@@ -157,10 +157,12 @@ module.exports.parseMIMEType = parseMIMEType
|
|
|
157
157
|
module.exports.serializeAMimeType = serializeAMimeType
|
|
158
158
|
|
|
159
159
|
const { CloseEvent, ErrorEvent, MessageEvent } = require('./lib/web/websocket/events')
|
|
160
|
-
|
|
160
|
+
const { WebSocket, ping } = require('./lib/web/websocket/websocket')
|
|
161
|
+
module.exports.WebSocket = WebSocket
|
|
161
162
|
module.exports.CloseEvent = CloseEvent
|
|
162
163
|
module.exports.ErrorEvent = ErrorEvent
|
|
163
164
|
module.exports.MessageEvent = MessageEvent
|
|
165
|
+
module.exports.ping = ping
|
|
164
166
|
|
|
165
167
|
module.exports.WebSocketStream = require('./lib/web/websocket/stream/websocketstream').WebSocketStream
|
|
166
168
|
module.exports.WebSocketError = require('./lib/web/websocket/stream/websocketerror').WebSocketError
|
package/lib/api/readable.js
CHANGED
|
@@ -89,9 +89,7 @@ class BodyReadable extends Readable {
|
|
|
89
89
|
// promise (i.e micro tick) for installing an 'error' listener will
|
|
90
90
|
// never get a chance and will always encounter an unhandled exception.
|
|
91
91
|
if (!this[kUsed]) {
|
|
92
|
-
setImmediate(
|
|
93
|
-
callback(err)
|
|
94
|
-
})
|
|
92
|
+
setImmediate(callback, err)
|
|
95
93
|
} else {
|
|
96
94
|
callback(err)
|
|
97
95
|
}
|
package/lib/core/request.js
CHANGED
|
@@ -42,7 +42,8 @@ class Request {
|
|
|
42
42
|
reset,
|
|
43
43
|
expectContinue,
|
|
44
44
|
servername,
|
|
45
|
-
throwOnError
|
|
45
|
+
throwOnError,
|
|
46
|
+
maxRedirections
|
|
46
47
|
}, handler) {
|
|
47
48
|
if (typeof path !== 'string') {
|
|
48
49
|
throw new InvalidArgumentError('path must be a string')
|
|
@@ -86,6 +87,10 @@ class Request {
|
|
|
86
87
|
throw new InvalidArgumentError('invalid throwOnError')
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
if (maxRedirections != null && maxRedirections !== 0) {
|
|
91
|
+
throw new InvalidArgumentError('maxRedirections is not supported, use the redirect interceptor')
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
this.headersTimeout = headersTimeout
|
|
90
95
|
|
|
91
96
|
this.bodyTimeout = bodyTimeout
|
package/lib/core/tree.js
CHANGED
|
@@ -60,12 +60,12 @@ const removeAllListeners = util.removeAllListeners
|
|
|
60
60
|
|
|
61
61
|
let extractBody
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
function lazyllhttp () {
|
|
64
64
|
const llhttpWasmData = process.env.JEST_WORKER_ID ? require('../llhttp/llhttp-wasm.js') : undefined
|
|
65
65
|
|
|
66
66
|
let mod
|
|
67
67
|
try {
|
|
68
|
-
mod =
|
|
68
|
+
mod = new WebAssembly.Module(require('../llhttp/llhttp_simd-wasm.js'))
|
|
69
69
|
} catch (e) {
|
|
70
70
|
/* istanbul ignore next */
|
|
71
71
|
|
|
@@ -73,10 +73,10 @@ async function lazyllhttp () {
|
|
|
73
73
|
// being enabled, but the occurring of this other error
|
|
74
74
|
// * https://github.com/emscripten-core/emscripten/issues/11495
|
|
75
75
|
// got me to remove that check to avoid breaking Node 12.
|
|
76
|
-
mod =
|
|
76
|
+
mod = new WebAssembly.Module(llhttpWasmData || require('../llhttp/llhttp-wasm.js'))
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
return
|
|
79
|
+
return new WebAssembly.Instance(mod, {
|
|
80
80
|
env: {
|
|
81
81
|
/**
|
|
82
82
|
* @param {number} p
|
|
@@ -165,11 +165,6 @@ async function lazyllhttp () {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
let llhttpInstance = null
|
|
168
|
-
/**
|
|
169
|
-
* @type {Promise<WebAssembly.Instance>|null}
|
|
170
|
-
*/
|
|
171
|
-
let llhttpPromise = lazyllhttp()
|
|
172
|
-
llhttpPromise.catch()
|
|
173
168
|
|
|
174
169
|
/**
|
|
175
170
|
* @type {Parser|null}
|
|
@@ -732,7 +727,7 @@ class Parser {
|
|
|
732
727
|
// We must wait a full event loop cycle to reuse this socket to make sure
|
|
733
728
|
// that non-spec compliant servers are not closing the connection even if they
|
|
734
729
|
// said they won't.
|
|
735
|
-
setImmediate(
|
|
730
|
+
setImmediate(client[kResume])
|
|
736
731
|
} else {
|
|
737
732
|
client[kResume]()
|
|
738
733
|
}
|
|
@@ -769,11 +764,7 @@ async function connectH1 (client, socket) {
|
|
|
769
764
|
client[kSocket] = socket
|
|
770
765
|
|
|
771
766
|
if (!llhttpInstance) {
|
|
772
|
-
|
|
773
|
-
socket.on('error', noop)
|
|
774
|
-
llhttpInstance = await llhttpPromise
|
|
775
|
-
llhttpPromise = null
|
|
776
|
-
socket.off('error', noop)
|
|
767
|
+
llhttpInstance = lazyllhttp()
|
|
777
768
|
}
|
|
778
769
|
|
|
779
770
|
if (socket.errored) {
|
|
@@ -1297,9 +1288,9 @@ function writeStream (abort, body, client, request, socket, contentLength, heade
|
|
|
1297
1288
|
.on('error', onFinished)
|
|
1298
1289
|
|
|
1299
1290
|
if (body.errorEmitted ?? body.errored) {
|
|
1300
|
-
setImmediate(
|
|
1291
|
+
setImmediate(onFinished, body.errored)
|
|
1301
1292
|
} else if (body.endEmitted ?? body.readableEnded) {
|
|
1302
|
-
setImmediate(
|
|
1293
|
+
setImmediate(onFinished, null)
|
|
1303
1294
|
}
|
|
1304
1295
|
|
|
1305
1296
|
if (body.closeEmitted ?? body.closed) {
|
|
@@ -241,7 +241,10 @@ class CacheHandler {
|
|
|
241
241
|
* @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
|
|
242
242
|
*/
|
|
243
243
|
function canCacheResponse (cacheType, statusCode, resHeaders, cacheControlDirectives) {
|
|
244
|
-
|
|
244
|
+
// Allow caching for status codes 200 and 307 (original behavior)
|
|
245
|
+
// Also allow caching for other status codes that are heuristically cacheable
|
|
246
|
+
// when they have explicit cache directives
|
|
247
|
+
if (statusCode !== 200 && statusCode !== 307 && !HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode)) {
|
|
245
248
|
return false
|
|
246
249
|
}
|
|
247
250
|
|
|
@@ -42,7 +42,8 @@ class RedirectHandler {
|
|
|
42
42
|
|
|
43
43
|
this.dispatch = dispatch
|
|
44
44
|
this.location = null
|
|
45
|
-
|
|
45
|
+
const { maxRedirections: _, ...cleanOpts } = opts
|
|
46
|
+
this.opts = cleanOpts // opts must be a copy, exclude maxRedirections
|
|
46
47
|
this.maxRedirections = maxRedirections
|
|
47
48
|
this.handler = handler
|
|
48
49
|
this.history = []
|
|
@@ -138,7 +139,6 @@ class RedirectHandler {
|
|
|
138
139
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin)
|
|
139
140
|
this.opts.path = path
|
|
140
141
|
this.opts.origin = origin
|
|
141
|
-
this.opts.maxRedirections = 0
|
|
142
142
|
this.opts.query = null
|
|
143
143
|
}
|
|
144
144
|
|
package/lib/interceptor/cache.js
CHANGED
|
@@ -301,11 +301,11 @@ module.exports = (opts = {}) => {
|
|
|
301
301
|
assertCacheMethods(methods, 'opts.methods')
|
|
302
302
|
|
|
303
303
|
if (typeof cacheByDefault !== 'undefined' && typeof cacheByDefault !== 'number') {
|
|
304
|
-
throw new TypeError(`
|
|
304
|
+
throw new TypeError(`expected opts.cacheByDefault to be number or undefined, got ${typeof cacheByDefault}`)
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
if (typeof type !== 'undefined' && type !== 'shared' && type !== 'private') {
|
|
308
|
-
throw new TypeError(`
|
|
308
|
+
throw new TypeError(`expected opts.type to be shared, private, or undefined, got ${typeof type}`)
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
const globalOpts = {
|
|
@@ -11,7 +11,7 @@ function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections }
|
|
|
11
11
|
return dispatch(opts, handler)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const dispatchOpts = { ...rest
|
|
14
|
+
const dispatchOpts = { ...rest } // Stop sub dispatcher from also redirecting.
|
|
15
15
|
const redirectHandler = new RedirectHandler(dispatch, maxRedirections, dispatchOpts, handler)
|
|
16
16
|
return dispatch(dispatchOpts, redirectHandler)
|
|
17
17
|
}
|
package/lib/util/cache.js
CHANGED
|
@@ -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/web/cache/cache.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
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')
|
|
@@ -7,8 +9,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/fetch/body.js
CHANGED
|
@@ -4,7 +4,6 @@ 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
|
|
@@ -17,6 +16,8 @@ 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
|
}
|