Compare commits

..

2 Commits

Author SHA1 Message Date
Jarred Sumner
8f0d75f00a Merge branch 'main' into jarred/split-subprocess 2025-06-12 12:19:07 +02:00
Jarred Sumner
c2f9b1a96f WIP split up subprocess 2025-06-06 18:02:29 -07:00
1329 changed files with 55071 additions and 86829 deletions

View File

@@ -128,8 +128,11 @@ const testPlatforms = [
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "x64", profile: "asan", distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "20.04", tier: "oldest" },
{ os: "linux", arch: "x64", distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04", tier: "oldest" },
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "20.04", tier: "oldest" },
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21", tier: "latest" },

View File

@@ -1,92 +0,0 @@
# Upgrading Bun's Self-Reported Node.js Version
This guide explains how to upgrade the Node.js version that Bun reports for compatibility with Node.js packages and native addons.
## Overview
Bun reports a Node.js version for compatibility with the Node.js ecosystem. This affects:
- `process.version` output
- Node-API (N-API) compatibility
- Native addon ABI compatibility
- V8 API compatibility for addons using V8 directly
## Files That Always Need Updates
### 1. Bootstrap Scripts
- `scripts/bootstrap.sh` - Update `NODEJS_VERSION=`
- `scripts/bootstrap.ps1` - Update `$NODEJS_VERSION =`
### 2. CMake Configuration
- `cmake/Options.cmake`
- `NODEJS_VERSION` - The Node.js version string (e.g., "24.3.0")
- `NODEJS_ABI_VERSION` - The ABI version number (find using command below)
### 3. Version Strings
- `src/bun.js/bindings/BunProcess.cpp`
- Update `Bun__versions_node` with the Node.js version
- Update `Bun__versions_v8` with the V8 version (find using command below)
### 4. N-API Version
- `src/napi/js_native_api.h`
- Update `NAPI_VERSION` define (check Node.js release notes)
## Files That May Need Updates
Only check these if the build fails or tests crash after updating version numbers:
- V8 compatibility files in `src/bun.js/bindings/v8/` (if V8 API changed)
- Test files (if Node.js requires newer C++ standard)
## Quick Commands to Find Version Info
```bash
# Get latest Node.js version info
curl -s https://nodejs.org/dist/index.json | jq '.[0]'
# Get V8 version for a specific Node.js version (replace v24.3.0)
curl -s https://nodejs.org/dist/v24.3.0/node-v24.3.0-headers.tar.gz | tar -xzO node-v24.3.0/include/node/node_version.h | grep V8_VERSION
# Get ABI version for a specific Node.js version
curl -s https://nodejs.org/dist/v24.3.0/node-v24.3.0-headers.tar.gz | tar -xzO node-v24.3.0/include/node/node_version.h | grep NODE_MODULE_VERSION
# Or use the ABI registry
curl -s https://raw.githubusercontent.com/nodejs/node/main/doc/abi_version_registry.json | jq '.NODE_MODULE_VERSION."<version>"'
```
## Update Process
1. **Gather version info** using the commands above
2. **Update the required files** listed in the sections above
3. **Build and test**:
```bash
bun bd
bun bd -e "console.log(process.version)"
bun bd -e "console.log(process.versions.v8)"
bun bd test test/v8/v8.test.ts
bun bd test test/napi/napi.test.ts
```
4. **Check for V8 API changes** only if build fails or tests crash:
- Compare v8-function-callback.h between versions
- Check v8-internal.h for Isolate size changes
- Look for new required APIs in build errors
## If Build Fails or Tests Crash
The V8 API rarely has breaking changes between minor Node.js versions. If you encounter issues:
1. Check build errors for missing symbols or type mismatches
2. Compare V8 headers between old and new Node.js versions
3. Most issues can be resolved by implementing missing functions or adjusting structures
## Testing Checklist
- [ ] `process.version` returns correct version
- [ ] `process.versions.v8` returns correct V8 version
- [ ] `process.config.variables.node_module_version` returns correct ABI
- [ ] V8 tests pass
- [ ] N-API tests pass
## Notes
- Most upgrades only require updating version numbers
- Major V8 version changes (rare) may require API updates
- The V8 shim implements only APIs used by common native addons

View File

@@ -1,23 +0,0 @@
Upgrade Bun's Webkit fork to the latest upstream version of Webkit.
To do that:
- cd vendor/WebKit
- git fetch upstream
- git merge upstream main
- Fix the merge conflicts
- cd ../../ (back to bun)
- make jsc-build (this will take about 7 minutes)
- While it compiles, in another task review the JSC commits between the last version of Webkit and the new version. Write up a summary of the webkit changes in a file called "webkit-changes.md"
- bun run build:local (build a build of Bun with the new Webkit, make sure it compiles)
- After making sure it compiles, run some code to make sure things work. something like ./build/debug-local/bun-debug --print '42' should be all you need
- cd vendor/WebKit
- git commit -am "Upgrade Webkit to the latest version"
- git push
- get the commit SHA in the vendor/WebKit directory of your new commit
- cd ../../ (back to bun)
- Update WEBKIT_VERSION in cmake/tools/SetupWebKit.cmake to the commit SHA of your new commit
- git checkout -b bun/webkit-upgrade-<commit-sha>
- commit + push (without adding the webkit-changes.md file)
- create PR titled "Upgrade Webkit to the <commit-sha>", paste your webkit-changes.md into the PR description
- delete the webkit-changes.md file

View File

@@ -1,10 +0,0 @@
{
"snapshot": "snapshot-20250706-71021aff-cc0d-4a7f-a468-d443b16c4bf1",
"install": "bun install",
"terminals": [
{
"name": "bun build",
"command": "bun run build"
}
]
}

View File

