Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e007924
Report Node.js 26.3.0 (V8 14.6.202.34, ABI 147)
Jarred-Sumner Jun 4, 2026
93fbd32
webstreams: settle pending BYOB reads when the reader is cancelled
Jarred-Sumner Jun 4, 2026
0e69614
node:stream, node:http: sync behavioral changes from Node 24.x to 26.x
Jarred-Sumner Jun 4, 2026
0393015
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 4, 2026
f6fd2e0
Bump bootstrap image versions for Node.js 26.3.0 [build images]
Jarred-Sumner Jun 4, 2026
0eb7699
Node 26: restore native upgrade pieces (V8 14.6 shim, ABI 147) [build…
Jarred-Sumner Jun 4, 2026
e18ae49
http, streams: review fixes for the Node 26 sync [build images]
Jarred-Sumner Jun 4, 2026
406f816
bootstrap: keep the xwin cache on the same filesystem as the splat ou…
Jarred-Sumner Jun 4, 2026
8561035
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 4, 2026
bc2e101
Rebuild CI images for the Node 26.3.0 toolchain [build images]
Jarred-Sumner Jun 4, 2026
a53ae4c
bootstrap: install musl Node.js from unofficial-builds.nodejs.org [bu…
Jarred-Sumner Jun 4, 2026
09f9406
Fix node-gyp on Windows and stale cipher stream tests for Node 26 [bu…
Jarred-Sumner Jun 5, 2026
6abf57e
streams: let destroyed-flagged readables still flush to piped destina…
Jarred-Sumner Jun 5, 2026
95d7107
next-pages tests: skip puppeteer browser download when it can't be us…
Jarred-Sumner Jun 5, 2026
7f3e8d3
Address review feedback: escape-slot lifetime, set-cookie consistency…
Jarred-Sumner Jun 5, 2026
b42f9b9
http2: destroy streams on socket close; never RST a never-started req…
Jarred-Sumner Jun 6, 2026
a00465f
v8: restore HandleScopeData when a Bun handle scope pops [build images]
Jarred-Sumner Jun 6, 2026
33e7483
http2: validate options.signal before the pre-aborted fast path
Jarred-Sumner Jun 6, 2026
ac7e1ce
packer: switch the windows-x64 bake VM to Standard_D4as_v7
Jarred-Sumner Jun 6, 2026
f38abb2
bootstrap: bump image versions past stale v35 tags [build images]
Jarred-Sumner Jun 6, 2026
44a873f
http2: send GOAWAY on graceful close(); run test node-gyp builds unde…
Jarred-Sumner Jun 6, 2026
f983083
v8 tests: run the node-side fixture build under bun too [build images]
Jarred-Sumner Jun 6, 2026
5f70d03
process.config: report clang=1 on macOS; surface exit codes in v8 har…
Jarred-Sumner Jun 6, 2026
6292587
complex-workspace test: skip lifecycle scripts on Windows [build images]
Jarred-Sumner Jun 6, 2026
7082a16
bootstrap: install Chrome on x64 apt images; recognize google-chrome …
Jarred-Sumner Jun 6, 2026
e8b24a3
symbols.def: export Value::IsArray/IsBigInt/IsInt32/IsMap [build images]
Jarred-Sumner Jun 6, 2026
38eadf7
v8 fixture: add stderr step markers to the tests that die on Windows …
Jarred-Sumner Jun 7, 2026
beaee52
v8: export the V8_INLINE members MSVC debug builds import [build images]
Jarred-Sumner Jun 7, 2026
6077779
http2: lastStreamId <= 0 in goaway means the actual last stream [buil…
Jarred-Sumner Jun 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
clang = pkgs.clang_21;
lld = pkgs.lld_21;

# Node.js 24 - matching the bootstrap script (targets 24.3.0, actual version from nixpkgs-unstable)
nodejs = pkgs.nodejs_24;
# Node.js 26 - matching the bootstrap script (targets 26.3.0, actual version from nixpkgs-unstable)
nodejs = pkgs.nodejs_26;

# Build tools and dependencies
packages = [
Expand Down
8 changes: 4 additions & 4 deletions scripts/bootstrap.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Version: 19
# Version: 21
# A script that installs the dependencies needed to build and test Bun on Windows.
# Supports both x64 and ARM64 using Scoop for package management.
# Used by Azure [build images] pipeline.
Expand Down Expand Up @@ -215,9 +215,9 @@ function Install-Git {
}

function Install-NodeJs {
# Pin to match the ABI version Bun expects (NODE_MODULE_VERSION 137).
# Latest Node (25.x) uses ABI 141 which breaks node-gyp tests.
$nodejsVersion = "24.3.0"
# Pin to match the ABI version Bun expects (NODE_MODULE_VERSION 147).
# A mismatched Node ABI breaks node-gyp tests.
$nodejsVersion = "26.3.0"
Install-Scoop-Package "nodejs@$nodejsVersion" -Command node

# Seed node-gyp's cache so napi tests don't re-download headers + node.lib
Expand Down
20 changes: 15 additions & 5 deletions scripts/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh
# Version: 34
# Version: 36

# A script that installs the dependencies needed to build and test Bun.
# This should work on macOS and Linux with a POSIX shell.
Expand Down Expand Up @@ -780,7 +780,7 @@ install_common_software() {
}

nodejs_version_exact() {
print "24.3.0"
print "26.3.0"
}

nodejs_version() {
Expand Down Expand Up @@ -819,7 +819,11 @@ install_nodejs() {

case "$abi" in
musl)
nodejs_mirror="https://bun-nodejs-release.s3.us-west-1.amazonaws.com"
# nodejs.org doesn't publish musl binaries; the unofficial-builds
# project (nodejs/unofficial-builds) ships both x64-musl and
# arm64-musl for current releases. (The old private S3 mirror at
# bun-nodejs-release predates arm64-musl being available there.)
nodejs_mirror="https://unofficial-builds.nodejs.org/download/release"
nodejs_foldername="node-v$nodejs_version-$nodejs_platform-$nodejs_arch-musl"
;;
*)
Expand Down Expand Up @@ -1450,21 +1454,27 @@ install_windows_sysroot() {

execute_sudo rm -rf "$sysroot"
execute_sudo mkdir -p "$sysroot"
# The cache must live on the same filesystem as the output: splat moves
# unpacked files with rename(2), which fails with EXDEV (cross-device
# link) when the download dir is on tmpfs and /opt is not.
xwin_cache="$sysroot.cache"
execute_sudo rm -rf "$xwin_cache"
execute_sudo mkdir -p "$xwin_cache"
# Both target arches in one splat; --include-debug-libs so /MTd (debug
# CRT) links work; --include-atl for <atlstr.h> (rescle.cpp);
# winsysroot-style + MS arch notation so clang-cl and lld-link resolve it
# with a single /winsysroot flag; symlinks stay ON (default) to fix
# include/lib casing on a case-sensitive filesystem.
# stdout is dropped: xwin draws progress bars there even without a TTY,
# which floods the image-build log. Errors stay on stderr.
execute_sudo "$xwin_dir/xwin" --accept-license --arch x86_64,aarch64 --sdk-version 10.0.26100 --crt-version 14.44.17.14 --include-atl --cache-dir "$xwin_dir/cache" \
execute_sudo "$xwin_dir/xwin" --accept-license --arch x86_64,aarch64 --sdk-version 10.0.26100 --crt-version 14.44.17.14 --include-atl --cache-dir "$xwin_cache" \
splat --use-winsysroot-style --preserve-ms-arch-notation --include-debug-libs \
--output "$sysroot" >/dev/null
# clang-cl/lld-link compose SDK paths as "Include"/"Lib" (title case);
# the winsysroot-style splat writes lowercase — alias both spellings.
execute_sudo ln -s include "$sysroot/Windows Kits/10/Include"
execute_sudo ln -s lib "$sysroot/Windows Kits/10/Lib"
execute_sudo rm -rf "$xwin_dir"
execute_sudo rm -rf "$xwin_dir" "$xwin_cache"
# No WINDOWS_SYSROOT export — detectWindowsSysroot() picks up
# /opt/winsysroot by well-known path.
}
Expand Down
6 changes: 5 additions & 1 deletion scripts/build/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,10 @@ function emitJsModules({ n, cfg, sources, o, dirStamp }: Ctx): void {

// InternalModuleRegistry.cpp is read by the script (for a sanity check).
const extraInput = resolve(cfg.cwd, "src", "jsc", "bindings", "InternalModuleRegistry.cpp");
// replacements.ts bakes ErrorCode.ts indices into every bundled module
// ($makeErrorWithCode(N, ...)); without this dep an ErrorCode.ts edit leaves
// stale error numbers in the JS bundles while the C++ enum regenerates.
const errorCodeInput = resolve(cfg.cwd, "src", "jsc", "bindings", "ErrorCode.ts");

// Written into src/ (not codegenDir) — see zigFilesGeneratedIntoSrc at top.
const js2nativeZig = resolve(cfg.cwd, zigFilesGeneratedIntoSrc[1]);
Expand Down Expand Up @@ -791,7 +795,7 @@ function emitJsModules({ n, cfg, sources, o, dirStamp }: Ctx): void {
n.build({
outputs,
rule: "codegen",
inputs: [script, ...sources.js, ...sources.jsCodegen, extraInput],
inputs: [script, ...sources.js, ...sources.jsCodegen, extraInput, errorCodeInput],
orderOnlyInputs: [dirStamp],
vars: {
cwd: cfg.cwd,
Expand Down
7 changes: 6 additions & 1 deletion scripts/build/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { execSync } from "node:child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, symlinkSync } from "node:fs";
import { homedir, arch as hostArch, platform as hostPlatform } from "node:os";
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
import { NODEJS_ABI_VERSION, NODEJS_VERSION } from "./deps/nodejs-headers.ts";
import { NODEJS_ABI_VERSION, NODEJS_V8_VERSION, NODEJS_VERSION } from "./deps/nodejs-headers.ts";
import { WEBKIT_VERSION } from "./deps/webkit.ts";
import { assert, BuildError } from "./error.ts";
import { resolveMacosSdkPath } from "./macos-sdk.ts";
Expand Down Expand Up @@ -61,6 +61,7 @@ export interface Host {
const versionDefaults = {
nodejsVersion: NODEJS_VERSION,
nodejsAbiVersion: NODEJS_ABI_VERSION,
nodejsV8Version: NODEJS_V8_VERSION,
webkitVersion: WEBKIT_VERSION,
};

Expand Down Expand Up @@ -307,6 +308,7 @@ export interface Config {
/** Node.js compat version. Default in versions.ts; override to test a bump. */
nodejsVersion: string;
nodejsAbiVersion: string;
nodejsV8Version: string;
/** WebKit commit. Default in versions.ts; override to test a WebKit branch. */
webkitVersion: string;
}
Expand Down Expand Up @@ -368,6 +370,7 @@ export interface PartialConfig {
// Version pins (defaults in versions.ts).
nodejsVersion?: string;
nodejsAbiVersion?: string;
nodejsV8Version?: string;
webkitVersion?: string;
}

Expand Down Expand Up @@ -1019,6 +1022,7 @@ export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Con
// to test a branch before bumping the pinned default.
const nodejsVersion = partial.nodejsVersion ?? versionDefaults.nodejsVersion;
const nodejsAbiVersion = partial.nodejsAbiVersion ?? versionDefaults.nodejsAbiVersion;
const nodejsV8Version = partial.nodejsV8Version ?? versionDefaults.nodejsV8Version;
const webkitVersion = partial.webkitVersion ?? versionDefaults.webkitVersion;

// ─── macOS SDK ───
Expand Down Expand Up @@ -1180,6 +1184,7 @@ export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Con
version,
revision,
nodejsVersion,
nodejsV8Version,
nodejsAbiVersion,
canaryRevision,
webkitVersion,
Expand Down
7 changes: 5 additions & 2 deletions scripts/build/deps/nodejs-headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import type { Dependency } from "../source.ts";
* download URL, and passed to zig as -Dreported_nodejs_version.
* Override via `--nodejs-version=X.Y.Z` to test a bump.
*/
export const NODEJS_VERSION = "24.3.0";
export const NODEJS_VERSION = "26.3.0";

/** Node.js NODE_MODULE_VERSION — for native addon ABI compat. */
export const NODEJS_ABI_VERSION = "137";
export const NODEJS_ABI_VERSION = "147";
Comment thread
claude[bot] marked this conversation as resolved.

/** V8 version reported by process.versions.v8 — must match the pinned Node.js version's. */
export const NODEJS_V8_VERSION = "14.6.202.34-node.20";

export const nodejsHeaders: Dependency = {
name: "nodejs",
Expand Down
6 changes: 5 additions & 1 deletion scripts/build/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,14 +742,18 @@ export const defines: Flag[] = [
},
{
// Shell-escaped quotes so clang receives literal quotes in the define
// (the preprocessor needs the string to be "24.3.0", not bare 24.3.0).
// (the preprocessor needs the string to be "26.3.0", not bare 26.3.0).
flag: c => `REPORTED_NODEJS_VERSION=\\"${c.nodejsVersion}\\"`,
desc: "Node.js version string reported by process.version",
},
{
flag: c => `REPORTED_NODEJS_ABI_VERSION=${c.nodejsAbiVersion}`,
desc: "Node.js ABI version (process.versions.modules)",
},
{
flag: c => `REPORTED_NODEJS_V8_VERSION=\\"${c.nodejsV8Version}\\"`,
desc: "V8 version string (process.versions.v8)",
},
{
// Hardcoded ON — experimental flag not exposed in config
flag: "USE_BUN_MIMALLOC=1",
Expand Down
6 changes: 5 additions & 1 deletion scripts/packer/windows-x64.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ source "azure-arm" "windows-x64" {

// Build VM — only used during image creation, not for CI runners.
// CI runner VM sizes are set in ci.mjs (azureVmSizes).
vm_size = "Standard_D4ds_v6"
// D4as_v7 (AMD): D4ds_v6 hit repeated AllocationFailed (no capacity for
// that size in the region); Azure's allocation-guidance suggested this
// size as an in-region alternative. Build-only VM, so the CPU vendor
// doesn't affect the produced image.
vm_size = "Standard_D4as_v7"

// Use existing resource group instead of creating a temp one
build_resource_group_name = var.resource_group
Expand Down
8 changes: 4 additions & 4 deletions src/js/builtins/CompressionStream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export function initializeCompressionStream(this, format) {
const zlib = require("node:zlib");
const stream = require("node:stream");
const { newBufferSourceTransformPairFromDuplex } = require("internal/webstreams_adapters");

const builders = {
"deflate": zlib.createDeflate,
Expand All @@ -13,9 +13,9 @@ export function initializeCompressionStream(this, format) {
if (!(format in builders))
throw $ERR_INVALID_ARG_VALUE("format", format, "must be one of: " + Object.keys(builders).join(", "));

const handle = builders[format]();
$putByIdDirectPrivate(this, "readable", stream.Readable.toWeb(handle));
$putByIdDirectPrivate(this, "writable", stream.Writable.toWeb(handle));
const transform = newBufferSourceTransformPairFromDuplex(builders[format]());
$putByIdDirectPrivate(this, "readable", transform.readable);
$putByIdDirectPrivate(this, "writable", transform.writable);

return this;
}
Expand Down
8 changes: 4 additions & 4 deletions src/js/builtins/DecompressionStream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export function initializeDecompressionStream(this, format) {
const zlib = require("node:zlib");
const stream = require("node:stream");
const { newBufferSourceTransformPairFromDuplex } = require("internal/webstreams_adapters");

const builders = {
"deflate": zlib.createInflate,
Expand All @@ -13,9 +13,9 @@ export function initializeDecompressionStream(this, format) {
if (!(format in builders))
throw $ERR_INVALID_ARG_VALUE("format", format, "must be one of: " + Object.keys(builders).join(", "));

const handle = builders[format]();
$putByIdDirectPrivate(this, "readable", stream.Readable.toWeb(handle));
$putByIdDirectPrivate(this, "writable", stream.Writable.toWeb(handle));
const transform = newBufferSourceTransformPairFromDuplex(builders[format]());
$putByIdDirectPrivate(this, "readable", transform.readable);
$putByIdDirectPrivate(this, "writable", transform.writable);

return this;
}
Expand Down
13 changes: 13 additions & 0 deletions src/js/builtins/ReadableStreamInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,19 @@ export function readableStreamCancel(stream: ReadableStream, reason: any) {
if (state === $streamErrored) return Promise.$reject($getByIdDirectPrivate(stream, "storedError"));
$readableStreamClose(stream);

// https://streams.spec.whatwg.org/#readable-stream-cancel step 5: a BYOB
// reader's pending read requests are closed with undefined ($readableStreamClose
// only settles default-reader read requests; respond(0) is not coming after cancel).
const reader = $getByIdDirectPrivate(stream, "reader");
if (reader && $isReadableStreamBYOBReader(reader)) {
const requests = $getByIdDirectPrivate(reader, "readIntoRequests");
if (requests.isNotEmpty()) {
$putByIdDirectPrivate(reader, "readIntoRequests", $createFIFO());
for (var request = requests.shift(); request; request = requests.shift())
$fulfillPromise(request, { value: undefined, done: true });
}
}

const controller = $getByIdDirectPrivate(stream, "readableStreamController");
if (controller === null) return Promise.$resolve();

Expand Down
4 changes: 4 additions & 0 deletions src/js/internal/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ function emitErrorNt(msg, err, callback) {
const setMaxHTTPHeaderSize = $newZigFunction("node_http_binding.zig", "setMaxHTTPHeaderSize", 1);
const getMaxHTTPHeaderSize = $newZigFunction("node_http_binding.zig", "getMaxHTTPHeaderSize", 0);
const kOutHeaders = Symbol("kOutHeaders");
const kProxyConfig = Symbol("kProxyConfig");
const kWaitForProxyTunnel = Symbol("kWaitForProxyTunnel");

function ipToInt(ip) {
const octets = ip.split(".");
Expand Down Expand Up @@ -532,6 +534,7 @@ export {
kPendingCallbacks,
kPort,
kProtocol,
kProxyConfig,
kRealListen,
kRequest,
kRes,
Expand All @@ -542,6 +545,7 @@ export {
kTls,
kUpgradeOrConnect,
kUseDefaultPort,
kWaitForProxyTunnel,
noBodySymbol,
optionsSymbol,
parseProxyConfigFromEnv,
Expand Down
19 changes: 13 additions & 6 deletions src/js/internal/primordials.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,33 @@ const arrayToSafePromiseIterable = (promises, mapFn) =>
const PromiseAll = Promise.all;
const PromiseResolve = Promise.$resolve.bind(Promise);
const SafePromiseAll = (promises, mapFn) => PromiseAll(arrayToSafePromiseIterable(promises, mapFn));
const SafePromiseAllReturnArrayLike = (promises, mapFn) =>
// Shared scheduler for SafePromiseAllReturnVoid/ReturnArrayLike: `returnVal`
// is null for the void variant (no result bookkeeping, resolves with nothing).
const safePromiseAllCollect = (promises, mapFn, returnVal) =>
new Promise((resolve, reject) => {
const { length } = promises;

const returnVal = Array(length);
ObjectSetPrototypeOf(returnVal, null);
if (length === 0) resolve(returnVal);
if (length === 0) resolve(returnVal ?? undefined);

let pendingPromises = length;
for (let i = 0; i < length; i++) {
const promise = mapFn != null ? mapFn(promises[i], i) : promises[i];
PromisePrototypeThen.$call(
PromiseResolve(promise),
result => {
returnVal[i] = result;
if (--pendingPromises === 0) resolve(returnVal);
if (returnVal !== null) returnVal[i] = result;
if (--pendingPromises === 0) resolve(returnVal ?? undefined);
},
reject,
);
}
});
const SafePromiseAllReturnVoid = (promises, mapFn) => safePromiseAllCollect(promises, mapFn, null);
const SafePromiseAllReturnArrayLike = (promises, mapFn) => {
const returnVal = Array(promises.length);
ObjectSetPrototypeOf(returnVal, null);
return safePromiseAllCollect(promises, mapFn, returnVal);
};

export default {
Array,
Expand All @@ -136,6 +142,7 @@ export default {
),
SafePromiseAll,
SafePromiseAllReturnArrayLike,
SafePromiseAllReturnVoid,
SafeSet: makeSafe(
Set,
class SafeSet extends Set {
Expand Down
4 changes: 2 additions & 2 deletions src/js/internal/streams/duplex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ Duplex.fromWeb = function (pair, options) {
return lazyWebStreams().newStreamDuplexFromReadableWritablePair(pair, options);
};

Duplex.toWeb = function (duplex) {
return lazyWebStreams().newReadableWritablePairFromDuplex(duplex);
Duplex.toWeb = function (duplex, options) {
return lazyWebStreams().newReadableWritablePairFromDuplex(duplex, options);
};

let duplexify;
Expand Down
2 changes: 1 addition & 1 deletion src/js/internal/streams/duplexify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ function _duplexify(pair) {
eos(r, err => {
readable = false;
if (err) {
destroyer(r, err);
destroyer(w, err);
}
onfinished(err);
});
Expand Down
Loading