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 17f3f4b6798bb7..a1a0d4651c82be 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 01df32d2fb4ded..711d7e8a1e4de5 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 9257875c1c32f4..535c7339e3900e 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 c439d7cd555167..fed1803890923e 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 5ce088f4338bde..2940bf6d0c62de 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 5442c0e8d2c20c..3de5603c8a90e2 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 aa6e2973532eb4..b1c553d4e0ff75 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 3b6af35fbed0cf..3abaa8bd6d5ef4 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 d7fab14768ad0f..d6c7a3339e8dd6 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 32e3acbdaad499..27e237c6e3b0c7 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 d65ffba0ff6c1c..3dca9ec26ad290 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 e47161c2a854a0..cf8bc91018d08d 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 5993adaf556621..efed03fe5655ec 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 cc1aa047bdc217..3fe7b5435763a0 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 76046cf66c4fa8..5225e7f708ae8e 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 da1cfef4f9f8db..948a3590a547a4 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 70b6eadb4ce10c..edc5be8426ca66 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 2f39972874ceed..0d664b740107e2 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 37ba1dbafd4a67..00df654800cef8 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 fd8333a8c0d82d..48b15e7ba99d94 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 9c2ab2b626bb99..5f221564f4bd40 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 4bb718d95c4d59..f698a75aaaa164 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 0dfba0ac106c0d..9c253e1656f1a2 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 e949f98b2673af..970e64cd3e44da 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 4013eb9e666812..3633b9cc8a915b 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 bcfd0680555466..ba26bc133003e1 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 167390fcd70237..b0418125a3bd12 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 484575ce7d3622..e2f6a11aad031a 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 f1ec5477a13375..9760fb5e59137c 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 68e1127b50df92..ee6a127abc144e 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 bbb83bd907e61d..9cde69dc3a278a 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 1397bc5048ab30..e1ed227f4e3644 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 c0d12ef1a3e381..040928716ff8e4 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 31778889549a93..956fcdea77cf6f 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 fb41969e5629f2..8c64c331e88960 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 2060175c577477..500cc335794c6a 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 379ea248e49f2b..1aebfc354bfd2c 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 b7bb989a67165a..795230b845fb97 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 9e55028bc6bae4..42101dc55b1d49 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 125a6ab5b7c272..5e027004eb75e7 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 c7f5aeec64c2c6..be4a272d48c5de 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 d5679304f8ce35..6a68c794ad71ff 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 586d01806dc802..86008c0430e866 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 dc48efc55adec5..1e13ce0edb42c4 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 e0f65c54f1ae54..d29cfa8290479a 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 762db67a14a92d..4569b2f5fea203 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 e78f94f486fa24..a2b0ceccf1c2fb 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 a036d164bde65f..4e2941a69a51a8 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 04f8e5d7e00839..a2ce5becdbca84 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 16f7d512d1ca6c..8ba1ca1e3d4720 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 a2d935991505f1..29a1e44466912b 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 16a587591db07a..268338afb7295d 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 ea15ff20c96b97..8c28a651244cee 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 040fead6ce89f7..b4f99129dd9d68 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 cae32b74743e5b..ef77ee038da0cf 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 6c8a2bedc2f5f9..a2e829cf938811 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 e8c44e9249258e..c7b82c49410a7c 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 71c6807298594a..4f33f5523dd486 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 3936a927c4724c..08dd01f8fe3fba 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 47a39d7fc2735d..6bcb661c79bf8f 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 a7db0633e39829..ed906693ad6fd3 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 973afad93ecc9b..c11ea8c087848d 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 398c154096478c..5753f201319119 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 1a5127e028f826..101467ddeefe1f 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 dd7ee35f3c8cda..5a3202a4ff01cf 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 f2e9a00fc60271..2be387a1e73640 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 e68322f6acd4c4..c8abb8a2d8366a 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 c465029671edd8..8c670722f80564 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 93452be11a1e23..0f77764fe40b77 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 fe8894b78730b4..b6d6d883516bea 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 9ce5c3ce2a2803..9f5d0e63104a64 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 0c43d8f32c68bc..b09b88dd139345 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 8f3a5a18e573ec..fc29f1d31f0dfe 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 fe8b4d2049439e..8cfbdf38b5b0eb 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 abe90f92af34ac..e1b8405035546f 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 71b98a1777d074..d946d81cbd8afd 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 e69df00bca2963..9eb29fb4481a3c 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 d2ab3b88f85ab9..186bafe9463d02 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 4311e6b8c5d9f2..fceb0df0cd5636 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 c24440bd1e59d5..2933c61ce5fa91 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 6e27691a0b2a56..dbaba93ed54f93 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 83a4ce10c5d91b..493e6cb1d33cb2 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 74970ccf0445c4..bece14d8722854 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 815253042195a2..c1d66ea00bd301 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 74970ccf0445c4..bece14d8722854 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 dc128687164d57..8f19faa98668d9 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 bc586d135c0ac0..d05b2f70f74732 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 6f961dad20b536..5817f933f072bb 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 dc128687164d57..8f19faa98668d9 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 6c302f9228c5e4..1d9a3522fdb618 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 3348147f3ea63f..5dc62e5b9b15c9 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 ed512664d165e1..4ff77e2bfc5077 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 e1a4ae53c7269a..3093e46992df47 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 af069e4cc3df2f..39e1f084e01fe6 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 4f106df86b5d97..556f0dfbd72f3c 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 3cca23ec1b425e..e650ba236489bb 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 42ae54b5209a17..4ca762b771f953 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 459186018ecaec..eb3b91543ba832 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 8882c3c02de5f3..9e5d95121fc803 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 81b76cc15e4d9d..34679682f668e3 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 6d2344b8b392e0..2b69dbef11476d 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 bbbbf89379a118..bbf94f8c6d6f0c 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 dc99f5862a6224..c7b49300ec7fc6 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 fd5031ea4a819b..c10ca607c807da 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 ead9e3c7e4c87f..8f31d4901328ec 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 fb6c1be15e3ea1..770043489d442c 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 49b3f4bc8bf791..5ab4e57aa14387 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 6416d406ecd92a..c416987cf96536 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 033fb38d993690..d54914f51b5c28 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 bd91a82c58aa3d..e79fb3baeffaf0 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 3923544b3c7f03..682597d475dbe2 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 5e7d8bb4b2a91a..d145f9fd7786f4 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 a29037b85b5596..f0c938b487b913 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 cd96969eef56e3..1fdc4b141a6385 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 f29b59c40365e4..a70b7b9a2ef3de 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 aba315c84af6be..f83860de83a6ee 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 d8f6e9a0d7c207..8b6f66dcd10ce0 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 a63bc7448170a5..6edbc0f85a5f23 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 b1feb37f97e65a..0a05d36bd08eb1 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 cb1988b6c87353..93b1918dd013ea 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 e56db03a460f49..a78b52c47efe7e 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 71ee13917ab43e..a3f4be5e782914 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 5da01d0591d8b9..d2b1fc4f87ce55 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 af8c6446d92ae9..21d5427b9c9ed1 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 c9388088caefc5..acc8f2d92c333a 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 be84290240e8d2..c6a4bab4d8f894 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 ce0efcd2f4b849..734081750bf5cb 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 fdebccd4e245b6..9552895bc22683 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 312c8db5ac892d..37ff1b8737eb61 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 101d734340e2db..166735565c2761 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 bb3862c28f171e..898f6d610d706a 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 74df5f005fc1e4..3ad67413a2cd72 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 126e3b5df7741b..cce948fc162dda 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 b1b0721e887ca4..f455b6c30ed69b 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 63baf946d3ecdf..b14ab552ac9286 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 e66633d4d6d9d0..682939fc09d198 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 a3e21d4e3423d8..1f04c2f6c1d136 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 5c10939e657b83..08e269a26bdfef 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 1e830ee1fbc9dd..c830ceba237ed2 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 b59bd0d47c342d..2e93ce1f632a03 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 97967d37fd50df..a8adbbda2377e0 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 92268682bf9ba3..dce43f3c7e8c77 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 6733ebaf1024f2..6515f1696a798e 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 73ccb5381b8cba..a7cebf8874cd3f 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 1b1ddce412c714..97067bb89b9e36 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 b47dde96e7999e..f199cb3d640ad2 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 5f9f4ca6484242..85b085c9b650e1 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 34d6986b082560..2cb84fb4a0ef7a 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 5f218c62a4f80c..83274456496c15 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 6dca287b328cc8..75f3be8531a6d0 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 0d000c2cd96204..af68b9b24ea56f 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 780817c02045b1..610338bd8bd4a3 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 b42f0b103e6b42..02cc66b0150dd1 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 2db8bca1089ea5..cd710c428ddf17 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 f32d59c82db753..f464319bb0d854 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 fd034c0705e28a..cd9a7c26ac97b0 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 56959e9995d4b1..66519c550186ac 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 5c45170b49472a..8fa563cae83a09 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 e59cc5a422a7fb..29b3262891bfb6 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 98cc5faa23f7ff..44d5facd930b02 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 c43c249b090a54..376cabeb8a36aa 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 2f671212598076..c851b7a555380a 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 036031994e1313..a8f1f08ae120ad 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 2a7d646379ec4e..505563e99aad90 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 d3f9596e60684a..af860ad121a0a6 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 7f36fc2e7530f2..17d7de9e16e429 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 294bff4b69c5e5..a02bc9eece79bc 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 1ab9ef3e750ebb..642c26ba345279 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 d93b5141304c1f..b9c9f105a1c1fa 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 1ab9ef3e750ebb..642c26ba345279 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 b46a1ddb4ad57f..6958d47974a24c 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 67a9d094c397ae..99a49edc84bd2f 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 d8e39ef4b547aa..dc14f02c396dd7 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 b46a1ddb4ad57f..6958d47974a24c 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 b9cfdf5263f3c6..b198dea22a6a3e 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 42bfab4fea453b..c0526d9b2940ba 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 ef646e12ec8f3f..dc20fcab2b9339 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 f7874d9f36fdb1..6af18270ffdfbb 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 b0692f6df78c4c..8941fd3c51878e 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 5340af005eca40..a3b22bde63c6b0 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 5ec5febb66f839..8b8cc8f8badbaa 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 c97d3f5576749b..e416f4f99d60d8 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 18905b74db6e16..7499745f8019d0 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 352732b7e1264c..bc0f23df01f23d 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 330c8ce424b140..aeddbfe88fa5df 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 9e71ab90ee4ea0..5a3753ddf1ea1d 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 99cf43ccda6695..8210328523bdbe 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 e0327ee5655af8..6c69a4f1490ce3 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 0557affb4693f6..30a11a167e1288 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 6eb2a169918a51..7518673ce2e8b4 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 16cead7c7251dc..6a02bb6563e293 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 74d6ac12cdd800..448334f47105ab 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 e2319e1694542e..266e019be9c3da 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 c52e20d7429426..2bb7eea7aec571 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 9363ce73e51cde..ef0dcc0f6d1643 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 00000000000000..0f5b98ff046645
--- /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 083194ab4237b0..b85767803531eb 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 854b8e619281e4..e4eb159b398797 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 00000000000000..d0eb79f4f5dd72
--- /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 00000000000000..d81cc0710eb6cf
--- /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 3839338bc2da98..760d717c69ef8c 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 55d628f0c5e7f3..a6ca3ba074d340 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 7a06ef9230e3fa..ee544351d32e0b 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 ee544351d32e0b..080e3532c3c0c2 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 6d173a633266dc..d54f61a372b7a9 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 759bbfee76b573..83770306267768 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 13e41d67c635c2..cf7e45fe375f3e 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 00000000000000..9384d4a8361c74
--- /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 5f51add4cdf68a..ae8553ee2022d6 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 7a713a2ea432c1..00000000000000
--- 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 0c3571a80c10aa..916bbf15654784 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 9384d4a8361c74..eddd6af230801b 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 814ea396f60144..da2f9dcd74b2bb 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 603685290ad85f..caef82e37f7a3c 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 00000000000000..fe11794e9c032d
--- /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 00000000000000..f4d242aa508248
--- /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 b60f988be3214e..a215a1ab9c0d2b 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 ff636e44063aa7..c808fb414473d6 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 2043bd0a88e897..fce296c1d56848 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 f53aec68ce8bb3..2c9bd70cf4d45f 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 dc28053240aa0b..292eb0b2eac353 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 e129d73ef1581c..29346a81836160 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 4b2853479b9576..8b556a584fec2e 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 4cf4d52a16ea93..053ed5692d6a80 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 c11c2c36735dae..2d0e784e206b0b 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 67b4cc2037b8e0..da2c468f11cdc3 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 accf7830d91498..91bdf8d2d7ce5e 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 d0c04be7c6ebc3..ad1c2c9966085b 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 cc4838eeecb00f..8e7e5dc89409a1 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 71ded75c9d2572..a3f103bc0b8987 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 8ef079b5158249..d2711fcb9ae71f 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 de3d1f2f183274..9ed7df2afd8c15 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 39edad8f6b3f59..54ef253b0b7388 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 dcd6548d41be78..587b8cf0dd8e52 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 b21f7a8260ca6a..eb097bcb00aaf0 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 587b8cf0dd8e52..bf3ef4fd4b5d24 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 97178d2ae87e5f..ea2e37563034e2 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 00000000000000..fd247b4b063192
--- /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 77bfa8a76cdf42..06a4f8a2398555 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 f8be25f6f880b8..5ccf2264e9aa4d 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 447a23ad70166e..c3077e9b323cac 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 00aa708bea59f1..c027770afa4798 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 945d02c2d1c0e7..83d61daa36e1b7 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 1278a0a0903c65..24ac32b1e51993 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 8521730bd03cdf..e2148e954217b9 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 466baf31c14c76..e9c0208b75986d 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 9862585a92391c..087ea563c9dfd4 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 38506f501c4a05..3f937df2250380 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 fe8488e1bfa50e..34ba4ce440c53f 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 d2711fcb9ae71f..ff046283257568 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 9ed7df2afd8c15..7b2efa49468c0b 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 00000000000000..0020e6b4a9e917
--- /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 00000000000000..9c3066124b7c7b
--- /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 00000000000000..661e52c566f789
--- /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 cb0a9242e051ac..9b4a0b9d6f3b35 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 cd2b4c0107594a..66a8ee48fc68b2 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 8b6fe36e1fece1..13dde3c0d21337 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 74ece724e207a6..e3c3223789032b 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 b7f3943bcd5263..f45bda17ef2a19 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 fb669ea12eeff3..0f9442ea7ad746 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 a6ca3ba074d340..60b03b1563b230 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