From 91dda5facf359c46bc70a4ddd8867b7df77f206f Mon Sep 17 00:00:00 2001
From: Matteo Collina
Date: Tue, 7 Oct 2025 15:53:53 +0200
Subject: [PATCH 01/90] deps: update undici to 6.22.0
Signed-off-by: Matteo Collina
PR-URL: https://github.com/nodejs/node/pull/60112
Reviewed-By: Richard Lau
Reviewed-By: Matthew Aitken
---
deps/undici/src/docs/docs/api/ProxyAgent.md | 5 +-
deps/undici/src/index-fetch.js | 3 +
deps/undici/src/lib/core/errors.js | 162 ++
deps/undici/src/lib/dispatcher/proxy-agent.js | 84 +-
deps/undici/src/lib/llhttp/wasm_build_env.txt | 4 +-
deps/undici/src/lib/mock/mock-errors.js | 11 +
deps/undici/src/lib/web/fetch/body.js | 9 +-
deps/undici/src/lib/web/fetch/response.js | 5 +
deps/undici/src/package.json | 2 +-
deps/undici/src/types/proxy-agent.d.ts | 1 +
deps/undici/undici.js | 1413 ++++++++++++++++-
src/undici_version.h | 2 +-
12 files changed, 1679 insertions(+), 22 deletions(-)
diff --git a/deps/undici/src/docs/docs/api/ProxyAgent.md b/deps/undici/src/docs/docs/api/ProxyAgent.md
index 17f3f4b6798b..a1a0d4651c82 100644
--- a/deps/undici/src/docs/docs/api/ProxyAgent.md
+++ b/deps/undici/src/docs/docs/api/ProxyAgent.md
@@ -22,8 +22,9 @@ For detailed information on the parsing process and potential validation errors,
* **token** `string` (optional) - It can be passed by a string of token for authentication.
* **auth** `string` (**deprecated**) - Use token.
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
-* **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
-* **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. See [TLS](https://nodejs.org/api/tls.html#tlsconnectoptions-callback).
+* **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
+* **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
+* **proxyTunnel** `boolean` (optional) - For connections involving secure protocols, Undici will always establish a tunnel via the HTTP2 CONNECT extension. If proxyTunnel is set to true, this will occur for unsecured proxy/endpoint connections as well. Currently, there is no way to facilitate HTTP1 IP tunneling as described in https://www.rfc-editor.org/rfc/rfc9484.html#name-http-11-request. If proxyTunnel is set to false (the default), ProxyAgent connections where both the Proxy and Endpoint are unsecured will issue all requests to the Proxy, and prefix the endpoint request path with the endpoint origin address.
Examples:
diff --git a/deps/undici/src/index-fetch.js b/deps/undici/src/index-fetch.js
index 01df32d2fb4d..711d7e8a1e4d 100644
--- a/deps/undici/src/index-fetch.js
+++ b/deps/undici/src/index-fetch.js
@@ -26,6 +26,9 @@ module.exports.createFastMessageEvent = createFastMessageEvent
module.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource
+const api = require('./lib/api')
+const Dispatcher = require('./lib/dispatcher/dispatcher')
+Object.assign(Dispatcher.prototype, api)
// Expose the fetch implementation to be enabled in Node.js core via a flag
module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent
module.exports.getGlobalDispatcher = getGlobalDispatcher
diff --git a/deps/undici/src/lib/core/errors.js b/deps/undici/src/lib/core/errors.js
index 9257875c1c32..535c7339e390 100644
--- a/deps/undici/src/lib/core/errors.js
+++ b/deps/undici/src/lib/core/errors.js
@@ -1,13 +1,21 @@
'use strict'
+const kUndiciError = Symbol.for('undici.error.UND_ERR')
class UndiciError extends Error {
constructor (message) {
super(message)
this.name = 'UndiciError'
this.code = 'UND_ERR'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kUndiciError] === true
+ }
+
+ [kUndiciError] = true
}
+const kConnectTimeoutError = Symbol.for('undici.error.UND_ERR_CONNECT_TIMEOUT')
class ConnectTimeoutError extends UndiciError {
constructor (message) {
super(message)
@@ -15,8 +23,15 @@ class ConnectTimeoutError extends UndiciError {
this.message = message || 'Connect Timeout Error'
this.code = 'UND_ERR_CONNECT_TIMEOUT'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kConnectTimeoutError] === true
+ }
+
+ [kConnectTimeoutError] = true
}
+const kHeadersTimeoutError = Symbol.for('undici.error.UND_ERR_HEADERS_TIMEOUT')
class HeadersTimeoutError extends UndiciError {
constructor (message) {
super(message)
@@ -24,8 +39,15 @@ class HeadersTimeoutError extends UndiciError {
this.message = message || 'Headers Timeout Error'
this.code = 'UND_ERR_HEADERS_TIMEOUT'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kHeadersTimeoutError] === true
+ }
+
+ [kHeadersTimeoutError] = true
}
+const kHeadersOverflowError = Symbol.for('undici.error.UND_ERR_HEADERS_OVERFLOW')
class HeadersOverflowError extends UndiciError {
constructor (message) {
super(message)
@@ -33,8 +55,15 @@ class HeadersOverflowError extends UndiciError {
this.message = message || 'Headers Overflow Error'
this.code = 'UND_ERR_HEADERS_OVERFLOW'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kHeadersOverflowError] === true
+ }
+
+ [kHeadersOverflowError] = true
}
+const kBodyTimeoutError = Symbol.for('undici.error.UND_ERR_BODY_TIMEOUT')
class BodyTimeoutError extends UndiciError {
constructor (message) {
super(message)
@@ -42,8 +71,15 @@ class BodyTimeoutError extends UndiciError {
this.message = message || 'Body Timeout Error'
this.code = 'UND_ERR_BODY_TIMEOUT'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kBodyTimeoutError] === true
+ }
+
+ [kBodyTimeoutError] = true
}
+const kResponseStatusCodeError = Symbol.for('undici.error.UND_ERR_RESPONSE_STATUS_CODE')
class ResponseStatusCodeError extends UndiciError {
constructor (message, statusCode, headers, body) {
super(message)
@@ -55,8 +91,15 @@ class ResponseStatusCodeError extends UndiciError {
this.statusCode = statusCode
this.headers = headers
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kResponseStatusCodeError] === true
+ }
+
+ [kResponseStatusCodeError] = true
}
+const kInvalidArgumentError = Symbol.for('undici.error.UND_ERR_INVALID_ARG')
class InvalidArgumentError extends UndiciError {
constructor (message) {
super(message)
@@ -64,8 +107,15 @@ class InvalidArgumentError extends UndiciError {
this.message = message || 'Invalid Argument Error'
this.code = 'UND_ERR_INVALID_ARG'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kInvalidArgumentError] === true
+ }
+
+ [kInvalidArgumentError] = true
}
+const kInvalidReturnValueError = Symbol.for('undici.error.UND_ERR_INVALID_RETURN_VALUE')
class InvalidReturnValueError extends UndiciError {
constructor (message) {
super(message)
@@ -73,16 +123,31 @@ class InvalidReturnValueError extends UndiciError {
this.message = message || 'Invalid Return Value Error'
this.code = 'UND_ERR_INVALID_RETURN_VALUE'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kInvalidReturnValueError] === true
+ }
+
+ [kInvalidReturnValueError] = true
}
+const kAbortError = Symbol.for('undici.error.UND_ERR_ABORT')
class AbortError extends UndiciError {
constructor (message) {
super(message)
this.name = 'AbortError'
this.message = message || 'The operation was aborted'
+ this.code = 'UND_ERR_ABORT'
+ }
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kAbortError] === true
}
+
+ [kAbortError] = true
}
+const kRequestAbortedError = Symbol.for('undici.error.UND_ERR_ABORTED')
class RequestAbortedError extends AbortError {
constructor (message) {
super(message)
@@ -90,8 +155,15 @@ class RequestAbortedError extends AbortError {
this.message = message || 'Request aborted'
this.code = 'UND_ERR_ABORTED'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kRequestAbortedError] === true
+ }
+
+ [kRequestAbortedError] = true
}
+const kInformationalError = Symbol.for('undici.error.UND_ERR_INFO')
class InformationalError extends UndiciError {
constructor (message) {
super(message)
@@ -99,8 +171,15 @@ class InformationalError extends UndiciError {
this.message = message || 'Request information'
this.code = 'UND_ERR_INFO'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kInformationalError] === true
+ }
+
+ [kInformationalError] = true
}
+const kRequestContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH')
class RequestContentLengthMismatchError extends UndiciError {
constructor (message) {
super(message)
@@ -108,8 +187,15 @@ class RequestContentLengthMismatchError extends UndiciError {
this.message = message || 'Request body length does not match content-length header'
this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kRequestContentLengthMismatchError] === true
+ }
+
+ [kRequestContentLengthMismatchError] = true
}
+const kResponseContentLengthMismatchError = Symbol.for('undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH')
class ResponseContentLengthMismatchError extends UndiciError {
constructor (message) {
super(message)
@@ -117,8 +203,15 @@ class ResponseContentLengthMismatchError extends UndiciError {
this.message = message || 'Response body length does not match content-length header'
this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kResponseContentLengthMismatchError] === true
+ }
+
+ [kResponseContentLengthMismatchError] = true
}
+const kClientDestroyedError = Symbol.for('undici.error.UND_ERR_DESTROYED')
class ClientDestroyedError extends UndiciError {
constructor (message) {
super(message)
@@ -126,8 +219,15 @@ class ClientDestroyedError extends UndiciError {
this.message = message || 'The client is destroyed'
this.code = 'UND_ERR_DESTROYED'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kClientDestroyedError] === true
+ }
+
+ [kClientDestroyedError] = true
}
+const kClientClosedError = Symbol.for('undici.error.UND_ERR_CLOSED')
class ClientClosedError extends UndiciError {
constructor (message) {
super(message)
@@ -135,8 +235,15 @@ class ClientClosedError extends UndiciError {
this.message = message || 'The client is closed'
this.code = 'UND_ERR_CLOSED'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kClientClosedError] === true
+ }
+
+ [kClientClosedError] = true
}
+const kSocketError = Symbol.for('undici.error.UND_ERR_SOCKET')
class SocketError extends UndiciError {
constructor (message, socket) {
super(message)
@@ -145,8 +252,15 @@ class SocketError extends UndiciError {
this.code = 'UND_ERR_SOCKET'
this.socket = socket
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kSocketError] === true
+ }
+
+ [kSocketError] = true
}
+const kNotSupportedError = Symbol.for('undici.error.UND_ERR_NOT_SUPPORTED')
class NotSupportedError extends UndiciError {
constructor (message) {
super(message)
@@ -154,8 +268,15 @@ class NotSupportedError extends UndiciError {
this.message = message || 'Not supported error'
this.code = 'UND_ERR_NOT_SUPPORTED'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kNotSupportedError] === true
+ }
+
+ [kNotSupportedError] = true
}
+const kBalancedPoolMissingUpstreamError = Symbol.for('undici.error.UND_ERR_BPL_MISSING_UPSTREAM')
class BalancedPoolMissingUpstreamError extends UndiciError {
constructor (message) {
super(message)
@@ -163,8 +284,15 @@ class BalancedPoolMissingUpstreamError extends UndiciError {
this.message = message || 'No upstream has been added to the BalancedPool'
this.code = 'UND_ERR_BPL_MISSING_UPSTREAM'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kBalancedPoolMissingUpstreamError] === true
+ }
+
+ [kBalancedPoolMissingUpstreamError] = true
}
+const kHTTPParserError = Symbol.for('undici.error.UND_ERR_HTTP_PARSER')
class HTTPParserError extends Error {
constructor (message, code, data) {
super(message)
@@ -172,8 +300,15 @@ class HTTPParserError extends Error {
this.code = code ? `HPE_${code}` : undefined
this.data = data ? data.toString() : undefined
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kHTTPParserError] === true
+ }
+
+ [kHTTPParserError] = true
}
+const kResponseExceededMaxSizeError = Symbol.for('undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE')
class ResponseExceededMaxSizeError extends UndiciError {
constructor (message) {
super(message)
@@ -181,8 +316,15 @@ class ResponseExceededMaxSizeError extends UndiciError {
this.message = message || 'Response content exceeded max size'
this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kResponseExceededMaxSizeError] === true
+ }
+
+ [kResponseExceededMaxSizeError] = true
}
+const kRequestRetryError = Symbol.for('undici.error.UND_ERR_REQ_RETRY')
class RequestRetryError extends UndiciError {
constructor (message, code, { headers, data }) {
super(message)
@@ -193,8 +335,15 @@ class RequestRetryError extends UndiciError {
this.data = data
this.headers = headers
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kRequestRetryError] === true
+ }
+
+ [kRequestRetryError] = true
}
+const kResponseError = Symbol.for('undici.error.UND_ERR_RESPONSE')
class ResponseError extends UndiciError {
constructor (message, code, { headers, data }) {
super(message)
@@ -205,8 +354,15 @@ class ResponseError extends UndiciError {
this.data = data
this.headers = headers
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kResponseError] === true
+ }
+
+ [kResponseError] = true
}
+const kSecureProxyConnectionError = Symbol.for('undici.error.UND_ERR_PRX_TLS')
class SecureProxyConnectionError extends UndiciError {
constructor (cause, message, options) {
super(message, { cause, ...(options ?? {}) })
@@ -215,6 +371,12 @@ class SecureProxyConnectionError extends UndiciError {
this.code = 'UND_ERR_PRX_TLS'
this.cause = cause
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kSecureProxyConnectionError] === true
+ }
+
+ [kSecureProxyConnectionError] = true
}
module.exports = {
diff --git a/deps/undici/src/lib/dispatcher/proxy-agent.js b/deps/undici/src/lib/dispatcher/proxy-agent.js
index c439d7cd5551..fed180389092 100644
--- a/deps/undici/src/lib/dispatcher/proxy-agent.js
+++ b/deps/undici/src/lib/dispatcher/proxy-agent.js
@@ -1,12 +1,13 @@
'use strict'
-const { kProxy, kClose, kDestroy, kInterceptors } = require('../core/symbols')
+const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = require('../core/symbols')
const { URL } = require('node:url')
const Agent = require('./agent')
const Pool = require('./pool')
const DispatcherBase = require('./dispatcher-base')
const { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require('../core/errors')
const buildConnector = require('../core/connect')
+const Client = require('./client')
const kAgent = Symbol('proxy agent')
const kClient = Symbol('proxy client')
@@ -14,6 +15,7 @@ const kProxyHeaders = Symbol('proxy headers')
const kRequestTls = Symbol('request tls settings')
const kProxyTls = Symbol('proxy tls settings')
const kConnectEndpoint = Symbol('connect endpoint function')
+const kTunnelProxy = Symbol('tunnel proxy')
function defaultProtocolPort (protocol) {
return protocol === 'https:' ? 443 : 80
@@ -25,6 +27,69 @@ function defaultFactory (origin, opts) {
const noop = () => {}
+function defaultAgentFactory (origin, opts) {
+ if (opts.connections === 1) {
+ return new Client(origin, opts)
+ }
+ return new Pool(origin, opts)
+}
+
+class Http1ProxyWrapper extends DispatcherBase {
+ #client
+
+ constructor (proxyUrl, { headers = {}, connect, factory }) {
+ super()
+ if (!proxyUrl) {
+ throw new InvalidArgumentError('Proxy URL is mandatory')
+ }
+
+ this[kProxyHeaders] = headers
+ if (factory) {
+ this.#client = factory(proxyUrl, { connect })
+ } else {
+ this.#client = new Client(proxyUrl, { connect })
+ }
+ }
+
+ [kDispatch] (opts, handler) {
+ const onHeaders = handler.onHeaders
+ handler.onHeaders = function (statusCode, data, resume) {
+ if (statusCode === 407) {
+ if (typeof handler.onError === 'function') {
+ handler.onError(new InvalidArgumentError('Proxy Authentication Required (407)'))
+ }
+ return
+ }
+ if (onHeaders) onHeaders.call(this, statusCode, data, resume)
+ }
+
+ // Rewrite request as an HTTP1 Proxy request, without tunneling.
+ const {
+ origin,
+ path = '/',
+ headers = {}
+ } = opts
+
+ opts.path = origin + path
+
+ if (!('host' in headers) && !('Host' in headers)) {
+ const { host } = new URL(origin)
+ headers.host = host
+ }
+ opts.headers = { ...this[kProxyHeaders], ...headers }
+
+ return this.#client[kDispatch](opts, handler)
+ }
+
+ async [kClose] () {
+ return this.#client.close()
+ }
+
+ async [kDestroy] (err) {
+ return this.#client.destroy(err)
+ }
+}
+
class ProxyAgent extends DispatcherBase {
constructor (opts) {
super()
@@ -38,6 +103,8 @@ class ProxyAgent extends DispatcherBase {
throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.')
}
+ const { proxyTunnel = true } = opts
+
const url = this.#getUrl(opts)
const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url
@@ -48,6 +115,7 @@ class ProxyAgent extends DispatcherBase {
this[kRequestTls] = opts.requestTls
this[kProxyTls] = opts.proxyTls
this[kProxyHeaders] = opts.headers || {}
+ this[kTunnelProxy] = proxyTunnel
if (opts.auth && opts.token) {
throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token')
@@ -62,9 +130,23 @@ class ProxyAgent extends DispatcherBase {
const connect = buildConnector({ ...opts.proxyTls })
this[kConnectEndpoint] = buildConnector({ ...opts.requestTls })
+
+ const agentFactory = opts.factory || defaultAgentFactory
+ const factory = (origin, options) => {
+ const { protocol } = new URL(origin)
+ if (!this[kTunnelProxy] && protocol === 'http:' && this[kProxy].protocol === 'http:') {
+ return new Http1ProxyWrapper(this[kProxy].uri, {
+ headers: this[kProxyHeaders],
+ connect,
+ factory: agentFactory
+ })
+ }
+ return agentFactory(origin, options)
+ }
this[kClient] = clientFactory(url, { connect })
this[kAgent] = new Agent({
...opts,
+ factory,
connect: async (opts, callback) => {
let requestedPath = opts.host
if (!opts.port) {
diff --git a/deps/undici/src/lib/llhttp/wasm_build_env.txt b/deps/undici/src/lib/llhttp/wasm_build_env.txt
index 5ce088f4338b..2940bf6d0c62 100644
--- a/deps/undici/src/lib/llhttp/wasm_build_env.txt
+++ b/deps/undici/src/lib/llhttp/wasm_build_env.txt
@@ -1,12 +1,12 @@
-> undici@6.21.2 prebuild:wasm
+> undici@6.22.0 prebuild:wasm
> node build/wasm.js --prebuild
> docker build --platform=linux/aarch64 -t llhttp_wasm_builder -f /Users/matteo/repos/node/deps/undici/src/build/Dockerfile /Users/matteo/repos/node/deps/undici/src
-> undici@6.21.2 build:wasm
+> undici@6.22.0 build:wasm
> node build/wasm.js --docker
> docker run --rm -t --platform=linux/aarch64 --mount type=bind,source=/Users/matteo/repos/node/deps/undici/src/lib/llhttp,target=/home/node/undici/lib/llhttp llhttp_wasm_builder node build/wasm.js
diff --git a/deps/undici/src/lib/mock/mock-errors.js b/deps/undici/src/lib/mock/mock-errors.js
index 5442c0e8d2c2..3de5603c8a90 100644
--- a/deps/undici/src/lib/mock/mock-errors.js
+++ b/deps/undici/src/lib/mock/mock-errors.js
@@ -2,6 +2,11 @@
const { UndiciError } = require('../core/errors')
+const kMockNotMatchedError = Symbol.for('undici.error.UND_MOCK_ERR_MOCK_NOT_MATCHED')
+
+/**
+ * The request does not match any registered mock dispatches.
+ */
class MockNotMatchedError extends UndiciError {
constructor (message) {
super(message)
@@ -10,6 +15,12 @@ class MockNotMatchedError extends UndiciError {
this.message = message || 'The request does not match any registered mock dispatches'
this.code = 'UND_MOCK_ERR_MOCK_NOT_MATCHED'
}
+
+ static [Symbol.hasInstance] (instance) {
+ return instance && instance[kMockNotMatchedError] === true
+ }
+
+ [kMockNotMatchedError] = true
}
module.exports = {
diff --git a/deps/undici/src/lib/web/fetch/body.js b/deps/undici/src/lib/web/fetch/body.js
index aa6e2973532e..b1c553d4e0ff 100644
--- a/deps/undici/src/lib/web/fetch/body.js
+++ b/deps/undici/src/lib/web/fetch/body.js
@@ -162,7 +162,10 @@ function extractBody (object, keepalive = false) {
}
}
- const chunk = textEncoder.encode(`--${boundary}--`)
+ // CRLF is appended to the body to function with legacy servers and match other implementations.
+ // https://github.com/curl/curl/blob/3434c6b46e682452973972e8313613dfa58cd690/lib/mime.c#L1029-L1030
+ // https://github.com/form-data/form-data/issues/63
+ const chunk = textEncoder.encode(`--${boundary}--\r\n`)
blobParts.push(chunk)
length += chunk.byteLength
if (hasUnknownSizeValue) {
@@ -293,10 +296,6 @@ function cloneBody (instance, body) {
// 1. Let « out1, out2 » be the result of teeing body’s stream.
const [out1, out2] = body.stream.tee()
- if (hasFinalizationRegistry) {
- streamRegistry.register(instance, new WeakRef(out1))
- }
-
// 2. Set body’s stream to out1.
body.stream = out1
diff --git a/deps/undici/src/lib/web/fetch/response.js b/deps/undici/src/lib/web/fetch/response.js
index 3b6af35fbed0..3abaa8bd6d5e 100644
--- a/deps/undici/src/lib/web/fetch/response.js
+++ b/deps/undici/src/lib/web/fetch/response.js
@@ -240,6 +240,11 @@ class Response {
// 2. Let clonedResponse be the result of cloning this’s response.
const clonedResponse = cloneResponse(this[kState])
+ // Note: To re-register because of a new stream.
+ if (hasFinalizationRegistry && this[kState].body?.stream) {
+ streamRegistry.register(this, new WeakRef(this[kState].body.stream))
+ }
+
// 3. Return the result of creating a Response object, given
// clonedResponse, this’s headers’s guard, and this’s relevant Realm.
return fromInnerResponse(clonedResponse, getHeadersGuard(this[kHeaders]))
diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json
index d7fab14768ad..d6c7a3339e8d 100644
--- a/deps/undici/src/package.json
+++ b/deps/undici/src/package.json
@@ -1,6 +1,6 @@
{
"name": "undici",
- "version": "6.21.2",
+ "version": "6.22.0",
"description": "An HTTP/1.1 client, written from scratch for Node.js",
"homepage": "https://undici.nodejs.org",
"bugs": {
diff --git a/deps/undici/src/types/proxy-agent.d.ts b/deps/undici/src/types/proxy-agent.d.ts
index 32e3acbdaad4..27e237c6e3b0 100644
--- a/deps/undici/src/types/proxy-agent.d.ts
+++ b/deps/undici/src/types/proxy-agent.d.ts
@@ -24,5 +24,6 @@ declare namespace ProxyAgent {
requestTls?: buildConnector.BuildOptions;
proxyTls?: buildConnector.BuildOptions;
clientFactory?(origin: URL, opts: object): Dispatcher;
+ proxyTunnel?: boolean;
}
}
diff --git a/deps/undici/undici.js b/deps/undici/undici.js
index d65ffba0ff6c..3dca9ec26ad2 100644
--- a/deps/undici/undici.js
+++ b/deps/undici/undici.js
@@ -10,6 +10,7 @@ var __commonJS = (cb, mod) => function __require() {
var require_errors = __commonJS({
"lib/core/errors.js"(exports2, module2) {
"use strict";
+ var kUndiciError = Symbol.for("undici.error.UND_ERR");
var UndiciError = class extends Error {
static {
__name(this, "UndiciError");
@@ -19,7 +20,12 @@ var require_errors = __commonJS({
this.name = "UndiciError";
this.code = "UND_ERR";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kUndiciError] === true;
+ }
+ [kUndiciError] = true;
};
+ var kConnectTimeoutError = Symbol.for("undici.error.UND_ERR_CONNECT_TIMEOUT");
var ConnectTimeoutError = class extends UndiciError {
static {
__name(this, "ConnectTimeoutError");
@@ -30,7 +36,12 @@ var require_errors = __commonJS({
this.message = message || "Connect Timeout Error";
this.code = "UND_ERR_CONNECT_TIMEOUT";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kConnectTimeoutError] === true;
+ }
+ [kConnectTimeoutError] = true;
};
+ var kHeadersTimeoutError = Symbol.for("undici.error.UND_ERR_HEADERS_TIMEOUT");
var HeadersTimeoutError = class extends UndiciError {
static {
__name(this, "HeadersTimeoutError");
@@ -41,7 +52,12 @@ var require_errors = __commonJS({
this.message = message || "Headers Timeout Error";
this.code = "UND_ERR_HEADERS_TIMEOUT";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kHeadersTimeoutError] === true;
+ }
+ [kHeadersTimeoutError] = true;
};
+ var kHeadersOverflowError = Symbol.for("undici.error.UND_ERR_HEADERS_OVERFLOW");
var HeadersOverflowError = class extends UndiciError {
static {
__name(this, "HeadersOverflowError");
@@ -52,7 +68,12 @@ var require_errors = __commonJS({
this.message = message || "Headers Overflow Error";
this.code = "UND_ERR_HEADERS_OVERFLOW";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kHeadersOverflowError] === true;
+ }
+ [kHeadersOverflowError] = true;
};
+ var kBodyTimeoutError = Symbol.for("undici.error.UND_ERR_BODY_TIMEOUT");
var BodyTimeoutError = class extends UndiciError {
static {
__name(this, "BodyTimeoutError");
@@ -63,7 +84,12 @@ var require_errors = __commonJS({
this.message = message || "Body Timeout Error";
this.code = "UND_ERR_BODY_TIMEOUT";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kBodyTimeoutError] === true;
+ }
+ [kBodyTimeoutError] = true;
};
+ var kResponseStatusCodeError = Symbol.for("undici.error.UND_ERR_RESPONSE_STATUS_CODE");
var ResponseStatusCodeError = class extends UndiciError {
static {
__name(this, "ResponseStatusCodeError");
@@ -78,7 +104,12 @@ var require_errors = __commonJS({
this.statusCode = statusCode;
this.headers = headers;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kResponseStatusCodeError] === true;
+ }
+ [kResponseStatusCodeError] = true;
};
+ var kInvalidArgumentError = Symbol.for("undici.error.UND_ERR_INVALID_ARG");
var InvalidArgumentError = class extends UndiciError {
static {
__name(this, "InvalidArgumentError");
@@ -89,7 +120,12 @@ var require_errors = __commonJS({
this.message = message || "Invalid Argument Error";
this.code = "UND_ERR_INVALID_ARG";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kInvalidArgumentError] === true;
+ }
+ [kInvalidArgumentError] = true;
};
+ var kInvalidReturnValueError = Symbol.for("undici.error.UND_ERR_INVALID_RETURN_VALUE");
var InvalidReturnValueError = class extends UndiciError {
static {
__name(this, "InvalidReturnValueError");
@@ -100,7 +136,12 @@ var require_errors = __commonJS({
this.message = message || "Invalid Return Value Error";
this.code = "UND_ERR_INVALID_RETURN_VALUE";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kInvalidReturnValueError] === true;
+ }
+ [kInvalidReturnValueError] = true;
};
+ var kAbortError = Symbol.for("undici.error.UND_ERR_ABORT");
var AbortError = class extends UndiciError {
static {
__name(this, "AbortError");
@@ -109,8 +150,14 @@ var require_errors = __commonJS({
super(message);
this.name = "AbortError";
this.message = message || "The operation was aborted";
+ this.code = "UND_ERR_ABORT";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kAbortError] === true;
+ }
+ [kAbortError] = true;
};
+ var kRequestAbortedError = Symbol.for("undici.error.UND_ERR_ABORTED");
var RequestAbortedError = class extends AbortError {
static {
__name(this, "RequestAbortedError");
@@ -121,7 +168,12 @@ var require_errors = __commonJS({
this.message = message || "Request aborted";
this.code = "UND_ERR_ABORTED";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kRequestAbortedError] === true;
+ }
+ [kRequestAbortedError] = true;
};
+ var kInformationalError = Symbol.for("undici.error.UND_ERR_INFO");
var InformationalError = class extends UndiciError {
static {
__name(this, "InformationalError");
@@ -132,7 +184,12 @@ var require_errors = __commonJS({
this.message = message || "Request information";
this.code = "UND_ERR_INFO";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kInformationalError] === true;
+ }
+ [kInformationalError] = true;
};
+ var kRequestContentLengthMismatchError = Symbol.for("undici.error.UND_ERR_REQ_CONTENT_LENGTH_MISMATCH");
var RequestContentLengthMismatchError = class extends UndiciError {
static {
__name(this, "RequestContentLengthMismatchError");
@@ -143,7 +200,12 @@ var require_errors = __commonJS({
this.message = message || "Request body length does not match content-length header";
this.code = "UND_ERR_REQ_CONTENT_LENGTH_MISMATCH";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kRequestContentLengthMismatchError] === true;
+ }
+ [kRequestContentLengthMismatchError] = true;
};
+ var kResponseContentLengthMismatchError = Symbol.for("undici.error.UND_ERR_RES_CONTENT_LENGTH_MISMATCH");
var ResponseContentLengthMismatchError = class extends UndiciError {
static {
__name(this, "ResponseContentLengthMismatchError");
@@ -154,7 +216,12 @@ var require_errors = __commonJS({
this.message = message || "Response body length does not match content-length header";
this.code = "UND_ERR_RES_CONTENT_LENGTH_MISMATCH";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kResponseContentLengthMismatchError] === true;
+ }
+ [kResponseContentLengthMismatchError] = true;
};
+ var kClientDestroyedError = Symbol.for("undici.error.UND_ERR_DESTROYED");
var ClientDestroyedError = class extends UndiciError {
static {
__name(this, "ClientDestroyedError");
@@ -165,7 +232,12 @@ var require_errors = __commonJS({
this.message = message || "The client is destroyed";
this.code = "UND_ERR_DESTROYED";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kClientDestroyedError] === true;
+ }
+ [kClientDestroyedError] = true;
};
+ var kClientClosedError = Symbol.for("undici.error.UND_ERR_CLOSED");
var ClientClosedError = class extends UndiciError {
static {
__name(this, "ClientClosedError");
@@ -176,7 +248,12 @@ var require_errors = __commonJS({
this.message = message || "The client is closed";
this.code = "UND_ERR_CLOSED";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kClientClosedError] === true;
+ }
+ [kClientClosedError] = true;
};
+ var kSocketError = Symbol.for("undici.error.UND_ERR_SOCKET");
var SocketError = class extends UndiciError {
static {
__name(this, "SocketError");
@@ -188,7 +265,12 @@ var require_errors = __commonJS({
this.code = "UND_ERR_SOCKET";
this.socket = socket;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kSocketError] === true;
+ }
+ [kSocketError] = true;
};
+ var kNotSupportedError = Symbol.for("undici.error.UND_ERR_NOT_SUPPORTED");
var NotSupportedError = class extends UndiciError {
static {
__name(this, "NotSupportedError");
@@ -199,7 +281,12 @@ var require_errors = __commonJS({
this.message = message || "Not supported error";
this.code = "UND_ERR_NOT_SUPPORTED";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kNotSupportedError] === true;
+ }
+ [kNotSupportedError] = true;
};
+ var kBalancedPoolMissingUpstreamError = Symbol.for("undici.error.UND_ERR_BPL_MISSING_UPSTREAM");
var BalancedPoolMissingUpstreamError = class extends UndiciError {
static {
__name(this, "BalancedPoolMissingUpstreamError");
@@ -210,7 +297,12 @@ var require_errors = __commonJS({
this.message = message || "No upstream has been added to the BalancedPool";
this.code = "UND_ERR_BPL_MISSING_UPSTREAM";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kBalancedPoolMissingUpstreamError] === true;
+ }
+ [kBalancedPoolMissingUpstreamError] = true;
};
+ var kHTTPParserError = Symbol.for("undici.error.UND_ERR_HTTP_PARSER");
var HTTPParserError = class extends Error {
static {
__name(this, "HTTPParserError");
@@ -221,7 +313,12 @@ var require_errors = __commonJS({
this.code = code ? `HPE_${code}` : void 0;
this.data = data ? data.toString() : void 0;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kHTTPParserError] === true;
+ }
+ [kHTTPParserError] = true;
};
+ var kResponseExceededMaxSizeError = Symbol.for("undici.error.UND_ERR_RES_EXCEEDED_MAX_SIZE");
var ResponseExceededMaxSizeError = class extends UndiciError {
static {
__name(this, "ResponseExceededMaxSizeError");
@@ -232,7 +329,12 @@ var require_errors = __commonJS({
this.message = message || "Response content exceeded max size";
this.code = "UND_ERR_RES_EXCEEDED_MAX_SIZE";
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kResponseExceededMaxSizeError] === true;
+ }
+ [kResponseExceededMaxSizeError] = true;
};
+ var kRequestRetryError = Symbol.for("undici.error.UND_ERR_REQ_RETRY");
var RequestRetryError = class extends UndiciError {
static {
__name(this, "RequestRetryError");
@@ -246,7 +348,12 @@ var require_errors = __commonJS({
this.data = data;
this.headers = headers;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kRequestRetryError] === true;
+ }
+ [kRequestRetryError] = true;
};
+ var kResponseError = Symbol.for("undici.error.UND_ERR_RESPONSE");
var ResponseError = class extends UndiciError {
static {
__name(this, "ResponseError");
@@ -260,7 +367,12 @@ var require_errors = __commonJS({
this.data = data;
this.headers = headers;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kResponseError] === true;
+ }
+ [kResponseError] = true;
};
+ var kSecureProxyConnectionError = Symbol.for("undici.error.UND_ERR_PRX_TLS");
var SecureProxyConnectionError = class extends UndiciError {
static {
__name(this, "SecureProxyConnectionError");
@@ -272,6 +384,10 @@ var require_errors = __commonJS({
this.code = "UND_ERR_PRX_TLS";
this.cause = cause;
}
+ static [Symbol.hasInstance](instance) {
+ return instance && instance[kSecureProxyConnectionError] === true;
+ }
+ [kSecureProxyConnectionError] = true;
};
module2.exports = {
AbortError,
@@ -379,7 +495,7 @@ var require_dispatcher = __commonJS({
"lib/dispatcher/dispatcher.js"(exports2, module2) {
"use strict";
var EventEmitter = require("node:events");
- var Dispatcher = class extends EventEmitter {
+ var Dispatcher2 = class extends EventEmitter {
static {
__name(this, "Dispatcher");
}
@@ -410,7 +526,7 @@ var require_dispatcher = __commonJS({
return new ComposedDispatcher(this, dispatch);
}
};
- var ComposedDispatcher = class extends Dispatcher {
+ var ComposedDispatcher = class extends Dispatcher2 {
static {
__name(this, "ComposedDispatcher");
}
@@ -431,7 +547,7 @@ var require_dispatcher = __commonJS({
return this.#dispatcher.destroy(...args);
}
};
- module2.exports = Dispatcher;
+ module2.exports = Dispatcher2;
}
});
@@ -439,7 +555,7 @@ var require_dispatcher = __commonJS({
var require_dispatcher_base = __commonJS({
"lib/dispatcher/dispatcher-base.js"(exports2, module2) {
"use strict";
- var Dispatcher = require_dispatcher();
+ var Dispatcher2 = require_dispatcher();
var {
ClientDestroyedError,
ClientClosedError,
@@ -449,7 +565,7 @@ var require_dispatcher_base = __commonJS({
var kOnDestroyed = Symbol("onDestroyed");
var kOnClosed = Symbol("onClosed");
var kInterceptedDispatch = Symbol("Intercepted Dispatch");
- var DispatcherBase = class extends Dispatcher {
+ var DispatcherBase = class extends Dispatcher2 {
static {
__name(this, "DispatcherBase");
}
@@ -5543,7 +5659,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
}
}
}
- const chunk = textEncoder.encode(`--${boundary}--`);
+ const chunk = textEncoder.encode(`--${boundary}--\r
+`);
blobParts.push(chunk);
length += chunk.byteLength;
if (hasUnknownSizeValue) {
@@ -5623,9 +5740,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
__name(safelyExtractBody, "safelyExtractBody");
function cloneBody(instance, body) {
const [out1, out2] = body.stream.tee();
- if (hasFinalizationRegistry) {
- streamRegistry.register(instance, new WeakRef(out1));
- }
body.stream = out1;
return {
stream: out2,
@@ -8287,19 +8401,21 @@ var require_global2 = __commonJS({
var require_proxy_agent = __commonJS({
"lib/dispatcher/proxy-agent.js"(exports2, module2) {
"use strict";
- var { kProxy, kClose, kDestroy, kInterceptors } = require_symbols();
+ var { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = require_symbols();
var { URL: URL2 } = require("node:url");
var Agent = require_agent();
var Pool = require_pool();
var DispatcherBase = require_dispatcher_base();
var { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = require_errors();
var buildConnector = require_connect();
+ var Client = require_client();
var kAgent = Symbol("proxy agent");
var kClient = Symbol("proxy client");
var kProxyHeaders = Symbol("proxy headers");
var kRequestTls = Symbol("request tls settings");
var kProxyTls = Symbol("proxy tls settings");
var kConnectEndpoint = Symbol("connect endpoint function");
+ var kTunnelProxy = Symbol("tunnel proxy");
function defaultProtocolPort(protocol) {
return protocol === "https:" ? 443 : 80;
}
@@ -8310,6 +8426,62 @@ var require_proxy_agent = __commonJS({
__name(defaultFactory, "defaultFactory");
var noop = /* @__PURE__ */ __name(() => {
}, "noop");
+ function defaultAgentFactory(origin, opts) {
+ if (opts.connections === 1) {
+ return new Client(origin, opts);
+ }
+ return new Pool(origin, opts);
+ }
+ __name(defaultAgentFactory, "defaultAgentFactory");
+ var Http1ProxyWrapper = class extends DispatcherBase {
+ static {
+ __name(this, "Http1ProxyWrapper");
+ }
+ #client;
+ constructor(proxyUrl, { headers = {}, connect, factory }) {
+ super();
+ if (!proxyUrl) {
+ throw new InvalidArgumentError("Proxy URL is mandatory");
+ }
+ this[kProxyHeaders] = headers;
+ if (factory) {
+ this.#client = factory(proxyUrl, { connect });
+ } else {
+ this.#client = new Client(proxyUrl, { connect });
+ }
+ }
+ [kDispatch](opts, handler) {
+ const onHeaders = handler.onHeaders;
+ handler.onHeaders = function(statusCode, data, resume) {
+ if (statusCode === 407) {
+ if (typeof handler.onError === "function") {
+ handler.onError(new InvalidArgumentError("Proxy Authentication Required (407)"));
+ }
+ return;
+ }
+ if (onHeaders)
+ onHeaders.call(this, statusCode, data, resume);
+ };
+ const {
+ origin,
+ path = "/",
+ headers = {}
+ } = opts;
+ opts.path = origin + path;
+ if (!("host" in headers) && !("Host" in headers)) {
+ const { host } = new URL2(origin);
+ headers.host = host;
+ }
+ opts.headers = { ...this[kProxyHeaders], ...headers };
+ return this.#client[kDispatch](opts, handler);
+ }
+ async [kClose]() {
+ return this.#client.close();
+ }
+ async [kDestroy](err) {
+ return this.#client.destroy(err);
+ }
+ };
var ProxyAgent = class extends DispatcherBase {
static {
__name(this, "ProxyAgent");
@@ -8323,6 +8495,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 url = this.#getUrl(opts);
const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url;
this[kProxy] = { uri: href, protocol };
@@ -8330,6 +8503,7 @@ var require_proxy_agent = __commonJS({
this[kRequestTls] = opts.requestTls;
this[kProxyTls] = opts.proxyTls;
this[kProxyHeaders] = opts.headers || {};
+ this[kTunnelProxy] = proxyTunnel;
if (opts.auth && opts.token) {
throw new InvalidArgumentError("opts.auth cannot be used in combination with opts.token");
} else if (opts.auth) {
@@ -8341,9 +8515,22 @@ var require_proxy_agent = __commonJS({
}
const connect = buildConnector({ ...opts.proxyTls });
this[kConnectEndpoint] = buildConnector({ ...opts.requestTls });
+ const agentFactory = opts.factory || defaultAgentFactory;
+ const factory = /* @__PURE__ */ __name((origin2, options) => {
+ const { protocol: protocol2 } = new URL2(origin2);
+ if (!this[kTunnelProxy] && protocol2 === "http:" && this[kProxy].protocol === "http:") {
+ return new Http1ProxyWrapper(this[kProxy].uri, {
+ headers: this[kProxyHeaders],
+ connect,
+ factory: agentFactory
+ });
+ }
+ return agentFactory(origin2, options);
+ }, "factory");
this[kClient] = clientFactory(url, { connect });
this[kAgent] = new Agent({
...opts,
+ factory,
connect: async (opts2, callback) => {
let requestedPath = opts2.host;
if (!opts2.port) {
@@ -9195,6 +9382,9 @@ var require_response = __commonJS({
});
}
const clonedResponse = cloneResponse(this[kState]);
+ if (hasFinalizationRegistry && this[kState].body?.stream) {
+ streamRegistry.register(this, new WeakRef(this[kState].body.stream));
+ }
return fromInnerResponse(clonedResponse, getHeadersGuard(this[kHeaders]));
}
[nodeUtil.inspect.custom](depth, options) {
@@ -13500,6 +13690,1206 @@ var require_eventsource = __commonJS({
}
});
+// lib/api/readable.js
+var require_readable = __commonJS({
+ "lib/api/readable.js"(exports2, module2) {
+ "use strict";
+ var assert = require("node:assert");
+ var { Readable } = require("node:stream");
+ var { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require_errors();
+ var util = require_util();
+ var { ReadableStreamFrom } = require_util();
+ var kConsume = Symbol("kConsume");
+ var kReading = Symbol("kReading");
+ var kBody = Symbol("kBody");
+ var kAbort = Symbol("kAbort");
+ var kContentType = Symbol("kContentType");
+ var kContentLength = Symbol("kContentLength");
+ var noop = /* @__PURE__ */ __name(() => {
+ }, "noop");
+ var BodyReadable = class extends Readable {
+ static {
+ __name(this, "BodyReadable");
+ }
+ constructor({
+ resume,
+ abort,
+ contentType = "",
+ contentLength,
+ highWaterMark = 64 * 1024
+ // Same as nodejs fs streams.
+ }) {
+ super({
+ autoDestroy: true,
+ read: resume,
+ highWaterMark
+ });
+ this._readableState.dataEmitted = false;
+ this[kAbort] = abort;
+ this[kConsume] = null;
+ this[kBody] = null;
+ this[kContentType] = contentType;
+ this[kContentLength] = contentLength;
+ this[kReading] = false;
+ }
+ destroy(err) {
+ if (!err && !this._readableState.endEmitted) {
+ err = new RequestAbortedError();
+ }
+ if (err) {
+ this[kAbort]();
+ }
+ return super.destroy(err);
+ }
+ _destroy(err, callback) {
+ if (!this[kReading]) {
+ setImmediate(() => {
+ callback(err);
+ });
+ } else {
+ callback(err);
+ }
+ }
+ on(ev, ...args) {
+ if (ev === "data" || ev === "readable") {
+ this[kReading] = true;
+ }
+ return super.on(ev, ...args);
+ }
+ addListener(ev, ...args) {
+ return this.on(ev, ...args);
+ }
+ off(ev, ...args) {
+ const ret = super.off(ev, ...args);
+ if (ev === "data" || ev === "readable") {
+ this[kReading] = this.listenerCount("data") > 0 || this.listenerCount("readable") > 0;
+ }
+ return ret;
+ }
+ removeListener(ev, ...args) {
+ return this.off(ev, ...args);
+ }
+ push(chunk) {
+ if (this[kConsume] && chunk !== null) {
+ consumePush(this[kConsume], chunk);
+ return this[kReading] ? super.push(chunk) : true;
+ }
+ return super.push(chunk);
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-text
+ async text() {
+ return consume(this, "text");
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-json
+ async json() {
+ return consume(this, "json");
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-blob
+ async blob() {
+ return consume(this, "blob");
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-bytes
+ async bytes() {
+ return consume(this, "bytes");
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-arraybuffer
+ async arrayBuffer() {
+ return consume(this, "arrayBuffer");
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-formdata
+ async formData() {
+ throw new NotSupportedError();
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-bodyused
+ get bodyUsed() {
+ return util.isDisturbed(this);
+ }
+ // https://fetch.spec.whatwg.org/#dom-body-body
+ get body() {
+ if (!this[kBody]) {
+ this[kBody] = ReadableStreamFrom(this);
+ if (this[kConsume]) {
+ this[kBody].getReader();
+ assert(this[kBody].locked);
+ }
+ }
+ return this[kBody];
+ }
+ async dump(opts) {
+ let limit = Number.isFinite(opts?.limit) ? opts.limit : 128 * 1024;
+ const signal = opts?.signal;
+ if (signal != null && (typeof signal !== "object" || !("aborted" in signal))) {
+ throw new InvalidArgumentError("signal must be an AbortSignal");
+ }
+ signal?.throwIfAborted();
+ if (this._readableState.closeEmitted) {
+ return null;
+ }
+ return await new Promise((resolve, reject) => {
+ if (this[kContentLength] > limit) {
+ this.destroy(new AbortError());
+ }
+ const onAbort = /* @__PURE__ */ __name(() => {
+ this.destroy(signal.reason ?? new AbortError());
+ }, "onAbort");
+ signal?.addEventListener("abort", onAbort);
+ this.on("close", function() {
+ signal?.removeEventListener("abort", onAbort);
+ if (signal?.aborted) {
+ reject(signal.reason ?? new AbortError());
+ } else {
+ resolve(null);
+ }
+ }).on("error", noop).on("data", function(chunk) {
+ limit -= chunk.length;
+ if (limit <= 0) {
+ this.destroy();
+ }
+ }).resume();
+ });
+ }
+ };
+ function isLocked(self) {
+ return self[kBody] && self[kBody].locked === true || self[kConsume];
+ }
+ __name(isLocked, "isLocked");
+ function isUnusable(self) {
+ return util.isDisturbed(self) || isLocked(self);
+ }
+ __name(isUnusable, "isUnusable");
+ async function consume(stream, type) {
+ assert(!stream[kConsume]);
+ return new Promise((resolve, reject) => {
+ if (isUnusable(stream)) {
+ const rState = stream._readableState;
+ if (rState.destroyed && rState.closeEmitted === false) {
+ stream.on("error", (err) => {
+ reject(err);
+ }).on("close", () => {
+ reject(new TypeError("unusable"));
+ });
+ } else {
+ reject(rState.errored ?? new TypeError("unusable"));
+ }
+ } else {
+ queueMicrotask(() => {
+ stream[kConsume] = {
+ type,
+ stream,
+ resolve,
+ reject,
+ length: 0,
+ body: []
+ };
+ stream.on("error", function(err) {
+ consumeFinish(this[kConsume], err);
+ }).on("close", function() {
+ if (this[kConsume].body !== null) {
+ consumeFinish(this[kConsume], new RequestAbortedError());
+ }
+ });
+ consumeStart(stream[kConsume]);
+ });
+ }
+ });
+ }
+ __name(consume, "consume");
+ function consumeStart(consume2) {
+ if (consume2.body === null) {
+ return;
+ }
+ const { _readableState: state } = consume2.stream;
+ if (state.bufferIndex) {
+ const start = state.bufferIndex;
+ const end = state.buffer.length;
+ for (let n = start; n < end; n++) {
+ consumePush(consume2, state.buffer[n]);
+ }
+ } else {
+ for (const chunk of state.buffer) {
+ consumePush(consume2, chunk);
+ }
+ }
+ if (state.endEmitted) {
+ consumeEnd(this[kConsume]);
+ } else {
+ consume2.stream.on("end", function() {
+ consumeEnd(this[kConsume]);
+ });
+ }
+ consume2.stream.resume();
+ while (consume2.stream.read() != null) {
+ }
+ }
+ __name(consumeStart, "consumeStart");
+ function chunksDecode(chunks, length) {
+ if (chunks.length === 0 || length === 0) {
+ return "";
+ }
+ const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks, length);
+ const bufferLength = buffer.length;
+ const start = bufferLength > 2 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191 ? 3 : 0;
+ return buffer.utf8Slice(start, bufferLength);
+ }
+ __name(chunksDecode, "chunksDecode");
+ function chunksConcat(chunks, length) {
+ if (chunks.length === 0 || length === 0) {
+ return new Uint8Array(0);
+ }
+ if (chunks.length === 1) {
+ return new Uint8Array(chunks[0]);
+ }
+ const buffer = new Uint8Array(Buffer.allocUnsafeSlow(length).buffer);
+ let offset = 0;
+ for (let i = 0; i < chunks.length; ++i) {
+ const chunk = chunks[i];
+ buffer.set(chunk, offset);
+ offset += chunk.length;
+ }
+ return buffer;
+ }
+ __name(chunksConcat, "chunksConcat");
+ function consumeEnd(consume2) {
+ const { type, body, resolve, stream, length } = consume2;
+ try {
+ if (type === "text") {
+ resolve(chunksDecode(body, length));
+ } else if (type === "json") {
+ resolve(JSON.parse(chunksDecode(body, length)));
+ } else if (type === "arrayBuffer") {
+ resolve(chunksConcat(body, length).buffer);
+ } else if (type === "blob") {
+ resolve(new Blob(body, { type: stream[kContentType] }));
+ } else if (type === "bytes") {
+ resolve(chunksConcat(body, length));
+ }
+ consumeFinish(consume2);
+ } catch (err) {
+ stream.destroy(err);
+ }
+ }
+ __name(consumeEnd, "consumeEnd");
+ function consumePush(consume2, chunk) {
+ consume2.length += chunk.length;
+ consume2.body.push(chunk);
+ }
+ __name(consumePush, "consumePush");
+ function consumeFinish(consume2, err) {
+ if (consume2.body === null) {
+ return;
+ }
+ if (err) {
+ consume2.reject(err);
+ } else {
+ consume2.resolve();
+ }
+ consume2.type = null;
+ consume2.stream = null;
+ consume2.resolve = null;
+ consume2.reject = null;
+ consume2.length = 0;
+ consume2.body = null;
+ }
+ __name(consumeFinish, "consumeFinish");
+ module2.exports = { Readable: BodyReadable, chunksDecode };
+ }
+});
+
+// lib/api/util.js
+var require_util5 = __commonJS({
+ "lib/api/util.js"(exports2, module2) {
+ var assert = require("node:assert");
+ var {
+ ResponseStatusCodeError
+ } = require_errors();
+ var { chunksDecode } = require_readable();
+ var CHUNK_LIMIT = 128 * 1024;
+ async function getResolveErrorBodyCallback({ callback, body, contentType, statusCode, statusMessage, headers }) {
+ assert(body);
+ let chunks = [];
+ let length = 0;
+ try {
+ for await (const chunk of body) {
+ chunks.push(chunk);
+ length += chunk.length;
+ if (length > CHUNK_LIMIT) {
+ chunks = [];
+ length = 0;
+ break;
+ }
+ }
+ } catch {
+ chunks = [];
+ length = 0;
+ }
+ const message = `Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ""}`;
+ if (statusCode === 204 || !contentType || !length) {
+ queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers)));
+ return;
+ }
+ const stackTraceLimit = Error.stackTraceLimit;
+ Error.stackTraceLimit = 0;
+ let payload;
+ try {
+ if (isContentTypeApplicationJson(contentType)) {
+ payload = JSON.parse(chunksDecode(chunks, length));
+ } else if (isContentTypeText(contentType)) {
+ payload = chunksDecode(chunks, length);
+ }
+ } catch {
+ } finally {
+ Error.stackTraceLimit = stackTraceLimit;
+ }
+ queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers, payload)));
+ }
+ __name(getResolveErrorBodyCallback, "getResolveErrorBodyCallback");
+ var isContentTypeApplicationJson = /* @__PURE__ */ __name((contentType) => {
+ return contentType.length > 15 && contentType[11] === "/" && contentType[0] === "a" && contentType[1] === "p" && contentType[2] === "p" && contentType[3] === "l" && contentType[4] === "i" && contentType[5] === "c" && contentType[6] === "a" && contentType[7] === "t" && contentType[8] === "i" && contentType[9] === "o" && contentType[10] === "n" && contentType[12] === "j" && contentType[13] === "s" && contentType[14] === "o" && contentType[15] === "n";
+ }, "isContentTypeApplicationJson");
+ var isContentTypeText = /* @__PURE__ */ __name((contentType) => {
+ return contentType.length > 4 && contentType[4] === "/" && contentType[0] === "t" && contentType[1] === "e" && contentType[2] === "x" && contentType[3] === "t";
+ }, "isContentTypeText");
+ module2.exports = {
+ getResolveErrorBodyCallback,
+ isContentTypeApplicationJson,
+ isContentTypeText
+ };
+ }
+});
+
+// lib/api/api-request.js
+var require_api_request = __commonJS({
+ "lib/api/api-request.js"(exports2, module2) {
+ "use strict";
+ var assert = require("node:assert");
+ var { Readable } = require_readable();
+ var { InvalidArgumentError, RequestAbortedError } = require_errors();
+ var util = require_util();
+ var { getResolveErrorBodyCallback } = require_util5();
+ var { AsyncResource } = require("node:async_hooks");
+ var RequestHandler = class extends AsyncResource {
+ static {
+ __name(this, "RequestHandler");
+ }
+ constructor(opts, callback) {
+ if (!opts || typeof opts !== "object") {
+ throw new InvalidArgumentError("invalid opts");
+ }
+ const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError, highWaterMark } = opts;
+ try {
+ if (typeof callback !== "function") {
+ throw new InvalidArgumentError("invalid callback");
+ }
+ if (highWaterMark && (typeof highWaterMark !== "number" || highWaterMark < 0)) {
+ throw new InvalidArgumentError("invalid highWaterMark");
+ }
+ if (signal && typeof signal.on !== "function" && typeof signal.addEventListener !== "function") {
+ throw new InvalidArgumentError("signal must be an EventEmitter or EventTarget");
+ }
+ if (method === "CONNECT") {
+ throw new InvalidArgumentError("invalid method");
+ }
+ if (onInfo && typeof onInfo !== "function") {
+ throw new InvalidArgumentError("invalid onInfo callback");
+ }
+ super("UNDICI_REQUEST");
+ } catch (err) {
+ if (util.isStream(body)) {
+ util.destroy(body.on("error", util.nop), err);
+ }
+ throw err;
+ }
+ this.method = method;
+ this.responseHeaders = responseHeaders || null;
+ this.opaque = opaque || null;
+ this.callback = callback;
+ this.res = null;
+ this.abort = null;
+ this.body = body;
+ this.trailers = {};
+ this.context = null;
+ this.onInfo = onInfo || null;
+ this.throwOnError = throwOnError;
+ this.highWaterMark = highWaterMark;
+ this.signal = signal;
+ this.reason = null;
+ this.removeAbortListener = null;
+ if (util.isStream(body)) {
+ body.on("error", (err) => {
+ this.onError(err);
+ });
+ }
+ if (this.signal) {
+ if (this.signal.aborted) {
+ this.reason = this.signal.reason ?? new RequestAbortedError();
+ } else {
+ this.removeAbortListener = util.addAbortListener(this.signal, () => {
+ this.reason = this.signal.reason ?? new RequestAbortedError();
+ if (this.res) {
+ util.destroy(this.res.on("error", util.nop), this.reason);
+ } else if (this.abort) {
+ this.abort(this.reason);
+ }
+ if (this.removeAbortListener) {
+ this.res?.off("close", this.removeAbortListener);
+ this.removeAbortListener();
+ this.removeAbortListener = null;
+ }
+ });
+ }
+ }
+ }
+ onConnect(abort, context) {
+ if (this.reason) {
+ abort(this.reason);
+ return;
+ }
+ assert(this.callback);
+ this.abort = abort;
+ this.context = context;
+ }
+ onHeaders(statusCode, rawHeaders, resume, statusMessage) {
+ const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this;
+ const headers = responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ if (statusCode < 200) {
+ if (this.onInfo) {
+ this.onInfo({ statusCode, headers });
+ }
+ return;
+ }
+ const parsedHeaders = responseHeaders === "raw" ? util.parseHeaders(rawHeaders) : headers;
+ const contentType = parsedHeaders["content-type"];
+ const contentLength = parsedHeaders["content-length"];
+ const res = new Readable({
+ resume,
+ abort,
+ contentType,
+ contentLength: this.method !== "HEAD" && contentLength ? Number(contentLength) : null,
+ highWaterMark
+ });
+ if (this.removeAbortListener) {
+ res.on("close", this.removeAbortListener);
+ }
+ this.callback = null;
+ this.res = res;
+ if (callback !== null) {
+ if (this.throwOnError && statusCode >= 400) {
+ this.runInAsyncScope(
+ getResolveErrorBodyCallback,
+ null,
+ { callback, body: res, contentType, statusCode, statusMessage, headers }
+ );
+ } else {
+ this.runInAsyncScope(callback, null, null, {
+ statusCode,
+ headers,
+ trailers: this.trailers,
+ opaque,
+ body: res,
+ context
+ });
+ }
+ }
+ }
+ onData(chunk) {
+ return this.res.push(chunk);
+ }
+ onComplete(trailers) {
+ util.parseHeaders(trailers, this.trailers);
+ this.res.push(null);
+ }
+ onError(err) {
+ const { res, callback, body, opaque } = this;
+ if (callback) {
+ this.callback = null;
+ queueMicrotask(() => {
+ this.runInAsyncScope(callback, null, err, { opaque });
+ });
+ }
+ if (res) {
+ this.res = null;
+ queueMicrotask(() => {
+ util.destroy(res, err);
+ });
+ }
+ if (body) {
+ this.body = null;
+ util.destroy(body, err);
+ }
+ if (this.removeAbortListener) {
+ res?.off("close", this.removeAbortListener);
+ this.removeAbortListener();
+ this.removeAbortListener = null;
+ }
+ }
+ };
+ function request(opts, callback) {
+ if (callback === void 0) {
+ return new Promise((resolve, reject) => {
+ request.call(this, opts, (err, data) => {
+ return err ? reject(err) : resolve(data);
+ });
+ });
+ }
+ try {
+ this.dispatch(opts, new RequestHandler(opts, callback));
+ } catch (err) {
+ if (typeof callback !== "function") {
+ throw err;
+ }
+ const opaque = opts?.opaque;
+ queueMicrotask(() => callback(err, { opaque }));
+ }
+ }
+ __name(request, "request");
+ module2.exports = request;
+ module2.exports.RequestHandler = RequestHandler;
+ }
+});
+
+// lib/api/abort-signal.js
+var require_abort_signal = __commonJS({
+ "lib/api/abort-signal.js"(exports2, module2) {
+ var { addAbortListener } = require_util();
+ var { RequestAbortedError } = require_errors();
+ var kListener = Symbol("kListener");
+ var kSignal = Symbol("kSignal");
+ function abort(self) {
+ if (self.abort) {
+ self.abort(self[kSignal]?.reason);
+ } else {
+ self.reason = self[kSignal]?.reason ?? new RequestAbortedError();
+ }
+ removeSignal(self);
+ }
+ __name(abort, "abort");
+ function addSignal(self, signal) {
+ self.reason = null;
+ self[kSignal] = null;
+ self[kListener] = null;
+ if (!signal) {
+ return;
+ }
+ if (signal.aborted) {
+ abort(self);
+ return;
+ }
+ self[kSignal] = signal;
+ self[kListener] = () => {
+ abort(self);
+ };
+ addAbortListener(self[kSignal], self[kListener]);
+ }
+ __name(addSignal, "addSignal");
+ function removeSignal(self) {
+ if (!self[kSignal]) {
+ return;
+ }
+ if ("removeEventListener" in self[kSignal]) {
+ self[kSignal].removeEventListener("abort", self[kListener]);
+ } else {
+ self[kSignal].removeListener("abort", self[kListener]);
+ }
+ self[kSignal] = null;
+ self[kListener] = null;
+ }
+ __name(removeSignal, "removeSignal");
+ module2.exports = {
+ addSignal,
+ removeSignal
+ };
+ }
+});
+
+// lib/api/api-stream.js
+var require_api_stream = __commonJS({
+ "lib/api/api-stream.js"(exports2, module2) {
+ "use strict";
+ var assert = require("node:assert");
+ var { finished, PassThrough } = require("node:stream");
+ var { InvalidArgumentError, InvalidReturnValueError } = require_errors();
+ var util = require_util();
+ var { getResolveErrorBodyCallback } = require_util5();
+ var { AsyncResource } = require("node:async_hooks");
+ var { addSignal, removeSignal } = require_abort_signal();
+ var StreamHandler = class extends AsyncResource {
+ static {
+ __name(this, "StreamHandler");
+ }
+ constructor(opts, factory, callback) {
+ if (!opts || typeof opts !== "object") {
+ throw new InvalidArgumentError("invalid opts");
+ }
+ const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts;
+ try {
+ if (typeof callback !== "function") {
+ throw new InvalidArgumentError("invalid callback");
+ }
+ if (typeof factory !== "function") {
+ throw new InvalidArgumentError("invalid factory");
+ }
+ if (signal && typeof signal.on !== "function" && typeof signal.addEventListener !== "function") {
+ throw new InvalidArgumentError("signal must be an EventEmitter or EventTarget");
+ }
+ if (method === "CONNECT") {
+ throw new InvalidArgumentError("invalid method");
+ }
+ if (onInfo && typeof onInfo !== "function") {
+ throw new InvalidArgumentError("invalid onInfo callback");
+ }
+ super("UNDICI_STREAM");
+ } catch (err) {
+ if (util.isStream(body)) {
+ util.destroy(body.on("error", util.nop), err);
+ }
+ throw err;
+ }
+ this.responseHeaders = responseHeaders || null;
+ this.opaque = opaque || null;
+ this.factory = factory;
+ this.callback = callback;
+ this.res = null;
+ this.abort = null;
+ this.context = null;
+ this.trailers = null;
+ this.body = body;
+ this.onInfo = onInfo || null;
+ this.throwOnError = throwOnError || false;
+ if (util.isStream(body)) {
+ body.on("error", (err) => {
+ this.onError(err);
+ });
+ }
+ addSignal(this, signal);
+ }
+ onConnect(abort, context) {
+ if (this.reason) {
+ abort(this.reason);
+ return;
+ }
+ assert(this.callback);
+ this.abort = abort;
+ this.context = context;
+ }
+ onHeaders(statusCode, rawHeaders, resume, statusMessage) {
+ const { factory, opaque, context, callback, responseHeaders } = this;
+ const headers = responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ if (statusCode < 200) {
+ if (this.onInfo) {
+ this.onInfo({ statusCode, headers });
+ }
+ return;
+ }
+ this.factory = null;
+ let res;
+ if (this.throwOnError && statusCode >= 400) {
+ const parsedHeaders = responseHeaders === "raw" ? util.parseHeaders(rawHeaders) : headers;
+ const contentType = parsedHeaders["content-type"];
+ res = new PassThrough();
+ this.callback = null;
+ this.runInAsyncScope(
+ getResolveErrorBodyCallback,
+ null,
+ { callback, body: res, contentType, statusCode, statusMessage, headers }
+ );
+ } else {
+ if (factory === null) {
+ return;
+ }
+ res = this.runInAsyncScope(factory, null, {
+ statusCode,
+ headers,
+ opaque,
+ context
+ });
+ if (!res || typeof res.write !== "function" || typeof res.end !== "function" || typeof res.on !== "function") {
+ throw new InvalidReturnValueError("expected Writable");
+ }
+ finished(res, { readable: false }, (err) => {
+ const { callback: callback2, res: res2, opaque: opaque2, trailers, abort } = this;
+ this.res = null;
+ if (err || !res2.readable) {
+ util.destroy(res2, err);
+ }
+ this.callback = null;
+ this.runInAsyncScope(callback2, null, err || null, { opaque: opaque2, trailers });
+ if (err) {
+ abort();
+ }
+ });
+ }
+ res.on("drain", resume);
+ this.res = res;
+ const needDrain = res.writableNeedDrain !== void 0 ? res.writableNeedDrain : res._writableState?.needDrain;
+ return needDrain !== true;
+ }
+ onData(chunk) {
+ const { res } = this;
+ return res ? res.write(chunk) : true;
+ }
+ onComplete(trailers) {
+ const { res } = this;
+ removeSignal(this);
+ if (!res) {
+ return;
+ }
+ this.trailers = util.parseHeaders(trailers);
+ res.end();
+ }
+ onError(err) {
+ const { res, callback, opaque, body } = this;
+ removeSignal(this);
+ this.factory = null;
+ if (res) {
+ this.res = null;
+ util.destroy(res, err);
+ } else if (callback) {
+ this.callback = null;
+ queueMicrotask(() => {
+ this.runInAsyncScope(callback, null, err, { opaque });
+ });
+ }
+ if (body) {
+ this.body = null;
+ util.destroy(body, err);
+ }
+ }
+ };
+ function stream(opts, factory, callback) {
+ if (callback === void 0) {
+ return new Promise((resolve, reject) => {
+ stream.call(this, opts, factory, (err, data) => {
+ return err ? reject(err) : resolve(data);
+ });
+ });
+ }
+ try {
+ this.dispatch(opts, new StreamHandler(opts, factory, callback));
+ } catch (err) {
+ if (typeof callback !== "function") {
+ throw err;
+ }
+ const opaque = opts?.opaque;
+ queueMicrotask(() => callback(err, { opaque }));
+ }
+ }
+ __name(stream, "stream");
+ module2.exports = stream;
+ }
+});
+
+// lib/api/api-pipeline.js
+var require_api_pipeline = __commonJS({
+ "lib/api/api-pipeline.js"(exports2, module2) {
+ "use strict";
+ var {
+ Readable,
+ Duplex,
+ PassThrough
+ } = require("node:stream");
+ var {
+ InvalidArgumentError,
+ InvalidReturnValueError,
+ RequestAbortedError
+ } = require_errors();
+ var util = require_util();
+ var { AsyncResource } = require("node:async_hooks");
+ var { addSignal, removeSignal } = require_abort_signal();
+ var assert = require("node:assert");
+ var kResume = Symbol("resume");
+ var PipelineRequest = class extends Readable {
+ static {
+ __name(this, "PipelineRequest");
+ }
+ constructor() {
+ super({ autoDestroy: true });
+ this[kResume] = null;
+ }
+ _read() {
+ const { [kResume]: resume } = this;
+ if (resume) {
+ this[kResume] = null;
+ resume();
+ }
+ }
+ _destroy(err, callback) {
+ this._read();
+ callback(err);
+ }
+ };
+ var PipelineResponse = class extends Readable {
+ static {
+ __name(this, "PipelineResponse");
+ }
+ constructor(resume) {
+ super({ autoDestroy: true });
+ this[kResume] = resume;
+ }
+ _read() {
+ this[kResume]();
+ }
+ _destroy(err, callback) {
+ if (!err && !this._readableState.endEmitted) {
+ err = new RequestAbortedError();
+ }
+ callback(err);
+ }
+ };
+ var PipelineHandler = class extends AsyncResource {
+ static {
+ __name(this, "PipelineHandler");
+ }
+ constructor(opts, handler) {
+ if (!opts || typeof opts !== "object") {
+ throw new InvalidArgumentError("invalid opts");
+ }
+ if (typeof handler !== "function") {
+ throw new InvalidArgumentError("invalid handler");
+ }
+ const { signal, method, opaque, onInfo, responseHeaders } = opts;
+ if (signal && typeof signal.on !== "function" && typeof signal.addEventListener !== "function") {
+ throw new InvalidArgumentError("signal must be an EventEmitter or EventTarget");
+ }
+ if (method === "CONNECT") {
+ throw new InvalidArgumentError("invalid method");
+ }
+ if (onInfo && typeof onInfo !== "function") {
+ throw new InvalidArgumentError("invalid onInfo callback");
+ }
+ super("UNDICI_PIPELINE");
+ this.opaque = opaque || null;
+ this.responseHeaders = responseHeaders || null;
+ this.handler = handler;
+ this.abort = null;
+ this.context = null;
+ this.onInfo = onInfo || null;
+ this.req = new PipelineRequest().on("error", util.nop);
+ this.ret = new Duplex({
+ readableObjectMode: opts.objectMode,
+ autoDestroy: true,
+ read: () => {
+ const { body } = this;
+ if (body?.resume) {
+ body.resume();
+ }
+ },
+ write: (chunk, encoding, callback) => {
+ const { req } = this;
+ if (req.push(chunk, encoding) || req._readableState.destroyed) {
+ callback();
+ } else {
+ req[kResume] = callback;
+ }
+ },
+ destroy: (err, callback) => {
+ const { body, req, res, ret, abort } = this;
+ if (!err && !ret._readableState.endEmitted) {
+ err = new RequestAbortedError();
+ }
+ if (abort && err) {
+ abort();
+ }
+ util.destroy(body, err);
+ util.destroy(req, err);
+ util.destroy(res, err);
+ removeSignal(this);
+ callback(err);
+ }
+ }).on("prefinish", () => {
+ const { req } = this;
+ req.push(null);
+ });
+ this.res = null;
+ addSignal(this, signal);
+ }
+ onConnect(abort, context) {
+ const { ret, res } = this;
+ if (this.reason) {
+ abort(this.reason);
+ return;
+ }
+ assert(!res, "pipeline cannot be retried");
+ assert(!ret.destroyed);
+ this.abort = abort;
+ this.context = context;
+ }
+ onHeaders(statusCode, rawHeaders, resume) {
+ const { opaque, handler, context } = this;
+ if (statusCode < 200) {
+ if (this.onInfo) {
+ const headers = this.responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ this.onInfo({ statusCode, headers });
+ }
+ return;
+ }
+ this.res = new PipelineResponse(resume);
+ let body;
+ try {
+ this.handler = null;
+ const headers = this.responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ body = this.runInAsyncScope(handler, null, {
+ statusCode,
+ headers,
+ opaque,
+ body: this.res,
+ context
+ });
+ } catch (err) {
+ this.res.on("error", util.nop);
+ throw err;
+ }
+ if (!body || typeof body.on !== "function") {
+ throw new InvalidReturnValueError("expected Readable");
+ }
+ body.on("data", (chunk) => {
+ const { ret, body: body2 } = this;
+ if (!ret.push(chunk) && body2.pause) {
+ body2.pause();
+ }
+ }).on("error", (err) => {
+ const { ret } = this;
+ util.destroy(ret, err);
+ }).on("end", () => {
+ const { ret } = this;
+ ret.push(null);
+ }).on("close", () => {
+ const { ret } = this;
+ if (!ret._readableState.ended) {
+ util.destroy(ret, new RequestAbortedError());
+ }
+ });
+ this.body = body;
+ }
+ onData(chunk) {
+ const { res } = this;
+ return res.push(chunk);
+ }
+ onComplete(trailers) {
+ const { res } = this;
+ res.push(null);
+ }
+ onError(err) {
+ const { ret } = this;
+ this.handler = null;
+ util.destroy(ret, err);
+ }
+ };
+ function pipeline(opts, handler) {
+ try {
+ const pipelineHandler = new PipelineHandler(opts, handler);
+ this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler);
+ return pipelineHandler.ret;
+ } catch (err) {
+ return new PassThrough().destroy(err);
+ }
+ }
+ __name(pipeline, "pipeline");
+ module2.exports = pipeline;
+ }
+});
+
+// lib/api/api-upgrade.js
+var require_api_upgrade = __commonJS({
+ "lib/api/api-upgrade.js"(exports2, module2) {
+ "use strict";
+ var { InvalidArgumentError, SocketError } = require_errors();
+ var { AsyncResource } = require("node:async_hooks");
+ var util = require_util();
+ var { addSignal, removeSignal } = require_abort_signal();
+ var assert = require("node:assert");
+ var UpgradeHandler = class extends AsyncResource {
+ static {
+ __name(this, "UpgradeHandler");
+ }
+ constructor(opts, callback) {
+ if (!opts || typeof opts !== "object") {
+ throw new InvalidArgumentError("invalid opts");
+ }
+ if (typeof callback !== "function") {
+ throw new InvalidArgumentError("invalid callback");
+ }
+ const { signal, opaque, responseHeaders } = opts;
+ if (signal && typeof signal.on !== "function" && typeof signal.addEventListener !== "function") {
+ throw new InvalidArgumentError("signal must be an EventEmitter or EventTarget");
+ }
+ super("UNDICI_UPGRADE");
+ this.responseHeaders = responseHeaders || null;
+ this.opaque = opaque || null;
+ this.callback = callback;
+ this.abort = null;
+ this.context = null;
+ addSignal(this, signal);
+ }
+ onConnect(abort, context) {
+ if (this.reason) {
+ abort(this.reason);
+ return;
+ }
+ assert(this.callback);
+ this.abort = abort;
+ this.context = null;
+ }
+ onHeaders() {
+ throw new SocketError("bad upgrade", null);
+ }
+ onUpgrade(statusCode, rawHeaders, socket) {
+ assert(statusCode === 101);
+ const { callback, opaque, context } = this;
+ removeSignal(this);
+ this.callback = null;
+ const headers = this.responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ this.runInAsyncScope(callback, null, null, {
+ headers,
+ socket,
+ opaque,
+ context
+ });
+ }
+ onError(err) {
+ const { callback, opaque } = this;
+ removeSignal(this);
+ if (callback) {
+ this.callback = null;
+ queueMicrotask(() => {
+ this.runInAsyncScope(callback, null, err, { opaque });
+ });
+ }
+ }
+ };
+ function upgrade(opts, callback) {
+ if (callback === void 0) {
+ return new Promise((resolve, reject) => {
+ upgrade.call(this, opts, (err, data) => {
+ return err ? reject(err) : resolve(data);
+ });
+ });
+ }
+ try {
+ const upgradeHandler = new UpgradeHandler(opts, callback);
+ this.dispatch({
+ ...opts,
+ method: opts.method || "GET",
+ upgrade: opts.protocol || "Websocket"
+ }, upgradeHandler);
+ } catch (err) {
+ if (typeof callback !== "function") {
+ throw err;
+ }
+ const opaque = opts?.opaque;
+ queueMicrotask(() => callback(err, { opaque }));
+ }
+ }
+ __name(upgrade, "upgrade");
+ module2.exports = upgrade;
+ }
+});
+
+// lib/api/api-connect.js
+var require_api_connect = __commonJS({
+ "lib/api/api-connect.js"(exports2, module2) {
+ "use strict";
+ var assert = require("node:assert");
+ var { AsyncResource } = require("node:async_hooks");
+ var { InvalidArgumentError, SocketError } = require_errors();
+ var util = require_util();
+ var { addSignal, removeSignal } = require_abort_signal();
+ var ConnectHandler = class extends AsyncResource {
+ static {
+ __name(this, "ConnectHandler");
+ }
+ constructor(opts, callback) {
+ if (!opts || typeof opts !== "object") {
+ throw new InvalidArgumentError("invalid opts");
+ }
+ if (typeof callback !== "function") {
+ throw new InvalidArgumentError("invalid callback");
+ }
+ const { signal, opaque, responseHeaders } = opts;
+ if (signal && typeof signal.on !== "function" && typeof signal.addEventListener !== "function") {
+ throw new InvalidArgumentError("signal must be an EventEmitter or EventTarget");
+ }
+ super("UNDICI_CONNECT");
+ this.opaque = opaque || null;
+ this.responseHeaders = responseHeaders || null;
+ this.callback = callback;
+ this.abort = null;
+ addSignal(this, signal);
+ }
+ onConnect(abort, context) {
+ if (this.reason) {
+ abort(this.reason);
+ return;
+ }
+ assert(this.callback);
+ this.abort = abort;
+ this.context = context;
+ }
+ onHeaders() {
+ throw new SocketError("bad connect", null);
+ }
+ onUpgrade(statusCode, rawHeaders, socket) {
+ const { callback, opaque, context } = this;
+ removeSignal(this);
+ this.callback = null;
+ let headers = rawHeaders;
+ if (headers != null) {
+ headers = this.responseHeaders === "raw" ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders);
+ }
+ this.runInAsyncScope(callback, null, null, {
+ statusCode,
+ headers,
+ socket,
+ opaque,
+ context
+ });
+ }
+ onError(err) {
+ const { callback, opaque } = this;
+ removeSignal(this);
+ if (callback) {
+ this.callback = null;
+ queueMicrotask(() => {
+ this.runInAsyncScope(callback, null, err, { opaque });
+ });
+ }
+ }
+ };
+ function connect(opts, callback) {
+ if (callback === void 0) {
+ return new Promise((resolve, reject) => {
+ connect.call(this, opts, (err, data) => {
+ return err ? reject(err) : resolve(data);
+ });
+ });
+ }
+ try {
+ const connectHandler = new ConnectHandler(opts, callback);
+ this.dispatch({ ...opts, method: "CONNECT" }, connectHandler);
+ } catch (err) {
+ if (typeof callback !== "function") {
+ throw err;
+ }
+ const opaque = opts?.opaque;
+ queueMicrotask(() => callback(err, { opaque }));
+ }
+ }
+ __name(connect, "connect");
+ module2.exports = connect;
+ }
+});
+
+// lib/api/index.js
+var require_api = __commonJS({
+ "lib/api/index.js"(exports2, module2) {
+ "use strict";
+ module2.exports.request = require_api_request();
+ module2.exports.stream = require_api_stream();
+ module2.exports.pipeline = require_api_pipeline();
+ module2.exports.upgrade = require_api_upgrade();
+ module2.exports.connect = require_api_connect();
+ }
+});
+
// index-fetch.js
var { getGlobalDispatcher, setGlobalDispatcher } = require_global2();
var EnvHttpProxyAgent = require_env_http_proxy_agent();
@@ -13523,6 +14913,9 @@ module.exports.ErrorEvent = ErrorEvent;
module.exports.MessageEvent = MessageEvent;
module.exports.createFastMessageEvent = createFastMessageEvent;
module.exports.EventSource = require_eventsource().EventSource;
+var api = require_api();
+var Dispatcher = require_dispatcher();
+Object.assign(Dispatcher.prototype, api);
module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent;
module.exports.getGlobalDispatcher = getGlobalDispatcher;
module.exports.setGlobalDispatcher = setGlobalDispatcher;
diff --git a/src/undici_version.h b/src/undici_version.h
index e47161c2a854..cf8bc91018d0 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 "6.21.2"
+#define UNDICI_VERSION "6.22.0"
#endif // SRC_UNDICI_VERSION_H_
From 7e0e86cb925d8b573cffee42f6001c9eea06bdfe Mon Sep 17 00:00:00 2001
From: npm CLI robot
Date: Tue, 7 Oct 2025 06:54:45 -0700
Subject: [PATCH 02/90] deps: upgrade npm to 10.9.4
PR-URL: https://github.com/nodejs/node/pull/60074
Reviewed-By: Colin Ihrig
Reviewed-By: Marco Ippolito
---
deps/npm/bin/npm.ps1 | 10 ++++++----
deps/npm/bin/npx.ps1 | 10 ++++++----
deps/npm/docs/content/commands/npm-ls.md | 2 +-
deps/npm/docs/content/commands/npm.md | 2 +-
deps/npm/docs/output/commands/npm-access.html | 4 ++--
deps/npm/docs/output/commands/npm-adduser.html | 4 ++--
deps/npm/docs/output/commands/npm-audit.html | 4 ++--
deps/npm/docs/output/commands/npm-bugs.html | 4 ++--
deps/npm/docs/output/commands/npm-cache.html | 4 ++--
deps/npm/docs/output/commands/npm-ci.html | 4 ++--
deps/npm/docs/output/commands/npm-completion.html | 4 ++--
deps/npm/docs/output/commands/npm-config.html | 4 ++--
deps/npm/docs/output/commands/npm-dedupe.html | 4 ++--
deps/npm/docs/output/commands/npm-deprecate.html | 4 ++--
deps/npm/docs/output/commands/npm-diff.html | 4 ++--
deps/npm/docs/output/commands/npm-dist-tag.html | 4 ++--
deps/npm/docs/output/commands/npm-docs.html | 4 ++--
deps/npm/docs/output/commands/npm-doctor.html | 4 ++--
deps/npm/docs/output/commands/npm-edit.html | 4 ++--
deps/npm/docs/output/commands/npm-exec.html | 4 ++--
deps/npm/docs/output/commands/npm-explain.html | 4 ++--
deps/npm/docs/output/commands/npm-explore.html | 4 ++--
deps/npm/docs/output/commands/npm-find-dupes.html | 4 ++--
deps/npm/docs/output/commands/npm-fund.html | 4 ++--
deps/npm/docs/output/commands/npm-help-search.html | 4 ++--
deps/npm/docs/output/commands/npm-help.html | 4 ++--
deps/npm/docs/output/commands/npm-hook.html | 4 ++--
deps/npm/docs/output/commands/npm-init.html | 4 ++--
deps/npm/docs/output/commands/npm-install-ci-test.html | 4 ++--
deps/npm/docs/output/commands/npm-install-test.html | 4 ++--
deps/npm/docs/output/commands/npm-install.html | 4 ++--
deps/npm/docs/output/commands/npm-link.html | 4 ++--
deps/npm/docs/output/commands/npm-login.html | 4 ++--
deps/npm/docs/output/commands/npm-logout.html | 4 ++--
deps/npm/docs/output/commands/npm-ls.html | 6 +++---
deps/npm/docs/output/commands/npm-org.html | 4 ++--
deps/npm/docs/output/commands/npm-outdated.html | 4 ++--
deps/npm/docs/output/commands/npm-owner.html | 4 ++--
deps/npm/docs/output/commands/npm-pack.html | 4 ++--
deps/npm/docs/output/commands/npm-ping.html | 4 ++--
deps/npm/docs/output/commands/npm-pkg.html | 4 ++--
deps/npm/docs/output/commands/npm-prefix.html | 4 ++--
deps/npm/docs/output/commands/npm-profile.html | 4 ++--
deps/npm/docs/output/commands/npm-prune.html | 4 ++--
deps/npm/docs/output/commands/npm-publish.html | 4 ++--
deps/npm/docs/output/commands/npm-query.html | 4 ++--
deps/npm/docs/output/commands/npm-rebuild.html | 4 ++--
deps/npm/docs/output/commands/npm-repo.html | 4 ++--
deps/npm/docs/output/commands/npm-restart.html | 4 ++--
deps/npm/docs/output/commands/npm-root.html | 4 ++--
deps/npm/docs/output/commands/npm-run-script.html | 4 ++--
deps/npm/docs/output/commands/npm-sbom.html | 4 ++--
deps/npm/docs/output/commands/npm-search.html | 4 ++--
deps/npm/docs/output/commands/npm-shrinkwrap.html | 4 ++--
deps/npm/docs/output/commands/npm-star.html | 4 ++--
deps/npm/docs/output/commands/npm-stars.html | 4 ++--
deps/npm/docs/output/commands/npm-start.html | 4 ++--
deps/npm/docs/output/commands/npm-stop.html | 4 ++--
deps/npm/docs/output/commands/npm-team.html | 4 ++--
deps/npm/docs/output/commands/npm-test.html | 4 ++--
deps/npm/docs/output/commands/npm-token.html | 4 ++--
deps/npm/docs/output/commands/npm-uninstall.html | 4 ++--
deps/npm/docs/output/commands/npm-unpublish.html | 4 ++--
deps/npm/docs/output/commands/npm-unstar.html | 4 ++--
deps/npm/docs/output/commands/npm-update.html | 4 ++--
deps/npm/docs/output/commands/npm-version.html | 4 ++--
deps/npm/docs/output/commands/npm-view.html | 4 ++--
deps/npm/docs/output/commands/npm-whoami.html | 4 ++--
deps/npm/docs/output/commands/npm.html | 6 +++---
deps/npm/docs/output/commands/npx.html | 4 ++--
deps/npm/docs/output/configuring-npm/folders.html | 4 ++--
deps/npm/docs/output/configuring-npm/install.html | 4 ++--
deps/npm/docs/output/configuring-npm/npm-global.html | 4 ++--
deps/npm/docs/output/configuring-npm/npm-json.html | 4 ++--
.../output/configuring-npm/npm-shrinkwrap-json.html | 4 ++--
deps/npm/docs/output/configuring-npm/npmrc.html | 4 ++--
deps/npm/docs/output/configuring-npm/package-json.html | 4 ++--
.../docs/output/configuring-npm/package-lock-json.html | 4 ++--
deps/npm/docs/output/using-npm/config.html | 4 ++--
.../docs/output/using-npm/dependency-selectors.html | 4 ++--
deps/npm/docs/output/using-npm/developers.html | 4 ++--
deps/npm/docs/output/using-npm/logging.html | 4 ++--
deps/npm/docs/output/using-npm/orgs.html | 4 ++--
deps/npm/docs/output/using-npm/package-spec.html | 4 ++--
deps/npm/docs/output/using-npm/registry.html | 4 ++--
deps/npm/docs/output/using-npm/removal.html | 4 ++--
deps/npm/docs/output/using-npm/scope.html | 4 ++--
deps/npm/docs/output/using-npm/scripts.html | 4 ++--
deps/npm/docs/output/using-npm/workspaces.html | 4 ++--
deps/npm/man/man1/npm-access.1 | 2 +-
deps/npm/man/man1/npm-adduser.1 | 2 +-
deps/npm/man/man1/npm-audit.1 | 2 +-
deps/npm/man/man1/npm-bugs.1 | 2 +-
deps/npm/man/man1/npm-cache.1 | 2 +-
deps/npm/man/man1/npm-ci.1 | 2 +-
deps/npm/man/man1/npm-completion.1 | 2 +-
deps/npm/man/man1/npm-config.1 | 2 +-
deps/npm/man/man1/npm-dedupe.1 | 2 +-
deps/npm/man/man1/npm-deprecate.1 | 2 +-
deps/npm/man/man1/npm-diff.1 | 2 +-
deps/npm/man/man1/npm-dist-tag.1 | 2 +-
deps/npm/man/man1/npm-docs.1 | 2 +-
deps/npm/man/man1/npm-doctor.1 | 2 +-
deps/npm/man/man1/npm-edit.1 | 2 +-
deps/npm/man/man1/npm-exec.1 | 2 +-
deps/npm/man/man1/npm-explain.1 | 2 +-
deps/npm/man/man1/npm-explore.1 | 2 +-
deps/npm/man/man1/npm-find-dupes.1 | 2 +-
deps/npm/man/man1/npm-fund.1 | 2 +-
deps/npm/man/man1/npm-help-search.1 | 2 +-
deps/npm/man/man1/npm-help.1 | 2 +-
deps/npm/man/man1/npm-hook.1 | 2 +-
deps/npm/man/man1/npm-init.1 | 2 +-
deps/npm/man/man1/npm-install-ci-test.1 | 2 +-
deps/npm/man/man1/npm-install-test.1 | 2 +-
deps/npm/man/man1/npm-install.1 | 2 +-
deps/npm/man/man1/npm-link.1 | 2 +-
deps/npm/man/man1/npm-login.1 | 2 +-
deps/npm/man/man1/npm-logout.1 | 2 +-
deps/npm/man/man1/npm-ls.1 | 4 ++--
deps/npm/man/man1/npm-org.1 | 2 +-
deps/npm/man/man1/npm-outdated.1 | 2 +-
deps/npm/man/man1/npm-owner.1 | 2 +-
deps/npm/man/man1/npm-pack.1 | 2 +-
deps/npm/man/man1/npm-ping.1 | 2 +-
deps/npm/man/man1/npm-pkg.1 | 2 +-
deps/npm/man/man1/npm-prefix.1 | 2 +-
deps/npm/man/man1/npm-profile.1 | 2 +-
deps/npm/man/man1/npm-prune.1 | 2 +-
deps/npm/man/man1/npm-publish.1 | 2 +-
deps/npm/man/man1/npm-query.1 | 2 +-
deps/npm/man/man1/npm-rebuild.1 | 2 +-
deps/npm/man/man1/npm-repo.1 | 2 +-
deps/npm/man/man1/npm-restart.1 | 2 +-
deps/npm/man/man1/npm-root.1 | 2 +-
deps/npm/man/man1/npm-run-script.1 | 2 +-
deps/npm/man/man1/npm-sbom.1 | 2 +-
deps/npm/man/man1/npm-search.1 | 2 +-
deps/npm/man/man1/npm-shrinkwrap.1 | 2 +-
deps/npm/man/man1/npm-star.1 | 2 +-
deps/npm/man/man1/npm-stars.1 | 2 +-
deps/npm/man/man1/npm-start.1 | 2 +-
deps/npm/man/man1/npm-stop.1 | 2 +-
deps/npm/man/man1/npm-team.1 | 2 +-
deps/npm/man/man1/npm-test.1 | 2 +-
deps/npm/man/man1/npm-token.1 | 2 +-
deps/npm/man/man1/npm-uninstall.1 | 2 +-
deps/npm/man/man1/npm-unpublish.1 | 2 +-
deps/npm/man/man1/npm-unstar.1 | 2 +-
deps/npm/man/man1/npm-update.1 | 2 +-
deps/npm/man/man1/npm-version.1 | 2 +-
deps/npm/man/man1/npm-view.1 | 2 +-
deps/npm/man/man1/npm-whoami.1 | 2 +-
deps/npm/man/man1/npm.1 | 4 ++--
deps/npm/man/man1/npx.1 | 2 +-
deps/npm/man/man5/folders.5 | 2 +-
deps/npm/man/man5/install.5 | 2 +-
deps/npm/man/man5/npm-global.5 | 2 +-
deps/npm/man/man5/npm-json.5 | 2 +-
deps/npm/man/man5/npm-shrinkwrap-json.5 | 2 +-
deps/npm/man/man5/npmrc.5 | 2 +-
deps/npm/man/man5/package-json.5 | 2 +-
deps/npm/man/man5/package-lock-json.5 | 2 +-
deps/npm/man/man7/config.7 | 2 +-
deps/npm/man/man7/dependency-selectors.7 | 2 +-
deps/npm/man/man7/developers.7 | 2 +-
deps/npm/man/man7/logging.7 | 2 +-
deps/npm/man/man7/orgs.7 | 2 +-
deps/npm/man/man7/package-spec.7 | 2 +-
deps/npm/man/man7/registry.7 | 2 +-
deps/npm/man/man7/removal.7 | 2 +-
deps/npm/man/man7/scope.7 | 2 +-
deps/npm/man/man7/scripts.7 | 2 +-
deps/npm/man/man7/workspaces.7 | 2 +-
deps/npm/package.json | 2 +-
175 files changed, 274 insertions(+), 270 deletions(-)
diff --git a/deps/npm/bin/npm.ps1 b/deps/npm/bin/npm.ps1
index 5993adaf5566..efed03fe5655 100644
--- a/deps/npm/bin/npm.ps1
+++ b/deps/npm/bin/npm.ps1
@@ -1,5 +1,7 @@
#!/usr/bin/env pwsh
+Set-StrictMode -Version 'Latest'
+
$NODE_EXE="$PSScriptRoot/node.exe"
if (-not (Test-Path $NODE_EXE)) {
$NODE_EXE="$PSScriptRoot/node"
@@ -27,7 +29,7 @@ if ($MyInvocation.ExpectingInput) { # takes pipeline input
} elseif (-not $MyInvocation.Line) { # used "-File" argument
& $NODE_EXE $NPM_CLI_JS $args
} else { # used "-Command" argument
- if ($MyInvocation.Statement) {
+ if (($MyInvocation | Get-Member -Name 'Statement') -and $MyInvocation.Statement) {
$NPM_ORIGINAL_COMMAND = $MyInvocation.Statement
} else {
$NPM_ORIGINAL_COMMAND = (
@@ -38,9 +40,9 @@ if ($MyInvocation.ExpectingInput) { # takes pipeline input
$NODE_EXE = $NODE_EXE.Replace("``", "````")
$NPM_CLI_JS = $NPM_CLI_JS.Replace("``", "````")
- $NPM_NO_REDIRECTS_COMMAND = [Management.Automation.Language.Parser]::ParseInput($NPM_ORIGINAL_COMMAND, [ref] $null, [ref] $null).
- EndBlock.Statements.PipelineElements.CommandElements.Extent.Text -join ' '
- $NPM_ARGS = $NPM_NO_REDIRECTS_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim()
+ $NPM_COMMAND_ARRAY = [Management.Automation.Language.Parser]::ParseInput($NPM_ORIGINAL_COMMAND, [ref] $null, [ref] $null).
+ EndBlock.Statements.PipelineElements.CommandElements.Extent.Text
+ $NPM_ARGS = ($NPM_COMMAND_ARRAY | Select-Object -Skip 1) -join ' '
Invoke-Expression "& `"$NODE_EXE`" `"$NPM_CLI_JS`" $NPM_ARGS"
}
diff --git a/deps/npm/bin/npx.ps1 b/deps/npm/bin/npx.ps1
index cc1aa047bdc2..3fe7b5435763 100644
--- a/deps/npm/bin/npx.ps1
+++ b/deps/npm/bin/npx.ps1
@@ -1,5 +1,7 @@
#!/usr/bin/env pwsh
+Set-StrictMode -Version 'Latest'
+
$NODE_EXE="$PSScriptRoot/node.exe"
if (-not (Test-Path $NODE_EXE)) {
$NODE_EXE="$PSScriptRoot/node"
@@ -27,7 +29,7 @@ if ($MyInvocation.ExpectingInput) { # takes pipeline input
} elseif (-not $MyInvocation.Line) { # used "-File" argument
& $NODE_EXE $NPX_CLI_JS $args
} else { # used "-Command" argument
- if ($MyInvocation.Statement) {
+ if (($MyInvocation | Get-Member -Name 'Statement') -and $MyInvocation.Statement) {
$NPX_ORIGINAL_COMMAND = $MyInvocation.Statement
} else {
$NPX_ORIGINAL_COMMAND = (
@@ -38,9 +40,9 @@ if ($MyInvocation.ExpectingInput) { # takes pipeline input
$NODE_EXE = $NODE_EXE.Replace("``", "````")
$NPX_CLI_JS = $NPX_CLI_JS.Replace("``", "````")
- $NPX_NO_REDIRECTS_COMMAND = [Management.Automation.Language.Parser]::ParseInput($NPX_ORIGINAL_COMMAND, [ref] $null, [ref] $null).
- EndBlock.Statements.PipelineElements.CommandElements.Extent.Text -join ' '
- $NPX_ARGS = $NPX_NO_REDIRECTS_COMMAND.Substring($MyInvocation.InvocationName.Length).Trim()
+ $NPX_COMMAND_ARRAY = [Management.Automation.Language.Parser]::ParseInput($NPX_ORIGINAL_COMMAND, [ref] $null, [ref] $null).
+ EndBlock.Statements.PipelineElements.CommandElements.Extent.Text
+ $NPX_ARGS = ($NPX_COMMAND_ARRAY | Select-Object -Skip 1) -join ' '
Invoke-Expression "& `"$NODE_EXE`" `"$NPX_CLI_JS`" $NPX_ARGS"
}
diff --git a/deps/npm/docs/content/commands/npm-ls.md b/deps/npm/docs/content/commands/npm-ls.md
index 76046cf66c4f..5225e7f708ae 100644
--- a/deps/npm/docs/content/commands/npm-ls.md
+++ b/deps/npm/docs/content/commands/npm-ls.md
@@ -27,7 +27,7 @@ packages will *also* show the paths to the specified packages. For
example, running `npm ls promzard` in npm's source tree will show:
```bash
-npm@10.9.3 /path/to/npm
+npm@10.9.4 /path/to/npm
└─┬ init-package-json@0.0.4
└── promzard@0.1.5
```
diff --git a/deps/npm/docs/content/commands/npm.md b/deps/npm/docs/content/commands/npm.md
index da1cfef4f9f8..948a3590a547 100644
--- a/deps/npm/docs/content/commands/npm.md
+++ b/deps/npm/docs/content/commands/npm.md
@@ -14,7 +14,7 @@ Note: This command is unaware of workspaces.
### Version
-10.9.3
+10.9.4
### Description
diff --git a/deps/npm/docs/output/commands/npm-access.html b/deps/npm/docs/output/commands/npm-access.html
index 70b6eadb4ce1..edc5be8426ca 100644
--- a/deps/npm/docs/output/commands/npm-access.html
+++ b/deps/npm/docs/output/commands/npm-access.html
@@ -141,9 +141,9 @@
-
+
npm-access
- @10.9.3
+ @10.9.4
Set access level on published packages
diff --git a/deps/npm/docs/output/commands/npm-adduser.html b/deps/npm/docs/output/commands/npm-adduser.html
index 2f39972874ce..0d664b740107 100644
--- a/deps/npm/docs/output/commands/npm-adduser.html
+++ b/deps/npm/docs/output/commands/npm-adduser.html
@@ -141,9 +141,9 @@
-
+
npm-adduser
- @10.9.3
+ @10.9.4
Add a registry user account
diff --git a/deps/npm/docs/output/commands/npm-audit.html b/deps/npm/docs/output/commands/npm-audit.html
index 37ba1dbafd4a..00df654800ce 100644
--- a/deps/npm/docs/output/commands/npm-audit.html
+++ b/deps/npm/docs/output/commands/npm-audit.html
@@ -141,9 +141,9 @@
-
+
npm-audit
- @10.9.3
+ @10.9.4
Run a security audit
diff --git a/deps/npm/docs/output/commands/npm-bugs.html b/deps/npm/docs/output/commands/npm-bugs.html
index fd8333a8c0d8..48b15e7ba99d 100644
--- a/deps/npm/docs/output/commands/npm-bugs.html
+++ b/deps/npm/docs/output/commands/npm-bugs.html
@@ -141,9 +141,9 @@
-
+
npm-bugs
- @10.9.3
+ @10.9.4
Report bugs for a package in a web browser
diff --git a/deps/npm/docs/output/commands/npm-cache.html b/deps/npm/docs/output/commands/npm-cache.html
index 9c2ab2b626bb..5f221564f4bd 100644
--- a/deps/npm/docs/output/commands/npm-cache.html
+++ b/deps/npm/docs/output/commands/npm-cache.html
@@ -141,9 +141,9 @@
-
+
npm-cache
- @10.9.3
+ @10.9.4
Manipulates packages cache
diff --git a/deps/npm/docs/output/commands/npm-ci.html b/deps/npm/docs/output/commands/npm-ci.html
index 4bb718d95c4d..f698a75aaaa1 100644
--- a/deps/npm/docs/output/commands/npm-ci.html
+++ b/deps/npm/docs/output/commands/npm-ci.html
@@ -141,9 +141,9 @@
-
+
npm-ci
- @10.9.3
+ @10.9.4
Clean install a project
diff --git a/deps/npm/docs/output/commands/npm-completion.html b/deps/npm/docs/output/commands/npm-completion.html
index 0dfba0ac106c..9c253e1656f1 100644
--- a/deps/npm/docs/output/commands/npm-completion.html
+++ b/deps/npm/docs/output/commands/npm-completion.html
@@ -141,9 +141,9 @@
-
+
npm-completion
- @10.9.3
+ @10.9.4
Tab Completion for npm
diff --git a/deps/npm/docs/output/commands/npm-config.html b/deps/npm/docs/output/commands/npm-config.html
index e949f98b2673..970e64cd3e44 100644
--- a/deps/npm/docs/output/commands/npm-config.html
+++ b/deps/npm/docs/output/commands/npm-config.html
@@ -141,9 +141,9 @@
-
+
npm-config
- @10.9.3
+ @10.9.4
Manage the npm configuration files
diff --git a/deps/npm/docs/output/commands/npm-dedupe.html b/deps/npm/docs/output/commands/npm-dedupe.html
index 4013eb9e6668..3633b9cc8a91 100644
--- a/deps/npm/docs/output/commands/npm-dedupe.html
+++ b/deps/npm/docs/output/commands/npm-dedupe.html
@@ -141,9 +141,9 @@
-
+
npm-dedupe
- @10.9.3
+ @10.9.4
Reduce duplication in the package tree
diff --git a/deps/npm/docs/output/commands/npm-deprecate.html b/deps/npm/docs/output/commands/npm-deprecate.html
index bcfd06805554..ba26bc133003 100644
--- a/deps/npm/docs/output/commands/npm-deprecate.html
+++ b/deps/npm/docs/output/commands/npm-deprecate.html
@@ -141,9 +141,9 @@
-
+
npm-deprecate
- @10.9.3
+ @10.9.4
Deprecate a version of a package
diff --git a/deps/npm/docs/output/commands/npm-diff.html b/deps/npm/docs/output/commands/npm-diff.html
index 167390fcd702..b0418125a3bd 100644
--- a/deps/npm/docs/output/commands/npm-diff.html
+++ b/deps/npm/docs/output/commands/npm-diff.html
@@ -141,9 +141,9 @@
-
+
npm-diff
- @10.9.3
+ @10.9.4
The registry diff command
diff --git a/deps/npm/docs/output/commands/npm-dist-tag.html b/deps/npm/docs/output/commands/npm-dist-tag.html
index 484575ce7d36..e2f6a11aad03 100644
--- a/deps/npm/docs/output/commands/npm-dist-tag.html
+++ b/deps/npm/docs/output/commands/npm-dist-tag.html
@@ -141,9 +141,9 @@
-
+
npm-dist-tag
- @10.9.3
+ @10.9.4
Modify package distribution tags
diff --git a/deps/npm/docs/output/commands/npm-docs.html b/deps/npm/docs/output/commands/npm-docs.html
index f1ec5477a133..9760fb5e5913 100644
--- a/deps/npm/docs/output/commands/npm-docs.html
+++ b/deps/npm/docs/output/commands/npm-docs.html
@@ -141,9 +141,9 @@
-
+
npm-docs
- @10.9.3
+ @10.9.4
Open documentation for a package in a web browser
diff --git a/deps/npm/docs/output/commands/npm-doctor.html b/deps/npm/docs/output/commands/npm-doctor.html
index 68e1127b50df..ee6a127abc14 100644
--- a/deps/npm/docs/output/commands/npm-doctor.html
+++ b/deps/npm/docs/output/commands/npm-doctor.html
@@ -141,9 +141,9 @@
-
+
npm-doctor
- @10.9.3
+ @10.9.4
Check the health of your npm environment
diff --git a/deps/npm/docs/output/commands/npm-edit.html b/deps/npm/docs/output/commands/npm-edit.html
index bbb83bd907e6..9cde69dc3a27 100644
--- a/deps/npm/docs/output/commands/npm-edit.html
+++ b/deps/npm/docs/output/commands/npm-edit.html
@@ -141,9 +141,9 @@
-
+
npm-edit
- @10.9.3
+ @10.9.4
Edit an installed package
diff --git a/deps/npm/docs/output/commands/npm-exec.html b/deps/npm/docs/output/commands/npm-exec.html
index 1397bc5048ab..e1ed227f4e36 100644
--- a/deps/npm/docs/output/commands/npm-exec.html
+++ b/deps/npm/docs/output/commands/npm-exec.html
@@ -141,9 +141,9 @@
-
+
npm-exec
- @10.9.3
+ @10.9.4
Run a command from a local or remote npm package
diff --git a/deps/npm/docs/output/commands/npm-explain.html b/deps/npm/docs/output/commands/npm-explain.html
index c0d12ef1a3e3..040928716ff8 100644
--- a/deps/npm/docs/output/commands/npm-explain.html
+++ b/deps/npm/docs/output/commands/npm-explain.html
@@ -141,9 +141,9 @@
-
+
npm-explain
- @10.9.3
+ @10.9.4
Explain installed packages
diff --git a/deps/npm/docs/output/commands/npm-explore.html b/deps/npm/docs/output/commands/npm-explore.html
index 31778889549a..956fcdea77cf 100644
--- a/deps/npm/docs/output/commands/npm-explore.html
+++ b/deps/npm/docs/output/commands/npm-explore.html
@@ -141,9 +141,9 @@
-
+
npm-explore
- @10.9.3
+ @10.9.4
Browse an installed package
diff --git a/deps/npm/docs/output/commands/npm-find-dupes.html b/deps/npm/docs/output/commands/npm-find-dupes.html
index fb41969e5629..8c64c331e889 100644
--- a/deps/npm/docs/output/commands/npm-find-dupes.html
+++ b/deps/npm/docs/output/commands/npm-find-dupes.html
@@ -141,9 +141,9 @@
-
+
npm-find-dupes
- @10.9.3
+ @10.9.4
Find duplication in the package tree
diff --git a/deps/npm/docs/output/commands/npm-fund.html b/deps/npm/docs/output/commands/npm-fund.html
index 2060175c5774..500cc335794c 100644
--- a/deps/npm/docs/output/commands/npm-fund.html
+++ b/deps/npm/docs/output/commands/npm-fund.html
@@ -141,9 +141,9 @@
-
+
npm-fund
- @10.9.3
+ @10.9.4
Retrieve funding information
diff --git a/deps/npm/docs/output/commands/npm-help-search.html b/deps/npm/docs/output/commands/npm-help-search.html
index 379ea248e49f..1aebfc354bfd 100644
--- a/deps/npm/docs/output/commands/npm-help-search.html
+++ b/deps/npm/docs/output/commands/npm-help-search.html
@@ -141,9 +141,9 @@
-
+
npm-help-search
- @10.9.3
+ @10.9.4
Search npm help documentation
diff --git a/deps/npm/docs/output/commands/npm-help.html b/deps/npm/docs/output/commands/npm-help.html
index b7bb989a6716..795230b845fb 100644
--- a/deps/npm/docs/output/commands/npm-help.html
+++ b/deps/npm/docs/output/commands/npm-help.html
@@ -141,9 +141,9 @@
-
+
npm-help
- @10.9.3
+ @10.9.4
Get help on npm
diff --git a/deps/npm/docs/output/commands/npm-hook.html b/deps/npm/docs/output/commands/npm-hook.html
index 9e55028bc6ba..42101dc55b1d 100644
--- a/deps/npm/docs/output/commands/npm-hook.html
+++ b/deps/npm/docs/output/commands/npm-hook.html
@@ -141,9 +141,9 @@
-
+
npm-hook
- @10.9.3
+ @10.9.4
Manage registry hooks
diff --git a/deps/npm/docs/output/commands/npm-init.html b/deps/npm/docs/output/commands/npm-init.html
index 125a6ab5b7c2..5e027004eb75 100644
--- a/deps/npm/docs/output/commands/npm-init.html
+++ b/deps/npm/docs/output/commands/npm-init.html
@@ -141,9 +141,9 @@
-
+
npm-init
- @10.9.3
+ @10.9.4
Create a package.json file
diff --git a/deps/npm/docs/output/commands/npm-install-ci-test.html b/deps/npm/docs/output/commands/npm-install-ci-test.html
index c7f5aeec64c2..be4a272d48c5 100644
--- a/deps/npm/docs/output/commands/npm-install-ci-test.html
+++ b/deps/npm/docs/output/commands/npm-install-ci-test.html
@@ -141,9 +141,9 @@
-
+
npm-install-ci-test
- @10.9.3
+ @10.9.4
Install a project with a clean slate and run tests
diff --git a/deps/npm/docs/output/commands/npm-install-test.html b/deps/npm/docs/output/commands/npm-install-test.html
index d5679304f8ce..6a68c794ad71 100644
--- a/deps/npm/docs/output/commands/npm-install-test.html
+++ b/deps/npm/docs/output/commands/npm-install-test.html
@@ -141,9 +141,9 @@
-
+
npm-install-test
- @10.9.3
+ @10.9.4
Install package(s) and run tests
diff --git a/deps/npm/docs/output/commands/npm-install.html b/deps/npm/docs/output/commands/npm-install.html
index 586d01806dc8..86008c0430e8 100644
--- a/deps/npm/docs/output/commands/npm-install.html
+++ b/deps/npm/docs/output/commands/npm-install.html
@@ -141,9 +141,9 @@
-
+
npm-install
- @10.9.3
+ @10.9.4
Install a package
diff --git a/deps/npm/docs/output/commands/npm-link.html b/deps/npm/docs/output/commands/npm-link.html
index dc48efc55ade..1e13ce0edb42 100644
--- a/deps/npm/docs/output/commands/npm-link.html
+++ b/deps/npm/docs/output/commands/npm-link.html
@@ -141,9 +141,9 @@
-
+
npm-link
- @10.9.3
+ @10.9.4
Symlink a package folder
diff --git a/deps/npm/docs/output/commands/npm-login.html b/deps/npm/docs/output/commands/npm-login.html
index e0f65c54f1ae..d29cfa829047 100644
--- a/deps/npm/docs/output/commands/npm-login.html
+++ b/deps/npm/docs/output/commands/npm-login.html
@@ -141,9 +141,9 @@
-
+
npm-login
- @10.9.3
+ @10.9.4
Login to a registry user account
diff --git a/deps/npm/docs/output/commands/npm-logout.html b/deps/npm/docs/output/commands/npm-logout.html
index 762db67a14a9..4569b2f5fea2 100644
--- a/deps/npm/docs/output/commands/npm-logout.html
+++ b/deps/npm/docs/output/commands/npm-logout.html
@@ -141,9 +141,9 @@
-
+
npm-logout
- @10.9.3
+ @10.9.4
Log out of the registry
diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html
index e78f94f486fa..a2b0ceccf1c2 100644
--- a/deps/npm/docs/output/commands/npm-ls.html
+++ b/deps/npm/docs/output/commands/npm-ls.html
@@ -141,9 +141,9 @@
-
+
npm-ls
- @10.9.3
+ @10.9.4
List installed packages
@@ -168,7 +168,7 @@ Description
the results to only the paths to the packages named. Note that nested
packages will also show the paths to the specified packages. For
example, running npm ls promzard in npm's source tree will show:
-npm@10.9.3 /path/to/npm
+npm@10.9.4 /path/to/npm
└─┬ init-package-json@0.0.4
└── promzard@0.1.5
diff --git a/deps/npm/docs/output/commands/npm-org.html b/deps/npm/docs/output/commands/npm-org.html
index a036d164bde6..4e2941a69a51 100644
--- a/deps/npm/docs/output/commands/npm-org.html
+++ b/deps/npm/docs/output/commands/npm-org.html
@@ -141,9 +141,9 @@
-
+
npm-org
- @10.9.3
+ @10.9.4
Manage orgs
diff --git a/deps/npm/docs/output/commands/npm-outdated.html b/deps/npm/docs/output/commands/npm-outdated.html
index 04f8e5d7e008..a2ce5becdbca 100644
--- a/deps/npm/docs/output/commands/npm-outdated.html
+++ b/deps/npm/docs/output/commands/npm-outdated.html
@@ -141,9 +141,9 @@
-
+
npm-outdated
- @10.9.3
+ @10.9.4
Check for outdated packages
diff --git a/deps/npm/docs/output/commands/npm-owner.html b/deps/npm/docs/output/commands/npm-owner.html
index 16f7d512d1ca..8ba1ca1e3d47 100644
--- a/deps/npm/docs/output/commands/npm-owner.html
+++ b/deps/npm/docs/output/commands/npm-owner.html
@@ -141,9 +141,9 @@
-
+
npm-owner
- @10.9.3
+ @10.9.4
Manage package owners
diff --git a/deps/npm/docs/output/commands/npm-pack.html b/deps/npm/docs/output/commands/npm-pack.html
index a2d935991505..29a1e4446691 100644
--- a/deps/npm/docs/output/commands/npm-pack.html
+++ b/deps/npm/docs/output/commands/npm-pack.html
@@ -141,9 +141,9 @@
-
+
npm-pack
- @10.9.3
+ @10.9.4
Create a tarball from a package
diff --git a/deps/npm/docs/output/commands/npm-ping.html b/deps/npm/docs/output/commands/npm-ping.html
index 16a587591db0..268338afb729 100644
--- a/deps/npm/docs/output/commands/npm-ping.html
+++ b/deps/npm/docs/output/commands/npm-ping.html
@@ -141,9 +141,9 @@
-
+
npm-ping
- @10.9.3
+ @10.9.4
Ping npm registry
diff --git a/deps/npm/docs/output/commands/npm-pkg.html b/deps/npm/docs/output/commands/npm-pkg.html
index ea15ff20c96b..8c28a651244c 100644
--- a/deps/npm/docs/output/commands/npm-pkg.html
+++ b/deps/npm/docs/output/commands/npm-pkg.html
@@ -141,9 +141,9 @@
-
+
npm-pkg
- @10.9.3
+ @10.9.4
Manages your package.json
diff --git a/deps/npm/docs/output/commands/npm-prefix.html b/deps/npm/docs/output/commands/npm-prefix.html
index 040fead6ce89..b4f99129dd9d 100644
--- a/deps/npm/docs/output/commands/npm-prefix.html
+++ b/deps/npm/docs/output/commands/npm-prefix.html
@@ -141,9 +141,9 @@
-
+
npm-prefix
- @10.9.3
+ @10.9.4
Display prefix
diff --git a/deps/npm/docs/output/commands/npm-profile.html b/deps/npm/docs/output/commands/npm-profile.html
index cae32b74743e..ef77ee038da0 100644
--- a/deps/npm/docs/output/commands/npm-profile.html
+++ b/deps/npm/docs/output/commands/npm-profile.html
@@ -141,9 +141,9 @@
-
+
npm-profile
- @10.9.3
+ @10.9.4
Change settings on your registry profile
diff --git a/deps/npm/docs/output/commands/npm-prune.html b/deps/npm/docs/output/commands/npm-prune.html
index 6c8a2bedc2f5..a2e829cf9388 100644
--- a/deps/npm/docs/output/commands/npm-prune.html
+++ b/deps/npm/docs/output/commands/npm-prune.html
@@ -141,9 +141,9 @@
-
+
npm-prune
- @10.9.3
+ @10.9.4
Remove extraneous packages
diff --git a/deps/npm/docs/output/commands/npm-publish.html b/deps/npm/docs/output/commands/npm-publish.html
index e8c44e924925..c7b82c49410a 100644
--- a/deps/npm/docs/output/commands/npm-publish.html
+++ b/deps/npm/docs/output/commands/npm-publish.html
@@ -141,9 +141,9 @@
-
+
npm-publish
- @10.9.3
+ @10.9.4
Publish a package
diff --git a/deps/npm/docs/output/commands/npm-query.html b/deps/npm/docs/output/commands/npm-query.html
index 71c680729859..4f33f5523dd4 100644
--- a/deps/npm/docs/output/commands/npm-query.html
+++ b/deps/npm/docs/output/commands/npm-query.html
@@ -141,9 +141,9 @@
-
+
npm-query
- @10.9.3
+ @10.9.4
Dependency selector query
diff --git a/deps/npm/docs/output/commands/npm-rebuild.html b/deps/npm/docs/output/commands/npm-rebuild.html
index 3936a927c472..08dd01f8fe3f 100644
--- a/deps/npm/docs/output/commands/npm-rebuild.html
+++ b/deps/npm/docs/output/commands/npm-rebuild.html
@@ -141,9 +141,9 @@
-
+
npm-rebuild
- @10.9.3
+ @10.9.4
Rebuild a package
diff --git a/deps/npm/docs/output/commands/npm-repo.html b/deps/npm/docs/output/commands/npm-repo.html
index 47a39d7fc273..6bcb661c79bf 100644
--- a/deps/npm/docs/output/commands/npm-repo.html
+++ b/deps/npm/docs/output/commands/npm-repo.html
@@ -141,9 +141,9 @@
-
+
npm-repo
- @10.9.3
+ @10.9.4
Open package repository page in the browser
diff --git a/deps/npm/docs/output/commands/npm-restart.html b/deps/npm/docs/output/commands/npm-restart.html
index a7db0633e398..ed906693ad6f 100644
--- a/deps/npm/docs/output/commands/npm-restart.html
+++ b/deps/npm/docs/output/commands/npm-restart.html
@@ -141,9 +141,9 @@
-
+
npm-restart
- @10.9.3
+ @10.9.4
Restart a package
diff --git a/deps/npm/docs/output/commands/npm-root.html b/deps/npm/docs/output/commands/npm-root.html
index 973afad93ecc..c11ea8c08784 100644
--- a/deps/npm/docs/output/commands/npm-root.html
+++ b/deps/npm/docs/output/commands/npm-root.html
@@ -141,9 +141,9 @@
-
+
npm-root
- @10.9.3
+ @10.9.4
Display npm root
diff --git a/deps/npm/docs/output/commands/npm-run-script.html b/deps/npm/docs/output/commands/npm-run-script.html
index 398c15409647..5753f2013191 100644
--- a/deps/npm/docs/output/commands/npm-run-script.html
+++ b/deps/npm/docs/output/commands/npm-run-script.html
@@ -141,9 +141,9 @@
-
+
npm-run-script
- @10.9.3
+ @10.9.4
Run arbitrary package scripts
diff --git a/deps/npm/docs/output/commands/npm-sbom.html b/deps/npm/docs/output/commands/npm-sbom.html
index 1a5127e028f8..101467ddeefe 100644
--- a/deps/npm/docs/output/commands/npm-sbom.html
+++ b/deps/npm/docs/output/commands/npm-sbom.html
@@ -141,9 +141,9 @@
-
+
npm-sbom
- @10.9.3
+ @10.9.4
Generate a Software Bill of Materials (SBOM)
diff --git a/deps/npm/docs/output/commands/npm-search.html b/deps/npm/docs/output/commands/npm-search.html
index dd7ee35f3c8c..5a3202a4ff01 100644
--- a/deps/npm/docs/output/commands/npm-search.html
+++ b/deps/npm/docs/output/commands/npm-search.html
@@ -141,9 +141,9 @@
-
+
npm-search
- @10.9.3
+ @10.9.4
Search for packages
diff --git a/deps/npm/docs/output/commands/npm-shrinkwrap.html b/deps/npm/docs/output/commands/npm-shrinkwrap.html
index f2e9a00fc602..2be387a1e736 100644
--- a/deps/npm/docs/output/commands/npm-shrinkwrap.html
+++ b/deps/npm/docs/output/commands/npm-shrinkwrap.html
@@ -141,9 +141,9 @@
-
+
npm-shrinkwrap
- @10.9.3
+ @10.9.4
Lock down dependency versions for publication
diff --git a/deps/npm/docs/output/commands/npm-star.html b/deps/npm/docs/output/commands/npm-star.html
index e68322f6acd4..c8abb8a2d836 100644
--- a/deps/npm/docs/output/commands/npm-star.html
+++ b/deps/npm/docs/output/commands/npm-star.html
@@ -141,9 +141,9 @@
-
+
npm-star
- @10.9.3
+ @10.9.4
Mark your favorite packages
diff --git a/deps/npm/docs/output/commands/npm-stars.html b/deps/npm/docs/output/commands/npm-stars.html
index c465029671ed..8c670722f805 100644
--- a/deps/npm/docs/output/commands/npm-stars.html
+++ b/deps/npm/docs/output/commands/npm-stars.html
@@ -141,9 +141,9 @@
-
+
npm-stars
- @10.9.3
+ @10.9.4
View packages marked as favorites
diff --git a/deps/npm/docs/output/commands/npm-start.html b/deps/npm/docs/output/commands/npm-start.html
index 93452be11a1e..0f77764fe40b 100644
--- a/deps/npm/docs/output/commands/npm-start.html
+++ b/deps/npm/docs/output/commands/npm-start.html
@@ -141,9 +141,9 @@
-
+
npm-start
- @10.9.3
+ @10.9.4
Start a package
diff --git a/deps/npm/docs/output/commands/npm-stop.html b/deps/npm/docs/output/commands/npm-stop.html
index fe8894b78730..b6d6d883516b 100644
--- a/deps/npm/docs/output/commands/npm-stop.html
+++ b/deps/npm/docs/output/commands/npm-stop.html
@@ -141,9 +141,9 @@
-
+
npm-stop
- @10.9.3
+ @10.9.4
Stop a package
diff --git a/deps/npm/docs/output/commands/npm-team.html b/deps/npm/docs/output/commands/npm-team.html
index 9ce5c3ce2a28..9f5d0e63104a 100644
--- a/deps/npm/docs/output/commands/npm-team.html
+++ b/deps/npm/docs/output/commands/npm-team.html
@@ -141,9 +141,9 @@
-
+
npm-team
- @10.9.3
+ @10.9.4
Manage organization teams and team memberships
diff --git a/deps/npm/docs/output/commands/npm-test.html b/deps/npm/docs/output/commands/npm-test.html
index 0c43d8f32c68..b09b88dd1393 100644
--- a/deps/npm/docs/output/commands/npm-test.html
+++ b/deps/npm/docs/output/commands/npm-test.html
@@ -141,9 +141,9 @@
-
+
npm-test
- @10.9.3
+ @10.9.4
Test a package
diff --git a/deps/npm/docs/output/commands/npm-token.html b/deps/npm/docs/output/commands/npm-token.html
index 8f3a5a18e573..fc29f1d31f0d 100644
--- a/deps/npm/docs/output/commands/npm-token.html
+++ b/deps/npm/docs/output/commands/npm-token.html
@@ -141,9 +141,9 @@
-
+
npm-token
- @10.9.3
+ @10.9.4
Manage your authentication tokens
diff --git a/deps/npm/docs/output/commands/npm-uninstall.html b/deps/npm/docs/output/commands/npm-uninstall.html
index fe8b4d204943..8cfbdf38b5b0 100644
--- a/deps/npm/docs/output/commands/npm-uninstall.html
+++ b/deps/npm/docs/output/commands/npm-uninstall.html
@@ -141,9 +141,9 @@
-
+
npm-uninstall
- @10.9.3
+ @10.9.4
Remove a package
diff --git a/deps/npm/docs/output/commands/npm-unpublish.html b/deps/npm/docs/output/commands/npm-unpublish.html
index abe90f92af34..e1b840503554 100644
--- a/deps/npm/docs/output/commands/npm-unpublish.html
+++ b/deps/npm/docs/output/commands/npm-unpublish.html
@@ -141,9 +141,9 @@
-
+
npm-unpublish
- @10.9.3
+ @10.9.4
Remove a package from the registry
diff --git a/deps/npm/docs/output/commands/npm-unstar.html b/deps/npm/docs/output/commands/npm-unstar.html
index 71b98a1777d0..d946d81cbd8a 100644
--- a/deps/npm/docs/output/commands/npm-unstar.html
+++ b/deps/npm/docs/output/commands/npm-unstar.html
@@ -141,9 +141,9 @@
-
+
npm-unstar
- @10.9.3
+ @10.9.4
Remove an item from your favorite packages
diff --git a/deps/npm/docs/output/commands/npm-update.html b/deps/npm/docs/output/commands/npm-update.html
index e69df00bca29..9eb29fb4481a 100644
--- a/deps/npm/docs/output/commands/npm-update.html
+++ b/deps/npm/docs/output/commands/npm-update.html
@@ -141,9 +141,9 @@
-
+
npm-update
- @10.9.3
+ @10.9.4
Update packages
diff --git a/deps/npm/docs/output/commands/npm-version.html b/deps/npm/docs/output/commands/npm-version.html
index d2ab3b88f85a..186bafe9463d 100644
--- a/deps/npm/docs/output/commands/npm-version.html
+++ b/deps/npm/docs/output/commands/npm-version.html
@@ -141,9 +141,9 @@
-
+
npm-version
- @10.9.3
+ @10.9.4
Bump a package version
diff --git a/deps/npm/docs/output/commands/npm-view.html b/deps/npm/docs/output/commands/npm-view.html
index 4311e6b8c5d9..fceb0df0cd56 100644
--- a/deps/npm/docs/output/commands/npm-view.html
+++ b/deps/npm/docs/output/commands/npm-view.html
@@ -141,9 +141,9 @@
-
+
npm-view
- @10.9.3
+ @10.9.4
View registry info
diff --git a/deps/npm/docs/output/commands/npm-whoami.html b/deps/npm/docs/output/commands/npm-whoami.html
index c24440bd1e59..2933c61ce5fa 100644
--- a/deps/npm/docs/output/commands/npm-whoami.html
+++ b/deps/npm/docs/output/commands/npm-whoami.html
@@ -141,9 +141,9 @@
-
+
npm-whoami
- @10.9.3
+ @10.9.4
Display npm username
diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html
index 6e27691a0b2a..dbaba93ed54f 100644
--- a/deps/npm/docs/output/commands/npm.html
+++ b/deps/npm/docs/output/commands/npm.html
@@ -141,9 +141,9 @@
-
+
npm
- @10.9.3
+ @10.9.4
javascript package manager
@@ -158,7 +158,7 @@ Table of contents
Note: This command is unaware of workspaces.
Version
-10.9.3
+10.9.4
Description
npm is the package manager for the Node JavaScript platform. It puts
modules in place so that node can find them, and manages dependency
diff --git a/deps/npm/docs/output/commands/npx.html b/deps/npm/docs/output/commands/npx.html
index 83a4ce10c5d9..493e6cb1d33c 100644
--- a/deps/npm/docs/output/commands/npx.html
+++ b/deps/npm/docs/output/commands/npx.html
@@ -141,9 +141,9 @@
-
+
npx
- @10.9.3
+ @10.9.4
Run a command from a local or remote npm package
diff --git a/deps/npm/docs/output/configuring-npm/folders.html b/deps/npm/docs/output/configuring-npm/folders.html
index 74970ccf0445..bece14d87228 100644
--- a/deps/npm/docs/output/configuring-npm/folders.html
+++ b/deps/npm/docs/output/configuring-npm/folders.html
@@ -141,9 +141,9 @@
-
+
folders
- @10.9.3
+ @10.9.4
Folder Structures Used by npm
diff --git a/deps/npm/docs/output/configuring-npm/install.html b/deps/npm/docs/output/configuring-npm/install.html
index 815253042195..c1d66ea00bd3 100644
--- a/deps/npm/docs/output/configuring-npm/install.html
+++ b/deps/npm/docs/output/configuring-npm/install.html
@@ -141,9 +141,9 @@
-
+
install
- @10.9.3
+ @10.9.4
Download and install node and npm
diff --git a/deps/npm/docs/output/configuring-npm/npm-global.html b/deps/npm/docs/output/configuring-npm/npm-global.html
index 74970ccf0445..bece14d87228 100644
--- a/deps/npm/docs/output/configuring-npm/npm-global.html
+++ b/deps/npm/docs/output/configuring-npm/npm-global.html
@@ -141,9 +141,9 @@
-
+
folders
- @10.9.3
+ @10.9.4
Folder Structures Used by npm
diff --git a/deps/npm/docs/output/configuring-npm/npm-json.html b/deps/npm/docs/output/configuring-npm/npm-json.html
index dc128687164d..8f19faa98668 100644
--- a/deps/npm/docs/output/configuring-npm/npm-json.html
+++ b/deps/npm/docs/output/configuring-npm/npm-json.html
@@ -141,9 +141,9 @@
-
+
package.json
- @10.9.3
+ @10.9.4
Specifics of npm's package.json handling
diff --git a/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html b/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html
index bc586d135c0a..d05b2f70f747 100644
--- a/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html
+++ b/deps/npm/docs/output/configuring-npm/npm-shrinkwrap-json.html
@@ -141,9 +141,9 @@
-
+
npm-shrinkwrap.json
- @10.9.3
+ @10.9.4
A publishable lockfile
diff --git a/deps/npm/docs/output/configuring-npm/npmrc.html b/deps/npm/docs/output/configuring-npm/npmrc.html
index 6f961dad20b5..5817f933f072 100644
--- a/deps/npm/docs/output/configuring-npm/npmrc.html
+++ b/deps/npm/docs/output/configuring-npm/npmrc.html
@@ -141,9 +141,9 @@
-
+
npmrc
- @10.9.3
+ @10.9.4
The npm config files
diff --git a/deps/npm/docs/output/configuring-npm/package-json.html b/deps/npm/docs/output/configuring-npm/package-json.html
index dc128687164d..8f19faa98668 100644
--- a/deps/npm/docs/output/configuring-npm/package-json.html
+++ b/deps/npm/docs/output/configuring-npm/package-json.html
@@ -141,9 +141,9 @@
-
+
package.json
- @10.9.3
+ @10.9.4
Specifics of npm's package.json handling
diff --git a/deps/npm/docs/output/configuring-npm/package-lock-json.html b/deps/npm/docs/output/configuring-npm/package-lock-json.html
index 6c302f9228c5..1d9a3522fdb6 100644
--- a/deps/npm/docs/output/configuring-npm/package-lock-json.html
+++ b/deps/npm/docs/output/configuring-npm/package-lock-json.html
@@ -141,9 +141,9 @@
-
+
package-lock.json
- @10.9.3
+ @10.9.4
A manifestation of the manifest
diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html
index 3348147f3ea6..5dc62e5b9b15 100644
--- a/deps/npm/docs/output/using-npm/config.html
+++ b/deps/npm/docs/output/using-npm/config.html
@@ -141,9 +141,9 @@
-
+
config
- @10.9.3
+ @10.9.4
More than you probably want to know about npm configuration
diff --git a/deps/npm/docs/output/using-npm/dependency-selectors.html b/deps/npm/docs/output/using-npm/dependency-selectors.html
index ed512664d165..4ff77e2bfc50 100644
--- a/deps/npm/docs/output/using-npm/dependency-selectors.html
+++ b/deps/npm/docs/output/using-npm/dependency-selectors.html
@@ -141,9 +141,9 @@
-
+
Dependency Selector Syntax & Querying
- @10.9.3
+ @10.9.4
Dependency Selector Syntax & Querying
diff --git a/deps/npm/docs/output/using-npm/developers.html b/deps/npm/docs/output/using-npm/developers.html
index e1a4ae53c726..3093e46992df 100644
--- a/deps/npm/docs/output/using-npm/developers.html
+++ b/deps/npm/docs/output/using-npm/developers.html
@@ -141,9 +141,9 @@
-
+
developers
- @10.9.3
+ @10.9.4
Developer Guide
diff --git a/deps/npm/docs/output/using-npm/logging.html b/deps/npm/docs/output/using-npm/logging.html
index af069e4cc3df..39e1f084e01f 100644
--- a/deps/npm/docs/output/using-npm/logging.html
+++ b/deps/npm/docs/output/using-npm/logging.html
@@ -141,9 +141,9 @@
-
+
Logging
- @10.9.3
+ @10.9.4
Why, What & How We Log
diff --git a/deps/npm/docs/output/using-npm/orgs.html b/deps/npm/docs/output/using-npm/orgs.html
index 4f106df86b5d..556f0dfbd72f 100644
--- a/deps/npm/docs/output/using-npm/orgs.html
+++ b/deps/npm/docs/output/using-npm/orgs.html
@@ -141,9 +141,9 @@
-
+
orgs
- @10.9.3
+ @10.9.4
Working with Teams & Orgs
diff --git a/deps/npm/docs/output/using-npm/package-spec.html b/deps/npm/docs/output/using-npm/package-spec.html
index 3cca23ec1b42..e650ba236489 100644
--- a/deps/npm/docs/output/using-npm/package-spec.html
+++ b/deps/npm/docs/output/using-npm/package-spec.html
@@ -141,9 +141,9 @@
-
+
package-spec
- @10.9.3
+ @10.9.4
Package name specifier
diff --git a/deps/npm/docs/output/using-npm/registry.html b/deps/npm/docs/output/using-npm/registry.html
index 42ae54b5209a..4ca762b771f9 100644
--- a/deps/npm/docs/output/using-npm/registry.html
+++ b/deps/npm/docs/output/using-npm/registry.html
@@ -141,9 +141,9 @@
-
+
registry
- @10.9.3
+ @10.9.4
The JavaScript Package Registry
diff --git a/deps/npm/docs/output/using-npm/removal.html b/deps/npm/docs/output/using-npm/removal.html
index 459186018eca..eb3b91543ba8 100644
--- a/deps/npm/docs/output/using-npm/removal.html
+++ b/deps/npm/docs/output/using-npm/removal.html
@@ -141,9 +141,9 @@
-
+
removal
- @10.9.3
+ @10.9.4
Cleaning the Slate
diff --git a/deps/npm/docs/output/using-npm/scope.html b/deps/npm/docs/output/using-npm/scope.html
index 8882c3c02de5..9e5d95121fc8 100644
--- a/deps/npm/docs/output/using-npm/scope.html
+++ b/deps/npm/docs/output/using-npm/scope.html
@@ -141,9 +141,9 @@
-
+
scope
- @10.9.3
+ @10.9.4
Scoped packages
diff --git a/deps/npm/docs/output/using-npm/scripts.html b/deps/npm/docs/output/using-npm/scripts.html
index 81b76cc15e4d..34679682f668 100644
--- a/deps/npm/docs/output/using-npm/scripts.html
+++ b/deps/npm/docs/output/using-npm/scripts.html
@@ -141,9 +141,9 @@
-
+
scripts
- @10.9.3
+ @10.9.4
How npm handles the "scripts" field
diff --git a/deps/npm/docs/output/using-npm/workspaces.html b/deps/npm/docs/output/using-npm/workspaces.html
index 6d2344b8b392..2b69dbef1147 100644
--- a/deps/npm/docs/output/using-npm/workspaces.html
+++ b/deps/npm/docs/output/using-npm/workspaces.html
@@ -141,9 +141,9 @@
-
+
workspaces
- @10.9.3
+ @10.9.4
Working with workspaces
diff --git a/deps/npm/man/man1/npm-access.1 b/deps/npm/man/man1/npm-access.1
index bbbbf89379a1..bbf94f8c6d6f 100644
--- a/deps/npm/man/man1/npm-access.1
+++ b/deps/npm/man/man1/npm-access.1
@@ -1,4 +1,4 @@
-.TH "NPM-ACCESS" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-ACCESS" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-access\fR - Set access level on published packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-adduser.1 b/deps/npm/man/man1/npm-adduser.1
index dc99f5862a62..c7b49300ec7f 100644
--- a/deps/npm/man/man1/npm-adduser.1
+++ b/deps/npm/man/man1/npm-adduser.1
@@ -1,4 +1,4 @@
-.TH "NPM-ADDUSER" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-ADDUSER" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-adduser\fR - Add a registry user account
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-audit.1 b/deps/npm/man/man1/npm-audit.1
index fd5031ea4a81..c10ca607c807 100644
--- a/deps/npm/man/man1/npm-audit.1
+++ b/deps/npm/man/man1/npm-audit.1
@@ -1,4 +1,4 @@
-.TH "NPM-AUDIT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-AUDIT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-audit\fR - Run a security audit
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-bugs.1 b/deps/npm/man/man1/npm-bugs.1
index ead9e3c7e4c8..8f31d4901328 100644
--- a/deps/npm/man/man1/npm-bugs.1
+++ b/deps/npm/man/man1/npm-bugs.1
@@ -1,4 +1,4 @@
-.TH "NPM-BUGS" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-BUGS" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-bugs\fR - Report bugs for a package in a web browser
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-cache.1 b/deps/npm/man/man1/npm-cache.1
index fb6c1be15e3e..770043489d44 100644
--- a/deps/npm/man/man1/npm-cache.1
+++ b/deps/npm/man/man1/npm-cache.1
@@ -1,4 +1,4 @@
-.TH "NPM-CACHE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-CACHE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-cache\fR - Manipulates packages cache
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-ci.1 b/deps/npm/man/man1/npm-ci.1
index 49b3f4bc8bf7..5ab4e57aa143 100644
--- a/deps/npm/man/man1/npm-ci.1
+++ b/deps/npm/man/man1/npm-ci.1
@@ -1,4 +1,4 @@
-.TH "NPM-CI" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-CI" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-ci\fR - Clean install a project
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-completion.1 b/deps/npm/man/man1/npm-completion.1
index 6416d406ecd9..c416987cf965 100644
--- a/deps/npm/man/man1/npm-completion.1
+++ b/deps/npm/man/man1/npm-completion.1
@@ -1,4 +1,4 @@
-.TH "NPM-COMPLETION" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-COMPLETION" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-completion\fR - Tab Completion for npm
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1
index 033fb38d9936..d54914f51b5c 100644
--- a/deps/npm/man/man1/npm-config.1
+++ b/deps/npm/man/man1/npm-config.1
@@ -1,4 +1,4 @@
-.TH "NPM-CONFIG" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-CONFIG" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-config\fR - Manage the npm configuration files
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-dedupe.1 b/deps/npm/man/man1/npm-dedupe.1
index bd91a82c58aa..e79fb3baeffa 100644
--- a/deps/npm/man/man1/npm-dedupe.1
+++ b/deps/npm/man/man1/npm-dedupe.1
@@ -1,4 +1,4 @@
-.TH "NPM-DEDUPE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DEDUPE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-dedupe\fR - Reduce duplication in the package tree
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1
index 3923544b3c7f..682597d475db 100644
--- a/deps/npm/man/man1/npm-deprecate.1
+++ b/deps/npm/man/man1/npm-deprecate.1
@@ -1,4 +1,4 @@
-.TH "NPM-DEPRECATE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DEPRECATE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-deprecate\fR - Deprecate a version of a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-diff.1 b/deps/npm/man/man1/npm-diff.1
index 5e7d8bb4b2a9..d145f9fd7786 100644
--- a/deps/npm/man/man1/npm-diff.1
+++ b/deps/npm/man/man1/npm-diff.1
@@ -1,4 +1,4 @@
-.TH "NPM-DIFF" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DIFF" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-diff\fR - The registry diff command
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-dist-tag.1 b/deps/npm/man/man1/npm-dist-tag.1
index a29037b85b55..f0c938b487b9 100644
--- a/deps/npm/man/man1/npm-dist-tag.1
+++ b/deps/npm/man/man1/npm-dist-tag.1
@@ -1,4 +1,4 @@
-.TH "NPM-DIST-TAG" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DIST-TAG" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-dist-tag\fR - Modify package distribution tags
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-docs.1 b/deps/npm/man/man1/npm-docs.1
index cd96969eef56..1fdc4b141a63 100644
--- a/deps/npm/man/man1/npm-docs.1
+++ b/deps/npm/man/man1/npm-docs.1
@@ -1,4 +1,4 @@
-.TH "NPM-DOCS" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DOCS" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-docs\fR - Open documentation for a package in a web browser
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-doctor.1 b/deps/npm/man/man1/npm-doctor.1
index f29b59c40365..a70b7b9a2ef3 100644
--- a/deps/npm/man/man1/npm-doctor.1
+++ b/deps/npm/man/man1/npm-doctor.1
@@ -1,4 +1,4 @@
-.TH "NPM-DOCTOR" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-DOCTOR" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-doctor\fR - Check the health of your npm environment
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-edit.1 b/deps/npm/man/man1/npm-edit.1
index aba315c84af6..f83860de83a6 100644
--- a/deps/npm/man/man1/npm-edit.1
+++ b/deps/npm/man/man1/npm-edit.1
@@ -1,4 +1,4 @@
-.TH "NPM-EDIT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-EDIT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-edit\fR - Edit an installed package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-exec.1 b/deps/npm/man/man1/npm-exec.1
index d8f6e9a0d7c2..8b6f66dcd10c 100644
--- a/deps/npm/man/man1/npm-exec.1
+++ b/deps/npm/man/man1/npm-exec.1
@@ -1,4 +1,4 @@
-.TH "NPM-EXEC" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-EXEC" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-exec\fR - Run a command from a local or remote npm package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-explain.1 b/deps/npm/man/man1/npm-explain.1
index a63bc7448170..6edbc0f85a5f 100644
--- a/deps/npm/man/man1/npm-explain.1
+++ b/deps/npm/man/man1/npm-explain.1
@@ -1,4 +1,4 @@
-.TH "NPM-EXPLAIN" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-EXPLAIN" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-explain\fR - Explain installed packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-explore.1 b/deps/npm/man/man1/npm-explore.1
index b1feb37f97e6..0a05d36bd08e 100644
--- a/deps/npm/man/man1/npm-explore.1
+++ b/deps/npm/man/man1/npm-explore.1
@@ -1,4 +1,4 @@
-.TH "NPM-EXPLORE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-EXPLORE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-explore\fR - Browse an installed package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-find-dupes.1 b/deps/npm/man/man1/npm-find-dupes.1
index cb1988b6c873..93b1918dd013 100644
--- a/deps/npm/man/man1/npm-find-dupes.1
+++ b/deps/npm/man/man1/npm-find-dupes.1
@@ -1,4 +1,4 @@
-.TH "NPM-FIND-DUPES" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-FIND-DUPES" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-find-dupes\fR - Find duplication in the package tree
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-fund.1 b/deps/npm/man/man1/npm-fund.1
index e56db03a460f..a78b52c47efe 100644
--- a/deps/npm/man/man1/npm-fund.1
+++ b/deps/npm/man/man1/npm-fund.1
@@ -1,4 +1,4 @@
-.TH "NPM-FUND" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-FUND" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-fund\fR - Retrieve funding information
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-help-search.1 b/deps/npm/man/man1/npm-help-search.1
index 71ee13917ab4..a3f4be5e7829 100644
--- a/deps/npm/man/man1/npm-help-search.1
+++ b/deps/npm/man/man1/npm-help-search.1
@@ -1,4 +1,4 @@
-.TH "NPM-HELP-SEARCH" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-HELP-SEARCH" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-help-search\fR - Search npm help documentation
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-help.1 b/deps/npm/man/man1/npm-help.1
index 5da01d0591d8..d2b1fc4f87ce 100644
--- a/deps/npm/man/man1/npm-help.1
+++ b/deps/npm/man/man1/npm-help.1
@@ -1,4 +1,4 @@
-.TH "NPM-HELP" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-HELP" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-help\fR - Get help on npm
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-hook.1 b/deps/npm/man/man1/npm-hook.1
index af8c6446d92a..21d5427b9c9e 100644
--- a/deps/npm/man/man1/npm-hook.1
+++ b/deps/npm/man/man1/npm-hook.1
@@ -1,4 +1,4 @@
-.TH "NPM-HOOK" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-HOOK" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-hook\fR - Manage registry hooks
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-init.1 b/deps/npm/man/man1/npm-init.1
index c9388088caef..acc8f2d92c33 100644
--- a/deps/npm/man/man1/npm-init.1
+++ b/deps/npm/man/man1/npm-init.1
@@ -1,4 +1,4 @@
-.TH "NPM-INIT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-INIT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-init\fR - Create a package.json file
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-install-ci-test.1 b/deps/npm/man/man1/npm-install-ci-test.1
index be84290240e8..c6a4bab4d8f8 100644
--- a/deps/npm/man/man1/npm-install-ci-test.1
+++ b/deps/npm/man/man1/npm-install-ci-test.1
@@ -1,4 +1,4 @@
-.TH "NPM-INSTALL-CI-TEST" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-INSTALL-CI-TEST" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-install-ci-test\fR - Install a project with a clean slate and run tests
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-install-test.1 b/deps/npm/man/man1/npm-install-test.1
index ce0efcd2f4b8..734081750bf5 100644
--- a/deps/npm/man/man1/npm-install-test.1
+++ b/deps/npm/man/man1/npm-install-test.1
@@ -1,4 +1,4 @@
-.TH "NPM-INSTALL-TEST" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-INSTALL-TEST" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-install-test\fR - Install package(s) and run tests
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-install.1 b/deps/npm/man/man1/npm-install.1
index fdebccd4e245..9552895bc226 100644
--- a/deps/npm/man/man1/npm-install.1
+++ b/deps/npm/man/man1/npm-install.1
@@ -1,4 +1,4 @@
-.TH "NPM-INSTALL" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-INSTALL" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-install\fR - Install a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-link.1 b/deps/npm/man/man1/npm-link.1
index 312c8db5ac89..37ff1b8737eb 100644
--- a/deps/npm/man/man1/npm-link.1
+++ b/deps/npm/man/man1/npm-link.1
@@ -1,4 +1,4 @@
-.TH "NPM-LINK" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-LINK" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-link\fR - Symlink a package folder
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-login.1 b/deps/npm/man/man1/npm-login.1
index 101d734340e2..166735565c27 100644
--- a/deps/npm/man/man1/npm-login.1
+++ b/deps/npm/man/man1/npm-login.1
@@ -1,4 +1,4 @@
-.TH "NPM-LOGIN" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-LOGIN" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-login\fR - Login to a registry user account
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-logout.1 b/deps/npm/man/man1/npm-logout.1
index bb3862c28f17..898f6d610d70 100644
--- a/deps/npm/man/man1/npm-logout.1
+++ b/deps/npm/man/man1/npm-logout.1
@@ -1,4 +1,4 @@
-.TH "NPM-LOGOUT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-LOGOUT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-logout\fR - Log out of the registry
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1
index 74df5f005fc1..3ad67413a2cd 100644
--- a/deps/npm/man/man1/npm-ls.1
+++ b/deps/npm/man/man1/npm-ls.1
@@ -1,4 +1,4 @@
-.TH "NPM-LS" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-LS" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-ls\fR - List installed packages
.SS "Synopsis"
@@ -20,7 +20,7 @@ Positional arguments are \fBname@version-range\fR identifiers, which will limit
.P
.RS 2
.nf
-npm@10.9.3 /path/to/npm
+npm@10.9.4 /path/to/npm
└─┬ init-package-json@0.0.4
└── promzard@0.1.5
.fi
diff --git a/deps/npm/man/man1/npm-org.1 b/deps/npm/man/man1/npm-org.1
index 126e3b5df774..cce948fc162d 100644
--- a/deps/npm/man/man1/npm-org.1
+++ b/deps/npm/man/man1/npm-org.1
@@ -1,4 +1,4 @@
-.TH "NPM-ORG" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-ORG" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-org\fR - Manage orgs
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-outdated.1 b/deps/npm/man/man1/npm-outdated.1
index b1b0721e887c..f455b6c30ed6 100644
--- a/deps/npm/man/man1/npm-outdated.1
+++ b/deps/npm/man/man1/npm-outdated.1
@@ -1,4 +1,4 @@
-.TH "NPM-OUTDATED" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-OUTDATED" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-outdated\fR - Check for outdated packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-owner.1 b/deps/npm/man/man1/npm-owner.1
index 63baf946d3ec..b14ab552ac92 100644
--- a/deps/npm/man/man1/npm-owner.1
+++ b/deps/npm/man/man1/npm-owner.1
@@ -1,4 +1,4 @@
-.TH "NPM-OWNER" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-OWNER" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-owner\fR - Manage package owners
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-pack.1 b/deps/npm/man/man1/npm-pack.1
index e66633d4d6d9..682939fc09d1 100644
--- a/deps/npm/man/man1/npm-pack.1
+++ b/deps/npm/man/man1/npm-pack.1
@@ -1,4 +1,4 @@
-.TH "NPM-PACK" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PACK" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-pack\fR - Create a tarball from a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-ping.1 b/deps/npm/man/man1/npm-ping.1
index a3e21d4e3423..1f04c2f6c1d1 100644
--- a/deps/npm/man/man1/npm-ping.1
+++ b/deps/npm/man/man1/npm-ping.1
@@ -1,4 +1,4 @@
-.TH "NPM-PING" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PING" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-ping\fR - Ping npm registry
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-pkg.1 b/deps/npm/man/man1/npm-pkg.1
index 5c10939e657b..08e269a26bdf 100644
--- a/deps/npm/man/man1/npm-pkg.1
+++ b/deps/npm/man/man1/npm-pkg.1
@@ -1,4 +1,4 @@
-.TH "NPM-PKG" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PKG" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-pkg\fR - Manages your package.json
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-prefix.1 b/deps/npm/man/man1/npm-prefix.1
index 1e830ee1fbc9..c830ceba237e 100644
--- a/deps/npm/man/man1/npm-prefix.1
+++ b/deps/npm/man/man1/npm-prefix.1
@@ -1,4 +1,4 @@
-.TH "NPM-PREFIX" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PREFIX" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-prefix\fR - Display prefix
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-profile.1 b/deps/npm/man/man1/npm-profile.1
index b59bd0d47c34..2e93ce1f632a 100644
--- a/deps/npm/man/man1/npm-profile.1
+++ b/deps/npm/man/man1/npm-profile.1
@@ -1,4 +1,4 @@
-.TH "NPM-PROFILE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PROFILE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-profile\fR - Change settings on your registry profile
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-prune.1 b/deps/npm/man/man1/npm-prune.1
index 97967d37fd50..a8adbbda2377 100644
--- a/deps/npm/man/man1/npm-prune.1
+++ b/deps/npm/man/man1/npm-prune.1
@@ -1,4 +1,4 @@
-.TH "NPM-PRUNE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PRUNE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-prune\fR - Remove extraneous packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-publish.1 b/deps/npm/man/man1/npm-publish.1
index 92268682bf9b..dce43f3c7e8c 100644
--- a/deps/npm/man/man1/npm-publish.1
+++ b/deps/npm/man/man1/npm-publish.1
@@ -1,4 +1,4 @@
-.TH "NPM-PUBLISH" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-PUBLISH" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-publish\fR - Publish a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-query.1 b/deps/npm/man/man1/npm-query.1
index 6733ebaf1024..6515f1696a79 100644
--- a/deps/npm/man/man1/npm-query.1
+++ b/deps/npm/man/man1/npm-query.1
@@ -1,4 +1,4 @@
-.TH "NPM-QUERY" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-QUERY" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-query\fR - Dependency selector query
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-rebuild.1 b/deps/npm/man/man1/npm-rebuild.1
index 73ccb5381b8c..a7cebf8874cd 100644
--- a/deps/npm/man/man1/npm-rebuild.1
+++ b/deps/npm/man/man1/npm-rebuild.1
@@ -1,4 +1,4 @@
-.TH "NPM-REBUILD" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-REBUILD" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-rebuild\fR - Rebuild a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-repo.1 b/deps/npm/man/man1/npm-repo.1
index 1b1ddce412c7..97067bb89b9e 100644
--- a/deps/npm/man/man1/npm-repo.1
+++ b/deps/npm/man/man1/npm-repo.1
@@ -1,4 +1,4 @@
-.TH "NPM-REPO" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-REPO" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-repo\fR - Open package repository page in the browser
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-restart.1 b/deps/npm/man/man1/npm-restart.1
index b47dde96e799..f199cb3d640a 100644
--- a/deps/npm/man/man1/npm-restart.1
+++ b/deps/npm/man/man1/npm-restart.1
@@ -1,4 +1,4 @@
-.TH "NPM-RESTART" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-RESTART" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-restart\fR - Restart a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-root.1 b/deps/npm/man/man1/npm-root.1
index 5f9f4ca64842..85b085c9b650 100644
--- a/deps/npm/man/man1/npm-root.1
+++ b/deps/npm/man/man1/npm-root.1
@@ -1,4 +1,4 @@
-.TH "NPM-ROOT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-ROOT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-root\fR - Display npm root
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1
index 34d6986b0825..2cb84fb4a0ef 100644
--- a/deps/npm/man/man1/npm-run-script.1
+++ b/deps/npm/man/man1/npm-run-script.1
@@ -1,4 +1,4 @@
-.TH "NPM-RUN-SCRIPT" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-RUN-SCRIPT" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-run-script\fR - Run arbitrary package scripts
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-sbom.1 b/deps/npm/man/man1/npm-sbom.1
index 5f218c62a4f8..83274456496c 100644
--- a/deps/npm/man/man1/npm-sbom.1
+++ b/deps/npm/man/man1/npm-sbom.1
@@ -1,4 +1,4 @@
-.TH "NPM-SBOM" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-SBOM" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-sbom\fR - Generate a Software Bill of Materials (SBOM)
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-search.1 b/deps/npm/man/man1/npm-search.1
index 6dca287b328c..75f3be8531a6 100644
--- a/deps/npm/man/man1/npm-search.1
+++ b/deps/npm/man/man1/npm-search.1
@@ -1,4 +1,4 @@
-.TH "NPM-SEARCH" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-SEARCH" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-search\fR - Search for packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-shrinkwrap.1 b/deps/npm/man/man1/npm-shrinkwrap.1
index 0d000c2cd962..af68b9b24ea5 100644
--- a/deps/npm/man/man1/npm-shrinkwrap.1
+++ b/deps/npm/man/man1/npm-shrinkwrap.1
@@ -1,4 +1,4 @@
-.TH "NPM-SHRINKWRAP" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-SHRINKWRAP" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-shrinkwrap\fR - Lock down dependency versions for publication
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-star.1 b/deps/npm/man/man1/npm-star.1
index 780817c02045..610338bd8bd4 100644
--- a/deps/npm/man/man1/npm-star.1
+++ b/deps/npm/man/man1/npm-star.1
@@ -1,4 +1,4 @@
-.TH "NPM-STAR" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-STAR" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-star\fR - Mark your favorite packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-stars.1 b/deps/npm/man/man1/npm-stars.1
index b42f0b103e6b..02cc66b0150d 100644
--- a/deps/npm/man/man1/npm-stars.1
+++ b/deps/npm/man/man1/npm-stars.1
@@ -1,4 +1,4 @@
-.TH "NPM-STARS" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-STARS" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-stars\fR - View packages marked as favorites
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-start.1 b/deps/npm/man/man1/npm-start.1
index 2db8bca1089e..cd710c428ddf 100644
--- a/deps/npm/man/man1/npm-start.1
+++ b/deps/npm/man/man1/npm-start.1
@@ -1,4 +1,4 @@
-.TH "NPM-START" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-START" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-start\fR - Start a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-stop.1 b/deps/npm/man/man1/npm-stop.1
index f32d59c82db7..f464319bb0d8 100644
--- a/deps/npm/man/man1/npm-stop.1
+++ b/deps/npm/man/man1/npm-stop.1
@@ -1,4 +1,4 @@
-.TH "NPM-STOP" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-STOP" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-stop\fR - Stop a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-team.1 b/deps/npm/man/man1/npm-team.1
index fd034c0705e2..cd9a7c26ac97 100644
--- a/deps/npm/man/man1/npm-team.1
+++ b/deps/npm/man/man1/npm-team.1
@@ -1,4 +1,4 @@
-.TH "NPM-TEAM" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-TEAM" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-team\fR - Manage organization teams and team memberships
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-test.1 b/deps/npm/man/man1/npm-test.1
index 56959e9995d4..66519c550186 100644
--- a/deps/npm/man/man1/npm-test.1
+++ b/deps/npm/man/man1/npm-test.1
@@ -1,4 +1,4 @@
-.TH "NPM-TEST" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-TEST" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-test\fR - Test a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-token.1 b/deps/npm/man/man1/npm-token.1
index 5c45170b4947..8fa563cae83a 100644
--- a/deps/npm/man/man1/npm-token.1
+++ b/deps/npm/man/man1/npm-token.1
@@ -1,4 +1,4 @@
-.TH "NPM-TOKEN" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-TOKEN" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-token\fR - Manage your authentication tokens
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-uninstall.1 b/deps/npm/man/man1/npm-uninstall.1
index e59cc5a422a7..29b3262891bf 100644
--- a/deps/npm/man/man1/npm-uninstall.1
+++ b/deps/npm/man/man1/npm-uninstall.1
@@ -1,4 +1,4 @@
-.TH "NPM-UNINSTALL" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-UNINSTALL" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-uninstall\fR - Remove a package
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-unpublish.1 b/deps/npm/man/man1/npm-unpublish.1
index 98cc5faa23f7..44d5facd930b 100644
--- a/deps/npm/man/man1/npm-unpublish.1
+++ b/deps/npm/man/man1/npm-unpublish.1
@@ -1,4 +1,4 @@
-.TH "NPM-UNPUBLISH" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-UNPUBLISH" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-unpublish\fR - Remove a package from the registry
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-unstar.1 b/deps/npm/man/man1/npm-unstar.1
index c43c249b090a..376cabeb8a36 100644
--- a/deps/npm/man/man1/npm-unstar.1
+++ b/deps/npm/man/man1/npm-unstar.1
@@ -1,4 +1,4 @@
-.TH "NPM-UNSTAR" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-UNSTAR" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-unstar\fR - Remove an item from your favorite packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-update.1 b/deps/npm/man/man1/npm-update.1
index 2f6712125980..c851b7a55538 100644
--- a/deps/npm/man/man1/npm-update.1
+++ b/deps/npm/man/man1/npm-update.1
@@ -1,4 +1,4 @@
-.TH "NPM-UPDATE" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-UPDATE" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-update\fR - Update packages
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-version.1 b/deps/npm/man/man1/npm-version.1
index 036031994e13..a8f1f08ae120 100644
--- a/deps/npm/man/man1/npm-version.1
+++ b/deps/npm/man/man1/npm-version.1
@@ -1,4 +1,4 @@
-.TH "NPM-VERSION" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-VERSION" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-version\fR - Bump a package version
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-view.1 b/deps/npm/man/man1/npm-view.1
index 2a7d646379ec..505563e99aad 100644
--- a/deps/npm/man/man1/npm-view.1
+++ b/deps/npm/man/man1/npm-view.1
@@ -1,4 +1,4 @@
-.TH "NPM-VIEW" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-VIEW" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-view\fR - View registry info
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm-whoami.1 b/deps/npm/man/man1/npm-whoami.1
index d3f9596e6068..af860ad121a0 100644
--- a/deps/npm/man/man1/npm-whoami.1
+++ b/deps/npm/man/man1/npm-whoami.1
@@ -1,4 +1,4 @@
-.TH "NPM-WHOAMI" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-WHOAMI" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-whoami\fR - Display npm username
.SS "Synopsis"
diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1
index 7f36fc2e7530..17d7de9e16e4 100644
--- a/deps/npm/man/man1/npm.1
+++ b/deps/npm/man/man1/npm.1
@@ -1,4 +1,4 @@
-.TH "NPM" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPM" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm\fR - javascript package manager
.SS "Synopsis"
@@ -12,7 +12,7 @@ npm
Note: This command is unaware of workspaces.
.SS "Version"
.P
-10.9.3
+10.9.4
.SS "Description"
.P
npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency conflicts intelligently.
diff --git a/deps/npm/man/man1/npx.1 b/deps/npm/man/man1/npx.1
index 294bff4b69c5..a02bc9eece79 100644
--- a/deps/npm/man/man1/npx.1
+++ b/deps/npm/man/man1/npx.1
@@ -1,4 +1,4 @@
-.TH "NPX" "1" "June 2025" "NPM@10.9.3" ""
+.TH "NPX" "1" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpx\fR - Run a command from a local or remote npm package
.SS "Synopsis"
diff --git a/deps/npm/man/man5/folders.5 b/deps/npm/man/man5/folders.5
index 1ab9ef3e750e..642c26ba3452 100644
--- a/deps/npm/man/man5/folders.5
+++ b/deps/npm/man/man5/folders.5
@@ -1,4 +1,4 @@
-.TH "FOLDERS" "5" "June 2025" "NPM@10.9.3" ""
+.TH "FOLDERS" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBfolders\fR - Folder Structures Used by npm
.SS "Description"
diff --git a/deps/npm/man/man5/install.5 b/deps/npm/man/man5/install.5
index d93b5141304c..b9c9f105a1c1 100644
--- a/deps/npm/man/man5/install.5
+++ b/deps/npm/man/man5/install.5
@@ -1,4 +1,4 @@
-.TH "INSTALL" "5" "June 2025" "NPM@10.9.3" ""
+.TH "INSTALL" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBinstall\fR - Download and install node and npm
.SS "Description"
diff --git a/deps/npm/man/man5/npm-global.5 b/deps/npm/man/man5/npm-global.5
index 1ab9ef3e750e..642c26ba3452 100644
--- a/deps/npm/man/man5/npm-global.5
+++ b/deps/npm/man/man5/npm-global.5
@@ -1,4 +1,4 @@
-.TH "FOLDERS" "5" "June 2025" "NPM@10.9.3" ""
+.TH "FOLDERS" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBfolders\fR - Folder Structures Used by npm
.SS "Description"
diff --git a/deps/npm/man/man5/npm-json.5 b/deps/npm/man/man5/npm-json.5
index b46a1ddb4ad5..6958d47974a2 100644
--- a/deps/npm/man/man5/npm-json.5
+++ b/deps/npm/man/man5/npm-json.5
@@ -1,4 +1,4 @@
-.TH "PACKAGE.JSON" "5" "June 2025" "NPM@10.9.3" ""
+.TH "PACKAGE.JSON" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBpackage.json\fR - Specifics of npm's package.json handling
.SS "Description"
diff --git a/deps/npm/man/man5/npm-shrinkwrap-json.5 b/deps/npm/man/man5/npm-shrinkwrap-json.5
index 67a9d094c397..99a49edc84bd 100644
--- a/deps/npm/man/man5/npm-shrinkwrap-json.5
+++ b/deps/npm/man/man5/npm-shrinkwrap-json.5
@@ -1,4 +1,4 @@
-.TH "NPM-SHRINKWRAP.JSON" "5" "June 2025" "NPM@10.9.3" ""
+.TH "NPM-SHRINKWRAP.JSON" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpm-shrinkwrap.json\fR - A publishable lockfile
.SS "Description"
diff --git a/deps/npm/man/man5/npmrc.5 b/deps/npm/man/man5/npmrc.5
index d8e39ef4b547..dc14f02c396d 100644
--- a/deps/npm/man/man5/npmrc.5
+++ b/deps/npm/man/man5/npmrc.5
@@ -1,4 +1,4 @@
-.TH "NPMRC" "5" "June 2025" "NPM@10.9.3" ""
+.TH "NPMRC" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBnpmrc\fR - The npm config files
.SS "Description"
diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5
index b46a1ddb4ad5..6958d47974a2 100644
--- a/deps/npm/man/man5/package-json.5
+++ b/deps/npm/man/man5/package-json.5
@@ -1,4 +1,4 @@
-.TH "PACKAGE.JSON" "5" "June 2025" "NPM@10.9.3" ""
+.TH "PACKAGE.JSON" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBpackage.json\fR - Specifics of npm's package.json handling
.SS "Description"
diff --git a/deps/npm/man/man5/package-lock-json.5 b/deps/npm/man/man5/package-lock-json.5
index b9cfdf5263f3..b198dea22a6a 100644
--- a/deps/npm/man/man5/package-lock-json.5
+++ b/deps/npm/man/man5/package-lock-json.5
@@ -1,4 +1,4 @@
-.TH "PACKAGE-LOCK.JSON" "5" "June 2025" "NPM@10.9.3" ""
+.TH "PACKAGE-LOCK.JSON" "5" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBpackage-lock.json\fR - A manifestation of the manifest
.SS "Description"
diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7
index 42bfab4fea45..c0526d9b2940 100644
--- a/deps/npm/man/man7/config.7
+++ b/deps/npm/man/man7/config.7
@@ -1,4 +1,4 @@
-.TH "CONFIG" "7" "June 2025" "NPM@10.9.3" ""
+.TH "CONFIG" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBconfig\fR - More than you probably want to know about npm configuration
.SS "Description"
diff --git a/deps/npm/man/man7/dependency-selectors.7 b/deps/npm/man/man7/dependency-selectors.7
index ef646e12ec8f..dc20fcab2b93 100644
--- a/deps/npm/man/man7/dependency-selectors.7
+++ b/deps/npm/man/man7/dependency-selectors.7
@@ -1,4 +1,4 @@
-.TH "QUERYING" "7" "June 2025" "NPM@10.9.3" ""
+.TH "QUERYING" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBQuerying\fR - Dependency Selector Syntax & Querying
.SS "Description"
diff --git a/deps/npm/man/man7/developers.7 b/deps/npm/man/man7/developers.7
index f7874d9f36fd..6af18270ffdf 100644
--- a/deps/npm/man/man7/developers.7
+++ b/deps/npm/man/man7/developers.7
@@ -1,4 +1,4 @@
-.TH "DEVELOPERS" "7" "June 2025" "NPM@10.9.3" ""
+.TH "DEVELOPERS" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBdevelopers\fR - Developer Guide
.SS "Description"
diff --git a/deps/npm/man/man7/logging.7 b/deps/npm/man/man7/logging.7
index b0692f6df78c..8941fd3c5187 100644
--- a/deps/npm/man/man7/logging.7
+++ b/deps/npm/man/man7/logging.7
@@ -1,4 +1,4 @@
-.TH "LOGGING" "7" "June 2025" "NPM@10.9.3" ""
+.TH "LOGGING" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBLogging\fR - Why, What & How We Log
.SS "Description"
diff --git a/deps/npm/man/man7/orgs.7 b/deps/npm/man/man7/orgs.7
index 5340af005eca..a3b22bde63c6 100644
--- a/deps/npm/man/man7/orgs.7
+++ b/deps/npm/man/man7/orgs.7
@@ -1,4 +1,4 @@
-.TH "ORGS" "7" "June 2025" "NPM@10.9.3" ""
+.TH "ORGS" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBorgs\fR - Working with Teams & Orgs
.SS "Description"
diff --git a/deps/npm/man/man7/package-spec.7 b/deps/npm/man/man7/package-spec.7
index 5ec5febb66f8..8b8cc8f8badb 100644
--- a/deps/npm/man/man7/package-spec.7
+++ b/deps/npm/man/man7/package-spec.7
@@ -1,4 +1,4 @@
-.TH "PACKAGE-SPEC" "7" "June 2025" "NPM@10.9.3" ""
+.TH "PACKAGE-SPEC" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBpackage-spec\fR - Package name specifier
.SS "Description"
diff --git a/deps/npm/man/man7/registry.7 b/deps/npm/man/man7/registry.7
index c97d3f557674..e416f4f99d60 100644
--- a/deps/npm/man/man7/registry.7
+++ b/deps/npm/man/man7/registry.7
@@ -1,4 +1,4 @@
-.TH "REGISTRY" "7" "June 2025" "NPM@10.9.3" ""
+.TH "REGISTRY" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBregistry\fR - The JavaScript Package Registry
.SS "Description"
diff --git a/deps/npm/man/man7/removal.7 b/deps/npm/man/man7/removal.7
index 18905b74db6e..7499745f8019 100644
--- a/deps/npm/man/man7/removal.7
+++ b/deps/npm/man/man7/removal.7
@@ -1,4 +1,4 @@
-.TH "REMOVAL" "7" "June 2025" "NPM@10.9.3" ""
+.TH "REMOVAL" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBremoval\fR - Cleaning the Slate
.SS "Synopsis"
diff --git a/deps/npm/man/man7/scope.7 b/deps/npm/man/man7/scope.7
index 352732b7e126..bc0f23df01f2 100644
--- a/deps/npm/man/man7/scope.7
+++ b/deps/npm/man/man7/scope.7
@@ -1,4 +1,4 @@
-.TH "SCOPE" "7" "June 2025" "NPM@10.9.3" ""
+.TH "SCOPE" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBscope\fR - Scoped packages
.SS "Description"
diff --git a/deps/npm/man/man7/scripts.7 b/deps/npm/man/man7/scripts.7
index 330c8ce424b1..aeddbfe88fa5 100644
--- a/deps/npm/man/man7/scripts.7
+++ b/deps/npm/man/man7/scripts.7
@@ -1,4 +1,4 @@
-.TH "SCRIPTS" "7" "June 2025" "NPM@10.9.3" ""
+.TH "SCRIPTS" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBscripts\fR - How npm handles the "scripts" field
.SS "Description"
diff --git a/deps/npm/man/man7/workspaces.7 b/deps/npm/man/man7/workspaces.7
index 9e71ab90ee4e..5a3753ddf1ea 100644
--- a/deps/npm/man/man7/workspaces.7
+++ b/deps/npm/man/man7/workspaces.7
@@ -1,4 +1,4 @@
-.TH "WORKSPACES" "7" "June 2025" "NPM@10.9.3" ""
+.TH "WORKSPACES" "7" "September 2025" "NPM@10.9.4" ""
.SH "NAME"
\fBworkspaces\fR - Working with workspaces
.SS "Description"
diff --git a/deps/npm/package.json b/deps/npm/package.json
index 99cf43ccda66..8210328523bd 100644
--- a/deps/npm/package.json
+++ b/deps/npm/package.json
@@ -1,5 +1,5 @@
{
- "version": "10.9.3",
+ "version": "10.9.4",
"name": "npm",
"description": "a package manager for JavaScript",
"workspaces": [
From 99ea08dc43616254d25681204e566e3d49fc8d12 Mon Sep 17 00:00:00 2001
From: Xuguang Mei
Date: Tue, 7 Oct 2025 22:07:13 +0800
Subject: [PATCH 03/90] repl: add isValidParentheses check before wrap input
PR-URL: https://github.com/nodejs/node/pull/59607
Backport-PR-URL: https://github.com/nodejs/node/pull/60066
Reviewed-By: Ruben Bridgewater
Reviewed-By: Antoine du Hamel
---
lib/internal/repl/utils.js | 22 +++++++-
lib/repl.js | 4 +-
test/parallel/test-repl-preview.js | 86 +++++++++++++++++++++++++++++-
test/parallel/test-repl.js | 13 +++++
4 files changed, 121 insertions(+), 4 deletions(-)
diff --git a/lib/internal/repl/utils.js b/lib/internal/repl/utils.js
index e0327ee5655a..6c69a4f1490c 100644
--- a/lib/internal/repl/utils.js
+++ b/lib/internal/repl/utils.js
@@ -292,7 +292,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
function getInputPreview(input, callback) {
// For similar reasons as `defaultEval`, wrap expressions starting with a
// curly brace with parenthesis.
- if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';') {
+ if (!wrapped && input[0] === '{' && input[input.length - 1] !== ';' && isValidSyntax(input)) {
input = `(${input})`;
wrapped = true;
}
@@ -737,6 +737,25 @@ function setupReverseSearch(repl) {
const startsWithBraceRegExp = /^\s*{/;
const endsWithSemicolonRegExp = /;\s*$/;
+function isValidSyntax(input) {
+ try {
+ AcornParser.parse(input, {
+ ecmaVersion: 'latest',
+ allowAwaitOutsideFunction: true,
+ });
+ return true;
+ } catch {
+ try {
+ AcornParser.parse(`_=${input}`, {
+ ecmaVersion: 'latest',
+ allowAwaitOutsideFunction: true,
+ });
+ return true;
+ } catch {
+ return false;
+ }
+ }
+}
/**
* Checks if some provided code represents an object literal.
@@ -759,4 +778,5 @@ module.exports = {
setupPreview,
setupReverseSearch,
isObjectLiteral,
+ isValidSyntax,
};
diff --git a/lib/repl.js b/lib/repl.js
index 0557affb4693..30a11a167e12 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -172,6 +172,7 @@ const {
setupPreview,
setupReverseSearch,
isObjectLiteral,
+ isValidSyntax,
} = require('internal/repl/utils');
const {
constants: {
@@ -440,7 +441,7 @@ function REPLServer(prompt,
let awaitPromise = false;
const input = code;
- if (isObjectLiteral(code)) {
+ if (isObjectLiteral(code) && isValidSyntax(code)) {
// Add parentheses to make sure `code` is parsed as an expression
code = `(${StringPrototypeTrim(code)})\n`;
wrappedCmd = true;
@@ -1859,6 +1860,7 @@ module.exports = {
REPL_MODE_SLOPPY,
REPL_MODE_STRICT,
Recoverable,
+ isValidSyntax,
};
ObjectDefineProperty(module.exports, 'builtinModules', {
diff --git a/test/parallel/test-repl-preview.js b/test/parallel/test-repl-preview.js
index 6eb2a169918a..7518673ce2e8 100644
--- a/test/parallel/test-repl-preview.js
+++ b/test/parallel/test-repl-preview.js
@@ -157,6 +157,83 @@ async function tests(options) {
'\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
'\x1B[33m1\x1B[39m',
]
+ }, {
+ input: 'aaaa',
+ noPreview: 'Uncaught ReferenceError: aaaa is not defined',
+ preview: [
+ 'aaaa\r',
+ 'Uncaught ReferenceError: aaaa is not defined',
+ ]
+ }, {
+ input: '/0',
+ noPreview: '/0',
+ preview: [
+ '/0\r',
+ '/0',
+ '^',
+ '',
+ 'Uncaught SyntaxError: Invalid regular expression: missing /',
+ ]
+ }, {
+ input: '{})',
+ noPreview: '{})',
+ preview: [
+ '{})\r',
+ '{})',
+ ' ^',
+ '',
+ "Uncaught SyntaxError: Unexpected token ')'",
+ ],
+ }, {
+ input: "{ a: '{' }",
+ noPreview: "{ a: \x1B[32m'{'\x1B[39m }",
+ preview: [
+ "{ a: '{' }\r",
+ "{ a: \x1B[32m'{'\x1B[39m }",
+ ],
+ }, {
+ input: "{'{':0}",
+ noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
+ preview: [
+ "{'{':0}",
+ "\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r",
+ "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }",
+ ],
+ }, {
+ input: '{[Symbol.for("{")]: 0 }',
+ noPreview: '{ [\x1B[32mSymbol({)\x1B[39m]: \x1B[33m0\x1B[39m }',
+ preview: [
+ '{[Symbol.for("{")]: 0 }\r',
+ '{ [\x1B[32mSymbol({)\x1B[39m]: \x1B[33m0\x1B[39m }',
+ ],
+ }, {
+ input: '{},{}',
+ noPreview: '{}',
+ preview: [
+ '{},{}',
+ '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
+ '{}',
+ ],
+ }, {
+ input: '{} //',
+ noPreview: 'repl > ',
+ preview: [
+ '{} //\r',
+ ],
+ }, {
+ input: '{} //;',
+ noPreview: 'repl > ',
+ preview: [
+ '{} //;\r',
+ ],
+ }, {
+ input: '{throw 0}',
+ noPreview: 'Uncaught \x1B[33m0\x1B[39m',
+ preview: [
+ '{throw 0}',
+ '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
+ 'Uncaught \x1B[33m0\x1B[39m',
+ ],
}];
const hasPreview = repl.terminal &&
@@ -177,8 +254,13 @@ async function tests(options) {
assert.deepStrictEqual(lines, preview);
} else {
assert.ok(lines[0].includes(noPreview), lines.map(inspect));
- if (preview.length !== 1 || preview[0] !== `${input}\r`)
- assert.strictEqual(lines.length, 2);
+ if (preview.length !== 1 || preview[0] !== `${input}\r`) {
+ if (preview[preview.length - 1].includes('Uncaught SyntaxError')) {
+ assert.strictEqual(lines.length, 5);
+ } else {
+ assert.strictEqual(lines.length, 2);
+ }
+ }
}
}
}
diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js
index 16cead7c7251..6a02bb6563e2 100644
--- a/test/parallel/test-repl.js
+++ b/test/parallel/test-repl.js
@@ -328,6 +328,19 @@ const errorTests = [
expect: '[Function (anonymous)]'
},
// Multiline object
+ {
+ send: '{}),({}',
+ expect: '... ',
+ },
+ {
+ send: '}',
+ expect: [
+ '{}),({}',
+ kArrow,
+ '',
+ /^Uncaught SyntaxError: /,
+ ]
+ },
{
send: '{ a: ',
expect: '... '
From 223714236912fa5a3a214c2080a5554e233093f3 Mon Sep 17 00:00:00 2001
From: Chengzhong Wu
Date: Tue, 7 Oct 2025 22:08:51 +0800
Subject: [PATCH 04/90] module: link module with a module request record
When a module is being statically linked with module requests, if two
module requests with a same specifier but different attributes are
resolved to two modules, the module requests should be linked to these
two modules.
PR-URL: https://github.com/nodejs/node/pull/58886
Backport-PR-URL: https://github.com/nodejs/node/pull/60000
Refs: https://tc39.es/ecma262/#sec-HostLoadImportedModule
Refs: https://github.com/tc39/proposal-import-attributes?tab=readme-ov-file#how-would-this-proposal-work-with-caching
Reviewed-By: Guy Bedford
Reviewed-By: Joyee Cheung
---
lib/internal/modules/esm/module_job.js | 14 ++--
lib/internal/vm/module.js | 32 ++++---
src/module_wrap.cc | 84 ++++++++++++++-----
src/module_wrap.h | 55 +++++++++++-
.../test-esm-import-attributes-identity.mjs | 59 +++++++++++++
test/es-module/test-esm-json.mjs | 5 ++
.../es-module-loaders/hooks-input.mjs | 2 +
.../es-modules/json-modules-attributes.mjs | 5 ++
test/fixtures/primitive-42.json | 1 +
test/parallel/test-internal-module-wrap.js | 2 +-
10 files changed, 209 insertions(+), 50 deletions(-)
create mode 100644 test/es-module/test-esm-import-attributes-identity.mjs
create mode 100644 test/fixtures/es-modules/json-modules-attributes.mjs
create mode 100644 test/fixtures/primitive-42.json
diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js
index 74d6ac12cdd8..448334f47105 100644
--- a/lib/internal/modules/esm/module_job.js
+++ b/lib/internal/modules/esm/module_job.js
@@ -172,8 +172,7 @@ class ModuleJob extends ModuleJobBase {
const dependencyJobs = Array(moduleRequests.length);
ObjectSetPrototypeOf(dependencyJobs, null);
- // Specifiers should be aligned with the moduleRequests array in order.
- const specifiers = Array(moduleRequests.length);
+ // Modules should be aligned with the moduleRequests array in order.
const modulePromises = Array(moduleRequests.length);
// Iterate with index to avoid calling into userspace with `Symbol.iterator`.
for (let idx = 0; idx < moduleRequests.length; idx++) {
@@ -189,11 +188,10 @@ class ModuleJob extends ModuleJobBase {
return job.modulePromise;
});
modulePromises[idx] = modulePromise;
- specifiers[idx] = specifier;
}
const modules = await SafePromiseAllReturnArrayLike(modulePromises);
- this.module.link(specifiers, modules);
+ this.module.link(modules);
return dependencyJobs;
}
@@ -386,18 +384,16 @@ class ModuleJobSync extends ModuleJobBase {
try {
const moduleRequests = this.module.getModuleRequests();
- // Specifiers should be aligned with the moduleRequests array in order.
- const specifiers = Array(moduleRequests.length);
+ // Modules should be aligned with the moduleRequests array in order.
const modules = Array(moduleRequests.length);
const jobs = Array(moduleRequests.length);
for (let i = 0; i < moduleRequests.length; ++i) {
const { specifier, attributes } = moduleRequests[i];
- const job = this.#loader.getModuleJobForRequire(specifier, url, attributes);
- specifiers[i] = specifier;
+ const job = this.#loader.getModuleJobForRequire(specifier, this.url, attributes);
modules[i] = job.module;
jobs[i] = job;
}
- this.module.link(specifiers, modules);
+ this.module.link(modules);
this.linked = jobs;
} finally {
// Restore it - if it succeeds, we'll reset in the caller; Otherwise it's
diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js
index e2319e169454..266e019be9c3 100644
--- a/lib/internal/vm/module.js
+++ b/lib/internal/vm/module.js
@@ -134,23 +134,10 @@ class Module {
});
}
- let registry = { __proto__: null };
if (sourceText !== undefined) {
this[kWrap] = new ModuleWrap(identifier, context, sourceText,
options.lineOffset, options.columnOffset,
options.cachedData);
- registry = {
- __proto__: null,
- initializeImportMeta: options.initializeImportMeta,
- importModuleDynamically: options.importModuleDynamically ?
- importModuleDynamicallyWrap(options.importModuleDynamically) :
- undefined,
- };
- // This will take precedence over the referrer as the object being
- // passed into the callbacks.
- registry.callbackReferrer = this;
- const { registerModule } = require('internal/modules/esm/utils');
- registerModule(this[kWrap], registry);
} else {
assert(syntheticEvaluationSteps);
this[kWrap] = new ModuleWrap(identifier, context,
@@ -301,6 +288,19 @@ class SourceTextModule extends Module {
importModuleDynamically,
});
+ const registry = {
+ __proto__: null,
+ initializeImportMeta: options.initializeImportMeta,
+ importModuleDynamically: options.importModuleDynamically ?
+ importModuleDynamicallyWrap(options.importModuleDynamically) :
+ undefined,
+ };
+ // This will take precedence over the referrer as the object being
+ // passed into the callbacks.
+ registry.callbackReferrer = this;
+ const { registerModule } = require('internal/modules/esm/utils');
+ registerModule(this[kWrap], registry);
+
this.#moduleRequests = ObjectFreeze(ArrayPrototypeMap(this[kWrap].getModuleRequests(), (request) => {
return ObjectFreeze({
__proto__: null,
@@ -314,8 +314,7 @@ class SourceTextModule extends Module {
this.#statusOverride = 'linking';
// Iterates the module requests and links with the linker.
- // Specifiers should be aligned with the moduleRequests array in order.
- const specifiers = Array(this.#moduleRequests.length);
+ // Modules should be aligned with the moduleRequests array in order.
const modulePromises = Array(this.#moduleRequests.length);
// Iterates with index to avoid calling into userspace with `Symbol.iterator`.
for (let idx = 0; idx < this.#moduleRequests.length; idx++) {
@@ -342,12 +341,11 @@ class SourceTextModule extends Module {
return module[kWrap];
});
modulePromises[idx] = modulePromise;
- specifiers[idx] = specifier;
}
try {
const modules = await SafePromiseAllReturnArrayLike(modulePromises);
- this[kWrap].link(specifiers, modules);
+ this[kWrap].link(modules);
} catch (e) {
this.#error = e;
throw e;
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index c52e20d74294..2bb7eea7aec5 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -62,6 +62,51 @@ using v8::UnboundModuleScript;
using v8::Undefined;
using v8::Value;
+void ModuleCacheKey::MemoryInfo(MemoryTracker* tracker) const {
+ tracker->TrackField("specifier", specifier);
+ tracker->TrackField("import_attributes", import_attributes);
+}
+
+template
+ModuleCacheKey ModuleCacheKey::From(Local context,
+ Local specifier,
+ Local import_attributes) {
+ CHECK_EQ(import_attributes->Length() % elements_per_attribute, 0);
+ Isolate* isolate = context->GetIsolate();
+ std::size_t h1 = specifier->GetIdentityHash();
+ size_t num_attributes = import_attributes->Length() / elements_per_attribute;
+ ImportAttributeVector attributes;
+ attributes.reserve(num_attributes);
+
+ std::size_t h2 = 0;
+
+ for (int i = 0; i < import_attributes->Length();
+ i += elements_per_attribute) {
+ Local v8_key = import_attributes->Get(context, i).As();
+ Local v8_value =
+ import_attributes->Get(context, i + 1).As();
+ Utf8Value key_utf8(isolate, v8_key);
+ Utf8Value value_utf8(isolate, v8_value);
+
+ attributes.emplace_back(key_utf8.ToString(), value_utf8.ToString());
+ h2 ^= v8_key->GetIdentityHash();
+ h2 ^= v8_value->GetIdentityHash();
+ }
+
+ // Combine the hashes using a simple XOR and bit shift to reduce
+ // collisions. Note that the hash does not guarantee uniqueness.
+ std::size_t hash = h1 ^ (h2 << 1);
+
+ Utf8Value utf8_specifier(isolate, specifier);
+ return ModuleCacheKey{utf8_specifier.ToString(), attributes, hash};
+}
+
+ModuleCacheKey ModuleCacheKey::From(Local context,
+ Local v8_request) {
+ return From(
+ context, v8_request->GetSpecifier(), v8_request->GetImportAttributes());
+}
+
ModuleWrap::ModuleWrap(Realm* realm,
Local object,
Local module,
@@ -493,7 +538,7 @@ void ModuleWrap::GetModuleRequests(const FunctionCallbackInfo& args) {
realm, isolate, module->GetModuleRequests()));
}
-// moduleWrap.link(specifiers, moduleWraps)
+// moduleWrap.link(moduleWraps)
void ModuleWrap::Link(const FunctionCallbackInfo& args) {
Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate();
@@ -502,33 +547,28 @@ void ModuleWrap::Link(const FunctionCallbackInfo& args) {
ModuleWrap* dependent;
ASSIGN_OR_RETURN_UNWRAP(&dependent, args.This());
- CHECK_EQ(args.Length(), 2);
+ CHECK_EQ(args.Length(), 1);
- Local specifiers = args[0].As();
- Local modules = args[1].As();
- CHECK_EQ(specifiers->Length(), modules->Length());
+ Local requests =
+ dependent->module_.Get(isolate)->GetModuleRequests();
+ Local modules = args[0].As();
+ CHECK_EQ(modules->Length(), static_cast(requests->Length()));
- std::vector> specifiers_buffer;
- if (FromV8Array(context, specifiers, &specifiers_buffer).IsNothing()) {
- return;
- }
std::vector> modules_buffer;
if (FromV8Array(context, modules, &modules_buffer).IsNothing()) {
return;
}
- for (uint32_t i = 0; i < specifiers->Length(); i++) {
- Local specifier_str =
- specifiers_buffer[i].Get(isolate).As();
+ for (uint32_t i = 0; i < modules_buffer.size(); i++) {
Local module_object = modules_buffer[i].Get(isolate).As();
CHECK(
realm->isolate_data()->module_wrap_constructor_template()->HasInstance(
module_object));
- Utf8Value specifier(isolate, specifier_str);
- dependent->resolve_cache_[specifier.ToString()].Reset(isolate,
- module_object);
+ ModuleCacheKey module_cache_key = ModuleCacheKey::From(
+ context, requests->Get(context, i).As());
+ dependent->resolve_cache_[module_cache_key].Reset(isolate, module_object);
}
}
@@ -872,27 +912,27 @@ MaybeLocal ModuleWrap::ResolveModuleCallback(
return MaybeLocal();
}
- Utf8Value specifier_utf8(isolate, specifier);
- std::string specifier_std(*specifier_utf8, specifier_utf8.length());
+ ModuleCacheKey cache_key =
+ ModuleCacheKey::From(context, specifier, import_attributes);
ModuleWrap* dependent = GetFromModule(env, referrer);
if (dependent == nullptr) {
THROW_ERR_VM_MODULE_LINK_FAILURE(
- env, "request for '%s' is from invalid module", specifier_std);
+ env, "request for '%s' is from invalid module", cache_key.specifier);
return MaybeLocal();
}
- if (dependent->resolve_cache_.count(specifier_std) != 1) {
+ if (dependent->resolve_cache_.count(cache_key) != 1) {
THROW_ERR_VM_MODULE_LINK_FAILURE(
- env, "request for '%s' is not in cache", specifier_std);
+ env, "request for '%s' is not in cache", cache_key.specifier);
return MaybeLocal();
}
Local module_object =
- dependent->resolve_cache_[specifier_std].Get(isolate);
+ dependent->resolve_cache_[cache_key].Get(isolate);
if (module_object.IsEmpty() || !module_object->IsObject()) {
THROW_ERR_VM_MODULE_LINK_FAILURE(
- env, "request for '%s' did not return an object", specifier_std);
+ env, "request for '%s' did not return an object", cache_key.specifier);
return MaybeLocal();
}
diff --git a/src/module_wrap.h b/src/module_wrap.h
index 9363ce73e51c..ef0dcc0f6d16 100644
--- a/src/module_wrap.h
+++ b/src/module_wrap.h
@@ -33,6 +33,56 @@ enum HostDefinedOptions : int {
kLength = 9,
};
+/**
+ * ModuleCacheKey is used to uniquely identify a module request
+ * in the module cache. It is a composition of the module specifier
+ * and the import attributes.
+ */
+struct ModuleCacheKey : public MemoryRetainer {
+ using ImportAttributeVector =
+ std::vector>;
+
+ std::string specifier;
+ ImportAttributeVector import_attributes;
+ // A hash of the specifier, and import attributes.
+ // This does not guarantee uniqueness, but is used to reduce
+ // the number of comparisons needed when checking for equality.
+ std::size_t hash;
+
+ SET_MEMORY_INFO_NAME(ModuleCacheKey)
+ SET_SELF_SIZE(ModuleCacheKey)
+ void MemoryInfo(MemoryTracker* tracker) const override;
+
+ template
+ static ModuleCacheKey From(v8::Local context,
+ v8::Local specifier,
+ v8::Local import_attributes);
+ static ModuleCacheKey From(v8::Local context,
+ v8::Local v8_request);
+
+ struct Hash {
+ std::size_t operator()(const ModuleCacheKey& request) const {
+ return request.hash;
+ }
+ };
+
+ // Equality operator for ModuleCacheKey.
+ bool operator==(const ModuleCacheKey& other) const {
+ // Hash does not provide uniqueness guarantee, so ignore it.
+ return specifier == other.specifier &&
+ import_attributes == other.import_attributes;
+ }
+
+ private:
+ // Use public ModuleCacheKey::From to create instances.
+ ModuleCacheKey(std::string specifier,
+ ImportAttributeVector import_attributes,
+ std::size_t hash)
+ : specifier(specifier),
+ import_attributes(import_attributes),
+ hash(hash) {}
+};
+
class ModuleWrap : public BaseObject {
public:
enum InternalFields {
@@ -134,7 +184,10 @@ class ModuleWrap : public BaseObject {
static ModuleWrap* GetFromModule(node::Environment*, v8::Local);
v8::Global module_;
- std::unordered_map> resolve_cache_;
+ std::unordered_map,
+ ModuleCacheKey::Hash>
+ resolve_cache_;
contextify::ContextifyContext* contextify_context_ = nullptr;
bool synthetic_ = false;
int module_hash_;
diff --git a/test/es-module/test-esm-import-attributes-identity.mjs b/test/es-module/test-esm-import-attributes-identity.mjs
new file mode 100644
index 000000000000..0f5b98ff0466
--- /dev/null
+++ b/test/es-module/test-esm-import-attributes-identity.mjs
@@ -0,0 +1,59 @@
+import '../common/index.mjs';
+import assert from 'node:assert';
+import Module from 'node:module';
+
+// This test verifies that importing two modules with different import
+// attributes should result in different module instances, if the module loader
+// resolves to module instances.
+//
+// For example,
+// ```mjs
+// import * as secret1 from '../primitive-42.json' with { type: 'json' };
+// import * as secret2 from '../primitive-42.json';
+// ```
+// should result in two different module instances, if the second import
+// is been evaluated as a CommonJS module.
+//
+// ECMA-262 requires that in `HostLoadImportedModule`, if the operation is called
+// multiple times with two (referrer, moduleRequest) pairs, it should return the same
+// result. But if the moduleRequest is different, it should return a different,
+// and the module loader resolves to different module instances, it should return
+// different module instances.
+// Refs: https://tc39.es/ecma262/#sec-HostLoadImportedModule
+
+const kJsonModuleSpecifier = '../primitive-42.json';
+const fixtureUrl = new URL('../fixtures/primitive-42.json', import.meta.url).href;
+
+Module.registerHooks({
+ resolve: (specifier, context, nextResolve) => {
+ if (kJsonModuleSpecifier !== specifier) {
+ return nextResolve(specifier, context);
+ }
+ if (context.importAttributes.type === 'json') {
+ return {
+ url: fixtureUrl,
+ // Return a new importAttributes object to ensure
+ // that the module loader treats this as a same module request.
+ importAttributes: {
+ type: 'json',
+ },
+ shortCircuit: true,
+ format: 'json',
+ };
+ }
+ return {
+ url: fixtureUrl,
+ // Return a new importAttributes object to ensure
+ // that the module loader treats this as a same module request.
+ importAttributes: {},
+ shortCircuit: true,
+ format: 'module',
+ };
+ },
+});
+
+const { secretModule, secretJson, secretJson2 } = await import('../fixtures/es-modules/json-modules-attributes.mjs');
+assert.notStrictEqual(secretModule, secretJson);
+assert.strictEqual(secretJson, secretJson2);
+assert.strictEqual(secretJson.default, 42);
+assert.strictEqual(secretModule.default, undefined);
diff --git a/test/es-module/test-esm-json.mjs b/test/es-module/test-esm-json.mjs
index 083194ab4237..b85767803531 100644
--- a/test/es-module/test-esm-json.mjs
+++ b/test/es-module/test-esm-json.mjs
@@ -16,6 +16,11 @@ describe('ESM: importing JSON', () => {
assert.strictEqual(secret.ofLife, 42);
});
+ it('should load JSON with import calls', async () => {
+ const module = await import('../fixtures/experimental.json', { with: { type: 'json' } });
+ assert.strictEqual(module.default.ofLife, 42);
+ });
+
it('should not print an experimental warning', async () => {
const { code, signal, stderr } = await spawnPromisified(execPath, [
fixtures.path('/es-modules/json-modules.mjs'),
diff --git a/test/fixtures/es-module-loaders/hooks-input.mjs b/test/fixtures/es-module-loaders/hooks-input.mjs
index 854b8e619281..e4eb159b3987 100644
--- a/test/fixtures/es-module-loaders/hooks-input.mjs
+++ b/test/fixtures/es-module-loaders/hooks-input.mjs
@@ -30,6 +30,8 @@ export async function resolve(specifier, context, next) {
assert.deepStrictEqual(context.importAttributes, {
type: 'json',
});
+ } else {
+ throw new Error(`Unexpected resolve call: ${specifier}`);
}
// Ensure `context` has all and only the properties it's supposed to
diff --git a/test/fixtures/es-modules/json-modules-attributes.mjs b/test/fixtures/es-modules/json-modules-attributes.mjs
new file mode 100644
index 000000000000..d0eb79f4f5dd
--- /dev/null
+++ b/test/fixtures/es-modules/json-modules-attributes.mjs
@@ -0,0 +1,5 @@
+import * as secretJson from '../primitive-42.json' with { type: 'json' };
+import * as secretModule from '../primitive-42.json';
+import * as secretJson2 from '../primitive-42.json' with { type: 'json' };
+
+export { secretModule, secretJson, secretJson2 };
diff --git a/test/fixtures/primitive-42.json b/test/fixtures/primitive-42.json
new file mode 100644
index 000000000000..d81cc0710eb6
--- /dev/null
+++ b/test/fixtures/primitive-42.json
@@ -0,0 +1 @@
+42
diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js
index 3839338bc2da..760d717c69ef 100644
--- a/test/parallel/test-internal-module-wrap.js
+++ b/test/parallel/test-internal-module-wrap.js
@@ -14,7 +14,7 @@ const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
assert.strictEqual(moduleRequests.length, 1);
assert.strictEqual(moduleRequests[0].specifier, 'bar');
- foo.link(['bar'], [bar]);
+ foo.link([bar]);
foo.instantiate();
assert.strictEqual(await foo.evaluate(-1, false), undefined);
From 7a91282bf9179e322db42b39824ff6d280f5aa3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?0hm=E2=98=98=EF=B8=8F?=
<109351887+0hmX@users.noreply.github.com>
Date: Mon, 18 Aug 2025 01:54:01 +0530
Subject: [PATCH 05/90] src: use simdjson::pad
PR-URL: https://github.com/nodejs/node/pull/59391
Refs: https://github.com/nodejs/node/issues/59389
Reviewed-By: Daniel Lemire
Reviewed-By: Joyee Cheung
Reviewed-By: Luigi Pinca
---
src/node_modules.cc | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/src/node_modules.cc b/src/node_modules.cc
index 55d628f0c5e7..a6ca3ba074d3 100644
--- a/src/node_modules.cc
+++ b/src/node_modules.cc
@@ -102,23 +102,11 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
if (ReadFileSync(&package_config.raw_json, path.data()) < 0) {
return nullptr;
}
- // In some systems, std::string is annotated to generate an
- // AddressSanitizer: container-overflow error when reading beyond the end of
- // the string even when we are still within the capacity of the string.
- // https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
- // https://github.com/nodejs/node/issues/55584
- // The next lines are a workaround to avoid this false positive.
- size_t json_length = package_config.raw_json.size();
- package_config.raw_json.append(simdjson::SIMDJSON_PADDING, ' ');
- simdjson::padded_string_view json_view(package_config.raw_json.data(),
- json_length,
- package_config.raw_json.size());
- // End of workaround
-
simdjson::ondemand::document document;
simdjson::ondemand::object main_object;
simdjson::error_code error =
- binding_data->json_parser.iterate(json_view).get(document);
+ binding_data->json_parser.iterate(simdjson::pad(package_config.raw_json))
+ .get(document);
const auto throw_invalid_package_config = [error_context, path, realm]() {
if (error_context == nullptr) {
From 6021c3ac761497639c9853b90c59c67f39645847 Mon Sep 17 00:00:00 2001
From: Antoine du Hamel
Date: Wed, 10 Sep 2025 22:49:13 +0200
Subject: [PATCH 06/90] tools: copyedit `build-tarball.yml`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PR-URL: https://github.com/nodejs/node/pull/59808
Refs: https://www.shellcheck.net/wiki/SC2006
Refs: https://www.shellcheck.net/wiki/SC2086
Reviewed-By: Tierney Cyren
Reviewed-By: Michaël Zasso
Reviewed-By: Richard Lau
Reviewed-By: Luigi Pinca
---
.github/workflows/build-tarball.yml | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/build-tarball.yml b/.github/workflows/build-tarball.yml
index 7a06ef9230e3..ee544351d32e 100644
--- a/.github/workflows/build-tarball.yml
+++ b/.github/workflows/build-tarball.yml
@@ -51,16 +51,14 @@ jobs:
- name: Make tarball
run: |
export DISTTYPE=nightly
- export DATESTRING=`date "+%Y-%m-%d"`
+ export DATESTRING=$(date "+%Y-%m-%d")
export COMMIT=$(git rev-parse --short=10 "$GITHUB_SHA")
- ./configure && make tar -j8 SKIP_XZ=1
- mkdir tarballs
- mv *.tar.gz tarballs
+ ./configure && make tar -j4 SKIP_XZ=1
- name: Upload tarball artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: tarballs
- path: tarballs
+ path: '*.tar.gz'
compression-level: 0
test-tarball-linux:
needs: build-tarball
@@ -92,11 +90,10 @@ jobs:
path: tarballs
- name: Extract tarball
run: |
- tar xzf tarballs/*.tar.gz -C $RUNNER_TEMP
- echo "TAR_DIR=$RUNNER_TEMP/`basename tarballs/*.tar.gz .tar.gz`" >> $GITHUB_ENV
+ tar xzf tarballs/*.tar.gz -C "$RUNNER_TEMP"
+ echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV"
- name: Build
- run: |
- make -C "$TAR_DIR" build-ci -j4 V=1
+ run: make -C "$TAR_DIR" build-ci -j4 V=1
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -108,5 +105,4 @@ jobs:
mv tools/eslint "$TAR_DIR/tools"
mv tools/eslint-rules "$TAR_DIR/tools"
- name: Test
- run: |
- make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p dots --measure-flakiness 9"
+ run: make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p dots --measure-flakiness 9"
From ec5290fe01c1338c035f2d8a77af9ed2ae926fe2 Mon Sep 17 00:00:00 2001
From: Antoine du Hamel
Date: Mon, 8 Sep 2025 17:22:39 +0200
Subject: [PATCH 07/90] build: do not include custom ESLint rules testing in
tarball
PR-URL: https://github.com/nodejs/node/pull/59809
Reviewed-By: Richard Lau
Reviewed-By: Tierney Cyren
Reviewed-By: Luigi Pinca
---
.github/workflows/build-tarball.yml | 10 ----------
Makefile | 1 +
2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/.github/workflows/build-tarball.yml b/.github/workflows/build-tarball.yml
index ee544351d32e..080e3532c3c0 100644
--- a/.github/workflows/build-tarball.yml
+++ b/.github/workflows/build-tarball.yml
@@ -94,15 +94,5 @@ jobs:
echo "TAR_DIR=$RUNNER_TEMP/$(basename tarballs/*.tar.gz .tar.gz)" >> "$GITHUB_ENV"
- name: Build
run: make -C "$TAR_DIR" build-ci -j4 V=1
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- with:
- persist-credentials: false
- sparse-checkout: |
- tools/eslint
- tools/eslint-rules
- - name: Move directories needed for testing
- run: |
- mv tools/eslint "$TAR_DIR/tools"
- mv tools/eslint-rules "$TAR_DIR/tools"
- name: Test
run: make -C "$TAR_DIR" run-ci -j4 V=1 TEST_CI_ARGS="-p dots --measure-flakiness 9"
diff --git a/Makefile b/Makefile
index 6d173a633266..d54f61a372b7 100644
--- a/Makefile
+++ b/Makefile
@@ -1247,6 +1247,7 @@ $(TARBALL): release-only doc-only
$(RM) -r $(TARNAME)/tools/cpplint.py
$(RM) -r $(TARNAME)/tools/eslint
$(RM) -r $(TARNAME)/tools/eslint-rules
+ $(RM) -r $(TARNAME)/test/parallel/test-eslint-*
$(RM) -r $(TARNAME)/tools/license-builder.sh
$(RM) -r $(TARNAME)/tools/eslint/node_modules
$(RM) -r $(TARNAME)/tools/osx-*
From b632a1d98d11b005dcfdb3e78aba62ad0814897d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Zasso?=
Date: Thu, 11 Sep 2025 09:05:18 +0200
Subject: [PATCH 08/90] tools: skip test-internet workflow for draft PRs
PR-URL: https://github.com/nodejs/node/pull/59817
Reviewed-By: Chengzhong Wu
Reviewed-By: Luigi Pinca
Reviewed-By: Marco Ippolito
Reviewed-By: Antoine du Hamel
Reviewed-By: Moshe Atlow
---
.github/workflows/test-internet.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/test-internet.yml b/.github/workflows/test-internet.yml
index 759bbfee76b5..837703062677 100644
--- a/.github/workflows/test-internet.yml
+++ b/.github/workflows/test-internet.yml
@@ -42,7 +42,7 @@ permissions:
jobs:
test-internet:
- if: github.repository == 'nodejs/node' || github.event_name != 'schedule'
+ if: github.event_name == 'schedule' && github.repository == 'nodejs/node' || github.event.pull_request.draft == false
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
From d2a70571f8a0dd4f9f177df72695e288fbd54afa Mon Sep 17 00:00:00 2001
From: Chengzhong Wu
Date: Tue, 2 Sep 2025 11:31:09 +0100
Subject: [PATCH 09/90] lib,src: refactor assert to load error source from
memory
The source code is available from V8 API and assert can avoid reading
the source file from the filesystem and parse the file again.
PR-URL: https://github.com/nodejs/node/pull/59751
Reviewed-By: Marco Ippolito
---
lib/internal/assert/utils.js | 214 +-----------------
lib/internal/errors/error_source.js | 133 +++++++++++
src/node_errors.cc | 43 ++++
...ssert-builtins-not-read-from-filesystem.js | 48 ----
test/parallel/test-assert.js | 8 +-
5 files changed, 189 insertions(+), 257 deletions(-)
create mode 100644 lib/internal/errors/error_source.js
delete mode 100644 test/parallel/test-assert-builtins-not-read-from-filesystem.js
diff --git a/lib/internal/assert/utils.js b/lib/internal/assert/utils.js
index 13e41d67c635..cf7e45fe375f 100644
--- a/lib/internal/assert/utils.js
+++ b/lib/internal/assert/utils.js
@@ -1,38 +1,21 @@
'use strict';
const {
- ArrayPrototypeShift,
Error,
ErrorCaptureStackTrace,
- FunctionPrototypeBind,
- RegExpPrototypeSymbolReplace,
- SafeMap,
StringPrototypeCharCodeAt,
- StringPrototypeIncludes,
StringPrototypeReplace,
- StringPrototypeSlice,
- StringPrototypeSplit,
- StringPrototypeStartsWith,
} = primordials;
-const { Buffer } = require('buffer');
const {
isErrorStackTraceLimitWritable,
- overrideStackTrace,
} = require('internal/errors');
const AssertionError = require('internal/assert/assertion_error');
-const { openSync, closeSync, readSync } = require('fs');
-const { EOL } = require('internal/constants');
-const { BuiltinModule } = require('internal/bootstrap/realm');
const { isError } = require('internal/util');
-const errorCache = new SafeMap();
-const { fileURLToPath } = require('internal/url');
-
-let parseExpressionAt;
-let findNodeAround;
-let tokenizer;
-let decoder;
+const {
+ getErrorSourceExpression,
+} = require('internal/errors/error_source');
// Escape control characters but not \n and \t to keep the line breaks and
// indentation intact.
@@ -50,111 +33,7 @@ const meta = [
const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)];
-function findColumn(fd, column, code) {
- if (code.length > column + 100) {
- try {
- return parseCode(code, column);
- } catch {
- // End recursion in case no code could be parsed. The expression should
- // have been found after 2500 characters, so stop trying.
- if (code.length - column > 2500) {
- // eslint-disable-next-line no-throw-literal
- throw null;
- }
- }
- }
- // Read up to 2500 bytes more than necessary in columns. That way we address
- // multi byte characters and read enough data to parse the code.
- const bytesToRead = column - code.length + 2500;
- const buffer = Buffer.allocUnsafe(bytesToRead);
- const bytesRead = readSync(fd, buffer, 0, bytesToRead);
- code += decoder.write(buffer.slice(0, bytesRead));
- // EOF: fast path.
- if (bytesRead < bytesToRead) {
- return parseCode(code, column);
- }
- // Read potentially missing code.
- return findColumn(fd, column, code);
-}
-
-function getCode(fd, line, column) {
- let bytesRead = 0;
- if (line === 0) {
- // Special handle line number one. This is more efficient and simplifies the
- // rest of the algorithm. Read more than the regular column number in bytes
- // to prevent multiple reads in case multi byte characters are used.
- return findColumn(fd, column, '');
- }
- let lines = 0;
- // Prevent blocking the event loop by limiting the maximum amount of
- // data that may be read.
- let maxReads = 32; // bytesPerRead * maxReads = 512 KiB
- const bytesPerRead = 16384;
- // Use a single buffer up front that is reused until the call site is found.
- let buffer = Buffer.allocUnsafe(bytesPerRead);
- while (maxReads-- !== 0) {
- // Only allocate a new buffer in case the needed line is found. All data
- // before that can be discarded.
- buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead);
- bytesRead = readSync(fd, buffer, 0, bytesPerRead);
- // Read the buffer until the required code line is found.
- for (let i = 0; i < bytesRead; i++) {
- if (buffer[i] === 10 && ++lines === line) {
- // If the end of file is reached, directly parse the code and return.
- if (bytesRead < bytesPerRead) {
- return parseCode(buffer.toString('utf8', i + 1, bytesRead), column);
- }
- // Check if the read code is sufficient or read more until the whole
- // expression is read. Make sure multi byte characters are preserved
- // properly by using the decoder.
- const code = decoder.write(buffer.slice(i + 1, bytesRead));
- return findColumn(fd, column, code);
- }
- }
- }
-}
-
-function parseCode(code, offset) {
- // Lazy load acorn.
- if (parseExpressionAt === undefined) {
- const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
- ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk'));
-
- parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser);
- tokenizer = FunctionPrototypeBind(Parser.tokenizer, Parser);
- }
- let node;
- let start;
- // Parse the read code until the correct expression is found.
- for (const token of tokenizer(code, { ecmaVersion: 'latest' })) {
- start = token.start;
- if (start > offset) {
- // No matching expression found. This could happen if the assert
- // expression is bigger than the provided buffer.
- break;
- }
- try {
- node = parseExpressionAt(code, start, { ecmaVersion: 'latest' });
- // Find the CallExpression in the tree.
- node = findNodeAround(node, offset, 'CallExpression');
- if (node?.node.end >= offset) {
- return [
- node.node.start,
- StringPrototypeReplace(StringPrototypeSlice(code,
- node.node.start, node.node.end),
- escapeSequencesRegExp, escapeFn),
- ];
- }
- // eslint-disable-next-line no-unused-vars
- } catch (err) {
- continue;
- }
- }
- // eslint-disable-next-line no-throw-literal
- throw null;
-}
-
-function getErrMessage(message, fn) {
+function getErrMessage(fn) {
const tmpLimit = Error.stackTraceLimit;
const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable();
// Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
@@ -166,85 +45,10 @@ function getErrMessage(message, fn) {
ErrorCaptureStackTrace(err, fn);
if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
- overrideStackTrace.set(err, (_, stack) => stack);
- const call = err.stack[0];
-
- let filename = call.getFileName();
- const line = call.getLineNumber() - 1;
- let column = call.getColumnNumber() - 1;
- let identifier;
-
- if (filename) {
- identifier = `${filename}${line}${column}`;
-
- // Skip Node.js modules!
- if (StringPrototypeStartsWith(filename, 'node:') &&
- BuiltinModule.exists(StringPrototypeSlice(filename, 5))) {
- errorCache.set(identifier, undefined);
- return;
- }
- } else {
- return message;
- }
-
- if (errorCache.has(identifier)) {
- return errorCache.get(identifier);
- }
-
- let fd;
- try {
- // Set the stack trace limit to zero. This makes sure unexpected token
- // errors are handled faster.
- if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 0;
-
- if (decoder === undefined) {
- const { StringDecoder } = require('string_decoder');
- decoder = new StringDecoder('utf8');
- }
-
- // ESM file prop is a file proto. Convert that to path.
- // This ensure opensync will not throw ENOENT for ESM files.
- const fileProtoPrefix = 'file://';
- if (StringPrototypeStartsWith(filename, fileProtoPrefix)) {
- filename = fileURLToPath(filename);
- }
-
- fd = openSync(filename, 'r', 0o666);
- // Reset column and message.
- ({ 0: column, 1: message } = getCode(fd, line, column));
- // Flush unfinished multi byte characters.
- decoder.end();
-
- // Always normalize indentation, otherwise the message could look weird.
- if (StringPrototypeIncludes(message, '\n')) {
- if (EOL === '\r\n') {
- message = RegExpPrototypeSymbolReplace(/\r\n/g, message, '\n');
- }
- const frames = StringPrototypeSplit(message, '\n');
- message = ArrayPrototypeShift(frames);
- for (let i = 0; i < frames.length; i++) {
- const frame = frames[i];
- let pos = 0;
- while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
- pos++;
- }
- message += `\n ${StringPrototypeSlice(frame, pos)}`;
- }
- }
- message = `The expression evaluated to a falsy value:\n\n ${message}\n`;
- // Make sure to always set the cache! No matter if the message is
- // undefined or not
- errorCache.set(identifier, message);
-
- return message;
- } catch {
- // Invalidate cache to prevent trying to read this part again.
- errorCache.set(identifier, undefined);
- } finally {
- // Reset limit.
- if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
- if (fd !== undefined)
- closeSync(fd);
+ let source = getErrorSourceExpression(err);
+ if (source) {
+ source = StringPrototypeReplace(source, escapeSequencesRegExp, escapeFn);
+ return `The expression evaluated to a falsy value:\n\n ${source}\n`;
}
}
@@ -257,7 +61,7 @@ function innerOk(fn, argLen, value, message) {
message = 'No value argument passed to `assert.ok()`';
} else if (message == null) {
generatedMessage = true;
- message = getErrMessage(message, fn);
+ message = getErrMessage(fn);
} else if (isError(message)) {
throw message;
}
diff --git a/lib/internal/errors/error_source.js b/lib/internal/errors/error_source.js
new file mode 100644
index 000000000000..9384d4a8361c
--- /dev/null
+++ b/lib/internal/errors/error_source.js
@@ -0,0 +1,133 @@
+'use strict';
+
+const {
+ FunctionPrototypeBind,
+ StringPrototypeSlice,
+} = primordials;
+
+const {
+ getErrorSourcePositions,
+} = internalBinding('errors');
+
+/**
+ * Get the source location of an error.
+ *
+ * The `error.stack` must not have been accessed. The resolution is based on the structured
+ * error stack data.
+ * @param {Error|object} error An error object, or an object being invoked with ErrorCaptureStackTrace
+ * @returns {{sourceLine: string, startColumn: number}|undefined}
+ */
+function getErrorSourceLocation(error) {
+ const pos = getErrorSourcePositions(error);
+ const {
+ sourceLine,
+ startColumn,
+ } = pos;
+
+ return { sourceLine, startColumn };
+}
+
+const memberAccessTokens = [ '.', '?.', '[', ']' ];
+const memberNameTokens = [ 'name', 'string', 'num' ];
+let tokenizer;
+/**
+ * Get the first expression in a code string at the startColumn.
+ * @param {string} code source code line
+ * @param {number} startColumn which column the error is constructed
+ * @returns {string}
+ */
+function getFirstExpression(code, startColumn) {
+ // Lazy load acorn.
+ if (tokenizer === undefined) {
+ const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
+ tokenizer = FunctionPrototypeBind(Parser.tokenizer, Parser);
+ }
+
+ let lastToken;
+ let firstMemberAccessNameToken;
+ let terminatingCol;
+ let parenLvl = 0;
+ // Tokenize the line to locate the expression at the startColumn.
+ // The source line may be an incomplete JavaScript source, so do not parse the source line.
+ for (const token of tokenizer(code, { ecmaVersion: 'latest' })) {
+ // Peek before the startColumn.
+ if (token.start < startColumn) {
+ // There is a semicolon. This is a statement before the startColumn, so reset the memo.
+ if (token.type.label === ';') {
+ firstMemberAccessNameToken = null;
+ continue;
+ }
+ // Try to memo the member access expressions before the startColumn, so that the
+ // returned source code contains more info:
+ // assert.ok(value)
+ // ^ startColumn
+ // The member expression can also be like
+ // assert['ok'](value) or assert?.ok(value)
+ // ^ startColumn ^ startColumn
+ if (memberAccessTokens.includes(token.type.label) && lastToken?.type.label === 'name') {
+ // First member access name token must be a 'name'.
+ firstMemberAccessNameToken ??= lastToken;
+ } else if (!memberAccessTokens.includes(token.type.label) &&
+ !memberNameTokens.includes(token.type.label)) {
+ // Reset the memo if it is not a simple member access.
+ // For example: assert[(() => 'ok')()](value)
+ // ^ startColumn
+ firstMemberAccessNameToken = null;
+ }
+ lastToken = token;
+ continue;
+ }
+ // Now after the startColumn, this must be an expression.
+ if (token.type.label === '(') {
+ parenLvl++;
+ continue;
+ }
+ if (token.type.label === ')') {
+ parenLvl--;
+ if (parenLvl === 0) {
+ // A matched closing parenthesis found after the startColumn,
+ // terminate here. Include the token.
+ // (assert.ok(false), assert.ok(true))
+ // ^ startColumn
+ terminatingCol = token.start + 1;
+ break;
+ }
+ continue;
+ }
+ if (token.type.label === ';') {
+ // A semicolon found after the startColumn, terminate here.
+ // assert.ok(false); assert.ok(true));
+ // ^ startColumn
+ terminatingCol = token;
+ break;
+ }
+ // If no semicolon found after the startColumn. The string after the
+ // startColumn must be the expression.
+ // assert.ok(false)
+ // ^ startColumn
+ }
+ const start = firstMemberAccessNameToken?.start ?? startColumn;
+ return StringPrototypeSlice(code, start, terminatingCol);
+}
+
+/**
+ * Get the source expression of an error.
+ *
+ * The `error.stack` must not have been accessed, or the source location may be incorrect. The
+ * resolution is based on the structured error stack data.
+ * @param {Error|object} error An error object, or an object being invoked with ErrorCaptureStackTrace
+ * @returns {string|undefined}
+ */
+function getErrorSourceExpression(error) {
+ const loc = getErrorSourceLocation(error);
+ if (loc === undefined) {
+ return;
+ }
+ const { sourceLine, startColumn } = loc;
+ return getFirstExpression(sourceLine, startColumn);
+}
+
+module.exports = {
+ getErrorSourceLocation,
+ getErrorSourceExpression,
+};
diff --git a/src/node_errors.cc b/src/node_errors.cc
index 5f51add4cdf6..ae8553ee2022 100644
--- a/src/node_errors.cc
+++ b/src/node_errors.cc
@@ -1041,6 +1041,46 @@ void PerIsolateMessageListener(Local message, Local error) {
}
}
+void GetErrorSourcePositions(const FunctionCallbackInfo& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope scope(isolate);
+ Realm* realm = Realm::GetCurrent(args);
+ Local context = realm->context();
+
+ CHECK(args[0]->IsObject());
+
+ Local msg = Exception::CreateMessage(isolate, args[0]);
+
+ // Message::GetEndColumn may not reflect the actual end column in all cases.
+ // So only expose startColumn to JS land.
+ Local names[] = {
+ OneByteString(isolate, "sourceLine"),
+ OneByteString(isolate, "scriptResourceName"),
+ OneByteString(isolate, "lineNumber"),
+ OneByteString(isolate, "startColumn"),
+ };
+
+ Local source_line;
+ if (!msg->GetSourceLine(context).ToLocal(&source_line)) {
+ return;
+ }
+ int line_number;
+ if (!msg->GetLineNumber(context).To(&line_number)) {
+ return;
+ }
+
+ Local values[] = {
+ source_line,
+ msg->GetScriptOrigin().ResourceName(),
+ v8::Integer::New(isolate, line_number),
+ v8::Integer::New(isolate, msg->GetStartColumn()),
+ };
+ Local info =
+ Object::New(isolate, v8::Null(isolate), names, values, arraysize(names));
+
+ args.GetReturnValue().Set(info);
+}
+
void SetPrepareStackTraceCallback(const FunctionCallbackInfo& args) {
Realm* realm = Realm::GetCurrent(args);
CHECK(args[0]->IsFunction());
@@ -1106,6 +1146,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(SetEnhanceStackForFatalException);
registry->Register(NoSideEffectsToString);
registry->Register(TriggerUncaughtException);
+ registry->Register(GetErrorSourcePositions);
}
void Initialize(Local target,
@@ -1133,6 +1174,8 @@ void Initialize(Local target,
context, target, "noSideEffectsToString", NoSideEffectsToString);
SetMethod(
context, target, "triggerUncaughtException", TriggerUncaughtException);
+ SetMethod(
+ context, target, "getErrorSourcePositions", GetErrorSourcePositions);
Isolate* isolate = context->GetIsolate();
Local exit_codes = Object::New(isolate);
diff --git a/test/parallel/test-assert-builtins-not-read-from-filesystem.js b/test/parallel/test-assert-builtins-not-read-from-filesystem.js
deleted file mode 100644
index 7a713a2ea432..000000000000
--- a/test/parallel/test-assert-builtins-not-read-from-filesystem.js
+++ /dev/null
@@ -1,48 +0,0 @@
-'use strict';
-
-// Do not read filesystem when creating AssertionError messages for code in
-// builtin modules.
-
-require('../common');
-const assert = require('assert');
-const EventEmitter = require('events');
-const e = new EventEmitter();
-e.on('hello', assert);
-
-if (process.argv[2] !== 'child') {
- const tmpdir = require('../common/tmpdir');
- tmpdir.refresh();
- const { spawnSync } = require('child_process');
-
- let threw = false;
- try {
- e.emit('hello', false);
- } catch (err) {
- const frames = err.stack.split('\n');
- const [, filename, line, column] = frames[1].match(/\((.+):(\d+):(\d+)\)/);
- // Spawn a child process to avoid the error having been cached in the assert
- // module's `errorCache` Map.
-
- const { output, status, error } =
- spawnSync(process.execPath,
- [process.argv[1], 'child', filename, line, column],
- { cwd: tmpdir.path, env: process.env });
- assert.ifError(error);
- assert.strictEqual(status, 0, `Exit code: ${status}\n${output}`);
- threw = true;
- }
- assert.ok(threw);
-} else {
- const { writeFileSync } = require('fs');
- const [, , , filename, line, column] = process.argv;
- const data = `${'\n'.repeat(line - 1)}${' '.repeat(column - 1)}` +
- 'ok(failed(badly));';
-
- writeFileSync(filename, data);
- assert.throws(
- () => e.emit('hello', false),
- {
- message: 'false == true'
- }
- );
-}
diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js
index 0c3571a80c10..916bbf156547 100644
--- a/test/parallel/test-assert.js
+++ b/test/parallel/test-assert.js
@@ -624,7 +624,7 @@ test('Test strict assert', () => {
code: 'ERR_ASSERTION',
constructor: strict.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
- "strict.ok(\n typeof 123 === 'string'\n )\n"
+ 'strict.ok(\n'
}
);
Error.stackTraceLimit = tmpLimit;
@@ -1017,20 +1017,20 @@ test('Additional asserts', () => {
}
);
- // Works in eval.
+ // Works in eval. Source code in eval can be shown.
assert.throws(
() => new Function('assert', 'assert(1 === 2);')(assert),
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
- message: 'false == true'
+ message: 'The expression evaluated to a falsy value:\n\n assert(1 === 2)\n'
}
);
assert.throws(
() => eval('console.log("FOO");\nassert.ok(1 === 2);'),
{
code: 'ERR_ASSERTION',
- message: 'false == true'
+ message: 'The expression evaluated to a falsy value:\n\n assert.ok(1 === 2)\n'
}
);
From 12e553529cf7af10383799caf068545ab995e174 Mon Sep 17 00:00:00 2001
From: Chengzhong Wu
Date: Wed, 3 Sep 2025 21:49:02 +0100
Subject: [PATCH 10/90] lib: add source map support for assert messages
Map source lines in assert messages with cached source maps.
PR-URL: https://github.com/nodejs/node/pull/59751
Reviewed-By: Marco Ippolito
---
lib/internal/errors/error_source.js | 38 +++++++++++-
.../source_map/prepare_stack_trace.js | 52 ++--------------
lib/internal/source_map/source_map_cache.js | 61 ++++++++++++++++++-
.../source_map_assert_source_line.snapshot | 20 ++++++
.../output/source_map_assert_source_line.ts | 14 +++++
.../source_map_enclosing_function.snapshot | 1 -
.../output/source_map_eval.snapshot | 1 -
.../source_map_reference_error_tabs.snapshot | 1 -
...ource_map_throw_async_stack_trace.snapshot | 1 -
.../source_map_throw_construct.snapshot | 1 -
.../source_map_throw_first_tick.snapshot | 1 -
.../output/source_map_throw_icu.snapshot | 1 -
.../source_map_throw_set_immediate.snapshot | 1 -
test/parallel/test-node-output-sourcemaps.mjs | 1 +
14 files changed, 135 insertions(+), 59 deletions(-)
create mode 100644 test/fixtures/source-map/output/source_map_assert_source_line.snapshot
create mode 100644 test/fixtures/source-map/output/source_map_assert_source_line.ts
diff --git a/lib/internal/errors/error_source.js b/lib/internal/errors/error_source.js
index 9384d4a8361c..eddd6af23080 100644
--- a/lib/internal/errors/error_source.js
+++ b/lib/internal/errors/error_source.js
@@ -8,9 +8,15 @@ const {
const {
getErrorSourcePositions,
} = internalBinding('errors');
+const {
+ getSourceMapsSupport,
+ findSourceMap,
+ getSourceLine,
+} = require('internal/source_map/source_map_cache');
/**
- * Get the source location of an error.
+ * Get the source location of an error. If source map is enabled, resolve the source location
+ * based on the source map.
*
* The `error.stack` must not have been accessed. The resolution is based on the structured
* error stack data.
@@ -21,10 +27,35 @@ function getErrorSourceLocation(error) {
const pos = getErrorSourcePositions(error);
const {
sourceLine,
+ scriptResourceName,
+ lineNumber,
startColumn,
} = pos;
- return { sourceLine, startColumn };
+ // Source map is not enabled. Return the source line directly.
+ if (!getSourceMapsSupport().enabled) {
+ return { sourceLine, startColumn };
+ }
+
+ const sm = findSourceMap(scriptResourceName);
+ if (sm === undefined) {
+ return;
+ }
+ const {
+ originalLine,
+ originalColumn,
+ originalSource,
+ } = sm.findEntry(lineNumber - 1, startColumn);
+ const originalSourceLine = getSourceLine(sm, originalSource, originalLine, originalColumn);
+
+ if (!originalSourceLine) {
+ return;
+ }
+
+ return {
+ sourceLine: originalSourceLine,
+ startColumn: originalColumn,
+ };
}
const memberAccessTokens = [ '.', '?.', '[', ']' ];
@@ -111,7 +142,8 @@ function getFirstExpression(code, startColumn) {
}
/**
- * Get the source expression of an error.
+ * Get the source expression of an error. If source map is enabled, resolve the source location
+ * based on the source map.
*
* The `error.stack` must not have been accessed, or the source location may be incorrect. The
* resolution is based on the structured error stack data.
diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js
index 814ea396f601..da2f9dcd74b2 100644
--- a/lib/internal/source_map/prepare_stack_trace.js
+++ b/lib/internal/source_map/prepare_stack_trace.js
@@ -1,11 +1,9 @@
'use strict';
const {
- ArrayPrototypeIndexOf,
ArrayPrototypeJoin,
ArrayPrototypeMap,
ErrorPrototypeToString,
- RegExpPrototypeSymbolSplit,
SafeStringIterator,
StringPrototypeRepeat,
StringPrototypeSlice,
@@ -16,8 +14,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
debug = fn;
});
const { getStringWidth } = require('internal/util/inspect');
-const { readFileSync } = require('fs');
-const { findSourceMap } = require('internal/source_map/source_map_cache');
+const { findSourceMap, getSourceLine } = require('internal/source_map/source_map_cache');
const {
kIsNodeError,
} = require('internal/errors');
@@ -155,21 +152,13 @@ function getErrorSource(
originalLine,
originalColumn,
) {
- const originalSourcePathNoScheme =
- StringPrototypeStartsWith(originalSourcePath, 'file://') ?
- fileURLToPath(originalSourcePath) : originalSourcePath;
- const source = getOriginalSource(
- sourceMap.payload,
- originalSourcePath,
- );
- if (typeof source !== 'string') {
- return;
- }
- const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1);
- const line = lines[originalLine];
+ const line = getSourceLine(sourceMap, originalSourcePath, originalLine);
if (!line) {
return;
}
+ const originalSourcePathNoScheme =
+ StringPrototypeStartsWith(originalSourcePath, 'file://') ?
+ fileURLToPath(originalSourcePath) : originalSourcePath;
// Display ^ in appropriate position, regardless of whether tabs or
// spaces are used:
@@ -182,39 +171,10 @@ function getErrorSource(
prefix = StringPrototypeSlice(prefix, 0, -1); // The last character is '^'.
const exceptionLine =
- `${originalSourcePathNoScheme}:${originalLine + 1}\n${line}\n${prefix}^\n\n`;
+ `${originalSourcePathNoScheme}:${originalLine + 1}\n${line}\n${prefix}^\n`;
return exceptionLine;
}
-/**
- * Retrieve the original source code from the source map's `sources` list or disk.
- * @param {import('internal/source_map/source_map').SourceMap.payload} payload
- * @param {string} originalSourcePath - path or url of the original source
- * @returns {string | undefined} - the source content or undefined if file not found
- */
-function getOriginalSource(payload, originalSourcePath) {
- let source;
- // payload.sources has been normalized to be an array of absolute urls.
- const sourceContentIndex =
- ArrayPrototypeIndexOf(payload.sources, originalSourcePath);
- if (payload.sourcesContent?.[sourceContentIndex]) {
- // First we check if the original source content was provided in the
- // source map itself:
- source = payload.sourcesContent[sourceContentIndex];
- } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) {
- // If no sourcesContent was found, attempt to load the original source
- // from disk:
- debug(`read source of ${originalSourcePath} from filesystem`);
- const originalSourcePathNoScheme = fileURLToPath(originalSourcePath);
- try {
- source = readFileSync(originalSourcePathNoScheme, 'utf8');
- } catch (err) {
- debug(err);
- }
- }
- return source;
-}
-
/**
* Retrieve exact line in the original source code from the source map's `sources` list or disk.
* @param {string} fileName - actual file name
diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js
index 603685290ad8..caef82e37f7a 100644
--- a/lib/internal/source_map/source_map_cache.js
+++ b/lib/internal/source_map/source_map_cache.js
@@ -1,13 +1,16 @@
'use strict';
const {
+ ArrayPrototypeIndexOf,
ArrayPrototypePush,
JSONParse,
ObjectFreeze,
RegExpPrototypeExec,
+ RegExpPrototypeSymbolSplit,
SafeMap,
StringPrototypeCodePointAt,
StringPrototypeSplit,
+ StringPrototypeStartsWith,
} = primordials;
// See https://tc39.es/ecma426/ for SourceMap V3 specification.
@@ -16,6 +19,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
debug = fn;
});
+const { readFileSync } = require('fs');
const { validateBoolean, validateObject } = require('internal/validators');
const {
setSourceMapsEnabled: setSourceMapsNative,
@@ -277,8 +281,7 @@ function lineLengths(content) {
*/
function sourceMapFromFile(mapURL) {
try {
- const fs = require('fs');
- const content = fs.readFileSync(fileURLToPath(mapURL), 'utf8');
+ const content = readFileSync(fileURLToPath(mapURL), 'utf8');
const data = JSONParse(content);
return sourcesToAbsolute(mapURL, data);
} catch (err) {
@@ -400,8 +403,62 @@ function findSourceMap(sourceURL) {
}
}
+/**
+ * Retrieve the original source code from the source map's `sources` list or disk.
+ * @param {import('internal/source_map/source_map').SourceMap.payload} payload
+ * @param {string} originalSourcePath - path or url of the original source
+ * @returns {string | undefined} - the source content or undefined if file not found
+ */
+function getOriginalSource(payload, originalSourcePath) {
+ let source;
+ // payload.sources has been normalized to be an array of absolute urls.
+ const sourceContentIndex =
+ ArrayPrototypeIndexOf(payload.sources, originalSourcePath);
+ if (payload.sourcesContent?.[sourceContentIndex]) {
+ // First we check if the original source content was provided in the
+ // source map itself:
+ source = payload.sourcesContent[sourceContentIndex];
+ } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) {
+ // If no sourcesContent was found, attempt to load the original source
+ // from disk:
+ debug(`read source of ${originalSourcePath} from filesystem`);
+ const originalSourcePathNoScheme = fileURLToPath(originalSourcePath);
+ try {
+ source = readFileSync(originalSourcePathNoScheme, 'utf8');
+ } catch (err) {
+ debug(err);
+ }
+ }
+ return source;
+}
+
+/**
+ * Get the line of source in the source map.
+ * @param {import('internal/source_map/source_map').SourceMap} sourceMap
+ * @param {string} originalSourcePath path or url of the original source
+ * @param {number} originalLine line number in the original source
+ * @returns {string|undefined} source line if found
+ */
+function getSourceLine(
+ sourceMap,
+ originalSourcePath,
+ originalLine,
+) {
+ const source = getOriginalSource(
+ sourceMap.payload,
+ originalSourcePath,
+ );
+ if (typeof source !== 'string') {
+ return;
+ }
+ const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1);
+ const line = lines[originalLine];
+ return line;
+}
+
module.exports = {
findSourceMap,
+ getSourceLine,
getSourceMapsSupport,
setSourceMapsSupport,
maybeCacheSourceMap,
diff --git a/test/fixtures/source-map/output/source_map_assert_source_line.snapshot b/test/fixtures/source-map/output/source_map_assert_source_line.snapshot
new file mode 100644
index 000000000000..fe11794e9c03
--- /dev/null
+++ b/test/fixtures/source-map/output/source_map_assert_source_line.snapshot
@@ -0,0 +1,20 @@
+AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
+
+ assert(false)
+
+ at Object. (*/test/fixtures/source-map/output/source_map_assert_source_line.ts:11:3)
+ *
+ *
+ *
+ *
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
+ *
+ *
+ *
+ generatedMessage: true,
+ code: 'ERR_ASSERTION',
+ actual: false,
+ expected: true,
+ operator: '==',
+ diff: 'simple'
+}
diff --git a/test/fixtures/source-map/output/source_map_assert_source_line.ts b/test/fixtures/source-map/output/source_map_assert_source_line.ts
new file mode 100644
index 000000000000..f4d242aa5082
--- /dev/null
+++ b/test/fixtures/source-map/output/source_map_assert_source_line.ts
@@ -0,0 +1,14 @@
+// Flags: --enable-source-maps --experimental-transform-types --no-warnings
+
+require('../../../common');
+const assert = require('node:assert');
+
+enum Bar {
+ makeSureTransformTypes,
+}
+
+try {
+ assert(false);
+} catch (e) {
+ console.log(e);
+}
diff --git a/test/fixtures/source-map/output/source_map_enclosing_function.snapshot b/test/fixtures/source-map/output/source_map_enclosing_function.snapshot
index b60f988be321..a215a1ab9c0d 100644
--- a/test/fixtures/source-map/output/source_map_enclosing_function.snapshot
+++ b/test/fixtures/source-map/output/source_map_enclosing_function.snapshot
@@ -2,7 +2,6 @@
throw err
^
-
Error: an error!
at functionD (*/test/fixtures/source-map/enclosing-call-site.js:16:17)
at functionC (*/test/fixtures/source-map/enclosing-call-site.js:10:3)
diff --git a/test/fixtures/source-map/output/source_map_eval.snapshot b/test/fixtures/source-map/output/source_map_eval.snapshot
index ff636e44063a..c808fb414473 100644
--- a/test/fixtures/source-map/output/source_map_eval.snapshot
+++ b/test/fixtures/source-map/output/source_map_eval.snapshot
@@ -2,7 +2,6 @@
alert "I knew it!"
^
-
ReferenceError: alert is not defined
at Object.eval (*/synthesized/workspace/tabs-source-url.coffee:26:2)
at eval (*/synthesized/workspace/tabs-source-url.coffee:1:14)
diff --git a/test/fixtures/source-map/output/source_map_reference_error_tabs.snapshot b/test/fixtures/source-map/output/source_map_reference_error_tabs.snapshot
index 2043bd0a88e8..fce296c1d568 100644
--- a/test/fixtures/source-map/output/source_map_reference_error_tabs.snapshot
+++ b/test/fixtures/source-map/output/source_map_reference_error_tabs.snapshot
@@ -2,7 +2,6 @@
alert "I knew it!"
^
-
ReferenceError: alert is not defined
at Object. (*/test/fixtures/source-map/tabs.coffee:26:2)
at Object. (*/test/fixtures/source-map/tabs.coffee:1:14)
diff --git a/test/fixtures/source-map/output/source_map_throw_async_stack_trace.snapshot b/test/fixtures/source-map/output/source_map_throw_async_stack_trace.snapshot
index f53aec68ce8b..2c9bd70cf4d4 100644
--- a/test/fixtures/source-map/output/source_map_throw_async_stack_trace.snapshot
+++ b/test/fixtures/source-map/output/source_map_throw_async_stack_trace.snapshot
@@ -2,7 +2,6 @@
throw new Error('message')
^
-
Error: message
at Throw (*/test/fixtures/source-map/output/source_map_throw_async_stack_trace.mts:13:9)
at async Promise.all (index 3)
diff --git a/test/fixtures/source-map/output/source_map_throw_construct.snapshot b/test/fixtures/source-map/output/source_map_throw_construct.snapshot
index dc28053240aa..292eb0b2eac3 100644
--- a/test/fixtures/source-map/output/source_map_throw_construct.snapshot
+++ b/test/fixtures/source-map/output/source_map_throw_construct.snapshot
@@ -2,7 +2,6 @@
throw new Error('message');
^
-
Error: message
at new Foo (*/test/fixtures/source-map/output/source_map_throw_construct.mts:13:11)
at (*/test/fixtures/source-map/output/source_map_throw_construct.mts:17:1)
diff --git a/test/fixtures/source-map/output/source_map_throw_first_tick.snapshot b/test/fixtures/source-map/output/source_map_throw_first_tick.snapshot
index e129d73ef158..29346a818361 100644
--- a/test/fixtures/source-map/output/source_map_throw_first_tick.snapshot
+++ b/test/fixtures/source-map/output/source_map_throw_first_tick.snapshot
@@ -3,7 +3,6 @@ reachable
throw Error('an exception');
^
-
Error: an exception
at branch (*/test/fixtures/source-map/typescript-throw.ts:18:11)
at Object. (*/test/fixtures/source-map/typescript-throw.ts:24:1)
diff --git a/test/fixtures/source-map/output/source_map_throw_icu.snapshot b/test/fixtures/source-map/output/source_map_throw_icu.snapshot
index 4b2853479b95..8b556a584fec 100644
--- a/test/fixtures/source-map/output/source_map_throw_icu.snapshot
+++ b/test/fixtures/source-map/output/source_map_throw_icu.snapshot
@@ -2,7 +2,6 @@
("あ 🐕 🐕", throw Error("an error"));
^
-
Error: an error
at Object.createElement (*/test/fixtures/source-map/icu.jsx:3:23)
at Object. (*/test/fixtures/source-map/icu.jsx:9:5)
diff --git a/test/fixtures/source-map/output/source_map_throw_set_immediate.snapshot b/test/fixtures/source-map/output/source_map_throw_set_immediate.snapshot
index 4cf4d52a16ea..053ed5692d6a 100644
--- a/test/fixtures/source-map/output/source_map_throw_set_immediate.snapshot
+++ b/test/fixtures/source-map/output/source_map_throw_set_immediate.snapshot
@@ -2,7 +2,6 @@
throw Error('goodbye');
^
-
Error: goodbye
at Hello (*/test/fixtures/source-map/uglify-throw-original.js:5:9)
at Immediate. (*/test/fixtures/source-map/uglify-throw-original.js:9:3)
diff --git a/test/parallel/test-node-output-sourcemaps.mjs b/test/parallel/test-node-output-sourcemaps.mjs
index c11c2c36735d..2d0e784e206b 100644
--- a/test/parallel/test-node-output-sourcemaps.mjs
+++ b/test/parallel/test-node-output-sourcemaps.mjs
@@ -14,6 +14,7 @@ describe('sourcemaps output', { concurrency: !process.env.TEST_PARALLEL }, () =>
);
const tests = [
+ { name: 'source-map/output/source_map_assert_source_line.ts' },
{ name: 'source-map/output/source_map_disabled_by_api.js' },
{ name: 'source-map/output/source_map_disabled_by_process_api.js' },
{ name: 'source-map/output/source_map_enabled_by_api.js' },
From bb6fd7c2d1fe6efdfd212a0162effc1188f1ea61 Mon Sep 17 00:00:00 2001
From: Anna Henningsen
Date: Thu, 11 Sep 2025 16:07:36 +0200
Subject: [PATCH 11/90] src: ensure `v8::Eternal` is empty before setting it
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
V8 does not check this for us, but this is a requirement of the API.
PR-URL: https://github.com/nodejs/node/pull/59825
Reviewed-By: James M Snell
Reviewed-By: Chengzhong Wu
Reviewed-By: Juan José Arboleda
---
src/env-inl.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/env-inl.h b/src/env-inl.h
index 67b4cc2037b8..da2c468f11cd 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -873,6 +873,7 @@ void Environment::set_process_exit_handler(
return PropertyName##_.Get(isolate_); \
} \
inline void IsolateData::set_##PropertyName(v8::Local value) { \
+ CHECK(PropertyName##_.IsEmpty()); \
PropertyName##_.Set(isolate_, value); \
}
PER_ISOLATE_TEMPLATE_PROPERTIES(V)
From 9833dc606019a74b52fc9cbbed7ee0912f010edd Mon Sep 17 00:00:00 2001
From: Nam Yooseong <102887277+meteorqz6@users.noreply.github.com>
Date: Fri, 12 Sep 2025 15:40:24 +0900
Subject: [PATCH 12/90] doc: rephrase dynamic import() description
The description is updated to clarify that dynamic import() is
asynchronous, dynamic, and works in both CJS and ESM contexts.
The new phrasing also avoids implying it is the only method for
loading ES modules in CommonJS.
Fixes: https://github.com/nodejs/node/issues/59077
PR-URL: https://github.com/nodejs/node/pull/59224
Reviewed-By: Antoine du Hamel
Reviewed-By: Matteo Collina
---
doc/api/esm.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/doc/api/esm.md b/doc/api/esm.md
index accf7830d914..91bdf8d2d7ce 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -331,8 +331,9 @@ fs.readFileSync === readFileSync;
## `import()` expressions
-[Dynamic `import()`][] is supported in both CommonJS and ES modules. In CommonJS
-modules it can be used to load ES modules.
+[Dynamic `import()`][] provides an asynchronous way to import modules. It is
+supported in both CommonJS and ES modules, and can be used to load both CommonJS
+and ES modules.
## `import.meta`
From 24c224960c4becf8fdc5ea5ccfbe6207b90dbb63 Mon Sep 17 00:00:00 2001
From: simon-id
Date: Fri, 12 Sep 2025 14:59:26 +0200
Subject: [PATCH 13/90] url: add type checking to urlToHttpOptions()
PR-URL: https://github.com/nodejs/node/pull/59753
Reviewed-By: James M Snell
Reviewed-By: Ruben Bridgewater
Reviewed-By: Bryan English
---
lib/internal/url.js | 3 +++
test/parallel/test-url-urltooptions.js | 9 +++++++++
2 files changed, 12 insertions(+)
diff --git a/lib/internal/url.js b/lib/internal/url.js
index d0c04be7c6eb..ad1c2c996608 100644
--- a/lib/internal/url.js
+++ b/lib/internal/url.js
@@ -90,6 +90,8 @@ const { Buffer } = require('buffer');
const {
validateFunction,
+ validateObject,
+ kValidateObjectAllowObjects,
} = require('internal/validators');
const { percentDecode } = require('internal/data_url');
@@ -1430,6 +1432,7 @@ function domainToUnicode(domain) {
* @returns {Record}
*/
function urlToHttpOptions(url) {
+ validateObject(url, 'url', kValidateObjectAllowObjects);
const { hostname, pathname, port, username, password, search } = url;
const options = {
__proto__: null,
diff --git a/test/parallel/test-url-urltooptions.js b/test/parallel/test-url-urltooptions.js
index cc4838eeecb0..8e7e5dc89409 100644
--- a/test/parallel/test-url-urltooptions.js
+++ b/test/parallel/test-url-urltooptions.js
@@ -35,3 +35,12 @@ assert.strictEqual(copiedOpts.pathname, undefined);
assert.strictEqual(copiedOpts.search, undefined);
assert.strictEqual(copiedOpts.hash, undefined);
assert.strictEqual(copiedOpts.href, undefined);
+
+// Test when passed an invalid argument
+assert.throws(() => {
+ urlToHttpOptions('http://127.0.0.1');
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "url" argument must be of type object. Received type string (\'http://127.0.0.1\')',
+ name: 'TypeError'
+});
From 16c4b466f47e550637f030a2178470375ddadb3e Mon Sep 17 00:00:00 2001
From: Nam Yooseong <102887277+meteorqz6@users.noreply.github.com>
Date: Fri, 12 Sep 2025 21:59:37 +0900
Subject: [PATCH 14/90] benchmark: calibrate config cluster/echo.js
PR-URL: https://github.com/nodejs/node/pull/59836
Reviewed-By: Rafael Gonzaga
Reviewed-By: Daeyeon Jeong
---
benchmark/cluster/echo.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/benchmark/cluster/echo.js b/benchmark/cluster/echo.js
index 71ded75c9d25..a3f103bc0b89 100644
--- a/benchmark/cluster/echo.js
+++ b/benchmark/cluster/echo.js
@@ -8,7 +8,7 @@ if (cluster.isPrimary) {
payload: ['string', 'object'],
sendsPerBroadcast: [1, 10],
serialization: ['json', 'advanced'],
- n: [1e5],
+ n: [1e3],
});
function main({
From 4cc84c96f46b7227e4672f2a88d26603bd6496f2 Mon Sep 17 00:00:00 2001
From: Jeetu Suthar <129197623+JeetuSuthar@users.noreply.github.com>
Date: Fri, 12 Sep 2025 21:28:57 +0530
Subject: [PATCH 15/90] node-api: make napi_delete_reference use
node_api_basic_env
PR-URL: https://github.com/nodejs/node/pull/59684
Refs: https://github.com/nodejs/node/issues/59583
Reviewed-By: Chengzhong Wu
Reviewed-By: Vladimir Morozov
---
src/js_native_api.h | 2 +-
src/js_native_api_v8.cc | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/js_native_api.h b/src/js_native_api.h
index 8ef079b51582..d2711fcb9ae7 100644
--- a/src/js_native_api.h
+++ b/src/js_native_api.h
@@ -352,7 +352,7 @@ napi_create_reference(napi_env env,
// Deletes a reference. The referenced value is released, and may
// be GC'd unless there are other references to it.
-NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(napi_env env,
+NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env,
napi_ref ref);
// Increments the reference count, optionally returning the resulting count.
diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc
index de3d1f2f1832..9ed7df2afd8c 100644
--- a/src/js_native_api_v8.cc
+++ b/src/js_native_api_v8.cc
@@ -2771,7 +2771,8 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
// there are other references to it.
// For a napi_reference returned from `napi_wrap`, this must be called in the
// finalizer.
-napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
+napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env,
+ napi_ref ref) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV(env);
From 4d748add05626cb3e3d421c34694dcd5a70bea21 Mon Sep 17 00:00:00 2001
From: Anna Henningsen
Date: Mon, 4 Aug 2025 18:08:15 +0200
Subject: [PATCH 16/90] src: remove `std::array` overload of
`FIXED_ONE_BYTE_STRING`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This overload was only used in one place, in a cold path, and in
particular in a place where the compiler would be able to generate
the exact same code using just a call to `.size()`.
PR-URL: https://github.com/nodejs/node/pull/59826
Reviewed-By: James M Snell
Reviewed-By: Juan José Arboleda
Reviewed-By: Gerhard Stöbich
Reviewed-By: Chengzhong Wu
---
src/node_os.cc | 2 +-
src/util.h | 8 --------
2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/node_os.cc b/src/node_os.cc
index 39edad8f6b3f..54ef253b0b73 100644
--- a/src/node_os.cc
+++ b/src/node_os.cc
@@ -260,7 +260,7 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo& args) {
result.emplace_back(OneByteString(isolate, ip));
result.emplace_back(OneByteString(isolate, netmask));
result.emplace_back(family);
- result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac));
+ result.emplace_back(OneByteString(isolate, mac.data(), mac.size() - 1));
result.emplace_back(
Boolean::New(env->isolate(), interfaces[i].is_internal));
if (interfaces[i].address.address4.sin_family == AF_INET6) {
diff --git a/src/util.h b/src/util.h
index dcd6548d41be..587b8cf0dd8e 100644
--- a/src/util.h
+++ b/src/util.h
@@ -355,14 +355,6 @@ inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate,
return OneByteString(isolate, data, N - 1);
}
-template
- requires(N > 0)
-inline v8::Local FIXED_ONE_BYTE_STRING(
- v8::Isolate* isolate, const std::array& arr) {
- CHECK_EQ(arr[N - 1], '\0');
- return OneByteString(isolate, arr.data(), N - 1);
-}
-
// tolower() is locale-sensitive. Use ToLower() instead.
inline char ToLower(char c);
inline std::string ToLower(const std::string& in);
From 34dcb7dc321de1d11ae062ad426d4365ec997e6e Mon Sep 17 00:00:00 2001
From: Anna Henningsen
Date: Mon, 4 Aug 2025 18:58:33 +0200
Subject: [PATCH 17/90] src: create strings in `FIXED_ONE_BYTE_STRING` as
internalized
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These string keys can generally be assumed to be long-lived.
PR-URL: https://github.com/nodejs/node/pull/59826
Reviewed-By: James M Snell
Reviewed-By: Juan José Arboleda
Reviewed-By: Gerhard Stöbich
Reviewed-By: Chengzhong Wu
---
src/util-inl.h | 31 ++++++++++++++++---------------
src/util.h | 32 ++++++++++++++++++++------------
2 files changed, 36 insertions(+), 27 deletions(-)
diff --git a/src/util-inl.h b/src/util-inl.h
index b21f7a8260ca..eb097bcb00aa 100644
--- a/src/util-inl.h
+++ b/src/util-inl.h
@@ -159,33 +159,34 @@ constexpr ContainerOfHelper ContainerOf(Inner Outer::*field,
inline v8::Local OneByteString(v8::Isolate* isolate,
const char* data,
- int length) {
- return v8::String::NewFromOneByte(isolate,
- reinterpret_cast(data),
- v8::NewStringType::kNormal,
- length).ToLocalChecked();
+ int length,
+ v8::NewStringType type) {
+ return v8::String::NewFromOneByte(
+ isolate, reinterpret_cast(data), type, length)
+ .ToLocalChecked();
}
inline v8::Local OneByteString(v8::Isolate* isolate,
const signed char* data,
- int length) {
- return v8::String::NewFromOneByte(isolate,
- reinterpret_cast(data),
- v8::NewStringType::kNormal,
- length).ToLocalChecked();
+ int length,
+ v8::NewStringType type) {
+ return v8::String::NewFromOneByte(
+ isolate, reinterpret_cast(data), type, length)
+ .ToLocalChecked();
}
inline v8::Local OneByteString(v8::Isolate* isolate,
const unsigned char* data,
- int length) {
- return v8::String::NewFromOneByte(
- isolate, data, v8::NewStringType::kNormal, length)
+ int length,
+ v8::NewStringType type) {
+ return v8::String::NewFromOneByte(isolate, data, type, length)
.ToLocalChecked();
}
inline v8::Local OneByteString(v8::Isolate* isolate,
- std::string_view str) {
- return OneByteString(isolate, str.data(), str.size());
+ std::string_view str,
+ v8::NewStringType type) {
+ return OneByteString(isolate, str.data(), str.size(), type);
}
char ToLower(char c) {
diff --git a/src/util.h b/src/util.h
index 587b8cf0dd8e..bf3ef4fd4b5d 100644
--- a/src/util.h
+++ b/src/util.h
@@ -330,21 +330,29 @@ class KVStore {
};
// Convenience wrapper around v8::String::NewFromOneByte().
-inline v8::Local OneByteString(v8::Isolate* isolate,
- const char* data,
- int length = -1);
+inline v8::Local OneByteString(
+ v8::Isolate* isolate,
+ const char* data,
+ int length = -1,
+ v8::NewStringType type = v8::NewStringType::kNormal);
// For the people that compile with -funsigned-char.
-inline v8::Local OneByteString(v8::Isolate* isolate,
- const signed char* data,
- int length = -1);
+inline v8::Local OneByteString(
+ v8::Isolate* isolate,
+ const signed char* data,
+ int length = -1,
+ v8::NewStringType type = v8::NewStringType::kNormal);
-inline v8::Local OneByteString(v8::Isolate* isolate,
- const unsigned char* data,
- int length = -1);
+inline v8::Local OneByteString(
+ v8::Isolate* isolate,
+ const unsigned char* data,
+ int length = -1,
+ v8::NewStringType type = v8::NewStringType::kNormal);
-inline v8::Local OneByteString(v8::Isolate* isolate,
- std::string_view str);
+inline v8::Local OneByteString(
+ v8::Isolate* isolate,
+ std::string_view str,
+ v8::NewStringType type = v8::NewStringType::kNormal);
// Used to be a macro, hence the uppercase name.
template
@@ -352,7 +360,7 @@ template
inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate,
const char (&data)[N]) {
CHECK_EQ(data[N - 1], '\0');
- return OneByteString(isolate, data, N - 1);
+ return OneByteString(isolate, data, N - 1, v8::NewStringType::kInternalized);
}
// tolower() is locale-sensitive. Use ToLower() instead.
From e790eb6b5005e3cdf56cae79cc6689b7f6f58bd8 Mon Sep 17 00:00:00 2001
From: Ruben Bridgewater
Date: Sun, 14 Sep 2025 02:06:47 +0200
Subject: [PATCH 18/90] repl: fix cpu overhead pasting big strings to the REPL
Pasting input should not trigger any completions and other
calculations. This is now handled by just writing the string to the
terminal in case the user is pasting. As soon as pasting is done,
the completions will be re-enabled.
Fixes: https://github.com/nodejs/node/issues/40626
Fixes: https://github.com/nodejs/node/issues/43343
PR-URL: https://github.com/nodejs/node/pull/59857
Reviewed-By: Luigi Pinca
Reviewed-By: James M Snell
---
lib/internal/readline/interface.js | 6 ++++
test/parallel/test-repl-paste-big-data.js | 36 +++++++++++++++++++++++
2 files changed, 42 insertions(+)
create mode 100644 test/parallel/test-repl-paste-big-data.js
diff --git a/lib/internal/readline/interface.js b/lib/internal/readline/interface.js
index 97178d2ae87e..ea2e37563034 100644
--- a/lib/internal/readline/interface.js
+++ b/lib/internal/readline/interface.js
@@ -624,6 +624,12 @@ class Interface extends InterfaceConstructor {
[kInsertString](c) {
this[kBeforeEdit](this.line, this.cursor);
+ if (!this.isCompletionEnabled) {
+ this.line += c;
+ this.cursor += c.length;
+ this[kWriteToOutput](c);
+ return;
+ }
if (this.cursor < this.line.length) {
const beg = StringPrototypeSlice(this.line, 0, this.cursor);
const end = StringPrototypeSlice(
diff --git a/test/parallel/test-repl-paste-big-data.js b/test/parallel/test-repl-paste-big-data.js
new file mode 100644
index 000000000000..fd247b4b0631
--- /dev/null
+++ b/test/parallel/test-repl-paste-big-data.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const common = require('../common');
+const repl = require('repl');
+const stream = require('stream');
+const assert = require('assert');
+
+// Pasting big input should not cause the process to have a huge CPU usage.
+
+const cpuUsage = process.cpuUsage();
+
+const r = initRepl();
+r.input.emit('data', 'a'.repeat(2e4) + '\n');
+r.input.emit('data', '.exit\n');
+
+r.once('exit', common.mustCall(() => {
+ const diff = process.cpuUsage(cpuUsage);
+ assert.ok(diff.user < 1e6);
+}));
+
+function initRepl() {
+ const input = new stream();
+ input.write = input.pause = input.resume = () => {};
+ input.readable = true;
+
+ const output = new stream();
+ output.write = () => {};
+ output.writable = true;
+
+ return repl.start({
+ input,
+ output,
+ terminal: true,
+ prompt: ''
+ });
+}
From f2fbcc576d50ae0af3587c49678242e05d5978f0 Mon Sep 17 00:00:00 2001
From: Ruben Bridgewater
Date: Sun, 14 Sep 2025 02:06:57 +0200
Subject: [PATCH 19/90] util: fix debuglog.enabled not being present with
callback logger
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The method returned by the callback is missing the .enabled property.
This is added in a consistent way that it also verifies that it's a
getter.
Fixes: https://github.com/nodejs/node/issues/56676
PR-URL: https://github.com/nodejs/node/pull/59858
Reviewed-By: Michaël Zasso
Reviewed-By: James M Snell
---
lib/internal/util/debuglog.js | 11 ++++++++++-
test/sequential/test-util-debug.js | 11 +++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js
index 77bfa8a76cdf..06a4f8a23985 100644
--- a/lib/internal/util/debuglog.js
+++ b/lib/internal/util/debuglog.js
@@ -94,8 +94,17 @@ function debuglog(set, cb) {
// Only invokes debuglogImpl() when the debug function is
// called for the first time.
debug = debuglogImpl(enabled, set);
- if (typeof cb === 'function')
+ if (typeof cb === 'function') {
+ ObjectDefineProperty(debug, 'enabled', {
+ __proto__: null,
+ get() {
+ return enabled;
+ },
+ configurable: true,
+ enumerable: true,
+ });
cb(debug);
+ }
switch (args.length) {
case 1: return debug(args[0]);
case 2: return debug(args[0], args[1]);
diff --git a/test/sequential/test-util-debug.js b/test/sequential/test-util-debug.js
index f8be25f6f880..5ccf2264e9aa 100644
--- a/test/sequential/test-util-debug.js
+++ b/test/sequential/test-util-debug.js
@@ -57,7 +57,7 @@ function parent() {
function test(environ, shouldWrite, section, forceColors = false) {
let expectErr = '';
- const expectOut = shouldWrite ? 'enabled\n' : 'disabled\n';
+ const expectOut = shouldWrite ? 'outer enabled\ninner enabled\n' : 'outer disabled\ninner disabled\n';
const spawn = require('child_process').spawn;
const child = spawn(process.execPath, [__filename, 'child', section], {
@@ -117,11 +117,18 @@ function child(section) {
Object.defineProperty(process.stderr, 'hasColors', {
value: tty.WriteStream.prototype.hasColors
});
+
+ let innerDebug = null;
// eslint-disable-next-line no-restricted-syntax
const debug = util.debuglog(section, common.mustCall((cb) => {
assert.strictEqual(typeof cb, 'function');
+ innerDebug = cb;
}));
debug('this', { is: 'a' }, /debugging/);
debug('num=%d str=%s obj=%j', 1, 'a', { foo: 'bar' });
- console.log(debug.enabled ? 'enabled' : 'disabled');
+ console.log(debug.enabled ? 'outer enabled' : 'outer disabled');
+ console.log(innerDebug.enabled ? 'inner enabled' : 'inner disabled');
+
+ assert.strictEqual(typeof Object.getOwnPropertyDescriptor(debug, 'enabled').get, 'function');
+ assert.strictEqual(typeof Object.getOwnPropertyDescriptor(innerDebug, 'enabled').get, 'function');
}
From 8fd669c70dbf694a38783b7ff58883aa35354f75 Mon Sep 17 00:00:00 2001
From: yusheng chen
Date: Sun, 14 Sep 2025 08:39:24 +0800
Subject: [PATCH 20/90] doc: type improvement of file `http.md`
`outgoingMessage.setHeader` and `outgoingMessage.getHeader` section
PR-URL: https://github.com/nodejs/node/pull/58189
Reviewed-By: Ethan Arrowood
Reviewed-By: Ruben Bridgewater
---
doc/api/http.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/api/http.md b/doc/api/http.md
index 447a23ad7016..c3077e9b323c 100644
--- a/doc/api/http.md
+++ b/doc/api/http.md
@@ -2116,7 +2116,7 @@ added: v0.4.0
-->
* `name` {string}
-* Returns: {any}
+* Returns: {number | string | string\[] | undefined}
Reads out a header that's already been queued but not sent to the client.
The name is case-insensitive. The type of the return value depends
@@ -2251,7 +2251,7 @@ added: v0.4.0
-->
* `name` {string}
-* `value` {any}
+* `value` {number | string | string\[]}
* Returns: {http.ServerResponse}
Returns the response object.
@@ -3202,7 +3202,7 @@ added: v0.4.0
-->
* `name` {string} Name of header
-* Returns: {string | undefined}
+* Returns: {number | string | string\[] | undefined}
Gets the value of the HTTP header with the given name. If that header is not
set, the returned value will be `undefined`.
@@ -3304,7 +3304,7 @@ added: v0.4.0
-->
* `name` {string} Header name
-* `value` {any} Header value
+* `value` {number | string | string\[]} Header value
* Returns: {this}
Sets a single header value. If the header already exists in the to-be-sent
From f88752ddb66a262fe27d25d81a26212c9ff77f43 Mon Sep 17 00:00:00 2001
From: Mikhail
Date: Sun, 14 Sep 2025 03:39:32 +0300
Subject: [PATCH 21/90] url: replaced slice with at
PR-URL: https://github.com/nodejs/node/pull/59181
Reviewed-By: Jordan Harband
Reviewed-By: Zeyu "Alex" Yang
Reviewed-By: Daniel Lemire
Reviewed-By: Luigi Pinca
Reviewed-By: Ruben Bridgewater
---
lib/url.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/url.js b/lib/url.js
index 00aa708bea59..c027770afa47 100644
--- a/lib/url.js
+++ b/lib/url.js
@@ -22,10 +22,12 @@
'use strict';
const {
+ ArrayPrototypeJoin,
Boolean,
Int8Array,
ObjectAssign,
ObjectKeys,
+ StringPrototypeAt,
StringPrototypeCharCodeAt,
StringPrototypeIndexOf,
StringPrototypeReplaceAll,
@@ -923,7 +925,7 @@ Url.prototype.resolveObject = function resolveObject(relative) {
// If a url ENDs in . or .., then it must get a trailing slash.
// however, if it ends in anything else non-slashy,
// then it must NOT get a trailing slash.
- let last = srcPath.slice(-1)[0];
+ let last = srcPath[srcPath.length - 1];
const hasTrailingSlash = (
((result.host || relative.host || srcPath.length > 1) &&
(last === '.' || last === '..')) || last === '');
@@ -956,7 +958,7 @@ Url.prototype.resolveObject = function resolveObject(relative) {
srcPath.unshift('');
}
- if (hasTrailingSlash && (srcPath.join('/').slice(-1) !== '/')) {
+ if (hasTrailingSlash && StringPrototypeAt(ArrayPrototypeJoin(srcPath, '/'), -1) !== '/') {
srcPath.push('');
}
From a71dd592e37d3d9c73792e617ba30fd26d71ee68 Mon Sep 17 00:00:00 2001
From: Bruno Rodrigues
Date: Sat, 13 Sep 2025 21:39:40 -0300
Subject: [PATCH 22/90] benchmark: calibrate config dgram multi-buffer
PR-URL: https://github.com/nodejs/node/pull/59696
Reviewed-By: Rafael Gonzaga
Reviewed-By: Ruben Bridgewater
---
benchmark/dgram/multi-buffer.js | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/benchmark/dgram/multi-buffer.js b/benchmark/dgram/multi-buffer.js
index 945d02c2d1c0..83d61daa36e1 100644
--- a/benchmark/dgram/multi-buffer.js
+++ b/benchmark/dgram/multi-buffer.js
@@ -5,18 +5,18 @@ const common = require('../common.js');
const dgram = require('dgram');
const PORT = common.PORT;
-// `num` is the number of send requests to queue up each time.
+// `n` is the number of send requests to queue up each time.
// Keep it reasonably high (>10) otherwise you're benchmarking the speed of
// event loop cycles more than anything else.
const bench = common.createBenchmark(main, {
- len: [64, 256, 1024],
- num: [100],
- chunks: [1, 2, 4, 8],
+ len: [64, 512, 1024],
+ n: [100],
+ chunks: [1, 8],
type: ['send', 'recv'],
dur: [5],
});
-function main({ dur, len, num, type, chunks }) {
+function main({ dur, len, n, type, chunks }) {
const chunk = [];
for (let i = 0; i < chunks; i++) {
chunk.push(Buffer.allocUnsafe(Math.round(len / chunks)));
@@ -26,11 +26,11 @@ function main({ dur, len, num, type, chunks }) {
const socket = dgram.createSocket('udp4');
function onsend() {
- if (sent++ % num === 0) {
+ if (sent++ % n === 0) {
// The setImmediate() is necessary to have event loop progress on OSes
// that only perform synchronous I/O on nonblocking UDP sockets.
setImmediate(() => {
- for (let i = 0; i < num; i++) {
+ for (let i = 0; i < n; i++) {
socket.send(chunk, PORT, '127.0.0.1', onsend);
}
});
From 758271ae66b1e0fb4fbda62e0f20c831101bb129 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EB=B0=A9=EC=A7=84=ED=98=81?=
Date: Sun, 14 Sep 2025 09:40:06 +0900
Subject: [PATCH 23/90] http: optimize checkIsHttpToken for short strings
Use lookup table instead of regex for strings shorter than 10
characters to improve performance for common short header names
while maintaining compatibility.
PR-URL: https://github.com/nodejs/node/pull/59832
Reviewed-By: Ethan Arrowood
Reviewed-By: Tim Perry
Reviewed-By: Luigi Pinca
Reviewed-By: Ruben Bridgewater
---
lib/_http_common.js | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/lib/_http_common.js b/lib/_http_common.js
index 1278a0a0903c..24ac32b1e519 100644
--- a/lib/_http_common.js
+++ b/lib/_http_common.js
@@ -24,6 +24,7 @@
const {
MathMin,
Symbol,
+ Uint8Array,
} = primordials;
const { setImmediate } = require('timers');
@@ -205,14 +206,49 @@ function freeParser(parser, req, socket) {
}
}
+// Character code ranges for valid HTTP tokens
+// Valid chars: ^_`a-zA-Z-0-9!#$%&'*+.|~
+// Based on RFC 7230 Section 3.2.6 token definition
+// See https://tools.ietf.org/html/rfc7230#section-3.2.6
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
+const validTokenChars = new Uint8Array([
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32-47 (!"#$%&'()*+,-./)
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 (0-9:;<=>?)
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64-79 (@A-O)
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80-95 (P-Z[\]^_)
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96-111 (`a-o)
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112-127 (p-z{|}~)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-159
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-175
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-191
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-207
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-223
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-239
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-255
+]);
+
/**
* Verifies that the given val is a valid HTTP token
* per the rules defined in RFC 7230
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
*/
function checkIsHttpToken(val) {
- return tokenRegExp.test(val);
+ if (val.length >= 10) {
+ return tokenRegExp.test(val);
+ }
+
+ if (val.length === 0) return false;
+
+ // Use lookup table for short strings, regex for longer ones
+ for (let i = 0; i < val.length; i++) {
+ if (!validTokenChars[val.charCodeAt(i)]) {
+ return false;
+ }
+ }
+ return true;
}
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
From a33ed9bf9681aef183f936631379588a66ffe9ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9?=
Date: Sun, 14 Sep 2025 17:46:00 +0100
Subject: [PATCH 24/90] inspector: ensure adequate memory allocation for
`Binary::toBase64`
PR-URL: https://github.com/nodejs/node/pull/59870
Reviewed-By: Anna Henningsen
Reviewed-By: Chengzhong Wu
Reviewed-By: Luigi Pinca
Reviewed-By: James M Snell
---
src/inspector/node_string.cc | 8 +++----
.../test-inspector-network-data-received.js | 24 +++++++++++++++----
2 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/src/inspector/node_string.cc b/src/inspector/node_string.cc
index 8521730bd03c..e2148e954217 100644
--- a/src/inspector/node_string.cc
+++ b/src/inspector/node_string.cc
@@ -108,15 +108,15 @@ size_t StringUtil::CharacterCount(const std::string_view s) {
}
String Binary::toBase64() const {
- MaybeStackBuffer buffer;
- size_t str_len = simdutf::base64_length_from_binary(bytes_->size());
- buffer.SetLength(str_len);
+ size_t expected_base64_length =
+ simdutf::base64_length_from_binary(bytes_->size());
+ MaybeStackBuffer buffer(expected_base64_length);
size_t len =
simdutf::binary_to_base64(reinterpret_cast(bytes_->data()),
bytes_->size(),
buffer.out());
- CHECK_EQ(len, str_len);
+ CHECK_EQ(len, expected_base64_length);
return buffer.ToString();
}
diff --git a/test/parallel/test-inspector-network-data-received.js b/test/parallel/test-inspector-network-data-received.js
index 466baf31c14c..e9c0208b7598 100644
--- a/test/parallel/test-inspector-network-data-received.js
+++ b/test/parallel/test-inspector-network-data-received.js
@@ -11,6 +11,9 @@ const assert = require('node:assert');
const { waitUntil } = require('../common/inspector-helper');
const { setTimeout } = require('node:timers/promises');
+// The complete payload string received by the network agent
+const payloadString = `Hello, world${'.'.repeat(4096)}`;
+
const session = new inspector.Session();
session.connect();
session.post('Network.enable');
@@ -67,9 +70,20 @@ async function triggerNetworkEvents(requestId, charset) {
});
await setTimeout(1);
- Network.loadingFinished({
+ // Test inspector binary conversions with large input
+ const chunk3 = Buffer.allocUnsafe(4096).fill('.');
+ Network.dataReceived({
requestId,
timestamp: 5,
+ dataLength: chunk3.byteLength,
+ encodedDataLength: chunk3.byteLength,
+ data: chunk3,
+ });
+ await setTimeout(1);
+
+ Network.loadingFinished({
+ requestId,
+ timestamp: 6,
});
}
@@ -116,7 +130,7 @@ test('should stream Network.dataReceived with data chunks', async () => {
const data = Buffer.concat(chunks);
assert.strictEqual(data.byteLength, totalDataLength, data);
- assert.strictEqual(data.toString('utf8'), 'Hello, world');
+ assert.strictEqual(data.toString('utf8'), payloadString);
});
test('Network.streamResourceContent should send all buffered chunks', async () => {
@@ -131,7 +145,7 @@ test('Network.streamResourceContent should send all buffered chunks', async () =
const { bufferedData } = await session.post('Network.streamResourceContent', {
requestId,
});
- assert.strictEqual(Buffer.from(bufferedData, 'base64').toString('utf8'), 'Hello, world');
+ assert.strictEqual(Buffer.from(bufferedData, 'base64').toString('utf8'), payloadString);
});
test('Network.streamResourceContent should reject if request id not found', async () => {
@@ -158,7 +172,7 @@ test('Network.getResponseBody should send all buffered binary data', async () =>
requestId,
});
assert.strictEqual(base64Encoded, true);
- assert.strictEqual(body, Buffer.from('Hello, world').toString('base64'));
+ assert.strictEqual(body, Buffer.from(payloadString).toString('base64'));
});
test('Network.getResponseBody should send all buffered text data', async () => {
@@ -174,5 +188,5 @@ test('Network.getResponseBody should send all buffered text data', async () => {
requestId,
});
assert.strictEqual(base64Encoded, false);
- assert.strictEqual(body, 'Hello, world');
+ assert.strictEqual(body, payloadString);
});
From 0a3a3f729e916f6b8d20394294d5e3804f322c30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?=
Date: Mon, 15 Sep 2025 08:54:35 +0200
Subject: [PATCH 25/90] doc: add security escalation policy
PR-URL: https://github.com/nodejs/node/pull/59806
Refs: https://github.com/openjs-foundation/cross-project-council/pull/1588
Reviewed-By: Marco Ippolito
Reviewed-By: Rafael Gonzaga
Reviewed-By: Richard Lau
Reviewed-By: James M Snell
---
SECURITY.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/SECURITY.md b/SECURITY.md
index 9862585a9239..087ea563c9df 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -15,6 +15,13 @@ you informed of the progress being made towards a fix and full announcement,
and may ask for additional information or guidance surrounding the reported
issue.
+If you do not receive an acknowledgement of your report within 6 business
+days, or if you cannot find a private security contact for the project, you
+may escalate to the OpenJS Foundation CNA at `security@lists.openjsf.org`.
+
+If the project acknowledges your report but does not provide any further
+response or engagement within 14 days, escalation is also appropriate.
+
### Node.js bug bounty program
The Node.js project engages in an official bug bounty program for security
From 28ef564ecde1583df3003ef5467991f22e5eb81a Mon Sep 17 00:00:00 2001
From: Nam Yooseong <102887277+meteorqz6@users.noreply.github.com>
Date: Tue, 16 Sep 2025 00:54:23 +0900
Subject: [PATCH 26/90] typings: remove unused imports
PR-URL: https://github.com/nodejs/node/pull/59880
Reviewed-By: Daeyeon Jeong
Reviewed-By: Antoine du Hamel
Reviewed-By: Luigi Pinca
---
typings/internalBinding/zlib.d.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/typings/internalBinding/zlib.d.ts b/typings/internalBinding/zlib.d.ts
index 38506f501c4a..3f937df22503 100644
--- a/typings/internalBinding/zlib.d.ts
+++ b/typings/internalBinding/zlib.d.ts
@@ -1,5 +1,3 @@
-import { TypedArray } from '../globals';
-
declare namespace InternalZlibBinding {
class ZlibBase {
// These attributes are not used by the C++ binding, but declared on JS side.
From 6d24b88fbc21596391bd34745ae8555f94720926 Mon Sep 17 00:00:00 2001
From: Mert Can Altin
Date: Tue, 16 Sep 2025 12:57:06 +0300
Subject: [PATCH 27/90] node-api: added SharedArrayBuffer api
PR-URL: https://github.com/nodejs/node/pull/59071
Reviewed-By: Chengzhong Wu
---
doc/api/n-api.md | 73 +++++++++-
src/js_native_api.h | 8 ++
src/js_native_api_v8.cc | 61 +++++++-
.../test_sharedarraybuffer/binding.gyp | 8 ++
.../test_sharedarraybuffer/test.js | 67 +++++++++
.../test_sharedarraybuffer.c | 130 ++++++++++++++++++
6 files changed, 334 insertions(+), 13 deletions(-)
create mode 100644 test/js-native-api/test_sharedarraybuffer/binding.gyp
create mode 100644 test/js-native-api/test_sharedarraybuffer/test.js
create mode 100644 test/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c
diff --git a/doc/api/n-api.md b/doc/api/n-api.md
index fe8488e1bfa5..34ba4ce440c5 100644
--- a/doc/api/n-api.md
+++ b/doc/api/n-api.md
@@ -3293,6 +3293,10 @@ Specification.
```c
@@ -3303,21 +3307,20 @@ napi_status napi_get_arraybuffer_info(napi_env env,
```
* `[in] env`: The environment that the API is invoked under.
-* `[in] arraybuffer`: `napi_value` representing the `ArrayBuffer` being queried.
-* `[out] data`: The underlying data buffer of the `ArrayBuffer`. If byte\_length
+* `[in] arraybuffer`: `napi_value` representing the `ArrayBuffer` or `SharedArrayBuffer` being queried.
+* `[out] data`: The underlying data buffer of the `ArrayBuffer` or `SharedArrayBuffer`
is `0`, this may be `NULL` or any other pointer value.
* `[out] byte_length`: Length in bytes of the underlying data buffer.
Returns `napi_ok` if the API succeeded.
-This API is used to retrieve the underlying data buffer of an `ArrayBuffer` and
-its length.
+This API is used to retrieve the underlying data buffer of an `ArrayBuffer` or `SharedArrayBuffer` and its length.
_WARNING_: Use caution while using this API. The lifetime of the underlying data
-buffer is managed by the `ArrayBuffer` even after it's returned. A
+buffer is managed by the `ArrayBuffer` or `SharedArrayBuffer` even after it's returned. A
possible safe way to use this API is in conjunction with
[`napi_create_reference`][], which can be used to guarantee control over the
-lifetime of the `ArrayBuffer`. It's also safe to use the returned data buffer
+lifetime of the `ArrayBuffer` or `SharedArrayBuffer`. It's also safe to use the returned data buffer
within the same callback as long as there are no calls to other APIs that might
trigger a GC.
@@ -4272,6 +4275,63 @@ This API represents the invocation of the `ArrayBuffer` `IsDetachedBuffer`
operation as defined in [Section isDetachedBuffer][] of the ECMAScript Language
Specification.
+### `node_api_is_sharedarraybuffer`
+
+
+
+> Stability: 1 - Experimental
+
+```c
+napi_status node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result)
+```
+
+* `[in] env`: The environment that the API is invoked under.
+* `[in] value`: The JavaScript value to check.
+* `[out] result`: Whether the given `napi_value` represents a `SharedArrayBuffer`.
+
+Returns `napi_ok` if the API succeeded.
+
+This API checks if the Object passed in is a `SharedArrayBuffer`.
+
+### `node_api_create_sharedarraybuffer`
+
+
+
+> Stability: 1 - Experimental
+
+```c
+napi_status node_api_create_sharedarraybuffer(napi_env env,
+ size_t byte_length,
+ void** data,
+ napi_value* result)
+```
+
+* `[in] env`: The environment that the API is invoked under.
+* `[in] byte_length`: The length in bytes of the shared array buffer to create.
+* `[out] data`: Pointer to the underlying byte buffer of the `SharedArrayBuffer`.
+ `data` can optionally be ignored by passing `NULL`.
+* `[out] result`: A `napi_value` representing a JavaScript `SharedArrayBuffer`.
+
+Returns `napi_ok` if the API succeeded.
+
+This API returns a Node-API value corresponding to a JavaScript `SharedArrayBuffer`.
+`SharedArrayBuffer`s are used to represent fixed-length binary data buffers that
+can be shared across multiple workers.
+
+The `SharedArrayBuffer` allocated will have an underlying byte buffer whose size is
+determined by the `byte_length` parameter that's passed in.
+The underlying buffer is optionally returned back to the caller in case the
+caller wants to directly manipulate the buffer. This buffer can only be
+written to directly from native code. To write to this buffer from JavaScript,
+a typed array or `DataView` object would need to be created.
+
+JavaScript `SharedArrayBuffer` objects are described in
+[Section SharedArrayBuffer objects][] of the ECMAScript Language Specification.
+
## Working with JavaScript properties
Node-API exposes a set of APIs to get and set properties on JavaScript
@@ -6785,6 +6845,7 @@ the add-on's file name during loading.
[Section IsArray]: https://tc39.es/ecma262/#sec-isarray
[Section IsStrctEqual]: https://tc39.es/ecma262/#sec-strict-equality-comparison
[Section Promise objects]: https://tc39.es/ecma262/#sec-promise-objects
+[Section SharedArrayBuffer objects]: https://tc39.es/ecma262/#sec-sharedarraybuffer-objects
[Section ToBoolean]: https://tc39.es/ecma262/#sec-toboolean
[Section ToNumber]: https://tc39.es/ecma262/#sec-tonumber
[Section ToObject]: https://tc39.es/ecma262/#sec-toobject
diff --git a/src/js_native_api.h b/src/js_native_api.h
index d2711fcb9ae7..ff0462832575 100644
--- a/src/js_native_api.h
+++ b/src/js_native_api.h
@@ -474,6 +474,14 @@ napi_get_dataview_info(napi_env env,
napi_value* arraybuffer,
size_t* byte_offset);
+#ifdef NAPI_EXPERIMENTAL
+#define NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
+NAPI_EXTERN napi_status NAPI_CDECL
+node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result);
+NAPI_EXTERN napi_status NAPI_CDECL node_api_create_sharedarraybuffer(
+ napi_env env, size_t byte_length, void** data, napi_value* result);
+#endif // NAPI_EXPERIMENTAL
+
// version management
NAPI_EXTERN napi_status NAPI_CDECL napi_get_version(node_api_basic_env env,
uint32_t* result);
diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc
index 9ed7df2afd8c..7b2efa49468c 100644
--- a/src/js_native_api_v8.cc
+++ b/src/js_native_api_v8.cc
@@ -3075,21 +3075,68 @@ napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
CHECK_ARG(env, arraybuffer);
v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer);
- RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
- v8::Local ab = value.As();
+ if (value->IsArrayBuffer()) {
+ v8::Local ab = value.As();
- if (data != nullptr) {
- *data = ab->Data();
- }
+ if (data != nullptr) {
+ *data = ab->Data();
+ }
- if (byte_length != nullptr) {
- *byte_length = ab->ByteLength();
+ if (byte_length != nullptr) {
+ *byte_length = ab->ByteLength();
+ }
+ } else if (value->IsSharedArrayBuffer()) {
+ v8::Local sab = value.As();
+
+ if (data != nullptr) {
+ *data = sab->Data();
+ }
+
+ if (byte_length != nullptr) {
+ *byte_length = sab->ByteLength();
+ }
+ } else {
+ return napi_set_last_error(env, napi_invalid_arg);
}
return napi_clear_last_error(env);
}
+napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env,
+ napi_value value,
+ bool* result) {
+ CHECK_ENV_NOT_IN_GC(env);
+ CHECK_ARG(env, value);
+ CHECK_ARG(env, result);
+
+ v8::Local val = v8impl::V8LocalValueFromJsValue(value);
+ *result = val->IsSharedArrayBuffer();
+
+ return napi_clear_last_error(env);
+}
+
+napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env,
+ size_t byte_length,
+ void** data,
+ napi_value* result) {
+ NAPI_PREAMBLE(env);
+ CHECK_ARG(env, result);
+
+ v8::Isolate* isolate = env->isolate;
+ v8::Local buffer =
+ v8::SharedArrayBuffer::New(isolate, byte_length);
+
+ // Optionally return a pointer to the buffer's data, to avoid another call to
+ // retrieve it.
+ if (data != nullptr) {
+ *data = buffer->Data();
+ }
+
+ *result = v8impl::JsValueFromV8LocalValue(buffer);
+ return GET_RETURN_STATUS(env);
+}
+
napi_status NAPI_CDECL napi_is_typedarray(napi_env env,
napi_value value,
bool* result) {
diff --git a/test/js-native-api/test_sharedarraybuffer/binding.gyp b/test/js-native-api/test_sharedarraybuffer/binding.gyp
new file mode 100644
index 000000000000..0020e6b4a9e9
--- /dev/null
+++ b/test/js-native-api/test_sharedarraybuffer/binding.gyp
@@ -0,0 +1,8 @@
+{
+ "targets": [
+ {
+ "target_name": "test_sharedarraybuffer",
+ "sources": [ "test_sharedarraybuffer.c" ]
+ }
+ ]
+}
diff --git a/test/js-native-api/test_sharedarraybuffer/test.js b/test/js-native-api/test_sharedarraybuffer/test.js
new file mode 100644
index 000000000000..9c3066124b7c
--- /dev/null
+++ b/test/js-native-api/test_sharedarraybuffer/test.js
@@ -0,0 +1,67 @@
+'use strict';
+
+const common = require('../../common');
+const assert = require('assert');
+const test_sharedarraybuffer = require(`./build/${common.buildType}/test_sharedarraybuffer`);
+
+{
+ const sab = new SharedArrayBuffer(16);
+ const ab = new ArrayBuffer(16);
+ const obj = {};
+ const arr = [];
+
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(sab), true);
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(ab), false);
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(obj), false);
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(arr), false);
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(null), false);
+ assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(undefined), false);
+}
+
+// Test node_api_create_sharedarraybuffer
+{
+ const sab = test_sharedarraybuffer.TestCreateSharedArrayBuffer(16);
+ assert(sab instanceof SharedArrayBuffer);
+ assert.strictEqual(sab.byteLength, 16);
+}
+
+// Test node_api_create_get_sharedarraybuffer_info
+{
+ const sab = new SharedArrayBuffer(32);
+ const byteLength = test_sharedarraybuffer.TestGetSharedArrayBufferInfo(sab);
+ assert.strictEqual(byteLength, 32);
+}
+
+// Test data access
+{
+ const sab = new SharedArrayBuffer(8);
+ const result = test_sharedarraybuffer.TestSharedArrayBufferData(sab);
+ assert.strictEqual(result, true);
+
+ // Check if data was written correctly
+ const view = new Uint8Array(sab);
+ for (let i = 0; i < 8; i++) {
+ assert.strictEqual(view[i], i % 256);
+ }
+}
+
+// Test data pointer from existing SharedArrayBuffer
+{
+ const sab = new SharedArrayBuffer(16);
+ const result = test_sharedarraybuffer.TestSharedArrayBufferData(sab);
+ assert.strictEqual(result, true);
+}
+
+// Test zero-length SharedArrayBuffer
+{
+ const sab = test_sharedarraybuffer.TestCreateSharedArrayBuffer(0);
+ assert(sab instanceof SharedArrayBuffer);
+ assert.strictEqual(sab.byteLength, 0);
+}
+
+// Test invalid arguments
+{
+ assert.throws(() => {
+ test_sharedarraybuffer.TestGetSharedArrayBufferInfo({});
+ }, { name: 'Error', message: 'Invalid argument' });
+}
diff --git a/test/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c b/test/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c
new file mode 100644
index 000000000000..661e52c566f7
--- /dev/null
+++ b/test/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c
@@ -0,0 +1,130 @@
+#define NAPI_EXPERIMENTAL
+#include
+#include
+#include "../common.h"
+#include "../entry_point.h"
+
+static napi_value TestIsSharedArrayBuffer(napi_env env,
+ napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
+
+ bool is_sharedarraybuffer;
+ NODE_API_CALL(
+ env, node_api_is_sharedarraybuffer(env, args[0], &is_sharedarraybuffer));
+
+ napi_value ret;
+ NODE_API_CALL(env, napi_get_boolean(env, is_sharedarraybuffer, &ret));
+
+ return ret;
+}
+
+static napi_value TestCreateSharedArrayBuffer(napi_env env,
+ napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
+
+ napi_valuetype valuetype0;
+ NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
+
+ NODE_API_ASSERT(
+ env,
+ valuetype0 == napi_number,
+ "Wrong type of arguments. Expects a number as first argument.");
+
+ int32_t byte_length;
+ NODE_API_CALL(env, napi_get_value_int32(env, args[0], &byte_length));
+
+ NODE_API_ASSERT(env,
+ byte_length >= 0,
+ "Invalid byte length. Expects a non-negative integer.");
+
+ napi_value ret;
+ void* data;
+ NODE_API_CALL(
+ env, node_api_create_sharedarraybuffer(env, byte_length, &data, &ret));
+
+ return ret;
+}
+
+static napi_value TestGetSharedArrayBufferInfo(napi_env env,
+ napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
+
+ void* data;
+ size_t byte_length;
+ NODE_API_CALL(env,
+ napi_get_arraybuffer_info(env, args[0], &data, &byte_length));
+
+ napi_value ret;
+ NODE_API_CALL(env, napi_create_uint32(env, byte_length, &ret));
+
+ return ret;
+}
+
+static void WriteTestDataToBuffer(void* data, size_t byte_length) {
+ if (byte_length > 0 && data != NULL) {
+ uint8_t* bytes = (uint8_t*)data;
+ for (size_t i = 0; i < byte_length; i++) {
+ bytes[i] = i % 256;
+ }
+ }
+}
+
+static napi_value TestSharedArrayBufferData(napi_env env,
+ napi_callback_info info) {
+ size_t argc = 1;
+ napi_value args[1];
+ NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
+
+ NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
+
+ void* data;
+ size_t byte_length;
+ NODE_API_CALL(env,
+ napi_get_arraybuffer_info(env, args[0], &data, &byte_length));
+
+ WriteTestDataToBuffer(data, byte_length);
+
+ // Return the same data pointer validity
+ bool data_valid = (data != NULL) && (byte_length > 0);
+
+ napi_value ret;
+ NODE_API_CALL(env, napi_get_boolean(env, data_valid, &ret));
+
+ return ret;
+}
+
+EXTERN_C_START
+napi_value Init(napi_env env, napi_value exports) {
+ napi_property_descriptor descriptors[] = {
+ DECLARE_NODE_API_PROPERTY("TestIsSharedArrayBuffer",
+ TestIsSharedArrayBuffer),
+ DECLARE_NODE_API_PROPERTY("TestCreateSharedArrayBuffer",
+ TestCreateSharedArrayBuffer),
+ DECLARE_NODE_API_PROPERTY("TestGetSharedArrayBufferInfo",
+ TestGetSharedArrayBufferInfo),
+ DECLARE_NODE_API_PROPERTY("TestSharedArrayBufferData",
+ TestSharedArrayBufferData),
+ };
+
+ NODE_API_CALL(
+ env,
+ napi_define_properties(env,
+ exports,
+ sizeof(descriptors) / sizeof(*descriptors),
+ descriptors));
+
+ return exports;
+}
+EXTERN_C_END
From ba00875f0143ed4bd2f52cbcf4511e8ea326535d Mon Sep 17 00:00:00 2001
From: Matteo Collina
Date: Tue, 16 Sep 2025 22:24:38 +0200
Subject: [PATCH 28/90] stream: use new AsyncResource instead of bind
The bind method uses ObjectDefineProperty that shows up
in flamegraphs. This changes it to avoid the utility.
Signed-off-by: Matteo Collina
PR-URL: https://github.com/nodejs/node/pull/59867
Reviewed-By: Robert Nagy
Reviewed-By: Ruben Bridgewater
Reviewed-By: Rafael Gonzaga
Reviewed-By: James M Snell
Reviewed-By: Luigi Pinca
Reviewed-By: Stephen Belanger
---
lib/internal/streams/end-of-stream.js | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js
index cb0a9242e051..9b4a0b9d6f3b 100644
--- a/lib/internal/streams/end-of-stream.js
+++ b/lib/internal/streams/end-of-stream.js
@@ -45,7 +45,7 @@ const {
} = require('internal/streams/utils');
// Lazy load
-let AsyncLocalStorage;
+let AsyncResource;
let addAbortListener;
function isRequest(stream) {
@@ -54,6 +54,14 @@ function isRequest(stream) {
const nop = () => {};
+function bindAsyncResource(fn, type) {
+ AsyncResource ??= require('async_hooks').AsyncResource;
+ const resource = new AsyncResource(type);
+ return function(...args) {
+ return resource.runInAsyncScope(fn, this, ...args);
+ };
+}
+
function eos(stream, options, callback) {
if (arguments.length === 2) {
callback = options;
@@ -66,8 +74,9 @@ function eos(stream, options, callback) {
validateFunction(callback, 'callback');
validateAbortSignal(options.signal, 'options.signal');
- AsyncLocalStorage ??= require('async_hooks').AsyncLocalStorage;
- callback = once(AsyncLocalStorage.bind(callback));
+ // Avoid AsyncResource.bind() because it calls ObjectDefineProperties which
+ // is a bottleneck here.
+ callback = once(bindAsyncResource(callback, 'STREAM_END_OF_STREAM'));
if (isReadableStream(stream) || isWritableStream(stream)) {
return eosWeb(stream, options, callback);
From 26b40bad021ff764828411194fb0f6de6efb08e1 Mon Sep 17 00:00:00 2001
From: Moonki Choi
Date: Wed, 17 Sep 2025 15:34:40 +0900
Subject: [PATCH 29/90] src: replace FIXED_ONE_BYTE_STRING with
Environment-cached strings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PR-URL: https://github.com/nodejs/node/pull/59891
Reviewed-By: James M Snell
Reviewed-By: Anna Henningsen
Reviewed-By: Luigi Pinca
Reviewed-By: Gerhard Stöbich
---
src/node_realm.cc | 3 +--
src/node_sqlite.cc | 6 ++----
src/node_webstorage.cc | 9 ++++-----
src/stream_wrap.cc | 3 +--
4 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/src/node_realm.cc b/src/node_realm.cc
index cd2b4c010759..66a8ee48fc68 100644
--- a/src/node_realm.cc
+++ b/src/node_realm.cc
@@ -52,8 +52,7 @@ void Realm::CreateProperties() {
CHECK(primordials->IsObject());
set_primordials(primordials.As());
- Local prototype_string =
- FIXED_ONE_BYTE_STRING(isolate(), "prototype");
+ Local prototype_string = env_->prototype_string();
#define V(EnvPropertyName, PrimordialsPropertyName) \
{ \
diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index 8b6fe36e1fec..13dde3c0d213 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -1215,9 +1215,7 @@ void DatabaseSync::CustomFunction(const FunctionCallbackInfo& args) {
argc = -1;
} else {
Local js_len;
- if (!fn->Get(env->context(),
- FIXED_ONE_BYTE_STRING(env->isolate(), "length"))
- .ToLocal(&js_len)) {
+ if (!fn->Get(env->context(), env->length_string()).ToLocal(&js_len)) {
return;
}
argc = js_len.As()->Value();
@@ -1448,7 +1446,7 @@ void DatabaseSync::CreateSession(const FunctionCallbackInfo& args) {
Local options = args[0].As();
- Local table_key = FIXED_ONE_BYTE_STRING(env->isolate(), "table");
+ Local table_key = env->table_string();
bool hasIt;
if (!options->HasOwnProperty(env->context(), table_key).To(&hasIt)) {
return;
diff --git a/src/node_webstorage.cc b/src/node_webstorage.cc
index 74ece724e207..e3c322378903 100644
--- a/src/node_webstorage.cc
+++ b/src/node_webstorage.cc
@@ -741,11 +741,10 @@ static void Initialize(Local target,
Local length_getter =
FunctionTemplate::New(isolate, StorageLengthGetter);
- ctor_tmpl->PrototypeTemplate()->SetAccessorProperty(
- FIXED_ONE_BYTE_STRING(isolate, "length"),
- length_getter,
- Local(),
- DontDelete);
+ ctor_tmpl->PrototypeTemplate()->SetAccessorProperty(env->length_string(),
+ length_getter,
+ Local(),
+ DontDelete);
SetProtoMethod(isolate, ctor_tmpl, "clear", Clear);
SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "getItem", GetItem);
diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc
index b7f3943bcd52..f45bda17ef2a 100644
--- a/src/stream_wrap.cc
+++ b/src/stream_wrap.cc
@@ -84,8 +84,7 @@ void LibuvStreamWrap::Initialize(Local target,
sw->InstanceTemplate()->Set(env->oncomplete_string(), v8::Null(isolate));
sw->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(isolate, "callback"),
v8::Null(isolate));
- sw->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(isolate, "handle"),
- v8::Null(isolate));
+ sw->InstanceTemplate()->Set(env->handle_string(), v8::Null(isolate));
sw->Inherit(AsyncWrap::GetConstructorTemplate(env));
From 36b68db7f5a6690930ffae42764dab86cb9105a2 Mon Sep 17 00:00:00 2001
From: Michael Smith
Date: Wed, 17 Sep 2025 06:26:52 -0400
Subject: [PATCH 30/90] src: reduce the nearest parent package JSON cache size
PR-URL: https://github.com/nodejs/node/pull/59888
Reviewed-By: Matteo Collina
Reviewed-By: Rafael Gonzaga
Reviewed-By: Edy Silva
Reviewed-By: Ruben Bridgewater
---
lib/internal/modules/package_json_reader.js | 56 +++++++++++++++++++--
src/node_modules.cc | 39 --------------
src/node_modules.h | 2 -
typings/internalBinding/modules.d.ts | 1 -
4 files changed, 52 insertions(+), 46 deletions(-)
diff --git a/lib/internal/modules/package_json_reader.js b/lib/internal/modules/package_json_reader.js
index fb669ea12eef..0f9442ea7ad7 100644
--- a/lib/internal/modules/package_json_reader.js
+++ b/lib/internal/modules/package_json_reader.js
@@ -6,7 +6,9 @@ const {
ObjectDefineProperty,
RegExpPrototypeExec,
SafeMap,
+ StringPrototypeEndsWith,
StringPrototypeIndexOf,
+ StringPrototypeLastIndexOf,
StringPrototypeSlice,
} = primordials;
const {
@@ -26,6 +28,7 @@ const {
const { kEmptyObject } = require('internal/util');
const modulesBinding = internalBinding('modules');
const path = require('path');
+const permission = require('internal/process/permission');
const { validateString } = require('internal/validators');
const internalFsBinding = internalBinding('fs');
@@ -127,6 +130,45 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
};
}
+/**
+ * Given a file path, walk the filesystem upwards until we find its closest parent
+ * `package.json` file, stopping when:
+ * 1. we find a `package.json` file;
+ * 2. we find a path that we do not have permission to read;
+ * 3. we find a containing `node_modules` directory;
+ * 4. or, we reach the filesystem root
+ * @returns {undefined | string}
+ */
+function findParentPackageJSON(checkPath) {
+ const enabledPermission = permission.isEnabled();
+
+ const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, path.sep);
+ let separatorIndex;
+
+ do {
+ separatorIndex = StringPrototypeLastIndexOf(checkPath, path.sep);
+ checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex);
+
+ if (enabledPermission && !permission.has('fs.read', checkPath + path.sep)) {
+ return undefined;
+ }
+
+ if (StringPrototypeEndsWith(checkPath, path.sep + 'node_modules')) {
+ return undefined;
+ }
+
+ const maybePackageJSONPath = checkPath + path.sep + 'package.json';
+ const stat = internalFsBinding.internalModuleStat(checkPath + path.sep + 'package.json');
+
+ const packageJSONExists = stat === 0;
+ if (packageJSONExists) {
+ return maybePackageJSONPath;
+ }
+ } while (separatorIndex > rootSeparatorIndex);
+
+ return undefined;
+}
+
/**
* Get the nearest parent package.json file from a given path.
* Return the package.json data and the path to the package.json file, or undefined.
@@ -134,11 +176,17 @@ function read(jsonPath, { base, specifier, isESM } = kEmptyObject) {
* @returns {undefined | DeserializedPackageConfig}
*/
function getNearestParentPackageJSON(checkPath) {
- if (nearestParentPackageJSONCache.has(checkPath)) {
- return nearestParentPackageJSONCache.get(checkPath);
+ const nearestParentPackageJSON = findParentPackageJSON(checkPath);
+
+ if (nearestParentPackageJSON === undefined) {
+ return undefined;
+ }
+
+ if (nearestParentPackageJSONCache.has(nearestParentPackageJSON)) {
+ return nearestParentPackageJSONCache.get(nearestParentPackageJSON);
}
- const result = modulesBinding.getNearestParentPackageJSON(checkPath);
+ const result = modulesBinding.readPackageJSON(nearestParentPackageJSON);
if (result === undefined) {
nearestParentPackageJSONCache.set(checkPath, undefined);
@@ -146,7 +194,7 @@ function getNearestParentPackageJSON(checkPath) {
}
const packageConfig = deserializePackageJSON(checkPath, result);
- nearestParentPackageJSONCache.set(checkPath, packageConfig);
+ nearestParentPackageJSONCache.set(nearestParentPackageJSON, packageConfig);
return packageConfig;
}
diff --git a/src/node_modules.cc b/src/node_modules.cc
index a6ca3ba074d3..60b03b1563b2 100644
--- a/src/node_modules.cc
+++ b/src/node_modules.cc
@@ -315,40 +315,6 @@ const BindingData::PackageConfig* BindingData::TraverseParent(
return nullptr;
}
-void BindingData::GetNearestParentPackageJSON(
- const v8::FunctionCallbackInfo