-26.4.0
+26.5.0
+26.4.0 26.3.1 26.3.0 26.2.0
diff --git a/README.md b/README.md
index dc175eadf5bb6a..45a01ca66fef4a 100644
--- a/README.md
+++ b/README.md
@@ -319,8 +319,6 @@ For information about the governance of the Node.js project, see
**Dario Piotrowicz** <> (he/him)
* [deokjinkim](https://github.com/deokjinkim) -
**Deokjin Kim** <> (he/him)
-* [edsadr](https://github.com/edsadr) -
- **Adrian Estrada** <> (he/him)
* [ErickWendel](https://github.com/ErickWendel) -
**Erick Wendel** <> (he/him)
* [Ethan-Arrowood](https://github.com/Ethan-Arrowood) -
@@ -513,6 +511,8 @@ For information about the governance of the Node.js project, see
**Xu Meng** <> (he/him)
* [dnlup](https://github.com/dnlup) -
**dnlup** <>
+* [edsadr](https://github.com/edsadr) -
+ **Adrian Estrada** <> (he/him)
* [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) -
**Robert Jefe Lindstaedt** <>
* [estliberitas](https://github.com/estliberitas) -
diff --git a/doc/api/buffer.md b/doc/api/buffer.md
index 463d56abe8626f..7e380f7425fbd5 100644
--- a/doc/api/buffer.md
+++ b/doc/api/buffer.md
@@ -596,7 +596,7 @@ UTF-8 string.
### `blob.textStream()`
* Returns: {ReadableStream}
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 63ec4185d6321a..d3c8d415326593 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -1040,7 +1040,7 @@ added:
- v23.6.0
- v22.20.0
changes:
- - version: REPLACEME
+ - version: v26.5.0
pr-url: https://github.com/nodejs/node/pull/64221
description: This is enabled by default.
-->
@@ -1274,7 +1274,7 @@ Previously gated the entire `import.meta.resolve` feature.
> Stability: 1.0 - Early development
@@ -1361,7 +1361,7 @@ added:
- v22.0.0
- v20.17.0
changes:
- - version: REPLACEME
+ - version: v26.5.0
pr-url: https://github.com/nodejs/node/pull/64154
description: Print the top-level awaits without evaluating the modules.
-->
diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md
index c21e83e114572b..e5ace9abcfdddc 100644
--- a/doc/api/diagnostics_channel.md
+++ b/doc/api/diagnostics_channel.md
@@ -948,7 +948,7 @@ added:
- v19.9.0
- v18.19.0
changes:
- - version: REPLACEME
+ - version: v26.5.0
pr-url: https://github.com/nodejs/node/pull/62407
description: Non-native-Promise thenables are now returned as-is,
preserving their original type and methods.
diff --git a/doc/api/errors.md b/doc/api/errors.md
index 4175574f1661d1..db3eb81eff6b90 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -2825,7 +2825,7 @@ A QUIC session failed because version negotiation is required.
diff --git a/doc/api/ffi.md b/doc/api/ffi.md
index 172af6bbc6edee..0623e876083b82 100644
--- a/doc/api/ffi.md
+++ b/doc/api/ffi.md
@@ -715,6 +715,25 @@ This is unsafe and dangerous. The returned pointer can become invalid if the
underlying memory is detached, resized, transferred, or otherwise invalidated.
Using stale pointers can cause memory corruption or process crashes.
+## `ffi.getCurrentEventLoop()`
+
+
+
+* Returns: {bigint}
+
+Returns the address of the current thread's `uv_loop_t` as a `bigint`.
+
+The returned address is for the current Node.js environment. In the main thread,
+this is the main thread event loop. In a worker thread, this is that worker's
+event loop.
+
+This is unsafe and dangerous. The returned pointer is only valid for the lifetime
+of the current environment. Using it after the environment exits, or from native
+code that assumes a different thread or lifetime, can crash the process or
+corrupt memory.
+
## Safety notes
The `node:ffi` module does not track pointer validity, memory ownership, or
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index 60c42ab1a8b8ba..78a3b6210326bb 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -1707,7 +1707,7 @@ are not guaranteed to reflect any correct state of the event loop.
diff --git a/doc/api/webstreams.md b/doc/api/webstreams.md
index 263106b5d9e74a..cf03323913b07a 100644
--- a/doc/api/webstreams.md
+++ b/doc/api/webstreams.md
@@ -109,7 +109,7 @@ For more details refer to the relevant documentation:
### `ReadableStreamTee(stream[, cloneForBranch2])`
> Stability: 1 - Experimental
diff --git a/doc/api/zlib.md b/doc/api/zlib.md
index 3f32a7507b1fa0..f32715ba6cc6b2 100644
--- a/doc/api/zlib.md
+++ b/doc/api/zlib.md
@@ -801,7 +801,7 @@ These advanced options are available for controlling decompression:
diff --git a/doc/changelogs/CHANGELOG_V26.md b/doc/changelogs/CHANGELOG_V26.md
index 650f73bf8ae91f..82ec10df0cf598 100644
--- a/doc/changelogs/CHANGELOG_V26.md
+++ b/doc/changelogs/CHANGELOG_V26.md
@@ -8,6 +8,7 @@
+26.5.0 26.4.0 26.3.1 26.3.0
@@ -46,6 +47,157 @@
* [io.js](CHANGELOG_IOJS.md)
* [Archive](CHANGELOG_ARCHIVE.md)
+
+
+## 2026-07-08, Version 26.5.0 (Current), @richardlau
+
+### Notable Changes
+
+#### New release key
+
+Welcome to our newest releaser, [Stewart X Addison](https://github.com/sxa). Future Node.js releases may be signed with his [release key](https://github.com/nodejs/node/blob/main/README.md#release-keys), `655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD`.
+
+#### Other notable changes
+
+* \[[`55f48446c7`](https://github.com/nodejs/node/commit/55f48446c7)] - **(SEMVER-MINOR)** **buffer**: implement blob.textStream() (Matthew Aitken) [#64036](https://github.com/nodejs/node/pull/64036)
+* \[[`b373202efc`](https://github.com/nodejs/node/commit/b373202efc)] - **(SEMVER-MINOR)** **esm**: add `--experimental-import-text` flag (Efe) [#62300](https://github.com/nodejs/node/pull/62300)
+* \[[`39e0c14455`](https://github.com/nodejs/node/commit/39e0c14455)] - **(SEMVER-MINOR)** **perf\_hooks**: sample delay per event loop iteration (Pablo Erhard) [#62935](https://github.com/nodejs/node/pull/62935)
+* \[[`999a83c937`](https://github.com/nodejs/node/commit/999a83c937)] - **(SEMVER-MINOR)** **stream**: expose ReadableStreamTee (Matteo Collina) [#64195](https://github.com/nodejs/node/pull/64195)
+* \[[`4e0236dc3d`](https://github.com/nodejs/node/commit/4e0236dc3d)] - **(SEMVER-MINOR)** **tls**: report negotiated TLS groups (Filip Skokan) [#64119](https://github.com/nodejs/node/pull/64119)
+
+### Commits
+
+* \[[`87648c0a6c`](https://github.com/nodejs/node/commit/87648c0a6c)] - **benchmark**: trim down the argon2 sets (Filip Skokan) [#64218](https://github.com/nodejs/node/pull/64218)
+* \[[`a483bfd3f0`](https://github.com/nodejs/node/commit/a483bfd3f0)] - **buffer**: remove unreachable overflow check in atob (haramjeong) [#60161](https://github.com/nodejs/node/pull/60161)
+* \[[`6d14279688`](https://github.com/nodejs/node/commit/6d14279688)] - **buffer**: add fast api for isUtf8 and isAscii (Gürgün Dayıoğlu) [#64169](https://github.com/nodejs/node/pull/64169)
+* \[[`55f48446c7`](https://github.com/nodejs/node/commit/55f48446c7)] - **(SEMVER-MINOR)** **buffer**: implement blob.textStream() (Matthew Aitken) [#64036](https://github.com/nodejs/node/pull/64036)
+* \[[`a67d9a7a44`](https://github.com/nodejs/node/commit/a67d9a7a44)] - **build**: allow linting node.1 (Aviv Keller) [#64157](https://github.com/nodejs/node/pull/64157)
+* \[[`06c1fbc25b`](https://github.com/nodejs/node/commit/06c1fbc25b)] - **build**: enable Maglev for riscv64 (Jamie Magee) [#62605](https://github.com/nodejs/node/pull/62605)
+* \[[`518309c363`](https://github.com/nodejs/node/commit/518309c363)] - **build**: suppress clang errors building libffi on Windows (René) [#64222](https://github.com/nodejs/node/pull/64222)
+* \[[`6a80ab485c`](https://github.com/nodejs/node/commit/6a80ab485c)] - **build**: add manually-dispatched stress-test workflow (Joyee Cheung) [#64118](https://github.com/nodejs/node/pull/64118)
+* \[[`f4e7bf1f1c`](https://github.com/nodejs/node/commit/f4e7bf1f1c)] - **build**: pin envinfo versions in github actions (Joyee Cheung) [#64117](https://github.com/nodejs/node/pull/64117)
+* \[[`66f6ac0d86`](https://github.com/nodejs/node/commit/66f6ac0d86)] - **build**: support setting an emulator from configure script (Ivan Trubach) [#53899](https://github.com/nodejs/node/pull/53899)
+* \[[`7f26c54aa6`](https://github.com/nodejs/node/commit/7f26c54aa6)] - **child\_process**: fix permission model propagation via NODE\_OPTIONS (Matteo Collina) [#63972](https://github.com/nodejs/node/pull/63972)
+* \[[`32bb554f5b`](https://github.com/nodejs/node/commit/32bb554f5b)] - **crypto**: fix large DH generator validation (Tobias Nießen) [#64092](https://github.com/nodejs/node/pull/64092)
+* \[[`0908d76ef6`](https://github.com/nodejs/node/commit/0908d76ef6)] - **crypto**: reject small-order EdDSA points during verify (Filip Skokan) [#64026](https://github.com/nodejs/node/pull/64026)
+* \[[`7f7e5863c2`](https://github.com/nodejs/node/commit/7f7e5863c2)] - **deps**: update undici to 8.7.0 (Node.js GitHub Bot) [#64282](https://github.com/nodejs/node/pull/64282)
+* \[[`af91029801`](https://github.com/nodejs/node/commit/af91029801)] - **deps**: update nghttp3 to 1.17.0 (Node.js GitHub Bot) [#64182](https://github.com/nodejs/node/pull/64182)
+* \[[`2e500ba7b0`](https://github.com/nodejs/node/commit/2e500ba7b0)] - **deps**: update googletest to 8b53336594cc52213c6c2c7a0b29194fa896d039 (Node.js GitHub Bot) [#64181](https://github.com/nodejs/node/pull/64181)
+* \[[`74e3aa24ba`](https://github.com/nodejs/node/commit/74e3aa24ba)] - **deps**: update sqlite to 3.53.3 (Node.js GitHub Bot) [#64180](https://github.com/nodejs/node/pull/64180)
+* \[[`c7e57f55a7`](https://github.com/nodejs/node/commit/c7e57f55a7)] - **deps**: c-ares: cherry-pick 8ba37af8e3fb (René) [#64110](https://github.com/nodejs/node/pull/64110)
+* \[[`879fdc4daf`](https://github.com/nodejs/node/commit/879fdc4daf)] - **deps**: V8: backport da20a197a7f9 (Kevin Gibbons) [#64101](https://github.com/nodejs/node/pull/64101)
+* \[[`a640543a7c`](https://github.com/nodejs/node/commit/a640543a7c)] - **deps**: V8: cherry-pick 0cc9eb22c0b0 (Kevin Gibbons) [#64101](https://github.com/nodejs/node/pull/64101)
+* \[[`feefd179e5`](https://github.com/nodejs/node/commit/feefd179e5)] - **deps**: V8: cherry-pick 1a391f98cc7a (Kevin Gibbons) [#64101](https://github.com/nodejs/node/pull/64101)
+* \[[`8ef643d4b0`](https://github.com/nodejs/node/commit/8ef643d4b0)] - **deps**: update googletest to 0b1e895ba4226c2fda5ee0178c9b5b1195a741aa (Node.js GitHub Bot) [#64039](https://github.com/nodejs/node/pull/64039)
+* \[[`9e50bb0655`](https://github.com/nodejs/node/commit/9e50bb0655)] - **dgram**: skip dns.lookup() for literal IP addresses (Ruben Bridgewater) [#64133](https://github.com/nodejs/node/pull/64133)
+* \[[`dc052c095c`](https://github.com/nodejs/node/commit/dc052c095c)] - **diagnostics\_channel**: return original thenable (Stephen Belanger) [#62407](https://github.com/nodejs/node/pull/62407)
+* \[[`a22a840293`](https://github.com/nodejs/node/commit/a22a840293)] - **doc**: clarify QUIC stream state wording (EduardF1) [#63660](https://github.com/nodejs/node/pull/63660)
+* \[[`8d4bec2d71`](https://github.com/nodejs/node/commit/8d4bec2d71)] - **doc**: update Http2SecureServer.on("timeout") default value (YuSheng Chen) [#64187](https://github.com/nodejs/node/pull/64187)
+* \[[`da88f70afa`](https://github.com/nodejs/node/commit/da88f70afa)] - **doc**: add note on visibility of CI failures to new contributor guide (Stewart X Addison) [#64256](https://github.com/nodejs/node/pull/64256)
+* \[[`20ce359ccb`](https://github.com/nodejs/node/commit/20ce359ccb)] - **doc**: clarify HTTP/1.1 response ordering (Matteo Collina) [#64213](https://github.com/nodejs/node/pull/64213)
+* \[[`05eae2835c`](https://github.com/nodejs/node/commit/05eae2835c)] - **doc**: recommend node-stress-single-test for flaky tests (Trivikram Kamat) [#64223](https://github.com/nodejs/node/pull/64223)
+* \[[`3966eb67e7`](https://github.com/nodejs/node/commit/3966eb67e7)] - **doc**: fix typo in examples (Vas Sudanagunta) [#64184](https://github.com/nodejs/node/pull/64184)
+* \[[`12a2b9daa3`](https://github.com/nodejs/node/commit/12a2b9daa3)] - **doc**: fix typo in node-config-schema.json (Hamid Reza Ghavami) [#64188](https://github.com/nodejs/node/pull/64188)
+* \[[`0854482671`](https://github.com/nodejs/node/commit/0854482671)] - **doc**: clarify defense-in-depth issues (Matteo Collina) [#64215](https://github.com/nodejs/node/pull/64215)
+* \[[`ef4915fc3a`](https://github.com/nodejs/node/commit/ef4915fc3a)] - **doc**: fix Fast FFI argument count in ffi.md (Daijiro Wachi) [#63960](https://github.com/nodejs/node/pull/63960)
+* \[[`bb2eed863c`](https://github.com/nodejs/node/commit/bb2eed863c)] - **doc**: add sxa GPG key (ed25519) (Stewart X Addison) [#64193](https://github.com/nodejs/node/pull/64193)
+* \[[`b7bf6e3a06`](https://github.com/nodejs/node/commit/b7bf6e3a06)] - **doc**: add guide and answers to FAQs for first-time contributors (Joyee Cheung) [#63685](https://github.com/nodejs/node/pull/63685)
+* \[[`ff537ba858`](https://github.com/nodejs/node/commit/ff537ba858)] - **doc**: update `Http2Server.close` & `Http2SecureServer.close` (YuSheng Chen) [#63298](https://github.com/nodejs/node/pull/63298)
+* \[[`f3db304588`](https://github.com/nodejs/node/commit/f3db304588)] - **doc**: update list of people in `SECURITY.md` (Richard Lau) [#64152](https://github.com/nodejs/node/pull/64152)
+* \[[`2a126647b0`](https://github.com/nodejs/node/commit/2a126647b0)] - **doc**: clarify vfs is not a sandbox (Matteo Collina) [#64143](https://github.com/nodejs/node/pull/64143)
+* \[[`85fc79dd9b`](https://github.com/nodejs/node/commit/85fc79dd9b)] - **doc**: fix broken links and duplicate stability label (Antoine du Hamel) [#64130](https://github.com/nodejs/node/pull/64130)
+* \[[`189e830eb3`](https://github.com/nodejs/node/commit/189e830eb3)] - **doc**: add missing option to man page (Richard Lau) [#64156](https://github.com/nodejs/node/pull/64156)
+* \[[`7a16ccccd0`](https://github.com/nodejs/node/commit/7a16ccccd0)] - **doc**: announce upcoming end of tier 2 support for macOS x64 (Antoine du Hamel) [#63931](https://github.com/nodejs/node/pull/63931)
+* \[[`d5f826045f`](https://github.com/nodejs/node/commit/d5f826045f)] - **doc**: update toolchain for official AIX releases (Richard Lau) [#64068](https://github.com/nodejs/node/pull/64068)
+* \[[`60abc4400f`](https://github.com/nodejs/node/commit/60abc4400f)] - **doc**: fix callback example import in fs docs (Kamal Rawal) [#63912](https://github.com/nodejs/node/pull/63912)
+* \[[`e470c74a6c`](https://github.com/nodejs/node/commit/e470c74a6c)] - **doc**: fix keepAliveTimeout default in http.createServer options (Jahanzaib iqbal) [#63974](https://github.com/nodejs/node/pull/63974)
+* \[[`851b460583`](https://github.com/nodejs/node/commit/851b460583)] - **esm**: improve ERR\_REQUIRE\_ASYNC\_MODULE (Joyee Cheung) [#64260](https://github.com/nodejs/node/pull/64260)
+* \[[`0cd443df39`](https://github.com/nodejs/node/commit/0cd443df39)] - **esm**: print required top-level await locations without evaluating (Joyee Cheung) [#64154](https://github.com/nodejs/node/pull/64154)
+* \[[`b373202efc`](https://github.com/nodejs/node/commit/b373202efc)] - **(SEMVER-MINOR)** **esm**: add `--experimental-import-text` flag (Efe) [#62300](https://github.com/nodejs/node/pull/62300)
+* \[[`eacfbd0ca5`](https://github.com/nodejs/node/commit/eacfbd0ca5)] - **http**: add CONNECT method handling for default Host header with proxy (Archkon) [#64114](https://github.com/nodejs/node/pull/64114)
+* \[[`aeb539a383`](https://github.com/nodejs/node/commit/aeb539a383)] - **http**: fix drain event with cork/uncork (David Evans) [#64038](https://github.com/nodejs/node/pull/64038)
+* \[[`8e8874b216`](https://github.com/nodejs/node/commit/8e8874b216)] - **http**: document and validate options.path when it's in absolute-form (Joyee Cheung) [#64108](https://github.com/nodejs/node/pull/64108)
+* \[[`eb2e96bc28`](https://github.com/nodejs/node/commit/eb2e96bc28)] - **inspector**: fix crash when writing to closed inspector socket (ympark2011) [#64209](https://github.com/nodejs/node/pull/64209)
+* \[[`243b0e4e57`](https://github.com/nodejs/node/commit/243b0e4e57)] - **lib**: reject string "0" in validatePort when allowZero is false (Daijiro Wachi) [#64174](https://github.com/nodejs/node/pull/64174)
+* \[[`34a537c0ed`](https://github.com/nodejs/node/commit/34a537c0ed)] - **lib**: use `__proto__: null` when calling `ObjectDefineProperty` (Antoine du Hamel) [#64239](https://github.com/nodejs/node/pull/64239)
+* \[[`1f72393f19`](https://github.com/nodejs/node/commit/1f72393f19)] - **lib**: lazily initialize kEvents and kHandlers maps (Guilherme Araújo) [#63702](https://github.com/nodejs/node/pull/63702)
+* \[[`92a3dc3191`](https://github.com/nodejs/node/commit/92a3dc3191)] - **lib,permission**: fix addon permission drop (Martin Wagner) [#64007](https://github.com/nodejs/node/pull/64007)
+* \[[`87b8f2a296`](https://github.com/nodejs/node/commit/87b8f2a296)] - **meta**: fix linter warning in `stale.yml` (Antoine du Hamel) [#64281](https://github.com/nodejs/node/pull/64281)
+* \[[`829c4a5913`](https://github.com/nodejs/node/commit/829c4a5913)] - **meta**: bump actions/cache from 5.0.5 to 6.1.0 (dependabot\[bot]) [#64248](https://github.com/nodejs/node/pull/64248)
+* \[[`0808dcd31c`](https://github.com/nodejs/node/commit/0808dcd31c)] - **meta**: bump github/codeql-action/autobuild from 4.36.1 to 4.36.2 (dependabot\[bot]) [#64247](https://github.com/nodejs/node/pull/64247)
+* \[[`64aa17058f`](https://github.com/nodejs/node/commit/64aa17058f)] - **meta**: bump github/codeql-action/analyze from 4.36.1 to 4.36.2 (dependabot\[bot]) [#64246](https://github.com/nodejs/node/pull/64246)
+* \[[`873d1e0412`](https://github.com/nodejs/node/commit/873d1e0412)] - **meta**: bump actions/checkout from 6.0.2 to 7.0.0 (dependabot\[bot]) [#64245](https://github.com/nodejs/node/pull/64245)
+* \[[`fe460ccf0b`](https://github.com/nodejs/node/commit/fe460ccf0b)] - **meta**: bump codecov/codecov-action from 6.0.1 to 7.0.0 (dependabot\[bot]) [#64244](https://github.com/nodejs/node/pull/64244)
+* \[[`845c63ed50`](https://github.com/nodejs/node/commit/845c63ed50)] - **meta**: bump rtCamp/action-slack-notify from 2.3.3 to 2.4.0 (dependabot\[bot]) [#64243](https://github.com/nodejs/node/pull/64243)
+* \[[`2cad2d6de5`](https://github.com/nodejs/node/commit/2cad2d6de5)] - **meta**: bump github/codeql-action/init from 4.36.1 to 4.36.2 (dependabot\[bot]) [#64242](https://github.com/nodejs/node/pull/64242)
+* \[[`0ddde950c7`](https://github.com/nodejs/node/commit/0ddde950c7)] - **meta**: bump actions/setup-python from 6.2.0 to 6.3.0 (dependabot\[bot]) [#64241](https://github.com/nodejs/node/pull/64241)
+* \[[`c0a8760d2f`](https://github.com/nodejs/node/commit/c0a8760d2f)] - **meta**: bump github/codeql-action/upload-sarif from 4.36.1 to 4.36.2 (dependabot\[bot]) [#64240](https://github.com/nodejs/node/pull/64240)
+* \[[`f49704b9d0`](https://github.com/nodejs/node/commit/f49704b9d0)] - **meta**: clarify V8 flags are outside threat model (Matteo Collina) [#64224](https://github.com/nodejs/node/pull/64224)
+* \[[`6b8dc58e6e`](https://github.com/nodejs/node/commit/6b8dc58e6e)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#64057](https://github.com/nodejs/node/pull/64057)
+* \[[`fe5260cca7`](https://github.com/nodejs/node/commit/fe5260cca7)] - **meta**: update status of past strategic initiatives (Joyee Cheung) [#63480](https://github.com/nodejs/node/pull/63480)
+* \[[`7b01040008`](https://github.com/nodejs/node/commit/7b01040008)] - **meta**: speed up stale bot (Aviv Keller) [#64075](https://github.com/nodejs/node/pull/64075)
+* \[[`874c46c24f`](https://github.com/nodejs/node/commit/874c46c24f)] - **meta**: update sccache version in test-linux-quic (René) [#64043](https://github.com/nodejs/node/pull/64043)
+* \[[`48c5c86363`](https://github.com/nodejs/node/commit/48c5c86363)] - **module**: enable import support for addons by default (Chengzhong Wu) [#64221](https://github.com/nodejs/node/pull/64221)
+* \[[`39e0c14455`](https://github.com/nodejs/node/commit/39e0c14455)] - **(SEMVER-MINOR)** **perf\_hooks**: sample delay per event loop iteration (Pablo Erhard) [#62935](https://github.com/nodejs/node/pull/62935)
+* \[[`f90f1bd032`](https://github.com/nodejs/node/commit/f90f1bd032)] - **perf\_hooks**: add NODE\_PERFORMANCE\_GC\_MINOR\_MARK\_SWEEP constant (Attila Szegedi) [#63877](https://github.com/nodejs/node/pull/63877)
+* \[[`bdf32628c7`](https://github.com/nodejs/node/commit/bdf32628c7)] - **process**: fix finalization cleanup ref tracking (Trivikram Kamat) [#64087](https://github.com/nodejs/node/pull/64087)
+* \[[`9a65b7fff4`](https://github.com/nodejs/node/commit/9a65b7fff4)] - **quic**: drop version negotiation packets with oversized CIDs (Mohamed Sayed) [#64228](https://github.com/nodejs/node/pull/64228)
+* \[[`2699fe4706`](https://github.com/nodejs/node/commit/2699fe4706)] - **quic**: fixes undefined handle in QuicStream kInspect (Marten Richter) [#64170](https://github.com/nodejs/node/pull/64170)
+* \[[`00dea28bb3`](https://github.com/nodejs/node/commit/00dea28bb3)] - **repl**: lazy-load acorn and defer vm context creation (Daijiro Wachi) [#63879](https://github.com/nodejs/node/pull/63879)
+* \[[`ce659a1cf9`](https://github.com/nodejs/node/commit/ce659a1cf9)] - **src**: fix escaping of single quotes in task runner (Antoine du Hamel) [#64089](https://github.com/nodejs/node/pull/64089)
+* \[[`dbb3126e5c`](https://github.com/nodejs/node/commit/dbb3126e5c)] - **src**: abstract tracing agent for both legacy and perfetto (Chengzhong Wu) [#64053](https://github.com/nodejs/node/pull/64053)
+* \[[`12edf1d68d`](https://github.com/nodejs/node/commit/12edf1d68d)] - **src**: avoid redundant call to `std::get_if<>()` (Tobias Nießen) [#64094](https://github.com/nodejs/node/pull/64094)
+* \[[`eda91b6d01`](https://github.com/nodejs/node/commit/eda91b6d01)] - **src**: avoid copying source string in TextEncoder.encode (Yagiz Nizipli) [#63897](https://github.com/nodejs/node/pull/63897)
+* \[[`efbbb9a03c`](https://github.com/nodejs/node/commit/efbbb9a03c)] - **stream**: preserve half-open duplexes in async iteration (Efe) [#64275](https://github.com/nodejs/node/pull/64275)
+* \[[`999a83c937`](https://github.com/nodejs/node/commit/999a83c937)] - **(SEMVER-MINOR)** **stream**: expose ReadableStreamTee (Matteo Collina) [#64195](https://github.com/nodejs/node/pull/64195)
+* \[[`ab5ed72903`](https://github.com/nodejs/node/commit/ab5ed72903)] - **stream**: reject iter consumers on abort (Trivikram Kamat) [#64066](https://github.com/nodejs/node/pull/64066)
+* \[[`d3fa77c5e2`](https://github.com/nodejs/node/commit/d3fa77c5e2)] - **stream**: fix merge abort for pending sources (Trivikram Kamat) [#64013](https://github.com/nodejs/node/pull/64013)
+* \[[`38b99140ed`](https://github.com/nodejs/node/commit/38b99140ed)] - **stream**: refactor unnecessary optional chaining away (Antoine du Hamel) [#64253](https://github.com/nodejs/node/pull/64253)
+* \[[`c81f894ebe`](https://github.com/nodejs/node/commit/c81f894ebe)] - **stream**: cut per-chunk overhead in WHATWG streams (Matteo Collina) [#64252](https://github.com/nodejs/node/pull/64252)
+* \[[`f162234f24`](https://github.com/nodejs/node/commit/f162234f24)] - **stream**: normalize Broadcast.from() byte inputs (Trivikram Kamat) [#64082](https://github.com/nodejs/node/pull/64082)
+* \[[`1182ad8f3b`](https://github.com/nodejs/node/commit/1182ad8f3b)] - **stream**: proxy first own method in Readable.wrap() (Daijiro Wachi) [#64048](https://github.com/nodejs/node/pull/64048)
+* \[[`d0b830b382`](https://github.com/nodejs/node/commit/d0b830b382)] - **stream**: observe abort while awaiting pipeTo source (Trivikram Kamat) [#64015](https://github.com/nodejs/node/pull/64015)
+* \[[`f7adcd8359`](https://github.com/nodejs/node/commit/f7adcd8359)] - **stream**: respect iter consumer abort signals (Trivikram Kamat) [#63997](https://github.com/nodejs/node/pull/63997)
+* \[[`b09e624c6f`](https://github.com/nodejs/node/commit/b09e624c6f)] - **test**: make blob desiredSize assertion robust (Trivikram Kamat) [#64106](https://github.com/nodejs/node/pull/64106)
+* \[[`d0d8f0c774`](https://github.com/nodejs/node/commit/d0d8f0c774)] - **test**: update WPT for urlpattern to 11a459a2b1 (Node.js GitHub Bot) [#64037](https://github.com/nodejs/node/pull/64037)
+* \[[`ff9122c20c`](https://github.com/nodejs/node/commit/ff9122c20c)] - **test**: improve lcov reporter snapshot diagnostics (Trivikram Kamat) [#64049](https://github.com/nodejs/node/pull/64049)
+* \[[`570952d4f3`](https://github.com/nodejs/node/commit/570952d4f3)] - **test**: keep finalization close fixture ref alive (Trivikram Kamat) [#64085](https://github.com/nodejs/node/pull/64085)
+* \[[`1b4f213380`](https://github.com/nodejs/node/commit/1b4f213380)] - **test**: fix typo from overriden to overridden (parkhojeong) [#63403](https://github.com/nodejs/node/pull/63403)
+* \[[`4c91090b8b`](https://github.com/nodejs/node/commit/4c91090b8b)] - **test**: fix typo from funciton to function (parkhojeong) [#63403](https://github.com/nodejs/node/pull/63403)
+* \[[`bf080c7917`](https://github.com/nodejs/node/commit/bf080c7917)] - **test**: mark hr-time WPT flaky on macos15-x64 (Trivikram Kamat) [#64054](https://github.com/nodejs/node/pull/64054)
+* \[[`24e32098c5`](https://github.com/nodejs/node/commit/24e32098c5)] - **test**: use one-off agent in http consumed timeout test (Trivikram Kamat) [#64052](https://github.com/nodejs/node/pull/64052)
+* \[[`3229886de2`](https://github.com/nodejs/node/commit/3229886de2)] - **test**: fix flaky test-runner coverage threshold test (Trivikram Kamat) [#64051](https://github.com/nodejs/node/pull/64051)
+* \[[`83b91ea6ec`](https://github.com/nodejs/node/commit/83b91ea6ec)] - **test\_runner**: filter execArgv fallback for child tests (Trivikram Kamat) [#64056](https://github.com/nodejs/node/pull/64056)
+* \[[`269b609a3d`](https://github.com/nodejs/node/commit/269b609a3d)] - **test\_runner**: improve coverage failure diagnostics (Trivikram Kamat) [#64050](https://github.com/nodejs/node/pull/64050)
+* \[[`0342744c34`](https://github.com/nodejs/node/commit/0342744c34)] - **test\_runner**: add timestamp to JUnit reporter testsuites (sangwook) [#64029](https://github.com/nodejs/node/pull/64029)
+* \[[`086741d121`](https://github.com/nodejs/node/commit/086741d121)] - **timers**: reuse Timeout objects in setStreamTimeout (Matteo Collina) [#64254](https://github.com/nodejs/node/pull/64254)
+* \[[`4e0236dc3d`](https://github.com/nodejs/node/commit/4e0236dc3d)] - **(SEMVER-MINOR)** **tls**: report negotiated TLS groups (Filip Skokan) [#64119](https://github.com/nodejs/node/pull/64119)
+* \[[`3bdd7e20be`](https://github.com/nodejs/node/commit/3bdd7e20be)] - **tls**: handle large RSA exponents in X.509 cert (Tobias Nießen) [#64093](https://github.com/nodejs/node/pull/64093)
+* \[[`c96c838977`](https://github.com/nodejs/node/commit/c96c838977)] - **tools**: update RUSTC\_VERSION for remaining GHA workflows (René) [#64325](https://github.com/nodejs/node/pull/64325)
+* \[[`ee873b7aaf`](https://github.com/nodejs/node/commit/ee873b7aaf)] - **tools**: bump `temporal_rs` version (Antoine du Hamel) [#63281](https://github.com/nodejs/node/pull/63281)
+* \[[`ea3b870155`](https://github.com/nodejs/node/commit/ea3b870155)] - **tools**: remove `envinfo` from our workflows (Antoine du Hamel) [#64259](https://github.com/nodejs/node/pull/64259)
+* \[[`d940f02e8b`](https://github.com/nodejs/node/commit/d940f02e8b)] - **tools**: bump the eslint group in /tools/eslint with 8 updates (dependabot\[bot]) [#64249](https://github.com/nodejs/node/pull/64249)
+* \[[`fe0ea2bb5d`](https://github.com/nodejs/node/commit/fe0ea2bb5d)] - **tools**: bump @node-core/doc-kit (dependabot\[bot]) [#64010](https://github.com/nodejs/node/pull/64010)
+* \[[`4dceefde1e`](https://github.com/nodejs/node/commit/4dceefde1e)] - **tools**: bump undici from 6.24.1 to 6.27.0 in /tools/doc (dependabot\[bot]) [#64031](https://github.com/nodejs/node/pull/64031)
+* \[[`6e187db7d7`](https://github.com/nodejs/node/commit/6e187db7d7)] - **tools**: update c-ares updater script (Antoine du Hamel) [#64194](https://github.com/nodejs/node/pull/64194)
+* \[[`657a35f5a2`](https://github.com/nodejs/node/commit/657a35f5a2)] - **tools**: validate version number in release proposal commit message lint (Antoine du Hamel) [#64070](https://github.com/nodejs/node/pull/64070)
+* \[[`17228a861c`](https://github.com/nodejs/node/commit/17228a861c)] - **tools**: add GHA benchmark runner (Antoine du Hamel) [#60293](https://github.com/nodejs/node/pull/60293)
+* \[[`6d11a71d91`](https://github.com/nodejs/node/commit/6d11a71d91)] - **tools**: update `build-shared/action.yml` to a reusable workflow (Antoine du Hamel) [#64059](https://github.com/nodejs/node/pull/64059)
+* \[[`7a17c50b7f`](https://github.com/nodejs/node/commit/7a17c50b7f)] - **tools**: update libffi updater script (Antoine du Hamel) [#64046](https://github.com/nodejs/node/pull/64046)
+* \[[`28047a3e71`](https://github.com/nodejs/node/commit/28047a3e71)] - **tools**: exclude `libffi` changes from `test-shared` GHA CI (Antoine du Hamel) [#64047](https://github.com/nodejs/node/pull/64047)
+* \[[`58d9685acc`](https://github.com/nodejs/node/commit/58d9685acc)] - **typings**: add typing for crypto (Filip Skokan) [#64122](https://github.com/nodejs/node/pull/64122)
+* \[[`7a9dcad44d`](https://github.com/nodejs/node/commit/7a9dcad44d)] - **util**: fix OOM in inspect color stack formatting (Ijtihed Kilani) [#64022](https://github.com/nodejs/node/pull/64022)
+* \[[`d5f01bbbde`](https://github.com/nodejs/node/commit/d5f01bbbde)] - **vfs**: reject rename into descendant directory (Trivikram Kamat) [#64285](https://github.com/nodejs/node/pull/64285)
+* \[[`0b6af91081`](https://github.com/nodejs/node/commit/0b6af91081)] - **vfs**: handle current-position sentinel in memory files (Trivikram Kamat) [#64163](https://github.com/nodejs/node/pull/64163)
+* \[[`322230d641`](https://github.com/nodejs/node/commit/322230d641)] - **vfs**: support writeFileSync with virtual fds (Trivikram Kamat) [#64165](https://github.com/nodejs/node/pull/64165)
+* \[[`9395d209c7`](https://github.com/nodejs/node/commit/9395d209c7)] - **vfs**: avoid recursive readdir symlink cycles (Matteo Collina) [#64168](https://github.com/nodejs/node/pull/64168)
+* \[[`bbdd7643b6`](https://github.com/nodejs/node/commit/bbdd7643b6)] - **vfs**: read RealFSProvider files from open fd (Trivikram Kamat) [#64104](https://github.com/nodejs/node/pull/64104)
+* \[[`92859b8097`](https://github.com/nodejs/node/commit/92859b8097)] - **vm**: fix copying PropertyDescriptor (Chengzhong Wu) [#64073](https://github.com/nodejs/node/pull/64073)
+* \[[`9046035475`](https://github.com/nodejs/node/commit/9046035475)] - **zlib**: validate flush king for all streams (Ic3b3rg) [#63746](https://github.com/nodejs/node/pull/63746)
+* \[[`98be4304a3`](https://github.com/nodejs/node/commit/98be4304a3)] - **zlib**: validate flush kind for brotli streams (Ic3b3rg) [#63746](https://github.com/nodejs/node/pull/63746)
+* \[[`90007a59a9`](https://github.com/nodejs/node/commit/90007a59a9)] - **zlib**: expose rejectGarbageAfterEnd option (Filip Skokan) [#64023](https://github.com/nodejs/node/pull/64023)
+* \[[`5933516066`](https://github.com/nodejs/node/commit/5933516066)] - **zlib**: reject trailing gzip members in web streams (Filip Skokan) [#64023](https://github.com/nodejs/node/pull/64023)
+
## 2026-06-24, Version 26.4.0 (Current), @aduh95
diff --git a/doc/contributing/pull-requests.md b/doc/contributing/pull-requests.md
index 0893460cbc8b0d..7215c2440a8441 100644
--- a/doc/contributing/pull-requests.md
+++ b/doc/contributing/pull-requests.md
@@ -540,6 +540,13 @@ specific details of how to do this are included in the new collaborator
test run for you as approvals for the pull request come in.
If not, you can ask a collaborator or triager to start a CI run.
+CI access is only available to collaborators and members of the platform
+teams. If you are not yet in one of those teams then you will need someone
+to relay the results to you. If a CI has been completed and failed and a
+day or so has passed, it will be worth commenting in the issue to say you
+cannot see what failed and to politely request in the PR that someone gives
+you that information.
+
Ideally, the code change will pass ("be green") on all platform configurations
supported by Node.js. This means that all tests pass and there are no linting
errors. In reality, however, it is not uncommon for the CI infrastructure itself
diff --git a/doc/contributing/releases.md b/doc/contributing/releases.md
index 1f33d8f2771732..7f96cba0511c43 100644
--- a/doc/contributing/releases.md
+++ b/doc/contributing/releases.md
@@ -39,24 +39,47 @@ official release builds for Node.js, hosted on .
## Who can make a release?
-Release authorization is given by the Node.js TSC. Once authorized, an
-individual must have the following:
+There is a distinction between individuals who can "prepare" a release by
+adding the commits to the branches in GitHub and those who can "publish" the
+releases to nodejs.org.
+
+Individuals who are Members of the
+[backporters team](https://github.com/orgs/nodejs/teams/backporters)
+(restricted link) can land things on the staging branches and prepare
+releases including creating the proposal branches which will be discussed
+later in this document.
+
+Authorization to publish releases is given by the Node.js TSC. This is
+required to publish a release after it has been prepared. If you are working
+on preparing a release for the first time you will be able to do most of the
+steps as a member of the backporters team and have someone else who is
+already onboarded promote the release on your behalf. Once authorized by the
+TSC as a releaser, an individual will require the following to publish
+releases themselves:
### 1. Jenkins release access
-There are three relevant Jenkins jobs that should be used for a release flow:
+There are four relevant Jenkins jobs that should be used for a release flow:
**a.** **Test runs:**
**[node-test-pull-request](https://ci.nodejs.org/job/node-test-pull-request/)**
is used for a final full-test run to ensure that the current _HEAD_ is stable.
-**b.** **Nightly builds:** (optional)
+**b.** **CitGM:**
+**[citgm-smoker](https://ci.nodejs.org/job/citgm-smoker/)** is used to run
+the [CitGM](https://github.com/nodejs/citgm/) tool which tests a build of
+Node.js against a defined set of community modules. This is used during a
+release process to ensure that none of the commonly used modules which are
+tested by CitGM show functional regressions with the new Node.js version
+which could impact users.
+
+**c.** **Nightly builds:** (optional)
**[iojs+release](https://ci-release.nodejs.org/job/iojs+release/)** can be used
to create a nightly release for the current _HEAD_ if public test releases are
required. Builds triggered with this job are published straight to
and are available for public download.
-**c.** **Release builds:**
+**d.** **Release builds:**
**[iojs+release](https://ci-release.nodejs.org/job/iojs+release/)** does all of
the work to build all required release assets. Promotion of the release files is
a manual step once they are ready (see below).
@@ -66,8 +89,8 @@ this access to individuals authorized by the TSC.
### 2. \ access
-The _dist_ user on nodejs.org controls the assets available in
-. is an alias for
+The _dist_ user on the `nodejs.org` host controls the assets available in
+. is an alias for
.
The Jenkins release build workers upload their artifacts to the web server as
@@ -90,6 +113,12 @@ responsible for that release. In order to be able to verify downloaded binaries,
the public should be able to check that the `SHASUMS256.txt` file has been
signed by someone who has been authorized to create a release.
+If you do not currently have a key then you should create one with a
+suitable strength with `gpg --full-generate-key`. The default options
+(currently "ECC sign and encrypt" and "Curve 25519") are good choices and
+consistent with the
+[ssh key recommendations in the GOVERNANCE.md file](https://github.com/nodejs/Release/blob/main/GOVERNANCE.md#ssh-key-guidance).
+
The public keys should be fetchable from a known third-party keyserver.
The OpenPGP keyserver at is recommended.
Use the [submission](https://keys.openpgp.org/upload) form to submit
@@ -108,11 +137,30 @@ gpg --keyserver hkps://keys.openpgp.org --recv-keys
The key you use may be a child/subkey of an existing key.
+If you wish to also upload your key to the commonly used Ubuntu keyservers
+you can do so with
+
+```bash
+gpg --keyserver keyserver.ubuntu.com --send-keys
+```
+
+and check it by switching the server name in the `--recv-keys` operation
+list above to the Ubuntu keyserver. For more information take a look at the
+[Ubuntu GPG howto wiki page](https://help.ubuntu.com/community/GnuPrivacyGuardHowto).
+
Additionally, full GPG key fingerprints for individuals authorized to release
should be listed in the Node.js GitHub README.md file.
-> It is recommended to sign all commits under the Node.js repository.
-> Run: `git config commit.gpgsign true` inside the `node` folder.
+> All commits to branches in `nodejs/node` other than `main` and `actions/*` MUST be signed
+> otherwise pushing to those branches will be rejected by the GitHub rules
+> enforced by the Node.js project.
+> Run: `git config commit.gpgsign true` inside the `node` folder or use the
+> `-S` flag on your git operations (The examples in this document will
+> include `-S` expliticlty)
+
+While GitHub allows signing individual commits using an ssh key,
+that is not covered here as this will not allow you to sign releases, so you
+will need to set up a GPG signing key in GitHub.
## How to create a release
@@ -123,7 +171,7 @@ Notes:
* Version strings are listed below as _"vx.y.z"_ or _"x.y.z"_. Substitute for
the release version.
* Examples will use the fictional release version `1.2.3`.
-* When preparing a security release, follow the security steps in the details
+* When preparing a _security release_, follow the security steps in the details
sections.
### 0. Pre-release steps
@@ -136,13 +184,6 @@ and the release blog post is available on the project website.
Build can be contacted best by opening up an issue on the [Build issue
tracker][].
-When preparing a security release, contact Build at least two weekdays in
-advance of the expected release. To ensure that the security patch(es) can be
-properly tested, run a `node-test-pull-request` job against the `main` branch
-of the `nodejs-private/node-private` repository a day or so before the
-[CI lockdown procedure][] begins. This is to confirm that Jenkins can properly
-access the private repository.
-
### 1. Update the staging branch
Checkout the staging branch locally.
@@ -174,11 +215,11 @@ When landing the PR add the `Backport-PR-URL:` line to each commit. Close the
backport PR with `Landed in ...`. Update the label on the original PR from
`backport-requested-vN.x` to `backported-to-vN.x`.
-You can add the `Backport-PR-URL` metadata by using `--backport` with
-`git node land`
+You can add the `Backport-PR-URL` metadata automatically when landing by
+using `--backport` with `git node land`:
```bash
-git node land --backport $PR-NUMBER
+git node land -S --backport $PR-NUMBER
```
To determine the relevant commits, use
@@ -189,16 +230,32 @@ metadata, as well as the GitHub labels such as `semver-minor` and
omitted from a commit, the commit will show up because it's unsure if it's a
duplicate or not.
+A `branch-diff` run can use a lot of credits and users are
+[limited by default to 5000 per hour](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2026-03-10).
+It is not unusual for a run of branch-diff to
+use around 1000 of these. For this reason it is recommended that when you
+run branch-diff you redirect the output to a file and then process it. You
+can check your current usage with `gh rate_limit` if you have the GitHub CLI
+installed and configured, or using the curl command from
+[this link](https://docs.github.com/en/rest/rate-limit/rate-limit?apiVersion=2026-03-10)
+with authentication e.g.
+
+```bash
+curl -H "Authorization: token $YOURGITHUBTOKEN" -X GET https://api.github.com/rate_limit
+```
+
For a list of commits that could be landed in a minor release on v1.x:
```bash
N=1 sh -c 'branch-diff v$N.x-staging upstream/main --exclude-label=semver-major,dont-land-on-v$N.x,backport-requested-v$N.x,backport-blocked-v$N.x,backport-open-v$N.x,backported-to-v$N.x --filter-release --format=simple'
```
-If the target branch is an LTS line, you should also exclude the `baking-for-lts`:
+If the target branch is an LTS line, you should also exclude the
+`baking-for-lts` and use a Current version released at least two weeks before the expected release date.
+In this example we use 25.5.0 as the base version to prepare a release for Node.js 24:
```bash
-N=1 sh -c 'branch-diff v$N.x-staging upstream/main --exclude-label=semver-major,dont-land-on-v$N.x,backport-requested-v$N.x,backport-blocked-v$N.x,backport-open-v$N.x,backported-to-v$N.x,baking-for-lts --filter-release --format=simple'
+N=24 sh -c 'branch-diff v$N.x-staging v26.5.0 --exclude-label=semver-major,dont-land-on-v$N.x,backport-requested-v$N.x,backport-blocked-v$N.x,backport-open-v$N.x,backported-to-v$N.x,baking-for-lts --filter-release --format=simple'
```
Previously released commits and version bumps do not need to be
@@ -216,6 +273,11 @@ Carefully review the list of commits:
When you are ready to cherry-pick commits, you can automate with the following
command.
+Since this is slightly different from the previous branch-diff output - it
+contains only the commit SHAs and in revert order - you may wish to save
+this before piping it directly to `git cherry-pick` in case it does not go
+cleanly.
+
```bash
N=1 sh -c 'branch-diff v$N.x-staging upstream/main --exclude-label=semver-major,dont-land-on-v$N.x,backport-requested-v$N.x,backport-blocked-v$N.x,backport-open-v$N.x,backported-to-v$N.x --filter-release --format=sha --reverse' | xargs git cherry-pick -S
```
@@ -351,6 +413,8 @@ This will ensure they are included in the "Notable Changes" section of the CHANG
### 3. Update `src/node_version.h`
+_(This step will be done automatically if you are using `create-release-proposal` or `git node release --prepare`)_
+
Set the version for the proposed release using the following macros, which are
already defined in `src/node_version.h`:
@@ -369,6 +433,8 @@ be produced with a version string that does not have a trailing pre-release tag:
### 4. Update the changelog
+_(This step will be done automatically if you are using `create-release-proposal` or `git node release --prepare`)_
+
#### Step 1: Collect the formatted list of changes
Collect a formatted list of commits since the last release. Use
@@ -485,13 +551,15 @@ are formatted correctly.
If this release includes new APIs then it is necessary to document that they
were first added in this version. The relevant commits should already include
`REPLACEME` tags (see [Writing Documentation](./api-documentation.md#writing-documentation)).
-Check for these tags with
```bash
grep REPLACEME doc/api/*.md
```
-and substitute this node version with
+The above command will check for the presence of the tags and show you which
+files need to be updated. You can then perform the replacements with one of
+the following commands using either `sed` or `perl`. In these examples
+`$VERSION` must be prefixed with a `v`:
```bash
sed -i "s/REPLACEME/$VERSION/g" doc/api/*.md
@@ -509,8 +577,6 @@ or
perl -pi -e "s/REPLACEME/$VERSION/g" doc/api/*.md
```
-`$VERSION` should be prefixed with a `v`.
-
If this release includes any new deprecations it is necessary to ensure that
those are assigned a proper static deprecation code. These are listed in the
docs (see `doc/api/deprecations.md`) and in the source as `DEP00XX`. The code
@@ -520,6 +586,8 @@ run.
### 5. Create release commit
+_(This step will be done automatically if you are using `create-release-proposal` or `git node release --prepare`)_
+
The `CHANGELOG.md`, `doc/changelogs/CHANGELOG_Vx.md`, `src/node_version.h`, and
`REPLACEME` changes should be the final commit that will be tagged for the
release. When committing these to git, use the following message format:
@@ -561,6 +629,8 @@ Otherwise, you will leak the commits before the security release.
### 6. Propose release on GitHub
+_(This step will be done automatically if you are using `create-release-proposal` or `git node release --prepare`)_
+
Push the release branch to `nodejs/node`, not to your own fork. This allows
release branches to more easily be passed between members of the release team if
necessary.
@@ -612,13 +682,18 @@ purpose. Run it once with the base `vx.x` branch as a reference and with the
proposal branch to check if new regressions could be introduced in the
ecosystem.
-Use `ncu-ci` to compare `vx.x` run (10) and proposal branch (11)
+Use `ncu-ci` with the two build numbers from the `citgm-smoker` job to
+compare the base `vx.x` run (10) and the new proposal branch (11).
```bash
npm i -g @node-core/utils
ncu-ci citgm 10 11
```
+A number of the modules tested by CitGM are not completely
+reliable so differences shown by the comparison are not immediately cause
+for concern.
+
Security release
@@ -744,7 +819,7 @@ Once you have produced builds that you're happy with you can either run
`git node release --promote`:
```bash
-git node release --promote https://github.com/nodejs/node/pull/XXXX -S
+git node release -S --promote https://github.com/nodejs/node/pull/XXXX
```
to automate the remaining steps until step 16 or you can perform it manually
@@ -760,11 +835,10 @@ fetch the proposal from using the `--fetch-from` flag.
When promoting several releases, you can pass multiple URLs:
```bash
-git node release --promote \
+git node release -S --promote \
--fetch-from git@github.com:nodejs-private/node-private.git \
https://github.com/nodejs-private/node-private/pull/XXXX \
- https://github.com/nodejs-private/node-private/pull/XXXX \
- -S
+ https://github.com/nodejs-private/node-private/pull/XXXX
```
@@ -1494,7 +1568,6 @@ Typical resolution: sign the release again.
```
[Build issue tracker]: https://github.com/nodejs/build/issues/new
-[CI lockdown procedure]: https://github.com/nodejs/build/blob/HEAD/doc/jenkins-guide.md#restricting-access-for-security-releases
[Node.js Snap management repository]: https://github.com/nodejs/snap
[Snap]: https://snapcraft.io/node
[`create-release-post.yml`]: https://github.com/nodejs/nodejs.org/actions/workflows/create-release-post.yml
diff --git a/lib/ffi.js b/lib/ffi.js
index 876e71334e3c2e..d91b6d4521e679 100644
--- a/lib/ffi.js
+++ b/lib/ffi.js
@@ -42,6 +42,7 @@ const {
getUint64,
getFloat32,
getFloat64,
+ getCurrentEventLoop,
exportBytes,
getRawPointer,
kFastArguments,
@@ -320,6 +321,7 @@ module.exports = {
getUint64,
getFloat32,
getFloat64,
+ getCurrentEventLoop,
getRawPointer,
setInt8,
setUint8,
diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js
index b8c07443e93e2a..b565d1d86b9892 100644
--- a/lib/internal/streams/readable.js
+++ b/lib/internal/streams/readable.js
@@ -679,15 +679,17 @@ Readable.prototype.read = function(n) {
// If we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
- // the 'readable' event and move on.
+ // the 'readable' event and move on. `state.length` cannot change
+ // within this block, so it is loaded once instead of three times.
+ const stateLength = state.length;
if (n === 0 &&
(state[kState] & kNeedReadable) !== 0 &&
((state.highWaterMark !== 0 ?
- state.length >= state.highWaterMark :
- state.length > 0) ||
+ stateLength >= state.highWaterMark :
+ stateLength > 0) ||
(state[kState] & kEnded) !== 0)) {
debug('read: emitReadable');
- if (state.length === 0 && (state[kState] & kEnded) !== 0)
+ if (stateLength === 0 && (state[kState] & kEnded) !== 0)
endReadable(this);
else
emitReadable(this);
@@ -1635,8 +1637,14 @@ Readable._fromList = fromList;
// This function is designed to be inlinable, so please take care when making
// changes to the function body.
function fromList(n, state) {
+ // `state.length` cannot change while this function runs (only the
+ // caller updates it, after this returns) and the chunk lengths feeding
+ // the copy loops cannot change across the copy calls, so every
+ // repeated property load below is hoisted into a local.
+ const stateLength = state.length;
+
// nothing buffered.
- if (state.length === 0)
+ if (stateLength === 0)
return null;
let idx = state.bufferIndex;
@@ -1648,7 +1656,7 @@ function fromList(n, state) {
if ((state[kState] & kObjectMode) !== 0) {
ret = buf[idx];
buf[idx++] = null;
- } else if (!n || n >= state.length) {
+ } else if (!n || n >= stateLength) {
// Read it all, truncate the list.
if ((state[kState] & kDecoder) !== 0) {
ret = '';
@@ -1662,61 +1670,68 @@ function fromList(n, state) {
ret = buf[idx];
buf[idx++] = null;
} else {
- ret = Buffer.allocUnsafe(state.length);
+ ret = Buffer.allocUnsafe(stateLength);
let i = 0;
while (idx < len) {
- TypedArrayPrototypeSet(ret, buf[idx], i);
- i += buf[idx].length;
+ const data = buf[idx];
+ TypedArrayPrototypeSet(ret, data, i);
+ i += data.length;
buf[idx++] = null;
}
}
- } else if (n < buf[idx].length) {
- // `slice` is the same for buffers and strings.
- ret = buf[idx].slice(0, n);
- buf[idx] = buf[idx].slice(n);
- } else if (n === buf[idx].length) {
- // First chunk is a perfect match.
- ret = buf[idx];
- buf[idx++] = null;
- } else if ((state[kState] & kDecoder) !== 0) {
- ret = '';
- while (idx < len) {
- const str = buf[idx];
- if (n > str.length) {
- ret += str;
- n -= str.length;
- buf[idx++] = null;
- } else {
- if (n === str.length) {
+ } else {
+ const first = buf[idx];
+ const firstLength = first.length;
+ if (n < firstLength) {
+ // `slice` is the same for buffers and strings.
+ ret = first.slice(0, n);
+ buf[idx] = first.slice(n);
+ } else if (n === firstLength) {
+ // First chunk is a perfect match.
+ ret = first;
+ buf[idx++] = null;
+ } else if ((state[kState] & kDecoder) !== 0) {
+ ret = '';
+ while (idx < len) {
+ const str = buf[idx];
+ const strLength = str.length;
+ if (n > strLength) {
ret += str;
+ n -= strLength;
buf[idx++] = null;
} else {
- ret += str.slice(0, n);
- buf[idx] = str.slice(n);
+ if (n === strLength) {
+ ret += str;
+ buf[idx++] = null;
+ } else {
+ ret += str.slice(0, n);
+ buf[idx] = str.slice(n);
+ }
+ break;
}
- break;
}
- }
- } else {
- ret = Buffer.allocUnsafe(n);
-
- const retLen = n;
- while (idx < len) {
- const data = buf[idx];
- if (n > data.length) {
- TypedArrayPrototypeSet(ret, data, retLen - n);
- n -= data.length;
- buf[idx++] = null;
- } else {
- if (n === data.length) {
+ } else {
+ ret = Buffer.allocUnsafe(n);
+
+ const retLen = n;
+ while (idx < len) {
+ const data = buf[idx];
+ const dataLength = data.length;
+ if (n > dataLength) {
TypedArrayPrototypeSet(ret, data, retLen - n);
+ n -= dataLength;
buf[idx++] = null;
} else {
- TypedArrayPrototypeSet(ret, new FastBuffer(data.buffer, data.byteOffset, n), retLen - n);
- buf[idx] = new FastBuffer(data.buffer, data.byteOffset + n, data.length - n);
+ if (n === dataLength) {
+ TypedArrayPrototypeSet(ret, data, retLen - n);
+ buf[idx++] = null;
+ } else {
+ TypedArrayPrototypeSet(ret, new FastBuffer(data.buffer, data.byteOffset, n), retLen - n);
+ buf[idx] = new FastBuffer(data.buffer, data.byteOffset + n, dataLength - n);
+ }
+ break;
}
- break;
}
}
}
diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js
index a9d57ddeeb1614..c5a2c437d9b78b 100644
--- a/lib/internal/webstreams/readablestream.js
+++ b/lib/internal/webstreams/readablestream.js
@@ -113,9 +113,11 @@ const {
extractSizeAlgorithm,
getNonWritablePropertyDescriptor,
isBrandCheck,
+ kEmptyQueue,
kState,
kType,
lazyTransfer,
+ materializeQueue,
nonOpCancel,
nonOpPull,
nonOpStart,
@@ -2523,6 +2525,11 @@ function readableStreamDefaultControllerEnqueue(controller, chunk) {
reader[kType] === 'ReadableStreamDefaultReader' &&
reader[kState].readRequests.length) {
readableStreamFulfillReadRequest(stream, chunk, false);
+ } else if (controllerState.sizeAlgorithm === defaultSizeAlgorithm) {
+ // The internal default size algorithm is never observable by user
+ // code, always returns 1, and cannot throw: enqueue with the
+ // constant instead of calling it.
+ enqueueValueWithSize(controller, chunk, 1);
} else {
try {
const chunkSize =
@@ -2680,7 +2687,7 @@ function setupReadableStreamDefaultController(
pulling: false,
pullFulfilled: undefined,
pullRejected: undefined,
- queue: [],
+ queue: kEmptyQueue,
queueTotalSize: 0,
started: false,
sizeAlgorithm,
@@ -3155,14 +3162,13 @@ function readableByteStreamControllerEnqueueChunkToQueue(
buffer,
byteOffset,
byteLength) {
- ArrayPrototypePush(
- controller[kState].queue,
- {
- buffer,
- byteOffset,
- byteLength,
- });
- controller[kState].queueTotalSize += byteLength;
+ const state = controller[kState];
+ materializeQueue(state).push({
+ buffer,
+ byteOffset,
+ byteLength,
+ });
+ state.queueTotalSize += byteLength;
}
function readableByteStreamControllerEnqueueDetachedPullIntoToQueue(
@@ -3217,7 +3223,7 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue(
} = controller[kState];
while (totalBytesToCopyRemaining) {
- const headOfQueue = queue[0];
+ const headOfQueue = queue.peek();
const bytesToCopy = MathMin(
totalBytesToCopyRemaining,
headOfQueue.byteLength);
@@ -3235,7 +3241,7 @@ function readableByteStreamControllerFillPullIntoDescriptorFromQueue(
headOfQueue.byteOffset,
bytesToCopy);
if (headOfQueue.byteLength === bytesToCopy) {
- ArrayPrototypeShift(queue);
+ queue.shift();
} else {
headOfQueue.byteOffset += bytesToCopy;
headOfQueue.byteLength -= bytesToCopy;
@@ -3451,7 +3457,7 @@ function readableByteStreamControllerDequeueChunk(controller) {
buffer,
byteOffset,
byteLength,
- } = ArrayPrototypeShift(controller[kState].queue);
+ } = controller[kState].queue.shift();
controller[kState].queueTotalSize -= byteLength;
readableByteStreamControllerHandleQueueDrain(controller);
@@ -3547,7 +3553,7 @@ function setupReadableByteStreamController(
pullRejected: undefined,
started: false,
stream,
- queue: [],
+ queue: kEmptyQueue,
queueTotalSize: 0,
highWaterMark,
pullAlgorithm,
diff --git a/lib/internal/webstreams/util.js b/lib/internal/webstreams/util.js
index fb5e6a3a8fc7d0..a27c61ed5e5db7 100644
--- a/lib/internal/webstreams/util.js
+++ b/lib/internal/webstreams/util.js
@@ -1,11 +1,10 @@
'use strict';
const {
+ Array,
ArrayBufferPrototypeGetByteLength,
ArrayBufferPrototypeGetDetached,
ArrayBufferPrototypeSlice,
- ArrayPrototypePush,
- ArrayPrototypeShift,
AsyncIteratorPrototype,
DataViewPrototypeGetBuffer,
DataViewPrototypeGetByteLength,
@@ -13,6 +12,7 @@ const {
FunctionPrototypeCall,
MathMax,
NumberIsNaN,
+ ObjectFreeze,
PromisePrototypeThen,
PromiseReject,
PromiseResolve,
@@ -152,6 +152,143 @@ function isBrandCheck(brand) {
};
}
+// Backing store for the spec's [[queue]]: a power-of-two ring buffer
+// instead of a plain array. Entries are pushed at the tail and consumed
+// at the head, so a plain array either moves every element or forces the
+// engine to re-linearize on each shift, and the default controllers would
+// additionally have to allocate a { value, size } wrapper object per
+// chunk just to keep the pair together. Default readable/writable
+// controller queues store each entry as (value, size) in two consecutive
+// slots via the *Pair methods; the readable byte controller queue stores
+// its chunk descriptor records in single slots via push/shift/peek. A
+// given instance only ever uses one of the two access patterns, so
+// head/tail stay aligned to the entry stride.
+class Queue {
+ constructor(listLength = 8) {
+ this.head = 0;
+ this.tail = 0;
+ // Number of logical entries currently in the queue: (value, size)
+ // pairs for default controller queues, descriptor records for byte
+ // controller queues.
+ this.length = 0;
+ this.capacityMask = listLength - 1;
+ this.list = new Array(listLength);
+ this.dequeuedSize = 0;
+ }
+
+ // Single-slot entries (readable byte controller chunk records).
+
+ push(entry) {
+ const tail = this.tail;
+ this.list[tail] = entry;
+ this.tail = (tail + 1) & this.capacityMask;
+ this.length++;
+ if (this.tail === this.head)
+ this.grow();
+ }
+
+ shift() {
+ const head = this.head;
+ const list = this.list;
+ const entry = list[head];
+ list[head] = undefined;
+ this.head = (head + 1) & this.capacityMask;
+ if (--this.length === 0)
+ this.rewind();
+ return entry;
+ }
+
+ peek() {
+ return this.list[this.head];
+ }
+
+ // Two-slot (value, size) entries (default controller queues). The
+ // stride is always 2 and capacities are even, so `tail + 1`/`head + 1`
+ // never need to wrap.
+
+ pushPair(value, size) {
+ const tail = this.tail;
+ const list = this.list;
+ list[tail] = value;
+ list[tail + 1] = size;
+ this.tail = (tail + 2) & this.capacityMask;
+ this.length++;
+ if (this.tail === this.head)
+ this.grow();
+ }
+
+ // Returns the dequeued value; the size of the same entry is left in
+ // `this.dequeuedSize` so that callers can update [[queueTotalSize]]
+ // without a per-entry wrapper object having to exist.
+ shiftPair() {
+ const head = this.head;
+ const list = this.list;
+ const value = list[head];
+ this.dequeuedSize = list[head + 1];
+ list[head] = undefined;
+ list[head + 1] = undefined;
+ this.head = (head + 2) & this.capacityMask;
+ if (--this.length === 0)
+ this.rewind();
+ return value;
+ }
+
+ peekPairValue() {
+ return this.list[this.head];
+ }
+
+ // The ring is completely full (the post-push tail caught up with the
+ // head): double the capacity, re-linearizing from the head so index
+ // arithmetic stays trivial.
+ grow() {
+ const list = this.list;
+ const capacity = list.length;
+ const head = this.head;
+ if (head !== 0) {
+ const relinearized = new Array(capacity * 2);
+ let n = 0;
+ for (let i = head; i < capacity; i++)
+ relinearized[n++] = list[i];
+ for (let i = 0; i < head; i++)
+ relinearized[n++] = list[i];
+ this.list = relinearized;
+ this.head = 0;
+ } else {
+ list.length = capacity * 2;
+ }
+ this.tail = capacity;
+ this.capacityMask = (capacity * 2) - 1;
+ }
+
+ // The queue just became empty: restart at slot 0 so shallow queues net
+ // sequential slot access, and drop the enlarged backing store after a
+ // large burst has fully drained.
+ rewind() {
+ this.head = 0;
+ this.tail = 0;
+ if (this.list.length > 1024) {
+ this.list.length = 8;
+ this.capacityMask = 0b111;
+ }
+ }
+}
+
+// Controllers start out with (and are reset to) this shared immutable
+// empty queue, so constructing a stream never allocates queue storage;
+// a real Queue is materialized by the enqueue paths on first use. All
+// dequeue/peek paths are guarded by `.length` (or the equivalent
+// [[queueTotalSize]]) checks, so they can never observe the sentinel in
+// a mutating way; it never stores entries, so it gets a zero-length
+// backing list.
+const kEmptyQueue = ObjectFreeze(new Queue(0));
+
+function materializeQueue(state) {
+ const queue = state.queue;
+ if (queue === kEmptyQueue)
+ return state.queue = new Queue();
+ return queue;
+}
+
// The queue helpers below run once per chunk on the hot paths of every
// default readable/writable stream, so they load the controller state a
// single time and don't assert the existence of the queue fields (both
@@ -159,25 +296,23 @@ function isBrandCheck(brand) {
// replaced wholesale).
function dequeueValue(controller) {
const state = controller[kState];
- assert(state.queue.length);
- const {
- value,
- size,
- } = ArrayPrototypeShift(state.queue);
- state.queueTotalSize = MathMax(0, state.queueTotalSize - size);
+ const queue = state.queue;
+ assert(queue.length);
+ const value = queue.shiftPair();
+ state.queueTotalSize = MathMax(0, state.queueTotalSize - queue.dequeuedSize);
return value;
}
function resetQueue(controller) {
const state = controller[kState];
- state.queue = [];
+ state.queue = kEmptyQueue;
state.queueTotalSize = 0;
}
function peekQueueValue(controller) {
const state = controller[kState];
assert(state.queue.length);
- return state.queue[0].value;
+ return state.queue.peekPairValue();
}
function enqueueValueWithSize(controller, value, size) {
@@ -188,7 +323,7 @@ function enqueueValueWithSize(controller, value, size) {
coercedSize === Infinity) {
throw new ERR_INVALID_ARG_VALUE.RangeError('size', size);
}
- ArrayPrototypePush(state.queue, { value, size: coercedSize });
+ materializeQueue(state).pushPair(value, coercedSize);
state.queueTotalSize += coercedSize;
}
@@ -284,9 +419,11 @@ module.exports = {
getNonWritablePropertyDescriptor,
isBrandCheck,
isPromisePending,
+ kEmptyQueue,
kState,
kType,
lazyTransfer,
+ materializeQueue,
nonOpCancel,
nonOpFlush,
nonOpPull,
diff --git a/lib/internal/webstreams/writablestream.js b/lib/internal/webstreams/writablestream.js
index 9a6af1aa4a1061..53b339e47fa798 100644
--- a/lib/internal/webstreams/writablestream.js
+++ b/lib/internal/webstreams/writablestream.js
@@ -67,6 +67,7 @@ const {
getNonWritablePropertyDescriptor,
isBrandCheck,
isPromisePending,
+ kEmptyQueue,
kState,
kType,
lazyTransfer,
@@ -1177,6 +1178,11 @@ function writableStreamDefaultControllerGetChunkSize(controller, chunk) {
return 1;
}
+ // The internal default size algorithm is never observable by user
+ // code, always returns 1, and cannot throw: skip the call.
+ if (sizeAlgorithm === defaultSizeAlgorithm)
+ return 1;
+
try {
return FunctionPrototypeCall(
sizeAlgorithm,
@@ -1293,7 +1299,7 @@ function setupWritableStreamDefaultController(
abortAlgorithm,
closeAlgorithm,
highWaterMark,
- queue: [],
+ queue: kEmptyQueue,
queueTotalSize: 0,
abortController: new AbortController(),
sizeAlgorithm,
diff --git a/lib/tls.js b/lib/tls.js
index 283e5f5bc7870a..296f6189da1728 100644
--- a/lib/tls.js
+++ b/lib/tls.js
@@ -431,8 +431,14 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
let valid = false;
let reason = 'Unknown reason';
- if (net.isIP(hostnameASCIIWithoutFQDN)) {
- valid = ips.includes(canonicalizeIP(hostnameASCIIWithoutFQDN));
+ // An IP literal must not be IDNA-normalized: domainToASCII() returns '' for
+ // an IPv6 literal (it is not a domain), so matching against the normalized
+ // host would skip IP-SAN matching for IPv6 entirely. Match IP hosts against
+ // the original hostname (net.isIP() rejects non-ASCII, so there is no IDNA
+ // confusion to guard against here); the normalized form is kept for the
+ // DNS-name path below.
+ if (net.isIP(hostname)) {
+ valid = ips.includes(canonicalizeIP(hostname));
if (!valid) {
reason =
`IP: ${hostname} is not in the cert's list: ` + ips.join(', ');
diff --git a/src/node_ffi.cc b/src/node_ffi.cc
index a5b51cb05c0692..8e5729cb4ee0d8 100644
--- a/src/node_ffi.cc
+++ b/src/node_ffi.cc
@@ -1215,6 +1215,17 @@ Local DynamicLibrary::GetConstructorTemplate(
return tmpl;
}
+void GetCurrentEventLoop(const FunctionCallbackInfo& args) {
+ Environment* env = Environment::GetCurrent(args);
+ Isolate* isolate = env->isolate();
+
+ THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, "");
+
+ args.GetReturnValue().Set(BigInt::NewFromUnsigned(
+ isolate,
+ static_cast(reinterpret_cast(env->event_loop()))));
+}
+
// Module initialization.
static void Initialize(Local