From 40e21b3ed6096d3f660a28678541ee6088d808e3 Mon Sep 17 00:00:00 2001 From: mcollina <52195+mcollina@users.noreply.github.com> Date: Tue, 14 Apr 2026 09:37:40 +0000 Subject: [PATCH] deps: update undici to 8.1.0 --- deps/undici/src/docs/docs/api/Client.md | 2 + deps/undici/src/docs/docs/api/Dispatcher.md | 4 +- .../best-practices/migrating-from-v7-to-v8.md | 231 +++++++++ deps/undici/src/index.js | 4 +- deps/undici/src/lib/core/util.js | 6 +- deps/undici/src/lib/dispatcher/agent.js | 2 +- .../src/lib/dispatcher/balanced-pool.js | 3 - deps/undici/src/lib/dispatcher/client.js | 5 +- .../src/lib/dispatcher/dispatcher-base.js | 22 + .../src/lib/dispatcher/dispatcher1-wrapper.js | 6 + deps/undici/src/lib/dispatcher/h2c-client.js | 2 +- deps/undici/src/lib/dispatcher/pool.js | 5 +- deps/undici/src/lib/dispatcher/proxy-agent.js | 8 +- .../src/lib/dispatcher/round-robin-pool.js | 3 - .../src/lib/dispatcher/socks5-proxy-agent.js | 130 ++--- .../src/lib/handler/redirect-handler.js | 52 +- deps/undici/src/lib/interceptor/decompress.js | 3 +- deps/undici/src/lib/interceptor/dns.js | 2 +- deps/undici/src/lib/llhttp/wasm_build_env.txt | 2 +- deps/undici/src/lib/util/cache.js | 13 +- deps/undici/src/lib/util/promise.js | 28 - deps/undici/src/lib/util/runtime-features.js | 37 +- deps/undici/src/lib/web/cache/cache.js | 14 +- deps/undici/src/lib/web/fetch/body.js | 3 +- deps/undici/src/lib/web/fetch/index.js | 26 +- deps/undici/src/lib/web/fetch/util.js | 6 +- deps/undici/src/lib/web/webidl/index.js | 6 +- .../lib/web/websocket/permessage-deflate.js | 44 +- deps/undici/src/lib/web/websocket/receiver.js | 84 ++- .../web/websocket/stream/websocketstream.js | 11 +- .../undici/src/lib/web/websocket/websocket.js | 7 +- deps/undici/src/package-lock.json | 454 ++++++++-------- deps/undici/src/package.json | 6 +- deps/undici/src/types/agent.d.ts | 2 - deps/undici/src/types/client.d.ts | 30 +- deps/undici/src/types/dispatcher.d.ts | 2 - deps/undici/src/types/h2c-client.d.ts | 12 +- deps/undici/src/types/pool.d.ts | 2 - deps/undici/src/types/round-robin-pool.d.ts | 2 - deps/undici/src/types/webidl.d.ts | 1 - deps/undici/undici.js | 487 +++++++++--------- src/undici_version.h | 2 +- 42 files changed, 966 insertions(+), 805 deletions(-) create mode 100644 deps/undici/src/docs/docs/best-practices/migrating-from-v7-to-v8.md delete mode 100644 deps/undici/src/lib/util/promise.js diff --git a/deps/undici/src/docs/docs/api/Client.md b/deps/undici/src/docs/docs/api/Client.md index d2268e3b39fe68..1eb6baab4825b3 100644 --- a/deps/undici/src/docs/docs/api/Client.md +++ b/deps/undici/src/docs/docs/api/Client.md @@ -24,6 +24,8 @@ Returns: `Client` * **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds. * **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB. * **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable. +* **webSocket** `WebSocketOptions` (optional) - WebSocket-specific configuration options. + * **maxPayloadSize** `number` (optional) - Default: `134217728` (128 MB) - Maximum allowed payload size in bytes for WebSocket messages. Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. Set to 0 to disable the limit. * **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections. * **connect** `ConnectOptions | Function | null` (optional) - Default: `null`. * **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. **Security Warning:** Disabling this option can expose your application to HTTP Request Smuggling attacks, where mismatched content-length headers cause servers and proxies to interpret request boundaries differently. This can lead to cache poisoning, credential hijacking, and bypassing security controls. Only disable this in controlled environments where you fully trust the request source. diff --git a/deps/undici/src/docs/docs/api/Dispatcher.md b/deps/undici/src/docs/docs/api/Dispatcher.md index c731690bcf4cb3..4ceba0122d53ee 100644 --- a/deps/undici/src/docs/docs/api/Dispatcher.md +++ b/deps/undici/src/docs/docs/api/Dispatcher.md @@ -533,7 +533,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`. `body` contains the following additional extensions: -- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144. +- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 131072. Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`. @@ -1031,7 +1031,7 @@ const client = new Client("http://service.example").compose( The `dump` interceptor enables you to dump the response body from a request upon a given limit. **Options** -- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the request's body exceeds this value then the connection will be closed. Default: `1048576`. +- `maxSize` - The maximum size (in bytes) of the response body to dump. If the size of the response's body exceeds this value then the connection will be closed. Default: `1048576`. > The `Dispatcher#options` also gets extended with the options `dumpMaxSize`, `abortOnDumped`, and `waitForTrailers` which can be used to configure the interceptor at a request-per-request basis. diff --git a/deps/undici/src/docs/docs/best-practices/migrating-from-v7-to-v8.md b/deps/undici/src/docs/docs/best-practices/migrating-from-v7-to-v8.md new file mode 100644 index 00000000000000..9db950570fff30 --- /dev/null +++ b/deps/undici/src/docs/docs/best-practices/migrating-from-v7-to-v8.md @@ -0,0 +1,231 @@ +# Migrating from Undici 7 to 8 + +This guide covers the changes you are most likely to hit when upgrading an +application or library from Undici v7 to v8. + +## Before you upgrade + +- Make sure your runtime is Node.js `>= 22.19.0`. +- If you have custom dispatchers, interceptors, or handlers, review the + handler API changes before updating. +- If you rely on HTTP/1.1-only behavior, plan to set `allowH2: false` + explicitly. + +## 1. Update your Node.js version + +Undici v8 requires Node.js `>= 22.19.0`. + +If you are still on Node.js 20 or an older Node.js 22 release, upgrade Node.js +first: + +```bash +node -v +``` + +If that command prints a version lower than `v22.19.0`, upgrade Node.js before +installing Undici v8. + +## 2. Migrate custom dispatcher handlers to the v2 API + +Undici v8 uses the newer dispatcher handler API consistently. + +If you implemented custom dispatchers, interceptors, or wrappers around +`dispatch()`, update legacy callbacks such as `onConnect`, `onHeaders`, and +`onComplete` to the newer callback names. + +### Old handler callbacks vs. v8 callbacks + +| Undici 7 style | Undici 8 style | +|---|---| +| `onConnect(abort, context)` | `onRequestStart(controller, context)` | +| `onHeaders(statusCode, rawHeaders, resume, statusText)` | `onResponseStart(controller, statusCode, headers, statusText)` | +| `onData(chunk)` | `onResponseData(controller, chunk)` | +| `onComplete(trailers)` | `onResponseEnd(controller, trailers)` | +| `onError(err)` | `onResponseError(controller, err)` | +| `onUpgrade(statusCode, rawHeaders, socket)` | `onRequestUpgrade(controller, statusCode, headers, socket)` | + +### Example + +Before: + +```js +client.dispatch(options, { + onConnect (abort) { + this.abort = abort + }, + onHeaders (statusCode, headers, resume) { + this.resume = resume + return true + }, + onData (chunk) { + chunks.push(chunk) + return true + }, + onComplete (trailers) { + console.log(trailers) + }, + onError (err) { + console.error(err) + } +}) +``` + +After: + +```js +client.dispatch(options, { + onRequestStart (controller) { + this.controller = controller + }, + onResponseStart (controller, statusCode, headers, statusText) { + console.log(statusCode, statusText, headers) + }, + onResponseData (controller, chunk) { + chunks.push(chunk) + }, + onResponseEnd (controller, trailers) { + console.log(trailers) + }, + onResponseError (controller, err) { + console.error(err) + } +}) +``` + +### Pause, resume, and abort now go through the controller + +In Undici v7, legacy handlers could return `false` or keep references to +`abort()` and `resume()` callbacks. In Undici v8, use the controller instead: + +```js +onRequestStart (controller) { + this.controller = controller +} + +onResponseData (controller, chunk) { + controller.pause() + setImmediate(() => controller.resume()) +} + +onResponseError (controller, err) { + controller.abort(err) +} +``` + +### Raw headers and trailers moved to the controller + +If you need the raw header arrays, read them from the controller: + +- `controller.rawHeaders` +- `controller.rawTrailers` + +## 3. Update `onBodySent()` handlers + +If you implemented `onBodySent()`, note that its signature changed. + +Before, handlers received counters: + +```js +onBodySent (chunkSize, totalBytesSent) {} +``` + +In Undici v8, handlers receive the actual chunk: + +```js +onBodySent (chunk) {} +``` + +If you need a notification that the whole body has been sent, use +`onRequestSent()`: + +```js +onRequestSent () { + console.log('request body fully sent') +} +``` + +## 4. If you need HTTP/1.1 only, disable HTTP/2 explicitly + +Undici v8 enables HTTP/2 by default when a TLS server negotiates it via ALPN. + +If your application depends on HTTP/1.1-specific behavior, set `allowH2: false` +explicitly. + +Before: + +```js +const client = new Client('https://example.com') +``` + +After, to keep HTTP/1.1 only: + +```js +const client = new Client('https://example.com', { + allowH2: false +}) +``` + +The same applies when you configure an `Agent`: + +```js +const agent = new Agent({ + allowH2: false +}) +``` + +## 5. Use real `Blob` and `File` instances + +Undici v8 no longer accepts fake Blob-like values that only imitate `Blob` or +`File` via properties such as `Symbol.toStringTag`. + +If you were passing custom objects that looked like `Blob`s, replace them with +actual `Blob` or `File` instances: + +```js +const body = new Blob(['hello']) +``` + +## 6. Avoid depending on the internal global dispatcher symbol + +`setGlobalDispatcher()` and `getGlobalDispatcher()` remain the public APIs and +should continue to be used. + +Internally, Undici v8 stores its dispatcher under +`Symbol.for('undici.globalDispatcher.2')` and mirrors a v1-compatible wrapper +for legacy consumers such as Node.js built-in `fetch`. + +If your code was reading or writing `Symbol.for('undici.globalDispatcher.1')` +directly, migrate to the public APIs instead: + +```js +import { setGlobalDispatcher, getGlobalDispatcher, Agent } from 'undici' + +setGlobalDispatcher(new Agent()) +const dispatcher = getGlobalDispatcher() +``` + +If you must expose a dispatcher to legacy v1 handler consumers, wrap it with +`Dispatcher1Wrapper`: + +```js +import { Agent, Dispatcher1Wrapper } from 'undici' + +const legacyCompatibleDispatcher = new Dispatcher1Wrapper(new Agent()) +``` + +## 7. Verify the upgrade + +After moving to Undici v8, it is worth checking these paths in your test suite: + +- requests that use a custom `dispatcher` +- `setGlobalDispatcher()` behavior +- any custom interceptor or retry handler +- uploads that use `Blob`, `File`, or `FormData` +- integrations that depend on HTTP/1.1-only behavior + +## Related documentation + +- [Dispatcher](/docs/api/Dispatcher.md) +- [Client](/docs/api/Client.md) +- [Global Installation](/docs/api/GlobalInstallation.md) +- [Undici Module vs. Node.js Built-in Fetch](/docs/best-practices/undici-vs-builtin-fetch.md) diff --git a/deps/undici/src/index.js b/deps/undici/src/index.js index 04e7338921136b..7ece983f1dbe70 100644 --- a/deps/undici/src/index.js +++ b/deps/undici/src/index.js @@ -105,14 +105,14 @@ function makeDispatcher (fn) { url = util.parseURL(url) } - const { agent, dispatcher = getGlobalDispatcher() } = opts + const { agent, dispatcher = getGlobalDispatcher(), ...restOpts } = opts if (agent) { throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?') } return fn.call(dispatcher, { - ...opts, + ...restOpts, origin: url.origin, path: url.search ? `${url.pathname}${url.search}` : url.pathname, method: opts.method || (opts.body ? 'PUT' : 'GET') diff --git a/deps/undici/src/lib/core/util.js b/deps/undici/src/lib/core/util.js index 1090b78969d261..0c72e5d598a353 100644 --- a/deps/undici/src/lib/core/util.js +++ b/deps/undici/src/lib/core/util.js @@ -12,8 +12,6 @@ const { InvalidArgumentError, ConnectTimeoutError } = require('./errors') const { headerNameLowerCasedRecord } = require('./constants') const { tree } = require('./tree') -const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(v => Number(v)) - class BodyAsyncIterable { constructor (body) { this[kBody] = body @@ -323,7 +321,7 @@ function isIterable (obj) { */ function hasSafeIterator (obj) { const prototype = Object.getPrototypeOf(obj) - const ownIterator = Object.prototype.hasOwnProperty.call(obj, Symbol.iterator) + const ownIterator = Object.hasOwn(obj, Symbol.iterator) return ownIterator || (prototype != null && prototype !== Object.prototype && typeof obj[Symbol.iterator] === 'function') } @@ -989,8 +987,6 @@ module.exports = { normalizedMethodRecords, isValidPort, isHttpOrHttpsPrefixed, - nodeMajor, - nodeMinor, safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']), wrapRequestBody, setupConnectTimeout, diff --git a/deps/undici/src/lib/dispatcher/agent.js b/deps/undici/src/lib/dispatcher/agent.js index da4496e352bdf4..a1cc7fd6817c59 100644 --- a/deps/undici/src/lib/dispatcher/agent.js +++ b/deps/undici/src/lib/dispatcher/agent.js @@ -35,7 +35,7 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('maxOrigins must be a number greater than 0') } - super() + super(options) if (connect && typeof connect !== 'function') { connect = { ...connect } diff --git a/deps/undici/src/lib/dispatcher/balanced-pool.js b/deps/undici/src/lib/dispatcher/balanced-pool.js index fa0fa97288ea3d..8bad487b3f2367 100644 --- a/deps/undici/src/lib/dispatcher/balanced-pool.js +++ b/deps/undici/src/lib/dispatcher/balanced-pool.js @@ -57,9 +57,6 @@ class BalancedPool extends PoolBase { super() this[kOptions] = { ...util.deepClone(opts) } - this[kOptions].interceptors = opts.interceptors - ? { ...opts.interceptors } - : undefined this[kIndex] = -1 this[kCurrentWeight] = 0 diff --git a/deps/undici/src/lib/dispatcher/client.js b/deps/undici/src/lib/dispatcher/client.js index f40fbb2c75bf1d..6ef97ba0a369a9 100644 --- a/deps/undici/src/lib/dispatcher/client.js +++ b/deps/undici/src/lib/dispatcher/client.js @@ -114,7 +114,8 @@ class Client extends DispatcherBase { useH2c, initialWindowSize, connectionWindowSize, - pingInterval + pingInterval, + webSocket } = {}) { if (keepAlive !== undefined) { throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead') @@ -222,7 +223,7 @@ class Client extends DispatcherBase { throw new InvalidArgumentError('pingInterval must be a positive integer, greater or equal to 0') } - super() + super({ webSocket }) if (typeof connect !== 'function') { connect = buildConnector({ diff --git a/deps/undici/src/lib/dispatcher/dispatcher-base.js b/deps/undici/src/lib/dispatcher/dispatcher-base.js index 59ad3abd046d66..aa5928b7c36247 100644 --- a/deps/undici/src/lib/dispatcher/dispatcher-base.js +++ b/deps/undici/src/lib/dispatcher/dispatcher-base.js @@ -10,6 +10,7 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require('../core/sy const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') +const kWebSocketOptions = Symbol('webSocketOptions') class DispatcherBase extends Dispatcher { /** @type {boolean} */ @@ -24,6 +25,23 @@ class DispatcherBase extends Dispatcher { /** @type {Array|null} */ [kOnClosed] = null + /** + * @param {import('../../types/dispatcher').DispatcherOptions} [opts] + */ + constructor (opts) { + super() + this[kWebSocketOptions] = opts?.webSocket ?? {} + } + + /** + * @returns {import('../../types/dispatcher').WebSocketOptions} + */ + get webSocketOptions () { + return { + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 // 128 MB default + } + } + /** @returns {boolean} */ get destroyed () { return this[kDestroyed] @@ -138,6 +156,10 @@ class DispatcherBase extends Dispatcher { throw new InvalidArgumentError('opts must be an object.') } + if (opts.dispatcher) { + throw new InvalidArgumentError('opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly.') + } + if (this[kDestroyed] || this[kOnDestroyed]) { throw new ClientDestroyedError() } diff --git a/deps/undici/src/lib/dispatcher/dispatcher1-wrapper.js b/deps/undici/src/lib/dispatcher/dispatcher1-wrapper.js index b5b69219dd4487..f5813288cb33c2 100644 --- a/deps/undici/src/lib/dispatcher/dispatcher1-wrapper.js +++ b/deps/undici/src/lib/dispatcher/dispatcher1-wrapper.js @@ -86,6 +86,12 @@ class Dispatcher1Wrapper extends Dispatcher { } dispatch (opts, handler) { + // Legacy (v1) consumers do not support HTTP/2, so force HTTP/1.1. + // See https://github.com/nodejs/undici/issues/4989 + if (opts.allowH2 !== false) { + opts = { ...opts, allowH2: false } + } + return this.#dispatcher.dispatch(opts, Dispatcher1Wrapper.wrapHandler(handler)) } diff --git a/deps/undici/src/lib/dispatcher/h2c-client.js b/deps/undici/src/lib/dispatcher/h2c-client.js index bd385225ea17e1..5fe7e778ba40e5 100644 --- a/deps/undici/src/lib/dispatcher/h2c-client.js +++ b/deps/undici/src/lib/dispatcher/h2c-client.js @@ -15,7 +15,7 @@ class H2CClient extends Client { ) } - const { connect, maxConcurrentStreams, pipelining, ...opts } = + const { maxConcurrentStreams, pipelining, ...opts } = clientOpts ?? {} let defaultMaxConcurrentStreams = 100 let defaultPipelining = 100 diff --git a/deps/undici/src/lib/dispatcher/pool.js b/deps/undici/src/lib/dispatcher/pool.js index 8419ac611e7907..24a2c41c2f396c 100644 --- a/deps/undici/src/lib/dispatcher/pool.js +++ b/deps/undici/src/lib/dispatcher/pool.js @@ -63,14 +63,11 @@ class Pool extends PoolBase { }) } - super() + super(options) this[kConnections] = connections || null this[kUrl] = util.parseOrigin(origin) this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath } - this[kOptions].interceptors = options.interceptors - ? { ...options.interceptors } - : undefined this[kFactory] = factory this.on('connect', (origin, targets) => { diff --git a/deps/undici/src/lib/dispatcher/proxy-agent.js b/deps/undici/src/lib/dispatcher/proxy-agent.js index 259f390937534b..f7bb461e5d49a9 100644 --- a/deps/undici/src/lib/dispatcher/proxy-agent.js +++ b/deps/undici/src/lib/dispatcher/proxy-agent.js @@ -104,7 +104,7 @@ class ProxyAgent extends DispatcherBase { throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.') } - const { proxyTunnel = true } = opts + const { proxyTunnel = true, connectTimeout } = opts super() @@ -128,9 +128,9 @@ class ProxyAgent extends DispatcherBase { this[kProxyHeaders]['proxy-authorization'] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString('base64')}` } - const connect = buildConnector({ ...opts.proxyTls }) - this[kConnectEndpoint] = buildConnector({ ...opts.requestTls }) - this[kConnectEndpointHTTP1] = buildConnector({ ...opts.requestTls, allowH2: false }) + const connect = buildConnector({ timeout: connectTimeout, ...opts.proxyTls }) + this[kConnectEndpoint] = buildConnector({ timeout: connectTimeout, ...opts.requestTls }) + this[kConnectEndpointHTTP1] = buildConnector({ timeout: connectTimeout, ...opts.requestTls, allowH2: false }) const agentFactory = opts.factory || defaultAgentFactory const factory = (origin, options) => { diff --git a/deps/undici/src/lib/dispatcher/round-robin-pool.js b/deps/undici/src/lib/dispatcher/round-robin-pool.js index dd065351050204..ac08f4609231c1 100644 --- a/deps/undici/src/lib/dispatcher/round-robin-pool.js +++ b/deps/undici/src/lib/dispatcher/round-robin-pool.js @@ -69,9 +69,6 @@ class RoundRobinPool extends PoolBase { this[kConnections] = connections || null this[kUrl] = util.parseOrigin(origin) this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath } - this[kOptions].interceptors = options.interceptors - ? { ...options.interceptors } - : undefined this[kFactory] = factory this[kIndex] = -1 diff --git a/deps/undici/src/lib/dispatcher/socks5-proxy-agent.js b/deps/undici/src/lib/dispatcher/socks5-proxy-agent.js index a2dd2f428c7117..4a2f65e6eed5e0 100644 --- a/deps/undici/src/lib/dispatcher/socks5-proxy-agent.js +++ b/deps/undici/src/lib/dispatcher/socks5-proxy-agent.js @@ -79,26 +79,28 @@ class Socks5ProxyAgent extends DispatcherBase { debug('creating SOCKS5 connection to', proxyHost, proxyPort) // Connect to the SOCKS5 proxy - const socket = await new Promise((resolve, reject) => { - const onConnect = () => { - socket.removeListener('error', onError) - resolve(socket) - } + const socketReady = Promise.withResolvers() - const onError = (err) => { - socket.removeListener('connect', onConnect) - reject(err) - } + const onSocketConnect = () => { + socket.removeListener('error', onSocketError) + socketReady.resolve(socket) + } - const socket = net.connect({ - host: proxyHost, - port: proxyPort - }) + const onSocketError = (err) => { + socket.removeListener('connect', onSocketConnect) + socketReady.reject(err) + } - socket.once('connect', onConnect) - socket.once('error', onError) + const socket = net.connect({ + host: proxyHost, + port: proxyPort }) + socket.once('connect', onSocketConnect) + socket.once('error', onSocketError) + + await socketReady.promise + // Create SOCKS5 client const socks5Client = new Socks5Client(socket, this[kProxyAuth]) @@ -112,58 +114,62 @@ class Socks5ProxyAgent extends DispatcherBase { await socks5Client.handshake() // Wait for authentication (if required) - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('SOCKS5 authentication timeout')) - }, 5000) - - const onAuthenticated = () => { - clearTimeout(timeout) - socks5Client.removeListener('error', onError) - resolve() - } + const authenticationReady = Promise.withResolvers() - const onError = (err) => { - clearTimeout(timeout) - socks5Client.removeListener('authenticated', onAuthenticated) - reject(err) - } + const authenticationTimeout = setTimeout(() => { + authenticationReady.reject(new Error('SOCKS5 authentication timeout')) + }, 5000) - // Check if already authenticated (for NO_AUTH method) - if (socks5Client.state === 'authenticated') { - clearTimeout(timeout) - resolve() - } else { - socks5Client.once('authenticated', onAuthenticated) - socks5Client.once('error', onError) - } - }) + const onAuthenticated = () => { + clearTimeout(authenticationTimeout) + socks5Client.removeListener('error', onAuthenticationError) + authenticationReady.resolve() + } + + const onAuthenticationError = (err) => { + clearTimeout(authenticationTimeout) + socks5Client.removeListener('authenticated', onAuthenticated) + authenticationReady.reject(err) + } + + // Check if already authenticated (for NO_AUTH method) + if (socks5Client.state === 'authenticated') { + clearTimeout(authenticationTimeout) + authenticationReady.resolve() + } else { + socks5Client.once('authenticated', onAuthenticated) + socks5Client.once('error', onAuthenticationError) + } + + await authenticationReady.promise // Send CONNECT command await socks5Client.connect(targetHost, targetPort) // Wait for connection - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('SOCKS5 connection timeout')) - }, 5000) - - const onConnected = (info) => { - debug('SOCKS5 tunnel established to', targetHost, targetPort, 'via', info) - clearTimeout(timeout) - socks5Client.removeListener('error', onError) - resolve() - } + const connectionReady = Promise.withResolvers() - const onError = (err) => { - clearTimeout(timeout) - socks5Client.removeListener('connected', onConnected) - reject(err) - } + const connectionTimeout = setTimeout(() => { + connectionReady.reject(new Error('SOCKS5 connection timeout')) + }, 5000) - socks5Client.once('connected', onConnected) - socks5Client.once('error', onError) - }) + const onConnected = (info) => { + debug('SOCKS5 tunnel established to', targetHost, targetPort, 'via', info) + clearTimeout(connectionTimeout) + socks5Client.removeListener('error', onConnectionError) + connectionReady.resolve() + } + + const onConnectionError = (err) => { + clearTimeout(connectionTimeout) + socks5Client.removeListener('connected', onConnected) + connectionReady.reject(err) + } + + socks5Client.once('connected', onConnected) + socks5Client.once('error', onConnectionError) + + await connectionReady.promise return socket } @@ -206,10 +212,10 @@ class Socks5ProxyAgent extends DispatcherBase { ...connectOpts.tls || {} }) - await new Promise((resolve, reject) => { - finalSocket.once('secureConnect', resolve) - finalSocket.once('error', reject) - }) + const tlsReady = Promise.withResolvers() + finalSocket.once('secureConnect', tlsReady.resolve) + finalSocket.once('error', tlsReady.reject) + await tlsReady.promise } callback(null, finalSocket) diff --git a/deps/undici/src/lib/handler/redirect-handler.js b/deps/undici/src/lib/handler/redirect-handler.js index d088d5488af9e9..bdc8cdfd004d97 100644 --- a/deps/undici/src/lib/handler/redirect-handler.js +++ b/deps/undici/src/lib/handler/redirect-handler.js @@ -1,30 +1,13 @@ 'use strict' const util = require('../core/util') -const { kBodyUsed } = require('../core/symbols') const assert = require('node:assert') const { InvalidArgumentError } = require('../core/errors') -const EE = require('node:events') const redirectableStatusCodes = [300, 301, 302, 303, 307, 308] -const kBody = Symbol('body') - const noop = () => {} -class BodyAsyncIterable { - constructor (body) { - this[kBody] = body - this[kBodyUsed] = false - } - - async * [Symbol.asyncIterator] () { - assert(!this[kBodyUsed], 'disturbed') - this[kBodyUsed] = true - yield * this[kBody] - } -} - class RedirectHandler { static buildDispatch (dispatcher, maxRedirections) { if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) { @@ -44,43 +27,10 @@ class RedirectHandler { this.location = null const { maxRedirections: _, ...cleanOpts } = opts this.opts = cleanOpts // opts must be a copy, exclude maxRedirections + this.opts.body = util.wrapRequestBody(this.opts.body) this.maxRedirections = maxRedirections this.handler = handler this.history = [] - - if (util.isStream(this.opts.body)) { - // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp - // so that it can be dispatched again? - // TODO (fix): Do we need 100-expect support to provide a way to do this properly? - if (util.bodyLength(this.opts.body) === 0) { - this.opts.body - .on('data', function () { - assert(false) - }) - } - - if (typeof this.opts.body.readableDidRead !== 'boolean') { - this.opts.body[kBodyUsed] = false - EE.prototype.on.call(this.opts.body, 'data', function () { - this[kBodyUsed] = true - }) - } - } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') { - // TODO (fix): We can't access ReadableStream internal state - // to determine whether or not it has been disturbed. This is just - // a workaround. - this.opts.body = new BodyAsyncIterable(this.opts.body) - } else if ( - this.opts.body && - typeof this.opts.body !== 'string' && - !ArrayBuffer.isView(this.opts.body) && - util.isIterable(this.opts.body) && - !util.isFormDataLike(this.opts.body) - ) { - // TODO: Should we allow re-using iterable if !this.opts.idempotent - // or through some other flag? - this.opts.body = new BodyAsyncIterable(this.opts.body) - } } onRequestStart (controller, context) { diff --git a/deps/undici/src/lib/interceptor/decompress.js b/deps/undici/src/lib/interceptor/decompress.js index c43565a0535dd7..295390ad9ceb21 100644 --- a/deps/undici/src/lib/interceptor/decompress.js +++ b/deps/undici/src/lib/interceptor/decompress.js @@ -3,7 +3,6 @@ const { createInflate, createGunzip, createBrotliDecompress, createZstdDecompress } = require('node:zlib') const { pipeline } = require('node:stream') const DecoratorHandler = require('../handler/decorator-handler') -const { runtimeFeatures } = require('../util/runtime-features') /** @typedef {import('node:stream').Transform} Transform */ /** @typedef {import('node:stream').Transform} Controller */ @@ -17,7 +16,7 @@ const supportedEncodings = { deflate: createInflate, compress: createInflate, 'x-compress': createInflate, - ...(runtimeFeatures.has('zstd') ? { zstd: createZstdDecompress } : {}) + zstd: createZstdDecompress } const defaultSkipStatusCodes = /** @type {const} */ ([204, 304]) diff --git a/deps/undici/src/lib/interceptor/dns.js b/deps/undici/src/lib/interceptor/dns.js index 38cb2fcbef33ab..6347d1ffefc3a7 100644 --- a/deps/undici/src/lib/interceptor/dns.js +++ b/deps/undici/src/lib/interceptor/dns.js @@ -7,7 +7,7 @@ const maxInt = Math.pow(2, 31) - 1 function hasSafeIterator (headers) { const prototype = Object.getPrototypeOf(headers) - const ownIterator = Object.prototype.hasOwnProperty.call(headers, Symbol.iterator) + const ownIterator = Object.hasOwn(headers, Symbol.iterator) return ownIterator || (prototype != null && prototype !== Object.prototype && typeof headers[Symbol.iterator] === 'function') } diff --git a/deps/undici/src/lib/llhttp/wasm_build_env.txt b/deps/undici/src/lib/llhttp/wasm_build_env.txt index c9b4eba395222b..dabace4f7e36cb 100644 --- a/deps/undici/src/lib/llhttp/wasm_build_env.txt +++ b/deps/undici/src/lib/llhttp/wasm_build_env.txt @@ -1,5 +1,5 @@ -> undici@8.0.2 build:wasm +> undici@8.1.0 build:wasm > node build/wasm.js --docker > docker run --rm --platform=linux/x86_64 --user 1001:1001 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/build/lib/llhttp --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/build,target=/home/node/build/build --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/deps,target=/home/node/build/deps -t ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970 node build/wasm.js diff --git a/deps/undici/src/lib/util/cache.js b/deps/undici/src/lib/util/cache.js index 5968f4efe5f415..2da2c044dbdd6d 100644 --- a/deps/undici/src/lib/util/cache.js +++ b/deps/undici/src/lib/util/cache.js @@ -374,9 +374,11 @@ function assertCacheMethods (methods, name = 'CacheMethods') { * @returns {string} */ function makeDeduplicationKey (cacheKey, excludeHeaders) { - // Create a deterministic string key from the cache key - // Include origin, method, path, and sorted headers - let key = `${cacheKey.origin}:${cacheKey.method}:${cacheKey.path}` + // Use JSON.stringify to produce a collision-resistant key. + // Previous format used `:` and `=` delimiters without escaping, which + // allowed different header sets to produce identical keys (e.g. + // {a:"x:b=y"} vs {a:"x", b:"y"}). See: https://github.com/nodejs/undici/issues/5012 + const headers = {} if (cacheKey.headers) { const sortedHeaders = Object.keys(cacheKey.headers).sort() @@ -385,12 +387,11 @@ function makeDeduplicationKey (cacheKey, excludeHeaders) { if (excludeHeaders?.has(header.toLowerCase())) { continue } - const value = cacheKey.headers[header] - key += `:${header}=${Array.isArray(value) ? value.join(',') : value}` + headers[header] = cacheKey.headers[header] } } - return key + return JSON.stringify([cacheKey.origin, cacheKey.method, cacheKey.path, headers]) } module.exports = { diff --git a/deps/undici/src/lib/util/promise.js b/deps/undici/src/lib/util/promise.js deleted file mode 100644 index 048f86e34ef9dc..00000000000000 --- a/deps/undici/src/lib/util/promise.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict' - -/** - * @template {*} T - * @typedef {Object} DeferredPromise - * @property {Promise} promise - * @property {(value?: T) => void} resolve - * @property {(reason?: any) => void} reject - */ - -/** - * @template {*} T - * @returns {DeferredPromise} An object containing a promise and its resolve/reject methods. - */ -function createDeferredPromise () { - let res - let rej - const promise = new Promise((resolve, reject) => { - res = resolve - rej = reject - }) - - return { promise, resolve: res, reject: rej } -} - -module.exports = { - createDeferredPromise -} diff --git a/deps/undici/src/lib/util/runtime-features.js b/deps/undici/src/lib/util/runtime-features.js index 3e62dc004d3352..f6fe3198e1da95 100644 --- a/deps/undici/src/lib/util/runtime-features.js +++ b/deps/undici/src/lib/util/runtime-features.js @@ -6,9 +6,7 @@ const lazyLoaders = { __proto__: null, 'node:crypto': () => require('node:crypto'), - 'node:sqlite': () => require('node:sqlite'), - 'node:worker_threads': () => require('node:worker_threads'), - 'node:zlib': () => require('node:zlib') + 'node:sqlite': () => require('node:sqlite') } /** @@ -27,35 +25,9 @@ function detectRuntimeFeatureByNodeModule (moduleName) { } } -/** - * @param {NodeModuleName} moduleName - * @param {string} property - * @returns {boolean} - */ -function detectRuntimeFeatureByExportedProperty (moduleName, property) { - const module = lazyLoaders[moduleName]() - return typeof module[property] !== 'undefined' -} - -const runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd']) - -/** @type {Record} */ -const exportedPropertyLookup = { - markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'], - zstd: ['node:zlib', 'createZstdDecompress'] -} - -/** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */ - const runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite']) /** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */ - -const features = /** @type {const} */ ([ - ...runtimeFeaturesAsNodeModule, - ...runtimeFeaturesByExportedProperty -]) - -/** @typedef {typeof features[number]} Feature */ +/** @typedef {RuntimeFeatureByNodeModule} Feature */ /** * @param {Feature} feature @@ -64,9 +36,6 @@ const features = /** @type {const} */ ([ function detectRuntimeFeature (feature) { if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) { return detectRuntimeFeatureByNodeModule(`node:${feature}`) - } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) { - const [moduleName, property] = exportedPropertyLookup[feature] - return detectRuntimeFeatureByExportedProperty(moduleName, property) } throw new TypeError(`unknown feature: ${feature}`) } @@ -101,7 +70,7 @@ class RuntimeFeatures { * @param {boolean} value */ set (feature, value) { - if (features.includes(feature) === false) { + if (runtimeFeaturesAsNodeModule.includes(feature) === false) { throw new TypeError(`unknown feature: ${feature}`) } this.#map.set(feature, value) diff --git a/deps/undici/src/lib/web/cache/cache.js b/deps/undici/src/lib/web/cache/cache.js index 10decbede6ad17..1f41a66a01bb29 100644 --- a/deps/undici/src/lib/web/cache/cache.js +++ b/deps/undici/src/lib/web/cache/cache.js @@ -10,8 +10,6 @@ const { cloneResponse, fromInnerResponse, getResponseState } = require('../fetch const { Request, fromInnerRequest, getRequestState } = require('../fetch/request') const { fetching } = require('../fetch/index') const { urlIsHttpHttpsScheme, readAllBytes } = require('../fetch/util') -const { createDeferredPromise } = require('../../util/promise') - /** * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation * @typedef {Object} CacheBatchOperation @@ -153,7 +151,7 @@ class Cache { requestList.push(r) // 5.6 - const responsePromise = createDeferredPromise() + const responsePromise = Promise.withResolvers() // 5.7 fetchControllers.push(fetching({ @@ -231,7 +229,7 @@ class Cache { } // 7.5 - const cacheJobPromise = createDeferredPromise() + const cacheJobPromise = Promise.withResolvers() // 7.6.1 let errorData = null @@ -325,7 +323,7 @@ class Cache { const clonedResponse = cloneResponse(innerResponse) // 10. - const bodyReadPromise = createDeferredPromise() + const bodyReadPromise = Promise.withResolvers() // 11. if (innerResponse.body != null) { @@ -364,7 +362,7 @@ class Cache { } // 19.1 - const cacheJobPromise = createDeferredPromise() + const cacheJobPromise = Promise.withResolvers() // 19.2.1 let errorData = null @@ -427,7 +425,7 @@ class Cache { operations.push(operation) - const cacheJobPromise = createDeferredPromise() + const cacheJobPromise = Promise.withResolvers() let errorData = null let requestResponses @@ -483,7 +481,7 @@ class Cache { } // 4. - const promise = createDeferredPromise() + const promise = Promise.withResolvers() // 5. // 5.1 diff --git a/deps/undici/src/lib/web/fetch/body.js b/deps/undici/src/lib/web/fetch/body.js index 5d1724965272a7..7e08b29fd6c9e9 100644 --- a/deps/undici/src/lib/web/fetch/body.js +++ b/deps/undici/src/lib/web/fetch/body.js @@ -14,7 +14,6 @@ const { isErrored, isDisturbed } = require('node:stream') const { isUint8Array } = require('node:util/types') const { serializeAMimeType } = require('./data-url') const { multipartFormDataParser } = require('./formdata-parser') -const { createDeferredPromise } = require('../../util/promise') const { parseJSONFromBytes } = require('../infra') const { utf8DecodeBytes } = require('../../encoding') const { runtimeFeatures } = require('../../util/runtime-features.js') @@ -431,7 +430,7 @@ function consumeBody (object, convertBytesToJSValue, instance, getInternalState) } // 2. Let promise be a new promise. - const promise = createDeferredPromise() + const promise = Promise.withResolvers() // 3. Let errorSteps given error be to reject promise with error. const errorSteps = promise.reject diff --git a/deps/undici/src/lib/web/fetch/index.js b/deps/undici/src/lib/web/fetch/index.js index 2f19d0cb1545cb..85d4c2e9feda0c 100644 --- a/deps/undici/src/lib/web/fetch/index.js +++ b/deps/undici/src/lib/web/fetch/index.js @@ -64,12 +64,7 @@ const { getGlobalDispatcher } = require('../../global') const { webidl } = require('../webidl') const { STATUS_CODES } = require('node:http') const { bytesMatch } = require('../subresource-integrity/subresource-integrity') -const { createDeferredPromise } = require('../../util/promise') const { isomorphicEncode } = require('../infra') -const { runtimeFeatures } = require('../../util/runtime-features') - -// Node.js v23.8.0+ and v22.15.0+ supports Zstandard -const hasZstd = runtimeFeatures.has('zstd') const GET_OR_HEAD = ['GET', 'HEAD'] @@ -136,7 +131,7 @@ function fetch (input, init = undefined) { webidl.argumentLengthCheck(arguments, 1, 'globalThis.fetch') // 1. Let p be a new promise. - let p = createDeferredPromise() + let p = Promise.withResolvers() // 2. Let requestObject be the result of invoking the initial value of // Request as constructor with input and init as arguments. If this throws @@ -1644,12 +1639,25 @@ async function httpNetworkOrCacheFetch ( // 14. If response’s status is 401, httpRequest’s response tainting is not "cors", // includeCredentials is true, and request’s traversable for user prompts is // a traversable navigable: - if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) { + // + // In Node.js there is no traversable navigable to prompt the user, but we + // still need to handle URL-embedded credentials so authentication retries + // for WebSocket handshakes continue to work. + if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && ( + request.useURLCredentials !== undefined || + isTraversableNavigable(request.traversableForUserPrompts) + )) { // 2. If request’s body is non-null, then: if (request.body != null) { // 1. If request’s body’s source is null, then return a network error. if (request.body.source == null) { - return makeNetworkError('expected non-null body source') + // Note: In Node.js, this code path should not be reached because + // isTraversableNavigable() returns false for non-navigable contexts. + // However, we handle it gracefully by returning the response instead of + // a network error, as we won't actually retry the request. + // This aligns with the Fetch spec discussion in whatwg/fetch#1132, + // which allows implementations flexibility when credentials can't be obtained. + return response } // 2. Set request’s body to the body of the result of safely extracting @@ -2251,7 +2259,7 @@ async function httpNetworkFetch ( flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH })) - } else if (coding === 'zstd' && hasZstd) { + } else if (coding === 'zstd') { decoders.push(zlib.createZstdDecompress({ flush: zlib.constants.ZSTD_e_continue, finishFlush: zlib.constants.ZSTD_e_end diff --git a/deps/undici/src/lib/web/fetch/util.js b/deps/undici/src/lib/web/fetch/util.js index fe63cb3a9b07e5..b77be74f5d40d1 100644 --- a/deps/undici/src/lib/web/fetch/util.js +++ b/deps/undici/src/lib/web/fetch/util.js @@ -1447,8 +1447,10 @@ function includesCredentials (url) { * @param {object|string} navigable */ function isTraversableNavigable (navigable) { - // TODO - return true + // Returns true only if we have an actual traversable navigable object + // that can prompt the user for credentials. In Node.js, this will always + // be false since there's no Window object or navigable. + return navigable != null && navigable !== 'client' && navigable !== 'no-traversable' } class EnvironmentSettingsObjectBase { diff --git a/deps/undici/src/lib/web/webidl/index.js b/deps/undici/src/lib/web/webidl/index.js index 5518b4959062c3..e5ee5cbb6e726b 100644 --- a/deps/undici/src/lib/web/webidl/index.js +++ b/deps/undici/src/lib/web/webidl/index.js @@ -2,7 +2,7 @@ const assert = require('node:assert') const { types, inspect } = require('node:util') -const { runtimeFeatures } = require('../../util/runtime-features') +const { markAsUncloneable } = require('node:worker_threads') const UNDEFINED = 1 const BOOLEAN = 2 @@ -158,9 +158,7 @@ webidl.util.TypeValueToString = function (o) { } } -webidl.util.markAsUncloneable = runtimeFeatures.has('markAsUncloneable') - ? require('node:worker_threads').markAsUncloneable - : () => {} +webidl.util.markAsUncloneable = markAsUncloneable // https://webidl.spec.whatwg.org/#abstract-opdef-converttoint webidl.util.ConvertToInt = function (V, bitLength, signedness, flags) { diff --git a/deps/undici/src/lib/web/websocket/permessage-deflate.js b/deps/undici/src/lib/web/websocket/permessage-deflate.js index 1f1a13038afb5f..6a6e43899c5a95 100644 --- a/deps/undici/src/lib/web/websocket/permessage-deflate.js +++ b/deps/undici/src/lib/web/websocket/permessage-deflate.js @@ -8,40 +8,35 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') const kLength = Symbol('kLength') -// Default maximum decompressed message size: 4 MB -const kDefaultMaxDecompressedSize = 4 * 1024 * 1024 - class PerMessageDeflate { /** @type {import('node:zlib').InflateRaw} */ #inflate #options = {} - /** @type {boolean} */ - #aborted = false - - /** @type {Function|null} */ - #currentCallback = null + #maxPayloadSize = 0 /** * @param {Map} extensions */ - constructor (extensions) { + constructor (extensions, options) { this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover') this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits') + + this.#maxPayloadSize = options.maxPayloadSize } + /** + * Decompress a compressed payload. + * @param {Buffer} chunk Compressed data + * @param {boolean} fin Final fragment flag + * @param {Function} callback Callback function + */ decompress (chunk, fin, callback) { // An endpoint uses the following algorithm to decompress a message. // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the // payload of the message. // 2. Decompress the resulting data using DEFLATE. - - if (this.#aborted) { - callback(new MessageSizeExceededError()) - return - } - if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS @@ -64,23 +59,12 @@ class PerMessageDeflate { this.#inflate[kLength] = 0 this.#inflate.on('data', (data) => { - if (this.#aborted) { - return - } - this.#inflate[kLength] += data.length - if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { - this.#aborted = true + if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) { + callback(new MessageSizeExceededError()) this.#inflate.removeAllListeners() - this.#inflate.destroy() this.#inflate = null - - if (this.#currentCallback) { - const cb = this.#currentCallback - this.#currentCallback = null - cb(new MessageSizeExceededError()) - } return } @@ -93,14 +77,13 @@ class PerMessageDeflate { }) } - this.#currentCallback = callback this.#inflate.write(chunk) if (fin) { this.#inflate.write(tail) } this.#inflate.flush(() => { - if (this.#aborted || !this.#inflate) { + if (!this.#inflate) { return } @@ -108,7 +91,6 @@ class PerMessageDeflate { this.#inflate[kBuffer].length = 0 this.#inflate[kLength] = 0 - this.#currentCallback = null callback(null, full) }) diff --git a/deps/undici/src/lib/web/websocket/receiver.js b/deps/undici/src/lib/web/websocket/receiver.js index 384808d1b7e8c3..9221e5cc54fccf 100644 --- a/deps/undici/src/lib/web/websocket/receiver.js +++ b/deps/undici/src/lib/web/websocket/receiver.js @@ -39,18 +39,23 @@ class ByteParser extends Writable { /** @type {import('./websocket').Handler} */ #handler + /** @type {number} */ + #maxPayloadSize + /** * @param {import('./websocket').Handler} handler * @param {Map|null} extensions + * @param {{ maxPayloadSize?: number }} [options] */ - constructor (handler, extensions) { + constructor (handler, extensions, options = {}) { super() this.#handler = handler this.#extensions = extensions == null ? new Map() : extensions + this.#maxPayloadSize = options.maxPayloadSize ?? 0 if (this.#extensions.has('permessage-deflate')) { - this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions)) + this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options)) } } @@ -66,6 +71,19 @@ class ByteParser extends Writable { this.run(callback) } + #validatePayloadLength () { + if ( + this.#maxPayloadSize > 0 && + !isControlFrame(this.#info.opcode) && + this.#info.payloadLength > this.#maxPayloadSize + ) { + failWebsocketConnection(this.#handler, 1009, 'Payload size exceeds maximum allowed size') + return false + } + + return true + } + /** * Runs whenever a new chunk is received. * Callback is called whenever there are no more chunks buffering, @@ -154,6 +172,10 @@ class ByteParser extends Writable { if (payloadLength <= 125) { this.#info.payloadLength = payloadLength this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (payloadLength === 126) { this.#state = parserStates.PAYLOADLENGTH_16 } else if (payloadLength === 127) { @@ -178,6 +200,10 @@ class ByteParser extends Writable { this.#info.payloadLength = buffer.readUInt16BE(0) this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.PAYLOADLENGTH_64) { if (this.#byteOffset < 8) { return callback() @@ -200,6 +226,10 @@ class ByteParser extends Writable { this.#info.payloadLength = lower this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { return callback() @@ -224,29 +254,39 @@ class ByteParser extends Writable { this.#state = parserStates.INFO } else { - this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => { - if (error) { - // Use 1009 (Message Too Big) for decompression size limit errors - const code = error instanceof MessageSizeExceededError ? 1009 : 1007 - failWebsocketConnection(this.#handler, code, error.message) - return - } - - this.writeFragments(data) + this.#extensions.get('permessage-deflate').decompress( + body, + this.#info.fin, + (error, data) => { + if (error) { + const code = error instanceof MessageSizeExceededError ? 1009 : 1007 + failWebsocketConnection(this.#handler, code, error.message) + return + } + + this.writeFragments(data) + + // Check cumulative fragment size + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnection(this.#handler, 1009, new MessageSizeExceededError().message) + return + } + + if (!this.#info.fin) { + this.#state = parserStates.INFO + this.#loop = true + this.run(callback) + return + } + + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()) - if (!this.#info.fin) { - this.#state = parserStates.INFO this.#loop = true + this.#state = parserStates.INFO this.run(callback) - return - } - - websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()) - - this.#loop = true - this.#state = parserStates.INFO - this.run(callback) - }) + }, + this.#fragmentsBytes + ) this.#loop = false break diff --git a/deps/undici/src/lib/web/websocket/stream/websocketstream.js b/deps/undici/src/lib/web/websocket/stream/websocketstream.js index ca40ad08dae576..d8061658fd968e 100644 --- a/deps/undici/src/lib/web/websocket/stream/websocketstream.js +++ b/deps/undici/src/lib/web/websocket/stream/websocketstream.js @@ -1,6 +1,5 @@ 'use strict' -const { createDeferredPromise } = require('../../../util/promise') const { environmentSettingsObject } = require('../../fetch/util') const { states, opcodes, sentCloseFrameState } = require('../constants') const { webidl } = require('../../webidl') @@ -21,11 +20,11 @@ class WebSocketStream { #url // Each WebSocketStream object has an associated opened promise , which is a promise. - /** @type {import('../../../util/promise').DeferredPromise} */ + /** @type {ReturnType} */ #openedPromise // Each WebSocketStream object has an associated closed promise , which is a promise. - /** @type {import('../../../util/promise').DeferredPromise} */ + /** @type {ReturnType} */ #closedPromise // Each WebSocketStream object has an associated readable stream , which is a ReadableStream . @@ -113,8 +112,8 @@ class WebSocketStream { this.#url = urlRecord.toString() // 6. Set this 's opened promise and closed promise to new promises. - this.#openedPromise = createDeferredPromise() - this.#closedPromise = createDeferredPromise() + this.#openedPromise = Promise.withResolvers() + this.#closedPromise = Promise.withResolvers() // 7. Apply backpressure to the WebSocket. // TODO @@ -202,7 +201,7 @@ class WebSocketStream { chunk = webidl.converters.WebSocketStreamWrite(chunk) // 1. Let promise be a new promise created in stream ’s relevant realm . - const promise = createDeferredPromise() + const promise = Promise.withResolvers() // 2. Let data be null. let data = null diff --git a/deps/undici/src/lib/web/websocket/websocket.js b/deps/undici/src/lib/web/websocket/websocket.js index da94ab5b352926..a2abd9c9ab60d9 100644 --- a/deps/undici/src/lib/web/websocket/websocket.js +++ b/deps/undici/src/lib/web/websocket/websocket.js @@ -468,7 +468,12 @@ class WebSocket extends EventTarget { // once this happens, the connection is open this.#handler.socket = response.socket - const parser = new ByteParser(this.#handler, parsedExtensions) + // Get maxPayloadSize from dispatcher options + const maxPayloadSize = this.#handler.controller.dispatcher?.webSocketOptions?.maxPayloadSize + + const parser = new ByteParser(this.#handler, parsedExtensions, { + maxPayloadSize + }) parser.on('drain', () => this.#handler.onParserDrain()) parser.on('error', (err) => this.#handler.onParserError(err)) diff --git a/deps/undici/src/package-lock.json b/deps/undici/src/package-lock.json index 79a2d4f36adf13..66122753babfaa 100644 --- a/deps/undici/src/package-lock.json +++ b/deps/undici/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "undici", - "version": "8.0.2", + "version": "8.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "undici", - "version": "8.0.2", + "version": "8.1.0", "license": "MIT", "devDependencies": { "@fastify/busboy": "3.2.0", @@ -19,7 +19,7 @@ "c8": "^10.0.0", "cross-env": "^10.0.0", "dns-packet": "^5.4.0", - "esbuild": "^0.27.3", + "esbuild": "^0.28.0", "eslint": "^9.9.0", "fast-check": "^4.1.1", "husky": "^9.0.7", @@ -27,7 +27,7 @@ "jsondiffpatch": "^0.7.3", "neostandard": "^0.12.0", "node-forge": "^1.3.1", - "proxy": "^2.1.1", + "proxy": "^4.0.0", "tsd": "^0.33.0", "typescript": "^6.0.2", "ws": "^8.11.0" @@ -623,9 +623,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], @@ -640,9 +640,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], @@ -657,9 +657,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], @@ -674,9 +674,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], @@ -691,9 +691,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], @@ -708,9 +708,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], @@ -725,9 +725,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], @@ -742,9 +742,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], @@ -759,9 +759,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], @@ -776,9 +776,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], @@ -793,9 +793,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], @@ -810,9 +810,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], @@ -827,9 +827,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], @@ -844,9 +844,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], @@ -861,9 +861,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], @@ -878,9 +878,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], @@ -895,9 +895,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], @@ -912,9 +912,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ "arm64" ], @@ -929,9 +929,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], @@ -946,9 +946,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "cpu": [ "arm64" ], @@ -963,9 +963,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], @@ -980,9 +980,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], @@ -997,9 +997,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ "x64" ], @@ -1014,9 +1014,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "cpu": [ "arm64" ], @@ -1031,9 +1031,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ "ia32" ], @@ -1048,9 +1048,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "cpu": [ "x64" ], @@ -1414,9 +1414,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", "engines": { @@ -1560,9 +1560,9 @@ } }, "node_modules/@jest/fake-timers/node_modules/@sinonjs/fake-timers": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.0.tgz", - "integrity": "sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==", + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", + "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2195,17 +2195,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", - "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/type-utils": "8.58.0", - "@typescript-eslint/utils": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2218,7 +2218,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.0", + "@typescript-eslint/parser": "^8.58.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -2234,16 +2234,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", - "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3" }, "engines": { @@ -2259,14 +2259,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.0", - "@typescript-eslint/types": "^8.58.0", + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", "debug": "^4.4.3" }, "engines": { @@ -2281,14 +2281,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0" + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2299,9 +2299,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", "dev": true, "license": "MIT", "engines": { @@ -2316,15 +2316,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", - "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2341,9 +2341,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", "dev": true, "license": "MIT", "engines": { @@ -2355,16 +2355,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.0", - "@typescript-eslint/tsconfig-utils": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2435,16 +2435,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0" + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2459,13 +2459,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/types": "8.58.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3363,9 +3363,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.14", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.14.tgz", - "integrity": "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==", + "version": "2.10.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", + "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3400,9 +3400,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -3568,15 +3568,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -3656,9 +3656,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001785", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001785.tgz", - "integrity": "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ==", + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", "dev": true, "funding": [ { @@ -4158,9 +4158,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.331", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", - "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "version": "1.5.336", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.336.tgz", + "integrity": "sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ==", "dev": true, "license": "ISC" }, @@ -4209,9 +4209,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { @@ -4298,16 +4298,16 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", - "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", @@ -4319,8 +4319,7 @@ "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0", - "safe-array-concat": "^1.1.3" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4387,9 +4386,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4400,32 +4399,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/escalade": { @@ -5576,9 +5575,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", "dependencies": { @@ -8589,9 +8588,9 @@ "license": "MIT" }, "node_modules/proxy": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.2.0.tgz", - "integrity": "sha512-nYclNIWj9UpXbVJ3W5EXIYiGR88AKZoGt90kyh3zoOBY5QW+7bbtPvMFgKGD4VJmpS3UXQXtlGXSg3lRNLOFLg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-4.0.0.tgz", + "integrity": "sha512-qYi7N3kisRTQW0Y13JL0upO8e9mVLpu4yuxrXvWzCsaJVA9yqVF+iQkC/rCdnx2lGPQhC+qjefWSY7M6SHclhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8603,7 +8602,7 @@ "proxy": "dist/bin/proxy.js" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/punycode": { @@ -8802,12 +8801,13 @@ } }, "node_modules/read-pkg/node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -9183,14 +9183,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -9792,14 +9792,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -10117,16 +10117,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", - "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.0", - "@typescript-eslint/parser": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0", - "@typescript-eslint/utils": "8.58.0" + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10160,9 +10160,9 @@ } }, "node_modules/undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", + "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", "dev": true, "license": "MIT", "engines": { diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json index 0304d1d51cc95c..22a284be304367 100644 --- a/deps/undici/src/package.json +++ b/deps/undici/src/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "8.0.2", + "version": "8.1.0", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { @@ -119,7 +119,7 @@ "c8": "^10.0.0", "cross-env": "^10.0.0", "dns-packet": "^5.4.0", - "esbuild": "^0.27.3", + "esbuild": "^0.28.0", "eslint": "^9.9.0", "fast-check": "^4.1.1", "husky": "^9.0.7", @@ -127,7 +127,7 @@ "jsondiffpatch": "^0.7.3", "neostandard": "^0.12.0", "node-forge": "^1.3.1", - "proxy": "^2.1.1", + "proxy": "^4.0.0", "tsd": "^0.33.0", "typescript": "^6.0.2", "ws": "^8.11.0" diff --git a/deps/undici/src/types/agent.d.ts b/deps/undici/src/types/agent.d.ts index b3b376d20c9a79..3d41105ef3e90d 100644 --- a/deps/undici/src/types/agent.d.ts +++ b/deps/undici/src/types/agent.d.ts @@ -22,8 +22,6 @@ declare namespace Agent { export interface Options extends Pool.Options { /** Default: `(origin, opts) => new Pool(origin, opts)`. */ factory?(origin: string | URL, opts: Object): Dispatcher; - - interceptors?: { Agent?: readonly Dispatcher.DispatchInterceptor[] } & Pool.Options['interceptors'] maxOrigins?: number } diff --git a/deps/undici/src/types/client.d.ts b/deps/undici/src/types/client.d.ts index a6e20221f68c40..13b7bf5899454d 100644 --- a/deps/undici/src/types/client.d.ts +++ b/deps/undici/src/types/client.d.ts @@ -30,12 +30,7 @@ export class Client extends Dispatcher { } export declare namespace Client { - export interface OptionsInterceptors { - Client: readonly Dispatcher.DispatchInterceptor[]; - } export interface Options { - /** TODO */ - interceptors?: OptionsInterceptors; /** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */ maxHeaderSize?: number; /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */ @@ -44,7 +39,7 @@ export declare namespace Client { socketTimeout?: never; /** @deprecated unsupported requestTimeout, use headersTimeout & bodyTimeout instead */ requestTimeout?: never; - /** TODO */ + /** The timeout for establishing a socket connection, in milliseconds. Use `0` to disable it entirely. Default: `10e3` milliseconds (10s). */ connectTimeout?: number; /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */ bodyTimeout?: number; @@ -60,7 +55,7 @@ export declare namespace Client { keepAliveMaxTimeout?: number; /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */ keepAliveTimeoutThreshold?: number; - /** TODO */ + /** An IPC endpoint, either a Unix domain socket or Windows named pipe. Default: `null`. */ socketPath?: string; /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */ pipelining?: number; @@ -68,23 +63,25 @@ export declare namespace Client { tls?: never; /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */ strictContentLength?: boolean; - /** TODO */ + /** Maximum number of TLS cached sessions used by the built-in connector. Use `0` to disable TLS session caching. Default: `100`. */ maxCachedSessions?: number; - /** TODO */ + /** Connector options passed to `buildConnector`, or a custom connector function. Default: `null`. */ connect?: Partial | buildConnector.connector; - /** TODO */ + /** The maximum number of requests to send over a single connection before it is reset. Use `0` to disable this limit. Default: `null`. */ maxRequestsPerClient?: number; - /** TODO */ + /** Local IP address the socket should connect from. */ localAddress?: string; /** Max response body size in bytes, -1 is disabled */ maxResponseSize?: number; + /** WebSocket-specific options */ + webSocket?: Client.WebSocketOptions; /** Enables a family autodetection algorithm that loosely implements section 5 of RFC 8305. */ autoSelectFamily?: boolean; /** The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. */ autoSelectFamilyAttemptTimeout?: number; /** * @description Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation. - * @default false + * @default true */ allowH2?: boolean; /** @@ -118,6 +115,15 @@ export declare namespace Client { bytesWritten?: number bytesRead?: number } + export interface WebSocketOptions { + /** + * Maximum allowed payload size in bytes for WebSocket messages. + * Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. + * Set to 0 to disable the limit. + * @default 134217728 (128 MB) + */ + maxPayloadSize?: number; + } } export default Client diff --git a/deps/undici/src/types/dispatcher.d.ts b/deps/undici/src/types/dispatcher.d.ts index 893caa2eeef651..de2545bc84dcf4 100644 --- a/deps/undici/src/types/dispatcher.d.ts +++ b/deps/undici/src/types/dispatcher.d.ts @@ -8,8 +8,6 @@ import { FormData } from './formdata' import Errors from './errors' import { Autocomplete } from './utility' -type AbortSignal = unknown - export default Dispatcher export type UndiciHeaders = Record | IncomingHttpHeaders | string[] | Iterable<[string, string | string[] | undefined]> | null diff --git a/deps/undici/src/types/h2c-client.d.ts b/deps/undici/src/types/h2c-client.d.ts index 7b9744970ad277..b921a347112687 100644 --- a/deps/undici/src/types/h2c-client.d.ts +++ b/deps/undici/src/types/h2c-client.d.ts @@ -32,7 +32,7 @@ export declare namespace H2CClient { maxHeaderSize?: number; /** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */ headersTimeout?: number; - /** TODO */ + /** The timeout for establishing a socket connection, in milliseconds. Use `0` to disable it entirely. Default: `10e3` milliseconds (10s). */ connectTimeout?: number; /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */ bodyTimeout?: number; @@ -42,19 +42,19 @@ export declare namespace H2CClient { keepAliveMaxTimeout?: number; /** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */ keepAliveTimeoutThreshold?: number; - /** TODO */ + /** An IPC endpoint, either a Unix domain socket or Windows named pipe. Default: `null`. */ socketPath?: string; /** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */ pipelining?: number; /** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */ strictContentLength?: boolean; - /** TODO */ + /** Maximum number of TLS cached sessions used by the built-in connector. Use `0` to disable TLS session caching. Default: `100`. */ maxCachedSessions?: number; - /** TODO */ + /** Connector options passed to `buildConnector`, or a custom connector function. Default: `null`. */ connect?: Omit, 'allowH2'> | buildConnector.connector; - /** TODO */ + /** The maximum number of requests to send over a single connection before it is reset. Use `0` to disable this limit. Default: `null`. */ maxRequestsPerClient?: number; - /** TODO */ + /** Local IP address the socket should connect from. */ localAddress?: string; /** Max response body size in bytes, -1 is disabled */ maxResponseSize?: number; diff --git a/deps/undici/src/types/pool.d.ts b/deps/undici/src/types/pool.d.ts index 120bb8b25ef970..d1e1bcdd6fe394 100644 --- a/deps/undici/src/types/pool.d.ts +++ b/deps/undici/src/types/pool.d.ts @@ -35,7 +35,5 @@ declare namespace Pool { connections?: number | null; /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */ clientTtl?: number | null; - - interceptors?: { Pool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors'] } } diff --git a/deps/undici/src/types/round-robin-pool.d.ts b/deps/undici/src/types/round-robin-pool.d.ts index 05ce2107aef673..5129d863375480 100644 --- a/deps/undici/src/types/round-robin-pool.d.ts +++ b/deps/undici/src/types/round-robin-pool.d.ts @@ -35,7 +35,5 @@ declare namespace RoundRobinPool { connections?: number | null; /** The amount of time before a client is removed from the pool and closed. `null` if no time limit. Default `null` */ clientTtl?: number | null; - - interceptors?: { RoundRobinPool?: readonly Dispatcher.DispatchInterceptor[] } & Client.Options['interceptors'] } } diff --git a/deps/undici/src/types/webidl.d.ts b/deps/undici/src/types/webidl.d.ts index f95d9b567378c1..b1873b9e52da23 100644 --- a/deps/undici/src/types/webidl.d.ts +++ b/deps/undici/src/types/webidl.d.ts @@ -87,7 +87,6 @@ interface WebidlUtil { /** * Mark a value as uncloneable for Node.js. - * This is only effective in some newer Node.js versions. */ markAsUncloneable (V: any): void diff --git a/deps/undici/undici.js b/deps/undici/undici.js index 22ec3444e6ec30..6be4f5b522b775 100644 --- a/deps/undici/undici.js +++ b/deps/undici/undici.js @@ -625,6 +625,7 @@ var require_dispatcher_base = __commonJS({ var { kDestroy, kClose, kClosed, kDestroyed, kDispatch } = require_symbols(); var kOnDestroyed = /* @__PURE__ */ Symbol("onDestroyed"); var kOnClosed = /* @__PURE__ */ Symbol("onClosed"); + var kWebSocketOptions = /* @__PURE__ */ Symbol("webSocketOptions"); var DispatcherBase = class extends Dispatcher2 { static { __name(this, "DispatcherBase"); @@ -637,6 +638,22 @@ var require_dispatcher_base = __commonJS({ [kClosed] = false; /** @type {Array|null} */ [kOnClosed] = null; + /** + * @param {import('../../types/dispatcher').DispatcherOptions} [opts] + */ + constructor(opts) { + super(); + this[kWebSocketOptions] = opts?.webSocket ?? {}; + } + /** + * @returns {import('../../types/dispatcher').WebSocketOptions} + */ + get webSocketOptions() { + return { + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 + // 128 MB default + }; + } /** @returns {boolean} */ get destroyed() { return this[kDestroyed]; @@ -727,6 +744,9 @@ var require_dispatcher_base = __commonJS({ if (!opts || typeof opts !== "object") { throw new InvalidArgumentError("opts must be an object."); } + if (opts.dispatcher) { + throw new InvalidArgumentError("opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly."); + } if (this[kDestroyed] || this[kOnDestroyed]) { throw new ClientDestroyedError(); } @@ -1569,7 +1589,6 @@ var require_util = __commonJS({ var { InvalidArgumentError, ConnectTimeoutError } = require_errors(); var { headerNameLowerCasedRecord } = require_constants(); var { tree } = require_tree(); - var [nodeMajor, nodeMinor] = process.versions.node.split(".", 2).map((v) => Number(v)); var BodyAsyncIterable = class { static { __name(this, "BodyAsyncIterable"); @@ -1736,7 +1755,7 @@ var require_util = __commonJS({ __name(isIterable, "isIterable"); function hasSafeIterator(obj) { const prototype = Object.getPrototypeOf(obj); - const ownIterator = Object.prototype.hasOwnProperty.call(obj, Symbol.iterator); + const ownIterator = Object.hasOwn(obj, Symbol.iterator); return ownIterator || prototype != null && prototype !== Object.prototype && typeof obj[Symbol.iterator] === "function"; } __name(hasSafeIterator, "hasSafeIterator"); @@ -2415,8 +2434,6 @@ var require_util = __commonJS({ normalizedMethodRecords, isValidPort, isHttpOrHttpsPrefixed, - nodeMajor, - nodeMinor, safeHTTPMethods: Object.freeze(["GET", "HEAD", "OPTIONS", "TRACE"]), wrapRequestBody, setupConnectTimeout, @@ -4558,121 +4575,13 @@ var require_data_url = __commonJS({ } }); -// lib/util/runtime-features.js -var require_runtime_features = __commonJS({ - "lib/util/runtime-features.js"(exports2, module2) { - "use strict"; - var lazyLoaders = { - __proto__: null, - "node:crypto": /* @__PURE__ */ __name(() => require("node:crypto"), "node:crypto"), - "node:sqlite": /* @__PURE__ */ __name(() => require("node:sqlite"), "node:sqlite"), - "node:worker_threads": /* @__PURE__ */ __name(() => require("node:worker_threads"), "node:worker_threads"), - "node:zlib": /* @__PURE__ */ __name(() => require("node:zlib"), "node:zlib") - }; - function detectRuntimeFeatureByNodeModule(moduleName) { - try { - lazyLoaders[moduleName](); - return true; - } catch (err) { - if (err.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && err.code !== "ERR_NO_CRYPTO") { - throw err; - } - return false; - } - } - __name(detectRuntimeFeatureByNodeModule, "detectRuntimeFeatureByNodeModule"); - function detectRuntimeFeatureByExportedProperty(moduleName, property) { - const module3 = lazyLoaders[moduleName](); - return typeof module3[property] !== "undefined"; - } - __name(detectRuntimeFeatureByExportedProperty, "detectRuntimeFeatureByExportedProperty"); - var runtimeFeaturesByExportedProperty = ( - /** @type {const} */ - ["markAsUncloneable", "zstd"] - ); - var exportedPropertyLookup = { - markAsUncloneable: ["node:worker_threads", "markAsUncloneable"], - zstd: ["node:zlib", "createZstdDecompress"] - }; - var runtimeFeaturesAsNodeModule = ( - /** @type {const} */ - ["crypto", "sqlite"] - ); - var features = ( - /** @type {const} */ - [ - ...runtimeFeaturesAsNodeModule, - ...runtimeFeaturesByExportedProperty - ] - ); - function detectRuntimeFeature(feature) { - if (runtimeFeaturesAsNodeModule.includes( - /** @type {RuntimeFeatureByNodeModule} */ - feature - )) { - return detectRuntimeFeatureByNodeModule(`node:${feature}`); - } else if (runtimeFeaturesByExportedProperty.includes( - /** @type {RuntimeFeatureByExportedProperty} */ - feature - )) { - const [moduleName, property] = exportedPropertyLookup[feature]; - return detectRuntimeFeatureByExportedProperty(moduleName, property); - } - throw new TypeError(`unknown feature: ${feature}`); - } - __name(detectRuntimeFeature, "detectRuntimeFeature"); - var RuntimeFeatures = class { - static { - __name(this, "RuntimeFeatures"); - } - /** @type {Map} */ - #map = /* @__PURE__ */ new Map(); - /** - * Clears all cached feature detections. - */ - clear() { - this.#map.clear(); - } - /** - * @param {Feature} feature - * @returns {boolean} - */ - has(feature) { - return this.#map.get(feature) ?? this.#detectRuntimeFeature(feature); - } - /** - * @param {Feature} feature - * @param {boolean} value - */ - set(feature, value) { - if (features.includes(feature) === false) { - throw new TypeError(`unknown feature: ${feature}`); - } - this.#map.set(feature, value); - } - /** - * @param {Feature} feature - * @returns {boolean} - */ - #detectRuntimeFeature(feature) { - const result = detectRuntimeFeature(feature); - this.#map.set(feature, result); - return result; - } - }; - var instance = new RuntimeFeatures(); - module2.exports.runtimeFeatures = instance; - module2.exports.default = instance; - } -}); - // lib/web/webidl/index.js var require_webidl = __commonJS({ "lib/web/webidl/index.js"(exports2, module2) { "use strict"; var assert = require("node:assert"); var { types, inspect } = require("node:util"); - var { runtimeFeatures } = require_runtime_features(); + var { markAsUncloneable } = require("node:worker_threads"); var UNDEFINED = 1; var BOOLEAN = 2; var STRING = 3; @@ -4792,8 +4701,7 @@ var require_webidl = __commonJS({ return "Object"; } }; - webidl.util.markAsUncloneable = runtimeFeatures.has("markAsUncloneable") ? require("node:worker_threads").markAsUncloneable : () => { - }; + webidl.util.markAsUncloneable = markAsUncloneable; webidl.util.ConvertToInt = function(V, bitLength, signedness, flags) { let upperBound; let lowerBound; @@ -6022,7 +5930,7 @@ var require_util2 = __commonJS({ } __name(includesCredentials, "includesCredentials"); function isTraversableNavigable(navigable) { - return true; + return navigable != null && navigable !== "client" && navigable !== "no-traversable"; } __name(isTraversableNavigable, "isTraversableNavigable"); var EnvironmentSettingsObjectBase = class { @@ -6598,23 +6506,83 @@ var require_formdata_parser = __commonJS({ } }); -// lib/util/promise.js -var require_promise = __commonJS({ - "lib/util/promise.js"(exports2, module2) { +// lib/util/runtime-features.js +var require_runtime_features = __commonJS({ + "lib/util/runtime-features.js"(exports2, module2) { "use strict"; - function createDeferredPromise() { - let res; - let rej; - const promise = new Promise((resolve, reject) => { - res = resolve; - rej = reject; - }); - return { promise, resolve: res, reject: rej }; + var lazyLoaders = { + __proto__: null, + "node:crypto": /* @__PURE__ */ __name(() => require("node:crypto"), "node:crypto"), + "node:sqlite": /* @__PURE__ */ __name(() => require("node:sqlite"), "node:sqlite") + }; + function detectRuntimeFeatureByNodeModule(moduleName) { + try { + lazyLoaders[moduleName](); + return true; + } catch (err) { + if (err.code !== "ERR_UNKNOWN_BUILTIN_MODULE" && err.code !== "ERR_NO_CRYPTO") { + throw err; + } + return false; + } } - __name(createDeferredPromise, "createDeferredPromise"); - module2.exports = { - createDeferredPromise + __name(detectRuntimeFeatureByNodeModule, "detectRuntimeFeatureByNodeModule"); + var runtimeFeaturesAsNodeModule = ( + /** @type {const} */ + ["crypto", "sqlite"] + ); + function detectRuntimeFeature(feature) { + if (runtimeFeaturesAsNodeModule.includes( + /** @type {RuntimeFeatureByNodeModule} */ + feature + )) { + return detectRuntimeFeatureByNodeModule(`node:${feature}`); + } + throw new TypeError(`unknown feature: ${feature}`); + } + __name(detectRuntimeFeature, "detectRuntimeFeature"); + var RuntimeFeatures = class { + static { + __name(this, "RuntimeFeatures"); + } + /** @type {Map} */ + #map = /* @__PURE__ */ new Map(); + /** + * Clears all cached feature detections. + */ + clear() { + this.#map.clear(); + } + /** + * @param {Feature} feature + * @returns {boolean} + */ + has(feature) { + return this.#map.get(feature) ?? this.#detectRuntimeFeature(feature); + } + /** + * @param {Feature} feature + * @param {boolean} value + */ + set(feature, value) { + if (runtimeFeaturesAsNodeModule.includes(feature) === false) { + throw new TypeError(`unknown feature: ${feature}`); + } + this.#map.set(feature, value); + } + /** + * @param {Feature} feature + * @returns {boolean} + */ + #detectRuntimeFeature(feature) { + const result = detectRuntimeFeature(feature); + this.#map.set(feature, result); + return result; + } }; + var instance = new RuntimeFeatures(); + module2.exports.runtimeFeatures = instance; + module2.exports.default = instance; } }); @@ -6636,7 +6604,6 @@ var require_body = __commonJS({ var { isUint8Array } = require("node:util/types"); var { serializeAMimeType } = require_data_url(); var { multipartFormDataParser } = require_formdata_parser(); - var { createDeferredPromise } = require_promise(); var { parseJSONFromBytes } = require_infra(); var { utf8DecodeBytes } = require_encoding(); var { runtimeFeatures } = require_runtime_features(); @@ -6868,7 +6835,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r if (bodyUnusable(object)) { return Promise.reject(new TypeError("Body is unusable: Body has already been read")); } - const promise = createDeferredPromise(); + const promise = Promise.withResolvers(); const errorSteps = promise.reject; const successSteps = /* @__PURE__ */ __name((data) => { try { @@ -8961,7 +8928,8 @@ var require_client = __commonJS({ useH2c, initialWindowSize, connectionWindowSize, - pingInterval + pingInterval, + webSocket } = {}) { if (keepAlive !== void 0) { throw new InvalidArgumentError("unsupported keepAlive, use pipelining=0 instead"); @@ -9039,7 +9007,7 @@ var require_client = __commonJS({ if (pingInterval != null && (typeof pingInterval !== "number" || !Number.isInteger(pingInterval) || pingInterval < 0)) { throw new InvalidArgumentError("pingInterval must be a positive integer, greater or equal to 0"); } - super(); + super({ webSocket }); if (typeof connect2 !== "function") { connect2 = buildConnector({ ...tls, @@ -9449,11 +9417,10 @@ var require_pool = __commonJS({ ...connect }); } - super(); + super(options); this[kConnections] = connections || null; this[kUrl] = util.parseOrigin(origin); this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl, socketPath }; - this[kOptions].interceptors = options.interceptors ? { ...options.interceptors } : void 0; this[kFactory] = factory; this.on("connect", (origin2, targets) => { if (clientTtl != null && clientTtl > 0) { @@ -9526,7 +9493,7 @@ var require_agent = __commonJS({ if (typeof maxOrigins !== "number" || Number.isNaN(maxOrigins) || maxOrigins <= 0) { throw new InvalidArgumentError("maxOrigins must be a number greater than 0"); } - super(); + super(options); if (connect && typeof connect !== "function") { connect = { ...connect }; } @@ -9714,6 +9681,9 @@ var require_dispatcher1_wrapper = __commonJS({ return new LegacyHandlerWrapper(handler); } dispatch(opts, handler) { + if (opts.allowH2 !== false) { + opts = { ...opts, allowH2: false }; + } return this.#dispatcher.dispatch(opts, _Dispatcher1Wrapper.wrapHandler(handler)); } close(...args) { @@ -10325,69 +10295,69 @@ var require_socks5_proxy_agent = __commonJS({ const proxyHost = this[kProxyUrl].hostname; const proxyPort = parseInt(this[kProxyUrl].port) || 1080; debug("creating SOCKS5 connection to", proxyHost, proxyPort); - const socket = await new Promise((resolve, reject) => { - const onConnect = /* @__PURE__ */ __name(() => { - socket2.removeListener("error", onError); - resolve(socket2); - }, "onConnect"); - const onError = /* @__PURE__ */ __name((err) => { - socket2.removeListener("connect", onConnect); - reject(err); - }, "onError"); - const socket2 = net.connect({ - host: proxyHost, - port: proxyPort - }); - socket2.once("connect", onConnect); - socket2.once("error", onError); + const socketReady = Promise.withResolvers(); + const onSocketConnect = /* @__PURE__ */ __name(() => { + socket.removeListener("error", onSocketError); + socketReady.resolve(socket); + }, "onSocketConnect"); + const onSocketError = /* @__PURE__ */ __name((err) => { + socket.removeListener("connect", onSocketConnect); + socketReady.reject(err); + }, "onSocketError"); + const socket = net.connect({ + host: proxyHost, + port: proxyPort }); + socket.once("connect", onSocketConnect); + socket.once("error", onSocketError); + await socketReady.promise; const socks5Client = new Socks5Client(socket, this[kProxyAuth]); socks5Client.on("error", (err) => { debug("SOCKS5 error:", err); socket.destroy(); }); await socks5Client.handshake(); - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error("SOCKS5 authentication timeout")); - }, 5e3); - const onAuthenticated = /* @__PURE__ */ __name(() => { - clearTimeout(timeout); - socks5Client.removeListener("error", onError); - resolve(); - }, "onAuthenticated"); - const onError = /* @__PURE__ */ __name((err) => { - clearTimeout(timeout); - socks5Client.removeListener("authenticated", onAuthenticated); - reject(err); - }, "onError"); - if (socks5Client.state === "authenticated") { - clearTimeout(timeout); - resolve(); - } else { - socks5Client.once("authenticated", onAuthenticated); - socks5Client.once("error", onError); - } - }); + const authenticationReady = Promise.withResolvers(); + const authenticationTimeout = setTimeout(() => { + authenticationReady.reject(new Error("SOCKS5 authentication timeout")); + }, 5e3); + const onAuthenticated = /* @__PURE__ */ __name(() => { + clearTimeout(authenticationTimeout); + socks5Client.removeListener("error", onAuthenticationError); + authenticationReady.resolve(); + }, "onAuthenticated"); + const onAuthenticationError = /* @__PURE__ */ __name((err) => { + clearTimeout(authenticationTimeout); + socks5Client.removeListener("authenticated", onAuthenticated); + authenticationReady.reject(err); + }, "onAuthenticationError"); + if (socks5Client.state === "authenticated") { + clearTimeout(authenticationTimeout); + authenticationReady.resolve(); + } else { + socks5Client.once("authenticated", onAuthenticated); + socks5Client.once("error", onAuthenticationError); + } + await authenticationReady.promise; await socks5Client.connect(targetHost, targetPort); - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error("SOCKS5 connection timeout")); - }, 5e3); - const onConnected = /* @__PURE__ */ __name((info) => { - debug("SOCKS5 tunnel established to", targetHost, targetPort, "via", info); - clearTimeout(timeout); - socks5Client.removeListener("error", onError); - resolve(); - }, "onConnected"); - const onError = /* @__PURE__ */ __name((err) => { - clearTimeout(timeout); - socks5Client.removeListener("connected", onConnected); - reject(err); - }, "onError"); - socks5Client.once("connected", onConnected); - socks5Client.once("error", onError); - }); + const connectionReady = Promise.withResolvers(); + const connectionTimeout = setTimeout(() => { + connectionReady.reject(new Error("SOCKS5 connection timeout")); + }, 5e3); + const onConnected = /* @__PURE__ */ __name((info) => { + debug("SOCKS5 tunnel established to", targetHost, targetPort, "via", info); + clearTimeout(connectionTimeout); + socks5Client.removeListener("error", onConnectionError); + connectionReady.resolve(); + }, "onConnected"); + const onConnectionError = /* @__PURE__ */ __name((err) => { + clearTimeout(connectionTimeout); + socks5Client.removeListener("connected", onConnected); + connectionReady.reject(err); + }, "onConnectionError"); + socks5Client.once("connected", onConnected); + socks5Client.once("error", onConnectionError); + await connectionReady.promise; return socket; } /** @@ -10419,10 +10389,10 @@ var require_socks5_proxy_agent = __commonJS({ servername: targetHost, ...connectOpts.tls || {} }); - await new Promise((resolve, reject) => { - finalSocket.once("secureConnect", resolve); - finalSocket.once("error", reject); - }); + const tlsReady = Promise.withResolvers(); + finalSocket.once("secureConnect", tlsReady.resolve); + finalSocket.once("error", tlsReady.reject); + await tlsReady.promise; } callback(null, finalSocket); } catch (err) { @@ -10555,7 +10525,7 @@ var require_proxy_agent = __commonJS({ if (typeof clientFactory !== "function") { throw new InvalidArgumentError("Proxy opts.clientFactory must be a function."); } - const { proxyTunnel = true } = opts; + const { proxyTunnel = true, connectTimeout } = opts; super(); const url = this.#getUrl(opts); const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url; @@ -10573,9 +10543,9 @@ var require_proxy_agent = __commonJS({ } else if (username && password) { this[kProxyHeaders]["proxy-authorization"] = `Basic ${Buffer.from(`${decodeURIComponent(username)}:${decodeURIComponent(password)}`).toString("base64")}`; } - const connect = buildConnector({ ...opts.proxyTls }); - this[kConnectEndpoint] = buildConnector({ ...opts.requestTls }); - this[kConnectEndpointHTTP1] = buildConnector({ ...opts.requestTls, allowH2: false }); + const connect = buildConnector({ timeout: connectTimeout, ...opts.proxyTls }); + this[kConnectEndpoint] = buildConnector({ timeout: connectTimeout, ...opts.requestTls }); + this[kConnectEndpointHTTP1] = buildConnector({ timeout: connectTimeout, ...opts.requestTls, allowH2: false }); const agentFactory = opts.factory || defaultAgentFactory; const factory = /* @__PURE__ */ __name((origin2, options) => { const { protocol: protocol2 } = new URL(origin2); @@ -12732,10 +12702,7 @@ var require_fetch = __commonJS({ var { webidl } = require_webidl(); var { STATUS_CODES } = require("node:http"); var { bytesMatch } = require_subresource_integrity(); - var { createDeferredPromise } = require_promise(); var { isomorphicEncode } = require_infra(); - var { runtimeFeatures } = require_runtime_features(); - var hasZstd = runtimeFeatures.has("zstd"); var GET_OR_HEAD = ["GET", "HEAD"]; var defaultUserAgent = typeof __UNDICI_IS_NODE__ !== "undefined" || true ? "node" : "undici"; var resolveObjectURL; @@ -12778,7 +12745,7 @@ var require_fetch = __commonJS({ __name(handleFetchDone, "handleFetchDone"); function fetch2(input, init = void 0) { webidl.argumentLengthCheck(arguments, 1, "globalThis.fetch"); - let p = createDeferredPromise(); + let p = Promise.withResolvers(); let requestObject; try { requestObject = new Request(input, init); @@ -13426,10 +13393,10 @@ var require_fetch = __commonJS({ response.rangeRequested = true; } response.requestIncludesCredentials = includeCredentials; - if (response.status === 401 && httpRequest.responseTainting !== "cors" && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) { + if (response.status === 401 && httpRequest.responseTainting !== "cors" && includeCredentials && (request.useURLCredentials !== void 0 || isTraversableNavigable(request.traversableForUserPrompts))) { if (request.body != null) { if (request.body.source == null) { - return makeNetworkError("expected non-null body source"); + return response; } request.body = safelyExtractBody(request.body.source)[0]; } @@ -13721,7 +13688,7 @@ var require_fetch = __commonJS({ flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH })); - } else if (coding === "zstd" && hasZstd) { + } else if (coding === "zstd") { decoders.push(zlib.createZstdDecompress({ flush: zlib.constants.ZSTD_e_continue, finishFlush: zlib.constants.ZSTD_e_end @@ -14620,7 +14587,6 @@ var require_permessage_deflate = __commonJS({ var tail = Buffer.from([0, 0, 255, 255]); var kBuffer = /* @__PURE__ */ Symbol("kBuffer"); var kLength = /* @__PURE__ */ Symbol("kLength"); - var kDefaultMaxDecompressedSize = 4 * 1024 * 1024; var PerMessageDeflate = class { static { __name(this, "PerMessageDeflate"); @@ -14628,22 +14594,22 @@ var require_permessage_deflate = __commonJS({ /** @type {import('node:zlib').InflateRaw} */ #inflate; #options = {}; - /** @type {boolean} */ - #aborted = false; - /** @type {Function|null} */ - #currentCallback = null; + #maxPayloadSize = 0; /** * @param {Map} extensions */ - constructor(extensions) { + constructor(extensions, options) { this.#options.serverNoContextTakeover = extensions.has("server_no_context_takeover"); this.#options.serverMaxWindowBits = extensions.get("server_max_window_bits"); + this.#maxPayloadSize = options.maxPayloadSize; } + /** + * Decompress a compressed payload. + * @param {Buffer} chunk Compressed data + * @param {boolean} fin Final fragment flag + * @param {Function} callback Callback function + */ decompress(chunk, fin, callback) { - if (this.#aborted) { - callback(new MessageSizeExceededError()); - return; - } if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS; if (this.#options.serverMaxWindowBits) { @@ -14662,20 +14628,11 @@ var require_permessage_deflate = __commonJS({ this.#inflate[kBuffer] = []; this.#inflate[kLength] = 0; this.#inflate.on("data", (data) => { - if (this.#aborted) { - return; - } this.#inflate[kLength] += data.length; - if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { - this.#aborted = true; + if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) { + callback(new MessageSizeExceededError()); this.#inflate.removeAllListeners(); - this.#inflate.destroy(); this.#inflate = null; - if (this.#currentCallback) { - const cb = this.#currentCallback; - this.#currentCallback = null; - cb(new MessageSizeExceededError()); - } return; } this.#inflate[kBuffer].push(data); @@ -14685,19 +14642,17 @@ var require_permessage_deflate = __commonJS({ callback(err); }); } - this.#currentCallback = callback; this.#inflate.write(chunk); if (fin) { this.#inflate.write(tail); } this.#inflate.flush(() => { - if (this.#aborted || !this.#inflate) { + if (!this.#inflate) { return; } const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength]); this.#inflate[kBuffer].length = 0; this.#inflate[kLength] = 0; - this.#currentCallback = null; callback(null, full); }); } @@ -14741,16 +14696,20 @@ var require_receiver = __commonJS({ #extensions; /** @type {import('./websocket').Handler} */ #handler; + /** @type {number} */ + #maxPayloadSize; /** * @param {import('./websocket').Handler} handler * @param {Map|null} extensions + * @param {{ maxPayloadSize?: number }} [options] */ - constructor(handler, extensions) { + constructor(handler, extensions, options = {}) { super(); this.#handler = handler; this.#extensions = extensions == null ? /* @__PURE__ */ new Map() : extensions; + this.#maxPayloadSize = options.maxPayloadSize ?? 0; if (this.#extensions.has("permessage-deflate")) { - this.#extensions.set("permessage-deflate", new PerMessageDeflate(extensions)); + this.#extensions.set("permessage-deflate", new PerMessageDeflate(extensions, options)); } } /** @@ -14763,6 +14722,13 @@ var require_receiver = __commonJS({ this.#loop = true; this.run(callback); } + #validatePayloadLength() { + if (this.#maxPayloadSize > 0 && !isControlFrame(this.#info.opcode) && this.#info.payloadLength > this.#maxPayloadSize) { + failWebsocketConnection(this.#handler, 1009, "Payload size exceeds maximum allowed size"); + return false; + } + return true; + } /** * Runs whenever a new chunk is received. * Callback is called whenever there are no more chunks buffering, @@ -14822,6 +14788,9 @@ var require_receiver = __commonJS({ if (payloadLength <= 125) { this.#info.payloadLength = payloadLength; this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (payloadLength === 126) { this.#state = parserStates.PAYLOADLENGTH_16; } else if (payloadLength === 127) { @@ -14842,6 +14811,9 @@ var require_receiver = __commonJS({ const buffer = this.consume(2); this.#info.payloadLength = buffer.readUInt16BE(0); this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (this.#state === parserStates.PAYLOADLENGTH_64) { if (this.#byteOffset < 8) { return callback(); @@ -14855,6 +14827,9 @@ var require_receiver = __commonJS({ } this.#info.payloadLength = lower; this.#state = parserStates.READ_DATA; + if (!this.#validatePayloadLength()) { + return; + } } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { return callback(); @@ -14871,24 +14846,33 @@ var require_receiver = __commonJS({ } this.#state = parserStates.INFO; } else { - this.#extensions.get("permessage-deflate").decompress(body, this.#info.fin, (error, data) => { - if (error) { - const code = error instanceof MessageSizeExceededError ? 1009 : 1007; - failWebsocketConnection(this.#handler, code, error.message); - return; - } - this.writeFragments(data); - if (!this.#info.fin) { - this.#state = parserStates.INFO; + this.#extensions.get("permessage-deflate").decompress( + body, + this.#info.fin, + (error, data) => { + if (error) { + const code = error instanceof MessageSizeExceededError ? 1009 : 1007; + failWebsocketConnection(this.#handler, code, error.message); + return; + } + this.writeFragments(data); + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnection(this.#handler, 1009, new MessageSizeExceededError().message); + return; + } + if (!this.#info.fin) { + this.#state = parserStates.INFO; + this.#loop = true; + this.run(callback); + return; + } + websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()); this.#loop = true; + this.#state = parserStates.INFO; this.run(callback); - return; - } - websocketMessageReceived(this.#handler, this.#info.binaryType, this.consumeFragments()); - this.#loop = true; - this.#state = parserStates.INFO; - this.run(callback); - }); + }, + this.#fragmentsBytes + ); this.#loop = false; break; } @@ -15413,7 +15397,10 @@ var require_websocket = __commonJS({ */ #onConnectionEstablished(response, parsedExtensions) { this.#handler.socket = response.socket; - const parser = new ByteParser(this.#handler, parsedExtensions); + const maxPayloadSize = this.#handler.controller.dispatcher?.webSocketOptions?.maxPayloadSize; + const parser = new ByteParser(this.#handler, parsedExtensions, { + maxPayloadSize + }); parser.on("drain", () => this.#handler.onParserDrain()); parser.on("error", (err) => this.#handler.onParserError(err)); this.#parser = parser; diff --git a/src/undici_version.h b/src/undici_version.h index be6b6c04f8fd03..aa33bd829f8307 100644 --- a/src/undici_version.h +++ b/src/undici_version.h @@ -2,5 +2,5 @@ // Refer to tools/dep_updaters/update-undici.sh #ifndef SRC_UNDICI_VERSION_H_ #define SRC_UNDICI_VERSION_H_ -#define UNDICI_VERSION "8.0.2" +#define UNDICI_VERSION "8.1.0" #endif // SRC_UNDICI_VERSION_H_