{"slug": "bun-v1-3-9", "title": "Bun v1.3.9", "summary": "Bun v1.3.9 introduces new `bun run --parallel` and `bun run --sequential` commands that allow running multiple package.json scripts concurrently or sequentially with colored, prefixed output, including full integration with `--filter` and `--workspaces` for monorepo support. The update also fixes HTTP/2 connection upgrades via `net.Server`, and adds `Symbol.dispose` support to `mock()` and `spyOn()` in `bun:test`, enabling automatic mock restoration using the `using` keyword.", "body_md": "To install Bun\ncurl -fsSL https://bun.sh/install | bash\nnpm install -g bun\npowershell -c \"irm bun.sh/install.ps1|iex\"\nscoop install bun\nbrew tap oven-sh/bun\nbrew install bun\ndocker pull oven/bun\ndocker run --rm --init --ulimit memlock=-1:-1 oven/bun\nTo upgrade Bun\nbun upgrade\nbun run --parallel\nand bun run --sequential\nRun multiple package.json scripts concurrently or sequentially with Foreman-style prefixed output. Includes full --filter\nand --workspaces\nintegration for running scripts in parallel or sequential across workspace packages.\n# Run \"build\" and \"test\" concurrently from the current package.json\nbun run --parallel build test\n# Run \"build\" and \"test\" sequentially with prefixed output\nbun run --sequential build test\n# Glob-matched script names\nbun run --parallel \"build:*\"\n# Run \"build\" in all workspace packages concurrently\nbun run --parallel --filter '*' build\n# Run \"build\" in all workspace packages sequentially\nbun run --sequential --workspaces build\n# Multiple scripts across all packages\nbun run --parallel --filter '*' build lint test\n# Continue running even if one package fails\nbun run --parallel --no-exit-on-error --filter '*' test\n# Skip packages missing the script\nbun run --parallel --workspaces --if-present build\nEach line of output is prefixed with a colored, padded label so you can tell which script produced it:\nbuild | compiling...\ntest | running suite...\nlint | checking files...\nWhen combined with --filter\nor --workspaces\n, labels include the package name:\npkg-a:build | compiling...\npkg-b:build | compiling...\n--parallel\nstarts all scripts immediately with interleaved, prefixed output. --sequential\nruns scripts one at a time in order. By default, a failure in any script kills all remaining scripts — use --no-exit-on-error\nto let them all finish.\nPre/post scripts (prebuild\n/postbuild\n) are automatically grouped with their main script and run in the correct dependency order within each group.\nHow is this different from --filter?\nbun --filter=\"pkg\" <script>\nrespects dependency order. It doesn't start a script until all it's dependendents are also run. This can be an issue when using long-lived watch-like scripts. --parallel\nand --sequential\ndo not respect dependency order so they won't wait.\nHTTP/2 Connection Upgrades via net.Server\nThe net.Server → Http2SecureServer\nconnection upgrade pattern now works correctly. This pattern is used by libraries like http2-wrapper, crawlee, and custom HTTP/2 proxy servers that accept raw TCP connections on a net.Server\nand forward them to an Http2SecureServer\nvia h2Server.emit('connection', rawSocket)\n.\nimport { createServer } from \"node:net\";\nimport { createSecureServer } from \"node:http2\";\nimport { readFileSync } from \"node:fs\";\nconst h2Server = createSecureServer({\nkey: readFileSync(\"key.pem\"),\ncert: readFileSync(\"cert.pem\"),\n});\nh2Server.on(\"stream\", (stream, headers) => {\nstream.respond({ \":status\": 200 });\nstream.end(\"Hello over HTTP/2!\");\n});\nconst netServer = createServer((rawSocket) => {\n// Forward the raw TCP connection to the HTTP/2 server\nh2Server.emit(\"connection\", rawSocket);\n});\nnetServer.listen(8443);\nSymbol.dispose\nsupport for mock()\nand spyOn()\nmock()\nand spyOn()\nnow implement Symbol.dispose\n, enabling the using\nkeyword to automatically restore mocks when they go out of scope. This eliminates the need to manually call mockRestore()\nor rely on afterEach\ncleanup.\nimport { spyOn, expect, test } from \"bun:test\";\ntest(\"auto-restores spy\", () => {\nconst obj = { method: () => \"original\" };\n{\nusing spy = spyOn(obj, \"method\").mockReturnValue(\"mocked\");\nexpect(obj.method()).toBe(\"mocked\");\n}\n// automatically restored when `spy` leaves scope\nexpect(obj.method()).toBe(\"original\");\n});\n[Symbol.dispose]\nis aliased to mockRestore\n, so it works with both spyOn()\nand mock()\n:\nimport { mock } from \"bun:test\";\nconst fn = mock(() => \"original\");\nfn();\nexpect(fn).toHaveBeenCalledTimes(1);\nfn[Symbol.dispose](); // same as fn.mockRestore()\nexpect(fn).toHaveBeenCalledTimes(0);\nNO_PROXY\nnow respected for explicit proxy options\nPreviously, setting NO_PROXY\nonly worked when the proxy was auto-detected from http_proxy\n/HTTP_PROXY\nenvironment variables. If you explicitly passed a proxy\noption to fetch()\nor new WebSocket()\n, the NO_PROXY\nenvironment variable was ignored.\nNow, NO_PROXY\nis always checked — even when a proxy is explicitly provided via the proxy\noption.\n// NO_PROXY=localhost\n// Previously, this would still use the proxy. Now it correctly bypasses it.\nawait fetch(\"http://localhost:3000/api\", {\nproxy: \"http://my-proxy:8080\",\n});\n// Same fix applies to WebSocket\nconst ws = new WebSocket(\"ws://localhost:3000/ws\", {\nproxy: \"http://my-proxy:8080\",\n});\n--cpu-prof-interval\nflag\nBun now supports the --cpu-prof-interval\nflag to configure the CPU profiler's sampling interval in microseconds, matching Node.js's flag of the same name. The default interval is 1000μs (1ms).\n# Sample every 500μs for higher resolution profiling\nbun --cpu-prof --cpu-prof-interval 500 index.js\nIf used without --cpu-prof\nor --cpu-prof-md\n, Bun will emit a warning.\nESM bytecode in --compile\nUsing --bytecode\nwith --format=esm\nis now supported. Previously, this was unsupported due to missing functionality in JavaScriptCore and now it's fully supported.\nWhen --bytecode\nis used without an explicit --format\n, it continues to default to CommonJS. In a future version of Bun, we may change that default to ESM to make the behavior more consistent.\nThanks to @alistair!\nFixed: Illegal instruction (SIGILL) crashes on ARMv8.0 aarch64 CPUs\nFixed crashes on older ARM64 processors (Cortex-A53, Raspberry Pi 4, AWS a1 instances) caused by mimalloc emitting LSE atomic instructions that require ARMv8.1 or later. Bun now correctly targets ARMv8.0 on Linux aarch64, using outline atomics for runtime dispatch.\nFaster Markdown-to-HTML rendering\nBun.Markdown\nnow uses SIMD-accelerated scanning to find characters that need HTML escaping (&\n, <\n, >\n, \"\n), resulting in 3-15% faster Markdown-to-HTML rendering throughput. Larger documents with fewer special characters see the biggest gains.\nThanks to @billywhizz for the contribution!\nFaster Bun.markdown.react()\nCached frequently-used HTML tag strings (div\n, p\n, h1\n-h6\n, etc.) in the React renderer for Bun.markdown.react()\n, avoiding repeated string allocations on every element creation.\nString object count reduced by 40% and heap size reduced by 6% for a typical render.\nFaster AbortSignal.abort()\nwith no listeners\nAbortSignal.abort()\nnow skips creating and dispatching an Event\nobject when there are no registered listeners, avoiding unnecessary object allocation and dispatch overhead. This results in a ~6% improvement in micro-benchmarks (~16ms saved per 1M calls).\nThanks to @sosukesuzuki for the contribution!\nJavaScriptCore upgrade\nRegExp SIMD Acceleration\nRegular expressions got a major performance boost with a new SIMD-accelerated prefix search, inspired by V8's approach. When a regex has alternatives with known leading characters (e.g., /aaaa|bbbb/\n), JSC now uses SIMD instructions to scan 16 bytes at a time, rapidly rejecting non-matching positions before falling back to scalar matching. This is implemented for both ARM64 (using TBL2) and x86_64 (using PTEST), so all platforms benefit.\nThe x86_64 codegen also gained new constant materialization primitives (move128ToVector\n, move64ToDouble\n, move32ToFloat\n) using broadcast and shuffle instructions, which are necessary for the SIMD regex paths and future SIMD optimizations.\n579b96614b75\n— SIMD fast prefix search for RegExp (ARM64)b7ed3dae4a6a\n— SIMD fast prefix search for RegExp (x86_64)aa596dded063\n— x86_64 constant materialization for SIMD masks\nRegExp JIT: Fixed-Count Parentheses\nNon-capturing parenthesized subpatterns with fixed-count quantifiers like (?:abc){3}\npreviously fell back to the slower Yarr interpreter. They are now JIT-compiled using a counter-based loop, yielding a ~3.9x speedup on affected patterns. A follow-up patch also added JIT support for fixed-count subpatterns with capture groups (e.g., /(a+){2}b/\n), correctly saving and restoring capture state across iterations.\nac63cc259d74\n— JIT support for non-capturing fixed-count parentheses (~3.9x faster)c8b66aa0832b\n— JIT support for fixed-count subpatterns with captures\nString#startsWith\nOptimized in DFG/FTL\nString.prototype.startsWith\nis now an intrinsic in the DFG and FTL JIT tiers, with constant folding support when both the string and search term are known at compile time.\nSet#size\nand Map#size\nOptimized in DFG/FTL and Inline Caches\nThe .size\ngetter on Set\nand Map\nis now handled as an intrinsic in the DFG/FTL tiers and inline caches, eliminating the overhead of a generic getter call.\nString#trim\nOptimized\nString.prototype.trim\n, trimStart\n, and trimEnd\nnow use direct pointer access via span8()\n/span16()\ninstead of indirect str[i]\ncharacter access, avoiding repeated bounds checking.\nObject.defineProperty\nHandled in DFG/FTL\nObject.defineProperty\nis now recognized as an intrinsic in the DFG and FTL JIT tiers. While this patch alone doesn't change benchmark numbers, it lays the groundwork for future optimizations that can specialize based on descriptor shape.\nString.prototype.replace\nReturns Ropes\nWhen using \"string\".replace(\"search\", \"replacement\")\nwith string arguments, JSC now constructs a rope (lazy concatenation) instead of eagerly copying the entire result. This avoids unnecessary allocations for the common case where the result is only used briefly. This aligns with V8's behavior.\nBugfixes\nNode.js compatibility improvements\n- Fixed:\nexistsSync('.')\n,statSync('.')\n, and othernode:fs\noperations incorrectly failing on Windows due to'.'\nbeing normalized to an empty string instead of the current directory. - Fixed:\nFunction.prototype.toString()\nwhitespace now matches V8/Node.js - Fixed 3 rare crashes in\nnode:http2\nBun APIs\n- Fixed:\nBun.stringWidth\nincorrectly reporting Thai SARA AA (U+0E32\n), SARA AM (U+0E33\n), and their Lao equivalents (U+0EB2\n,U+0EB3\n) as zero-width characters instead of width 1. These are spacing vowels, not combining marks, so common Thai words likeคำ\nnow correctly return a width of 2 instead of 1.\nWeb APIs\n- Fixed: a crash that could occur in the\nWebSocket\nclient when usingbinaryType = \"blob\"\nand receiving\"data\"\nevents when no event listener attached. - Fixed: Sequential HTTP requests with proxy-style absolute URLs (e.g.\nGET http://example.com/path HTTP/1.1\n) hanging on the 2nd+ request when using keep-alive connections. This affected HTTP proxy servers built with Bun, which could only handle one request per connection. - Fixed: A security issue in the HTTP server chunked encoding parser that could lead to request smuggling.\nTypeScript types\n- Fixed:\nBun.Build.CompileTarget\nTypeScript type was missing SIMD variants likebun-linux-x64-modern\n, causing type errors when cross-compiling with specific architecture targets.\n- Fixed: Missing\nbun-linux-x64-baseline\nandbun-linux-x64-modern\ncompile target types in TypeScript definitions, which caused type errors when usingBun.build()\nwith these valid targets.\n- Fixed:\nSocket.reload()\nTypeScript types now correctly expect{ socket: handler }\nto match runtime behavior, which requires the handler to be wrapped in asocket\nproperty.", "url": "https://wpnews.pro/news/bun-v1-3-9", "canonical_source": "https://bun.com/blog/bun-v1.3.9", "published_at": "2026-02-08 09:01:15+00:00", "updated_at": "2026-05-22 20:40:51.388005+00:00", "lang": "en", "topics": ["developer-tools", "open-source"], "entities": ["Bun"], "alternates": {"html": "https://wpnews.pro/news/bun-v1-3-9", "markdown": "https://wpnews.pro/news/bun-v1-3-9.md", "text": "https://wpnews.pro/news/bun-v1-3-9.txt", "jsonld": "https://wpnews.pro/news/bun-v1-3-9.jsonld"}}