@@ -1,41 +1,13 @@
---
description:
globs: src/**/*.cpp,src/**/*.zig
description: How to build Bun
globs:
alwaysApply: false
---
### Build Commands
# How to build Bun
- **Build debug version**: `bun bd` or `bun run build:debug`
- Creates a debug build at `./build/debug/bun-debug`
- Compilation takes ~2.5 minutes
- **Run tests with your debug build**: `bun bd test <test-file>`
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
- **Run any command with debug build**: `bun bd <command>`
Run:
### Run a file
To run a file, use:
```sh
bun bd <file> <...args>
```bash
bun bd
```
**CRITICAL**: Never use `bun <file>` directly. It will not have your changes.
### Logging
`BUN_DEBUG_$(SCOPE)=1` enables debug logs for a specific debug log scope.
Debug logs look like this:
```zig
const log = bun.Output.scoped(.${SCOPE}, false);
// ...later
log("MY DEBUG LOG", .{})
```
### Code Generation
Code generation happens automatically as part of the build process. There are no commands to run.

View File

@@ -1,9 +1,8 @@
---
description: How Zig works with JavaScriptCore bindings generator
globs:
globs:
alwaysApply: false
---
# Bun's JavaScriptCore Class Bindings Generator
This document explains how Bun's class bindings generator works to bridge Zig and JavaScript code through JavaScriptCore (JSC).
@@ -25,7 +24,7 @@ The `.classes.ts` files define the JavaScript API using a declarative approach:
```typescript
// Example: encoding.classes.ts
define({
name: "TextDecoder",
name: "TextDecoder",
constructor: true,
JSType: "object",
finalize: true,
@@ -41,18 +40,17 @@ define({
},
fatal: {
// Read-only property
getter: true,
getter: true,
},
ignoreBOM: {
// Read-only property
getter: true,
},
},
}
}
});
```
Each class definition specifies:
- The class name
- Whether it has a constructor
- JavaScript type (object, function, etc.)
@@ -89,7 +87,7 @@ pub const TextDecoder = struct {
// Fields
});
}
// Prototype methods - note return type includes JSError
pub fn decode(
this: *TextDecoder,
@@ -98,23 +96,23 @@ pub const TextDecoder = struct {
) bun.JSError!JSC.JSValue {
// Implementation
}
// Getters
pub fn getEncoding(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.createStringFromUTF8(globalObject, this.encoding);
}
pub fn getFatal(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.fatal);
}
// Cleanup - note standard pattern of using deinit/deref
fn deinit(this: *TextDecoder) void {
// Release any retained resources
// Free the pointer at the end.
bun.destroy(this);
}
// Finalize - called by JS garbage collector. This should call deinit, or deref if reference counted.
pub fn finalize(this: *TextDecoder) void {
this.deinit();
@@ -123,7 +121,6 @@ pub const TextDecoder = struct {
```
Key components in the Zig file:
- The struct containing native state
- `pub const js = JSC.Codegen.JS<ClassName>` to include generated code
- Constructor and methods using `bun.JSError!JSValue` return type for proper error handling
@@ -131,7 +128,6 @@ Key components in the Zig file:
- Methods matching the JavaScript interface
- Getters/setters for properties
- Proper resource cleanup pattern with `deinit()` and `finalize()`
- Update `src/bun.js/bindings/generated_classes_list.zig` to include the new class
## Code Generation System
@@ -144,7 +140,6 @@ The binding generator produces C++ code that connects JavaScript and Zig:
5. **Property Caching**: Implements the caching system for properties
The generated C++ code includes:
- A JSC wrapper class (`JSTextDecoder`)
- A prototype class (`JSTextDecoderPrototype`)
- A constructor function (`JSTextDecoderConstructor`)
@@ -157,29 +152,28 @@ The `CallFrame` object provides access to JavaScript execution context:
```zig
pub fn decode(
this: *TextDecoder,
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame
) bun.JSError!JSC.JSValue {
// Get arguments
const input = callFrame.argument(0);
const options = callFrame.argument(1);
// Get this value
const thisValue = callFrame.thisValue();
// Implementation with error handling
if (input.isUndefinedOrNull()) {
return globalObject.throw("Input cannot be null or undefined", .{});
}
// Return value or throw error
return JSC.JSValue.jsString(globalObject, "result");
}
```
CallFrame methods include:
- `argument(i)`: Get the i-th argument
- `argumentCount()`: Get the number of arguments
- `thisValue()`: Get the `this` value
@@ -207,17 +201,17 @@ JSC_DEFINE_CUSTOM_GETTER(TextDecoderPrototype__encodingGetterWrap, (...)) {
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSTextDecoder* thisObject = jsCast<JSTextDecoder*>(JSValue::decode(encodedThisValue));
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
// Check for cached value and return if present
if (JSValue cachedValue = thisObject->m_encoding.get())
return JSValue::encode(cachedValue);
// Get value from Zig implementation
JSC::JSValue result = JSC::JSValue::decode(
TextDecoderPrototype__getEncoding(thisObject->wrapped(), globalObject)
);
RETURN_IF_EXCEPTION(throwScope, {});
// Store in cache for future access
thisObject->m_encoding.set(vm, thisObject, result);
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
@@ -259,7 +253,7 @@ This system provides several key benefits:
1. **Automatic Memory Management**: The JavaScriptCore GC tracks and manages these values
2. **Proper Garbage Collection**: The WriteBarrier ensures values are properly visited during GC
3. **Consistent Access**: Zig code can easily get/set these cached JS values
4. **Performance**: Cached values avoid repeated computation or serialization
4. **Performance**: Cached values avoid repeated computation or serialization
### Use Cases
@@ -287,7 +281,7 @@ Bun uses a consistent pattern for resource cleanup:
pub fn deinit(this: *TextDecoder) void {
// Release resources like strings
this._encoding.deref(); // String deref pattern
// Free any buffers
if (this.buffer) |buffer| {
bun.default_allocator.free(buffer);
@@ -318,7 +312,7 @@ Bun uses `bun.JSError!JSValue` return type for proper error handling:
```zig
pub fn decode(
this: *TextDecoder,
this: *TextDecoder,
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame
) bun.JSError!JSC.JSValue {
@@ -326,14 +320,13 @@ pub fn decode(
if (callFrame.argumentCount() < 1) {
return globalObject.throw("Missing required argument", .{});
}
// Or returning a success value
return JSC.JSValue.jsString(globalObject, "Success!");
}
```
This pattern allows Zig functions to:
1. Return JavaScript values on success
2. Throw JavaScript exceptions on error
3. Propagate errors automatically through the call stack
@@ -346,7 +339,7 @@ The binding system includes robust error handling:
// Example of type checking in generated code
JSTextDecoder* thisObject = jsDynamicCast<JSTextDecoder*>(callFrame->thisValue());
if (UNLIKELY(!thisObject)) {
scope.throwException(lexicalGlobalObject,
scope.throwException(lexicalGlobalObject,
Bun::createInvalidThisError(lexicalGlobalObject, callFrame->thisValue(), "TextDecoder"_s));
return {};
}
@@ -358,7 +351,7 @@ The binding system creates proper JavaScript prototype chains:
1. **Constructor**: JSTextDecoderConstructor with standard .prototype property
2. **Prototype**: JSTextDecoderPrototype with methods and properties
3. **Instances**: Each JSTextDecoder instance with **proto** pointing to prototype
3. **Instances**: Each JSTextDecoder instance with __proto__ pointing to prototype
This ensures JavaScript inheritance works as expected:
@@ -367,7 +360,7 @@ This ensures JavaScript inheritance works as expected:
void JSTextDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSTextDecoderPrototype* prototype)
{
Base::finishCreation(vm, 0, "TextDecoder"_s, PropertyAdditionMode::WithoutStructureTransition);
// Set up the prototype chain
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
ASSERT(inherits(info()));
@@ -379,7 +372,7 @@ void JSTextDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globa
The binding system is optimized for performance:
1. **Direct Pointer Access**: JavaScript objects maintain a direct pointer to Zig objects
2. **Property Caching**: WriteBarrier caching avoids repeated native calls for stable properties
2. **Property Caching**: WriteBarrier caching avoids repeated native calls for stable properties
3. **Memory Management**: JSC garbage collection integrated with Zig memory management
4. **Type Conversion**: Fast paths for common JavaScript/Zig type conversions
@@ -388,7 +381,6 @@ The binding system is optimized for performance:
To create a new class binding in Bun:
1. **Define the class interface** in a `.classes.ts` file:
```typescript
define({
name: "MyClass",
@@ -401,13 +393,12 @@ To create a new class binding in Bun:
myProperty: {
getter: true,
cache: true,
},
},
}
}
});
```
2. **Implement the native functionality** in a `.zig` file:
```zig
pub const MyClass = struct {
// Generated bindings
@@ -418,9 +409,9 @@ To create a new class binding in Bun:
// State
value: []const u8,
pub const new = bun.TrivialNew(@This());
// Constructor
pub fn constructor(
globalObject: *JSGlobalObject,
@@ -429,7 +420,7 @@ To create a new class binding in Bun:
const arg = callFrame.argument(0);
// Implementation
}
// Method
pub fn myMethod(
this: *MyClass,
@@ -438,17 +429,17 @@ To create a new class binding in Bun:
) bun.JSError!JSC.JSValue {
// Implementation
}
// Getter
pub fn getMyProperty(this: *MyClass, globalObject: *JSGlobalObject) JSC.JSValue {
return JSC.JSValue.jsString(globalObject, this.value);
}
// Resource cleanup
pub fn deinit(this: *MyClass) void {
// Clean up resources
}
pub fn finalize(this: *MyClass) void {
this.deinit();
bun.destroy(this);
@@ -483,13 +474,11 @@ For each Zig class, the system generates:
### 3. Zig Bindings
- **External Function Declarations**:
```zig
extern fn TextDecoderPrototype__decode(*TextDecoder, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(JSC.conv) JSC.EncodedJSValue;
```
- **Cached Value Accessors**:
```zig
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue { ... }
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { ... }

View File

@@ -5,6 +5,4 @@
#
# git config blame.ignoreRevsFile .git-blame-ignore-revs
#
4ec410e0d7c5f6a712c323444edbf56b48d432d8 # make @import("bun") work in zig (#19096)
dedd433cbf2e2fe38e51bc166e08fbcc601ad42b # JSValue.undefined -> .jsUndefined()
6b4662ff55f58247cc2fd22e85b4f9805b0950a5 # JSValue.jsUndefined() -> .js_undefined
4ec410e0d7c5f6a712c323444edbf56b48d432d8 # make @import("bun") work in zig (#19096)

13
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,18 @@
# Project
/.github/CODEOWNERS @Jarred-Sumner
# Build system
/CMakeLists.txt @Electroid
/cmake/*.cmake @Electroid
/scripts/ @Electroid
# CI
/.buildkite/ @Electroid
/.github/workflows/ @Electroid
# Debugger protocol
/packages/bun-inspector-protocol/ @Electroid
/packages/bun-debug-adapter-protocol/ @Electroid
# Tests
/test/expectations.txt @Jarred-Sumner

View File

@@ -2,7 +2,6 @@ name: Prefilled crash report
description: Report a crash in Bun
labels:
- crash
- needs triage
body:
- type: markdown
attributes:

View File

@@ -46,8 +46,6 @@ jobs:
run: |
bun scripts/zig-remove-unreferenced-top-level-decls.ts src/
zig fmt src
bun scripts/sortImports src
zig fmt src
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:

View File

@@ -21,16 +21,16 @@ jobs:
set -euo pipefail
# Extract the commit hash from the line after COMMIT
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/CloneZstd.cmake)
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildZstd.cmake)
if [ -z "$CURRENT_VERSION" ]; then
echo "Error: Could not find COMMIT line in CloneZstd.cmake"
echo "Error: Could not find COMMIT line in BuildZstd.cmake"
exit 1
fi
# Validate that it looks like a git hash
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
echo "Error: Invalid git hash format in CloneZstd.cmake"
echo "Error: Invalid git hash format in BuildZstd.cmake"
echo "Found: $CURRENT_VERSION"
echo "Expected: 40 character hexadecimal string"
exit 1
@@ -76,7 +76,7 @@ jobs:
run: |
set -euo pipefail
# Handle multi-line format where COMMIT and its value are on separate lines
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/CloneZstd.cmake
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildZstd.cmake
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
@@ -84,7 +84,7 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |
cmake/targets/CloneZstd.cmake
cmake/targets/BuildZstd.cmake
commit-message: "deps: update zstd to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
title: "deps: update zstd to ${{ steps.check-version.outputs.tag }}"
delete-branch: true

1005
.vscode/launch.json generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
CLAUDE.md

36
AGENTS.md Normal file
View File

@@ -0,0 +1,36 @@
## bun tests
**IMPORTANT**: use the `bun bd` command instead of the `bun` command. For example:
✅ Good
```sh
bun bd test internal/ban-words.test.ts
bun bd ./foo.ts
```
The `bun bd` command runs the DEBUG build. If you forget to run the debug build, your changes will not be reflected..
### Run a file
To run a file, you can use the `bun bd <file-path>` command.
```sh
bun bd ./foo.ts
```
### Run tests
To run a single test, you need to use the `bun bd test <test-name>` command.
```sh
bun bd test internal/ban-words.test.ts
```
You must ALWAYS make sure to pass a file path to the `bun bd test <file-path>` command. DO NOT try to run ALL the tests at once unless you're in a specific subdirectory.
### Run a Node.js test
```sh
bun bd --silent node:test test-fs-link
```

245
CLAUDE.md
View File

@@ -1,245 +0,0 @@
This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed for speed, with a bundler, test runner, and Node.js-compatible package manager. It's written primarily in Zig with C++ for JavaScriptCore integration, powered by WebKit's JavaScriptCore engine.
## Building and Running Bun
### Build Commands
- **Build debug version**: `bun bd` or `bun run build:debug`
- Creates a debug build at `./build/debug/bun-debug`
- Compilation takes ~2.5 minutes
- **Run tests with your debug build**: `bun bd test <test-file>`
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
- **Run any command with debug build**: `bun bd <command>`
### Other Build Variants
- `bun run build:release` - Release build
Address sanitizer is enabled by default in debug builds of Bun.
## Testing
### Running Tests
- **Single test file**: `bun bd test test/js/bun/http/serve.test.ts`
- **Fuzzy match test file**: `bun bd test http/serve.test.ts`
- **With filter**: `bun bd test test/js/bun/http/serve.test.ts -t "should handle"`
### Test Organization
- `test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
- `test/js/node/` - Node.js compatibility tests
- `test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
- `test/cli/` - CLI command tests (install, run, test, etc.)
- `test/regression/issue/` - Regression tests (create one per bug fix)
- `test/bundler/` - Bundler and transpiler tests
- `test/integration/` - End-to-end integration tests
- `test/napi/` - N-API compatibility tests
- `test/v8/` - V8 C++ API compatibility tests
### Writing Tests
Tests use Bun's Jest-compatible test runner with proper test fixtures:
```typescript
import { test, expect } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
test("my feature", async () => {
// Create temp directory with test files
const dir = tempDirWithFiles("test-prefix", {
"index.js": `console.log("hello");`,
});
// Spawn Bun process
await using proc = Bun.spawn({
cmd: [bunExe(), "index.js"],
env: bunEnv,
cwd: dir,
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
expect(exitCode).toBe(0);
expect(stdout).toBe("hello\n");
});
```
## Code Architecture
### Language Structure
- **Zig code** (`src/*.zig`): Core runtime, JavaScript bindings, package manager
- **C++ code** (`src/bun.js/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources
### Core Source Organization
#### Runtime Core (`src/`)
- `bun.zig` - Main entry point
- `cli.zig` - CLI command orchestration
- `js_parser.zig`, `js_lexer.zig`, `js_printer.zig` - JavaScript parsing/printing
- `transpiler.zig` - Wrapper around js_parser with sourcemap support
- `resolver/` - Module resolution system
- `allocators/` - Custom memory allocators for performance
#### JavaScript Runtime (`src/bun.js/`)
- `bindings/` - C++ JavaScriptCore bindings
- Generated classes from `.classes.ts` files
- Manual bindings for complex APIs
- `api/` - Bun-specific APIs
- `server.zig` - HTTP server implementation
- `FFI.zig` - Foreign Function Interface
- `crypto.zig` - Cryptographic operations
- `glob.zig` - File pattern matching
- `node/` - Node.js compatibility layer
- Module implementations (fs, path, crypto, etc.)
- Process and Buffer APIs
- `webcore/` - Web API implementations
- `fetch.zig` - Fetch API
- `streams.zig` - Web Streams
- `Blob.zig`, `Response.zig`, `Request.zig`
- `event_loop/` - Event loop and task management
#### Build Tools & Package Manager
- `src/bundler/` - JavaScript bundler
- Advanced tree-shaking
- CSS processing
- HTML handling
- `src/install/` - Package manager
- `lockfile/` - Lockfile handling
- `npm.zig` - npm registry client
- `lifecycle_script_runner.zig` - Package scripts
#### Other Key Components
- `src/shell/` - Cross-platform shell implementation
- `src/css/` - CSS parser and processor
- `src/http/` - HTTP client implementation
- `websocket_client/` - WebSocket client (including deflate support)
- `src/sql/` - SQL database integrations
- `src/bake/` - Server-side rendering framework
### JavaScript Class Implementation (C++)
When implementing JavaScript classes in C++:
1. Create three classes if there's a public constructor:
- `class Foo : public JSC::JSDestructibleObject` (if has C++ fields)
- `class FooPrototype : public JSC::JSNonFinalObject`
- `class FooConstructor : public JSC::InternalFunction`
2. Define properties using HashTableValue arrays
3. Add iso subspaces for classes with C++ fields
4. Cache structures in ZigGlobalObject
## Development Workflow
### Code Formatting
- `bun run prettier` - Format JS/TS files
- `bun run zig-format` - Format Zig files
- `bun run clang-format` - Format C++ files
### Watching for Changes
- `bun run watch` - Incremental Zig compilation with error checking
- `bun run watch-windows` - Windows-specific watch mode
### Code Generation
Code generation happens automatically as part of the build process. The main scripts are:
- `src/codegen/generate-classes.ts` - Generates Zig & C++ bindings from `*.classes.ts` files
- `src/codegen/generate-jssink.ts` - Generates stream-related classes
- `src/codegen/bundle-modules.ts` - Bundles built-in modules like `node:fs`
- `src/codegen/bundle-functions.ts` - Bundles global functions like `ReadableStream`
In development, bundled modules can be reloaded without rebuilding Zig by running `bun run build`.
## JavaScript Modules (`src/js/`)
Built-in JavaScript modules use special syntax and are organized as:
- `node/` - Node.js compatibility modules (`node:fs`, `node:path`, etc.)
- `bun/` - Bun-specific modules (`bun:ffi`, `bun:sqlite`, etc.)
- `thirdparty/` - NPM modules we replace (like `ws`)
- `internal/` - Internal modules not exposed to users
- `builtins/` - Core JavaScript builtins (streams, console, etc.)
### Special Syntax in Built-in Modules
1. **`$` prefix** - Access to private properties and JSC intrinsics:
```js
const arr = $Array.from(...); // Private global
map.$set(...); // Private method
const arr2 = $newArrayWithSize(5); // JSC intrinsic
```
2. **`require()`** - Must use string literals, resolved at compile time:
```js
const fs = require("fs"); // Directly loads by numeric ID
```
3. **Debug helpers**:
- `$debug()` - Like console.log but stripped in release builds
- `$assert()` - Assertions stripped in release builds
- `if($debug) {}` - Check if debug env var is set
4. **Platform detection**: `process.platform` and `process.arch` are inlined and dead-code eliminated
5. **Export syntax**: Use `export default` which gets converted to a return statement:
```js
export default {
readFile,
writeFile,
};
```
Note: These are NOT ES modules. The preprocessor converts `$` to `@` (JSC's actual syntax) and handles the special functions.
## CI
Bun uses BuildKite for CI. To get the status of a PR, you can use the following command:
```bash
bun ci
```
## Important Development Notes
1. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
2. **Use `await using`** for proper resource cleanup with Bun APIs (Bun.spawn, Bun.serve, Bun.connect, etc.)
3. **Follow existing code style** - check neighboring files for patterns
4. **Create regression tests** in `test/regression/issue/` when fixing bugs
5. **Use absolute paths** - Always use absolute paths in file operations
6. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
7. **Memory management** - In Zig code, be careful with allocators and use defer for cleanup
8. **Cross-platform** - Test on macOS, Linux, and Windows when making platform-specific changes
9. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scope>=1` to enable specific scopes
10. **Transpiled source** - Find transpiled files in `/tmp/bun-debug-src/` for debugging
## Key APIs and Features
### Bun-Specific APIs
- **Bun.serve()** - High-performance HTTP server
- **Bun.spawn()** - Process spawning with better performance than Node.js
- **Bun.file()** - Fast file I/O operations
- **Bun.write()** - Unified API for writing to files, stdout, etc.
- **Bun.$ (Shell)** - Cross-platform shell scripting
- **Bun.SQLite** - Native SQLite integration
- **Bun.FFI** - Call native libraries from JavaScript
- **Bun.Glob** - Fast file pattern matching

View File

@@ -144,14 +144,6 @@ $ bun bd test foo.test.ts
$ bun bd ./foo.ts
```
Bun generally takes about 2.5 minutes to compile a debug build when there are Zig changes. If your development workflow is "change one line, save, rebuild", you will spend too much time waiting for the build to finish. Instead:
- Batch up your changes
- Ensure zls is running with incremental watching for LSP errors (if you use VSCode and install Zig and run `bun run build` once to download Zig, this should just work)
- Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code.
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, false)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
- src/js/\*\*.ts changes are pretty much instant to rebuild. C++ changes are a bit slower, but still much faster than the Zig code (Zig is one compilation unit, C++ is many).
## Code generation scripts
Several code generation scripts are used during Bun's build process. These are run automatically when changes are made to certain files.
@@ -187,7 +179,6 @@ To run a release build from a pull request, you can use the `bun-pr` npm package
bunx bun-pr <pr-number>
bunx bun-pr <branch-name>
bunx bun-pr "https://github.com/oven-sh/bun/pull/1234566"
bunx bun-pr --asan <pr-number> # Linux x64 only
```
This will download the release build from the pull request and add it to `$PATH` as `bun-${pr-number}`. You can then run the build with `bun-${pr-number}`.
@@ -198,18 +189,24 @@ bun-1234566 --version
This works by downloading the release build from the GitHub Actions artifacts on the linked pull request. You may need the `gh` CLI installed to authenticate with GitHub.
## AddressSanitizer
## Valgrind
[AddressSanitizer](https://en.wikipedia.org/wiki/AddressSanitizer) helps find memory issues, and is enabled by default in debug builds of Bun on Linux and macOS. This includes the Zig code and all dependencies. It makes the Zig code take about 2x longer to build, if that's stopping you from being productive you can disable it by setting `-Denable_asan=$<IF:$<BOOL:${ENABLE_ASAN}>,true,false>` to `-Denable_asan=false` in the `cmake/targets/BuildBun.cmake` file, but generally we recommend batching your changes up between builds.
On Linux, valgrind can help find memory issues.
To build a release build with Address Sanitizer, run:
Keep in mind:
- JavaScriptCore doesn't support valgrind. It will report spurious errors.
- Valgrind is slow
- Mimalloc will sometimes cause spurious errors when debug build is enabled
You'll need a very recent version of Valgrind due to DWARF 5 debug symbols. You may need to manually compile Valgrind instead of using it from your Linux package manager.
`--fair-sched=try` is necessary if running multithreaded code in Bun (such as the bundler). Otherwise it will hang.
```bash
$ bun run build:release:asan
$ valgrind --fair-sched=try --track-origins=yes bun-debug <args>
```
In CI, we run our test suite with at least one target that is built with Address Sanitizer.
## Building WebKit locally + Debug mode of JSC
WebKit is not cloned by default (to save time and disk space). To clone and build WebKit locally, run:

2
LATEST
View File

@@ -1 +1 @@
1.2.18
1.2.16

View File

@@ -1,28 +0,0 @@
import { SourceMap } from "node:module";
import { readFileSync } from "node:fs";
import { bench, run } from "../runner.mjs";
const json = JSON.parse(readFileSync(process.argv.at(-1), "utf-8"));
bench("new SourceMap(json)", () => {
return new SourceMap(json);
});
const map = new SourceMap(json);
const toRotate = [];
for (let j = 0; j < 10000; j++) {
if (map.findEntry(0, j).generatedColumn) {
toRotate.push(j);
if (toRotate.length > 5) break;
}
}
let i = 0;
bench("findEntry (match)", () => {
return map.findEntry(0, toRotate[i++ % 3]).generatedColumn;
});
bench("findEntry (no match)", () => {
return map.findEntry(0, 9999).generatedColumn;
});
await run();

View File

@@ -390,12 +390,6 @@ pub fn build(b: *Build) !void {
.{ .os = .windows, .arch = .x86_64 },
}, &.{ .Debug, .ReleaseFast });
}
{
const step = b.step("check-windows-debug", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
.{ .os = .windows, .arch = .x86_64 },
}, &.{.Debug});
}
{
const step = b.step("check-macos", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
@@ -403,13 +397,6 @@ pub fn build(b: *Build) !void {
.{ .os = .mac, .arch = .aarch64 },
}, &.{ .Debug, .ReleaseFast });
}
{
const step = b.step("check-macos-debug", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
.{ .os = .mac, .arch = .x86_64 },
.{ .os = .mac, .arch = .aarch64 },
}, &.{.Debug});
}
{
const step = b.step("check-linux", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
@@ -417,13 +404,6 @@ pub fn build(b: *Build) !void {
.{ .os = .linux, .arch = .aarch64 },
}, &.{ .Debug, .ReleaseFast });
}
{
const step = b.step("check-linux-debug", "Check for semantic analysis errors on Windows");
addMultiCheck(b, step, build_options, &.{
.{ .os = .linux, .arch = .x86_64 },
.{ .os = .linux, .arch = .aarch64 },
}, &.{.Debug});
}
// zig build translate-c-headers
{
@@ -533,8 +513,6 @@ fn getTranslateC(b: *Build, initial_target: std.Build.ResolvedTarget, optimize:
translate_c.defineCMacroRaw(b.fmt("{s}={d}", .{ str, @intFromBool(value) }));
}
translate_c.addIncludePath(b.path("vendor/zstd/lib"));
if (target.result.os.tag == .windows) {
// translate-c is unable to translate the unsuffixed windows functions
// like `SetCurrentDirectory` since they are defined with an odd macro

View File

@@ -4,6 +4,7 @@
"": {
"name": "bun",
"devDependencies": {
"@types/react": "^18.3.3",
"esbuild": "^0.21.4",
"mitata": "^0.1.11",
"peechy": "0.4.34",
@@ -28,17 +29,13 @@
"@types/node": "*",
},
"devDependencies": {
"@types/react": "^19",
"typescript": "^5.0.2",
},
"peerDependencies": {
"@types/react": "^19",
},
},
},
"overrides": {
"@types/bun": "workspace:packages/@types/bun",
"bun-types": "workspace:packages/bun-types",
"@types/bun": "workspace:packages/@types/bun",
},
"packages": {
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
@@ -91,7 +88,9 @@
"@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="],
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
"@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],
"@types/react": ["@types/react@18.3.21", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-gXLBtmlcRJeT09/sI4PxVwyrku6SaNUj/6cMubjE6T6XdY1fDmBL7r0nX0jbSZPU/Xr0KuwLLZh6aOYY5d91Xw=="],
"bun-types": ["bun-types@workspace:packages/bun-types"],

View File

@@ -139,10 +139,10 @@ endif()
optionx(REVISION STRING "The git revision of the build" DEFAULT ${DEFAULT_REVISION})
# Used in process.version, process.versions.node, napi, and elsewhere
optionx(NODEJS_VERSION STRING "The version of Node.js to report" DEFAULT "24.3.0")
optionx(NODEJS_VERSION STRING "The version of Node.js to report" DEFAULT "22.6.0")
# Used in process.versions.modules and compared while loading V8 modules
optionx(NODEJS_ABI_VERSION STRING "The ABI version of Node.js to report" DEFAULT "137")
optionx(NODEJS_ABI_VERSION STRING "The ABI version of Node.js to report" DEFAULT "127")
if(APPLE)
set(DEFAULT_STATIC_SQLITE OFF)

View File

@@ -1,31 +0,0 @@
# This script prepares Node.js headers for use with Bun
# It removes conflicting OpenSSL and libuv headers since Bun uses BoringSSL and its own libuv
if(NOT DEFINED NODE_INCLUDE_DIR)
message(FATAL_ERROR "NODE_INCLUDE_DIR not defined")
endif()
if(NOT EXISTS "${NODE_INCLUDE_DIR}/node")
message(FATAL_ERROR "Node headers not found at ${NODE_INCLUDE_DIR}/node")
endif()
# Remove OpenSSL headers that conflict with BoringSSL
if(EXISTS "${NODE_INCLUDE_DIR}/node/openssl")
file(REMOVE_RECURSE "${NODE_INCLUDE_DIR}/node/openssl")
message(STATUS "Removed conflicting OpenSSL headers")
endif()
# Remove libuv headers that might conflict
if(EXISTS "${NODE_INCLUDE_DIR}/node/uv")
file(REMOVE_RECURSE "${NODE_INCLUDE_DIR}/node/uv")
message(STATUS "Removed conflicting libuv headers")
endif()
if(EXISTS "${NODE_INCLUDE_DIR}/node/uv.h")
file(REMOVE "${NODE_INCLUDE_DIR}/node/uv.h")
message(STATUS "Removed conflicting uv.h header")
endif()
# Add the node directory to include path for cppgc
# This is needed because cppgc internal headers use relative includes
file(WRITE "${NODE_INCLUDE_DIR}/.node-headers-prepared" "1")

View File

@@ -28,7 +28,6 @@ src/bun.js/bindings/BunWorkerGlobalScope.cpp
src/bun.js/bindings/c-bindings.cpp
src/bun.js/bindings/CallSite.cpp
src/bun.js/bindings/CallSitePrototype.cpp
src/bun.js/bindings/CatchScopeBinding.cpp
src/bun.js/bindings/CodeCoverage.cpp
src/bun.js/bindings/ConsoleObject.cpp
src/bun.js/bindings/Cookie.cpp
@@ -100,9 +99,6 @@ src/bun.js/bindings/napi_finalizer.cpp
src/bun.js/bindings/napi_handle_scope.cpp
src/bun.js/bindings/napi_type_tag.cpp
src/bun.js/bindings/napi.cpp
src/bun.js/bindings/NapiClass.cpp
src/bun.js/bindings/NapiRef.cpp
src/bun.js/bindings/NapiWeakValue.cpp
src/bun.js/bindings/ncrpyto_engine.cpp
src/bun.js/bindings/ncrypto.cpp
src/bun.js/bindings/node/crypto/CryptoDhJob.cpp

View File

@@ -7,14 +7,12 @@ src/bun.js/api/h2.classes.ts
src/bun.js/api/html_rewriter.classes.ts
src/bun.js/api/JSBundler.classes.ts
src/bun.js/api/postgres.classes.ts
src/bun.js/api/ResumableSink.classes.ts
src/bun.js/api/S3Client.classes.ts
src/bun.js/api/S3Stat.classes.ts
src/bun.js/api/server.classes.ts
src/bun.js/api/Shell.classes.ts
src/bun.js/api/ShellArgs.classes.ts
src/bun.js/api/sockets.classes.ts
src/bun.js/api/sourcemap.classes.ts
src/bun.js/api/streams.classes.ts
src/bun.js/api/valkey.classes.ts
src/bun.js/api/zlib.classes.ts

View File

@@ -10,26 +10,7 @@ src/allocators/NullableAllocator.zig
src/analytics/analytics_schema.zig
src/analytics/analytics_thread.zig
src/api/schema.zig
src/ast/Ast.zig
src/ast/ASTMemoryAllocator.zig
src/ast/B.zig
src/ast/base.zig
src/ast/Binding.zig
src/ast/BundledAst.zig
src/ast/CharFreq.zig
src/ast/E.zig
src/ast/Expr.zig
src/ast/G.zig
src/ast/Macro.zig
src/ast/NewStore.zig
src/ast/Op.zig
src/ast/S.zig
src/ast/Scope.zig
src/ast/ServerComponentBoundary.zig
src/ast/Stmt.zig
src/ast/Symbol.zig
src/ast/TS.zig
src/ast/UseDirective.zig
src/async/posix_event_loop.zig
src/async/stub_event_loop.zig
src/async/windows_event_loop.zig
@@ -51,11 +32,7 @@ src/bun.js/api/bun/h2_frame_parser.zig
src/bun.js/api/bun/lshpack.zig
src/bun.js/api/bun/process.zig
src/bun.js/api/bun/socket.zig
src/bun.js/api/bun/socket/Handlers.zig
src/bun.js/api/bun/socket/Listener.zig
src/bun.js/api/bun/socket/SocketAddress.zig
src/bun.js/api/bun/socket/tls_socket_functions.zig
src/bun.js/api/bun/socket/WindowsNamedPipeContext.zig
src/bun.js/api/bun/spawn.zig
src/bun.js/api/bun/spawn/stdio.zig
src/bun.js/api/bun/ssl_wrapper.zig
@@ -92,10 +69,6 @@ src/bun.js/api/server/StaticRoute.zig
src/bun.js/api/server/WebSocketServerContext.zig
src/bun.js/api/streams.classes.zig
src/bun.js/api/Timer.zig
src/bun.js/api/Timer/EventLoopTimer.zig
src/bun.js/api/Timer/ImmediateObject.zig
src/bun.js/api/Timer/TimeoutObject.zig
src/bun.js/api/Timer/TimerObjectInternals.zig
src/bun.js/api/TOMLObject.zig
src/bun.js/api/UnsafeObject.zig
src/bun.js/bindgen_test.zig
@@ -104,7 +77,6 @@ src/bun.js/bindings/AnyPromise.zig
src/bun.js/bindings/bun-simdutf.zig
src/bun.js/bindings/CachedBytecode.zig
src/bun.js/bindings/CallFrame.zig
src/bun.js/bindings/CatchScope.zig
src/bun.js/bindings/codegen.zig
src/bun.js/bindings/CommonAbortReason.zig
src/bun.js/bindings/CommonStrings.zig
@@ -227,9 +199,6 @@ src/bun.js/node/util/parse_args_utils.zig
src/bun.js/node/util/parse_args.zig
src/bun.js/node/util/validators.zig
src/bun.js/node/win_watcher.zig
src/bun.js/node/zlib/NativeBrotli.zig
src/bun.js/node/zlib/NativeZlib.zig
src/bun.js/node/zlib/NativeZstd.zig
src/bun.js/ProcessAutoKiller.zig
src/bun.js/rare_data.zig
src/bun.js/ResolveMessage.zig
@@ -271,7 +240,6 @@ src/bun.js/webcore/prompt.zig
src/bun.js/webcore/ReadableStream.zig
src/bun.js/webcore/Request.zig
src/bun.js/webcore/Response.zig
src/bun.js/webcore/ResumableSink.zig
src/bun.js/webcore/S3Client.zig
src/bun.js/webcore/S3File.zig
src/bun.js/webcore/S3Stat.zig
@@ -323,7 +291,6 @@ src/ci_info.zig
src/cli.zig
src/cli/add_command.zig
src/cli/add_completions.zig
src/cli/Arguments.zig
src/cli/audit_command.zig
src/cli/build_command.zig
src/cli/bunx_command.zig
@@ -344,7 +311,6 @@ src/cli/package_manager_command.zig
src/cli/patch_command.zig
src/cli/patch_commit_command.zig
src/cli/pm_trusted_command.zig
src/cli/pm_version_command.zig
src/cli/pm_view_command.zig
src/cli/publish_command.zig
src/cli/remove_command.zig
@@ -524,29 +490,12 @@ src/hive_array.zig
src/hmac.zig
src/HTMLScanner.zig
src/http.zig
src/http/AsyncHTTP.zig
src/http/CertificateInfo.zig
src/http/Decompressor.zig
src/http/Encoding.zig
src/http/FetchRedirect.zig
src/http/HeaderBuilder.zig
src/http/Headers.zig
src/http/HTTPCertError.zig
src/http/HTTPContext.zig
src/http/HTTPRequestBody.zig
src/http/HTTPThread.zig
src/http/InitError.zig
src/http/InternalState.zig
src/http/Method.zig
src/http/MimeType.zig
src/http/ProxyTunnel.zig
src/http/SendFile.zig
src/http/Signals.zig
src/http/ThreadSafeStreamBuffer.zig
src/http/URLPath.zig
src/http/header_builder.zig
src/http/method.zig
src/http/mime_type.zig
src/http/url_path.zig
src/http/websocket_client.zig
src/http/websocket_client/CppWebSocket.zig
src/http/websocket_client/WebSocketDeflate.zig
src/http/websocket_client/WebSocketUpgradeClient.zig
src/http/websocket_http_client.zig
src/http/websocket.zig
@@ -556,17 +505,9 @@ src/import_record.zig
src/ini.zig
src/install/bin.zig
src/install/dependency.zig
src/install/ExternalSlice.zig
src/install/extract_tarball.zig
src/install/hoisted_install.zig
src/install/install_binding.zig
src/install/install.zig
src/install/integrity.zig
src/install/isolated_install.zig
src/install/isolated_install/Hardlinker.zig
src/install/isolated_install/Installer.zig
src/install/isolated_install/Store.zig
src/install/isolated_install/Symlinker.zig
src/install/lifecycle_script_runner.zig
src/install/lockfile.zig
src/install/lockfile/Buffers.zig
@@ -583,28 +524,10 @@ src/install/lockfile/printer/tree_printer.zig
src/install/lockfile/printer/Yarn.zig
src/install/lockfile/Tree.zig
src/install/migration.zig
src/install/NetworkTask.zig
src/install/npm.zig
src/install/PackageInstall.zig
src/install/PackageInstaller.zig
src/install/PackageManager.zig
src/install/PackageManager/CommandLineArguments.zig
src/install/PackageManager/install_with_manager.zig
src/install/PackageManager/PackageJSONEditor.zig
src/install/PackageManager/PackageManagerDirectories.zig
src/install/PackageManager/PackageManagerEnqueue.zig
src/install/PackageManager/PackageManagerLifecycle.zig
src/install/PackageManager/PackageManagerOptions.zig
src/install/PackageManager/PackageManagerResolution.zig
src/install/PackageManager/patchPackage.zig
src/install/PackageManager/processDependencyList.zig
src/install/PackageManager/ProgressStrings.zig
src/install/PackageManager/runTasks.zig
src/install/PackageManager/updatePackageJSONAndInstall.zig
src/install/PackageManager/UpdateRequest.zig
src/install/PackageManager/WorkspacePackageJSONCache.zig
src/install/PackageManagerTask.zig
src/install/PackageManifestMap.zig
src/install/padding_checker.zig
src/install/patch_install.zig
src/install/repository.zig
@@ -616,7 +539,6 @@ src/install/windows-shim/bun_shim_impl.zig
src/io/heap.zig
src/io/io.zig
src/io/MaxBuf.zig
src/io/openForWriting.zig
src/io/PipeReader.zig
src/io/pipes.zig
src/io/PipeWriter.zig
@@ -649,10 +571,6 @@ src/options.zig
src/output.zig
src/OutputFile.zig
src/patch.zig
src/paths.zig
src/paths/EnvPath.zig
src/paths/path_buffer_pool.zig
src/paths/Path.zig
src/perf.zig
src/pool.zig
src/Progress.zig
@@ -691,7 +609,6 @@ src/semver/SemverString.zig
src/semver/SlicedString.zig
src/semver/Version.zig
src/sha.zig
src/shell/AllocScope.zig
src/shell/braces.zig
src/shell/Builtin.zig
src/shell/builtin/basename.zig
@@ -716,118 +633,31 @@ src/shell/builtin/yes.zig
src/shell/EnvMap.zig
src/shell/EnvStr.zig
src/shell/interpreter.zig
src/shell/IO.zig
src/shell/IOReader.zig
src/shell/IOWriter.zig
src/shell/ParsedShellScript.zig
src/shell/RefCountedStr.zig
src/shell/shell.zig
src/shell/states/Assigns.zig
src/shell/states/Async.zig
src/shell/states/Base.zig
src/shell/states/Binary.zig
src/shell/states/Cmd.zig
src/shell/states/CondExpr.zig
src/shell/states/Expansion.zig
src/shell/states/If.zig
src/shell/states/Pipeline.zig
src/shell/states/Script.zig
src/shell/states/Stmt.zig
src/shell/states/Subshell.zig
src/shell/subproc.zig
src/shell/util.zig
src/shell/Yield.zig
src/sourcemap/CodeCoverage.zig
src/sourcemap/JSSourceMap.zig
src/sourcemap/LineOffsetTable.zig
src/sourcemap/sourcemap.zig
src/sourcemap/VLQ.zig
src/sql/DataCell.zig
src/sql/postgres.zig
src/sql/postgres/AnyPostgresError.zig
src/sql/postgres/AuthenticationState.zig
src/sql/postgres/CommandTag.zig
src/sql/postgres/ConnectionFlags.zig
src/sql/postgres/Data.zig
src/sql/postgres/DataCell.zig
src/sql/postgres/DebugSocketMonitorReader.zig
src/sql/postgres/DebugSocketMonitorWriter.zig
src/sql/postgres/ObjectIterator.zig
src/sql/postgres/PostgresCachedStructure.zig
src/sql/postgres/PostgresProtocol.zig
src/sql/postgres/PostgresRequest.zig
src/sql/postgres/PostgresSQLConnection.zig
src/sql/postgres/PostgresSQLContext.zig
src/sql/postgres/PostgresSQLQuery.zig
src/sql/postgres/PostgresSQLQueryResultMode.zig
src/sql/postgres/PostgresSQLStatement.zig
src/sql/postgres/PostgresTypes.zig
src/sql/postgres/protocol/ArrayList.zig
src/sql/postgres/protocol/Authentication.zig
src/sql/postgres/protocol/BackendKeyData.zig
src/sql/postgres/protocol/Close.zig
src/sql/postgres/protocol/ColumnIdentifier.zig
src/sql/postgres/protocol/CommandComplete.zig
src/sql/postgres/protocol/CopyData.zig
src/sql/postgres/protocol/CopyFail.zig
src/sql/postgres/protocol/CopyInResponse.zig
src/sql/postgres/protocol/CopyOutResponse.zig
src/sql/postgres/protocol/DataRow.zig
src/sql/postgres/protocol/DecoderWrap.zig
src/sql/postgres/protocol/Describe.zig
src/sql/postgres/protocol/ErrorResponse.zig
src/sql/postgres/protocol/Execute.zig
src/sql/postgres/protocol/FieldDescription.zig
src/sql/postgres/protocol/FieldMessage.zig
src/sql/postgres/protocol/FieldType.zig
src/sql/postgres/protocol/NegotiateProtocolVersion.zig
src/sql/postgres/protocol/NewReader.zig
src/sql/postgres/protocol/NewWriter.zig
src/sql/postgres/protocol/NoticeResponse.zig
src/sql/postgres/protocol/NotificationResponse.zig
src/sql/postgres/protocol/ParameterDescription.zig
src/sql/postgres/protocol/ParameterStatus.zig
src/sql/postgres/protocol/Parse.zig
src/sql/postgres/protocol/PasswordMessage.zig
src/sql/postgres/protocol/PortalOrPreparedStatement.zig
src/sql/postgres/protocol/ReadyForQuery.zig
src/sql/postgres/protocol/RowDescription.zig
src/sql/postgres/protocol/SASLInitialResponse.zig
src/sql/postgres/protocol/SASLResponse.zig
src/sql/postgres/protocol/StackReader.zig
src/sql/postgres/protocol/StartupMessage.zig
src/sql/postgres/protocol/TransactionStatusIndicator.zig
src/sql/postgres/protocol/WriteWrap.zig
src/sql/postgres/protocol/zHelpers.zig
src/sql/postgres/QueryBindingIterator.zig
src/sql/postgres/SASL.zig
src/sql/postgres/Signature.zig
src/sql/postgres/SocketMonitor.zig
src/sql/postgres/SSLMode.zig
src/sql/postgres/Status.zig
src/sql/postgres/TLSStatus.zig
src/sql/postgres/types/bool.zig
src/sql/postgres/types/bytea.zig
src/sql/postgres/types/date.zig
src/sql/postgres/types/int_types.zig
src/sql/postgres/types/json.zig
src/sql/postgres/types/numeric.zig
src/sql/postgres/types/PostgresString.zig
src/sql/postgres/types/Tag.zig
src/sql/postgres/postgres_protocol.zig
src/sql/postgres/postgres_types.zig
src/StandaloneModuleGraph.zig
src/StaticHashMap.zig
src/string_immutable.zig
src/string_types.zig
src/string.zig
src/string/escapeHTML.zig
src/string/HashedString.zig
src/string/MutableString.zig
src/string/paths.zig
src/string/PathString.zig
src/string/SmolStr.zig
src/string/StringBuilder.zig
src/string/StringJoiner.zig
src/string/unicode.zig
src/string/visible.zig
src/string/WTFStringImpl.zig
src/sync.zig
src/sys_uv.zig

View File

@@ -42,29 +42,6 @@ else()
set(CONFIGURE_DEPENDS "")
endif()
# --- Dependencies ---
set(BUN_DEPENDENCIES
BoringSSL
Brotli
Cares
Highway
LibDeflate
LolHtml
Lshpack
Mimalloc
TinyCC
Zlib
LibArchive # must be loaded after zlib
HdrHistogram # must be loaded after zlib
Zstd
)
include(CloneZstd)
# foreach(dependency ${BUN_DEPENDENCIES})
# include(Clone${dependency})
# endforeach()
# --- Codegen ---
set(BUN_ERROR_SOURCE ${CWD}/packages/bun-error)
@@ -605,7 +582,6 @@ register_command(
${BUN_ZIG_OUTPUT}
TARGETS
clone-zig
clone-zstd
SOURCES
${BUN_ZIG_SOURCES}
${BUN_ZIG_GENERATED_SOURCES}
@@ -650,13 +626,8 @@ register_command(
-DDOWNLOAD_PATH=${NODEJS_HEADERS_PATH}
-DDOWNLOAD_URL=https://nodejs.org/dist/v${NODEJS_VERSION}/node-v${NODEJS_VERSION}-headers.tar.gz
-P ${CWD}/cmake/scripts/DownloadUrl.cmake
COMMAND
${CMAKE_COMMAND}
-DNODE_INCLUDE_DIR=${NODEJS_HEADERS_PATH}/include
-P ${CWD}/cmake/scripts/PrepareNodeHeaders.cmake
OUTPUTS
${NODEJS_HEADERS_PATH}/include/node/node_version.h
${NODEJS_HEADERS_PATH}/include/.node-headers-prepared
)
list(APPEND BUN_CPP_SOURCES
@@ -678,14 +649,20 @@ if(WIN32)
else()
set(Bun_VERSION_WITH_TAG ${VERSION})
endif()
set(BUN_ICO_PATH ${CWD}/src/bun.ico)
configure_file(${CWD}/src/bun.ico ${CODEGEN_PATH}/bun.ico COPYONLY)
set(BUN_ICO_PATH ${CODEGEN_PATH}/bun.ico)
configure_file(
${CWD}/src/windows-app-info.rc
${CODEGEN_PATH}/windows-app-info.rc
@ONLY
)
set(WINDOWS_RESOURCES ${CODEGEN_PATH}/windows-app-info.rc)
add_custom_command(
OUTPUT ${CODEGEN_PATH}/windows-app-info.res
COMMAND rc.exe /fo ${CODEGEN_PATH}/windows-app-info.res ${CODEGEN_PATH}/windows-app-info.rc
DEPENDS ${CODEGEN_PATH}/windows-app-info.rc ${CODEGEN_PATH}/bun.ico
COMMENT "Adding Windows resource file ${CODEGEN_PATH}/windows-app-info.res with ico in ${CODEGEN_PATH}/bun.ico"
)
set(WINDOWS_RESOURCES ${CODEGEN_PATH}/windows-app-info.res)
endif()
# --- Executable ---
@@ -768,7 +745,6 @@ target_include_directories(${bun} PRIVATE
${VENDOR_PATH}
${VENDOR_PATH}/picohttpparser
${NODEJS_HEADERS_PATH}/include
${NODEJS_HEADERS_PATH}/include/node
)
if(NOT WIN32)
@@ -951,22 +927,14 @@ endif()
if(APPLE)
target_link_options(${bun} PUBLIC
-dead_strip
-dead_strip_dylibs
-Wl,-ld_new
-Wl,-no_compact_unwind
-Wl,-stack_size,0x1200000
-fno-keep-static-consts
-Wl,-map,${bun}.linker-map
)
# don't strip in debug, this seems to be needed so that the Zig std library
# `*dbHelper` DWARF symbols (used by LLDB for pretty printing) are in the
# output executable
if(NOT DEBUG)
target_link_options(${bun} PUBLIC
-dead_strip
-dead_strip_dylibs
)
endif()
endif()
if(LINUX)
@@ -1003,6 +971,7 @@ if(LINUX)
-Wl,-no-pie
-Wl,-icf=safe
-Wl,--as-needed
-Wl,--gc-sections
-Wl,-z,stack-size=12800000
-Wl,--compress-debug-sections=zlib
-Wl,-z,lazy
@@ -1018,15 +987,6 @@ if(LINUX)
-Wl,--build-id=sha1 # Better for debugging than default
-Wl,-Map=${bun}.linker-map
)
# don't strip in debug, this seems to be needed so that the Zig std library
# `*dbHelper` DWARF symbols (used by LLDB for pretty printing) are in the
# output executable
if(NOT DEBUG)
target_link_options(${bun} PUBLIC
-Wl,--gc-sections
)
endif()
endif()
# --- Symbols list ---
@@ -1094,6 +1054,22 @@ endif()
# --- Dependencies ---
set(BUN_DEPENDENCIES
BoringSSL
Brotli
Cares
Highway
LibDeflate
LolHtml
Lshpack
Mimalloc
TinyCC
Zlib
LibArchive # must be loaded after zlib
HdrHistogram # must be loaded after zlib
Zstd
)
if(WIN32)
list(APPEND BUN_DEPENDENCIES Libuv)
endif()

View File

@@ -1,3 +1,12 @@
register_repository(
NAME
zstd
REPOSITORY
facebook/zstd
COMMIT
f8745da6ff1ad1e7bab384bd1f9d742439278e99
)
register_cmake_command(
TARGET
zstd
@@ -14,6 +23,4 @@ register_cmake_command(
LIBRARIES
zstd_static WIN32
zstd UNIX
INCLUDES
lib
)

View File

@@ -1,8 +0,0 @@
register_repository(
NAME
zstd
REPOSITORY
facebook/zstd
COMMIT
f8745da6ff1ad1e7bab384bd1f9d742439278e99
)

View File

@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 29bbdff0f94f362891f8e007ae2a73f9bc3e66d3)
set(WEBKIT_VERSION 014cad89f528483e3fc431ff5ca6e2095d92e7bd)
endif()
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)

View File

@@ -261,7 +261,6 @@ _bun_pm_completion() {
'hash-string\:"print the string used to hash the lockfile" '
'hash-print\:"print the hash stored in the current lockfile" '
'cache\:"print the path to the cache folder" '
'version\:"bump the version in package.json and create a git tag" '
)
_alternative "args:cmd3:(($sub_commands))"
@@ -300,40 +299,6 @@ _bun_pm_completion() {
$pmargs &&
ret=0
;;
version)
version_args=(
"patch[increment patch version]"
"minor[increment minor version]"
"major[increment major version]"
"prepatch[increment patch version and add pre-release]"
"preminor[increment minor version and add pre-release]"
"premajor[increment major version and add pre-release]"
"prerelease[increment pre-release version]"
"from-git[use version from latest git tag]"
)
pmargs=(
"--no-git-tag-version[don't create a git commit and tag]"
"--allow-same-version[allow bumping to the same version]"
"-m[use the given message for the commit]:message"
"--message[use the given message for the commit]:message"
"--preid[identifier to prefix pre-release versions]:preid"
)
_arguments -s -C \
'1: :->cmd' \
'2: :->cmd2' \
'3: :->increment' \
$pmargs &&
ret=0
case $state in
increment)
_alternative "args:increment:(($version_args))"
;;
esac
;;
esac
@@ -574,7 +539,6 @@ _bun_update_completion() {
'--save[Save to package.json]' \
'--dry-run[Don'"'"'t install anything]' \
'--frozen-lockfile[Disallow changes to lockfile]' \
'--latest[Updates dependencies to latest version, regardless of compatibility]' \
'-f[Always request the latest versions from the registry & reinstall all dependencies]' \
'--force[Always request the latest versions from the registry & reinstall all dependencies]' \
'--cache-dir[Store & load cached data from a specific directory path]:cache-dir' \

View File

@@ -326,11 +326,7 @@ Bun.serve({
### HTML imports
Bun supports importing HTML files directly into your server code, enabling full-stack applications with both server-side and client-side code. HTML imports work in two modes:
**Development (`bun --hot`):** Assets are bundled on-demand at runtime, enabling hot module replacement (HMR) for a fast, iterative development experience. When you change your frontend code, the browser automatically updates without a full page reload.
**Production (`bun build`):** When building with `bun build --target=bun`, the `import index from "./index.html"` statement resolves to a pre-built manifest object containing all bundled client assets. `Bun.serve` consumes this manifest to serve optimized assets with zero runtime bundling overhead. This is ideal for deploying to production.
To add a client-side single-page app, you can use an HTML import:
```ts
import myReactSinglePageApp from "./index.html";
@@ -342,9 +338,9 @@ Bun.serve({
});
```
HTML imports don't just serve HTML — it's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.sh/docs/bundler), JavaScript transpiler and CSS parser. You can use this to build full-featured frontends with React, TypeScript, Tailwind CSS, and more.
HTML imports don't just serve HTML. It's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.sh/docs/bundler), JavaScript transpiler and CSS parser.
For a complete guide on building full-stack applications with HTML imports, including detailed examples and best practices, see [/docs/bundler/fullstack](https://bun.sh/docs/bundler/fullstack).
You can use this to build a full-featured frontend with React, TypeScript, Tailwind CSS, and more. Check out [/docs/bundler/fullstack](https://bun.sh/docs/bundler/fullstack) to learn more.
### Practical example: REST API

View File

@@ -160,8 +160,7 @@ const writer = s3file.writer({
partSize: 5 * 1024 * 1024,
});
for (let i = 0; i < 10; i++) {
writer.write(bigFile);
await writer.flush();
await writer.write(bigFile);
}
await writer.end();
```

View File

@@ -34,7 +34,7 @@ const proc = Bun.spawn(["cat"], {
),
});
const text = await proc.stdout.text();
const text = await new Response(proc.stdout).text();
console.log(text); // "const input = "hello world".repeat(400); ..."
```
@@ -113,34 +113,14 @@ proc.stdin.flush();
proc.stdin.end();
```
Passing a `ReadableStream` to `stdin` lets you pipe data from a JavaScript `ReadableStream` directly to the subprocess's input:
```ts
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello from ");
controller.enqueue("ReadableStream!");
controller.close();
},
});
const proc = Bun.spawn(["cat"], {
stdin: stream,
stdout: "pipe",
});
const output = await new Response(proc.stdout).text();
console.log(output); // "Hello from ReadableStream!"
```
## Output streams
You can read results from the subprocess via the `stdout` and `stderr` properties. By default these are instances of `ReadableStream`.
```ts
const proc = Bun.spawn(["bun", "--version"]);
const text = await proc.stdout.text();
console.log(text); // => "$BUN_LATEST_VERSION\n"
const text = await new Response(proc.stdout).text();
console.log(text); // => "$BUN_LATEST_VERSION"
```
Configure the output stream by passing one of the following values to `stdout/stderr`:

View File

@@ -582,11 +582,11 @@ Compresses a `Uint8Array` using zlib's DEFLATE algorithm.
const buf = Buffer.from("hello".repeat(100));
const compressed = Bun.deflateSync(buf);
buf; // => Buffer(500)
compressed; // => Uint8Array(12)
buf; // => Uint8Array(25)
compressed; // => Uint8Array(10)
```
The second argument supports the same set of configuration options as [`Bun.gzipSync`](#bun-gzipsync).
The second argument supports the same set of configuration options as [`Bun.gzipSync`](#bungzipsync).
## `Bun.inflateSync()`

View File

@@ -126,81 +126,6 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro
The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time.
## Full-stack executables
{% note %}
New in Bun v1.2.17
{% /note %}
Bun's `--compile` flag can create standalone executables that contain both server and client code, making it ideal for full-stack applications. When you import an HTML file in your server code, Bun automatically bundles all frontend assets (JavaScript, CSS, etc.) and embeds them into the executable. When Bun sees the HTML import on the server, it kicks off a frontend build process to bundle JavaScript, CSS, and other assets.
{% codetabs %}
```ts#server.ts
import { serve } from "bun";
import index from "./index.html";
const server = serve({
routes: {
"/": index,
"/api/hello": { GET: () => Response.json({ message: "Hello from API" }) },
},
});
console.log(`Server running at http://localhost:${server.port}`);
```
```html#index.html
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<h1>Hello World</h1>
<script src="./app.js"></script>
</body>
</html>
```
```js#app.js
console.log("Hello from the client!");
```
```css#styles.css
body {
background-color: #f0f0f0;
}
```
{% /codetabs %}
To build this into a single executable:
```sh
bun build --compile ./server.ts --outfile myapp
```
This creates a self-contained binary that includes:
- Your server code
- The Bun runtime
- All frontend assets (HTML, CSS, JavaScript)
- Any npm packages used by your server
The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run:
```sh
./myapp
```
Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets.
For more details on building full-stack applications with Bun, see the [full-stack guide](/docs/bundler/fullstack).
## Worker
To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments:
@@ -249,7 +174,7 @@ $ ./hello
Standalone executables support embedding files.
To embed files into an executable with `bun build --compile`, import the file in your code.
To embed files into an executable with `bun build --compile`, import the file in your code
```ts
// this becomes an internal file path
@@ -428,4 +353,5 @@ Currently, the `--compile` flag can only accept a single entrypoint at a time an
- `--splitting`
- `--public-path`
- `--target=node` or `--target=browser`
- `--format` - always outputs a binary executable. Internally, it's almost esm.
- `--no-bundle` - we always bundle everything into the executable.

View File

@@ -1,3 +1,5 @@
Using `Bun.serve()`'s `routes` option, you can run your frontend and backend in the same app with no extra steps.
To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`.
```ts
@@ -232,92 +234,7 @@ When `console: true` is set, Bun will stream console logs from the browser to th
#### Production mode
Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible.
##### Ahead of time bundling (recommended)
As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time.
```sh
$ bun build --target=bun --production --outdir=dist ./src/index.ts
```
When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that Bun.serve() can use to serve the assets.
```ts
import { serve } from "bun";
import index from "./index.html";
serve({
routes: { "/": index },
});
```
{% details summary="Internally, the `index` variable is a manifest object that looks something like this" %}
```json
{
"index": "./index.html",
"files": [
{
"input": "index.html",
"path": "./index-f2me3qnf.js",
"loader": "js",
"isEntry": true,
"headers": {
"etag": "eet6gn75",
"content-type": "text/javascript;charset=utf-8"
}
},
{
"input": "index.html",
"path": "./index.html",
"loader": "html",
"isEntry": true,
"headers": {
"etag": "r9njjakd",
"content-type": "text/html;charset=utf-8"
}
},
{
"input": "index.html",
"path": "./index-gysa5fmk.css",
"loader": "css",
"isEntry": true,
"headers": {
"etag": "50zb7x61",
"content-type": "text/css;charset=utf-8"
}
},
{
"input": "logo.svg",
"path": "./logo-kygw735p.svg",
"loader": "file",
"isEntry": false,
"headers": {
"etag": "kygw735p",
"content-type": "application/octet-stream"
}
},
{
"input": "react.svg",
"path": "./react-ck11dneg.svg",
"loader": "file",
"isEntry": false,
"headers": {
"etag": "ck11dneg",
"content-type": "application/octet-stream"
}
}
]
}
```
{% /details %}
##### Runtime bundling
When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`.
When serving your app in production, set `development: false` in `Bun.serve()`.
- Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts.
- Enables `Cache-Control` headers and `ETag` headers
@@ -381,6 +298,7 @@ Note: this is currently in `bunfig.toml` to make it possible to know statically
Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `<script>` and `<link>` tags in HTML files, uses them as entrypoints for [Bun's bundler](/docs/bundler), generates an optimized bundle for the JavaScript/TypeScript/TSX/JSX and CSS files, and serves the result.
1. **`<script>` processing**
- Transpiles TypeScript, JSX, and TSX in `<script>` tags
- Bundles imported dependencies
- Generates sourcemaps for debugging
@@ -391,6 +309,7 @@ Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `<script>` and `<
```
2. **`<link>` processing**
- Processes CSS imports and `<link>` tags
- Concatenates CSS files
- Rewrites `url` and asset paths to include content-addressable hashes in URLs
@@ -400,15 +319,18 @@ Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `<script>` and `<
```
3. **`<img>` & asset processing**
- Links to assets are rewritten to include content-addressable hashes in URLs
- Small assets in CSS files are inlined into `data:` URLs, reducing the total number of HTTP requests sent over the wire
4. **Rewrite HTML**
- Combines all `<script>` tags into a single `<script>` tag with a content-addressable hash in the URL
- Combines all `<link>` tags into a single `<link>` tag with a content-addressable hash in the URL
- Outputs a new HTML file
5. **Serve**
- All the output files from the bundler are exposed as static routes, using the same mechanism internally as when you pass a `Response` object to [`static` in `Bun.serve()`](/docs/api/http#static-routes).
This works similarly to how [`Bun.build` processes HTML files](/docs/bundler/html).

View File

@@ -26,7 +26,6 @@ The bundler is a key piece of infrastructure in the JavaScript ecosystem. As a b
- **Reducing HTTP requests.** A single package in `node_modules` may consist of hundreds of files, and large applications may have dozens of such dependencies. Loading each of these files with a separate HTTP request becomes untenable very quickly, so bundlers are used to convert our application source code into a smaller number of self-contained "bundles" that can be loaded with a single request.
- **Code transforms.** Modern apps are commonly built with languages or tools like TypeScript, JSX, and CSS modules, all of which must be converted into plain JavaScript and CSS before they can be consumed by a browser. The bundler is the natural place to configure these transformations.
- **Framework features.** Frameworks rely on bundler plugins & code transformations to implement common patterns like file-system routing, client-server code co-location (think `getServerSideProps` or Remix loaders), and server components.
- **Full-stack Applications.** Bun's bundler can handle both server and client code in a single command, enabling optimized production builds and single-file executables. With build-time HTML imports, you can bundle your entire application — frontend assets and backend server — into a single deployable unit.
Let's jump into the bundler API.
@@ -325,7 +324,7 @@ Depending on the target, Bun will apply different module resolution rules and op
---
- `bun`
- For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance. This is the target to use for building full-stack applications with build-time HTML imports, where both server and client code are bundled together.
- For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance.
All bundles generated with `target: "bun"` are marked with a special `// @bun` pragma, which indicates to the Bun runtime that there's no need to re-transpile the file before execution.

View File

@@ -137,7 +137,7 @@ console.log(contents); // => "Hello, world!"
import html from "./index.html" with { type: "text" };
```
When referenced during a build, the contents are inlined into the bundle as a string.
When referenced during a build, the contents are into the bundle as a string.
```ts
var contents = `Hello, world!`;
@@ -262,20 +262,6 @@ Currently, the list of selectors is:
- `video[poster]`
- `video[src]`
{% callout %}
**HTML Loader Behavior in Different Contexts**
The `html` loader behaves differently depending on how it's used:
1. **Static Build:** When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed.
2. **Runtime:** When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement.
3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production.
{% /callout %}
### `sh` loader
**Bun Shell loader**. Default for `.sh` files

View File

@@ -125,7 +125,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
- `--target`
- n/a
- Not supported. Bun's bundler performs no syntactic down-leveling at this time.
- No supported. Bun's bundler performs no syntactic down-leveling at this time.
---

View File

@@ -308,12 +308,14 @@ IF remote template
1. GET `registry.npmjs.org/@bun-examples/${template}/latest` and parse it
2. GET `registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz`
3. Decompress & extract `${template}-${latestVersion}.tgz` into `${destination}`
- If there are files that would overwrite, warn and exit unless `--force` is passed
IF GitHub repo
1. Download the tarball from GitHubs API
2. Decompress & extract into `${destination}`
- If there are files that would overwrite, warn and exit unless `--force` is passed
ELSE IF local template
@@ -331,6 +333,7 @@ ELSE IF local template
7. Run `${npmClient} install` unless `--no-install` is passed OR no dependencies are in package.json
8. Run any tasks defined in `"bun-create": { "postinstall" }` with the npm client
9. Run `git init; git add -A .; git commit -am "Initial Commit";`
- Rename `gitignore` to `.gitignore`. NPM automatically removes `.gitignore` files from appearing in packages.
- If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed
- Using libgit2 if available was tested and performed 3x slower in microbenchmarks

View File

@@ -28,7 +28,7 @@ $ bun install --filter '!pkg-c'
$ bun install --filter './packages/*'
# Save as above, but exclude the root package.json
$ bun install --filter '!./' --filter './packages/*'
$ bun install --filter --filter '!./' --filter './packages/*'
```
Similarly, `bun outdated` will display outdated dependencies for all packages in the monorepo, and `--filter` can be used to restrict the command to a subset of the packages:

View File

@@ -1,65 +0,0 @@
`bun info` displays package metadata from the npm registry.
## Usage
```bash
$ bun info react
```
This will display information about the `react` package, including its latest version, description, homepage, dependencies, and more.
## Viewing specific versions
To view information about a specific version:
```bash
$ bun info react@18.0.0
```
## Viewing specific properties
You can also query specific properties from the package metadata:
```bash
$ bun info react version
$ bun info react dependencies
$ bun info react repository.url
```
## JSON output
To get the output in JSON format, use the `--json` flag:
```bash
$ bun info react --json
```
## Alias
`bun pm view` is an alias for `bun info`:
```bash
$ bun pm view react # equivalent to: bun info react
```
## Examples
```bash
# View basic package information
$ bun info is-number
# View a specific version
$ bun info is-number@7.0.0
# View all available versions
$ bun info is-number versions
# View package dependencies
$ bun info express dependencies
# View package homepage
$ bun info lodash homepage
# Get JSON output
$ bun info react --json
```

View File

@@ -151,45 +151,3 @@ $ bun pm default-trusted
```
see the current list on GitHub [here](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt)
## version
To display current package version and help:
```bash
$ bun pm version
bun pm version v$BUN_LATEST_VERSION (ca7428e9)
Current package version: v1.0.0
Increment:
patch 1.0.0 → 1.0.1
minor 1.0.0 → 1.1.0
major 1.0.0 → 2.0.0
prerelease 1.0.0 → 1.0.1-0
prepatch 1.0.0 → 1.0.1-0
preminor 1.0.0 → 1.1.0-0
premajor 1.0.0 → 2.0.0-0
from-git Use version from latest git tag
1.2.3 Set specific version
Options:
--no-git-tag-version Skip git operations
--allow-same-version Prevents throwing error if version is the same
--message=<val>, -m Custom commit message, use %s for version substitution
--preid=<val> Prerelease identifier (i.e beta → 1.0.1-beta.0)
--force, -f Bypass dirty git history check
Examples:
$ bun pm version patch
$ bun pm version 1.2.3 --no-git-tag-version
$ bun pm version prerelease --preid beta --message "Release beta: %s"
```
To bump the version in `package.json`:
```bash
$ bun pm version patch
v1.0.1
```
Supports `patch`, `minor`, `major`, `premajor`, `preminor`, `prepatch`, `prerelease`, `from-git`, or specific versions like `1.2.3`. By default creates git commit and tag unless `--no-git-tag-version` was used to skip.

View File

@@ -30,7 +30,7 @@ $ bun add @prisma/client
We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. For simplicity we'll be using an in-memory SQLite database.
```bash
$ bunx --bun prisma init --datasource-provider sqlite
$ bunx prisma init --datasource-provider sqlite
```
---

View File

@@ -13,14 +13,14 @@ proc.stderr; // => ReadableStream
---
To read `stderr` until the child process exits, use .text()
To read `stderr` until the child process exits, use the [`Bun.readableStreamToText()`](https://bun.sh/docs/api/utils#bun-readablestreamto) convenience function.
```ts
const proc = Bun.spawn(["echo", "hello"], {
stderr: "pipe",
});
const errors: string = await proc.stderr.text();
const errors: string = await Bun.readableStreamToText(proc.stderr);
if (errors) {
// handle errors
}

View File

@@ -7,7 +7,7 @@ When using [`Bun.spawn()`](https://bun.sh/docs/api/spawn), the `stdout` of the c
```ts
const proc = Bun.spawn(["echo", "hello"]);
const output = await proc.stdout.text();
const output = await new Response(proc.stdout).text();
output; // => "hello"
```

View File

@@ -32,8 +32,8 @@ By default, the `stdout` of the child process can be consumed as a `ReadableStre
```ts
const proc = Bun.spawn(["echo", "hello"]);
const output = await proc.stdout.text();
output; // => "hello\n"
const output = await new Response(proc.stdout).text();
output; // => "hello"
```
---

View File

@@ -46,7 +46,7 @@ test
The `snap.test.ts.snap` file is a JavaScript file that exports a serialized version of the value passed into `expect()`. The `{foo: "bar"}` object has been serialized to JSON.
```js
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
// Bun Snapshot v1, https://goo.gl/fbAQLP
exports[`snapshot 1`] = `
{

View File

@@ -353,8 +353,6 @@ dryRun = false
To configure the directory where Bun puts globally installed packages.
Environment variable: `BUN_INSTALL_GLOBAL_DIR`
```toml
[install]
# where `bun install --global` installs packages
@@ -365,8 +363,6 @@ globalDir = "~/.bun/install/global"
To configure the directory where Bun installs globally installed binaries and CLIs.
Environment variable: `BUN_INSTALL_BIN`
```toml
# where globally-installed package bins are linked
globalBinDir = "~/.bun/bin"

View File

@@ -206,11 +206,13 @@ Understanding how `mock.module()` works helps you use it more effectively:
2. **Lazy Evaluation**: The mock factory callback is only evaluated when the module is actually imported or required.
3. **Path Resolution**: Bun automatically resolves the module specifier as though you were doing an import, supporting:
- Relative paths (`'./module'`)
- Absolute paths (`'/path/to/module'`)
- Package names (`'lodash'`)
4. **Import Timing Effects**:
- When mocking before first import: No side effects from the original module occur
- When mocking after import: The original module's side effects have already happened
- For this reason, using `--preload` is recommended for mocks that need to prevent side effects

View File

@@ -12,7 +12,7 @@ const C = bun.C;
const clap = @import("../src/deps/zig-clap/clap.zig");
const URL = @import("../src/url.zig").URL;
const Method = @import("../src/http/Method.zig").Method;
const Method = @import("../src/http/method.zig").Method;
const ColonListType = @import("../src/cli/colon_list_type.zig").ColonListType;
const HeadersTuple = ColonListType(string, noop_resolver);
const path_handler = @import("../src/resolver/resolve_path.zig");

View File

@@ -14,7 +14,7 @@ const clap = @import("../src/deps/zig-clap/clap.zig");
const URL = @import("../src/url.zig").URL;
const Headers = bun.http.Headers;
const Method = @import("../src/http/Method.zig").Method;
const Method = @import("../src/http/method.zig").Method;
const ColonListType = @import("../src/cli/colon_list_type.zig").ColonListType;
const HeadersTuple = ColonListType(string, noop_resolver);
const path_handler = @import("../src/resolver/resolve_path.zig");

View File

@@ -1,12 +1,13 @@
{
"private": true,
"name": "bun",
"version": "1.2.19",
"version": "1.2.17",
"workspaces": [
"./packages/bun-types",
"./packages/@types/bun"
],
"devDependencies": {
"@types/react": "^18.3.3",
"esbuild": "^0.21.4",
"mitata": "^0.1.11",
"peechy": "0.4.34",
@@ -23,9 +24,8 @@
},
"scripts": {
"build": "bun run build:debug",
"ci": "bun scripts/buildkite-failures.ts ",
"watch": "bun run zig build check --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib",
"watch-windows": "bun run zig build check-windows --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib",
"watch": "zig build check --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib -Doverride-no-export-cpp-apis=true",
"watch-windows": "zig build check-windows --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib",
"bd:v": "(bun run --silent build:debug &> /tmp/bun.debug.build.log || (cat /tmp/bun.debug.build.log && rm -rf /tmp/bun.debug.build.log && exit 1)) && rm -f /tmp/bun.debug.build.log && ./build/debug/bun-debug",
"bd": "BUN_DEBUG_QUIET_LOGS=1 bun bd:v",
"build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
@@ -74,7 +74,7 @@
"clang-tidy:diff": "bun run analysis --target clang-tidy-diff",
"zig-format": "bun run analysis:no-llvm --target zig-format",
"zig-format:check": "bun run analysis:no-llvm --target zig-format-check",
"prettier": "bunx --bun prettier@latest --plugin=prettier-plugin-organize-imports --config .prettierrc --write scripts packages src docs 'test/**/*.{test,spec}.{ts,tsx,js,jsx,mts,mjs,cjs,cts}' '!test/**/*fixture*.*'",
"prettier": "bunx prettier@latest --plugin=prettier-plugin-organize-imports --config .prettierrc --write scripts packages src docs 'test/**/*.{test,spec}.{ts,tsx,js,jsx,mts,mjs,cjs,cts}' '!test/**/*fixture*.*'",
"node:test": "node ./scripts/runner.node.mjs --quiet --exec-path=$npm_execpath --node-tests ",
"node:test:cp": "bun ./scripts/fetch-node-test.ts ",
"clean:zig": "rm -rf build/debug/cache/zig build/debug/CMakeCache.txt 'build/debug/*.o' .zig-cache zig-out || true",

View File

@@ -850,7 +850,7 @@ const Summary = ({ errorCount, onClose }: { errorCount: number; onClose: () => v
<a href="https://bun.sh/discord" target="_blank" className="BunError-Summary-help">
<svg width="18" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0)">
<g clip-path="url(#clip0)">
<path
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
fill="#5865F2"

View File

@@ -1,4 +1,4 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
// Bun Snapshot v1, https://goo.gl/fbAQLP
exports[`runTests() can run all tests 1`] = `
{

View File

@@ -124,7 +124,7 @@ const argv0 = argv0_stdout.toString().trim();
console.log(`Testing ${argv0} v${revision}`);
const ntStatusPath = "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.26100.0\\shared\\ntstatus.h";
const ntStatusPath = "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.22621.0\\shared\\ntstatus.h";
let ntstatus_header_cache = null;
function lookupWindowsError(code) {
if (ntstatus_header_cache === null) {

View File

@@ -1,4 +1,4 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
// Bun Snapshot v1, https://goo.gl/fbAQLP
exports[`Bun.plugin using { forceSide: 'server' } allows for imported components to be SSR'd: foo.svelte - head 1`] = `""`;

View File

@@ -72,7 +72,6 @@ async function buildRootModule(dryRun?: boolean) {
},
});
write(join(cwd, "bin", "bun.exe"), "");
write(join(cwd, "bin", "bunx.exe"), "");
write(
join(cwd, "bin", "README.txt"),
`The 'bun.exe' file is a placeholder for the binary file, which
@@ -106,7 +105,7 @@ without *requiring* a postinstall script.
),
bin: {
bun: "bin/bun.exe",
bunx: "bin/bunx.exe",
bunx: "bin/bun.exe",
},
os,
cpu,

View File

@@ -157,15 +157,3 @@ export function exists(path: string): boolean {
}
return false;
}
export function link(path: string, newPath: string): void {
debug("link", path, newPath);
try {
fs.unlinkSync(newPath);
fs.linkSync(path, newPath);
return;
} catch (error) {
copy(path, newPath);
debug("fs.linkSync failed, reverting to copy", error);
}
}

View File

@@ -1,7 +1,7 @@
import { unzipSync } from "zlib";
import { debug, error } from "../console";
import { fetch } from "../fetch";
import { chmod, join, link, rename, rm, tmp, write } from "../fs";
import { chmod, join, rename, rm, tmp, write } from "../fs";
import type { Platform } from "../platform";
import { abi, arch, os, supportedPlatforms } from "../platform";
import { spawn } from "../spawn";
@@ -125,7 +125,6 @@ export function optimizeBun(path: string): void {
os === "win32" ? 'powershell -c "irm bun.sh/install.ps1 | iex"' : "curl -fsSL https://bun.sh/install | bash";
try {
rename(path, join(__dirname, "bin", "bun.exe"));
link(join(__dirname, "bin", "bun.exe"), join(__dirname, "bin", "bunx.exe"));
return;
} catch (error) {
debug("optimizeBun failed", error);

View File

@@ -65,11 +65,13 @@ Note: The order of references in `index.d.ts` is important - `bun.ns.d.ts` must
### Best Practices
1. **Type Safety**
- Please use strict types instead of `any` where possible
- Leverage TypeScript's type system features (generics, unions, etc.)
- Document complex types with JSDoc comments
2. **Compatibility**
- Use `Bun.__internal.UseLibDomIfAvailable<LibDomName extends string, OurType>` for types that might conflict with lib.dom.d.ts (see [`./fetch.d.ts`](./fetch.d.ts) for a real example)
- `@types/node` often expects variables to always be defined (this was the biggest cause of most of the conflicts in the past!), so we use the `UseLibDomIfAvailable` type to make sure we don't overwrite `lib.dom.d.ts` but still provide Bun types while simultaneously declaring the variable exists (for Node to work) in the cases that we can.

View File

@@ -45,7 +45,6 @@ declare module "bun" {
type DOMHighResTimeStamp = number;
type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
type BlobOrStringOrBuffer = string | NodeJS.TypedArray | ArrayBufferLike | Blob;
type MaybePromise<T> = T | Promise<T>;
namespace __internal {
type LibDomIsLoaded = typeof globalThis extends { onabort: any } ? true : false;
@@ -853,8 +852,6 @@ declare module "bun" {
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks or the concatenated chunks as a {@link Uint8Array}.
*
* @deprecated Use {@link ReadableStream.bytes}
*/
function readableStreamToBytes(
stream: ReadableStream<ArrayBufferView | ArrayBufferLike>,
@@ -867,8 +864,6 @@ declare module "bun" {
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link Blob}.
*
* @deprecated Use {@link ReadableStream.blob}
*/
function readableStreamToBlob(stream: ReadableStream): Promise<Blob>;
@@ -911,8 +906,6 @@ declare module "bun" {
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.text}
*/
function readableStreamToText(stream: ReadableStream): Promise<string>;
@@ -923,8 +916,6 @@ declare module "bun" {
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.json}
*/
function readableStreamToJSON(stream: ReadableStream): Promise<any>;
@@ -1251,9 +1242,9 @@ declare module "bun" {
*/
writer(options?: { highWaterMark?: number }): FileSink;
// TODO
// readonly readable: ReadableStream<Uint8Array>;
// readonly writable: WritableStream<Uint8Array>;
readonly readable: ReadableStream;
// TODO: writable: WritableStream;
/**
* A UNIX timestamp indicating when the file was last modified.
@@ -1312,285 +1303,116 @@ declare module "bun" {
stat(): Promise<import("node:fs").Stats>;
}
namespace SQL {
type AwaitPromisesArray<T extends Array<PromiseLike<any>>> = {
[K in keyof T]: Awaited<T[K]>;
};
/**
* Configuration options for SQL client connection and behavior
* @example
* const config: SQLOptions = {
* host: 'localhost',
* port: 5432,
* user: 'dbuser',
* password: 'secretpass',
* database: 'myapp',
* idleTimeout: 30,
* max: 20,
* onconnect: (client) => {
* console.log('Connected to database');
* }
* };
*/
type ContextCallbackResult<T> = T extends Array<PromiseLike<any>> ? AwaitPromisesArray<T> : Awaited<T>;
type ContextCallback<T, SQL> = (sql: SQL) => Promise<T>;
/**
* Configuration options for SQL client connection and behavior
*
* @example
* ```ts
* const config: Bun.SQL.Options = {
* host: 'localhost',
* port: 5432,
* user: 'dbuser',
* password: 'secretpass',
* database: 'myapp',
* idleTimeout: 30,
* max: 20,
* onconnect: (client) => {
* console.log('Connected to database');
* }
* };
* ```
*/
interface Options {
/**
* Connection URL (can be string or URL object)
*/
url?: URL | string | undefined;
/**
* Database server hostname
* @default "localhost"
*/
host?: string | undefined;
/**
* Database server hostname (alias for host)
* @deprecated Prefer {@link host}
* @default "localhost"
*/
hostname?: string | undefined;
/**
* Database server port number
* @default 5432
*/
port?: number | string | undefined;
/**
* Database user for authentication
* @default "postgres"
*/
username?: string | undefined;
/**
* Database user for authentication (alias for username)
* @deprecated Prefer {@link username}
* @default "postgres"
*/
user?: string | undefined;
/**
* Database password for authentication
* @default ""
*/
password?: string | (() => MaybePromise<string>) | undefined;
/**
* Database password for authentication (alias for password)
* @deprecated Prefer {@link password}
* @default ""
*/
pass?: string | (() => MaybePromise<string>) | undefined;
/**
* Name of the database to connect to
* @default The username value
*/
database?: string | undefined;
/**
* Name of the database to connect to (alias for database)
* @deprecated Prefer {@link database}
* @default The username value
*/
db?: string | undefined;
/**
* Database adapter/driver to use
* @default "postgres"
*/
adapter?: "postgres" /*| "sqlite" | "mysql"*/ | (string & {}) | undefined;
/**
* Maximum time in seconds to wait for connection to become available
* @default 0 (no timeout)
*/
idleTimeout?: number | undefined;
/**
* Maximum time in seconds to wait for connection to become available (alias for idleTimeout)
* @deprecated Prefer {@link idleTimeout}
* @default 0 (no timeout)
*/
idle_timeout?: number | undefined;
/**
* Maximum time in seconds to wait when establishing a connection
* @default 30
*/
connectionTimeout?: number | undefined;
/**
* Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout)
* @deprecated Prefer {@link connectionTimeout}
* @default 30
*/
connection_timeout?: number | undefined;
/**
* Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout)
* @deprecated Prefer {@link connectionTimeout}
* @default 30
*/
connectTimeout?: number | undefined;
/**
* Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout)
* @deprecated Prefer {@link connectionTimeout}
* @default 30
*/
connect_timeout?: number | undefined;
/**
* Maximum lifetime in seconds of a connection
* @default 0 (no maximum lifetime)
*/
maxLifetime?: number | undefined;
/**
* Maximum lifetime in seconds of a connection (alias for maxLifetime)
* @deprecated Prefer {@link maxLifetime}
* @default 0 (no maximum lifetime)
*/
max_lifetime?: number | undefined;
/**
* Whether to use TLS/SSL for the connection
* @default false
*/
tls?: TLSOptions | boolean | undefined;
/**
* Whether to use TLS/SSL for the connection (alias for tls)
* @default false
*/
ssl?: TLSOptions | boolean | undefined;
// `.path` is currently unsupported in Bun, the implementation is incomplete.
//
// /**
// * Unix domain socket path for connection
// * @default ""
// */
// path?: string | undefined;
/**
* Callback function executed when a connection is established
*/
onconnect?: ((client: SQL) => void) | undefined;
/**
* Callback function executed when a connection is closed
*/
onclose?: ((client: SQL) => void) | undefined;
/**
* Postgres client runtime configuration options
*
* @see https://www.postgresql.org/docs/current/runtime-config-client.html
*/
connection?: Record<string, string | boolean | number> | undefined;
/**
* Maximum number of connections in the pool
* @default 10
*/
max?: number | undefined;
/**
* By default values outside i32 range are returned as strings. If this is true, values outside i32 range are returned as BigInts.
* @default false
*/
bigint?: boolean | undefined;
/**
* Automatic creation of prepared statements
* @default true
*/
prepare?: boolean | undefined;
}
/**
* Represents a SQL query that can be executed, with additional control methods
* Extends Promise to allow for async/await usage
*/
interface Query<T> extends Promise<T> {
/**
* Indicates if the query is currently executing
*/
active: boolean;
/**
* Indicates if the query has been cancelled
*/
cancelled: boolean;
/**
* Cancels the executing query
*/
cancel(): Query<T>;
/**
* Executes the query as a simple query, no parameters are allowed but can execute multiple commands separated by semicolons
*/
simple(): Query<T>;
/**
* Executes the query
*/
execute(): Query<T>;
/**
* Returns the raw query result
*/
raw(): Query<T>;
/**
* Returns only the values from the query result
*/
values(): Query<T>;
}
/**
* Callback function type for transaction contexts
* @param sql Function to execute SQL queries within the transaction
*/
type TransactionContextCallback<T> = ContextCallback<T, TransactionSQL>;
/**
* Callback function type for savepoint contexts
* @param sql Function to execute SQL queries within the savepoint
*/
type SavepointContextCallback<T> = ContextCallback<T, SavepointSQL>;
/**
* SQL.Helper represents a parameter or serializable
* value inside of a query.
*
* @example
* ```ts
* const helper = sql(users, 'id');
* await sql`insert into users ${helper}`;
* ```
*/
interface Helper<T> {
readonly value: T[];
readonly columns: (keyof T)[];
}
interface SQLOptions {
/** Connection URL (can be string or URL object) */
url?: URL | string;
/** Database server hostname */
host?: string;
/** Database server hostname (alias for host) */
hostname?: string;
/** Database server port number */
port?: number | string;
/** Database user for authentication */
username?: string;
/** Database user for authentication (alias for username) */
user?: string;
/** Database password for authentication */
password?: string | (() => Promise<string>);
/** Database password for authentication (alias for password) */
pass?: string | (() => Promise<string>);
/** Name of the database to connect to */
database?: string;
/** Name of the database to connect to (alias for database) */
db?: string;
/** Database adapter/driver to use */
adapter?: string;
/** Maximum time in seconds to wait for connection to become available */
idleTimeout?: number;
/** Maximum time in seconds to wait for connection to become available (alias for idleTimeout) */
idle_timeout?: number;
/** Maximum time in seconds to wait when establishing a connection */
connectionTimeout?: number;
/** Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout) */
connection_timeout?: number;
/** Maximum lifetime in seconds of a connection */
maxLifetime?: number;
/** Maximum lifetime in seconds of a connection (alias for maxLifetime) */
max_lifetime?: number;
/** Whether to use TLS/SSL for the connection */
tls?: TLSOptions | boolean;
/** Whether to use TLS/SSL for the connection (alias for tls) */
ssl?: TLSOptions | boolean;
/** Callback function executed when a connection is established */
onconnect?: (client: SQL) => void;
/** Callback function executed when a connection is closed */
onclose?: (client: SQL) => void;
/** Maximum number of connections in the pool */
max?: number;
/** By default values outside i32 range are returned as strings. If this is true, values outside i32 range are returned as BigInts. */
bigint?: boolean;
/** Automatic creation of prepared statements, defaults to true */
prepare?: boolean;
}
/**
* Represents a SQL query that can be executed, with additional control methods
* Extends Promise to allow for async/await usage
*/
interface SQLQuery<T = any> extends Promise<T> {
/** Indicates if the query is currently executing */
active: boolean;
/** Indicates if the query has been cancelled */
cancelled: boolean;
/** Cancels the executing query */
cancel(): SQLQuery<T>;
/** Execute as a simple query, no parameters are allowed but can execute multiple commands separated by semicolons */
simple(): SQLQuery<T>;
/** Executes the query */
execute(): SQLQuery<T>;
/** Returns the raw query result */
raw(): SQLQuery<T>;
/** Returns only the values from the query result */
values(): SQLQuery<T>;
}
/**
* Callback function type for transaction contexts
* @param sql Function to execute SQL queries within the transaction
*/
type SQLTransactionContextCallback = (sql: TransactionSQL) => Promise<any> | Array<SQLQuery>;
/**
* Callback function type for savepoint contexts
* @param sql Function to execute SQL queries within the savepoint
*/
type SQLSavepointContextCallback = (sql: SavepointSQL) => Promise<any> | Array<SQLQuery>;
/**
* Main SQL client interface providing connection and transaction management
*/
interface SQL extends AsyncDisposable {
interface SQL {
/**
* Executes a SQL query using template literals
* @example
@@ -1598,12 +1420,7 @@ declare module "bun" {
* const [user] = await sql`select * from users where id = ${1}`;
* ```
*/
<T = any>(strings: TemplateStringsArray, ...values: unknown[]): SQL.Query<T>;
/**
* Execute a SQL query using a string
*/
<T = any>(string: string): SQL.Query<T>;
(strings: string[] | TemplateStringsArray, ...values: any[]): SQLQuery;
/**
* Helper function for inserting an object into a query
@@ -1611,19 +1428,16 @@ declare module "bun" {
* @example
* ```ts
* // Insert an object
* const result = await sql`insert into users ${sql(users)} returning *`;
* const result = await sql`insert into users ${sql(users)} RETURNING *`;
*
* // Or pick specific columns
* const result = await sql`insert into users ${sql(users, "id", "name")} returning *`;
* const result = await sql`insert into users ${sql(users, "id", "name")} RETURNING *`;
*
* // Or a single object
* const result = await sql`insert into users ${sql(user)} returning *`;
* const result = await sql`insert into users ${sql(user)} RETURNING *`;
* ```
*/
<T extends { [Key in PropertyKey]: unknown }, Keys extends keyof T = keyof T>(
obj: T | T[] | readonly T[],
...columns: readonly Keys[]
): SQL.Helper<Pick<T, Keys>>;
<T extends { [Key in PropertyKey]: unknown }>(obj: T | T[] | readonly T[], ...columns: (keyof T)[]): SQLQuery;
/**
* Helper function for inserting any serializable value into a query
@@ -1633,7 +1447,7 @@ declare module "bun" {
* const result = await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;
* ```
*/
<T>(value: T): SQL.Helper<T>;
(obj: unknown): SQLQuery;
/**
* Commits a distributed transaction also know as prepared transaction in postgres or XA transaction in MySQL
@@ -1705,7 +1519,6 @@ declare module "bun" {
/**
* The reserve method pulls out a connection from the pool, and returns a client that wraps the single connection.
*
* This can be used for running queries on an isolated connection.
* Calling reserve in a reserved Sql will return a new reserved connection, not the same connection (behavior matches postgres package).
*
@@ -1731,10 +1544,7 @@ declare module "bun" {
* ```
*/
reserve(): Promise<ReservedSQL>;
/**
* Begins a new transaction.
*
/** Begins a new transaction
* Will reserve a connection for the transaction and supply a scoped sql instance for all transaction uses in the callback function. sql.begin will resolve with the returned value from the callback function.
* BEGIN is automatically sent with the optional options, and if anything fails ROLLBACK will be called so the connection can be released and execution can continue.
* @example
@@ -1758,11 +1568,8 @@ declare module "bun" {
* return [user, account]
* })
*/
begin<const T>(fn: SQL.TransactionContextCallback<T>): Promise<SQL.ContextCallbackResult<T>>;
/**
* Begins a new transaction with options.
*
begin(fn: SQLTransactionContextCallback): Promise<any>;
/** Begins a new transaction with options
* Will reserve a connection for the transaction and supply a scoped sql instance for all transaction uses in the callback function. sql.begin will resolve with the returned value from the callback function.
* BEGIN is automatically sent with the optional options, and if anything fails ROLLBACK will be called so the connection can be released and execution can continue.
* @example
@@ -1786,11 +1593,8 @@ declare module "bun" {
* return [user, account]
* })
*/
begin<const T>(options: string, fn: SQL.TransactionContextCallback<T>): Promise<SQL.ContextCallbackResult<T>>;
/**
* Alternative method to begin a transaction.
*
begin(options: string, fn: SQLTransactionContextCallback): Promise<any>;
/** Alternative method to begin a transaction
* Will reserve a connection for the transaction and supply a scoped sql instance for all transaction uses in the callback function. sql.transaction will resolve with the returned value from the callback function.
* BEGIN is automatically sent with the optional options, and if anything fails ROLLBACK will be called so the connection can be released and execution can continue.
* @alias begin
@@ -1815,15 +1619,11 @@ declare module "bun" {
* return [user, account]
* })
*/
transaction<const T>(fn: SQL.TransactionContextCallback<T>): Promise<SQL.ContextCallbackResult<T>>;
/**
* Alternative method to begin a transaction with options
transaction(fn: SQLTransactionContextCallback): Promise<any>;
/** Alternative method to begin a transaction with options
* Will reserve a connection for the transaction and supply a scoped sql instance for all transaction uses in the callback function. sql.transaction will resolve with the returned value from the callback function.
* BEGIN is automatically sent with the optional options, and if anything fails ROLLBACK will be called so the connection can be released and execution can continue.
*
* @alias {@link begin}
*
* @alias begin
* @example
* const [user, account] = await sql.transaction("read write", async sql => {
* const [user] = await sql`
@@ -1843,18 +1643,15 @@ declare module "bun" {
* returning *
* `
* return [user, account]
* });
* })
*/
transaction<const T>(options: string, fn: SQL.TransactionContextCallback<T>): Promise<SQL.ContextCallbackResult<T>>;
/**
* Begins a distributed transaction
transaction(options: string, fn: SQLTransactionContextCallback): Promise<any>;
/** Begins a distributed transaction
* Also know as Two-Phase Commit, in a distributed transaction, Phase 1 involves the coordinator preparing nodes by ensuring data is written and ready to commit, while Phase 2 finalizes with nodes committing or rolling back based on the coordinator's decision, ensuring durability and releasing locks.
* In PostgreSQL and MySQL distributed transactions persist beyond the original session, allowing privileged users or coordinators to commit/rollback them, ensuring support for distributed transactions, recovery, and administrative tasks.
* beginDistributed will automatic rollback if any exception are not caught, and you can commit and rollback later if everything goes well.
* PostgreSQL natively supports distributed transactions using PREPARE TRANSACTION, while MySQL uses XA Transactions, and MSSQL also supports distributed/XA transactions. However, in MSSQL, distributed transactions are tied to the original session, the DTC coordinator, and the specific connection.
* These transactions are automatically committed or rolled back following the same rules as regular transactions, with no option for manual intervention from other sessions, in MSSQL distributed transactions are used to coordinate transactions using Linked Servers.
*
* @example
* await sql.beginDistributed("numbers", async sql => {
* await sql`create table if not exists numbers (a int)`;
@@ -1864,38 +1661,31 @@ declare module "bun" {
* await sql.commitDistributed("numbers");
* // or await sql.rollbackDistributed("numbers");
*/
beginDistributed<const T>(
name: string,
fn: SQL.TransactionContextCallback<T>,
): Promise<SQL.ContextCallbackResult<T>>;
beginDistributed(name: string, fn: SQLTransactionContextCallback): Promise<any>;
/** Alternative method to begin a distributed transaction
* @alias {@link beginDistributed}
* @alias beginDistributed
*/
distributed<const T>(name: string, fn: SQL.TransactionContextCallback<T>): Promise<SQL.ContextCallbackResult<T>>;
distributed(name: string, fn: SQLTransactionContextCallback): Promise<any>;
/**If you know what you're doing, you can use unsafe to pass any string you'd like.
* Please note that this can lead to SQL injection if you're not careful.
* You can also nest sql.unsafe within a safe sql expression. This is useful if only part of your fraction has unsafe elements.
* @example
* const result = await sql.unsafe(`select ${danger} from users where id = ${dragons}`)
*/
unsafe<T = any>(string: string, values?: any[]): SQL.Query<T>;
unsafe(string: string, values?: any[]): SQLQuery;
/**
* Reads a file and uses the contents as a query.
* Optional parameters can be used if the file includes $1, $2, etc
* @example
* const result = await sql.file("query.sql", [1, 2, 3]);
*/
file<T = any>(filename: string, values?: any[]): SQL.Query<T>;
file(filename: string, values?: any[]): SQLQuery;
/**
* Current client options
*/
options: SQL.Options;
/** Current client options */
options: SQLOptions;
[Symbol.asyncDispose](): Promise<any>;
}
const SQL: {
/**
* Creates a new SQL client instance
@@ -1921,7 +1711,7 @@ declare module "bun" {
* const sql = new SQL("postgres://localhost:5432/mydb", { idleTimeout: 1000 });
* ```
*/
new (connectionString: string | URL, options: Omit<SQL.Options, "url">): SQL;
new (connectionString: string | URL, options: Omit<SQLOptions, "url">): SQL;
/**
* Creates a new SQL client instance with options
@@ -1933,18 +1723,17 @@ declare module "bun" {
* const sql = new SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 });
* ```
*/
new (options?: SQL.Options): SQL;
new (options?: SQLOptions): SQL;
};
/**
* Represents a reserved connection from the connection pool
* Extends SQL with additional release functionality
*/
interface ReservedSQL extends SQL, Disposable {
/**
* Releases the client back to the connection pool
*/
interface ReservedSQL extends SQL {
/** Releases the client back to the connection pool */
release(): void;
[Symbol.dispose](): void;
}
/**
@@ -1953,30 +1742,26 @@ declare module "bun" {
*/
interface TransactionSQL extends SQL {
/** Creates a savepoint within the current transaction */
savepoint<T>(name: string, fn: SQLSavepointContextCallback<T>): Promise<T>;
savepoint<T>(fn: SQLSavepointContextCallback<T>): Promise<T>;
savepoint(name: string, fn: SQLSavepointContextCallback): Promise<any>;
savepoint(fn: SQLSavepointContextCallback): Promise<any>;
}
/**
* Represents a savepoint within a transaction
*/
interface SavepointSQL extends SQL {}
type CSRFAlgorithm = "blake2b256" | "blake2b512" | "sha256" | "sha384" | "sha512" | "sha512-256";
interface CSRFGenerateOptions {
/**
* The number of milliseconds until the token expires. 0 means the token never expires.
* @default 24 * 60 * 60 * 1000 (24 hours)
*/
expiresIn?: number;
/**
* The encoding of the token.
* @default "base64url"
*/
encoding?: "base64" | "base64url" | "hex";
/**
* The algorithm to use for the token.
* @default "sha256"
@@ -1989,19 +1774,16 @@ declare module "bun" {
* The secret to use for the token. If not provided, a random default secret will be generated in memory and used.
*/
secret?: string;
/**
* The encoding of the token.
* @default "base64url"
*/
encoding?: "base64" | "base64url" | "hex";
/**
* The algorithm to use for the token.
* @default "sha256"
*/
algorithm?: CSRFAlgorithm;
/**
* The number of milliseconds until the token expires. 0 means the token never expires.
* @default 24 * 60 * 60 * 1000 (24 hours)
@@ -2011,11 +1793,15 @@ declare module "bun" {
/**
* SQL client
*
* @category Database
*/
const sql: SQL;
/**
* SQL client for PostgreSQL
*
* @category Database
*/
const postgres: SQL;
@@ -3510,7 +3296,7 @@ declare module "bun" {
[K in HTTPMethod]?: RouteHandlerWithWebSocketUpgrade<T>;
};
type RouteValue<T extends string> = Response | false | RouteHandler<T> | RouteHandlerObject<T> | HTMLBundle;
type RouteValue<T extends string> = Response | false | RouteHandler<T> | RouteHandlerObject<T>;
type RouteValueWithWebSocketUpgrade<T extends string> =
| RouteValue<T>
| RouteHandlerWithWebSocketUpgrade<T>
@@ -6073,7 +5859,7 @@ declare module "bun" {
const isMainThread: boolean;
/**
* Used when importing an HTML file at runtime or at build time.
* Used when importing an HTML file at runtime.
*
* @example
*
@@ -6081,34 +5867,10 @@ declare module "bun" {
* import app from "./index.html";
* ```
*
* Bun.build support for this isn't imlpemented yet.
*/
interface HTMLBundle {
index: string;
/** Array of generated output files with metadata. This only exists when built ahead of time with `Bun.build` or `bun build` */
files?: Array<{
/** Original source file path. */
input?: string;
/** Generated output file path (with content hash, if included in naming) */
path: string;
/** File type/loader used (js, css, html, file, etc.) */
loader: Loader;
/** Whether this file is an entry point */
isEntry: boolean;
/** HTTP headers including ETag and Content-Type */
headers: {
/** ETag for caching */
etag: string;
/** MIME type with charset */
"content-type": string;
/**
* Additional headers may be added in the future.
*/
[key: string]: string;
};
}>;
}
/**
@@ -7203,6 +6965,8 @@ declare module "bun" {
maxBuffer?: number;
}
type ReadableIO = ReadableStream<Uint8Array> | number | undefined;
type ReadableToIO<X extends Readable> = X extends "pipe" | undefined
? ReadableStream<Uint8Array>
: X extends BunFile | ArrayBufferView | number
@@ -7917,56 +7681,6 @@ declare module "bun" {
timestamp?: number | Date,
): Buffer;
/**
* Generate a UUIDv5, which is a name-based UUID based on the SHA-1 hash of a namespace UUID and a name.
*
* @param name The name to use for the UUID
* @param namespace The namespace to use for the UUID
* @param encoding The encoding to use for the UUID
*
*
* @example
* ```js
* import { randomUUIDv5 } from "bun";
* const uuid = randomUUIDv5("www.example.com", "dns");
* console.log(uuid); // "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
* ```
*
* ```js
* import { randomUUIDv5 } from "bun";
* const uuid = randomUUIDv5("www.example.com", "url");
* console.log(uuid); // "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
* ```
*/
function randomUUIDv5(
name: string | BufferSource,
namespace: string | BufferSource | "dns" | "url" | "oid" | "x500",
/**
* @default "hex"
*/
encoding?: "hex" | "base64" | "base64url",
): string;
/**
* Generate a UUIDv5 as a Buffer
*
* @param name The name to use for the UUID
* @param namespace The namespace to use for the UUID
* @param encoding The encoding to use for the UUID
*
* @example
* ```js
* import { randomUUIDv5 } from "bun";
* const uuid = randomUUIDv5("www.example.com", "url", "buffer");
* console.log(uuid); // <Buffer 6b a7 b8 11 9d ad 11 d1 80 b4 00 c0 4f d4 30 c8>
* ```
*/
function randomUUIDv5(
name: string | BufferSource,
namespace: string | BufferSource | "dns" | "url" | "oid" | "x500",
encoding: "buffer",
): Buffer;
/**
* Types for `bun.lock`
*/

View File

@@ -14,23 +14,10 @@ declare module "bun" {
): void;
}
/** @deprecated Use {@link SQL.Query Bun.SQL.Query} */
type SQLQuery<T = any> = SQL.Query<T>;
/** @deprecated Use {@link SQL.TransactionContextCallback Bun.SQL.TransactionContextCallback} */
type SQLTransactionContextCallback<T> = SQL.TransactionContextCallback<T>;
/** @deprecated Use {@link SQL.SavepointContextCallback Bun.SQL.SavepointContextCallback} */
type SQLSavepointContextCallback<T> = SQL.SavepointContextCallback<T>;
/** @deprecated Use {@link SQL.Options Bun.SQL.Options} */
type SQLOptions = SQL.Options;
/**
* @deprecated Renamed to `ErrorLike`
*/
type Errorlike = ErrorLike;
interface TLSOptions {
/**
* File path to a TLS key
@@ -40,7 +27,6 @@ declare module "bun" {
* @deprecated since v0.6.3 - Use `key: Bun.file(path)` instead.
*/
keyFile?: string;
/**
* File path to a TLS certificate
*
@@ -49,7 +35,6 @@ declare module "bun" {
* @deprecated since v0.6.3 - Use `cert: Bun.file(path)` instead.
*/
certFile?: string;
/**
* File path to a .pem file for a custom root CA
*
@@ -57,9 +42,6 @@ declare module "bun" {
*/
caFile?: string;
}
/** @deprecated This type is unused in Bun's declarations and may be removed in the future */
type ReadableIO = ReadableStream<Uint8Array> | number | undefined;
}
declare namespace NodeJS {

View File

@@ -1,276 +0,0 @@
declare module "bun" {
export namespace __experimental {
/**
* Base interface for static site generation route parameters.
*
* Supports both single string values and arrays of strings for dynamic route segments.
* This is typically used for route parameters like `[slug]`, `[...rest]`, or `[id]`.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @example
* ```tsx
* // Simple slug parameter
* type BlogParams = { slug: string };
*
* // Multiple parameters
* type ProductParams = {
* category: string;
* id: string;
* };
*
* // Catch-all routes with string arrays
* type DocsParams = {
* path: string[];
* };
* ```
*/
export interface SSGParamsLike {
[key: string]: string | string[];
}
/**
* Configuration object for a single static route to be generated.
*
* Each path object contains the parameters needed to render a specific
* instance of a dynamic route at build time.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @template Params - The shape of route parameters for this path
*
* @example
* ```tsx
* // Single blog post path
* const blogPath: SSGPath<{ slug: string }> = {
* params: { slug: "my-first-post" }
* };
*
* // Product page with multiple params
* const productPath: SSGPath<{ category: string; id: string }> = {
* params: {
* category: "electronics",
* id: "laptop-123"
* }
* };
*
* // Documentation with catch-all route
* const docsPath: SSGPath<{ path: string[] }> = {
* params: { path: ["getting-started", "installation"] }
* };
* ```
*/
export interface SSGPath<Params extends SSGParamsLike = SSGParamsLike> {
params: Params;
}
/**
* Array of static paths to be generated at build time.
*
* This type represents the collection of all route configurations
* that should be pre-rendered for a dynamic route.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @template Params - The shape of route parameters for these paths
*
* @example
* ```tsx
* // Array of blog post paths
* const blogPaths: SSGPaths<{ slug: string }> = [
* { params: { slug: "introduction-to-bun" } },
* { params: { slug: "performance-benchmarks" } },
* { params: { slug: "getting-started-guide" } }
* ];
*
* // Mixed parameter types
* const productPaths: SSGPaths<{ category: string; id: string }> = [
* { params: { category: "books", id: "javascript-guide" } },
* { params: { category: "electronics", id: "smartphone-x" } }
* ];
* ```
*/
export type SSGPaths<Params extends SSGParamsLike = SSGParamsLike> = SSGPath<Params>[];
/**
* Props interface for SSG page components.
*
* This interface defines the shape of props that will be passed to your
* static page components during the build process. The `params` object
* contains the route parameters extracted from the URL pattern.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @template Params - The shape of route parameters for this page
*
* @example
* ```tsx
* // Blog post component props
* interface BlogPageProps extends SSGPageProps<{ slug: string }> {
* // params: { slug: string } is automatically included
* }
*
* // Product page component props
* interface ProductPageProps extends SSGPageProps<{
* category: string;
* id: string;
* }> {
* // params: { category: string; id: string } is automatically included
* }
*
* // Usage in component
* function BlogPost({ params }: BlogPageProps) {
* const { slug } = params; // TypeScript knows slug is a string
* return <h1>Blog post: {slug}</h1>;
* }
* ```
*/
export interface SSGPageProps<Params extends SSGParamsLike = SSGParamsLike> {
params: Params;
}
/**
* React component type for SSG pages that can be statically generated.
*
* This type represents a React component that receives SSG page props
* and can be rendered at build time. The component can be either a regular
* React component or an async React Server Component for advanced use cases
* like data fetching during static generation.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @template Params - The shape of route parameters for this page component
*
* @example
* ```tsx
* // Regular synchronous SSG page component
* const BlogPost: SSGPage<{ slug: string }> = ({ params }) => {
* return (
* <article>
* <h1>Blog Post: {params.slug}</h1>
* <p>This content was generated at build time!</p>
* </article>
* );
* };
*
* // Async React Server Component for data fetching
* const AsyncBlogPost: SSGPage<{ slug: string }> = async ({ params }) => {
* // Fetch data during static generation
* const post = await fetchBlogPost(params.slug);
* const author = await fetchAuthor(post.authorId);
*
* return (
* <article>
* <h1>{post.title}</h1>
* <p>By {author.name}</p>
* <div dangerouslySetInnerHTML={{ __html: post.content }} />
* </article>
* );
* };
*
* // Product page with multiple params and async data fetching
* const ProductPage: SSGPage<{ category: string; id: string }> = async ({ params }) => {
* const [product, reviews] = await Promise.all([
* fetchProduct(params.category, params.id),
* fetchProductReviews(params.id)
* ]);
*
* return (
* <div>
* <h1>{product.name}</h1>
* <p>Category: {params.category}</p>
* <p>Price: ${product.price}</p>
* <div>
* <h2>Reviews ({reviews.length})</h2>
* {reviews.map(review => (
* <div key={review.id}>{review.comment}</div>
* ))}
* </div>
* </div>
* );
* };
* ```
*/
export type SSGPage<Params extends SSGParamsLike = SSGParamsLike> = React.ComponentType<SSGPageProps<Params>>;
/**
* getStaticPaths is Bun's implementation of SSG (Static Site Generation) path determination.
*
* This function is called at your app's build time to determine which
* dynamic routes should be pre-rendered as static pages. It returns an
* array of path parameters that will be used to generate static pages for
* dynamic routes (e.g., [slug].tsx, [category]/[id].tsx).
*
* The function can be either synchronous or asynchronous, allowing you to
* fetch data from APIs, databases, or file systems to determine which paths
* should be statically generated.
*
* @warning These APIs are experimental and might be moved/changed in future releases.
*
* @template Params - The shape of route parameters for the dynamic route
*
* @returns An object containing an array of paths to be statically generated
*
* @example
* ```tsx
* // In pages/blog/[slug].tsx ———————————————————╮
* export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => {
* // Fetch all blog posts from your CMS or API at build time
* const posts = await fetchBlogPosts();
*
* return {
* paths: posts.map((post) => ({
* params: { slug: post.slug }
* }))
* };
* };
*
* // In pages/products/[category]/[id].tsx
* export const getStaticPaths: GetStaticPaths<{
* category: string;
* id: string;
* }> = async () => {
* // Fetch products from database
* const products = await db.products.findMany({
* select: { id: true, category: { slug: true } }
* });
*
* return {
* paths: products.map(product => ({
* params: {
* category: product.category.slug,
* id: product.id
* }
* }))
* };
* };
*
* // In pages/docs/[...path].tsx (catch-all route)
* export const getStaticPaths: GetStaticPaths<{ path: string[] }> = async () => {
* // Read documentation structure from file system
* const docPaths = await getDocumentationPaths('./content/docs');
*
* return {
* paths: docPaths.map(docPath => ({
* params: { path: docPath.split('/') }
* }))
* };
* };
*
* // Synchronous example with static data
* export const getStaticPaths: GetStaticPaths<{ id: string }> = () => {
* const staticIds = ['1', '2', '3', '4', '5'];
*
* return {
* paths: staticIds.map(id => ({
* params: { id }
* }))
* };
* };
* ```
*/
export type GetStaticPaths<Params extends SSGParamsLike = SSGParamsLike> = () => MaybePromise<{
paths: SSGPaths<Params>;
}>;
}
}

View File

@@ -19,7 +19,7 @@ declare module "*/bun.lock" {
}
declare module "*.html" {
var contents: import("bun").HTMLBundle;
// In Bun v1.2, this might change to Bun.HTMLBundle
var contents: any;
export = contents;
}

View File

@@ -20,7 +20,6 @@
/// <reference path="./deprecated.d.ts" />
/// <reference path="./redis.d.ts" />
/// <reference path="./shell.d.ts" />
/// <reference path="./experimental.d.ts" />
/// <reference path="./bun.ns.d.ts" />

View File

@@ -1,26 +1,5 @@
export {};
declare module "stream/web" {
interface ReadableStream {
/**
* Consume a ReadableStream as text
*/
text(): Promise<string>;
/**
* Consume a ReadableStream as a Uint8Array
*/
bytes(): Promise<Uint8Array>;
/**
* Consume a ReadableStream as JSON
*/
json(): Promise<any>;
/**
* Consume a ReadableStream as a Blob
*/
blob(): Promise<Blob>;
}
}
declare global {
namespace NodeJS {
interface ProcessEnv extends Bun.Env, ImportMetaEnv {}

View File

@@ -11,19 +11,13 @@
"files": [
"./*.d.ts",
"docs/**/*.md",
"docs/*.md",
"CLAUDE.md",
"README.md"
"docs/*.md"
],
"homepage": "https://bun.sh",
"dependencies": {
"@types/node": "*"
},
"peerDependencies": {
"@types/react": "^19"
},
"devDependencies": {
"@types/react": "^19",
"typescript": "^5.0.2"
},
"scripts": {

View File

@@ -220,11 +220,12 @@ declare module "bun" {
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, ...options: string[]): Promise<"OK" | string | null>;
/**
* Delete a key(s)
* @param keys The keys to delete
* Delete a key
* @param key The key to delete
* @param keys Additional keys to delete
* @returns Promise that resolves with the number of keys removed
*/
del(...keys: RedisClient.KeyLike[]): Promise<number>;
del(key: RedisClient.KeyLike, ...keys: RedisClient.KeyLike[]): Promise<number>;
/**
* Increment the integer value of a key by one

View File

@@ -764,79 +764,6 @@ declare module "bun:sqlite" {
*/
readonly paramsCount: number;
/**
* The actual SQLite column types from the first row of the result set.
* Useful for expressions and computed columns, which are not covered by `declaredTypes`
*
* Returns an array of SQLite type constants as uppercase strings:
* - `"INTEGER"` for integer values
* - `"FLOAT"` for floating-point values
* - `"TEXT"` for text values
* - `"BLOB"` for binary data
* - `"NULL"` for null values
* - `null` for unknown/unsupported types
*
* **Requirements:**
* - Only available for read-only statements (SELECT queries)
* - For non-read-only statements, throws an error
*
* **Behavior:**
* - Uses `sqlite3_column_type()` to get actual data types from the first row
* - Returns `null` for columns with unknown SQLite type constants
*
* @example
* ```ts
* const stmt = db.prepare("SELECT id, name, age FROM users WHERE id = 1");
*
* console.log(stmt.columnTypes);
* // => ["INTEGER", "TEXT", "INTEGER"]
*
* // For expressions:
* const exprStmt = db.prepare("SELECT length('bun') AS str_length");
* console.log(exprStmt.columnTypes);
* // => ["INTEGER"]
* ```
*
* @throws Error if statement is not read-only (INSERT, UPDATE, DELETE, etc.)
* @since Bun v1.2.13
*/
readonly columnTypes: Array<"INTEGER" | "FLOAT" | "TEXT" | "BLOB" | "NULL" | null>;
/**
* The declared column types from the table schema.
*
* Returns an array of declared type strings from `sqlite3_column_decltype()`:
* - Raw type strings as declared in the CREATE TABLE statement
* - `null` for columns without declared types (e.g., expressions, computed columns)
*
* **Requirements:**
* - Statement must be executed at least once before accessing this property
* - Available for both read-only and read-write statements
*
* **Behavior:**
* - Uses `sqlite3_column_decltype()` to get schema-declared types
* - Returns the exact type string from the table definition
*
* @example
* ```ts
* // For table columns:
* const stmt = db.prepare("SELECT id, name, weight FROM products");
* stmt.get();
* console.log(stmt.declaredTypes);
* // => ["INTEGER", "TEXT", "REAL"]
*
* // For expressions (no declared types):
* const exprStmt = db.prepare("SELECT length('bun') AS str_length");
* exprStmt.get();
* console.log(exprStmt.declaredTypes);
* // => [null]
* ```
*
* @throws Error if statement hasn't been executed
* @since Bun v1.2.13
*/
readonly declaredTypes: Array<string | null>;
/**
* Finalize the prepared statement, freeing the resources used by the
* statement and preventing it from being executed again.
@@ -913,12 +840,6 @@ declare module "bun:sqlite" {
* Native object representing the underlying `sqlite3_stmt`
*
* This is left untyped because the ABI of the native bindings may change at any time.
*
* For stable, typed access to statement metadata, use the typed properties on the Statement class:
* - {@link columnNames} for column names
* - {@link paramsCount} for parameter count
* - {@link columnTypes} for actual data types from the first row
* - {@link declaredTypes} for schema-declared column types
*/
readonly native: any;
}

View File

@@ -1184,6 +1184,14 @@ declare module "bun:test" {
* expect(null).toBeInstanceOf(Array); // fail
*/
toBeInstanceOf(value: unknown): void;
/**
* Asserts that the expected value is an instance of value
*
* @example
* expect([]).toBeInstanceOf(Array);
* expect(null).toBeInstanceOf(Array); // fail
*/
toBeInstanceOf(value: unknown): void;
/**
* Asserts that a value is `undefined`.
*

View File

@@ -95,7 +95,8 @@ WIN32_EXPORT struct us_socket_context_t *us_create_child_socket_context(int ssl,
```c
/* Write up to length bytes of data. Returns actual bytes written. Will call the on_writable callback of active socket context on failure to write everything off in one go.
WIN32_EXPORT int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length);
* Set hint msg_more if you have more immediate data to write. */
WIN32_EXPORT int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more);
/* Set a low precision, high performance timer on a socket. A socket can only have one single active timer at any given point in time. Will remove any such pre set timer */
WIN32_EXPORT void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds);

View File

@@ -762,9 +762,9 @@ ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_le
}
#else
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
ssize_t written = bsd_send(fd, header, header_length);
ssize_t written = bsd_send(fd, header, header_length, 0);
if (written == header_length) {
ssize_t second_write = bsd_send(fd, payload, payload_length);
ssize_t second_write = bsd_send(fd, payload, payload_length, 0);
if (second_write > 0) {
written += second_write;
}
@@ -773,7 +773,7 @@ ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_le
}
#endif
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length) {
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) {
while (1) {
// MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD)
@@ -781,8 +781,13 @@ ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length) {
#define MSG_NOSIGNAL 0
#endif
// use TCP_NOPUSH
ssize_t rc = send(fd, buf, length, MSG_NOSIGNAL | MSG_DONTWAIT);
#ifdef MSG_MORE
// for Linux we do not want signals
ssize_t rc = send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL | MSG_DONTWAIT);
#else
// use TCP_NOPUSH
ssize_t rc = send(fd, buf, length, MSG_NOSIGNAL | MSG_DONTWAIT);
#endif
if (UNLIKELY(IS_EINTR(rc))) {
continue;

View File

@@ -23,6 +23,7 @@
#ifndef _WIN32
#include <arpa/inet.h>
#endif
#define CONCURRENT_CONNECTIONS 4
// clang-format off
@@ -42,20 +43,17 @@ int us_raw_root_certs(struct us_cert_string_t**out){
void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) {
/* us_listen_socket_t extends us_socket_t so we close in similar ways */
struct us_socket_t* s = &ls->s;
if (!us_socket_is_closed(0, s)) {
struct us_socket_context_t* context = s->context;
struct us_loop_t* loop = context->loop;
us_internal_socket_context_unlink_listen_socket(ssl, context, ls);
us_poll_stop((struct us_poll_t *) s, loop);
bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
if (!us_socket_is_closed(0, &ls->s)) {
us_internal_socket_context_unlink_listen_socket(ssl, ls->s.context, ls);
us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop);
bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s));
/* Link this socket to the close-list and let it be deleted after this iteration */
s->next = loop->data.closed_head;
loop->data.closed_head = s;
ls->s.next = ls->s.context->loop->data.closed_head;
ls->s.context->loop->data.closed_head = &ls->s;
/* Any socket with prev = context is marked as closed */
s->prev = (struct us_socket_t *) context;
ls->s.prev = (struct us_socket_t *) ls->s.context;
}
/* We cannot immediately free a listen socket as we can be inside an accept loop */
@@ -93,18 +91,16 @@ void us_internal_socket_context_unlink_listen_socket(int ssl, struct us_socket_c
context->iterator = ls->s.next;
}
struct us_socket_t* prev = ls->s.prev;
struct us_socket_t* next = ls->s.next;
if (prev == next) {
if (ls->s.prev == ls->s.next) {
context->head_listen_sockets = 0;
} else {
if (prev) {
prev->next = next;
if (ls->s.prev) {
ls->s.prev->next = ls->s.next;
} else {
context->head_listen_sockets = (struct us_listen_socket_t *) next;
context->head_listen_sockets = (struct us_listen_socket_t *) ls->s.next;
}
if (next) {
next->prev = prev;
if (ls->s.next) {
ls->s.next->prev = ls->s.prev;
}
}
us_socket_context_unref(ssl, context);
@@ -116,35 +112,31 @@ void us_internal_socket_context_unlink_socket(int ssl, struct us_socket_context_
context->iterator = s->next;
}
struct us_socket_t* prev = s->prev;
struct us_socket_t* next = s->next;
if (prev == next) {
if (s->prev == s->next) {
context->head_sockets = 0;
} else {
if (prev) {
prev->next = next;
if (s->prev) {
s->prev->next = s->next;
} else {
context->head_sockets = next;
context->head_sockets = s->next;
}
if (next) {
next->prev = prev;
if (s->next) {
s->next->prev = s->prev;
}
}
us_socket_context_unref(ssl, context);
}
void us_internal_socket_context_unlink_connecting_socket(int ssl, struct us_socket_context_t *context, struct us_connecting_socket_t *c) {
struct us_connecting_socket_t* prev = c->prev_pending;
struct us_connecting_socket_t* next = c->next_pending;
if (prev == next) {
if (c->prev_pending == c->next_pending) {
context->head_connecting_sockets = 0;
} else {
if (prev) {
prev->next_pending = next;
if (c->prev_pending) {
c->prev_pending->next_pending = c->next_pending;
} else {
context->head_connecting_sockets = next;
context->head_connecting_sockets = c->next_pending;
}
if (next) {
next->prev_pending = prev;
if (c->next_pending) {
c->next_pending->prev_pending = c->prev_pending;
}
}
us_socket_context_unref(ssl, context);
@@ -152,12 +144,11 @@ void us_internal_socket_context_unlink_connecting_socket(int ssl, struct us_sock
/* We always add in the top, so we don't modify any s.next */
void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
struct us_socket_t* s = &ls->s;
s->context = context;
s->next = (struct us_socket_t *) context->head_listen_sockets;
s->prev = 0;
ls->s.context = context;
ls->s.next = (struct us_socket_t *) context->head_listen_sockets;
ls->s.prev = 0;
if (context->head_listen_sockets) {
context->head_listen_sockets->s.prev = s;
context->head_listen_sockets->s.prev = &ls->s;
}
context->head_listen_sockets = ls;
us_socket_context_ref(0, context);
@@ -375,15 +366,15 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co
us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE);
struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p;
struct us_socket_t* s = &ls->s;
s->context = context;
s->timeout = 255;
s->long_timeout = 255;
s->flags.low_prio_state = 0;
s->flags.is_paused = 0;
s->flags.is_ipc = 0;
s->next = 0;
s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
ls->s.context = context;
ls->s.timeout = 255;
ls->s.long_timeout = 255;
ls->s.flags.low_prio_state = 0;
ls->s.flags.is_paused = 0;
ls->s.flags.is_ipc = 0;
ls->s.next = 0;
ls->s.flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
us_internal_socket_context_link_listen_socket(context, ls);
ls->socket_ext_size = socket_ext_size;
@@ -409,16 +400,15 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock
us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE);
struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p;
struct us_socket_t* s = &ls->s;
s->connect_state = NULL;
s->context = context;
s->timeout = 255;
s->long_timeout = 255;
s->flags.low_prio_state = 0;
s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
s->flags.is_paused = 0;
s->flags.is_ipc = 0;
s->next = 0;
ls->s.connect_state = NULL;
ls->s.context = context;
ls->s.timeout = 255;
ls->s.long_timeout = 255;
ls->s.flags.low_prio_state = 0;
ls->s.flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
ls->s.flags.is_paused = 0;
ls->s.flags.is_ipc = 0;
ls->s.next = 0;
us_internal_socket_context_link_listen_socket(context, ls);
ls->socket_ext_size = socket_ext_size;
@@ -525,10 +515,9 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
}
// if there is only one result we can immediately connect
struct addrinfo_result_entry* entries = result->entries;
if (entries && entries->info.ai_next == NULL) {
if (result->entries && result->entries->info.ai_next == NULL) {
struct sockaddr_storage addr;
init_addr_with_port(&entries->info, port, &addr);
init_addr_with_port(&result->entries->info, port, &addr);
*has_dns_resolved = 1;
struct us_socket_t *s = us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size);
Bun__addrinfo_freeRequest(ai_req, s == NULL);
@@ -568,19 +557,17 @@ int start_connections(struct us_connecting_socket_t *c, int count) {
}
++opened;
bsd_socket_nodelay(connect_socket_fd, 1);
struct us_loop_t* loop = c->context->loop;
struct us_socket_context_t* context = c->context;
struct us_socket_t *s = (struct us_socket_t *)us_create_poll(loop, 0, sizeof(struct us_socket_t) + c->socket_ext_size);
s->context = context;
struct us_socket_t *s = (struct us_socket_t *)us_create_poll(c->context->loop, 0, sizeof(struct us_socket_t) + c->socket_ext_size);
s->context = c->context;
s->timeout = c->timeout;
s->long_timeout = c->long_timeout;
struct us_socket_flags* flags = &s->flags;
flags->low_prio_state = 0;
flags->allow_half_open = (c->options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
flags->is_paused = 0;
flags->is_ipc = 0;
s->flags.low_prio_state = 0;
s->flags.allow_half_open = (c->options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
s->flags.is_paused = 0;
s->flags.is_ipc = 0;
/* Link it into context so that timeout fires properly */
us_internal_socket_context_link_socket(context, s);
us_internal_socket_context_link_socket(s->context, s);
// TODO check this, specifically how it interacts with the SSL code
// does this work when we create multiple sockets at once? will we need multiple SSL contexts?
@@ -592,10 +579,10 @@ int start_connections(struct us_connecting_socket_t *c, int count) {
c->connecting_head = s;
s->connect_state = c;
struct us_poll_t* poll = &s->p;
/* Connect sockets are semi-sockets just like listen sockets */
us_poll_init(poll, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
us_poll_start(poll, loop, LIBUS_SOCKET_WRITABLE);
us_poll_init(&s->p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
us_poll_start(&s->p, s->context->loop, LIBUS_SOCKET_WRITABLE);
}
return opened;
}
@@ -787,50 +774,42 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con
if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
return s;
}
struct us_socket_context_t *old_context = s->context;
struct us_loop_t *loop = old_context->loop;
/* We need to be sure that we still holding a reference*/
us_socket_context_ref(ssl, old_context);
if (s->flags.low_prio_state != 1) {
/* We need to be sure that we still holding a reference*/
us_socket_context_ref(ssl, context);
/* This properly updates the iterator if in on_timeout */
us_internal_socket_context_unlink_socket(ssl, old_context, s);
} else {
/* We manually ref/unref context to handle context life cycle with low-priority queue */
us_socket_context_unref(ssl, old_context);
us_internal_socket_context_unlink_socket(ssl, s->context, s);
}
struct us_connecting_socket_t *c = s->connect_state;
struct us_socket_t *new_s = s;
if (ext_size != -1) {
struct us_poll_t *pool_ref = &s->p;
new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) + ext_size);
new_s = (struct us_socket_t *) us_poll_resize(&s->p, s->context->loop, sizeof(struct us_socket_t) + ext_size);
if (c) {
c->connecting_head = new_s;
struct us_socket_context_t *old_context = s->context;
c->context = context;
us_internal_socket_context_unlink_connecting_socket(ssl, old_context, c);
us_internal_socket_context_link_connecting_socket(ssl, context, c);
us_internal_socket_context_unlink_connecting_socket(ssl, old_context, c);
}
}
new_s->context = context;
new_s->timeout = 255;
new_s->long_timeout = 255;
if (new_s->flags.low_prio_state == 1) {
/* update pointers in low-priority queue */
if (!new_s->prev) loop->data.low_prio_head = new_s;
if (!new_s->prev) new_s->context->loop->data.low_prio_head = new_s;
else new_s->prev->next = new_s;
if (new_s->next) new_s->next->prev = new_s;
/* We manually ref/unref context to handle context life cycle with low-priority queue */
us_socket_context_ref(ssl, context);
} else {
us_internal_socket_context_link_socket(context, new_s);
us_socket_context_unref(ssl, context);
}
/* We can safely unref the old context here with can potentially be freed */
us_socket_context_unref(ssl, old_context);
return new_s;
}

View File

@@ -44,7 +44,10 @@ void *sni_find(void *sni, const char *hostname);
#include <wolfssl/options.h>
#endif
#include "./root_certs_header.h"
#include "./root_certs.h"
/* These are in root_certs.cpp */
extern X509_STORE *us_get_default_ca_store();
struct loop_ssl_data {
char *ssl_read_input, *ssl_read_output;
@@ -52,6 +55,10 @@ struct loop_ssl_data {
unsigned int ssl_read_input_offset;
struct us_socket_t *ssl_socket;
int last_write_was_msg_more;
int msg_more;
BIO *shared_rbio;
BIO *shared_wbio;
BIO_METHOD *shared_biom;
@@ -135,7 +142,10 @@ int BIO_s_custom_write(BIO *bio, const char *data, int length) {
struct loop_ssl_data *loop_ssl_data =
(struct loop_ssl_data *)BIO_get_data(bio);
int written = us_socket_write(0, loop_ssl_data->ssl_socket, data, length);
loop_ssl_data->last_write_was_msg_more =
loop_ssl_data->msg_more || length == 16413;
int written = us_socket_write(0, loop_ssl_data->ssl_socket, data, length,
loop_ssl_data->last_write_was_msg_more);
BIO_clear_retry_flags(bio);
if (!written) {
@@ -185,6 +195,7 @@ struct loop_ssl_data * us_internal_set_loop_ssl_data(struct us_internal_ssl_sock
loop_ssl_data->ssl_read_input_length = 0;
loop_ssl_data->ssl_read_input_offset = 0;
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
return loop_ssl_data;
}
@@ -657,6 +668,8 @@ void us_internal_init_loop_ssl_data(struct us_loop_t *loop) {
us_calloc(1, sizeof(struct loop_ssl_data));
loop_ssl_data->ssl_read_input_length = 0;
loop_ssl_data->ssl_read_input_offset = 0;
loop_ssl_data->last_write_was_msg_more = 0;
loop_ssl_data->msg_more = 0;
loop_ssl_data->ssl_read_output =
us_malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2);
@@ -1731,16 +1744,17 @@ us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s) {
}
int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s,
const char *data, int length) {
const char *data, int length,
int msg_more) {
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) {
return 0;
}
return us_socket_write(0, &s->s, data, length);
return us_socket_write(0, &s->s, data, length, msg_more);
}
int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,
const char *data, int length) {
const char *data, int length, int msg_more) {
if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s) || length == 0) {
return 0;
@@ -1761,8 +1775,14 @@ int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,
loop_ssl_data->ssl_read_input_length = 0;
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = msg_more;
loop_ssl_data->last_write_was_msg_more = 0;
int written = SSL_write(s->ssl, data, length);
loop_ssl_data->msg_more = 0;
if (loop_ssl_data->last_write_was_msg_more && !msg_more) {
us_socket_flush(0, &s->s);
}
if (written > 0) {
return written;
@@ -1819,6 +1839,7 @@ void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
// on_data and checked in the BIO
loop_ssl_data->ssl_socket = &s->s;
loop_ssl_data->msg_more = 0;
// sets SSL_SENT_SHUTDOWN and waits for the other side to do the same
int ret = SSL_shutdown(s->ssl);

View File

@@ -1,9 +1,10 @@
// MSVC doesn't support C11 stdatomic.h propertly yet.
// so we use C++ std::atomic instead.
#include "./root_certs.h"
#include "./root_certs_header.h"
#include "./internal/internal.h"
#include <atomic>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <string.h>
static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]);
@@ -133,23 +134,6 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) {
return root_certs_size;
}
struct us_default_ca_certificates {
X509 *root_cert_instances[root_certs_size];
STACK_OF(X509) *root_extra_cert_instances;
};
us_default_ca_certificates* us_get_default_ca_certificates() {
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL};
us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances);
return &default_ca_certificates;
}
STACK_OF(X509) *us_get_root_extra_cert_instances() {
return us_get_default_ca_certificates()->root_extra_cert_instances;
}
extern "C" X509_STORE *us_get_default_ca_store() {
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
@@ -161,9 +145,10 @@ extern "C" X509_STORE *us_get_default_ca_store() {
return NULL;
}
us_default_ca_certificates *default_ca_certificates = us_get_default_ca_certificates();
X509** root_cert_instances = default_ca_certificates->root_cert_instances;
STACK_OF(X509) *root_extra_cert_instances = default_ca_certificates->root_extra_cert_instances;
static X509 *root_cert_instances[root_certs_size] = {NULL};
static STACK_OF(X509) *root_extra_cert_instances = NULL;
us_internal_init_root_certs(root_cert_instances, root_extra_cert_instances);
// load all root_cert_instances on the default ca store
for (size_t i = 0; i < root_certs_size; i++) {

View File

@@ -1,4 +1,4 @@
// Maintaining the root certificates
// Maintaining the root certificates
//
// `src/crypto/root_certs.h` contains a compiled-in set of root certificates used as trust anchors
// for TLS certificate validation.
@@ -23,7 +23,7 @@
// `src/crypto/root_certs.h`.
// * Using `git diff-files` to determine which certificate have been added and/or
// removed.
//
//
#include "libusockets.h"
static struct us_cert_string_t root_certs[] = {

View File

@@ -1,13 +0,0 @@
#include <openssl/pem.h>
#include <openssl/x509.h>
#ifdef __cplusplus
#define CPPDECL extern "C"
STACK_OF(X509) *us_get_root_extra_cert_instances();
#else
#define CPPDECL extern
#endif
CPPDECL X509_STORE *us_get_default_ca_store();

View File

@@ -19,6 +19,7 @@
#include "internal/internal.h"
#include <stdlib.h>
#include <time.h>
#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE)
void Bun__internal_dispatch_ready_poll(void* loop, void* poll);
@@ -109,7 +110,7 @@ struct us_loop_t *us_timer_loop(struct us_timer_t *t) {
}
#if defined(LIBUS_USE_EPOLL)
#if defined(LIBUS_USE_EPOLL)
#include <sys/syscall.h>
#include <signal.h>
@@ -130,9 +131,9 @@ extern ssize_t sys_epoll_pwait2(int epfd, struct epoll_event* events, int maxeve
static int bun_epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, const struct timespec *timeout) {
int ret;
sigset_t mask;
sigset_t mask;
sigemptyset(&mask);
if (has_epoll_pwait2 != 0) {
do {
ret = sys_epoll_pwait2(epfd, events, maxevents, timeout, &mask);
@@ -145,7 +146,7 @@ static int bun_epoll_pwait2(int epfd, struct epoll_event *events, int maxevents,
has_epoll_pwait2 = 0;
}
int timeoutMs = -1;
int timeoutMs = -1;
if (timeout) {
timeoutMs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
}
@@ -177,7 +178,7 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t
if (has_epoll_pwait2 == -1) {
if (Bun__isEpollPwait2SupportedOnLinuxKernel() == 0) {
has_epoll_pwait2 = 0;
}
}
}
#else
@@ -337,7 +338,7 @@ void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct
// if new events does not contain the ready events of this poll then remove (no we filter that out later on)
SET_READY_POLL(loop, i, new_poll);
num_entries_possibly_remaining--;
}
}
@@ -357,16 +358,16 @@ int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_d
if ((new_events & LIBUS_SOCKET_READABLE) != (old_events & LIBUS_SOCKET_READABLE)) {
EV_SET64(&change_list[change_length++], fd, EVFILT_READ, is_readable ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0);
}
if(!is_readable && !is_writable) {
if(!(old_events & LIBUS_SOCKET_WRITABLE)) {
// if we are not reading or writing, we need to add writable to receive FIN
EV_SET64(&change_list[change_length++], fd, EVFILT_WRITE, EV_ADD, 0, 0, (uint64_t)(void*)user_data, 0, 0);
}
} else if ((new_events & LIBUS_SOCKET_WRITABLE) != (old_events & LIBUS_SOCKET_WRITABLE)) {
/* Do they differ in writable? */
/* Do they differ in writable? */
EV_SET64(&change_list[change_length++], fd, EVFILT_WRITE, (new_events & LIBUS_SOCKET_WRITABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0);
}
}
int ret;
do {
ret = kevent64(kqfd, change_list, change_length, change_list, change_length, KEVENT_FLAG_ERROR_EVENTS, NULL);
@@ -380,18 +381,19 @@ int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_d
struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size) {
int events = us_poll_events(p);
struct us_poll_t *new_p = us_realloc(p, sizeof(struct us_poll_t) + ext_size);
if (p != new_p) {
if (p != new_p && events) {
#ifdef LIBUS_USE_EPOLL
/* Hack: forcefully update poll by stripping away already set events */
new_p->state.poll_type = us_internal_poll_type(new_p);
us_poll_change(new_p, loop, events);
#else
/* Forcefully update poll by resetting them with new_p as user data */
kqueue_change(loop->fd, new_p->state.fd, 0, LIBUS_SOCKET_WRITABLE | LIBUS_SOCKET_READABLE, new_p);
#endif /* This is needed for epoll also (us_change_poll doesn't update the old poll) */
kqueue_change(loop->fd, new_p->state.fd, 0, events, new_p);
#endif
/* This is needed for epoll also (us_change_poll doesn't update the old poll) */
us_internal_loop_update_pending_ready_polls(loop, p, new_p, events, events);
}
@@ -445,7 +447,7 @@ void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
kqueue_change(loop->fd, p->state.fd, old_events, events, p);
#endif
/* Set all removed events to null-polls in pending ready poll list */
// us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events);
//us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events);
}
}
@@ -671,7 +673,7 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f
// using it for notifications and not for any other purpose.
mach_port_limits_t limits = { .mpl_qlimit = 1 };
kr = mach_port_set_attributes(self, cb->port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
if (UNLIKELY(kr != KERN_SUCCESS)) {
return NULL;
}
@@ -686,7 +688,7 @@ void us_internal_async_close(struct us_internal_async *a) {
struct kevent64_s event;
uint64_t ptr = (uint64_t)(void*)internal_cb;
EV_SET64(&event, ptr, EVFILT_MACHPORT, EV_DELETE, 0, 0, (uint64_t)(void*)internal_cb, 0,0);
int ret;
do {
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
@@ -718,7 +720,7 @@ void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_int
event.ext[1] = MACHPORT_BUF_LEN;
event.udata = (uint64_t)(void*)internal_cb;
int ret;
int ret;
do {
ret = kevent64(internal_cb->loop->fd, &event, 1, &event, 1, KEVENT_FLAG_ERROR_EVENTS, NULL);
} while (IS_EINTR(ret));
@@ -748,12 +750,12 @@ void us_internal_async_wakeup(struct us_internal_async *a) {
0, // Fail instantly if the port is full
MACH_PORT_NULL
);
switch (kr) {
case KERN_SUCCESS: {
break;
}
// This means that the send would've blocked because the
// queue is full. We assume success because the port is full.
case MACH_SEND_TIMED_OUT: {

View File

@@ -108,7 +108,7 @@ struct addrinfo_result {
#define us_internal_ssl_socket_r struct us_internal_ssl_socket_t *nonnull_arg
extern int Bun__addrinfo_get(struct us_loop_t* loop, const char* host, uint16_t port, struct addrinfo_request** ptr);
extern int Bun__addrinfo_set(struct addrinfo_request* ptr, struct us_connecting_socket_t* socket);
extern int Bun__addrinfo_set(struct addrinfo_request* ptr, struct us_connecting_socket_t* socket);
extern void Bun__addrinfo_freeRequest(struct addrinfo_request* addrinfo_req, int error);
extern struct addrinfo_result *Bun__addrinfo_getRequestResult(struct addrinfo_request* addrinfo_req);
@@ -158,7 +158,7 @@ void us_internal_socket_after_open(us_socket_r s, int error);
struct us_internal_ssl_socket_t *
us_internal_ssl_socket_close(us_internal_ssl_socket_r s, int code,
void *reason);
int us_internal_handle_dns_results(us_loop_r loop);
/* Sockets are polls */
@@ -167,9 +167,9 @@ struct us_socket_flags {
/* If true, the readable side is paused */
bool is_paused: 1;
/* Allow to stay alive after FIN/EOF */
bool allow_half_open: 1;
bool allow_half_open: 1;
/* 0 = not in low-prio queue, 1 = is in low-prio queue, 2 = was in low-prio queue in this iteration */
unsigned char low_prio_state: 2;
unsigned char low_prio_state: 2;
/* If true, the socket should be read using readmsg to support receiving file descriptors */
bool is_ipc: 1;
@@ -299,7 +299,7 @@ struct us_socket_context_t {
struct us_connecting_socket_t *(*on_connect_error)(struct us_connecting_socket_t *, int code);
struct us_socket_t *(*on_socket_connect_error)(struct us_socket_t *, int code);
int (*is_low_prio)(struct us_socket_t *);
};
/* Internal SSL interface */
@@ -310,7 +310,7 @@ struct us_internal_ssl_socket_t;
typedef void (*us_internal_on_handshake_t)(
struct us_internal_ssl_socket_t *, int success,
struct us_bun_verify_error_t verify_error, void *custom_data);
void us_internal_socket_context_free(int ssl, struct us_socket_context_t *context);
/* SNI functions */
void us_internal_ssl_socket_context_add_server_name(
@@ -421,9 +421,10 @@ struct us_socket_t *us_internal_ssl_socket_context_connect_unix(
size_t pathlen, int options, int socket_ext_size);
int us_internal_ssl_socket_write(us_internal_ssl_socket_r s,
const char *data, int length);
const char *data, int length, int msg_more);
int us_internal_ssl_socket_raw_write(us_internal_ssl_socket_r s,
const char *data, int length);
const char *data, int length,
int msg_more);
void us_internal_ssl_socket_timeout(us_internal_ssl_socket_r s,
unsigned int seconds);

View File

@@ -210,7 +210,7 @@ ssize_t bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags);
#if !defined(_WIN32)
ssize_t bsd_recvmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct msghdr *msg, int flags);
#endif
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length);
ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more);
#if !defined(_WIN32)
ssize_t bsd_sendmsg(LIBUS_SOCKET_DESCRIPTOR fd, const struct msghdr *msg, int flags);
#endif

View File

@@ -419,8 +419,9 @@ struct us_poll_t *us_poll_resize(us_poll_r p, us_loop_r loop, unsigned int ext_s
void *us_socket_get_native_handle(int ssl, us_socket_r s) nonnull_fn_decl;
/* Write up to length bytes of data. Returns actual bytes written.
* Will call the on_writable callback of active socket context on failure to write everything off in one go. */
int us_socket_write(int ssl, us_socket_r s, const char * nonnull_arg data, int length) nonnull_fn_decl;
* Will call the on_writable callback of active socket context on failure to write everything off in one go.
* Set hint msg_more if you have more immediate data to write. */
int us_socket_write(int ssl, us_socket_r s, const char * nonnull_arg data, int length, int msg_more) nonnull_fn_decl;
/* Special path for non-SSL sockets. Used to send header and payload in one go. Works like us_socket_write. */
int us_socket_write2(int ssl, us_socket_r s, const char *header, int header_length, const char *payload, int payload_length) nonnull_fn_decl;
@@ -439,7 +440,7 @@ void *us_connecting_socket_ext(int ssl, struct us_connecting_socket_t *c) nonnul
/* Return the socket context of this socket */
struct us_socket_context_t *us_socket_context(int ssl, us_socket_r s) nonnull_fn_decl __attribute__((returns_nonnull));
/* Flush any pending data */
/* Withdraw any msg_more status and flush any pending data */
void us_socket_flush(int ssl, us_socket_r s) nonnull_fn_decl;
/* Shuts down the connection by sending FIN and/or close_notify */
@@ -470,7 +471,7 @@ void us_socket_local_address(int ssl, us_socket_r s, char *nonnull_arg buf, int
struct us_socket_t *us_socket_pair(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR* fds);
struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int ipc);
struct us_socket_t *us_socket_wrap_with_tls(int ssl, us_socket_r s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
int us_socket_raw_write(int ssl, us_socket_r s, const char *data, int length);
int us_socket_raw_write(int ssl, us_socket_r s, const char *data, int length, int msg_more);
struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length);
int us_raw_root_certs(struct us_cert_string_t**out);
unsigned int us_get_remote_address_info(char *buf, us_socket_r s, const char **dest, int *port, int *is_ipv6);

View File

@@ -128,7 +128,7 @@ void us_internal_timer_sweep(struct us_loop_t *loop) {
if (context->iterator == s && long_ticks == s->long_timeout) {
s->long_timeout = 255;
if (context->on_socket_long_timeout != NULL) context->on_socket_long_timeout(s);
}
}
/* Check for unlink / link (if the event handler did not modify the chain, we step 1) */
if (s == context->iterator) {
@@ -336,13 +336,12 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
case POLL_TYPE_SOCKET: {
/* We should only use s, no p after this point */
struct us_socket_t *s = (struct us_socket_t *) p;
/* The context can change after calling a callback but the loop is always the same */
struct us_loop_t* loop = s->context->loop;
if (events & LIBUS_SOCKET_WRITABLE && !error) {
/* Note: if we failed a write as a socket of one loop then adopted
* to another loop, this will be wrong. Absurd case though */
loop->data.last_write_failed = 0;
s->context->loop->data.last_write_failed = 0;
s = s->context->on_writable(s);
if (!s || us_socket_is_closed(0, s)) {
@@ -350,8 +349,8 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
}
/* If we have no failed write or if we shut down, then stop polling for more writable */
if (!loop->data.last_write_failed || us_socket_is_shut_down(0, s)) {
us_poll_change(&s->p, loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
if (!s->context->loop->data.last_write_failed || us_socket_is_shut_down(0, s)) {
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
}
}
@@ -359,28 +358,25 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
/* Contexts may prioritize down sockets that are currently readable, e.g. when SSL handshake has to be done.
* SSL handshakes are CPU intensive, so we limit the number of handshakes per loop iteration, and move the rest
* to the low-priority queue */
struct us_socket_context_t *context = s->context;
struct us_socket_flags* flags = &s->flags;
if (context->is_low_prio(s)) {
if (flags->low_prio_state == 2) {
flags->low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */
} else if (loop->data.low_prio_budget > 0) {
loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */
if (s->context->is_low_prio(s)) {
if (s->flags.low_prio_state == 2) {
s->flags.low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */
} else if (s->context->loop->data.low_prio_budget > 0) {
s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */
} else {
struct us_poll_t* poll = &s->p;
us_poll_change(poll, loop, us_poll_events(poll) & LIBUS_SOCKET_WRITABLE);
us_socket_context_ref(0, context);
us_internal_socket_context_unlink_socket(0, context, s);
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
us_socket_context_ref(0, s->context);
us_internal_socket_context_unlink_socket(0, s->context, s);
/* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are
* maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts
* under high load */
s->prev = 0;
s->next = loop->data.low_prio_head;
s->next = s->context->loop->data.low_prio_head;
if (s->next) s->next->prev = s;
loop->data.low_prio_head = s;
s->context->loop->data.low_prio_head = s;
flags->low_prio_state = 1;
s->flags.low_prio_state = 1;
break;
}
@@ -389,6 +385,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
size_t repeat_recv_count = 0;
do {
const struct us_loop_t* loop = s->context->loop;
#ifdef _WIN32
const int recv_flags = MSG_PUSH_IMMEDIATE;
#else
@@ -401,20 +398,20 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
struct msghdr msg = {0};
struct iovec iov = {0};
char cmsg_buf[CMSG_SPACE(sizeof(int))];
iov.iov_base = loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING;
iov.iov_len = LIBUS_RECV_BUFFER_LENGTH;
msg.msg_flags = 0;
msg.msg_flags = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_controllen = CMSG_LEN(sizeof(int));
msg.msg_control = cmsg_buf;
length = bsd_recvmsg(us_poll_fd(&s->p), &msg, recv_flags);
// Extract file descriptor if present
if (length > 0 && msg.msg_controllen > 0) {
struct cmsghdr *cmsg_ptr = CMSG_FIRSTHDR(&msg);
@@ -442,14 +439,14 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
// - the event loop isn't very busy, so we can read multiple times in a row
#define LOOP_ISNT_VERY_BUSY_THRESHOLD 25
if (
s && length >= (LIBUS_RECV_BUFFER_LENGTH - 24 * 1024) && length <= LIBUS_RECV_BUFFER_LENGTH &&
(error || loop->num_ready_polls < LOOP_ISNT_VERY_BUSY_THRESHOLD) &&
s && length >= (LIBUS_RECV_BUFFER_LENGTH - 24 * 1024) && length <= LIBUS_RECV_BUFFER_LENGTH &&
(error || loop->num_ready_polls < LOOP_ISNT_VERY_BUSY_THRESHOLD) &&
!us_socket_is_closed(0, s)
) {
repeat_recv_count += error == 0;
// When not hung up, read a maximum of 10 times to avoid starving other sockets
// We don't bother with ioctl(FIONREAD) because we've set MSG_DONTWAIT
// We don't bother with ioctl(FIONREAD) because we've set MSG_DONTWAIT
if (!(repeat_recv_count > 10 && loop->num_ready_polls > 2)) {
continue;
}
@@ -481,7 +478,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
}
if(s->flags.allow_half_open) {
/* We got a Error but is EOF and we allow half open so stop polling for readable and keep going*/
us_poll_change(&s->p, loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
s = s->context->on_end(s);
} else {
/* We dont allow half open just emit end and close the socket */
@@ -489,7 +486,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
s = us_socket_close(0, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, NULL);
return;
}
}
}
/* Such as epollerr or EV_ERROR */
if (error && s) {
/* Todo: decide what code we give here */

View File

@@ -357,17 +357,17 @@ void *us_connecting_socket_get_native_handle(int ssl, struct us_connecting_socke
return (void *) (uintptr_t) -1;
}
int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length) {
int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) {
#ifndef LIBUS_NO_SSL
if (ssl) {
return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length);
return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more);
}
#endif
if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
return 0;
}
int written = bsd_send(us_poll_fd(&s->p), data, length);
int written = bsd_send(us_poll_fd(&s->p), data, length, msg_more);
if (written != length) {
s->context->loop->data.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
@@ -387,29 +387,29 @@ int us_socket_ipc_write_fd(struct us_socket_t *s, const char* data, int length,
struct msghdr msg = {0};
struct iovec iov = {0};
char cmsgbuf[CMSG_SPACE(sizeof(int))];
iov.iov_base = (void*)data;
iov.iov_len = length;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(cmsg) = fd;
int sent = bsd_sendmsg(us_poll_fd(&s->p), &msg, 0);
if (sent != length) {
s->context->loop->data.last_write_failed = 1;
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
return sent < 0 ? 0 : sent;
}
#endif
@@ -495,14 +495,14 @@ struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_clien
}
int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length) {
int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) {
#ifndef LIBUS_NO_SSL
if (ssl) {
return us_internal_ssl_socket_raw_write((struct us_internal_ssl_socket_t *) s, data, length);
return us_internal_ssl_socket_raw_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more);
}
#endif
// non-TLS is always raw
return us_socket_write(ssl, s, data, length);
return us_socket_write(ssl, s, data, length, msg_more);
}
unsigned int us_get_remote_address_info(char *buf, struct us_socket_t *s, const char **dest, int *port, int *is_ipv6)

View File

@@ -249,7 +249,7 @@ public:
}
static TemplatedApp<SSL>* create(SocketContextOptions options = {}) {
auto* httpContext = HttpContext<SSL>::create(Loop::get(), options);
if (!httpContext) {
return nullptr;
@@ -646,3 +646,4 @@ typedef TemplatedApp<false> App;
typedef TemplatedApp<true> SSLApp;
}

View File

@@ -224,7 +224,7 @@ public:
/**
* Flushes the socket buffer by writing as much data as possible to the underlying socket.
*
*
* @return The total number of bytes successfully written to the socket
*/
size_t flush() {
@@ -237,24 +237,24 @@ public:
/* Get the associated asynchronous socket data structure */
AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData();
size_t total_written = 0;
/* Continue flushing as long as we have data in the buffer */
while (asyncSocketData->buffer.length()) {
/* Get current buffer size */
size_t buffer_len = asyncSocketData->buffer.length();
/* Limit write size to INT_MAX as the underlying socket API uses int for length */
int max_flush_len = std::min(buffer_len, (size_t)INT_MAX);
/* Attempt to write data to the socket */
int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), max_flush_len);
int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), max_flush_len, 0);
total_written += written;
/* Check if we couldn't write the entire buffer */
if ((unsigned int) written < buffer_len) {
/* Remove the successfully written data from the buffer */
asyncSocketData->buffer.erase((unsigned int) written);
/* If we wrote less than we attempted, the socket buffer is likely full
* likely is used as an optimization hint to the compiler
* since written < buffer_len is very likely to be true
@@ -297,7 +297,7 @@ public:
int max_flush_len = std::min(buffer_len, (size_t)INT_MAX);
/* Write off as much as we can */
int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), max_flush_len);
int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), max_flush_len, /*nextLength != 0 | */length);
/* On failure return, otherwise continue down the function */
if ((unsigned int) written < buffer_len) {
/* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */
@@ -317,7 +317,7 @@ public:
asyncSocketData->buffer.clear();
}
if (length) {
if (length) {
if (loopData->isCorkedWith(this)) {
/* We are corked */
if (LoopData::CORK_BUFFER_SIZE - loopData->getCorkOffset() >= (unsigned int) length) {
@@ -342,7 +342,7 @@ public:
}
} else {
/* We are not corked */
int written = us_socket_write(SSL, (us_socket_t *) this, src, length);
int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0);
/* Did we fail? */
if (written < length) {

View File

@@ -52,7 +52,7 @@ public:
if (key.length() < 2) {
return true;
}
ScrambleArea s = getFeatures(key);
s.val = perfectHash(s.val);
return filter[s.p[0]] &&

View File

@@ -76,7 +76,7 @@ namespace uWS {
data.remove_prefix(1);
}
}
auto len = data.length();
if(len) {
// consume extension
@@ -93,20 +93,20 @@ namespace uWS {
/* RFC 9110: Token format (TLDR; anything bellow 32 is not allowed)
* TODO: add support for quoted-strings values (RFC 9110: 3.2.6. Quoted-String)
* Example of chunked encoding with extensions:
*
*
* 4;key=value\r\n
* Wiki\r\n
* 5;foo=bar;baz=quux\r\n
* pedia\r\n
* 0\r\n
* \r\n
*
*
* The chunk size is in hex (4, 5, 0), followed by optional
* semicolon-separated extensions. Extensions consist of a key
* (token) and optional value. The value may be a token or a
* quoted string. The chunk data follows the CRLF after the
* extensions and must be exactly the size specified.
*
*
* RFC 7230 Section 4.1.1 defines chunk extensions as:
* chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
* chunk-ext-name = token
@@ -116,7 +116,7 @@ namespace uWS {
state = STATE_IS_ERROR;
return;
}
data.remove_prefix(1);
}
}

View File

@@ -17,5 +17,5 @@ namespace uWS {
//printf("Constructing http3contextdata: %p\n", this);
}
};
}

View File

@@ -77,7 +77,7 @@ namespace uWS {
/* If not already written */
writeStatus("200 OK");
// has body is determined by the ending so this is perfect here
us_quic_socket_context_send_headers(nullptr, (us_quic_stream_t *) this, responseData->headerOffset, data.length() > 0);

View File

@@ -14,7 +14,7 @@ namespace uWS {
/* Status is always first header just like for h1 */
unsigned int headerOffset = 0;
/* Write offset */
uint64_t offset = 0;

View File

@@ -90,7 +90,7 @@ private:
MACRO("UNLINK") \
MACRO("UNLOCK") \
MACRO("UNSUBSCRIBE") \
#ifndef _WIN32
static constexpr std::array<const std::string, 35> HTTP_METHODS = {
@@ -108,12 +108,12 @@ private:
FOR_EACH_HTTP_METHOD(MACRO)
#undef MACRO
};
static std::span<const std::string> getAllHttpMethods() {
static std::once_flag flag;
static std::array<std::string, 35> methods;
std::call_once(flag, []() {
methods = {
methods = {
#define MACRO(name) std::string {name},
FOR_EACH_HTTP_METHOD(MACRO)
#undef MACRO
@@ -201,7 +201,7 @@ private:
/* Call filter */
HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
for (auto &f : httpContextData->filterHandlers) {
f((HttpResponse<SSL> *) s, -1);
}
@@ -276,7 +276,7 @@ private:
/* Mark pending request and emit it */
httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
/* Mark this response as connectionClose if ancient or connection: close */
if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
@@ -336,7 +336,7 @@ private:
}, [httpResponseData](void *user, std::string_view data, bool fin) -> void * {
/* We always get an empty chunk even if there is no data */
if (httpResponseData->inStream) {
/* Todo: can this handle timeout for non-post as well? */
if (fin) {
/* If we just got the last chunk (or empty chunk), disable timeout */
@@ -374,7 +374,7 @@ private:
});
auto httpErrorStatusCode = result.httpErrorStatusCode();
/* Mark that we are no longer parsing Http */
httpContextData->flags.isParsingHttp = false;
/* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
@@ -383,12 +383,12 @@ private:
httpContextData->onClientError(SSL, s, result.parserError, data, length);
}
/* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
us_socket_write(SSL, s, httpErrorResponses[httpErrorStatusCode].data(), (int) httpErrorResponses[httpErrorStatusCode].length());
us_socket_write(SSL, s, httpErrorResponses[httpErrorStatusCode].data(), (int) httpErrorResponses[httpErrorStatusCode].length(), false);
us_socket_shutdown(SSL, s);
/* Close any socket on HTTP errors */
us_socket_close(SSL, s, 0, nullptr);
}
auto returnedData = result.returnedData;
/* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
if (returnedData != nullptr) {
@@ -471,12 +471,12 @@ private:
* and will fall through to the next section of code
*/
}
/* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
if (httpResponseData->onWritable) {
/* We are now writable, so hang timeout again, the user does not have to do anything so we should hang until end or tryEnd rearms timeout */
us_socket_timeout(SSL, s, 0);
/* We expect the developer to return whether or not write was successful (true).
* If write was never called, the developer should still return true so that we may drain. */
bool success = httpResponseData->callOnWritable(reinterpret_cast<HttpResponse<SSL> *>(asyncSocket), httpResponseData->offset);
@@ -497,7 +497,7 @@ private:
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
if (asyncSocket->getBufferedAmount() == 0) {
asyncSocket->shutdown();
/* We need to force close after sending FIN since we want to hinder
* clients from keeping to send their huge data */
@@ -588,7 +588,7 @@ public:
methods = getAllHttpMethods();
} else {
methods_buffer[0] = std::string(method);
methods = {methods_buffer.data(), 1};
methods = {methods_buffer.data(), 1};
}
uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
@@ -616,7 +616,7 @@ public:
}
}
httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets), httpContextData](auto *r) mutable {
auto user = r->getUserData();
@@ -667,3 +667,5 @@ public:
};
}

View File

@@ -39,3 +39,4 @@ static const std::string_view httpErrorResponses[] = {
}

View File

@@ -108,7 +108,7 @@ namespace uWS
}
/* Returns true if there was an error */
/* Returns true if there was an error */
bool isError() {
return parserError != HTTP_PARSER_ERROR_NONE;
}
@@ -403,7 +403,7 @@ namespace uWS
static bool isValidMethod(std::string_view str, bool useStrictMethodValidation) {
if (str.empty()) return false;
if (useStrictMethodValidation) {
return Bun__HTTPMethod__from(str.data(), str.length()) != -1;
}
@@ -613,13 +613,13 @@ namespace uWS
return HttpParserResult::shortRead();
}
postPaddedBuffer = requestLineResult.position;
if(requestLineResult.isAncientHTTP) {
isAncientHTTP = true;
}
/* No request headers found */
const char * headerStart = (headers[0].key.length() > 0) ? headers[0].key.data() : end;
/* Check if we can see if headers follow or not */
if (postPaddedBuffer + 2 > end) {
/* Not enough data to check for \r\n */
@@ -711,7 +711,7 @@ namespace uWS
}
}
} else {
if(postPaddedBuffer[0] == '\r') {
// invalid char after \r
return HttpParserResult::error(HTTP_ERROR_400_BAD_REQUEST, HTTP_PARSER_ERROR_INVALID_REQUEST);
@@ -757,7 +757,7 @@ namespace uWS
/* Add all headers to bloom filter */
req->bf.reset();
for (HttpRequest::Header *h = req->headers; (++h)->key.length(); ) {
req->bf.add(h->key);
}
@@ -864,7 +864,7 @@ namespace uWS
break;
}
}
return HttpParserResult::success(consumedTotal, user);
}
@@ -1000,3 +1000,4 @@ public:
};
}

View File

@@ -106,7 +106,7 @@ public:
if (closeConnection) {
/* We can only write the header once */
if (!(httpResponseData->state & (HttpResponseData<SSL>::HTTP_END_CALLED))) {
/* HTTP 1.1 must send this back unless the client already sent it to us.
* It is a connection close when either of the two parties say so but the
* one party must tell the other one so.
@@ -125,10 +125,10 @@ public:
/* We do not have tryWrite-like functionalities, so ignore optional in this path */
/* Write the chunked data if there is any (this will not send zero chunks) */
this->write(data, nullptr);
/* Terminating 0 chunk */
Super::write("0\r\n\r\n", 5);
@@ -470,7 +470,7 @@ public:
writeStatus(HTTP_200_OK);
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
if (!(httpResponseData->state & HttpResponseData<SSL>::HTTP_WROTE_CONTENT_LENGTH_HEADER) && !httpResponseData->fromAncientRequest) {
if (!(httpResponseData->state & HttpResponseData<SSL>::HTTP_WRITE_CALLED)) {
/* Write mark on first call to write */
@@ -532,7 +532,7 @@ public:
}
return !has_failed;
}
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
@@ -545,7 +545,7 @@ public:
Super::write("\r\n", 2);
httpResponseData->state |= HttpResponseData<SSL>::HTTP_WRITE_CALLED;
}
writeUnsignedHex((unsigned int) data.length());
Super::write("\r\n", 2);
} else if (!(httpResponseData->state & HttpResponseData<SSL>::HTTP_WRITE_CALLED)) {
@@ -578,7 +578,7 @@ public:
// Write End of Chunked Encoding after data has been written
Super::write("\r\n", 2);
}
/* Reset timeout on each sended chunk */
this->resetTimeout();

View File

@@ -109,3 +109,5 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
};
}

View File

@@ -87,7 +87,7 @@ private:
LoopData *loopData = (LoopData *) us_loop_ext((struct us_loop_t *) loop);
loopData->dateTimer = us_create_timer((struct us_loop_t *) loop, 1, sizeof(LoopData *));
loopData->updateDate();
memcpy(us_timer_ext(loopData->dateTimer), &loopData, sizeof(LoopData *));
us_timer_set(loopData->dateTimer, [](struct us_timer_t *t) {
LoopData *loopData;
@@ -103,7 +103,7 @@ private:
~LoopCleaner() {
// There's no need to call this destructor if Bun is in the process of exiting.
// This is both a performance thing, and also to prevent freeing some things which are not meant to be freed
// such as uv_tty_t
// such as uv_tty_t
if(loop && cleanMe && !bun_is_exiting()) {
cleanMe = false;
loop->free();

View File

@@ -97,11 +97,11 @@ public:
this->corkedSocket = nullptr;
this->corkOffset = 0;
}
unsigned int getCorkOffset() {
return this->corkOffset;
}
void setCorkOffset(unsigned int offset) {
this->corkOffset = offset;
}
@@ -109,7 +109,7 @@ public:
void incrementCorkedOffset(unsigned int offset) {
this->corkOffset += offset;
}
char* getCorkBuffer() {
return this->corkBuffer;
}

View File

@@ -260,7 +260,7 @@ public:
/* This one always resets needsDrainage before it calls any cb's.
* Otherwise we would stackoverflow when sending after publish but before drain. */
drainImpl(s);
/* If we drained last subscriber, also clear outgoingMessages */
if (!drainableSubscribers) {
outgoingMessages.clear();
@@ -363,3 +363,5 @@ public:
};
}

View File

@@ -1,4 +1,4 @@
# Version: 9
# Version: 8
# A script that installs the dependencies needed to build and test Bun.
# This should work on Windows 10 or newer with PowerShell.
@@ -240,11 +240,11 @@ function Install-Git {
}
function Install-NodeJs {
Install-Package nodejs -Command node -Version "24.3.0"
Install-Package nodejs -Command node -Version "22.9.0"
}
function Install-Bun {
Install-Package bun -Version "1.2.17"
Install-Package bun -Version "1.1.30"
}
function Install-Cygwin {

Some files were not shown because too many files have changed in this diff Show More