Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 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;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Build tools and dependencies
packages = [
Expand All @@ -54,7 +54,7 @@
# Bun itself (for running build scripts via `bun bd`)
pkgs.bun

# Node.js - version pinned to 24
# Node.js - version pinned to 26
nodejs

# Python for build scripts
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: 20
# 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
31 changes: 26 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: 37

# 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 Expand Up @@ -1768,6 +1778,17 @@ install_chromium() {
else
install_packages libasound2
fi

# Install Chrome itself on x64 (no arm64 build exists): with a system
# browser present, puppeteer-based tests skip their per-run ~300MB
# Chrome for Testing download entirely (see
# test/harness.ts getPuppeteerInstallEnv).
if [ "$arch" = "x64" ]; then
chrome_deb=$(download_file "https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb")
# Best-effort: execute_sudo aborts the whole script on failure, so the
# fallback chain must run inside a single sudo'd shell.
execute_sudo sh -c "apt-get install -y '$chrome_deb' || dpkg -i '$chrome_deb' || true"
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
;;
dnf | yum)
install_packages \
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";

/** 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
Loading