From 23468fd76b62b48aa49920f7f06dbe23396cfeca Mon Sep 17 00:00:00 2001 From: Tim Perry <1526883+pimterry@users.noreply.github.com> Date: Sat, 20 Sep 2025 20:18:23 +0200 Subject: [PATCH 01/36] http2: fix allowHttp1+Upgrade, broken by shouldUpgradeCallback This is required to use HTTP/1 websockets on an HTTP/2 server, which is fairly common as websockets over HTTP/2 is much less widely supported. This was broken by the recent shouldUpgradeCallback HTTP/1 addition, which wasn't correctly added to the corresponding allowHttp1 part of the HTTP/2 implementation. PR-URL: https://github.com/nodejs/node/pull/59924 Backport-PR-URL: https://github.com/nodejs/node/pull/60341 Reviewed-By: Luigi Pinca Reviewed-By: Rafael Gonzaga Reviewed-By: Matteo Collina --- lib/internal/http2/core.js | 3 + test/common/websocket-server.js | 108 ++++++++++++++++++ .../test-http2-allow-http1-upgrade-ws.js | 39 +++++++ 3 files changed, 150 insertions(+) create mode 100644 test/common/websocket-server.js create mode 100644 test/parallel/test-http2-allow-http1-upgrade-ws.js diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 73a478a7b708e2..335113acc8c23d 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -3379,6 +3379,9 @@ class Http2SecureServer extends TLSServer { this.headersTimeout = 60_000; // Minimum between 60 seconds or requestTimeout this.requestTimeout = 300_000; // 5 minutes this.connectionsCheckingInterval = 30_000; // 30 seconds + this.shouldUpgradeCallback = function() { + return this.listenerCount('upgrade') > 0; + }; this.on('listening', setupConnectionsTracking); } if (typeof requestListener === 'function') diff --git a/test/common/websocket-server.js b/test/common/websocket-server.js new file mode 100644 index 00000000000000..7f2447396972f7 --- /dev/null +++ b/test/common/websocket-server.js @@ -0,0 +1,108 @@ +'use strict'; +const common = require('./index'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http = require('http'); +const crypto = require('crypto'); + +class WebSocketServer { + constructor({ + port = 0, + server, + }) { + this.port = port; + this.server = server || http.createServer(); + this.clients = new Set(); + + this.server.on('upgrade', this.handleUpgrade.bind(this)); + } + + start() { + return new Promise((resolve) => { + this.server.listen(this.port, () => { + this.port = this.server.address().port; + resolve(); + }); + }).catch((err) => { + console.error('Failed to start WebSocket server:', err); + }); + } + + handleUpgrade(req, socket, head) { + const key = req.headers['sec-websocket-key']; + const acceptKey = this.generateAcceptValue(key); + const responseHeaders = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${acceptKey}`, + ]; + + socket.write(responseHeaders.join('\r\n') + '\r\n\r\n'); + this.clients.add(socket); + + socket.on('data', (buffer) => { + const opcode = buffer[0] & 0x0f; + + if (opcode === 0x8) { + // Send a minimal close frame in response: + socket.write(Buffer.from([0x88, 0x00])); + socket.end(); + this.clients.delete(socket); + return; + } + + socket.write(this.encodeMessage('Hello from server!')); + }); + + socket.on('close', () => { + this.clients.delete(socket); + }); + + socket.on('error', (err) => { + console.error('Socket error:', err); + this.clients.delete(socket); + }); + } + + generateAcceptValue(secWebSocketKey) { + return crypto + .createHash('sha1') + .update(secWebSocketKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary') + .digest('base64'); + } + + decodeMessage(buffer) { + const secondByte = buffer[1]; + const length = secondByte & 127; + const maskStart = 2; + const dataStart = maskStart + 4; + const masks = buffer.slice(maskStart, dataStart); + const data = buffer.slice(dataStart, dataStart + length); + const result = Buffer.alloc(length); + + for (let i = 0; i < length; i++) { + result[i] = data[i] ^ masks[i % 4]; + } + + return result.toString(); + } + + encodeMessage(message) { + const msgBuffer = Buffer.from(message); + const length = msgBuffer.length; + const frame = [0x81]; + + if (length < 126) { + frame.push(length); + } else if (length < 65536) { + frame.push(126, (length >> 8) & 0xff, length & 0xff); + } else { + throw new Error('Message too long'); + } + + return Buffer.concat([Buffer.from(frame), msgBuffer]); + } +} + +module.exports = WebSocketServer; diff --git a/test/parallel/test-http2-allow-http1-upgrade-ws.js b/test/parallel/test-http2-allow-http1-upgrade-ws.js new file mode 100644 index 00000000000000..028dc4e4cde71c --- /dev/null +++ b/test/parallel/test-http2-allow-http1-upgrade-ws.js @@ -0,0 +1,39 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const http2 = require('http2'); + +const undici = require('internal/deps/undici/undici'); +const WebSocketServer = require('../common/websocket-server'); + +(async function main() { + const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), + allowHTTP1: true, + }); + + server.on('request', common.mustNotCall()); + new WebSocketServer({ server }); // Handles websocket 'upgrade' events + + await new Promise((resolve) => server.listen(0, resolve)); + + await new Promise((resolve, reject) => { + const ws = new WebSocket(`wss://localhost:${server.address().port}`, { + dispatcher: new undici.EnvHttpProxyAgent({ + connect: { rejectUnauthorized: false } + }) + }); + ws.addEventListener('open', common.mustCall(() => { + ws.close(); + resolve(); + })); + ws.addEventListener('error', reject); + }); + + server.close(); +})().then(common.mustCall()); From fe492c7ace76b85e04a92fc87a713e4fd8ed9d6b Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Sun, 13 Jul 2025 21:10:18 +0100 Subject: [PATCH 02/36] process: fix hrtime fast call signatures PR-URL: https://github.com/nodejs/node/pull/59600 Reviewed-By: Yagiz Nizipli Reviewed-By: Anna Henningsen --- src/node_process.h | 25 +++++++++--------- src/node_process_methods.cc | 28 ++++++++++----------- test/parallel/test-process-hrtime-bigint.js | 15 ++++++++++- test/parallel/test-process-hrtime.js | 15 ++++++++++- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/node_process.h b/src/node_process.h index b88a2f99483ad2..64393302d2cfd9 100644 --- a/src/node_process.h +++ b/src/node_process.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_debug.h" #include "node_snapshotable.h" #include "v8-fast-api-calls.h" #include "v8.h" @@ -72,23 +73,23 @@ class BindingData : public SnapshotableObject { SET_SELF_SIZE(BindingData) static BindingData* FromV8Value(v8::Local receiver); - static void NumberImpl(BindingData* receiver); + static void HrtimeImpl(BindingData* receiver); - static void FastNumber(v8::Local unused, - v8::Local receiver) { - NumberImpl(FromV8Value(receiver)); + static void FastHrtime(v8::Local receiver) { + TRACK_V8_FAST_API_CALL("process.hrtime"); + HrtimeImpl(FromV8Value(receiver)); } - static void SlowNumber(const v8::FunctionCallbackInfo& args); + static void SlowHrtime(const v8::FunctionCallbackInfo& args); - static void BigIntImpl(BindingData* receiver); + static void HrtimeBigIntImpl(BindingData* receiver); - static void FastBigInt(v8::Local unused, - v8::Local receiver) { - BigIntImpl(FromV8Value(receiver)); + static void FastHrtimeBigInt(v8::Local receiver) { + TRACK_V8_FAST_API_CALL("process.hrtimeBigInt"); + HrtimeBigIntImpl(FromV8Value(receiver)); } - static void SlowBigInt(const v8::FunctionCallbackInfo& args); + static void SlowHrtimeBigInt(const v8::FunctionCallbackInfo& args); static void LoadEnvFile(const v8::FunctionCallbackInfo& args); @@ -101,8 +102,8 @@ class BindingData : public SnapshotableObject { // These need to be static so that we have their addresses available to // register as external references in the snapshot at environment creation // time. - static v8::CFunction fast_number_; - static v8::CFunction fast_bigint_; + static v8::CFunction fast_hrtime_; + static v8::CFunction fast_hrtime_bigint_; }; } // namespace process diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 1cb08b715865f8..9dcca7509f37f2 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -651,22 +651,22 @@ BindingData::BindingData(Realm* realm, hrtime_buffer_.MakeWeak(); } -v8::CFunction BindingData::fast_number_(v8::CFunction::Make(FastNumber)); -v8::CFunction BindingData::fast_bigint_(v8::CFunction::Make(FastBigInt)); +CFunction BindingData::fast_hrtime_(CFunction::Make(FastHrtime)); +CFunction BindingData::fast_hrtime_bigint_(CFunction::Make(FastHrtimeBigInt)); void BindingData::AddMethods(Isolate* isolate, Local target) { SetFastMethodNoSideEffect( - isolate, target, "hrtime", SlowNumber, &fast_number_); + isolate, target, "hrtime", SlowHrtime, &fast_hrtime_); SetFastMethodNoSideEffect( - isolate, target, "hrtimeBigInt", SlowBigInt, &fast_bigint_); + isolate, target, "hrtimeBigInt", SlowHrtimeBigInt, &fast_hrtime_bigint_); } void BindingData::RegisterExternalReferences( ExternalReferenceRegistry* registry) { - registry->Register(SlowNumber); - registry->Register(SlowBigInt); - registry->Register(fast_number_); - registry->Register(fast_bigint_); + registry->Register(SlowHrtime); + registry->Register(SlowHrtimeBigInt); + registry->Register(fast_hrtime_); + registry->Register(fast_hrtime_bigint_); } BindingData* BindingData::FromV8Value(Local value) { @@ -688,14 +688,14 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const { // broken into the upper/lower 32 bits to be converted back in JS, // because there is no Uint64Array in JS. // The third entry contains the remaining nanosecond part of the value. -void BindingData::NumberImpl(BindingData* receiver) { +void BindingData::HrtimeImpl(BindingData* receiver) { uint64_t t = uv_hrtime(); receiver->hrtime_buffer_[0] = (t / NANOS_PER_SEC) >> 32; receiver->hrtime_buffer_[1] = (t / NANOS_PER_SEC) & 0xffffffff; receiver->hrtime_buffer_[2] = t % NANOS_PER_SEC; } -void BindingData::BigIntImpl(BindingData* receiver) { +void BindingData::HrtimeBigIntImpl(BindingData* receiver) { uint64_t t = uv_hrtime(); // The buffer is a Uint32Array, so we need to reinterpret it as a // Uint64Array to write the value. The buffer is valid at this scope so we @@ -705,12 +705,12 @@ void BindingData::BigIntImpl(BindingData* receiver) { fields[0] = t; } -void BindingData::SlowBigInt(const FunctionCallbackInfo& args) { - BigIntImpl(FromJSObject(args.This())); +void BindingData::SlowHrtimeBigInt(const FunctionCallbackInfo& args) { + HrtimeBigIntImpl(FromJSObject(args.This())); } -void BindingData::SlowNumber(const v8::FunctionCallbackInfo& args) { - NumberImpl(FromJSObject(args.This())); +void BindingData::SlowHrtime(const FunctionCallbackInfo& args) { + HrtimeImpl(FromJSObject(args.This())); } bool BindingData::PrepareForSerialization(Local context, diff --git a/test/parallel/test-process-hrtime-bigint.js b/test/parallel/test-process-hrtime-bigint.js index e5ce40a994d815..9d0e0e347b0179 100644 --- a/test/parallel/test-process-hrtime-bigint.js +++ b/test/parallel/test-process-hrtime-bigint.js @@ -1,10 +1,13 @@ +// Flags: --allow-natives-syntax --expose-internals --no-warnings 'use strict'; // Tests that process.hrtime.bigint() works. -require('../common'); +const common = require('../common'); const assert = require('assert'); +const { internalBinding } = require('internal/test/binding'); + const start = process.hrtime.bigint(); assert.strictEqual(typeof start, 'bigint'); @@ -12,3 +15,13 @@ const end = process.hrtime.bigint(); assert.strictEqual(typeof end, 'bigint'); assert(end - start >= 0n); + +eval('%PrepareFunctionForOptimization(process.hrtime.bigint)'); +assert(process.hrtime.bigint()); +eval('%OptimizeFunctionOnNextCall(process.hrtime.bigint)'); +assert(process.hrtime.bigint()); + +if (common.isDebug) { + const { getV8FastApiCallCount } = internalBinding('debug'); + assert.strictEqual(getV8FastApiCallCount('process.hrtimeBigInt'), 1); +} diff --git a/test/parallel/test-process-hrtime.js b/test/parallel/test-process-hrtime.js index 34ef514aac309b..e5815ebe10cc91 100644 --- a/test/parallel/test-process-hrtime.js +++ b/test/parallel/test-process-hrtime.js @@ -19,10 +19,13 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +// Flags: --allow-natives-syntax --expose-internals --no-warnings 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); +const { internalBinding } = require('internal/test/binding'); + // The default behavior, return an Array "tuple" of numbers const tuple = process.hrtime(); @@ -72,3 +75,13 @@ function validateTuple(tuple) { const diff = process.hrtime([0, 1e9 - 1]); assert(diff[1] >= 0); // https://github.com/nodejs/node/issues/4751 + +eval('%PrepareFunctionForOptimization(process.hrtime)'); +assert(process.hrtime()); +eval('%OptimizeFunctionOnNextCall(process.hrtime)'); +assert(process.hrtime()); + +if (common.isDebug) { + const { getV8FastApiCallCount } = internalBinding('debug'); + assert.strictEqual(getV8FastApiCallCount('process.hrtime'), 1); +} From 81548abdf6e682e8c1d1f3078b95fb429a484112 Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Sun, 13 Jul 2025 21:23:11 +0100 Subject: [PATCH 03/36] wasi: fix WasiFunction fast call signature PR-URL: https://github.com/nodejs/node/pull/59600 Reviewed-By: Yagiz Nizipli Reviewed-By: Anna Henningsen --- src/node_wasi.cc | 1 - src/node_wasi.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 090866960beb8f..85e549e4592e1d 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -265,7 +265,6 @@ inline void EinvalError() {} template R WASI::WasiFunction::FastCallback( - Local unused, Local receiver, Args... args, // NOLINTNEXTLINE(runtime/references) This is V8 api. diff --git a/src/node_wasi.h b/src/node_wasi.h index ef7d2e83b6728a..25551936e6be36 100644 --- a/src/node_wasi.h +++ b/src/node_wasi.h @@ -160,8 +160,7 @@ class WASI : public BaseObject, v8::Local); private: - static R FastCallback(v8::Local unused, - v8::Local receiver, + static R FastCallback(v8::Local receiver, Args..., v8::FastApiCallbackOptions&); From cee362242bbd13d0a7dec89228a2bec61acd7a9a Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Sun, 13 Jul 2025 21:38:50 +0100 Subject: [PATCH 04/36] timers: fix binding fast call signatures PR-URL: https://github.com/nodejs/node/pull/59600 Reviewed-By: Yagiz Nizipli Reviewed-By: Anna Henningsen --- src/timers.cc | 15 ++++++------- src/timers.h | 11 +++------- test/parallel/test-timers-fast-calls.js | 28 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 test/parallel/test-timers-fast-calls.js diff --git a/src/timers.cc b/src/timers.cc index bf90e68479da14..da4206187f7c7d 100644 --- a/src/timers.cc +++ b/src/timers.cc @@ -53,9 +53,8 @@ void BindingData::SlowScheduleTimer(const FunctionCallbackInfo& args) { } } -void BindingData::FastScheduleTimer(Local unused, - Local receiver, - int64_t duration) { +void BindingData::FastScheduleTimer(Local receiver, int64_t duration) { + TRACK_V8_FAST_API_CALL("timers.scheduleTimer"); ScheduleTimerImpl(FromJSObject(receiver), duration); } @@ -69,9 +68,8 @@ void BindingData::SlowToggleTimerRef( args[0]->IsTrue()); } -void BindingData::FastToggleTimerRef(Local unused, - Local receiver, - bool ref) { +void BindingData::FastToggleTimerRef(Local receiver, bool ref) { + TRACK_V8_FAST_API_CALL("timers.toggleTimerRef"); ToggleTimerRefImpl(FromJSObject(receiver), ref); } @@ -85,9 +83,8 @@ void BindingData::SlowToggleImmediateRef( args[0]->IsTrue()); } -void BindingData::FastToggleImmediateRef(Local unused, - Local receiver, - bool ref) { +void BindingData::FastToggleImmediateRef(Local receiver, bool ref) { + TRACK_V8_FAST_API_CALL("timers.toggleImmediateRef"); ToggleImmediateRefImpl(FromJSObject(receiver), ref); } diff --git a/src/timers.h b/src/timers.h index 3c3a4d60d34ae8..01cc612e8b26a2 100644 --- a/src/timers.h +++ b/src/timers.h @@ -31,23 +31,18 @@ class BindingData : public SnapshotableObject { static void SlowScheduleTimer( const v8::FunctionCallbackInfo& args); - static void FastScheduleTimer(v8::Local unused, - v8::Local receiver, + static void FastScheduleTimer(v8::Local receiver, int64_t duration); static void ScheduleTimerImpl(BindingData* data, int64_t duration); static void SlowToggleTimerRef( const v8::FunctionCallbackInfo& args); - static void FastToggleTimerRef(v8::Local unused, - v8::Local receiver, - bool ref); + static void FastToggleTimerRef(v8::Local receiver, bool ref); static void ToggleTimerRefImpl(BindingData* data, bool ref); static void SlowToggleImmediateRef( const v8::FunctionCallbackInfo& args); - static void FastToggleImmediateRef(v8::Local unused, - v8::Local receiver, - bool ref); + static void FastToggleImmediateRef(v8::Local receiver, bool ref); static void ToggleImmediateRefImpl(BindingData* data, bool ref); static void CreatePerIsolateProperties(IsolateData* isolate_data, diff --git a/test/parallel/test-timers-fast-calls.js b/test/parallel/test-timers-fast-calls.js new file mode 100644 index 00000000000000..06387f46c363d4 --- /dev/null +++ b/test/parallel/test-timers-fast-calls.js @@ -0,0 +1,28 @@ +// Flags: --allow-natives-syntax --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { internalBinding } = require('internal/test/binding'); +const binding = internalBinding('timers'); + +function testFastCalls() { + binding.scheduleTimer(1); + binding.toggleTimerRef(true); + binding.toggleTimerRef(false); + binding.toggleImmediateRef(true); + binding.toggleImmediateRef(false); +} + +eval('%PrepareFunctionForOptimization(testFastCalls)'); +testFastCalls(); +eval('%OptimizeFunctionOnNextCall(testFastCalls)'); +testFastCalls(); + +if (common.isDebug) { + const { getV8FastApiCallCount } = internalBinding('debug'); + assert.strictEqual(getV8FastApiCallCount('timers.scheduleTimer'), 1); + assert.strictEqual(getV8FastApiCallCount('timers.toggleTimerRef'), 2); + assert.strictEqual(getV8FastApiCallCount('timers.toggleImmediateRef'), 2); +} From 78efd1be4ac41ac71ca3fad210dd85d09cb049c2 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Mon, 1 Sep 2025 18:47:05 +0100 Subject: [PATCH 05/36] benchmark: update num to n in dgram offset-length PR-URL: https://github.com/nodejs/node/pull/59708 Reviewed-By: Rafael Gonzaga Reviewed-By: Ruben Bridgewater --- benchmark/dgram/offset-length.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/benchmark/dgram/offset-length.js b/benchmark/dgram/offset-length.js index ac0fee731eab20..a41c81a82ebc10 100644 --- a/benchmark/dgram/offset-length.js +++ b/benchmark/dgram/offset-length.js @@ -5,28 +5,28 @@ 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: [1, 64, 256, 1024], - num: [100], + n: [100], type: ['send', 'recv'], dur: [5], }); -function main({ dur, len, num, type }) { +function main({ dur, len, n, type }) { const chunk = Buffer.allocUnsafe(len); let sent = 0; let received = 0; 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, 0, chunk.length, PORT, '127.0.0.1', onsend); } }); From af0a8ba7f80e3af6c0708d11dc17c14c4779d2c1 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Mon, 1 Sep 2025 19:48:18 +0100 Subject: [PATCH 06/36] benchmark: adjust dgram offset-length len values PR-URL: https://github.com/nodejs/node/pull/59708 Reviewed-By: Rafael Gonzaga Reviewed-By: Ruben Bridgewater --- benchmark/dgram/offset-length.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/dgram/offset-length.js b/benchmark/dgram/offset-length.js index a41c81a82ebc10..85381da8de78e0 100644 --- a/benchmark/dgram/offset-length.js +++ b/benchmark/dgram/offset-length.js @@ -9,7 +9,7 @@ const PORT = common.PORT; // 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: [1, 64, 256, 1024], + len: [1, 512, 1024], n: [100], type: ['send', 'recv'], dur: [5], From af3a59dba8809e6828be8ca9024fe22ac5564fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20St=C3=B6bich?= Date: Wed, 24 Sep 2025 19:22:00 +0200 Subject: [PATCH 07/36] test: verify tracing channel doesn't swallow unhandledRejection Add a test to verify that TracingChannel.tracePromise doesn't swallow unhandledRejection events in case no then/catch handler is set by user. PR-URL: https://github.com/nodejs/node/pull/59974 Reviewed-By: Luigi Pinca Reviewed-By: Darshan Sen --- ...annel-tracing-channel-promise-unhandled.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/parallel/test-diagnostics-channel-tracing-channel-promise-unhandled.js diff --git a/test/parallel/test-diagnostics-channel-tracing-channel-promise-unhandled.js b/test/parallel/test-diagnostics-channel-tracing-channel-promise-unhandled.js new file mode 100644 index 00000000000000..e24459774533ca --- /dev/null +++ b/test/parallel/test-diagnostics-channel-tracing-channel-promise-unhandled.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedError = new Error('test'); +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +process.on('unhandledRejection', common.mustCall((reason) => { + assert.deepStrictEqual(reason, expectedError); +})); + +function check(found) { + assert.deepStrictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(check), + asyncEnd: common.mustCall(check), + error: common.mustCall((found) => { + check(found); + assert.deepStrictEqual(found.error, expectedError); + }) +}; + +channel.subscribe(handlers); + +// Set no then/catch handler to verify unhandledRejection happens +channel.tracePromise(function(value) { + assert.deepStrictEqual(this, thisArg); + return Promise.reject(value); +}, input, thisArg, expectedError); From 4e8d99f0dc3d5afab36fb86f6f37f74386e7c981 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Fri, 12 Sep 2025 20:31:51 +0100 Subject: [PATCH 08/36] benchmark: update num to n in dgram offset-length PR-URL: https://github.com/nodejs/node/pull/59872 Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- benchmark/dgram/single-buffer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmark/dgram/single-buffer.js b/benchmark/dgram/single-buffer.js index 6a7fa3dde22942..bab8cee1594f24 100644 --- a/benchmark/dgram/single-buffer.js +++ b/benchmark/dgram/single-buffer.js @@ -10,23 +10,23 @@ const PORT = common.PORT; // event loop cycles more than anything else. const bench = common.createBenchmark(main, { len: [1, 64, 256, 1024], - num: [100], + n: [100], type: ['send', 'recv'], dur: [5], }); -function main({ dur, len, num, type }) { +function main({ dur, len, num: n, type }) { const chunk = Buffer.allocUnsafe(len); let sent = 0; let received = 0; 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 6764ce8756b0955b4b08c9406b663f6fd2696eb2 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Fri, 12 Sep 2025 20:42:23 +0100 Subject: [PATCH 09/36] benchmark: update count to n in permission startup PR-URL: https://github.com/nodejs/node/pull/59872 Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- benchmark/permission/permission-startup.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmark/permission/permission-startup.js b/benchmark/permission/permission-startup.js index 6a197cdff56111..d95caa01f605e0 100644 --- a/benchmark/permission/permission-startup.js +++ b/benchmark/permission/permission-startup.js @@ -19,12 +19,12 @@ const bench = common.createBenchmark(main, { ], prefixPath: ['/tmp'], nFiles: [10, 100, 1000], - count: [30], + n: [30], }); function spawnProcess(script, bench, state) { const cmd = process.execPath || process.argv[0]; - while (state.finished < state.count) { + while (state.finished < state.n) { const child = spawnSync(cmd, script); if (child.status !== 0) { console.log('---- STDOUT ----'); @@ -39,13 +39,13 @@ function spawnProcess(script, bench, state) { bench.start(); } - if (state.finished === state.count) { - bench.end(state.count); + if (state.finished === state.n) { + bench.end(state.n); } } } -function main({ count, script, nFiles, prefixPath }) { +function main({ n, script, nFiles, prefixPath }) { script = path.resolve(__dirname, '../../', `${script}.js`); const optionsWithScript = [ '--permission', @@ -54,6 +54,6 @@ function main({ count, script, nFiles, prefixPath }) { script, ]; const warmup = 3; - const state = { count, finished: -warmup }; + const state = { n, finished: -warmup }; spawnProcess(optionsWithScript, bench, state); } From af33e8e668c463041fdbb33cf9ba6d3021aef1b0 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Fri, 12 Sep 2025 20:46:24 +0100 Subject: [PATCH 10/36] benchmark: remove unused variable from util/priority-queue PR-URL: https://github.com/nodejs/node/pull/59872 Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- benchmark/util/priority-queue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/util/priority-queue.js b/benchmark/util/priority-queue.js index 2301c5a1ef6892..0a880a1c7cf29d 100644 --- a/benchmark/util/priority-queue.js +++ b/benchmark/util/priority-queue.js @@ -6,7 +6,7 @@ const bench = common.createBenchmark(main, { n: [1e5], }, { flags: ['--expose-internals'] }); -function main({ n, type }) { +function main({ n }) { const PriorityQueue = require('internal/priority_queue'); const queue = new PriorityQueue(); bench.start(); From c7c597e2ca80fdae5d845f84263319164ab52539 Mon Sep 17 00:00:00 2001 From: iknoom Date: Sat, 20 Sep 2025 15:33:45 +0900 Subject: [PATCH 11/36] src: use RAII for uv_process_options_t PR-URL: https://github.com/nodejs/node/pull/59945 Reviewed-By: Anna Henningsen --- src/process_wrap.cc | 83 ++++++++++++++++++++++----------------------- src/spawn_sync.cc | 44 +++++++++++------------- src/spawn_sync.h | 10 +++--- 3 files changed, 64 insertions(+), 73 deletions(-) diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 2853b0dc129fc3..d41ef9002b3db7 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -116,9 +116,10 @@ class ProcessWrap : public HandleWrap { return Just(stream); } - static Maybe ParseStdioOptions(Environment* env, - Local js_options, - uv_process_options_t* options) { + static Maybe ParseStdioOptions( + Environment* env, + Local js_options, + std::vector* options_stdio) { Local context = env->context(); Local stdio_key = env->stdio_string(); Local stdios_val; @@ -132,8 +133,7 @@ class ProcessWrap : public HandleWrap { Local stdios = stdios_val.As(); uint32_t len = stdios->Length(); - options->stdio = new uv_stdio_container_t[len]; - options->stdio_count = len; + options_stdio->resize(len); for (uint32_t i = 0; i < len; i++) { Local val; @@ -147,23 +147,23 @@ class ProcessWrap : public HandleWrap { } if (type->StrictEquals(env->ignore_string())) { - options->stdio[i].flags = UV_IGNORE; + (*options_stdio)[i].flags = UV_IGNORE; } else if (type->StrictEquals(env->pipe_string())) { - options->stdio[i].flags = static_cast( + (*options_stdio)[i].flags = static_cast( UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE); - if (!StreamForWrap(env, stdio).To(&options->stdio[i].data.stream)) { + if (!StreamForWrap(env, stdio).To(&(*options_stdio)[i].data.stream)) { return Nothing(); } } else if (type->StrictEquals(env->overlapped_string())) { - options->stdio[i].flags = static_cast( - UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE | - UV_OVERLAPPED_PIPE); - if (!StreamForWrap(env, stdio).To(&options->stdio[i].data.stream)) { + (*options_stdio)[i].flags = + static_cast(UV_CREATE_PIPE | UV_READABLE_PIPE | + UV_WRITABLE_PIPE | UV_OVERLAPPED_PIPE); + if (!StreamForWrap(env, stdio).To(&(*options_stdio)[i].data.stream)) { return Nothing(); } } else if (type->StrictEquals(env->wrap_string())) { - options->stdio[i].flags = UV_INHERIT_STREAM; - if (!StreamForWrap(env, stdio).To(&options->stdio[i].data.stream)) { + (*options_stdio)[i].flags = UV_INHERIT_STREAM; + if (!StreamForWrap(env, stdio).To(&(*options_stdio)[i].data.stream)) { return Nothing(); } } else { @@ -174,8 +174,8 @@ class ProcessWrap : public HandleWrap { } CHECK(fd_value->IsNumber()); int fd = FromV8Value(fd_value); - options->stdio[i].flags = UV_INHERIT_FD; - options->stdio[i].data.fd = fd; + (*options_stdio)[i].flags = UV_INHERIT_FD; + (*options_stdio)[i].data.fd = fd; } } return JustVoid(); @@ -199,8 +199,6 @@ class ProcessWrap : public HandleWrap { options.exit_cb = OnExit; - // TODO(bnoordhuis) is this possible to do without mallocing ? - // options.file Local file_v; if (!js_options->Get(context, env->file_string()).ToLocal(&file_v)) { @@ -251,23 +249,26 @@ class ProcessWrap : public HandleWrap { if (!js_options->Get(context, env->args_string()).ToLocal(&argv_v)) { return; } - if (!argv_v.IsEmpty() && argv_v->IsArray()) { + std::vector options_args; + std::vector args_vals; + if (argv_v->IsArray()) { Local js_argv = argv_v.As(); int argc = js_argv->Length(); CHECK_LT(argc, INT_MAX); // Check for overflow. - - // Heap allocate to detect errors. +1 is for nullptr. - options.args = new char*[argc + 1]; + args_vals.reserve(argc); + options_args.resize(argc + 1); for (int i = 0; i < argc; i++) { Local val; if (!js_argv->Get(context, i).ToLocal(&val)) { return; } node::Utf8Value arg(env->isolate(), val); - options.args[i] = strdup(*arg); - CHECK_NOT_NULL(options.args[i]); + args_vals.emplace_back(arg.ToString()); + options_args[i] = const_cast(args_vals.back().c_str()); + CHECK_NOT_NULL(options_args[i]); } - options.args[argc] = nullptr; + options_args[argc] = nullptr; + options.args = options_args.data(); } // options.cwd @@ -286,27 +287,35 @@ class ProcessWrap : public HandleWrap { if (!js_options->Get(context, env->env_pairs_string()).ToLocal(&env_v)) { return; } - if (!env_v.IsEmpty() && env_v->IsArray()) { + std::vector options_env; + std::vector env_vals; + if (env_v->IsArray()) { Local env_opt = env_v.As(); int envc = env_opt->Length(); CHECK_LT(envc, INT_MAX); // Check for overflow. - options.env = new char*[envc + 1]; // Heap allocated to detect errors. + env_vals.reserve(envc); + options_env.resize(envc + 1); for (int i = 0; i < envc; i++) { Local val; if (!env_opt->Get(context, i).ToLocal(&val)) { return; } node::Utf8Value pair(env->isolate(), val); - options.env[i] = strdup(*pair); - CHECK_NOT_NULL(options.env[i]); + env_vals.emplace_back(pair.ToString()); + options_env[i] = const_cast(env_vals.back().c_str()); + CHECK_NOT_NULL(options_env[i]); } - options.env[envc] = nullptr; + options_env[envc] = nullptr; + options.env = options_env.data(); } // options.stdio - if (ParseStdioOptions(env, js_options, &options).IsNothing()) { + std::vector options_stdio; + if (ParseStdioOptions(env, js_options, &options_stdio).IsNothing()) { return; } + options.stdio = options_stdio.data(); + options.stdio_count = options_stdio.size(); // options.windowsHide Local hide_v; @@ -361,18 +370,6 @@ class ProcessWrap : public HandleWrap { } } - if (options.args) { - for (int i = 0; options.args[i]; i++) free(options.args[i]); - delete [] options.args; - } - - if (options.env) { - for (int i = 0; options.env[i]; i++) free(options.env[i]); - delete [] options.env; - } - - delete[] options.stdio; - args.GetReturnValue().Set(err); } diff --git a/src/spawn_sync.cc b/src/spawn_sync.cc index 574afe60df8efe..47c4aaef348433 100644 --- a/src/spawn_sync.cc +++ b/src/spawn_sync.cc @@ -403,7 +403,6 @@ void SyncProcessRunner::Spawn(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(result); } - SyncProcessRunner::SyncProcessRunner(Environment* env) : max_buffer_(0), timeout_(0), @@ -412,14 +411,9 @@ SyncProcessRunner::SyncProcessRunner(Environment* env) uv_loop_(nullptr), stdio_count_(0), - uv_stdio_containers_(nullptr), stdio_pipes_initialized_(false), uv_process_options_(), - file_buffer_(nullptr), - args_buffer_(nullptr), - env_buffer_(nullptr), - cwd_buffer_(nullptr), uv_process_(), killed_(false), @@ -436,19 +430,12 @@ SyncProcessRunner::SyncProcessRunner(Environment* env) lifecycle_(kUninitialized), - env_(env) { -} - + env_(env) {} SyncProcessRunner::~SyncProcessRunner() { CHECK_EQ(lifecycle_, kHandlesClosed); stdio_pipes_.clear(); - delete[] file_buffer_; - delete[] args_buffer_; - delete[] cwd_buffer_; - delete[] env_buffer_; - delete[] uv_stdio_containers_; } @@ -808,12 +795,14 @@ Maybe SyncProcessRunner::ParseOptions(Local js_value) { Local js_options = js_value.As(); Local js_file; + const char* file_buffer; if (!js_options->Get(context, env()->file_string()).ToLocal(&js_file) || - !CopyJsString(js_file, &file_buffer_).To(&r)) { + !CopyJsString(js_file, &file_buffer).To(&r)) { return Nothing(); } if (r < 0) return Just(r); - uv_process_options_.file = file_buffer_; + file_buffer_.reset(file_buffer); + uv_process_options_.file = file_buffer_.get(); // Undocumented feature of Win32 CreateProcess API allows spawning // batch files directly but is potentially insecure because arguments @@ -825,23 +814,27 @@ Maybe SyncProcessRunner::ParseOptions(Local js_value) { #endif Local js_args; + char* args_buffer; if (!js_options->Get(context, env()->args_string()).ToLocal(&js_args) || - !CopyJsStringArray(js_args, &args_buffer_).To(&r)) { + !CopyJsStringArray(js_args, &args_buffer).To(&r)) { return Nothing(); } if (r < 0) return Just(r); - uv_process_options_.args = reinterpret_cast(args_buffer_); + args_buffer_.reset(args_buffer); + uv_process_options_.args = reinterpret_cast(args_buffer_.get()); Local js_cwd; if (!js_options->Get(context, env()->cwd_string()).ToLocal(&js_cwd)) { return Nothing(); } if (!js_cwd->IsNullOrUndefined()) { - if (!CopyJsString(js_cwd, &cwd_buffer_).To(&r)) { + const char* cwd_buffer; + if (!CopyJsString(js_cwd, &cwd_buffer).To(&r)) { return Nothing(); } if (r < 0) return Just(r); - uv_process_options_.cwd = cwd_buffer_; + cwd_buffer_.reset(cwd_buffer); + uv_process_options_.cwd = cwd_buffer_.get(); } Local js_env_pairs; @@ -850,12 +843,13 @@ Maybe SyncProcessRunner::ParseOptions(Local js_value) { return Nothing(); } if (!js_env_pairs->IsNullOrUndefined()) { - if (!CopyJsStringArray(js_env_pairs, &env_buffer_).To(&r)) { + char* env_buffer; + if (!CopyJsStringArray(js_env_pairs, &env_buffer).To(&r)) { return Nothing(); } if (r < 0) return Just(r); - - uv_process_options_.env = reinterpret_cast(env_buffer_); + env_buffer_.reset(env_buffer); + uv_process_options_.env = reinterpret_cast(env_buffer_.get()); } Local js_uid; if (!js_options->Get(context, env()->uid_string()).ToLocal(&js_uid)) { @@ -982,7 +976,7 @@ Maybe SyncProcessRunner::ParseStdioOptions(Local js_value) { js_stdio_options = js_value.As(); stdio_count_ = js_stdio_options->Length(); - uv_stdio_containers_ = new uv_stdio_container_t[stdio_count_]; + uv_stdio_containers_.resize(stdio_count_); stdio_pipes_.clear(); stdio_pipes_.resize(stdio_count_); @@ -1007,7 +1001,7 @@ Maybe SyncProcessRunner::ParseStdioOptions(Local js_value) { } } - uv_process_options_.stdio = uv_stdio_containers_; + uv_process_options_.stdio = uv_stdio_containers_.data(); uv_process_options_.stdio_count = stdio_count_; return Just(0); diff --git a/src/spawn_sync.h b/src/spawn_sync.h index 4478487c8f403e..9c8b0c563c4c45 100644 --- a/src/spawn_sync.h +++ b/src/spawn_sync.h @@ -205,15 +205,15 @@ class SyncProcessRunner { uv_loop_t* uv_loop_; uint32_t stdio_count_; - uv_stdio_container_t* uv_stdio_containers_; + std::vector uv_stdio_containers_; std::vector> stdio_pipes_; bool stdio_pipes_initialized_; uv_process_options_t uv_process_options_; - const char* file_buffer_; - char* args_buffer_; - char* env_buffer_; - const char* cwd_buffer_; + std::unique_ptr file_buffer_; + std::unique_ptr args_buffer_; + std::unique_ptr env_buffer_; + std::unique_ptr cwd_buffer_; uv_process_t uv_process_; bool killed_; From 54b439fb5a7ba0b3a5e270cb126c93f01dd730ec Mon Sep 17 00:00:00 2001 From: iknoom Date: Mon, 22 Sep 2025 21:47:15 +0900 Subject: [PATCH 12/36] src: fill `options_args`, `options_env` after vectors are finalized PR-URL: https://github.com/nodejs/node/pull/59945 Reviewed-By: Anna Henningsen --- src/process_wrap.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/process_wrap.cc b/src/process_wrap.cc index d41ef9002b3db7..d27ca7da7b587b 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -256,7 +256,6 @@ class ProcessWrap : public HandleWrap { int argc = js_argv->Length(); CHECK_LT(argc, INT_MAX); // Check for overflow. args_vals.reserve(argc); - options_args.resize(argc + 1); for (int i = 0; i < argc; i++) { Local val; if (!js_argv->Get(context, i).ToLocal(&val)) { @@ -264,10 +263,13 @@ class ProcessWrap : public HandleWrap { } node::Utf8Value arg(env->isolate(), val); args_vals.emplace_back(arg.ToString()); - options_args[i] = const_cast(args_vals.back().c_str()); + } + options_args.resize(args_vals.size() + 1); + for (size_t i = 0; i < args_vals.size(); i++) { + options_args[i] = const_cast(args_vals[i].c_str()); CHECK_NOT_NULL(options_args[i]); } - options_args[argc] = nullptr; + options_args.back() = nullptr; options.args = options_args.data(); } @@ -294,7 +296,6 @@ class ProcessWrap : public HandleWrap { int envc = env_opt->Length(); CHECK_LT(envc, INT_MAX); // Check for overflow. env_vals.reserve(envc); - options_env.resize(envc + 1); for (int i = 0; i < envc; i++) { Local val; if (!env_opt->Get(context, i).ToLocal(&val)) { @@ -302,10 +303,13 @@ class ProcessWrap : public HandleWrap { } node::Utf8Value pair(env->isolate(), val); env_vals.emplace_back(pair.ToString()); - options_env[i] = const_cast(env_vals.back().c_str()); + } + options_env.resize(env_vals.size() + 1); + for (size_t i = 0; i < env_vals.size(); i++) { + options_env[i] = const_cast(env_vals[i].c_str()); CHECK_NOT_NULL(options_env[i]); } - options_env[envc] = nullptr; + options_env.back() = nullptr; options.env = options_env.data(); } From b58df4799569fcfaa6d57c6082aaeabb4c30ce04 Mon Sep 17 00:00:00 2001 From: Deokjin Kim Date: Wed, 24 Sep 2025 00:22:19 +0900 Subject: [PATCH 13/36] test: fix typo of test-benchmark-readline.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "bechmark" -> "benchmark" in test-benchmark-readline.js. And fix test name in test-benchmark-validators.js. PR-URL: https://github.com/nodejs/node/pull/59993 Reviewed-By: Luigi Pinca Reviewed-By: Daeyeon Jeong Reviewed-By: Ulises Gascón --- .../{test-bechmark-readline.js => test-benchmark-readline.js} | 0 test/benchmark/test-benchmark-validators.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/benchmark/{test-bechmark-readline.js => test-benchmark-readline.js} (100%) diff --git a/test/benchmark/test-bechmark-readline.js b/test/benchmark/test-benchmark-readline.js similarity index 100% rename from test/benchmark/test-bechmark-readline.js rename to test/benchmark/test-benchmark-readline.js diff --git a/test/benchmark/test-benchmark-validators.js b/test/benchmark/test-benchmark-validators.js index 37250f56588f51..4983991c0919a9 100644 --- a/test/benchmark/test-benchmark-validators.js +++ b/test/benchmark/test-benchmark-validators.js @@ -2,7 +2,7 @@ require('../common'); -// Minimal test for assert benchmarks. This makes sure the benchmarks aren't +// Minimal test for validators benchmarks. This makes sure the benchmarks aren't // completely broken but nothing more than that. const runBenchmark = require('../common/benchmark'); From 26394cd5bf352a3f4ea3e13df35722bb8192b244 Mon Sep 17 00:00:00 2001 From: Diango Gavidia Date: Wed, 24 Sep 2025 19:27:57 -0400 Subject: [PATCH 14/36] test: expand tls-check-server-identity coverage PR-URL: https://github.com/nodejs/node/pull/60002 Reviewed-By: Rafael Gonzaga Reviewed-By: Anna Henningsen --- .../test-tls-check-server-identity.js | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js index 3682aee37b9a56..6918638230c4da 100644 --- a/test/parallel/test-tls-check-server-identity.js +++ b/test/parallel/test-tls-check-server-identity.js @@ -62,6 +62,11 @@ const tests = [ cert: { subject: { CN: '.a.com' } }, error: 'Host: a.com. is not cert\'s CN: .a.com' }, + { + host: 'bad.x.example.com', + cert: { subject: { CN: 'bad..example.com' } }, + error: 'Host: bad.x.example.com. is not cert\'s CN: bad..example.com' + }, // IP address in CN. Technically allowed but so rare that we reject // it anyway. If we ever do start allowing them, we should take care @@ -129,6 +134,16 @@ const tests = [ cert: { subject: { CN: 'b*b.a.com' } }, error: 'Host: b.a.com. is not cert\'s CN: b*b.a.com' }, + { + host: 'bxa.a.com', + cert: { subject: { CN: 'b**.a.com' } }, + error: 'Host: bxa.a.com. is not cert\'s CN: b**.a.com' + }, + { + host: 'xbcd.a.com', + cert: { subject: { CN: 'ab*cd.a.com' } }, + error: 'Host: xbcd.a.com. is not cert\'s CN: ab*cd.a.com' + }, // Empty Cert { @@ -158,6 +173,11 @@ const tests = [ subject: { CN: ['foo.com', 'bar.com'] } // CN=foo.com; CN=bar.com; } }, + { + host: 'a.com', + cert: { subject: { CN: [''] } }, + error: 'Host: a.com. is not cert\'s CN: ' + }, // DNS names and CN { @@ -212,6 +232,46 @@ const tests = [ }, // DNS names + { + host: 'a.com', + cert: { + subjectaltname: 'DNS:', + subject: {} + }, + error: 'Host: a.com. is not in the cert\'s altnames: DNS:' + }, + { + host: 'bad.x.example.com', + cert: { + subjectaltname: 'DNS:bad..example.com', + subject: {} + }, + error: 'Host: bad.x.example.com. is not in the cert\'s altnames: DNS:bad..example.com' + }, + { + host: 'x.example.com', + cert: { + subjectaltname: 'DNS:caf\u00E9.example.com', // "café.example.com" + subject: {} + }, + error: 'Host: x.example.com. is not in the cert\'s altnames: DNS:caf\u00E9.example.com' + }, + { + host: 'xbcd.a.com', + cert: { + subjectaltname: 'DNS:ab*cd.a.com', + subject: {} + }, + error: 'Host: xbcd.a.com. is not in the cert\'s altnames: DNS:ab*cd.a.com' + }, + { + host: 'x.example.com', + cert: { + subjectaltname: 'DNS:bad label.com', + subject: {} + }, + error: 'Host: x.example.com. is not in the cert\'s altnames: DNS:bad label.com' + }, { host: 'a.com', cert: { subjectaltname: 'DNS:*.a.com', @@ -261,6 +321,14 @@ const tests = [ subject: {} } }, + { + host: 'bxa.a.com', + cert: { + subjectaltname: 'DNS:b**.a.com', + subject: {} + }, + error: 'Host: bxa.a.com. is not in the cert\'s altnames: DNS:b**.a.com' + }, // URI names { host: 'a.b.a.com', cert: { From df72dc96e9a3fe9e81dee6d06eaab27d49948147 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 27 Sep 2025 21:57:35 +0200 Subject: [PATCH 15/36] console,util: improve array inspection performance There is no need to do the own property check, since the descriptor is needed right afterwards anyway. PR-URL: https://github.com/nodejs/node/pull/60037 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Jordan Harband --- lib/internal/util/inspect.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index e81d13e5c787c4..f76e61756175da 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -2022,11 +2022,12 @@ function formatArray(ctx, value, recurseTimes) { const remaining = valLen - len; const output = []; for (let i = 0; i < len; i++) { - // Special handle sparse arrays. - if (!ObjectPrototypeHasOwnProperty(value, i)) { + const desc = ObjectGetOwnPropertyDescriptor(value, i); + if (desc === undefined) { + // Special handle sparse arrays. return formatSpecialArray(ctx, value, recurseTimes, len, output, i); } - ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, i, kArrayType)); + ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, i, kArrayType, desc)); } if (remaining > 0) { ArrayPrototypePush(output, remainingText(remaining)); From d1fb8a538d42d2b44c4f8840b982100303ddc4dd Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 04:42:06 +0200 Subject: [PATCH 16/36] src: avoid unnecessary string -> `char*` -> string round trips In these places we can just generate `std::string` directly, so there's no need to convert to an intermediate C string. PR-URL: https://github.com/nodejs/node/pull/60055 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Chengzhong Wu --- src/inspector/protocol_helper.h | 2 +- src/node_blob.cc | 8 +++----- src/node_contextify.cc | 4 ++-- src/node_errors.h | 4 ++-- src/node_messaging.cc | 2 +- src/quic/defs.h | 2 +- 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/inspector/protocol_helper.h b/src/inspector/protocol_helper.h index a0f88aa93532a4..5529ef523a0101 100644 --- a/src/inspector/protocol_helper.h +++ b/src/inspector/protocol_helper.h @@ -19,7 +19,7 @@ inline std::unique_ptr ToInspectorString( inline protocol::String ToProtocolString(v8::Isolate* isolate, v8::Local value) { Utf8Value buffer(isolate, value); - return *buffer; + return buffer.ToString(); } } // namespace node::inspector diff --git a/src/node_blob.cc b/src/node_blob.cc index eb0fbebc9df152..bb019dc62c1000 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -439,11 +439,9 @@ void Blob::StoreDataObject(const FunctionCallbackInfo& args) { Utf8Value type(isolate, args[3]); binding_data->store_data_object( - std::string(*key, key.length()), + key.ToString(), BlobBindingData::StoredDataObject( - BaseObjectPtr(blob), - length, - std::string(*type, type.length()))); + BaseObjectPtr(blob), length, type.ToString())); } // Note: applying the V8 Fast API to the following function does not produce @@ -483,7 +481,7 @@ void Blob::GetDataObject(const FunctionCallbackInfo& args) { Utf8Value key(isolate, args[0]); BlobBindingData::StoredDataObject stored = - binding_data->get_data_object(std::string(*key, key.length())); + binding_data->get_data_object(key.ToString()); if (stored.blob) { Local type; if (!String::NewFromUtf8(isolate, diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 21a08a738e5c35..d52388717e5938 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -292,10 +292,10 @@ BaseObjectPtr ContextifyContext::New( options->allow_code_gen_wasm); Utf8Value name_val(env->isolate(), options->name); - ContextInfo info(*name_val); + ContextInfo info(name_val.ToString()); if (!options->origin.IsEmpty()) { Utf8Value origin_val(env->isolate(), options->origin); - info.origin = *origin_val; + info.origin = origin_val.ToString(); } BaseObjectPtr result; diff --git a/src/node_errors.h b/src/node_errors.h index 791e835c2c2851..42e2bf8ea12497 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -268,12 +268,12 @@ inline void THROW_ERR_REQUIRE_ASYNC_MODULE( if (!parent_filename.IsEmpty() && parent_filename->IsString()) { Utf8Value utf8(env->isolate(), parent_filename); message += "\n From "; - message += utf8.out(); + message += utf8.ToStringView(); } if (!filename.IsEmpty() && filename->IsString()) { Utf8Value utf8(env->isolate(), filename); message += "\n Requiring "; - message += +utf8.out(); + message += utf8.ToStringView(); } THROW_ERR_REQUIRE_ASYNC_MODULE(env, message.c_str()); } diff --git a/src/node_messaging.cc b/src/node_messaging.cc index 1eff9399ff8751..3c5f38ba4f4927 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -1357,7 +1357,7 @@ std::unique_ptr JSTransferable::TransferOrClone() const { } Utf8Value deserialize_info_str(env()->isolate(), deserialize_info); if (*deserialize_info_str == nullptr) return {}; - return std::make_unique(*deserialize_info_str, + return std::make_unique(deserialize_info_str.ToString(), Global(env()->isolate(), data)); } diff --git a/src/quic/defs.h b/src/quic/defs.h index 628b2b754a36a5..0816e1d09874db 100644 --- a/src/quic/defs.h +++ b/src/quic/defs.h @@ -39,7 +39,7 @@ bool SetOption(Environment* env, if (!object->Get(env->context(), name).ToLocal(&value)) return false; if (!value->IsUndefined()) { Utf8Value utf8(env->isolate(), value); - options->*member = *utf8; + options->*member = utf8.ToString(); } return true; } From 1289ef89ecabbca81f8f0ef28e366d7f9ab91609 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 04:59:03 +0200 Subject: [PATCH 17/36] src: remove unnecessary shadowed functions on Utf8Value & BufferValue Both of these are already implemented on the superclass. PR-URL: https://github.com/nodejs/node/pull/60056 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Chengzhong Wu --- src/util.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/util.h b/src/util.h index f5803eb383f2b8..4214fe1e52a6f6 100644 --- a/src/util.h +++ b/src/util.h @@ -552,11 +552,6 @@ class Utf8Value : public MaybeStackBuffer { public: explicit Utf8Value(v8::Isolate* isolate, v8::Local value); - inline std::string ToString() const { return std::string(out(), length()); } - inline std::string_view ToStringView() const { - return std::string_view(out(), length()); - } - inline bool operator==(const char* a) const { return strcmp(out(), a) == 0; } inline bool operator!=(const char* a) const { return !(*this == a); } }; @@ -570,10 +565,6 @@ class BufferValue : public MaybeStackBuffer { public: explicit BufferValue(v8::Isolate* isolate, v8::Local value); - inline std::string ToString() const { return std::string(out(), length()); } - inline std::string_view ToStringView() const { - return std::string_view(out(), length()); - } inline std::u8string_view ToU8StringView() const { return std::u8string_view(reinterpret_cast(out()), length()); From 764d35647de512cd1347d36703b2f0a457751999 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 05:08:59 +0200 Subject: [PATCH 18/36] src: remove unnecessary `std::string` error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we can just use the classic `THROW_...()` methods directly, without needing to allocate an `std::string` for the message/format parameter, let's just do so. PR-URL: https://github.com/nodejs/node/pull/60057 Reviewed-By: James M Snell Reviewed-By: Juan José Arboleda Reviewed-By: Chengzhong Wu --- src/node_file.cc | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 8e4847cbf89878..77f8f1bd4e8294 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3147,24 +3147,25 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { if (!error_code) { // Check if src and dest are identical. if (std::filesystem::equivalent(src_path, dest_path)) { - std::string message = "src and dest cannot be the same %s"; - return THROW_ERR_FS_CP_EINVAL(env, message.c_str(), dest_path_str); + static constexpr const char* message = + "src and dest cannot be the same %s"; + return THROW_ERR_FS_CP_EINVAL(env, message, dest_path_str); } const bool dest_is_dir = dest_status.type() == std::filesystem::file_type::directory; if (src_is_dir && !dest_is_dir) { - std::string message = + static constexpr const char* message = "Cannot overwrite non-directory %s with directory %s"; return THROW_ERR_FS_CP_DIR_TO_NON_DIR( - env, message.c_str(), dest_path_str, src_path_str); + env, message, dest_path_str, src_path_str); } if (!src_is_dir && dest_is_dir) { - std::string message = + static constexpr const char* message = "Cannot overwrite directory %s with non-directory %s"; return THROW_ERR_FS_CP_NON_DIR_TO_DIR( - env, message.c_str(), dest_path_str, src_path_str); + env, message, dest_path_str, src_path_str); } } @@ -3173,9 +3174,9 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { } // Check if dest_path is a subdirectory of src_path. if (src_is_dir && dest_path_str.starts_with(src_path_str)) { - std::string message = "Cannot copy %s to a subdirectory of self %s"; - return THROW_ERR_FS_CP_EINVAL( - env, message.c_str(), src_path_str, dest_path_str); + static constexpr const char* message = + "Cannot copy %s to a subdirectory of self %s"; + return THROW_ERR_FS_CP_EINVAL(env, message, src_path_str, dest_path_str); } auto dest_parent = dest_path.parent_path(); @@ -3186,9 +3187,9 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { dest_parent.parent_path() != dest_parent) { if (std::filesystem::equivalent( src_path, dest_path.parent_path(), error_code)) { - std::string message = "Cannot copy %s to a subdirectory of self %s"; - return THROW_ERR_FS_CP_EINVAL( - env, message.c_str(), src_path_str, dest_path_str); + static constexpr const char* message = + "Cannot copy %s to a subdirectory of self %s"; + return THROW_ERR_FS_CP_EINVAL(env, message, src_path_str, dest_path_str); } // If equivalent fails, it's highly likely that dest_parent does not exist @@ -3200,23 +3201,24 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo& args) { } if (src_is_dir && !recursive) { - std::string message = + static constexpr const char* message = "Recursive option not enabled, cannot copy a directory: %s"; - return THROW_ERR_FS_EISDIR(env, message.c_str(), src_path_str); + return THROW_ERR_FS_EISDIR(env, message, src_path_str); } switch (src_status.type()) { case std::filesystem::file_type::socket: { - std::string message = "Cannot copy a socket file: %s"; - return THROW_ERR_FS_CP_SOCKET(env, message.c_str(), dest_path_str); + static constexpr const char* message = "Cannot copy a socket file: %s"; + return THROW_ERR_FS_CP_SOCKET(env, message, dest_path_str); } case std::filesystem::file_type::fifo: { - std::string message = "Cannot copy a FIFO pipe: %s"; - return THROW_ERR_FS_CP_FIFO_PIPE(env, message.c_str(), dest_path_str); + static constexpr const char* message = "Cannot copy a FIFO pipe: %s"; + return THROW_ERR_FS_CP_FIFO_PIPE(env, message, dest_path_str); } case std::filesystem::file_type::unknown: { - std::string message = "Cannot copy an unknown file type: %s"; - return THROW_ERR_FS_CP_UNKNOWN(env, message.c_str(), dest_path_str); + static constexpr const char* message = + "Cannot copy an unknown file type: %s"; + return THROW_ERR_FS_CP_UNKNOWN(env, message, dest_path_str); } default: break; From c3be1226c7853d772ead1655bc437761cd57ee56 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 14:16:50 +0200 Subject: [PATCH 19/36] src: allow `std::string_view` arguments to `SPrintF()` and friends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modernize the code so there is no need to work with raw C strings anymore. PR-URL: https://github.com/nodejs/node/pull/60058 Reviewed-By: James M Snell Reviewed-By: Juan José Arboleda Reviewed-By: Chengzhong Wu --- src/debug_utils-inl.h | 55 ++++++++++++++++++++++++++----------------- src/debug_utils.h | 4 ++-- src/node_errors.h | 17 +++++++------ 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/debug_utils-inl.h b/src/debug_utils-inl.h index 3736663d9977c0..a687708d92bfc1 100644 --- a/src/debug_utils-inl.h +++ b/src/debug_utils-inl.h @@ -65,33 +65,42 @@ std::string ToBaseString(const T& value) { return ToStringHelper::BaseConvert(value); } -inline std::string SPrintFImpl(const char* format) { - const char* p = strchr(format, '%'); - if (p == nullptr) [[unlikely]] - return format; - CHECK_EQ(p[1], '%'); // Only '%%' allowed when there are no arguments. +inline std::string SPrintFImpl(std::string_view format) { + auto offset = format.find('%'); + if (offset == std::string_view::npos) return std::string(format); + CHECK_LT(offset + 1, format.size()); + CHECK_EQ(format[offset + 1], + '%'); // Only '%%' allowed when there are no arguments. - return std::string(format, p + 1) + SPrintFImpl(p + 2); + return std::string(format.substr(0, offset + 1)) + + SPrintFImpl(format.substr(offset + 2)); } template std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string) - const char* format, Arg&& arg, Args&&... args) { - const char* p = strchr(format, '%'); - CHECK_NOT_NULL(p); // If you hit this, you passed in too many arguments. - std::string ret(format, p); + std::string_view format, + Arg&& arg, + Args&&... args) { + auto offset = format.find('%'); + CHECK_NE(offset, std::string_view::npos); // If you hit this, you passed in + // too many arguments. + std::string ret(format.substr(0, offset)); // Ignore long / size_t modifiers - while (strchr("lz", *++p) != nullptr) {} - switch (*p) { + while (++offset < format.size() && + (format[offset] == 'l' || format[offset] == 'z')) { + } + switch (offset == format.size() ? '\0' : format[offset]) { case '%': { - return ret + '%' + SPrintFImpl(p + 1, - std::forward(arg), - std::forward(args)...); + return ret + '%' + + SPrintFImpl(format.substr(offset + 1), + std::forward(arg), + std::forward(args)...); } default: { - return ret + '%' + SPrintFImpl(p, - std::forward(arg), - std::forward(args)...); + return ret + '%' + + SPrintFImpl(format.substr(offset), + std::forward(arg), + std::forward(args)...); } case 'd': case 'i': @@ -120,17 +129,21 @@ std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string) break; } } - return ret + SPrintFImpl(p + 1, std::forward(args)...); + return ret + + SPrintFImpl(format.substr(offset + 1), std::forward(args)...); } template std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string) - const char* format, Args&&... args) { + std::string_view format, + Args&&... args) { return SPrintFImpl(format, std::forward(args)...); } template -void COLD_NOINLINE FPrintF(FILE* file, const char* format, Args&&... args) { +void COLD_NOINLINE FPrintF(FILE* file, + std::string_view format, + Args&&... args) { FWrite(file, SPrintF(format, std::forward(args)...)); } diff --git a/src/debug_utils.h b/src/debug_utils.h index e1768d8a06159c..930539d3cb1625 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -33,9 +33,9 @@ inline std::string ToString(const T& value); // - Supports %p and %s. %d, %i and %u are aliases for %s. // - Accepts any class that has a ToString() method for stringification. template -inline std::string SPrintF(const char* format, Args&&... args); +inline std::string SPrintF(std::string_view format, Args&&... args); template -inline void FPrintF(FILE* file, const char* format, Args&&... args); +inline void FPrintF(FILE* file, std::string_view format, Args&&... args); void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str); // Listing the AsyncWrap provider types first enables us to cast directly diff --git a/src/node_errors.h b/src/node_errors.h index 42e2bf8ea12497..9d0488d0dba657 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -134,7 +134,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); #define V(code, type) \ template \ inline v8::Local code( \ - v8::Isolate* isolate, const char* format, Args&&... args) { \ + v8::Isolate* isolate, std::string_view format, Args&&... args) { \ std::string message; \ if (sizeof...(Args) == 0) { \ message = format; \ @@ -159,17 +159,18 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); } \ template \ inline void THROW_##code( \ - v8::Isolate* isolate, const char* format, Args&&... args) { \ + v8::Isolate* isolate, std::string_view format, Args&&... args) { \ isolate->ThrowException( \ code(isolate, format, std::forward(args)...)); \ } \ template \ inline void THROW_##code( \ - Environment* env, const char* format, Args&&... args) { \ + Environment* env, std::string_view format, Args&&... args) { \ THROW_##code(env->isolate(), format, std::forward(args)...); \ } \ template \ - inline void THROW_##code(Realm* realm, const char* format, Args&&... args) { \ + inline void THROW_##code( \ + Realm* realm, std::string_view format, Args&&... args) { \ THROW_##code(realm->isolate(), format, std::forward(args)...); \ } ERRORS_WITH_CODE(V) @@ -250,10 +251,8 @@ PREDEFINED_ERROR_MESSAGES(V) // Errors with predefined non-static messages inline void THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(Environment* env, int64_t timeout) { - std::ostringstream message; - message << "Script execution timed out after "; - message << timeout << "ms"; - THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, message.str().c_str()); + THROW_ERR_SCRIPT_EXECUTION_TIMEOUT( + env, "Script execution timed out after %dms", timeout); } inline void THROW_ERR_REQUIRE_ASYNC_MODULE( @@ -275,7 +274,7 @@ inline void THROW_ERR_REQUIRE_ASYNC_MODULE( message += "\n Requiring "; message += utf8.ToStringView(); } - THROW_ERR_REQUIRE_ASYNC_MODULE(env, message.c_str()); + THROW_ERR_REQUIRE_ASYNC_MODULE(env, message); } inline v8::Local ERR_BUFFER_TOO_LARGE(v8::Isolate* isolate) { From 9fae03c7d938d227c35cc4da00e2512f3e3f0f38 Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Tue, 30 Sep 2025 11:05:05 -0300 Subject: [PATCH 20/36] tools: use dependabot cooldown and move tools/doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/node/issues/59911 PR-URL: https://github.com/nodejs/node/pull/59978 Reviewed-By: Ulises Gascón Reviewed-By: Matteo Collina --- .github/dependabot.yml | 30 ++++++++++++++++++++++++++++++ .github/workflows/tools.yml | 17 ----------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b9770e23a2e353..9cc673d5a62fe7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,11 @@ updates: directory: / schedule: interval: monthly + cooldown: + - semver-major-days: 5 + - semver-minor-days: 5 + - semver-patch-days: 5 + commit-message: prefix: meta open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}} @@ -14,6 +19,10 @@ updates: directory: /tools/eslint schedule: interval: monthly + cooldown: + - semver-major-days: 5 + - semver-minor-days: 5 + - semver-patch-days: 5 commit-message: prefix: tools open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}} @@ -27,6 +36,10 @@ updates: directory: /tools/lint-md schedule: interval: monthly + cooldown: + - semver-major-days: 5 + - semver-minor-days: 5 + - semver-patch-days: 5 commit-message: prefix: tools open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}} @@ -35,3 +48,20 @@ updates: applies-to: version-updates patterns: - '*' + + - package-ecosystem: npm + directory: /tools/doc + schedule: + interval: weekly + cooldown: + - semver-major-days: 5 + - semver-minor-days: 5 + - semver-patch-days: 5 + commit-message: + prefix: tools + open-pull-requests-limit: ${{secrets.OPEN_PR_LIMIT}} + groups: + doc: + applies-to: version-updates + patterns: + - '*' diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 302e6e29d04ca6..38157bfc00e2bb 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -21,7 +21,6 @@ on: - c-ares - cjs-module-lexer - corepack - - doc - googletest - gyp-next - histogram @@ -119,22 +118,6 @@ jobs: run: | make corepack-update echo "NEW_VERSION=$(node deps/corepack/dist/corepack.js --version)" >> $GITHUB_ENV - - id: doc - subsystem: tools - label: tools - run: | - cd tools/doc - npm ci - NEW_VERSION=$(npm outdated --parseable | cut -d: -f4 | xargs) - if [ "$NEW_VERSION" != "" ]; then - echo "NEW_VERSION=new version" >> $GITHUB_ENV - rm -rf package-lock.json node_modules - # Include $NEW_VERSION to explicitly update the package.json - # entry for the dependency and also so that semver-major updates - # are not skipped. - npm install --ignore-scripts $NEW_VERSION - npm install --ignore-scripts - fi - id: googletest subsystem: deps label: dependencies, test From 6e386c063246ef2f413431593a156b6b091e0f02 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 03:19:49 +0200 Subject: [PATCH 21/36] src: make ToLower/ToUpper input args more flexible In particular, this enables passing `std::string_view` instead. PR-URL: https://github.com/nodejs/node/pull/60052 Reviewed-By: James M Snell Reviewed-By: Rafael Gonzaga --- src/util-inl.h | 24 ++++++++++++++++-------- src/util.h | 6 ++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/util-inl.h b/src/util-inl.h index eb097bcb00aaf0..17b870e2dd91ab 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -193,10 +193,14 @@ char ToLower(char c) { return std::tolower(c, std::locale::classic()); } -std::string ToLower(const std::string& in) { - std::string out(in.size(), 0); - for (size_t i = 0; i < in.size(); ++i) - out[i] = ToLower(in[i]); +template +std::string ToLower(const T& in) { + auto it = std::cbegin(in); + auto end = std::cend(in); + std::string out(std::distance(it, end), 0); + size_t i; + for (i = 0; it != end; ++it, ++i) out[i] = ToLower(*it); + DCHECK_EQ(i, out.size()); return out; } @@ -204,10 +208,14 @@ char ToUpper(char c) { return std::toupper(c, std::locale::classic()); } -std::string ToUpper(const std::string& in) { - std::string out(in.size(), 0); - for (size_t i = 0; i < in.size(); ++i) - out[i] = ToUpper(in[i]); +template +std::string ToUpper(const T& in) { + auto it = std::cbegin(in); + auto end = std::cend(in); + std::string out(std::distance(it, end), 0); + size_t i; + for (i = 0; it != end; ++it, ++i) out[i] = ToUpper(*it); + DCHECK_EQ(i, out.size()); return out; } diff --git a/src/util.h b/src/util.h index 4214fe1e52a6f6..8f27afbb9e4e45 100644 --- a/src/util.h +++ b/src/util.h @@ -365,11 +365,13 @@ inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, // tolower() is locale-sensitive. Use ToLower() instead. inline char ToLower(char c); -inline std::string ToLower(const std::string& in); +template +inline std::string ToLower(const T& in); // toupper() is locale-sensitive. Use ToUpper() instead. inline char ToUpper(char c); -inline std::string ToUpper(const std::string& in); +template +inline std::string ToUpper(const T& in); // strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead. inline bool StringEqualNoCase(const char* a, const char* b); From 987841a773cdfe9568790661ba884821144aaa83 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 03:21:07 +0200 Subject: [PATCH 22/36] src: avoid unnecessary string allocations in SPrintF impl If we can use a `std::string_view` instead of a `std::string`, let's just do that instead. PR-URL: https://github.com/nodejs/node/pull/60052 Reviewed-By: James M Snell Reviewed-By: Rafael Gonzaga --- src/debug_utils-inl.h | 45 +++++++++++++++++++++++++++++++------------ src/debug_utils.h | 2 ++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/debug_utils-inl.h b/src/debug_utils-inl.h index a687708d92bfc1..c10af8b1b618bc 100644 --- a/src/debug_utils-inl.h +++ b/src/debug_utils-inl.h @@ -11,25 +11,41 @@ namespace node { +template +concept StringViewConvertible = requires(T a) { + { + a.ToStringView() + } -> std::convertible_to; + }; +template +concept StringConvertible = requires(T a) { + { + a.ToString() + } -> std::convertible_to; + }; + struct ToStringHelper { template - static std::string Convert( - const T& value, - std::string(T::* to_string)() const = &T::ToString) { - return (value.*to_string)(); + requires(StringConvertible) && (!StringViewConvertible) + static std::string Convert(const T& value) { + return value.ToString(); } + template + requires StringViewConvertible + static std::string_view Convert(const T& value) { + return value.ToStringView(); + } + template ::value, bool>::type, typename dummy = bool> static std::string Convert(const T& value) { return std::to_string(value); } - static std::string Convert(const char* value) { + static std::string_view Convert(const char* value) { return value != nullptr ? value : "(null)"; } static std::string Convert(const std::string& value) { return value; } - static std::string Convert(std::string_view value) { - return std::string(value); - } + static std::string_view Convert(std::string_view value) { return value; } static std::string Convert(bool value) { return value ? "true" : "false"; } template >> - static std::string BaseConvert(T& value) { // NOLINT(runtime/references) + static auto BaseConvert(T&& value) { return Convert(std::forward(value)); } }; template -std::string ToString(const T& value) { +auto ToStringOrStringView(const T& value) { return ToStringHelper::Convert(value); } +template +std::string ToString(const T& value) { + return std::string(ToStringOrStringView(value)); +} + template -std::string ToBaseString(const T& value) { +auto ToBaseString(const T& value) { return ToStringHelper::BaseConvert(value); } @@ -106,7 +127,7 @@ std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string) case 'i': case 'u': case 's': - ret += ToString(arg); + ret += ToStringOrStringView(arg); break; case 'o': ret += ToBaseString<3>(arg); diff --git a/src/debug_utils.h b/src/debug_utils.h index 930539d3cb1625..8f6165e1b5faf4 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -26,6 +26,8 @@ class Environment; template inline std::string ToString(const T& value); +template +inline auto ToStringOrStringView(const T& value); // C++-style variant of sprintf()/fprintf() that: // - Returns an std::string From 4206406694f1d86c7d64582308af48ee3deaaf40 Mon Sep 17 00:00:00 2001 From: SRAVANI GUNDEPALLI <88590399+sravani1510@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:16:57 +0530 Subject: [PATCH 23/36] test: mark test-web-locks skip on IBM i PR-URL: https://github.com/nodejs/node/pull/59996 Reviewed-By: Richard Lau Reviewed-By: Abdirahim Musse Reviewed-By: Luigi Pinca --- test/parallel/parallel.status | 1 + 1 file changed, 1 insertion(+) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 67c0c04d2365e5..9822dc622ebfc2 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -155,6 +155,7 @@ test-inspector-network-content-type: SKIP test-fetch: SKIP test-without-async-context-frame: SKIP test-process-cpuUsage: PASS, FLAKY +test-web-locks: SKIP [$asan==on] From 81a3055710f85cd120f3ac69be2aa3728120f0f2 Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Wed, 1 Oct 2025 13:47:07 +0100 Subject: [PATCH 24/36] process: fix default `env` for `process.execve` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `env` parameter for `process.execve` is documented to default to `process.env`. PR-URL: https://github.com/nodejs/node/pull/60029 Refs: https://github.com/nodejs/build/pull/4156 Reviewed-By: Colin Ihrig Reviewed-By: Michaël Zasso Reviewed-By: Luigi Pinca --- lib/internal/process/per_thread.js | 32 ++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 092464c9ffd440..78805a6b5b87bb 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -283,7 +283,7 @@ function wrapProcessMethods(binding) { return true; } - function execve(execPath, args = [], env) { + function execve(execPath, args = [], env = process.env) { emitExperimentalWarning('process.execve'); const { isMainThread } = require('internal/worker'); @@ -305,22 +305,20 @@ function wrapProcessMethods(binding) { } const envArray = []; - if (env !== undefined) { - validateObject(env, 'env'); - - for (const { 0: key, 1: value } of ObjectEntries(env)) { - if ( - typeof key !== 'string' || - typeof value !== 'string' || - StringPrototypeIncludes(key, '\u0000') || - StringPrototypeIncludes(value, '\u0000') - ) { - throw new ERR_INVALID_ARG_VALUE( - 'env', env, 'must be an object with string keys and values without null bytes', - ); - } else { - ArrayPrototypePush(envArray, `${key}=${value}`); - } + validateObject(env, 'env'); + + for (const { 0: key, 1: value } of ObjectEntries(env)) { + if ( + typeof key !== 'string' || + typeof value !== 'string' || + StringPrototypeIncludes(key, '\u0000') || + StringPrototypeIncludes(value, '\u0000') + ) { + throw new ERR_INVALID_ARG_VALUE( + 'env', env, 'must be an object with string keys and values without null bytes', + ); + } else { + ArrayPrototypePush(envArray, `${key}=${value}`); } } From 609c063e81df2eac81a52ac8239b1ad0628cf995 Mon Sep 17 00:00:00 2001 From: Moonki Choi Date: Fri, 3 Oct 2025 07:31:09 +0900 Subject: [PATCH 25/36] src: remove unused variables from report PR-URL: https://github.com/nodejs/node/pull/60047 Reviewed-By: theanarkh Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Chengzhong Wu --- src/node_report.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/node_report.cc b/src/node_report.cc index df73a8204bc091..79747aa37c9164 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -752,7 +752,6 @@ static void PrintSystemInformation(JSONWriter* writer) { writer->json_objectstart("userLimits"); struct rlimit limit; - std::string soft, hard; for (size_t i = 0; i < arraysize(rlimit_strings); i++) { if (getrlimit(rlimit_strings[i].id, &limit) == 0) { @@ -788,8 +787,6 @@ static void PrintLoadedLibraries(JSONWriter* writer) { // Obtain and report the node and subcomponent version strings. static void PrintComponentVersions(JSONWriter* writer) { - std::stringstream buf; - writer->json_objectstart("componentVersions"); for (const auto& version : per_process::metadata.versions.pairs()) { From e4b95a5158b4bc3eaf7979ce23662c87ed838028 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sat, 4 Oct 2025 01:39:09 +0200 Subject: [PATCH 26/36] test: replace diagnostics_channel stackframe in output snapshots PR-URL: https://github.com/nodejs/node/pull/60024 Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- test/common/assertSnapshot.js | 4 +++- .../source-map/output/source_map_assert_source_line.snapshot | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/common/assertSnapshot.js b/test/common/assertSnapshot.js index 7a40c94389eda9..086561c9a0b52f 100644 --- a/test/common/assertSnapshot.js +++ b/test/common/assertSnapshot.js @@ -17,7 +17,9 @@ function replaceStackTrace(str, replacement = '$1*$7$8\n') { } function replaceInternalStackTrace(str) { - return str.replaceAll(/(\W+).*node:internal.*/g, '$1*'); + // Replace non-internal frame `at TracingChannel.traceSync (node:diagnostics_channel:328:14)` + // as well as `at node:internal/main/run_main_module:33:47` with `*`. + return str.replaceAll(/(\W+).*[(\s]node:.*/g, '$1*'); } function replaceWindowsLineEndings(str) { 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 index 9def6eb4d7bedb..62e611f330f97f 100644 --- a/test/fixtures/source-map/output/source_map_assert_source_line.snapshot +++ b/test/fixtures/source-map/output/source_map_assert_source_line.snapshot @@ -7,7 +7,7 @@ AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value: * * * - at TracingChannel.traceSync (node:diagnostics_channel:328:14) + * * * * From c2dd6eed2f9a052da1546b93390b9c7a560cbc3d Mon Sep 17 00:00:00 2001 From: Shima Ryuhei <65934663+islandryu@users.noreply.github.com> Date: Sat, 4 Oct 2025 11:12:19 +0900 Subject: [PATCH 27/36] process: fix wrong asyncContext under unhandled-rejections=strict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/60034 PR-URL: https://github.com/nodejs/node/pull/60103 Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gerhard Stöbich --- lib/internal/process/promises.js | 2 +- ...omise-unhandled-error-with-reading-file.js | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-promise-unhandled-error-with-reading-file.js diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 90eaef5c773718..db5cb8a9cf362b 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -343,7 +343,7 @@ function strictUnhandledRejectionsMode(promise, promiseInfo, promiseAsyncId) { reason : new UnhandledPromiseRejection(reason); // This destroys the async stack, don't clear it after triggerUncaughtException(err, true /* fromPromise */); - if (promiseAsyncId === undefined) { + if (promiseAsyncId !== undefined) { pushAsyncContext( promise[kAsyncIdSymbol], promise[kTriggerAsyncIdSymbol], diff --git a/test/parallel/test-promise-unhandled-error-with-reading-file.js b/test/parallel/test-promise-unhandled-error-with-reading-file.js new file mode 100644 index 00000000000000..5a037eec7ca229 --- /dev/null +++ b/test/parallel/test-promise-unhandled-error-with-reading-file.js @@ -0,0 +1,29 @@ +// Flags: --unhandled-rejections=strict +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +process.on('unhandledRejection', common.mustNotCall); + +process.on('uncaughtException', common.mustCall((err) => { + assert.ok(err.message.includes('foo')); +})); + + +async function readFile() { + return fs.promises.readFile(__filename); +} + +async function crash() { + throw new Error('foo'); +} + + +async function main() { + crash(); + readFile(); +} + +main(); From ea5cfd98c5543fee460939405f42ee7046a003ce Mon Sep 17 00:00:00 2001 From: BCD1me <152237136+Amemome@users.noreply.github.com> Date: Sat, 4 Oct 2025 12:17:35 +0900 Subject: [PATCH 28/36] lib: implement passive listener behavior per spec Implements the WHATWG DOM specification for passive event listeners, ensuring that calls to `preventDefault()` are correctly ignored within a passive listener context. An internal `kInPassiveListener` state is added to the Event object to track when a passive listener is executing. The `preventDefault()` method and the `returnValue` setter are modified to check this state, as well as the event's `cancelable` property. This state is reliably cleaned up within a `finally` block to prevent state pollution in case a listener throws an error. This resolves previously failing Web Platform Tests (WPT) in `AddEventListenerOptions-passive.any.js`. Refs: https://dom.spec.whatwg.org/#dom-event-preventdefault PR-URL: https://github.com/nodejs/node/pull/59995 Reviewed-By: Daeyeon Jeong Reviewed-By: Mattias Buelens Reviewed-By: Benjamin Gruenbaum Reviewed-By: Jason Zhang Reviewed-By: Matteo Collina --- lib/internal/event_target.js | 25 +++++++++++++++++-- ...ents-add-event-listener-options-passive.js | 3 +-- test/wpt/status/dom/events.json | 9 ------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index e6c24e3a058ca7..2f02510e3a3176 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -76,6 +76,7 @@ const { now } = require('internal/perf/utils'); const kType = Symbol('type'); const kDetail = Symbol('detail'); +const kInPassiveListener = Symbol('kInPassiveListener'); const isTrustedSet = new SafeWeakSet(); const isTrusted = ObjectGetOwnPropertyDescriptor({ @@ -127,6 +128,7 @@ class Event { this[kTarget] = null; this[kIsBeingDispatched] = false; + this[kInPassiveListener] = false; } /** @@ -178,6 +180,7 @@ class Event { preventDefault() { if (!isEvent(this)) throw new ERR_INVALID_THIS('Event'); + if (!this.#cancelable || this[kInPassiveListener]) return; this.#defaultPrevented = true; } @@ -266,6 +269,19 @@ class Event { return !this.#cancelable || !this.#defaultPrevented; } + /** + * @type {boolean} + */ + set returnValue(value) { + if (!isEvent(this)) + throw new ERR_INVALID_THIS('Event'); + + if (!value) { + if (!this.#cancelable || this[kInPassiveListener]) return; + this.#defaultPrevented = true; + } + } + /** * @type {boolean} */ @@ -760,7 +776,6 @@ class EventTarget { throw new ERR_EVENT_RECURSION(event.type); this[kHybridDispatch](event, event.type, event); - return event.defaultPrevented !== true; } @@ -813,8 +828,8 @@ class EventTarget { this[kRemoveListener](root.size, type, listener, capture); } + let arg; try { - let arg; if (handler.isNodeStyleListener) { arg = nodeValue; } else { @@ -824,6 +839,9 @@ class EventTarget { handler.callback.deref() : handler.callback; let result; if (callback) { + if (handler.passive && !handler.isNodeStyleListener) { + arg[kInPassiveListener] = true; + } result = FunctionPrototypeCall(callback, this, arg); if (!handler.isNodeStyleListener) { arg[kIsBeingDispatched] = false; @@ -833,6 +851,9 @@ class EventTarget { addCatch(result); } catch (err) { emitUncaughtException(err); + } finally { + if (arg?.[kInPassiveListener]) + arg[kInPassiveListener] = false; } handler = next; diff --git a/test/parallel/test-whatwg-events-add-event-listener-options-passive.js b/test/parallel/test-whatwg-events-add-event-listener-options-passive.js index 97984bd9aff828..0299c7fa5fb0cc 100644 --- a/test/parallel/test-whatwg-events-add-event-listener-options-passive.js +++ b/test/parallel/test-whatwg-events-add-event-listener-options-passive.js @@ -1,6 +1,6 @@ 'use strict'; -const common = require('../common'); +require('../common'); // Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html // in order to define the `document` ourselves @@ -58,7 +58,6 @@ const { testPassiveValue({}, true); testPassiveValue({ passive: false }, true); - common.skip('TODO: passive listeners is still broken'); testPassiveValue({ passive: 1 }, false); testPassiveValue({ passive: true }, false); testPassiveValue({ passive: 0 }, true); diff --git a/test/wpt/status/dom/events.json b/test/wpt/status/dom/events.json index c0f4104c452b85..8109e2372adfb8 100644 --- a/test/wpt/status/dom/events.json +++ b/test/wpt/status/dom/events.json @@ -1,13 +1,4 @@ { - "AddEventListenerOptions-passive.any.js": { - "fail": { - "expected": [ - "preventDefault should be ignored if-and-only-if the passive option is true", - "returnValue should be ignored if-and-only-if the passive option is true", - "passive behavior of one listener should be unaffected by the presence of other listeners" - ] - } - }, "Event-dispatch-listener-order.window.js": { "skip": "document is not defined" }, From 56abc4ac76d3f0865ad5732474a8dffa3195e589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrg=C3=BCn=20Day=C4=B1o=C4=9Flu?= Date: Sat, 4 Oct 2025 22:08:00 +0200 Subject: [PATCH 29/36] lib: optimize priority queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/60039 Reviewed-By: Juan José Arboleda Reviewed-By: Antoine du Hamel --- lib/internal/priority_queue.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/internal/priority_queue.js b/lib/internal/priority_queue.js index c172e2351c93ab..e6fdb61e9997c9 100644 --- a/lib/internal/priority_queue.js +++ b/lib/internal/priority_queue.js @@ -1,9 +1,5 @@ 'use strict'; -const { - Array, -} = primordials; - // The PriorityQueue is a basic implementation of a binary heap that accepts // a custom sorting function via its constructor. This function is passed // the two nodes to compare, similar to the native Array#sort. Crucially @@ -12,7 +8,7 @@ const { module.exports = class PriorityQueue { #compare = (a, b) => a - b; - #heap = new Array(64); + #heap = [undefined, undefined]; #setPosition; #size = 0; @@ -28,9 +24,6 @@ module.exports = class PriorityQueue { const pos = ++this.#size; heap[pos] = value; - if (heap.length === pos) - heap.length *= 2; - this.percolateUp(pos); } @@ -45,6 +38,7 @@ module.exports = class PriorityQueue { percolateDown(pos) { const compare = this.#compare; const setPosition = this.#setPosition; + const hasSetPosition = setPosition !== undefined; const heap = this.#heap; const size = this.#size; const hsize = size >> 1; @@ -62,7 +56,7 @@ module.exports = class PriorityQueue { if (compare(item, childItem) <= 0) break; - if (setPosition !== undefined) + if (hasSetPosition) setPosition(childItem, pos); heap[pos] = childItem; @@ -70,7 +64,7 @@ module.exports = class PriorityQueue { } heap[pos] = item; - if (setPosition !== undefined) + if (hasSetPosition) setPosition(item, pos); } @@ -78,6 +72,7 @@ module.exports = class PriorityQueue { const heap = this.#heap; const compare = this.#compare; const setPosition = this.#setPosition; + const hasSetPosition = setPosition !== undefined; const item = heap[pos]; while (pos > 1) { @@ -86,13 +81,13 @@ module.exports = class PriorityQueue { if (compare(parentItem, item) <= 0) break; heap[pos] = parentItem; - if (setPosition !== undefined) + if (hasSetPosition) setPosition(parentItem, pos); pos = parent; } heap[pos] = item; - if (setPosition !== undefined) + if (hasSetPosition) setPosition(item, pos); } From aac90d351b30067a46f835b5355f4bfb928e707d Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 5 Oct 2025 17:02:17 +0200 Subject: [PATCH 30/36] tools: verify signatures when updating nghttp* PR-URL: https://github.com/nodejs/node/pull/60113 Reviewed-By: Marco Ippolito Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig Reviewed-By: Yagiz Nizipli --- tools/dep_updaters/nghttp.kbx | Bin 0 -> 3171 bytes tools/dep_updaters/update-nghttp2.sh | 11 ++++++----- tools/dep_updaters/update-nghttp3.sh | 8 ++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 tools/dep_updaters/nghttp.kbx diff --git a/tools/dep_updaters/nghttp.kbx b/tools/dep_updaters/nghttp.kbx new file mode 100644 index 0000000000000000000000000000000000000000..60ad5134ecc66aaf9a7830cf17a594dd6d940a3d GIT binary patch literal 3171 zcmZuzc{tSD8~@H?Hw=S}EMwndEMu6FtXbpQl4Ybx_MPE|t|VEfD55N3B(h6nWKTkB zBrcMyDRiylVhTUHPru*qkNZB)Ip;m^d*1h)=Q*F}^S%H8pgN&2^@;|)Y$mZ zIMV+SPD3#0r}h8qP*Zcf=No< z65b9+X^Wfws&o44%$Kc8(1I~YJ3jc6uc0BSO3JBl?0Py8QP4VX* z0ziMR|1urE@F)_v528TL3Z+myP~dvK6hUTE_|!wHWaQT()D91>@-Zo)5N54wNZ^hn zbCTUU7BxMYQ^9RV!_WsJ)!pnoTW; z<5m%Dyi(z-L^ZpJo_9EDQ4Os(6L@BF`C0E7{vYdkiL}>&L*@=peto63Pg0WmYh23rg+Z+PT3t z(PJs}fc2ixiF83kuPq_^K7PdCiKvsLUq)q;o>u_T$6wh!DBx7> zA#4uwgMa1+k}g0IJYWFD1U>==_&9`_gN>C10*3N~Ss_pXFa!h^1%Z#n0_<$?U*MIq z0t$20GKXf{V_Hic9?9iTs6NW%-Mj)6UjOV`Nw9I<<66_+J5E_%>cl!O)G`B*!}|a# z7zXtic6|zh16>O6>TIg*v^ymYXPzj4K}Mjpz`%mER-tW;nz}Eg+xG1RRs0wb14z}k$Ccy3(Tib6tx`>N+*xT5rI$hi-gdRPm`xh z#AH05!W)xJC)7$FMYU#1B3E<{dK8_O`5NWrO_(glXEes;b8qNAIU%lL7h#9`F|*?5 zLrRS}-}yw~R3uFbEng=kCnHPC-Z4F}z?Nni`6h~9nOMj; zKOtb!Z%ErrMhooTrbLmUsV^&c&Sr4mp9|G*M`*b-%MW_QH>pTuP#y)&;mrMkWYOme zaw?tX%J=TI5{IR&lVNfzH1pj=4s7;Zdn~EN(CYdz_AaimaSK5!V@-OnVIQWLsZ8Gd? z&=S2QGa)#+N}G!HD?l|`dUM8!!QFMJT_d*F8f4vM_|Ltk z6Y9_I%Llh2y^7U$u;p`N^~!QjQf=dI3U@H9tJ-Zm6ht5sg$@YucI@(e%PH~ngRr-& z3#SqdLMwc6VQn|Zz7NEW!@KD3UzGi(+aC^3oYExll{|1gK3y060XAk6rCg)qDAAx! z-G*7Qd6xBy_ti-i({eD9;b-|0r3Ysto^+v$rc5ej*K(_1a=p0aA?&ObOe>7S|Jlkl z(t4w#-~zzPKLm@woRk|!<$BEaG`s0G++92`H6}egI5^pDlf~`7JhoO!+|E_1c=wlF zUfICGMify$M(Qs`gDSoFbdfz~OlAys4c5Z++9%TE8jZ#4tdial9QqL=w#kJncFfhI z+pd|?HbrA6qM}^R5VcwE9QY{C2jWLDPh?4Z;y5bZ^Ko z^PFC**LACIA=_FnDEey>K$tHQ?hWyYMK9;MOAlC$7$Kr6jA>t(S<8m!8KA(7p@7J~ zBkL2d6cN4E?h-v@Or-*ajTRK?=u{4awhu5Vr1D1Cmn2p9{}L7y_On{T;lQkH?x<4x zlLgpAJ$A>8owRb>v+uoMxiy&O`%ARaVHNt3i&_*=-XAOvc~+fm6D)q9uU&}nyQwqa zoFmEsmiAAw{OpqLmGvpFG;6_m)9MV?l!9G1qc?f-$vg{{oZX0wiM(L5kjkyG6g5YU zR!6qw$}x2ew5*l3cSo$49 z0|>>+XJpbDmlbxj%5~8n4MwYxejbDeU)N*;zw{wTQL7LojaO>!%8OoM$goEZj2{2{ifa+! z%%L+O9`jXL!$#-&)jC=$TRS9fLvLmD{9F)8De5_!=%kLRe=R#z$gcPYL!wS+G7Z6; z=2%`m8`Jx+F>tI5hYV{-d?Qwl7&5w@o0io~beXb`;?XJLm`c#1I<2ciJqHdq4-#JDL(&!4;P_KVj=m4*RU2NN`}U}^7xqU2)0hq zn4TsOCr?o4vyaw@x^GPW$#*Idc3Yyvny)rf;eYnrWCy)mruKaZOv=}rHqTU{o9Z=R zK2~=&qG?~uw>c1=qocePtY6k2ua%{%T8=XZ&Gr=5UaFsJm=94OIUmMoA(P#fkpIVmJH|@jq#7MZLDKWOj^jNG*bq+3MEOsq0*+_qQla``F zR}bm5W0n_%I8vA&Y$)QV+n{T9_Jl6fpqZp_TY0(+`J9VsoqA2dIi_NpXP6@ zjGFHp;lc&q+-S=5Y{Mc#+mI&~{E^TaHtDJYm?Pv?8m3 zxJP*<)w;-699D$fUC!|-GT5#mle9hhdI~Px7{jvOywkj};ZI|0rEHAf*ed>ljJ@=r z@1^oN=gGVspLIh;i8o5|L1i4pl+b-)M5@b0+56MGr4N(Le%33tU#$}7|Mx`YPC$YA zi`HHeZ05x`IM3qVw!_QJfmeS3g-?Te)HFILZlymPp*hgMT8f+LF@8~u!e|lw;EV2G R&(j=7bl;;aF~W_T{{e$Li(dc$ literal 0 HcmV?d00001 diff --git a/tools/dep_updaters/update-nghttp2.sh b/tools/dep_updaters/update-nghttp2.sh index ccb36caae13d4d..c19dedf1ca203f 100755 --- a/tools/dep_updaters/update-nghttp2.sh +++ b/tools/dep_updaters/update-nghttp2.sh @@ -42,18 +42,19 @@ cleanup () { trap cleanup INT TERM EXIT NGHTTP2_REF="v$NEW_VERSION" -NGHTTP2_TARBALL="nghttp2-$NEW_VERSION.tar.gz" +NGHTTP2_TARBALL="nghttp2-$NEW_VERSION.tar.xz" cd "$WORKSPACE" echo "Fetching nghttp2 source archive" curl -sL -o "$NGHTTP2_TARBALL" "https://github.com/nghttp2/nghttp2/releases/download/$NGHTTP2_REF/$NGHTTP2_TARBALL" -DEPOSITED_CHECKSUM=$(curl -sL "https://github.com/nghttp2/nghttp2/releases/download/$NGHTTP2_REF/checksums.txt" | grep "$NGHTTP2_TARBALL") +echo "Verifying PGP signature" +curl -sL "https://github.com/nghttp2/nghttp2/releases/download/${NGHTTP2_REF}/${NGHTTP2_TARBALL}.asc" \ +| gpgv --keyring "$BASE_DIR/tools/dep_updaters/nghttp.kbx" "$NGHTTP2_TARBALL" -log_and_verify_sha256sum "nghttp2" "$NGHTTP2_TARBALL" "$DEPOSITED_CHECKSUM" - -gzip -dc "$NGHTTP2_TARBALL" | tar xf - +echo "Unpacking archive" +tar xJf "$NGHTTP2_TARBALL" rm "$NGHTTP2_TARBALL" mv "nghttp2-$NEW_VERSION" nghttp2 diff --git a/tools/dep_updaters/update-nghttp3.sh b/tools/dep_updaters/update-nghttp3.sh index 1a4df351b8abba..dc71735300de35 100755 --- a/tools/dep_updaters/update-nghttp3.sh +++ b/tools/dep_updaters/update-nghttp3.sh @@ -48,8 +48,12 @@ cd "$WORKSPACE" echo "Fetching nghttp3 source archive..." curl -sL -o "$ARCHIVE_BASENAME.tar.xz" "https://github.com/ngtcp2/nghttp3/releases/download/${NGHTTP3_REF}/${ARCHIVE_BASENAME}.tar.xz" -SHA256="$(curl -sL "https://github.com/ngtcp2/nghttp3/releases/download/${NGHTTP3_REF}/checksums.txt" | grep 'tar.xz$')" -log_and_verify_sha256sum "nghttp3" "$ARCHIVE_BASENAME.tar.xz" "$SHA256" + +echo "Verifying PGP signature..." +curl -sL "https://github.com/ngtcp2/nghttp3/releases/download/${NGHTTP3_REF}/${ARCHIVE_BASENAME}.tar.xz.asc" \ +| gpgv --keyring "$BASE_DIR/tools/dep_updaters/nghttp.kbx" - "$ARCHIVE_BASENAME.tar.xz" + +echo "Unpacking archive..." tar -xJf "$ARCHIVE_BASENAME.tar.xz" rm "$ARCHIVE_BASENAME.tar.xz" mv "$ARCHIVE_BASENAME" nghttp3 From b928ea9716cb30a946cdd9ff1c84a3af0b4730d9 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Sun, 5 Oct 2025 18:09:57 +0200 Subject: [PATCH 31/36] test: ensure that the message event is fired Use `common.mustCallAtLeast()` to verify that the `'message'` event is fired. PR-URL: https://github.com/nodejs/node/pull/59952 Reviewed-By: Antoine du Hamel Reviewed-By: Jake Yuesong Li --- .../test-worker-message-port-infinite-message-loop.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-worker-message-port-infinite-message-loop.js b/test/parallel/test-worker-message-port-infinite-message-loop.js index 0cd1cc06802055..d5924d9c3bd086 100644 --- a/test/parallel/test-worker-message-port-infinite-message-loop.js +++ b/test/parallel/test-worker-message-port-infinite-message-loop.js @@ -11,7 +11,7 @@ const { MessageChannel } = require('worker_threads'); const { port1, port2 } = new MessageChannel(); let count = 0; -port1.on('message', () => { +port1.on('message', common.mustCallAtLeast(() => { if (count === 0) { setTimeout(common.mustCall(() => { port1.close(); @@ -20,7 +20,7 @@ port1.on('message', () => { port2.postMessage(0); assert(count++ < 10000, `hit ${count} loop iterations`); -}); +})); port2.postMessage(0); From 40fea57fddb7506b7a0502b963ed24e6a2225936 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 5 Oct 2025 18:57:01 +0100 Subject: [PATCH 32/36] tools: add message on auto-fixing js lint issues in gh workflow PR-URL: https://github.com/nodejs/node/pull/59128 Reviewed-By: Luigi Pinca Reviewed-By: Marco Ippolito Reviewed-By: James M Snell Reviewed-By: Antoine du Hamel --- .github/workflows/linters.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index b258df039221ac..8ef41b0a20cf33 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -103,7 +103,31 @@ jobs: - name: Environment Information run: npx envinfo - name: Lint JavaScript files - run: NODE=$(command -v node) make lint-js + run: | + set +e + NODE=$(command -v node) make lint-js + EXIT_CODE="$?" + if [ "$EXIT_CODE" != "0" ]; then + echo + echo 'ERROR: The JavaScript lint validation failed (the errors are logged above).' + echo ' Please fix the lint errors.' + if NODE=$(command -v node) make lint-js-fix > /dev/null 2>&1; then + echo ' Run:' + echo ' make lint-js-fix' + echo ' to fix the lint issues.' + git --no-pager diff + elif git diff --quiet --exit-code; then + echo ' None of the issue is auto-fixable, so manual fixes for' + echo ' all of the issues are required.' + else + echo ' Run:' + echo ' make lint-js-fix' + echo ' to fix the auto-fixable lint issues.' + echo ' Note that some manual fixes are also required.' + fi + echo + exit "$EXIT_CODE" + fi - name: Get release version numbers if: ${{ github.event.pull_request && github.event.pull_request.base.ref == github.event.pull_request.base.repo.default_branch }} id: get-released-versions From ef67d09f5015f88756f7c0bb9cc2317d9d475407 Mon Sep 17 00:00:00 2001 From: Haram Jeong <91401364+haramj@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:11:45 +0900 Subject: [PATCH 33/36] http: improve writeEarlyHints by avoiding for-of loop PR-URL: https://github.com/nodejs/node/pull/59958 Refs: https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration Reviewed-By: Daeyeon Jeong Reviewed-By: Antoine du Hamel --- lib/_http_server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 8cae06cfde9fa0..4ff976d02ca441 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -330,7 +330,9 @@ ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(hints, cb) { head += 'Link: ' + link + '\r\n'; - for (const key of ObjectKeys(hints)) { + const keys = ObjectKeys(hints); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; if (key !== 'link') { head += key + ': ' + hints[key] + '\r\n'; } From 21970970c7753adbe08b4c702d89209e86f38c51 Mon Sep 17 00:00:00 2001 From: iknoom Date: Thu, 25 Sep 2025 20:44:16 +0900 Subject: [PATCH 34/36] src: remove `AnalyzeTemporaryDtors` option from .clang-tidy PR-URL: https://github.com/nodejs/node/pull/60008 Reviewed-By: Anna Henningsen Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- src/.clang-tidy | 1 - 1 file changed, 1 deletion(-) diff --git a/src/.clang-tidy b/src/.clang-tidy index 9cb5edbe5bee8a..91aabe9a158575 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -22,7 +22,6 @@ Checks: '-*, readability-delete-null-pointer, ' WarningsAsErrors: '' HeaderFilterRegex: '' -AnalyzeTemporaryDtors: false FormatStyle: none User: nodejs/cpp CheckOptions: From 76b4cab8fc59e0f4721a3dd1b1762c4707e7df3b Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 28 Sep 2025 04:02:13 +0200 Subject: [PATCH 35/36] src: bring permissions macros in line with general C/C++ standards Specifically, avoid the hazard of unintentionally evaluating an argument multiple times during macro expansion, and do not assume the available of particular namespaces in the current scope. PR-URL: https://github.com/nodejs/node/pull/60053 Reviewed-By: James M Snell Reviewed-By: Rafael Gonzaga --- src/node_report.cc | 2 +- src/permission/permission.h | 50 ++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/node_report.cc b/src/node_report.cc index 79747aa37c9164..8ff711f12e19f7 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -854,7 +854,7 @@ std::string TriggerNodeReport(Isolate* isolate, THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, - std::string_view(Environment::GetCwd(env->exec_path())), + Environment::GetCwd(env->exec_path()), filename); } } diff --git a/src/permission/permission.h b/src/permission/permission.h index da796ab7b80559..00d612ee531ce9 100644 --- a/src/permission/permission.h +++ b/src/permission/permission.h @@ -27,25 +27,63 @@ class FSReqBase; namespace permission { -#define THROW_IF_INSUFFICIENT_PERMISSIONS(env, perm_, resource_, ...) \ +#define THROW_IF_INSUFFICIENT_PERMISSIONS(env, perm, resource, ...) \ do { \ - if (!env->permission()->is_granted(env, perm_, resource_)) [[unlikely]] { \ + node::Environment* env__ = (env); \ + const node::permission::PermissionScope perm__ = (perm); \ + const auto resource__ = (resource); \ + if (!env__->permission()->is_granted(env__, perm__, resource__)) \ + [[unlikely]] { \ node::permission::Permission::ThrowAccessDenied( \ - (env), perm_, resource_); \ + env__, perm__, resource__); \ return __VA_ARGS__; \ } \ } while (0) #define ASYNC_THROW_IF_INSUFFICIENT_PERMISSIONS( \ - env, wrap, perm_, resource_, ...) \ + env, wrap, perm, resource, ...) \ do { \ - if (!env->permission()->is_granted(env, perm_, resource_)) [[unlikely]] { \ + node::Environment* env__ = (env); \ + const node::permission::PermissionScope perm__ = (perm); \ + const auto resource__ = (resource); \ + if (!env__->permission()->is_granted(env__, perm__, resource__)) \ + [[unlikely]] { \ node::permission::Permission::AsyncThrowAccessDenied( \ - (env), wrap, perm_, resource_); \ + env__, (wrap), perm__, resource__); \ return __VA_ARGS__; \ } \ } while (0) +#define ERR_ACCESS_DENIED_IF_INSUFFICIENT_PERMISSIONS( \ + env, perm, resource, args, ...) \ + do { \ + node::Environment* env__ = (env); \ + const node::permission::PermissionScope perm__ = (perm); \ + const auto resource__ = (resource); \ + if (!env__->permission()->is_granted(env__, perm__, resource__)) \ + [[unlikely]] { \ + Local err_access; \ + if (node::permission::CreateAccessDeniedError(env__, perm__, resource__) \ + .ToLocal(&err_access)) { \ + args.GetReturnValue().Set(err_access); \ + } else { \ + args.GetReturnValue().Set(UV_EACCES); \ + } \ + return __VA_ARGS__; \ + } \ + } while (0) + +#define SET_INSUFFICIENT_PERMISSION_ERROR_CALLBACK(scope) \ + void InsufficientPermissionError(std::string_view resource) { \ + v8::HandleScope handle_scope(env()->isolate()); \ + v8::Context::Scope context_scope(env()->context()); \ + v8::Local arg; \ + if (!permission::CreateAccessDeniedError(env(), (scope), resource) \ + .ToLocal(&arg)) { \ + } \ + MakeCallback(env()->oncomplete_string(), 1, &arg); \ + } + class Permission { public: Permission(); From 6ac4ab19ad02803f03b54501193397563e99988e Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Thu, 23 Oct 2025 12:52:25 +0100 Subject: [PATCH 36/36] 2025-10-28, Version 22.21.1 'Jod' (LTS) PR-URL: https://github.com/nodejs/node/pull/60375 --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V22.md | 43 +++++++++++++++++++++++++++++++++ src/node_version.h | 2 +- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67aab9a199814b..e856c01f5e5d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,8 @@ release. -22.21.0
+22.21.1
+22.21.0
22.20.0
22.19.0
22.18.0
diff --git a/doc/changelogs/CHANGELOG_V22.md b/doc/changelogs/CHANGELOG_V22.md index 6dd993bb8bf6e3..c06362ced99373 100644 --- a/doc/changelogs/CHANGELOG_V22.md +++ b/doc/changelogs/CHANGELOG_V22.md @@ -9,6 +9,7 @@ +22.21.1
22.21.0
22.20.0
22.19.0
@@ -66,6 +67,48 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-10-28, Version 22.21.1 'Jod' (LTS), @aduh95 + +### Commits + +* \[[`af33e8e668`](https://github.com/nodejs/node/commit/af33e8e668)] - **benchmark**: remove unused variable from util/priority-queue (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872) +* \[[`6764ce8756`](https://github.com/nodejs/node/commit/6764ce8756)] - **benchmark**: update count to n in permission startup (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872) +* \[[`4e8d99f0dc`](https://github.com/nodejs/node/commit/4e8d99f0dc)] - **benchmark**: update num to n in dgram offset-length (Bruno Rodrigues) [#59872](https://github.com/nodejs/node/pull/59872) +* \[[`af0a8ba7f8`](https://github.com/nodejs/node/commit/af0a8ba7f8)] - **benchmark**: adjust dgram offset-length len values (Bruno Rodrigues) [#59708](https://github.com/nodejs/node/pull/59708) +* \[[`78efd1be4a`](https://github.com/nodejs/node/commit/78efd1be4a)] - **benchmark**: update num to n in dgram offset-length (Bruno Rodrigues) [#59708](https://github.com/nodejs/node/pull/59708) +* \[[`df72dc96e9`](https://github.com/nodejs/node/commit/df72dc96e9)] - **console,util**: improve array inspection performance (Ruben Bridgewater) [#60037](https://github.com/nodejs/node/pull/60037) +* \[[`ef67d09f50`](https://github.com/nodejs/node/commit/ef67d09f50)] - **http**: improve writeEarlyHints by avoiding for-of loop (Haram Jeong) [#59958](https://github.com/nodejs/node/pull/59958) +* \[[`23468fd76b`](https://github.com/nodejs/node/commit/23468fd76b)] - **http2**: fix allowHttp1+Upgrade, broken by shouldUpgradeCallback (Tim Perry) [#59924](https://github.com/nodejs/node/pull/59924) +* \[[`56abc4ac76`](https://github.com/nodejs/node/commit/56abc4ac76)] - **lib**: optimize priority queue (Gürgün Dayıoğlu) [#60039](https://github.com/nodejs/node/pull/60039) +* \[[`ea5cfd98c5`](https://github.com/nodejs/node/commit/ea5cfd98c5)] - **lib**: implement passive listener behavior per spec (BCD1me) [#59995](https://github.com/nodejs/node/pull/59995) +* \[[`c2dd6eed2f`](https://github.com/nodejs/node/commit/c2dd6eed2f)] - **process**: fix wrong asyncContext under unhandled-rejections=strict (Shima Ryuhei) [#60103](https://github.com/nodejs/node/pull/60103) +* \[[`81a3055710`](https://github.com/nodejs/node/commit/81a3055710)] - **process**: fix default `env` for `process.execve` (Richard Lau) [#60029](https://github.com/nodejs/node/pull/60029) +* \[[`fe492c7ace`](https://github.com/nodejs/node/commit/fe492c7ace)] - **process**: fix hrtime fast call signatures (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600) +* \[[`76b4cab8fc`](https://github.com/nodejs/node/commit/76b4cab8fc)] - **src**: bring permissions macros in line with general C/C++ standards (Anna Henningsen) [#60053](https://github.com/nodejs/node/pull/60053) +* \[[`21970970c7`](https://github.com/nodejs/node/commit/21970970c7)] - **src**: remove `AnalyzeTemporaryDtors` option from .clang-tidy (iknoom) [#60008](https://github.com/nodejs/node/pull/60008) +* \[[`609c063e81`](https://github.com/nodejs/node/commit/609c063e81)] - **src**: remove unused variables from report (Moonki Choi) [#60047](https://github.com/nodejs/node/pull/60047) +* \[[`987841a773`](https://github.com/nodejs/node/commit/987841a773)] - **src**: avoid unnecessary string allocations in SPrintF impl (Anna Henningsen) [#60052](https://github.com/nodejs/node/pull/60052) +* \[[`6e386c0632`](https://github.com/nodejs/node/commit/6e386c0632)] - **src**: make ToLower/ToUpper input args more flexible (Anna Henningsen) [#60052](https://github.com/nodejs/node/pull/60052) +* \[[`c3be1226c7`](https://github.com/nodejs/node/commit/c3be1226c7)] - **src**: allow `std::string_view` arguments to `SPrintF()` and friends (Anna Henningsen) [#60058](https://github.com/nodejs/node/pull/60058) +* \[[`764d35647d`](https://github.com/nodejs/node/commit/764d35647d)] - **src**: remove unnecessary `std::string` error messages (Anna Henningsen) [#60057](https://github.com/nodejs/node/pull/60057) +* \[[`1289ef89ec`](https://github.com/nodejs/node/commit/1289ef89ec)] - **src**: remove unnecessary shadowed functions on Utf8Value & BufferValue (Anna Henningsen) [#60056](https://github.com/nodejs/node/pull/60056) +* \[[`d1fb8a538d`](https://github.com/nodejs/node/commit/d1fb8a538d)] - **src**: avoid unnecessary string -> `char*` -> string round trips (Anna Henningsen) [#60055](https://github.com/nodejs/node/pull/60055) +* \[[`54b439fb5a`](https://github.com/nodejs/node/commit/54b439fb5a)] - **src**: fill `options_args`, `options_env` after vectors are finalized (iknoom) [#59945](https://github.com/nodejs/node/pull/59945) +* \[[`c7c597e2ca`](https://github.com/nodejs/node/commit/c7c597e2ca)] - **src**: use RAII for uv\_process\_options\_t (iknoom) [#59945](https://github.com/nodejs/node/pull/59945) +* \[[`b928ea9716`](https://github.com/nodejs/node/commit/b928ea9716)] - **test**: ensure that the message event is fired (Luigi Pinca) [#59952](https://github.com/nodejs/node/pull/59952) +* \[[`e4b95a5158`](https://github.com/nodejs/node/commit/e4b95a5158)] - **test**: replace diagnostics\_channel stackframe in output snapshots (Chengzhong Wu) [#60024](https://github.com/nodejs/node/pull/60024) +* \[[`4206406694`](https://github.com/nodejs/node/commit/4206406694)] - **test**: mark test-web-locks skip on IBM i (SRAVANI GUNDEPALLI) [#59996](https://github.com/nodejs/node/pull/59996) +* \[[`26394cd5bf`](https://github.com/nodejs/node/commit/26394cd5bf)] - **test**: expand tls-check-server-identity coverage (Diango Gavidia) [#60002](https://github.com/nodejs/node/pull/60002) +* \[[`b58df47995`](https://github.com/nodejs/node/commit/b58df47995)] - **test**: fix typo of test-benchmark-readline.js (Deokjin Kim) [#59993](https://github.com/nodejs/node/pull/59993) +* \[[`af3a59dba8`](https://github.com/nodejs/node/commit/af3a59dba8)] - **test**: verify tracing channel doesn't swallow unhandledRejection (Gerhard Stöbich) [#59974](https://github.com/nodejs/node/pull/59974) +* \[[`cee362242b`](https://github.com/nodejs/node/commit/cee362242b)] - **timers**: fix binding fast call signatures (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600) +* \[[`40fea57fdd`](https://github.com/nodejs/node/commit/40fea57fdd)] - **tools**: add message on auto-fixing js lint issues in gh workflow (Dario Piotrowicz) [#59128](https://github.com/nodejs/node/pull/59128) +* \[[`aac90d351b`](https://github.com/nodejs/node/commit/aac90d351b)] - **tools**: verify signatures when updating nghttp\* (Antoine du Hamel) [#60113](https://github.com/nodejs/node/pull/60113) +* \[[`9fae03c7d9`](https://github.com/nodejs/node/commit/9fae03c7d9)] - **tools**: use dependabot cooldown and move tools/doc (Rafael Gonzaga) [#59978](https://github.com/nodejs/node/pull/59978) +* \[[`81548abdf6`](https://github.com/nodejs/node/commit/81548abdf6)] - **wasi**: fix WasiFunction fast call signature (Renegade334) [#59600](https://github.com/nodejs/node/pull/59600) + ## 2025-10-20, Version 22.21.0 'Jod' (LTS), @aduh95 diff --git a/src/node_version.h b/src/node_version.h index 73e57c69861765..ba16ca3abcf821 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_VERSION_IS_LTS 1 #define NODE_VERSION_LTS_CODENAME "Jod" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)