Compare commits

..

8 Commits

Author SHA1 Message Date
Meghan Denny
72031eeafc Merge remote-tracking branch 'origin/main' into nektro-patch-34511 2025-09-05 21:04:03 -07:00
Meghan Denny
05f102ee58 fix test-http-no-content-length 2025-09-05 21:02:43 -07:00
Meghan Denny
609b575500 bump 2025-09-05 20:58:23 -07:00
Meghan Denny
1a0e3b22a8 fix socket.test.ts 2025-09-05 17:36:26 -07:00
Meghan Denny
32c8e541d6 fix fetch.unix.test.ts 2025-09-05 17:36:13 -07:00
Meghan Denny
f312e9ad30 fix posix release build 2025-09-05 16:46:36 -07:00
autofix-ci[bot]
d923684b4a [autofix.ci] apply automated fixes 2025-09-05 22:40:15 +00:00
Meghan Denny
eed648e28f node:net: improve SytemError errno consistency 2025-09-05 15:36:25 -07:00
1048 changed files with 29082 additions and 71077 deletions

View File

@@ -371,7 +371,7 @@ function getZigAgent(platform, options) {
* @returns {Agent}
*/
function getTestAgent(platform, options) {
const { os, arch, profile } = platform;
const { os, arch } = platform;
if (os === "darwin") {
return {
@@ -391,13 +391,6 @@ function getTestAgent(platform, options) {
}
if (arch === "aarch64") {
if (profile === "asan") {
return getEc2Agent(platform, options, {
instanceType: "c8g.2xlarge",
cpuCount: 2,
threadsPerCore: 1,
});
}
return getEc2Agent(platform, options, {
instanceType: "c8g.xlarge",
cpuCount: 2,
@@ -405,13 +398,6 @@ function getTestAgent(platform, options) {
});
}
if (profile === "asan") {
return getEc2Agent(platform, options, {
instanceType: "c7i.2xlarge",
cpuCount: 2,
threadsPerCore: 1,
});
}
return getEc2Agent(platform, options, {
instanceType: "c7i.xlarge",
cpuCount: 2,
@@ -552,7 +538,6 @@ function getLinkBunStep(platform, options) {
cancel_on_build_failing: isMergeQueue(),
env: {
BUN_LINK_ONLY: "ON",
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
...getBuildEnv(platform, options),
},
command: `${getBuildCommand(platform, options, "build-bun")} --target bun`,
@@ -615,10 +600,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
parallelism: unifiedTests ? undefined : os === "darwin" ? 2 : 10,
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
env: {
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
},
timeout_in_minutes: 60,
command:
os === "windows"
? `node .\\scripts\\runner.node.mjs ${args.join(" ")}`

4
.github/CODEOWNERS vendored
View File

@@ -3,7 +3,3 @@
# Tests
/test/expectations.txt @Jarred-Sumner
# Types
*.d.ts @alii
/packages/bun-types/ @alii

View File

@@ -25,7 +25,7 @@ runs:
echo "version=$LATEST" >> $GITHUB_OUTPUT
echo "message=$MESSAGE" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
add-paths: |
CMakeLists.txt

View File

@@ -1,19 +0,0 @@
name: Auto Assign Types Issues
on:
issues:
types: [labeled]
jobs:
auto-assign:
runs-on: ubuntu-latest
if: github.event.label.name == 'types'
permissions:
issues: write
steps:
- name: Assign to alii
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
gh issue edit ${{ github.event.issue.number }} --add-assignee alii

View File

@@ -105,16 +105,11 @@ jobs:
env:
GITHUB_ISSUE_BODY: ${{ github.event.issue.body }}
GITHUB_ISSUE_TITLE: ${{ github.event.issue.title }}
GITHUB_ISSUE_NUMBER: ${{ github.event.issue.number }}
shell: bash
run: |
LABELS=$(bun scripts/read-issue.ts)
bun scripts/is-outdated.ts
# Check for patterns that should close the issue
CLOSE_ACTION=$(bun scripts/handle-crash-patterns.ts)
echo "close-action=$CLOSE_ACTION" >> $GITHUB_OUTPUT
if [[ -f "is-outdated.txt" ]]; then
echo "is-outdated=true" >> $GITHUB_OUTPUT
fi
@@ -123,10 +118,6 @@ jobs:
echo "outdated=$(cat outdated.txt)" >> $GITHUB_OUTPUT
fi
if [[ -f "is-standalone.txt" ]]; then
echo "is-standalone=true" >> $GITHUB_OUTPUT
fi
if [[ -f "is-very-outdated.txt" ]]; then
echo "is-very-outdated=true" >> $GITHUB_OUTPUT
LABELS="$LABELS,old-version"
@@ -136,32 +127,9 @@ jobs:
echo "latest=$(cat LATEST)" >> $GITHUB_OUTPUT
echo "labels=$LABELS" >> $GITHUB_OUTPUT
rm -rf is-outdated.txt outdated.txt latest.txt is-very-outdated.txt is-standalone.txt
- name: Close issue if pattern detected
if: github.event.label.name == 'crash' && fromJson(steps.add-labels.outputs.close-action).close == true
uses: actions/github-script@v7
with:
script: |
const closeAction = JSON.parse('${{ steps.add-labels.outputs.close-action }}');
// Comment with the reason
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: closeAction.comment
});
// Close the issue
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
state: 'closed',
state_reason: closeAction.reason
});
rm -rf is-outdated.txt outdated.txt latest.txt is-very-outdated.txt
- name: Generate comment text with Sentry Link
if: github.event.label.name == 'crash' && fromJson(steps.add-labels.outputs.close-action).close != true
if: github.event.label.name == 'crash'
# ignore if fail
continue-on-error: true
id: generate-comment-text
@@ -195,17 +163,8 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
labels: ${{ steps.add-labels.outputs.labels }}
- name: Comment outdated (standalone executable)
if: steps.add-labels.outputs.is-outdated == 'true' && steps.add-labels.outputs.is-standalone == 'true' && github.event.label.name == 'crash' && steps.generate-comment-text.outputs.sentry-link == ''
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment"
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
@${{ github.event.issue.user.login }}, the latest version of Bun is v${{ steps.add-labels.outputs.latest }}, but the standalone executable is running Bun v${{ steps.add-labels.outputs.outdated }}. When the CLI using Bun's single-file executable next updates it might be fixed.
- name: Comment outdated
if: steps.add-labels.outputs.is-outdated == 'true' && steps.add-labels.outputs.is-standalone != 'true' && github.event.label.name == 'crash' && steps.generate-comment-text.outputs.sentry-link == ''
if: steps.add-labels.outputs.is-outdated == 'true' && github.event.label.name == 'crash' && steps.generate-comment-text.outputs.sentry-link == ''
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment"
@@ -219,22 +178,8 @@ jobs:
```sh
bun upgrade
```
- name: Comment with Sentry Link and outdated version (standalone executable)
if: steps.generate-comment-text.outputs.sentry-link != '' && github.event.label.name == 'crash' && steps.add-labels.outputs.is-outdated == 'true' && steps.add-labels.outputs.is-standalone == 'true'
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment"
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
@${{ github.event.issue.user.login }}, thank you for reporting this crash. The latest version of Bun is v${{ steps.add-labels.outputs.latest }}, but the standalone executable is running Bun v${{ steps.add-labels.outputs.outdated }}. When the CLI using Bun's single-file executable next updates it might be fixed.
For Bun's internal tracking, this issue is [${{ steps.generate-comment-text.outputs.sentry-id }}](${{ steps.generate-comment-text.outputs.sentry-link }}).
<!-- sentry-id: ${{ steps.generate-comment-text.outputs.sentry-id }} -->
<!-- sentry-link: ${{ steps.generate-comment-text.outputs.sentry-link }} -->
- name: Comment with Sentry Link and outdated version
if: steps.generate-comment-text.outputs.sentry-link != '' && github.event.label.name == 'crash' && steps.add-labels.outputs.is-outdated == 'true' && steps.add-labels.outputs.is-standalone != 'true'
if: steps.generate-comment-text.outputs.sentry-link != '' && github.event.label.name == 'crash' && steps.add-labels.outputs.is-outdated == 'true'
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment"

View File

@@ -0,0 +1,89 @@
name: Comment on updated submodule
on:
pull_request_target:
paths:
- "src/generated_versions_list.zig"
- ".github/workflows/on-submodule-update.yml"
jobs:
comment:
name: Comment
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'oven-sh' }}
permissions:
contents: read
pull-requests: write
issues: write
steps:
- name: Checkout current
uses: actions/checkout@v4
with:
sparse-checkout: |
src
- name: Hash generated versions list
id: hash
run: |
echo "hash=$(sha256sum src/generated_versions_list.zig | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
- name: Checkout base
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
sparse-checkout: |
src
- name: Hash base
id: base
run: |
echo "base=$(sha256sum src/generated_versions_list.zig | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
- name: Compare
id: compare
run: |
if [ "${{ steps.hash.outputs.hash }}" != "${{ steps.base.outputs.base }}" ]; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
- name: Find Comment
id: comment
uses: peter-evans/find-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: github-actions[bot]
body-includes: <!-- generated-comment submodule-updated -->
- name: Write Warning Comment
uses: peter-evans/create-or-update-comment@v4
if: steps.compare.outputs.changed == 'true'
with:
comment-id: ${{ steps.comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
edit-mode: replace
body: |
⚠️ **Warning:** @${{ github.actor }}, this PR has changes to submodule versions.
If this change was intentional, please ignore this message. If not, please undo changes to submodules and rebase your branch.
<!-- generated-comment submodule-updated -->
- name: Add labels
uses: actions-cool/issues-helper@v3
if: steps.compare.outputs.changed == 'true'
with:
actions: "add-labels"
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
labels: "changed-submodules"
- name: Remove labels
uses: actions-cool/issues-helper@v3
if: steps.compare.outputs.changed == 'false'
with:
actions: "remove-labels"
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
labels: "changed-submodules"
- name: Delete outdated comment
uses: actions-cool/issues-helper@v3
if: steps.compare.outputs.changed == 'false' && steps.comment.outputs.comment-id != ''
with:
actions: "delete-comment"
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.comment.outputs.comment-id }}

View File

@@ -80,7 +80,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -55,7 +55,7 @@ jobs:
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
exit 1
fi
# Try to get commit SHA from tag object (for annotated tags)
# If it fails, assume it's a lightweight tag pointing directly to commit
LATEST_SHA=$(curl -sL "https://api.github.com/repos/HdrHistogram/HdrHistogram_c/git/tags/$LATEST_TAG_SHA" 2>/dev/null | jq -r '.object.sha // empty')
@@ -83,7 +83,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -58,7 +58,7 @@ jobs:
TAG_OBJECT_SHA=$(echo "$TAG_REF" | jq -r '.object.sha')
TAG_OBJECT_TYPE=$(echo "$TAG_REF" | jq -r '.object.type')
if [ -z "$TAG_OBJECT_SHA" ] || [ "$TAG_OBJECT_SHA" = "null" ]; then
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
exit 1
@@ -99,7 +99,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -80,7 +80,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -80,7 +80,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -55,12 +55,12 @@ jobs:
TAG_REF_RESPONSE=$(curl -sL "https://api.github.com/repos/cloudflare/lol-html/git/refs/tags/$LATEST_TAG")
LATEST_TAG_SHA=$(echo "$TAG_REF_RESPONSE" | jq -r '.object.sha')
TAG_OBJECT_TYPE=$(echo "$TAG_REF_RESPONSE" | jq -r '.object.type')
if [ -z "$LATEST_TAG_SHA" ] || [ "$LATEST_TAG_SHA" = "null" ]; then
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
exit 1
fi
if [ "$TAG_OBJECT_TYPE" = "tag" ]; then
# This is an annotated tag, we need to get the commit it points to
LATEST_SHA=$(curl -sL "https://api.github.com/repos/cloudflare/lol-html/git/tags/$LATEST_TAG_SHA" | jq -r '.object.sha')
@@ -92,7 +92,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -59,7 +59,7 @@ jobs:
LATEST_TAG_SHA=$(echo "$TAG_REF" | jq -r '.object.sha')
TAG_TYPE=$(echo "$TAG_REF" | jq -r '.object.type')
if [ -z "$LATEST_TAG_SHA" ] || [ "$LATEST_TAG_SHA" = "null" ]; then
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
exit 1
@@ -97,7 +97,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -91,7 +91,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -1,79 +0,0 @@
name: Update vendor
on:
schedule:
- cron: "0 4 * * 0"
workflow_dispatch:
jobs:
check-update:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
strategy:
matrix:
package:
- elysia
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Check version
id: check-version
run: |
set -euo pipefail
# Extract the commit hash from the line after COMMIT
current=$(bun -p '(await Bun.file("test/vendor.json").json()).filter(v=>v.package===process.argv[1])[0].tag' ${{ matrix.package }})
repository=$(bun -p '(await Bun.file("test/vendor.json").json()).filter(v=>v.package===process.argv[1])[0].repository' ${{ matrix.package }} | cut -d'/' -f4,5)
if [ -z "$current" ]; then
echo "Error: Could not find COMMIT line in test/vendor.json"
exit 1
fi
echo "current=$current" >> $GITHUB_OUTPUT
echo "repository=$repository" >> $GITHUB_OUTPUT
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/${repository}/releases/latest)
if [ -z "$LATEST_RELEASE" ]; then
echo "Error: Failed to fetch latest release from GitHub API"
exit 1
fi
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
echo "Error: Could not extract tag name from GitHub API response"
exit 1
fi
echo "latest=$LATEST_TAG" >> $GITHUB_OUTPUT
- name: Update version if needed
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
run: |
set -euo pipefail
bun -e 'await Bun.write("test/vendor.json", JSON.stringify((await Bun.file("test/vendor.json").json()).map(v=>{if(v.package===process.argv[1])v.tag=process.argv[2];return v;}), null, 2) + "\n")' ${{ matrix.package }} ${{ steps.check-version.outputs.latest }}
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |
test/vendor.json
commit-message: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }} (${{ steps.check-version.outputs.latest }})"
title: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }}"
delete-branch: true
branch: deps/update-${{ matrix.package }}-${{ github.run_number }}
body: |
## What does this PR do?
Updates ${{ matrix.package }} to version ${{ steps.check-version.outputs.latest }}
Compare: https://github.com/${{ steps.check-version.outputs.repository }}/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-vendor.yml)

View File

@@ -80,7 +80,7 @@ jobs:
- name: Create Pull Request
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
add-paths: |

View File

@@ -45,8 +45,3 @@ jobs:
env:
VSCE_PAT: ${{ secrets.VSCODE_EXTENSION }}
working-directory: packages/bun-vscode/extension
- uses: actions/upload-artifact@v4
with:
name: bun-vscode-${{ github.event.inputs.version }}.vsix
path: packages/bun-vscode/extension/bun-vscode-${{ github.event.inputs.version }}.vsix

11
.vscode/launch.json generated vendored
View File

@@ -25,9 +25,6 @@
// "BUN_JSC_validateExceptionChecks": "1",
// "BUN_JSC_dumpSimulatedThrows": "1",
// "BUN_JSC_unexpectedExceptionStackTraceLimit": "20",
// "BUN_DESTRUCT_VM_ON_EXIT": "1",
// "ASAN_OPTIONS": "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=1:abort_on_error=1",
// "LSAN_OPTIONS": "malloc_context_size=100:print_suppressions=1:suppressions=${workspaceFolder}/test/leaksan.supp",
},
"console": "internalConsole",
"sourceMap": {
@@ -60,17 +57,11 @@
"name": "bun run [file]",
"program": "${workspaceFolder}/build/debug/bun-debug",
"args": ["${file}"],
"cwd": "${workspaceFolder}",
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "0",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
// "BUN_JSC_validateExceptionChecks": "1",
// "BUN_JSC_dumpSimulatedThrows": "1",
// "BUN_JSC_unexpectedExceptionStackTraceLimit": "20",
// "BUN_DESTRUCT_VM_ON_EXIT": "1",
// "ASAN_OPTIONS": "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=1:abort_on_error=1",
// "LSAN_OPTIONS": "malloc_context_size=100:print_suppressions=1:suppressions=${workspaceFolder}/test/leaksan.supp",
},
"console": "internalConsole",
"sourceMap": {

View File

@@ -4,14 +4,18 @@ This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed
### Build Commands
- **Build Bun**: `bun bd`
- **Build debug version**: `bun bd`
- Creates a debug build at `./build/debug/bun-debug`
- **CRITICAL**: no need for a timeout, the build is really fast!
- **CRITICAL**: DO NOT set a build timeout. Compilation takes ~5 minutes. Be patient.
- **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>`
Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.json script.
### Other Build Variants
- `bun run build:release` - Release build
Address sanitizer is enabled by default in debug builds of Bun.
## Testing

View File

@@ -31,11 +31,6 @@ include(SetupCcache)
parse_package_json(VERSION_VARIABLE DEFAULT_VERSION)
optionx(VERSION STRING "The version of Bun" DEFAULT ${DEFAULT_VERSION})
project(Bun VERSION ${VERSION})
# Bun uses C++23, which is compatible with BoringSSL's C++17 requirement
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(Options)
include(CompilerFlags)
@@ -48,9 +43,6 @@ include(SetupEsbuild)
include(SetupZig)
include(SetupRust)
# Generate dependency versions header
include(GenerateDependencyVersions)
# --- Targets ---
include(BuildBun)

View File

@@ -21,7 +21,7 @@ $ sudo pacman -S base-devel ccache cmake git go libiconv libtool make ninja pkg-
```
```bash#Fedora
$ sudo dnf install cargo clang19 llvm19 lld19 ccache cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
$ sudo dnf install cargo ccache cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
```
```bash#openSUSE Tumbleweed

2
LATEST
View File

@@ -1 +1 @@
1.2.22
1.2.21

View File

@@ -1,6 +1,6 @@
const isBun = typeof globalThis?.Bun?.sql !== "undefined";
import postgres from "postgres";
const sql = isBun ? Bun.sql : postgres();
const sql = isBun ? Bun.sql : postgres;
// Create the table if it doesn't exist
await sql`

View File

@@ -48,7 +48,6 @@ const BunBuildOptions = struct {
/// enable debug logs in release builds
enable_logs: bool = false,
enable_asan: bool,
enable_valgrind: bool,
tracy_callstack_depth: u16,
reported_nodejs_version: Version,
/// To make iterating on some '@embedFile's faster, we load them at runtime
@@ -95,7 +94,6 @@ const BunBuildOptions = struct {
opts.addOption(bool, "baseline", this.isBaseline());
opts.addOption(bool, "enable_logs", this.enable_logs);
opts.addOption(bool, "enable_asan", this.enable_asan);
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
opts.addOption(bool, "override_no_export_cpp_apis", this.override_no_export_cpp_apis);
@@ -215,21 +213,26 @@ pub fn build(b: *Build) !void {
var build_options = BunBuildOptions{
.target = target,
.optimize = optimize,
.os = os,
.arch = arch,
.codegen_path = codegen_path,
.codegen_embed = codegen_embed,
.no_llvm = no_llvm,
.override_no_export_cpp_apis = override_no_export_cpp_apis,
.version = try Version.parse(bun_version),
.canary_revision = canary: {
const rev = b.option(u32, "canary", "Treat this as a canary build") orelse 0;
break :canary if (rev == 0) null else rev;
},
.reported_nodejs_version = try Version.parse(
b.option([]const u8, "reported_nodejs_version", "Reported Node.js version") orelse
"0.0.0-unset",
),
.sha = sha: {
const sha_buildoption = b.option([]const u8, "sha", "Force the git sha");
const sha_github = b.graph.env_map.get("GITHUB_SHA");
@@ -265,10 +268,10 @@ pub fn build(b: *Build) !void {
break :sha sha;
},
.tracy_callstack_depth = b.option(u16, "tracy_callstack_depth", "") orelse 10,
.enable_logs = b.option(bool, "enable_logs", "Enable logs in release") orelse false,
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
};
// zig build obj
@@ -497,7 +500,6 @@ fn addMultiCheck(
.codegen_path = root_build_options.codegen_path,
.no_llvm = root_build_options.no_llvm,
.enable_asan = root_build_options.enable_asan,
.enable_valgrind = root_build_options.enable_valgrind,
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
};
@@ -585,15 +587,9 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
.root_module = root,
});
configureObj(b, opts, obj);
if (enableFastBuild(b)) obj.root_module.strip = true;
return obj;
}
fn enableFastBuild(b: *Build) bool {
const val = b.graph.env_map.get("BUN_BUILD_FAST") orelse return false;
return std.mem.eql(u8, val, "1");
}
fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
// Flags on root module get used for the compilation
obj.root_module.omit_frame_pointer = false;
@@ -604,7 +600,7 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
// Object options
obj.use_llvm = !opts.no_llvm;
obj.use_lld = if (opts.os == .mac) false else !opts.no_llvm;
if (opts.enable_asan and !enableFastBuild(b)) {
if (opts.enable_asan) {
if (@hasField(Build.Module, "sanitize_address")) {
obj.root_module.sanitize_address = true;
} else {
@@ -634,7 +630,7 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
obj.link_function_sections = true;
obj.link_data_sections = true;
if (opts.optimize == .Debug and opts.enable_valgrind) {
if (opts.optimize == .Debug) {
obj.root_module.valgrind = true;
}
}
@@ -743,7 +739,6 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
.{ .file = "node-fallbacks/url.js", .enable = opts.shouldEmbedCode() },
.{ .file = "node-fallbacks/util.js", .enable = opts.shouldEmbedCode() },
.{ .file = "node-fallbacks/zlib.js", .enable = opts.shouldEmbedCode() },
.{ .file = "eval/feedback.ts", .enable = opts.shouldEmbedCode() },
}) |entry| {
if (!@hasField(@TypeOf(entry), "enable") or entry.enable) {
const path = b.pathJoin(&.{ opts.codegen_path, entry.file });

View File

@@ -60,10 +60,10 @@ endif()
# Windows Code Signing Option
if(WIN32)
optionx(ENABLE_WINDOWS_CODESIGNING BOOL "Enable Windows code signing with DigiCert KeyLocker" DEFAULT OFF)
if(ENABLE_WINDOWS_CODESIGNING)
message(STATUS "Windows code signing: ENABLED")
# Check for required environment variables
if(NOT DEFINED ENV{SM_API_KEY})
message(WARNING "SM_API_KEY not set - code signing may fail")
@@ -114,10 +114,8 @@ endif()
if(DEBUG AND ((APPLE AND ARCH STREQUAL "aarch64") OR LINUX))
set(DEFAULT_ASAN ON)
set(DEFAULT_VALGRIND OFF)
else()
set(DEFAULT_ASAN OFF)
set(DEFAULT_VALGRIND OFF)
endif()
optionx(ENABLE_ASAN BOOL "If ASAN support should be enabled" DEFAULT ${DEFAULT_ASAN})

View File

@@ -13,10 +13,7 @@
},
{
"output": "JavaScriptSources.txt",
"paths": [
"src/js/**/*.{js,ts}",
"src/install/PackageManager/scanner-entry.ts"
]
"paths": ["src/js/**/*.{js,ts}"]
},
{
"output": "JavaScriptCodegenSources.txt",

View File

@@ -0,0 +1,506 @@
packages/bun-usockets/src/crypto/root_certs.cpp
packages/bun-usockets/src/crypto/sni_tree.cpp
src/bake/BakeGlobalObject.cpp
src/bake/BakeProduction.cpp
src/bake/BakeSourceProvider.cpp
src/bun.js/bindings/ActiveDOMCallback.cpp
src/bun.js/bindings/AsymmetricKeyValue.cpp
src/bun.js/bindings/AsyncContextFrame.cpp
src/bun.js/bindings/Base64Helpers.cpp
src/bun.js/bindings/bindings.cpp
src/bun.js/bindings/blob.cpp
src/bun.js/bindings/bun-simdutf.cpp
src/bun.js/bindings/bun-spawn.cpp
src/bun.js/bindings/BunClientData.cpp
src/bun.js/bindings/BunCommonStrings.cpp
src/bun.js/bindings/BunDebugger.cpp
src/bun.js/bindings/BunGCOutputConstraint.cpp
src/bun.js/bindings/BunGlobalScope.cpp
src/bun.js/bindings/BunHttp2CommonStrings.cpp
src/bun.js/bindings/BunInjectedScriptHost.cpp
src/bun.js/bindings/BunInspector.cpp
src/bun.js/bindings/BunJSCEventLoop.cpp
src/bun.js/bindings/BunObject.cpp
src/bun.js/bindings/BunPlugin.cpp
src/bun.js/bindings/BunProcess.cpp
src/bun.js/bindings/BunString.cpp
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
src/bun.js/bindings/CookieMap.cpp
src/bun.js/bindings/coroutine.cpp
src/bun.js/bindings/CPUFeatures.cpp
src/bun.js/bindings/decodeURIComponentSIMD.cpp
src/bun.js/bindings/DOMException.cpp
src/bun.js/bindings/DOMFormData.cpp
src/bun.js/bindings/DOMURL.cpp
src/bun.js/bindings/DOMWrapperWorld.cpp
src/bun.js/bindings/DoubleFormatter.cpp
src/bun.js/bindings/EncodeURIComponent.cpp
src/bun.js/bindings/EncodingTables.cpp
src/bun.js/bindings/ErrorCode.cpp
src/bun.js/bindings/ErrorStackFrame.cpp
src/bun.js/bindings/ErrorStackTrace.cpp
src/bun.js/bindings/EventLoopTaskNoContext.cpp
src/bun.js/bindings/ExposeNodeModuleGlobals.cpp
src/bun.js/bindings/ffi.cpp
src/bun.js/bindings/helpers.cpp
src/bun.js/bindings/highway_strings.cpp
src/bun.js/bindings/HTMLEntryPoint.cpp
src/bun.js/bindings/ImportMetaObject.cpp
src/bun.js/bindings/inlines.cpp
src/bun.js/bindings/InspectorBunFrontendDevServerAgent.cpp
src/bun.js/bindings/InspectorHTTPServerAgent.cpp
src/bun.js/bindings/InspectorLifecycleAgent.cpp
src/bun.js/bindings/InspectorTestReporterAgent.cpp
src/bun.js/bindings/InternalForTesting.cpp
src/bun.js/bindings/InternalModuleRegistry.cpp
src/bun.js/bindings/IPC.cpp
src/bun.js/bindings/isBuiltinModule.cpp
src/bun.js/bindings/JS2Native.cpp
src/bun.js/bindings/JSBigIntBinding.cpp
src/bun.js/bindings/JSBuffer.cpp
src/bun.js/bindings/JSBufferEncodingType.cpp
src/bun.js/bindings/JSBufferList.cpp
src/bun.js/bindings/JSBundlerPlugin.cpp
src/bun.js/bindings/JSBunRequest.cpp
src/bun.js/bindings/JSCommonJSExtensions.cpp
src/bun.js/bindings/JSCommonJSModule.cpp
src/bun.js/bindings/JSCTaskScheduler.cpp
src/bun.js/bindings/JSCTestingHelpers.cpp
src/bun.js/bindings/JSDOMExceptionHandling.cpp
src/bun.js/bindings/JSDOMFile.cpp
src/bun.js/bindings/JSDOMGlobalObject.cpp
src/bun.js/bindings/JSDOMWrapper.cpp
src/bun.js/bindings/JSDOMWrapperCache.cpp
src/bun.js/bindings/JSEnvironmentVariableMap.cpp
src/bun.js/bindings/JSFFIFunction.cpp
src/bun.js/bindings/JSMockFunction.cpp
src/bun.js/bindings/JSNextTickQueue.cpp
src/bun.js/bindings/JSNodePerformanceHooksHistogram.cpp
src/bun.js/bindings/JSNodePerformanceHooksHistogramConstructor.cpp
src/bun.js/bindings/JSNodePerformanceHooksHistogramPrototype.cpp
src/bun.js/bindings/JSPropertyIterator.cpp
src/bun.js/bindings/JSS3File.cpp
src/bun.js/bindings/JSSecrets.cpp
src/bun.js/bindings/JSSocketAddressDTO.cpp
src/bun.js/bindings/JSStringDecoder.cpp
src/bun.js/bindings/JSWrappingFunction.cpp
src/bun.js/bindings/JSX509Certificate.cpp
src/bun.js/bindings/JSX509CertificateConstructor.cpp
src/bun.js/bindings/JSX509CertificatePrototype.cpp
src/bun.js/bindings/linux_perf_tracing.cpp
src/bun.js/bindings/MarkedArgumentBufferBinding.cpp
src/bun.js/bindings/MarkingConstraint.cpp
src/bun.js/bindings/ModuleLoader.cpp
src/bun.js/bindings/napi_external.cpp
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
src/bun.js/bindings/node/crypto/CryptoGenDhKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoGenDsaKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoGenEcKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoGenKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoGenNidKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoGenRsaKeyPair.cpp
src/bun.js/bindings/node/crypto/CryptoHkdf.cpp
src/bun.js/bindings/node/crypto/CryptoKeygen.cpp
src/bun.js/bindings/node/crypto/CryptoKeys.cpp
src/bun.js/bindings/node/crypto/CryptoPrimes.cpp
src/bun.js/bindings/node/crypto/CryptoSignJob.cpp
src/bun.js/bindings/node/crypto/CryptoUtil.cpp
src/bun.js/bindings/node/crypto/JSCipher.cpp
src/bun.js/bindings/node/crypto/JSCipherConstructor.cpp
src/bun.js/bindings/node/crypto/JSCipherPrototype.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellman.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellmanConstructor.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellmanGroup.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellmanGroupConstructor.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellmanGroupPrototype.cpp
src/bun.js/bindings/node/crypto/JSDiffieHellmanPrototype.cpp
src/bun.js/bindings/node/crypto/JSECDH.cpp
src/bun.js/bindings/node/crypto/JSECDHConstructor.cpp
src/bun.js/bindings/node/crypto/JSECDHPrototype.cpp
src/bun.js/bindings/node/crypto/JSHash.cpp
src/bun.js/bindings/node/crypto/JSHmac.cpp
src/bun.js/bindings/node/crypto/JSKeyObject.cpp
src/bun.js/bindings/node/crypto/JSKeyObjectConstructor.cpp
src/bun.js/bindings/node/crypto/JSKeyObjectPrototype.cpp
src/bun.js/bindings/node/crypto/JSPrivateKeyObject.cpp
src/bun.js/bindings/node/crypto/JSPrivateKeyObjectConstructor.cpp
src/bun.js/bindings/node/crypto/JSPrivateKeyObjectPrototype.cpp
src/bun.js/bindings/node/crypto/JSPublicKeyObject.cpp
src/bun.js/bindings/node/crypto/JSPublicKeyObjectConstructor.cpp
src/bun.js/bindings/node/crypto/JSPublicKeyObjectPrototype.cpp
src/bun.js/bindings/node/crypto/JSSecretKeyObject.cpp
src/bun.js/bindings/node/crypto/JSSecretKeyObjectConstructor.cpp
src/bun.js/bindings/node/crypto/JSSecretKeyObjectPrototype.cpp
src/bun.js/bindings/node/crypto/JSSign.cpp
src/bun.js/bindings/node/crypto/JSVerify.cpp
src/bun.js/bindings/node/crypto/KeyObject.cpp
src/bun.js/bindings/node/crypto/node_crypto_binding.cpp
src/bun.js/bindings/node/http/JSConnectionsList.cpp
src/bun.js/bindings/node/http/JSConnectionsListConstructor.cpp
src/bun.js/bindings/node/http/JSConnectionsListPrototype.cpp
src/bun.js/bindings/node/http/JSHTTPParser.cpp
src/bun.js/bindings/node/http/JSHTTPParserConstructor.cpp
src/bun.js/bindings/node/http/JSHTTPParserPrototype.cpp
src/bun.js/bindings/node/http/NodeHTTPParser.cpp
src/bun.js/bindings/node/NodeTimers.cpp
src/bun.js/bindings/NodeAsyncHooks.cpp
src/bun.js/bindings/NodeDirent.cpp
src/bun.js/bindings/NodeFetch.cpp
src/bun.js/bindings/NodeFSStatBinding.cpp
src/bun.js/bindings/NodeFSStatFSBinding.cpp
src/bun.js/bindings/NodeHTTP.cpp
src/bun.js/bindings/NodeTimerObject.cpp
src/bun.js/bindings/NodeTLS.cpp
src/bun.js/bindings/NodeURL.cpp
src/bun.js/bindings/NodeValidator.cpp
src/bun.js/bindings/NodeVM.cpp
src/bun.js/bindings/NodeVMModule.cpp
src/bun.js/bindings/NodeVMScript.cpp
src/bun.js/bindings/NodeVMSourceTextModule.cpp
src/bun.js/bindings/NodeVMSyntheticModule.cpp
src/bun.js/bindings/NoOpForTesting.cpp
src/bun.js/bindings/ObjectBindings.cpp
src/bun.js/bindings/objects.cpp
src/bun.js/bindings/OsBinding.cpp
src/bun.js/bindings/Path.cpp
src/bun.js/bindings/ProcessBindingBuffer.cpp
src/bun.js/bindings/ProcessBindingConstants.cpp
src/bun.js/bindings/ProcessBindingFs.cpp
src/bun.js/bindings/ProcessBindingHTTPParser.cpp
src/bun.js/bindings/ProcessBindingNatives.cpp
src/bun.js/bindings/ProcessBindingTTYWrap.cpp
src/bun.js/bindings/ProcessBindingUV.cpp
src/bun.js/bindings/ProcessIdentifier.cpp
src/bun.js/bindings/RegularExpression.cpp
src/bun.js/bindings/S3Error.cpp
src/bun.js/bindings/ScriptExecutionContext.cpp
src/bun.js/bindings/SecretsDarwin.cpp
src/bun.js/bindings/SecretsLinux.cpp
src/bun.js/bindings/SecretsWindows.cpp
src/bun.js/bindings/Serialization.cpp
src/bun.js/bindings/ServerRouteList.cpp
src/bun.js/bindings/spawn.cpp
src/bun.js/bindings/SQLClient.cpp
src/bun.js/bindings/sqlite/JSSQLStatement.cpp
src/bun.js/bindings/StringBuilderBinding.cpp
src/bun.js/bindings/stripANSI.cpp
src/bun.js/bindings/Strong.cpp
src/bun.js/bindings/TextCodec.cpp
src/bun.js/bindings/TextCodecCJK.cpp
src/bun.js/bindings/TextCodecReplacement.cpp
src/bun.js/bindings/TextCodecSingleByte.cpp
src/bun.js/bindings/TextCodecUserDefined.cpp
src/bun.js/bindings/TextCodecWrapper.cpp
src/bun.js/bindings/TextEncoding.cpp
src/bun.js/bindings/TextEncodingRegistry.cpp
src/bun.js/bindings/Uint8Array.cpp
src/bun.js/bindings/Undici.cpp
src/bun.js/bindings/URLDecomposition.cpp
src/bun.js/bindings/URLSearchParams.cpp
src/bun.js/bindings/UtilInspect.cpp
src/bun.js/bindings/v8/node.cpp
src/bun.js/bindings/v8/shim/Function.cpp
src/bun.js/bindings/v8/shim/FunctionTemplate.cpp
src/bun.js/bindings/v8/shim/GlobalInternals.cpp
src/bun.js/bindings/v8/shim/Handle.cpp
src/bun.js/bindings/v8/shim/HandleScopeBuffer.cpp
src/bun.js/bindings/v8/shim/InternalFieldObject.cpp
src/bun.js/bindings/v8/shim/Map.cpp
src/bun.js/bindings/v8/shim/ObjectTemplate.cpp
src/bun.js/bindings/v8/shim/Oddball.cpp
src/bun.js/bindings/v8/shim/TaggedPointer.cpp
src/bun.js/bindings/v8/v8_api_internal.cpp
src/bun.js/bindings/v8/v8_internal.cpp
src/bun.js/bindings/v8/V8Array.cpp
src/bun.js/bindings/v8/V8Boolean.cpp
src/bun.js/bindings/v8/V8Context.cpp
src/bun.js/bindings/v8/V8EscapableHandleScope.cpp
src/bun.js/bindings/v8/V8EscapableHandleScopeBase.cpp
src/bun.js/bindings/v8/V8External.cpp
src/bun.js/bindings/v8/V8Function.cpp
src/bun.js/bindings/v8/V8FunctionCallbackInfo.cpp
src/bun.js/bindings/v8/V8FunctionTemplate.cpp
src/bun.js/bindings/v8/V8HandleScope.cpp
src/bun.js/bindings/v8/V8Isolate.cpp
src/bun.js/bindings/v8/V8Local.cpp
src/bun.js/bindings/v8/V8Maybe.cpp
src/bun.js/bindings/v8/V8Number.cpp
src/bun.js/bindings/v8/V8Object.cpp
src/bun.js/bindings/v8/V8ObjectTemplate.cpp
src/bun.js/bindings/v8/V8String.cpp
src/bun.js/bindings/v8/V8Template.cpp
src/bun.js/bindings/v8/V8Value.cpp
src/bun.js/bindings/Weak.cpp
src/bun.js/bindings/webcore/AbortController.cpp
src/bun.js/bindings/webcore/AbortSignal.cpp
src/bun.js/bindings/webcore/ActiveDOMObject.cpp
src/bun.js/bindings/webcore/BroadcastChannel.cpp
src/bun.js/bindings/webcore/BunBroadcastChannelRegistry.cpp
src/bun.js/bindings/webcore/CloseEvent.cpp
src/bun.js/bindings/webcore/CommonAtomStrings.cpp
src/bun.js/bindings/webcore/ContextDestructionObserver.cpp
src/bun.js/bindings/webcore/CustomEvent.cpp
src/bun.js/bindings/webcore/CustomEventCustom.cpp
src/bun.js/bindings/webcore/DOMJITHelpers.cpp
src/bun.js/bindings/webcore/ErrorCallback.cpp
src/bun.js/bindings/webcore/ErrorEvent.cpp
src/bun.js/bindings/webcore/Event.cpp
src/bun.js/bindings/webcore/EventContext.cpp
src/bun.js/bindings/webcore/EventDispatcher.cpp
src/bun.js/bindings/webcore/EventEmitter.cpp
src/bun.js/bindings/webcore/EventFactory.cpp
src/bun.js/bindings/webcore/EventListenerMap.cpp
src/bun.js/bindings/webcore/EventNames.cpp
src/bun.js/bindings/webcore/EventPath.cpp
src/bun.js/bindings/webcore/EventTarget.cpp
src/bun.js/bindings/webcore/EventTargetConcrete.cpp
src/bun.js/bindings/webcore/EventTargetFactory.cpp
src/bun.js/bindings/webcore/FetchHeaders.cpp
src/bun.js/bindings/webcore/HeaderFieldTokenizer.cpp
src/bun.js/bindings/webcore/HTTPHeaderField.cpp
src/bun.js/bindings/webcore/HTTPHeaderIdentifiers.cpp
src/bun.js/bindings/webcore/HTTPHeaderMap.cpp
src/bun.js/bindings/webcore/HTTPHeaderNames.cpp
src/bun.js/bindings/webcore/HTTPHeaderStrings.cpp
src/bun.js/bindings/webcore/HTTPHeaderValues.cpp
src/bun.js/bindings/webcore/HTTPParsers.cpp
src/bun.js/bindings/webcore/IdentifierEventListenerMap.cpp
src/bun.js/bindings/webcore/InternalWritableStream.cpp
src/bun.js/bindings/webcore/JSAbortAlgorithm.cpp
src/bun.js/bindings/webcore/JSAbortController.cpp
src/bun.js/bindings/webcore/JSAbortSignal.cpp
src/bun.js/bindings/webcore/JSAbortSignalCustom.cpp
src/bun.js/bindings/webcore/JSAddEventListenerOptions.cpp
src/bun.js/bindings/webcore/JSBroadcastChannel.cpp
src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.cpp
src/bun.js/bindings/webcore/JSCallbackData.cpp
src/bun.js/bindings/webcore/JSCloseEvent.cpp
src/bun.js/bindings/webcore/JSCookie.cpp
src/bun.js/bindings/webcore/JSCookieMap.cpp
src/bun.js/bindings/webcore/JSCountQueuingStrategy.cpp
src/bun.js/bindings/webcore/JSCustomEvent.cpp
src/bun.js/bindings/webcore/JSDOMBindingInternalsBuiltins.cpp
src/bun.js/bindings/webcore/JSDOMBuiltinConstructorBase.cpp
src/bun.js/bindings/webcore/JSDOMConstructorBase.cpp
src/bun.js/bindings/webcore/JSDOMConvertDate.cpp
src/bun.js/bindings/webcore/JSDOMConvertNumbers.cpp
src/bun.js/bindings/webcore/JSDOMConvertStrings.cpp
src/bun.js/bindings/webcore/JSDOMConvertWebGL.cpp
src/bun.js/bindings/webcore/JSDOMException.cpp
src/bun.js/bindings/webcore/JSDOMFormData.cpp
src/bun.js/bindings/webcore/JSDOMGuardedObject.cpp
src/bun.js/bindings/webcore/JSDOMIterator.cpp
src/bun.js/bindings/webcore/JSDOMOperation.cpp
src/bun.js/bindings/webcore/JSDOMPromise.cpp
src/bun.js/bindings/webcore/JSDOMPromiseDeferred.cpp
src/bun.js/bindings/webcore/JSDOMURL.cpp
src/bun.js/bindings/webcore/JSErrorCallback.cpp
src/bun.js/bindings/webcore/JSErrorEvent.cpp
src/bun.js/bindings/webcore/JSErrorEventCustom.cpp
src/bun.js/bindings/webcore/JSErrorHandler.cpp
src/bun.js/bindings/webcore/JSEvent.cpp
src/bun.js/bindings/webcore/JSEventCustom.cpp
src/bun.js/bindings/webcore/JSEventDOMJIT.cpp
src/bun.js/bindings/webcore/JSEventEmitter.cpp
src/bun.js/bindings/webcore/JSEventEmitterCustom.cpp
src/bun.js/bindings/webcore/JSEventInit.cpp
src/bun.js/bindings/webcore/JSEventListener.cpp
src/bun.js/bindings/webcore/JSEventListenerOptions.cpp
src/bun.js/bindings/webcore/JSEventModifierInit.cpp
src/bun.js/bindings/webcore/JSEventTarget.cpp
src/bun.js/bindings/webcore/JSEventTargetCustom.cpp
src/bun.js/bindings/webcore/JSEventTargetNode.cpp
src/bun.js/bindings/webcore/JSFetchHeaders.cpp
src/bun.js/bindings/webcore/JSMessageChannel.cpp
src/bun.js/bindings/webcore/JSMessageChannelCustom.cpp
src/bun.js/bindings/webcore/JSMessageEvent.cpp
src/bun.js/bindings/webcore/JSMessageEventCustom.cpp
src/bun.js/bindings/webcore/JSMessagePort.cpp
src/bun.js/bindings/webcore/JSMessagePortCustom.cpp
src/bun.js/bindings/webcore/JSMIMEBindings.cpp
src/bun.js/bindings/webcore/JSMIMEParams.cpp
src/bun.js/bindings/webcore/JSMIMEType.cpp
src/bun.js/bindings/webcore/JSPerformance.cpp
src/bun.js/bindings/webcore/JSPerformanceEntry.cpp
src/bun.js/bindings/webcore/JSPerformanceEntryCustom.cpp
src/bun.js/bindings/webcore/JSPerformanceMark.cpp
src/bun.js/bindings/webcore/JSPerformanceMarkOptions.cpp
src/bun.js/bindings/webcore/JSPerformanceMeasure.cpp
src/bun.js/bindings/webcore/JSPerformanceMeasureOptions.cpp
src/bun.js/bindings/webcore/JSPerformanceObserver.cpp
src/bun.js/bindings/webcore/JSPerformanceObserverCallback.cpp
src/bun.js/bindings/webcore/JSPerformanceObserverCustom.cpp
src/bun.js/bindings/webcore/JSPerformanceObserverEntryList.cpp
src/bun.js/bindings/webcore/JSPerformanceResourceTiming.cpp
src/bun.js/bindings/webcore/JSPerformanceServerTiming.cpp
src/bun.js/bindings/webcore/JSPerformanceTiming.cpp
src/bun.js/bindings/webcore/JSReadableByteStreamController.cpp
src/bun.js/bindings/webcore/JSReadableStream.cpp
src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.cpp
src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.cpp
src/bun.js/bindings/webcore/JSReadableStreamDefaultController.cpp
src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.cpp
src/bun.js/bindings/webcore/JSReadableStreamSink.cpp
src/bun.js/bindings/webcore/JSReadableStreamSource.cpp
src/bun.js/bindings/webcore/JSReadableStreamSourceCustom.cpp
src/bun.js/bindings/webcore/JSStructuredSerializeOptions.cpp
src/bun.js/bindings/webcore/JSTextDecoderStream.cpp
src/bun.js/bindings/webcore/JSTextEncoder.cpp
src/bun.js/bindings/webcore/JSTextEncoderStream.cpp
src/bun.js/bindings/webcore/JSTransformStream.cpp
src/bun.js/bindings/webcore/JSTransformStreamDefaultController.cpp
src/bun.js/bindings/webcore/JSURLSearchParams.cpp
src/bun.js/bindings/webcore/JSWasmStreamingCompiler.cpp
src/bun.js/bindings/webcore/JSWebSocket.cpp
src/bun.js/bindings/webcore/JSWorker.cpp
src/bun.js/bindings/webcore/JSWorkerOptions.cpp
src/bun.js/bindings/webcore/JSWritableStream.cpp
src/bun.js/bindings/webcore/JSWritableStreamDefaultController.cpp
src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.cpp
src/bun.js/bindings/webcore/JSWritableStreamSink.cpp
src/bun.js/bindings/webcore/MessageChannel.cpp
src/bun.js/bindings/webcore/MessageEvent.cpp
src/bun.js/bindings/webcore/MessagePort.cpp
src/bun.js/bindings/webcore/MessagePortChannel.cpp
src/bun.js/bindings/webcore/MessagePortChannelProvider.cpp
src/bun.js/bindings/webcore/MessagePortChannelProviderImpl.cpp
src/bun.js/bindings/webcore/MessagePortChannelRegistry.cpp
src/bun.js/bindings/webcore/NetworkLoadMetrics.cpp
src/bun.js/bindings/webcore/Performance.cpp
src/bun.js/bindings/webcore/PerformanceEntry.cpp
src/bun.js/bindings/webcore/PerformanceMark.cpp
src/bun.js/bindings/webcore/PerformanceMeasure.cpp
src/bun.js/bindings/webcore/PerformanceObserver.cpp
src/bun.js/bindings/webcore/PerformanceObserverEntryList.cpp
src/bun.js/bindings/webcore/PerformanceResourceTiming.cpp
src/bun.js/bindings/webcore/PerformanceServerTiming.cpp
src/bun.js/bindings/webcore/PerformanceTiming.cpp
src/bun.js/bindings/webcore/PerformanceUserTiming.cpp
src/bun.js/bindings/webcore/ReadableStream.cpp
src/bun.js/bindings/webcore/ReadableStreamDefaultController.cpp
src/bun.js/bindings/webcore/ReadableStreamSink.cpp
src/bun.js/bindings/webcore/ReadableStreamSource.cpp
src/bun.js/bindings/webcore/ResourceTiming.cpp
src/bun.js/bindings/webcore/RFC7230.cpp
src/bun.js/bindings/webcore/SerializedScriptValue.cpp
src/bun.js/bindings/webcore/ServerTiming.cpp
src/bun.js/bindings/webcore/ServerTimingParser.cpp
src/bun.js/bindings/webcore/StructuredClone.cpp
src/bun.js/bindings/webcore/TextEncoder.cpp
src/bun.js/bindings/webcore/WebCoreTypedArrayController.cpp
src/bun.js/bindings/webcore/WebSocket.cpp
src/bun.js/bindings/webcore/Worker.cpp
src/bun.js/bindings/webcore/WritableStream.cpp
src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithm.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CBC.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CBCOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFB.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CFBOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTR.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_CTROpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCM.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_GCMOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_KW.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmAES_KWOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmECDH.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmECDHOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSA.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmECDSAOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDF.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmHKDFOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmHMAC.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmHMACOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmPBKDF2.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmPBKDF2OpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistry.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRegistryOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEP.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_OAEPOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSS.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSA_PSSOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSAES_PKCS1_v1_5.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSAES_PKCS1_v1_5OpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmRSASSA_PKCS1_v1_5OpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA1.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA224.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA256.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA384.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA512.cpp
src/bun.js/bindings/webcrypto/CryptoAlgorithmX25519.cpp
src/bun.js/bindings/webcrypto/CryptoDigest.cpp
src/bun.js/bindings/webcrypto/CryptoKey.cpp
src/bun.js/bindings/webcrypto/CryptoKeyAES.cpp
src/bun.js/bindings/webcrypto/CryptoKeyEC.cpp
src/bun.js/bindings/webcrypto/CryptoKeyECOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoKeyHMAC.cpp
src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
src/bun.js/bindings/webcrypto/CryptoKeyRaw.cpp
src/bun.js/bindings/webcrypto/CryptoKeyRSA.cpp
src/bun.js/bindings/webcrypto/CryptoKeyRSAComponents.cpp
src/bun.js/bindings/webcrypto/CryptoKeyRSAOpenSSL.cpp
src/bun.js/bindings/webcrypto/JSAesCbcCfbParams.cpp
src/bun.js/bindings/webcrypto/JSAesCtrParams.cpp
src/bun.js/bindings/webcrypto/JSAesGcmParams.cpp
src/bun.js/bindings/webcrypto/JSAesKeyParams.cpp
src/bun.js/bindings/webcrypto/JSCryptoAesKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSCryptoAlgorithmParameters.cpp
src/bun.js/bindings/webcrypto/JSCryptoEcKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSCryptoHmacKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSCryptoKey.cpp
src/bun.js/bindings/webcrypto/JSCryptoKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSCryptoKeyPair.cpp
src/bun.js/bindings/webcrypto/JSCryptoKeyUsage.cpp
src/bun.js/bindings/webcrypto/JSCryptoRsaHashedKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSCryptoRsaKeyAlgorithm.cpp
src/bun.js/bindings/webcrypto/JSEcdhKeyDeriveParams.cpp
src/bun.js/bindings/webcrypto/JSEcdsaParams.cpp
src/bun.js/bindings/webcrypto/JSEcKeyParams.cpp
src/bun.js/bindings/webcrypto/JSHkdfParams.cpp
src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp
src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp
src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp
src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp
src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp
src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp
src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp
src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp
src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp
src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp
src/bun.js/bindings/webcrypto/JSX25519Params.cpp
src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp
src/bun.js/bindings/webcrypto/PhonyWorkQueue.cpp
src/bun.js/bindings/webcrypto/SerializedCryptoKeyWrapOpenSSL.cpp
src/bun.js/bindings/webcrypto/SubtleCrypto.cpp
src/bun.js/bindings/workaround-missing-symbols.cpp
src/bun.js/bindings/wtf-bindings.cpp
src/bun.js/bindings/ZigGeneratedCode.cpp
src/bun.js/bindings/ZigGlobalObject.cpp
src/bun.js/bindings/ZigSourceProvider.cpp
src/bun.js/modules/NodeModuleModule.cpp
src/bun.js/modules/NodeTTYModule.cpp
src/bun.js/modules/NodeUtilTypesModule.cpp
src/bun.js/modules/ObjectModule.cpp
src/deps/libuwsockets.cpp
src/io/io_darwin.cpp
src/vm/Semaphore.cpp
src/vm/SigintWatcher.cpp

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
oven-sh/boringssl
COMMIT
f1ffd9e83d4f5c28a9c70d73f9a4e6fcf310062f
7a5d984c69b0c34c4cbb56c6812eaa5b9bef485c
)
register_cmake_command(

View File

@@ -2,8 +2,6 @@ include(PathUtils)
if(DEBUG)
set(bun bun-debug)
elseif(ENABLE_ASAN AND ENABLE_VALGRIND)
set(bun bun-asan-valgrind)
elseif(ENABLE_ASAN)
set(bun bun-asan)
elseif(ENABLE_VALGRIND)
@@ -621,7 +619,6 @@ register_command(
-Dcpu=${ZIG_CPU}
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
-Dversion=${VERSION}
-Dreported_nodejs_version=${NODEJS_VERSION}
-Dcanary=${CANARY_REVISION}
@@ -639,7 +636,6 @@ register_command(
SOURCES
${BUN_ZIG_SOURCES}
${BUN_ZIG_GENERATED_SOURCES}
${CWD}/src/install/PackageManager/scanner-entry.ts # Is there a better way to do this?
)
set_property(TARGET bun-zig PROPERTY JOB_POOL compile_pool)
@@ -889,8 +885,12 @@ if(NOT WIN32)
endif()
if(ENABLE_ASAN)
target_compile_options(${bun} PUBLIC -fsanitize=address)
target_link_libraries(${bun} PUBLIC -fsanitize=address)
target_compile_options(${bun} PUBLIC
-fsanitize=address
)
target_link_libraries(${bun} PUBLIC
-fsanitize=address
)
endif()
target_compile_options(${bun} PUBLIC
@@ -929,8 +929,12 @@ if(NOT WIN32)
)
if(ENABLE_ASAN)
target_compile_options(${bun} PUBLIC -fsanitize=address)
target_link_libraries(${bun} PUBLIC -fsanitize=address)
target_compile_options(${bun} PUBLIC
-fsanitize=address
)
target_link_libraries(${bun} PUBLIC
-fsanitize=address
)
endif()
endif()
else()
@@ -964,7 +968,6 @@ if(WIN32)
/delayload:WSOCK32.dll
/delayload:ADVAPI32.dll
/delayload:IPHLPAPI.dll
/delayload:CRYPT32.dll
)
endif()
endif()
@@ -1006,7 +1009,6 @@ if(LINUX)
-Wl,--wrap=exp2
-Wl,--wrap=expf
-Wl,--wrap=fcntl64
-Wl,--wrap=gettid
-Wl,--wrap=log
-Wl,--wrap=log2
-Wl,--wrap=log2f
@@ -1058,7 +1060,7 @@ if(LINUX)
)
endif()
if (NOT DEBUG AND NOT ENABLE_ASAN AND NOT ENABLE_VALGRIND)
if (NOT DEBUG AND NOT ENABLE_ASAN)
target_link_options(${bun} PUBLIC
-Wl,-icf=safe
)
@@ -1123,9 +1125,6 @@ endif()
include_directories(${WEBKIT_INCLUDE_PATH})
# Include the generated dependency versions header
include_directories(${CMAKE_BINARY_DIR})
if(NOT WEBKIT_LOCAL AND NOT APPLE)
include_directories(${WEBKIT_INCLUDE_PATH}/wtf/unicode)
endif()
@@ -1185,7 +1184,6 @@ if(WIN32)
ntdll
userenv
dbghelp
crypt32
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
delayimp.lib
)
@@ -1361,20 +1359,12 @@ if(NOT BUN_CPP_ONLY)
if(ENABLE_BASELINE)
set(bunTriplet ${bunTriplet}-baseline)
endif()
if (ENABLE_ASAN AND ENABLE_VALGRIND)
set(bunTriplet ${bunTriplet}-asan-valgrind)
set(bunPath ${bunTriplet})
elseif (ENABLE_VALGRIND)
set(bunTriplet ${bunTriplet}-valgrind)
set(bunPath ${bunTriplet})
elseif(ENABLE_ASAN)
if(ENABLE_ASAN)
set(bunTriplet ${bunTriplet}-asan)
set(bunPath ${bunTriplet})
else()
string(REPLACE bun ${bunTriplet} bunPath ${bun})
endif()
set(bunFiles ${bunExe} features.json)
if(WIN32)
list(APPEND bunFiles ${bun}.pdb)

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
HdrHistogram/HdrHistogram_c
COMMIT
be60a9987ee48d0abf0d7b6a175bad8d6c1585d1
8dcce8f68512fca460b171bccc3a5afce0048779
)
register_cmake_command(

View File

@@ -4,8 +4,7 @@ register_repository(
REPOSITORY
libuv/libuv
COMMIT
# Corresponds to v1.51.0
5152db2cbfeb5582e9c27c5ea1dba2cd9e10759b
da527d8d2a908b824def74382761566371439003
)
if(WIN32)

View File

@@ -1,220 +0,0 @@
# GenerateDependencyVersions.cmake
# Generates a header file with all dependency versions
# Function to extract version from git tree object
function(get_git_tree_hash dep_name output_var)
execute_process(
COMMAND git rev-parse HEAD:./src/deps/${dep_name}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE commit_hash
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE result
)
if(result EQUAL 0 AND commit_hash)
set(${output_var} "${commit_hash}" PARENT_SCOPE)
else()
set(${output_var} "unknown" PARENT_SCOPE)
endif()
endfunction()
# Function to extract version from header file using regex
function(extract_version_from_header header_file regex_pattern output_var)
if(EXISTS "${header_file}")
file(STRINGS "${header_file}" version_line REGEX "${regex_pattern}")
if(version_line)
string(REGEX MATCH "${regex_pattern}" _match "${version_line}")
if(CMAKE_MATCH_1)
set(${output_var} "${CMAKE_MATCH_1}" PARENT_SCOPE)
else()
set(${output_var} "unknown" PARENT_SCOPE)
endif()
else()
set(${output_var} "unknown" PARENT_SCOPE)
endif()
else()
set(${output_var} "unknown" PARENT_SCOPE)
endif()
endfunction()
# Main function to generate the header file
function(generate_dependency_versions_header)
set(DEPS_PATH "${CMAKE_SOURCE_DIR}/src/deps")
set(VENDOR_PATH "${CMAKE_SOURCE_DIR}/vendor")
# Initialize version variables
set(DEPENDENCY_VERSIONS "")
# WebKit version (from SetupWebKit.cmake or command line)
if(WEBKIT_VERSION)
set(WEBKIT_VERSION_STR "${WEBKIT_VERSION}")
else()
set(WEBKIT_VERSION_STR "0ddf6f47af0a9782a354f61e06d7f83d097d9f84")
endif()
list(APPEND DEPENDENCY_VERSIONS "WEBKIT" "${WEBKIT_VERSION_STR}")
# Track input files so CMake reconfigures when they change
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
"${CMAKE_SOURCE_DIR}/package.json"
"${VENDOR_PATH}/libdeflate/libdeflate.h"
"${VENDOR_PATH}/zlib/zlib.h"
"${DEPS_PATH}/zstd/lib/zstd.h"
)
# Hardcoded dependency versions (previously from generated_versions_list.zig)
# These are the commit hashes/tree objects for each dependency
list(APPEND DEPENDENCY_VERSIONS "BORINGSSL" "29a2cd359458c9384694b75456026e4b57e3e567")
list(APPEND DEPENDENCY_VERSIONS "C_ARES" "d1722e6e8acaf10eb73fa995798a9cd421d9f85e")
list(APPEND DEPENDENCY_VERSIONS "LIBARCHIVE" "898dc8319355b7e985f68a9819f182aaed61b53a")
list(APPEND DEPENDENCY_VERSIONS "LIBDEFLATE_HASH" "dc76454a39e7e83b68c3704b6e3784654f8d5ac5")
list(APPEND DEPENDENCY_VERSIONS "LOLHTML" "8d4c273ded322193d017042d1f48df2766b0f88b")
list(APPEND DEPENDENCY_VERSIONS "LSHPACK" "3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0")
list(APPEND DEPENDENCY_VERSIONS "MIMALLOC" "4c283af60cdae205df5a872530c77e2a6a307d43")
list(APPEND DEPENDENCY_VERSIONS "PICOHTTPPARSER" "066d2b1e9ab820703db0837a7255d92d30f0c9f5")
list(APPEND DEPENDENCY_VERSIONS "TINYCC" "ab631362d839333660a265d3084d8ff060b96753")
list(APPEND DEPENDENCY_VERSIONS "ZLIB_HASH" "886098f3f339617b4243b286f5ed364b9989e245")
list(APPEND DEPENDENCY_VERSIONS "ZSTD_HASH" "794ea1b0afca0f020f4e57b6732332231fb23c70")
# Extract semantic versions from header files where available
extract_version_from_header(
"${VENDOR_PATH}/libdeflate/libdeflate.h"
"#define LIBDEFLATE_VERSION_STRING[ \t]+\"([0-9\\.]+)\""
LIBDEFLATE_VERSION_STRING
)
list(APPEND DEPENDENCY_VERSIONS "LIBDEFLATE_VERSION" "${LIBDEFLATE_VERSION_STRING}")
extract_version_from_header(
"${VENDOR_PATH}/zlib/zlib.h"
"#define[ \t]+ZLIB_VERSION[ \t]+\"([^\"]+)\""
ZLIB_VERSION_STRING
)
list(APPEND DEPENDENCY_VERSIONS "ZLIB_VERSION" "${ZLIB_VERSION_STRING}")
extract_version_from_header(
"${DEPS_PATH}/zstd/lib/zstd.h"
"#define[ \t]+ZSTD_VERSION_STRING[ \t]+\"([^\"]+)\""
ZSTD_VERSION_STRING
)
list(APPEND DEPENDENCY_VERSIONS "ZSTD_VERSION" "${ZSTD_VERSION_STRING}")
# Bun version from package.json
if(EXISTS "${CMAKE_SOURCE_DIR}/package.json")
file(READ "${CMAKE_SOURCE_DIR}/package.json" PACKAGE_JSON)
string(REGEX MATCH "\"version\"[ \t]*:[ \t]*\"([^\"]+)\"" _ ${PACKAGE_JSON})
if(CMAKE_MATCH_1)
set(BUN_VERSION_STRING "${CMAKE_MATCH_1}")
else()
set(BUN_VERSION_STRING "unknown")
endif()
else()
set(BUN_VERSION_STRING "${VERSION}")
endif()
list(APPEND DEPENDENCY_VERSIONS "BUN_VERSION" "${BUN_VERSION_STRING}")
# Node.js compatibility version (hardcoded as in the current implementation)
set(NODEJS_COMPAT_VERSION "22.12.0")
list(APPEND DEPENDENCY_VERSIONS "NODEJS_COMPAT_VERSION" "${NODEJS_COMPAT_VERSION}")
# Get Bun's git SHA for uws/usockets versions (they use Bun's own SHA)
execute_process(
COMMAND git rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE BUN_GIT_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT BUN_GIT_SHA)
set(BUN_GIT_SHA "unknown")
endif()
list(APPEND DEPENDENCY_VERSIONS "UWS" "${BUN_GIT_SHA}")
list(APPEND DEPENDENCY_VERSIONS "USOCKETS" "${BUN_GIT_SHA}")
# Zig version - hardcoded for now, can be updated as needed
# This should match the version of Zig used to build Bun
list(APPEND DEPENDENCY_VERSIONS "ZIG" "0.14.1")
# Generate the header file content
set(HEADER_CONTENT "// This file is auto-generated by CMake. Do not edit manually.\n")
string(APPEND HEADER_CONTENT "#ifndef BUN_DEPENDENCY_VERSIONS_H\n")
string(APPEND HEADER_CONTENT "#define BUN_DEPENDENCY_VERSIONS_H\n\n")
string(APPEND HEADER_CONTENT "#ifdef __cplusplus\n")
string(APPEND HEADER_CONTENT "extern \"C\" {\n")
string(APPEND HEADER_CONTENT "#endif\n\n")
string(APPEND HEADER_CONTENT "// Dependency versions\n")
# Process the version list
list(LENGTH DEPENDENCY_VERSIONS num_versions)
math(EXPR last_idx "${num_versions} - 1")
set(i 0)
while(i LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${i} name)
math(EXPR value_idx "${i} + 1")
if(value_idx LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${value_idx} value)
# Only emit #define if value is not "unknown"
if(NOT "${value}" STREQUAL "unknown")
string(APPEND HEADER_CONTENT "#define BUN_DEP_${name} \"${value}\"\n")
endif()
endif()
math(EXPR i "${i} + 2")
endwhile()
string(APPEND HEADER_CONTENT "\n")
string(APPEND HEADER_CONTENT "// C string constants for easy access\n")
# Create C string constants
set(i 0)
while(i LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${i} name)
math(EXPR value_idx "${i} + 1")
if(value_idx LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${value_idx} value)
# Only emit constant if value is not "unknown"
if(NOT "${value}" STREQUAL "unknown")
string(APPEND HEADER_CONTENT "static const char* const BUN_VERSION_${name} = \"${value}\";\n")
endif()
endif()
math(EXPR i "${i} + 2")
endwhile()
string(APPEND HEADER_CONTENT "\n#ifdef __cplusplus\n")
string(APPEND HEADER_CONTENT "}\n")
string(APPEND HEADER_CONTENT "#endif\n\n")
string(APPEND HEADER_CONTENT "#endif // BUN_DEPENDENCY_VERSIONS_H\n")
# Write the header file only if content has changed
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/bun_dependency_versions.h")
# Read existing content if file exists
set(EXISTING_CONTENT "")
if(EXISTS "${OUTPUT_FILE}")
file(READ "${OUTPUT_FILE}" EXISTING_CONTENT)
endif()
# Only write if content is different
if(NOT "${EXISTING_CONTENT}" STREQUAL "${HEADER_CONTENT}")
file(WRITE "${OUTPUT_FILE}" "${HEADER_CONTENT}")
message(STATUS "Updated dependency versions header: ${OUTPUT_FILE}")
else()
message(STATUS "Dependency versions header unchanged: ${OUTPUT_FILE}")
endif()
# Also create a more detailed version for debugging
set(DEBUG_OUTPUT_FILE "${CMAKE_BINARY_DIR}/bun_dependency_versions_debug.txt")
set(DEBUG_CONTENT "Bun Dependency Versions\n")
string(APPEND DEBUG_CONTENT "=======================\n\n")
set(i 0)
while(i LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${i} name)
math(EXPR value_idx "${i} + 1")
if(value_idx LESS num_versions)
list(GET DEPENDENCY_VERSIONS ${value_idx} value)
string(APPEND DEBUG_CONTENT "${name}: ${value}\n")
endif()
math(EXPR i "${i} + 2")
endwhile()
file(WRITE "${DEBUG_OUTPUT_FILE}" "${DEBUG_CONTENT}")
endfunction()
# Call the function to generate the header
generate_dependency_versions_header()

View File

@@ -131,9 +131,6 @@ else()
find_llvm_command(CMAKE_RANLIB llvm-ranlib)
if(LINUX)
find_llvm_command(LLD_PROGRAM ld.lld)
# Ensure vendor dependencies use lld instead of ld
list(APPEND CMAKE_ARGS -DCMAKE_EXE_LINKER_FLAGS=--ld-path=${LLD_PROGRAM})
list(APPEND CMAKE_ARGS -DCMAKE_SHARED_LINKER_FLAGS=--ld-path=${LLD_PROGRAM})
endif()
if(APPLE)
find_llvm_command(CMAKE_DSYMUTIL dsymutil)

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 69fa2714ab5f917c2d15501ff8cfdccfaea78882)
set(WEBKIT_VERSION f474428677de1fafaf13bb3b9a050fe3504dda25)
endif()
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)

View File

@@ -90,7 +90,6 @@ register_command(
-DZIG_PATH=${ZIG_PATH}
-DZIG_COMMIT=${ZIG_COMMIT}
-DENABLE_ASAN=${ENABLE_ASAN}
-DENABLE_VALGRIND=${ENABLE_VALGRIND}
-DZIG_COMPILER_SAFE=${ZIG_COMPILER_SAFE}
-P ${CWD}/cmake/scripts/DownloadZig.cmake
SOURCES

View File

@@ -122,7 +122,7 @@
},
{
"name": "reporter",
"description": "Test output reporter format. Available: 'junit' (requires --reporter-outfile). Default: console output.",
"description": "Specify the test reporter. Currently --reporter=junit is the only supported format.",
"hasValue": true,
"valueType": "val",
"required": false,
@@ -130,7 +130,7 @@
},
{
"name": "reporter-outfile",
"description": "Output file path for the reporter format (required with --reporter).",
"description": "The output file used for the format from --reporter.",
"hasValue": true,
"valueType": "val",
"required": false,

View File

@@ -665,6 +665,7 @@ _bun_test_completion() {
'--timeout[Set the per-test timeout in milliseconds, default is 5000.]:timeout' \
'--update-snapshots[Update snapshot files]' \
'--rerun-each[Re-run each test file <NUMBER> times, helps catch certain bugs]:rerun' \
'--only[Only run tests that are marked with "test.only()"]' \
'--todo[Include tests that are marked with "test.todo()"]' \
'--coverage[Generate a coverage profile]' \
'--bail[Exit the test suite after <NUMBER> failures. If you do not specify a number, it defaults to 1.]:bail' \

View File

@@ -184,7 +184,6 @@ Bun.hash.rapidhash("data", 1234);
- `"blake2b256"`
- `"blake2b512"`
- `"blake2s256"`
- `"md4"`
- `"md5"`
- `"ripemd160"`

View File

@@ -161,102 +161,6 @@ const randomTag = await redis.srandmember("tags");
const poppedTag = await redis.spop("tags");
```
## Pub/Sub
Bun provides native bindings for the [Redis
Pub/Sub](https://redis.io/docs/latest/develop/pubsub/) protocol. **New in Bun
1.2.23**
{% callout %}
**🚧** — The Redis Pub/Sub feature is experimental. Although we expect it to be
stable, we're currently actively looking for feedback and areas for improvement.
{% /callout %}
### Basic Usage
To get started publishing messages, you can set up a publisher in
`publisher.ts`:
```typescript#publisher.ts
import { RedisClient } from "bun";
const writer = new RedisClient("redis://localhost:6739");
await writer.connect();
writer.publish("general", "Hello everyone!");
writer.close();
```
In another file, create the subscriber in `subscriber.ts`:
```typescript#subscriber.ts
import { RedisClient } from "bun";
const listener = new RedisClient("redis://localhost:6739");
await listener.connect();
await listener.subscribe("general", (message, channel) => {
console.log(`Received: ${message}`);
});
```
In one shell, run your subscriber:
```bash
bun run subscriber.ts
```
and, in another, run your publisher:
```bash
bun run publisher.ts
```
{% callout %}
**Note:** The subscription mode takes over the `RedisClient` connection. A
client with subscriptions can only call `RedisClient.prototype.subscribe()`. In
other words, applications which need to message Redis need a separate
connection, acquirable through `.duplicate()`:
```typescript
import { RedisClient } from "bun";
const redis = new RedisClient("redis://localhost:6379");
await redis.connect();
const subscriber = await redis.duplicate();
await subscriber.subscribe("foo", () => {});
await redis.set("bar", "baz");
```
{% /callout %}
### Publishing
Publishing messages is done through the `publish()` method:
```typescript
await client.publish(channelName, message);
```
### Subscriptions
The Bun `RedisClient` allows you to subscribe to channels through the
`.subscribe()` method:
```typescript
await client.subscribe(channel, (message, channel) => {});
```
You can unsubscribe through the `.unsubscribe()` method:
```typescript
await client.unsubscribe(); // Unsubscribe from all channels.
await client.unsubscribe(channel); // Unsubscribe a particular channel.
await client.unsubscribe(channel, listener); // Unsubscribe a particular listener.
```
## Advanced Usage
### Command Execution and Pipelining
@@ -578,10 +482,9 @@ When connecting to Redis servers using older versions that don't support RESP3,
Current limitations of the Redis client we are planning to address in future versions:
- [ ] No dedicated API for pub/sub functionality (though you can use the raw command API)
- [ ] Transactions (MULTI/EXEC) must be done through raw commands for now
- [ ] Streams are supported but without dedicated methods
- [ ] Pub/Sub does not currently support binary data, nor pattern-based
subscriptions.
Unsupported features:

View File

@@ -604,12 +604,13 @@ const db = new SQL({
connectionTimeout: 30, // Timeout when establishing new connections
// SSL/TLS options
tls: {
rejectUnauthorized: true,
ca: "path/to/ca.pem",
key: "path/to/key.pem",
cert: "path/to/cert.pem",
},
ssl: "prefer", // or "disable", "require", "verify-ca", "verify-full"
// tls: {
// rejectUnauthorized: true,
// ca: "path/to/ca.pem",
// key: "path/to/key.pem",
// cert: "path/to/cert.pem",
// },
// Callbacks
onconnect: client => {

View File

@@ -3,7 +3,6 @@ In Bun, YAML is a first-class citizen alongside JSON and TOML.
Bun provides built-in support for YAML files through both runtime APIs and bundler integration. You can
- Parse YAML strings with `Bun.YAML.parse`
- Stringify JavaScript objects to YAML with `Bun.YAML.stringify`
- import & require YAML files as modules at runtime (including hot reloading & watch mode support)
- import & require YAML files in frontend apps via bun's bundler
@@ -105,7 +104,7 @@ const data = Bun.YAML.parse(yaml);
#### Error Handling
`Bun.YAML.parse()` throws an error if the YAML is invalid:
`Bun.YAML.parse()` throws a `SyntaxError` if the YAML is invalid:
```ts
try {
@@ -115,175 +114,6 @@ try {
}
```
### `Bun.YAML.stringify()`
Convert a JavaScript value into a YAML string. The API signature matches `JSON.stringify`:
```ts
YAML.stringify(value, replacer?, space?)
```
- `value`: The value to convert to YAML
- `replacer`: Currently only `null` or `undefined` (function replacers not yet supported)
- `space`: Number of spaces for indentation (e.g., `2`) or a string to use for indentation. **Without this parameter, outputs flow-style (single-line) YAML**
#### Basic Usage
```ts
import { YAML } from "bun";
const data = {
name: "John Doe",
age: 30,
hobbies: ["reading", "coding"],
};
// Without space - outputs flow-style (single-line) YAML
console.log(YAML.stringify(data));
// {name: John Doe,age: 30,hobbies: [reading,coding]}
// With space=2 - outputs block-style (multi-line) YAML
console.log(YAML.stringify(data, null, 2));
// name: John Doe
// age: 30
// hobbies:
// - reading
// - coding
```
#### Output Styles
```ts
const arr = [1, 2, 3];
// Flow style (single-line) - default
console.log(YAML.stringify(arr));
// [1,2,3]
// Block style (multi-line) - with indentation
console.log(YAML.stringify(arr, null, 2));
// - 1
// - 2
// - 3
```
#### String Quoting
`YAML.stringify()` automatically quotes strings when necessary:
- Strings that would be parsed as YAML keywords (`true`, `false`, `null`, `yes`, `no`, etc.)
- Strings that would be parsed as numbers
- Strings containing special characters or escape sequences
```ts
const examples = {
keyword: "true", // Will be quoted: "true"
number: "123", // Will be quoted: "123"
text: "hello world", // Won't be quoted: hello world
empty: "", // Will be quoted: ""
};
console.log(YAML.stringify(examples, null, 2));
// keyword: "true"
// number: "123"
// text: hello world
// empty: ""
```
#### Cycles and References
`YAML.stringify()` automatically detects and handles circular references using YAML anchors and aliases:
```ts
const obj = { name: "root" };
obj.self = obj; // Circular reference
const yamlString = YAML.stringify(obj, null, 2);
console.log(yamlString);
// &root
// name: root
// self:
// *root
// Objects with shared references
const shared = { id: 1 };
const data = {
first: shared,
second: shared,
};
console.log(YAML.stringify(data, null, 2));
// first:
// &first
// id: 1
// second:
// *first
```
#### Special Values
```ts
// Special numeric values
console.log(YAML.stringify(Infinity)); // .inf
console.log(YAML.stringify(-Infinity)); // -.inf
console.log(YAML.stringify(NaN)); // .nan
console.log(YAML.stringify(0)); // 0
console.log(YAML.stringify(-0)); // -0
// null and undefined
console.log(YAML.stringify(null)); // null
console.log(YAML.stringify(undefined)); // undefined (returns undefined, not a string)
// Booleans
console.log(YAML.stringify(true)); // true
console.log(YAML.stringify(false)); // false
```
#### Complex Objects
```ts
const config = {
server: {
port: 3000,
host: "localhost",
ssl: {
enabled: true,
cert: "/path/to/cert.pem",
key: "/path/to/key.pem",
},
},
database: {
connections: [
{ name: "primary", host: "db1.example.com" },
{ name: "replica", host: "db2.example.com" },
],
},
features: {
auth: true,
"rate-limit": 100, // Keys with special characters are preserved
},
};
const yamlString = YAML.stringify(config, null, 2);
console.log(yamlString);
// server:
// port: 3000
// host: localhost
// ssl:
// enabled: true
// cert: /path/to/cert.pem
// key: /path/to/key.pem
// database:
// connections:
// - name: primary
// host: db1.example.com
// - name: replica
// host: db2.example.com
// features:
// auth: true
// rate-limit: 100
```
## Module Import
### ES Modules
@@ -354,45 +184,6 @@ const { database, redis } = require("./config.yaml");
console.log(database.port); // 5432
```
### TypeScript Support
While Bun can import YAML files directly, TypeScript doesn't know the types of your YAML files by default. To add TypeScript support for your YAML imports, create a declaration file with `.d.ts` appended to the YAML filename (e.g., `config.yaml` → `config.yaml.d.ts`):
```yaml#config.yaml
features: "advanced"
server:
host: localhost
port: 3000
```
```ts#config.yaml.d.ts
const contents: {
features: string;
server: {
host: string;
port: number;
};
};
export = contents;
```
Now TypeScript will provide proper type checking and auto-completion:
```ts#app.ts
import config from "./config.yaml";
// TypeScript knows the types!
config.server.port; // number
config.server.host; // string
config.features; // string
// TypeScript will catch errors
config.server.unknown; // Error: Property 'unknown' does not exist
```
This approach works for both ES modules and CommonJS, giving you full type safety while Bun continues to handle the actual YAML parsing at runtime.
## Hot Reloading with YAML
One of the most powerful features of Bun's YAML support is hot reloading. When you run your application with `bun --hot`, changes to YAML files are automatically detected and reloaded without closing connections

View File

@@ -733,10 +733,6 @@ Whether to enable minification. Default `false`.
When targeting `bun`, identifiers will be minified by default.
{% /callout %}
{% callout %}
When `minify.syntax` is enabled, unused function and class expression names are removed unless `minify.keepNames` is set to `true` or `--keep-names` flag is used.
{% /callout %}
To enable all minification options:
{% codetabs group="a" %}
@@ -767,16 +763,12 @@ await Bun.build({
whitespace: true,
identifiers: true,
syntax: true,
keepNames: false, // default
},
})
```
```bash#CLI
$ bun build ./index.tsx --outdir ./out --minify-whitespace --minify-identifiers --minify-syntax
# To preserve function and class names during minification:
$ bun build ./index.tsx --outdir ./out --minify --keep-names
```
{% /codetabs %}
@@ -1561,7 +1553,6 @@ interface BuildConfig {
whitespace?: boolean;
syntax?: boolean;
identifiers?: boolean;
keepNames?: boolean;
};
/**
* Ignore dead code elimination/tree-shaking annotations such as @__PURE__ and package.json

View File

@@ -109,85 +109,6 @@ Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a t
$ bun test --timeout 20
```
## Concurrent test execution
By default, Bun runs all tests sequentially within each test file. You can enable concurrent execution to run async tests in parallel, significantly speeding up test suites with independent tests.
### `--concurrent` flag
Use the `--concurrent` flag to run all tests concurrently within their respective files:
```sh
$ bun test --concurrent
```
When this flag is enabled, all tests will run in parallel unless explicitly marked with `test.serial`.
### `--max-concurrency` flag
Control the maximum number of tests running simultaneously with the `--max-concurrency` flag:
```sh
# Limit to 4 concurrent tests
$ bun test --concurrent --max-concurrency 4
# Default: 20
$ bun test --concurrent
```
This helps prevent resource exhaustion when running many concurrent tests. The default value is 20.
### `test.concurrent`
Mark individual tests to run concurrently, even when the `--concurrent` flag is not used:
```ts
import { test, expect } from "bun:test";
// These tests run in parallel with each other
test.concurrent("concurrent test 1", async () => {
await fetch("/api/endpoint1");
expect(true).toBe(true);
});
test.concurrent("concurrent test 2", async () => {
await fetch("/api/endpoint2");
expect(true).toBe(true);
});
// This test runs sequentially
test("sequential test", () => {
expect(1 + 1).toBe(2);
});
```
### `test.serial`
Force tests to run sequentially, even when the `--concurrent` flag is enabled:
```ts
import { test, expect } from "bun:test";
let sharedState = 0;
// These tests must run in order
test.serial("first serial test", () => {
sharedState = 1;
expect(sharedState).toBe(1);
});
test.serial("second serial test", () => {
// Depends on the previous test
expect(sharedState).toBe(1);
sharedState = 2;
});
// This test can run concurrently if --concurrent is enabled
test("independent test", () => {
expect(true).toBe(true);
});
```
## Rerun tests
Use the `--rerun-each` flag to run each test multiple times. This is useful for detecting flaky or non-deterministic test failures.
@@ -196,36 +117,6 @@ Use the `--rerun-each` flag to run each test multiple times. This is useful for
$ bun test --rerun-each 100
```
## Randomize test execution order
Use the `--randomize` flag to run tests in a random order. This helps detect tests that depend on shared state or execution order.
```sh
$ bun test --randomize
```
When using `--randomize`, the seed used for randomization will be displayed in the test summary:
```sh
$ bun test --randomize
# ... test output ...
--seed=12345
2 pass
8 fail
Ran 10 tests across 2 files. [50.00ms]
```
### Reproducible random order with `--seed`
Use the `--seed` flag to specify a seed for the randomization. This allows you to reproduce the same test order when debugging order-dependent failures.
```sh
# Reproduce a previous randomized run
$ bun test --seed 123456
```
The `--seed` flag implies `--randomize`, so you don't need to specify both. Using the same seed value will always produce the same test execution order, making it easier to debug intermittent failures caused by test interdependencies.
## Bail out with `--bail`
Use the `--bail` flag to abort the test run early after a pre-determined number of test failures. By default Bun will run all tests and report all failures, but sometimes in CI environments it's preferable to terminate earlier to reduce CPU usage.

View File

@@ -9,9 +9,8 @@ $ bun create next-app
✔ What is your project named? … my-app
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use Tailwind CSS? ... No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Would you like to use App Router? (recommended) ... No / Yes
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /path/to/my-app.
```

View File

@@ -73,30 +73,4 @@ console.log(data.hobbies); // => ["reading", "coding"]
---
## TypeScript Support
To add TypeScript support for your YAML imports, create a declaration file with `.d.ts` appended to the YAML filename (e.g., `config.yaml` → `config.yaml.d.ts`);
```ts#config.yaml.d.ts
const contents: {
database: {
host: string;
port: number;
name: string;
};
server: {
port: number;
timeout: number;
};
features: {
auth: boolean;
rateLimit: boolean;
};
};
export = contents;
```
---
See [Docs > API > YAML](https://bun.com/docs/api/yaml) for complete documentation on YAML support in Bun.

View File

@@ -359,7 +359,7 @@ export default {
page("api/file-io", "File I/O", {
description: `Read and write files fast with Bun's heavily optimized file system API.`,
}), // "`Bun.write`"),
page("api/redis", "Redis Client", {
page("api/redis", "Redis client", {
description: `Bun provides a fast, native Redis client with automatic command pipelining for better performance.`,
}),
page("api/import-meta", "import.meta", {
@@ -407,9 +407,6 @@ export default {
page("api/cc", "C Compiler", {
description: `Build & run native C from JavaScript with Bun's native C compiler API`,
}), // "`bun:ffi`"),
page("api/secrets", "Secrets", {
description: `Store and retrieve sensitive credentials securely using the operating system's native credential storage APIs.`,
}), // "`Bun.secrets`"),
page("cli/test", "Testing", {
description: `Bun's built-in test runner is fast and uses Jest-compatible syntax.`,
}), // "`bun:test`"),

View File

@@ -232,23 +232,6 @@ Set path where coverage reports will be saved. Please notice, that it works only
coverageDir = "path/to/somewhere" # default "coverage"
```
### `test.concurrentTestGlob`
Specify a glob pattern to automatically run matching test files with concurrent test execution enabled. Test files matching this pattern will behave as if the `--concurrent` flag was passed, running all tests within those files concurrently.
```toml
[test]
concurrentTestGlob = "**/concurrent-*.test.ts"
```
This is useful for:
- Gradually migrating test suites to concurrent execution
- Running integration tests concurrently while keeping unit tests sequential
- Separating fast concurrent tests from tests that require sequential execution
The `--concurrent` CLI flag will override this setting when specified.
## Package manager
Package management is a complex issue; to support a range of use cases, the behavior of `bun install` can be configured under the `[install]` section.
@@ -538,7 +521,7 @@ When a security scanner is configured:
- Installation is cancelled if fatal issues are found
- Security warnings are displayed during installation
Learn more about [using and writing security scanners](/docs/install/security-scanner-api).
Learn more about [using and writing security scanners](/docs/install/security).
### `install.linker`

View File

@@ -46,25 +46,6 @@ smol = true # Reduce memory usage during test runs
This is equivalent to using the `--smol` flag on the command line.
### Test execution
#### concurrentTestGlob
Automatically run test files matching a glob pattern with concurrent test execution enabled. This is useful for gradually migrating test suites to concurrent execution or for running specific test types concurrently.
```toml
[test]
concurrentTestGlob = "**/concurrent-*.test.ts" # Run files matching this pattern concurrently
```
Test files matching this pattern will behave as if the `--concurrent` flag was passed, running all tests within those files concurrently. This allows you to:
- Gradually migrate your test suite to concurrent execution
- Run integration tests concurrently while keeping unit tests sequential
- Separate fast concurrent tests from tests that require sequential execution
The `--concurrent` CLI flag will override this setting when specified, forcing all tests to run concurrently regardless of the glob pattern.
### Coverage options
In addition to the options documented in the [coverage documentation](./coverage.md), the following options are available:

View File

@@ -1,132 +0,0 @@
# Concurrent Test Glob Example
This example demonstrates how to use the `concurrentTestGlob` option to selectively run tests concurrently based on file naming patterns.
## Project Structure
```text
my-project/
├── bunfig.toml
├── tests/
│ ├── unit/
│ │ ├── math.test.ts # Sequential
│ │ └── utils.test.ts # Sequential
│ └── integration/
│ ├── concurrent-api.test.ts # Concurrent
│ └── concurrent-database.test.ts # Concurrent
```
## Configuration
### bunfig.toml
```toml
[test]
# Run all test files with "concurrent-" prefix concurrently
concurrentTestGlob = "**/concurrent-*.test.ts"
```
## Test Files
### Unit Test (Sequential)
`tests/unit/math.test.ts`
```typescript
import { test, expect } from "bun:test";
// These tests run sequentially by default
// Good for tests that share state or have specific ordering requirements
let sharedState = 0;
test("addition", () => {
sharedState = 5 + 3;
expect(sharedState).toBe(8);
});
test("uses previous state", () => {
// This test depends on the previous test's state
expect(sharedState).toBe(8);
});
```
### Integration Test (Concurrent)
`tests/integration/concurrent-api.test.ts`
```typescript
import { test, expect } from "bun:test";
// These tests automatically run concurrently due to filename matching the glob pattern.
// Using test() is equivalent to test.concurrent() when the file matches concurrentTestGlob.
// Each test is independent and can run in parallel.
test("fetch user data", async () => {
const response = await fetch("/api/user/1");
expect(response.ok).toBe(true);
});
test("fetch posts", async () => {
const response = await fetch("/api/posts");
expect(response.ok).toBe(true);
});
test("fetch comments", async () => {
const response = await fetch("/api/comments");
expect(response.ok).toBe(true);
});
```
## Running Tests
```bash
# Run all tests - concurrent-*.test.ts files will run concurrently
bun test
# Override: Force ALL tests to run concurrently
# Note: This overrides bunfig.toml and runs all tests concurrently, regardless of glob
bun test --concurrent
# Run only unit tests (sequential)
bun test tests/unit
# Run only integration tests (concurrent due to glob pattern)
bun test tests/integration
```
## Benefits
1. **Gradual Migration**: Migrate to concurrent tests file by file by renaming them
2. **Clear Organization**: File naming convention indicates execution mode
3. **Performance**: Integration tests run faster in parallel
4. **Safety**: Unit tests remain sequential where needed
5. **Flexibility**: Easy to change execution mode by renaming files
## Migration Strategy
To migrate existing tests to concurrent execution:
1. Start with independent integration tests
2. Rename files to match the glob pattern: `mv api.test.ts concurrent-api.test.ts`
3. Verify tests still pass
4. Monitor for race conditions or shared state issues
5. Continue migrating stable tests incrementally
## Tips
- Use descriptive prefixes: `concurrent-`, `parallel-`, `async-`
- Keep related sequential tests together
- Document why certain tests must remain sequential
- Use `test.concurrent()` for fine-grained control in sequential files
(In files matched by `concurrentTestGlob`, plain `test()` already runs concurrently)
- Consider separate globs for different test types:
```toml
[test]
# Multiple patterns for different test categories
concurrentTestGlob = [
"**/integration/*.test.ts",
"**/e2e/*.test.ts",
"**/concurrent-*.test.ts"
]
```

View File

@@ -149,6 +149,12 @@ describe.only("only", () => {
The following command will only execute tests #2 and #3.
```sh
$ bun test --only
```
The following command will only execute tests #1, #2 and #3.
```sh
$ bun test
```
@@ -750,76 +756,3 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
- [`.toThrowErrorMatchingInlineSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot)
{% /table %}
## TypeScript Type Safety
Bun's test runner provides enhanced TypeScript support with intelligent type checking for your test assertions. The type system helps catch potential bugs at compile time while still allowing flexibility when needed.
### Strict Type Checking by Default
By default, Bun's test matchers enforce strict type checking between the actual value and expected value:
```ts
import { expect, test } from "bun:test";
test("strict typing", () => {
const str = "hello";
const num = 42;
expect(str).toBe("hello"); // ✅ OK: string to string
expect(num).toBe(42); // ✅ OK: number to number
expect(str).toBe(42); // ❌ TypeScript error: string vs number
});
```
This helps catch common mistakes where you might accidentally compare values of different types.
### Relaxed Type Checking with Type Parameters
Sometimes you need more flexibility in your tests, especially when working with:
- Dynamic data from APIs
- Polymorphic functions that can return multiple types
- Generic utility functions
- Migration of existing test suites
For these cases, you can "opt out" of strict type checking by providing an explicit type parameter to matcher methods:
```ts
import { expect, test } from "bun:test";
test("relaxed typing with type parameters", () => {
const value: unknown = getSomeValue();
// These would normally cause TypeScript errors, but type parameters allow them:
expect(value).toBe<number>(42); // No TS error, runtime check still works
expect(value).toEqual<string>("hello"); // No TS error, runtime check still works
expect(value).toStrictEqual<boolean>(true); // No TS error, runtime check still works
});
test("useful for dynamic data", () => {
const apiResponse: any = { status: "success" };
// Without type parameter: TypeScript error (any vs string)
// expect(apiResponse.status).toBe("success");
// With type parameter: No TypeScript error, runtime assertion still enforced
expect(apiResponse.status).toBe<string>("success"); // ✅ OK
});
```
### Migration from Looser Type Systems
If migrating from a test framework with looser TypeScript integration, you can use type parameters as a stepping stone:
```ts
// Old Jest test that worked but wasn't type-safe
expect(response.data).toBe(200); // No type error in some setups
// Bun equivalent with explicit typing during migration
expect(response.data).toBe<number>(200); // Explicit about expected type
// Ideal Bun test after refactoring
const statusCode: number = response.data;
expect(statusCode).toBe(200); // Type-safe without explicit parameter
```

View File

@@ -19,6 +19,3 @@ command script import -c bun_pretty_printer.py
command script delete btjs
command alias btjs p {printf("gathering btjs trace...\n");printf("%s\n", (char*)dumpBtjsTrace())}
# do not pass SIGHUP on to child process. it is often not the real error and the stop point will be nonsensical.
process handle -p false -s false -n true SIGHUP

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "bun",
"version": "1.2.23",
"version": "1.2.22",
"workspaces": [
"./packages/bun-types",
"./packages/@types/bun"
@@ -33,7 +33,7 @@
"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 --silent bd:v",
"build:debug": "export COMSPEC=\"C:\\Windows\\System32\\cmd.exe\" && bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug --log-level=NOTICE",
"build:debug:noasan": "export COMSPEC=\"C:\\Windows\\System32\\cmd.exe\" && bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=OFF -B build/debug --log-level=NOTICE",
"build:debug:asan": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=ON -B build/debug-asan --log-level=NOTICE",
"build:release": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -B build/release",
"build:ci": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DCI=true -B build/release-ci --verbose --fresh",
"build:assert": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_ASSERTIONS=ON -DENABLE_LOGS=ON -B build/release-assert",
@@ -84,11 +84,6 @@
"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",
"machine:linux:ubuntu": "./scripts/machine.mjs ssh --cloud=aws --arch=x64 --instance-type c7i.2xlarge --os=linux --distro=ubuntu --release=25.04",
"machine:linux:debian": "./scripts/machine.mjs ssh --cloud=aws --arch=x64 --instance-type c7i.2xlarge --os=linux --distro=debian --release=12",
"machine:linux:alpine": "./scripts/machine.mjs ssh --cloud=aws --arch=x64 --instance-type c7i.2xlarge --os=linux --distro=alpine --release=3.21",
"machine:linux:amazonlinux": "./scripts/machine.mjs ssh --cloud=aws --arch=x64 --instance-type c7i.2xlarge --os=linux --distro=amazonlinux --release=2023",
"machine:windows:2019": "./scripts/machine.mjs ssh --cloud=aws --arch=x64 --instance-type c7i.2xlarge --os=windows --release=2019",
"sync-webkit-source": "bun ./scripts/sync-webkit-source.ts"
}
}

View File

@@ -1,14 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "bun-error",
"dependencies": {
"preact": "^10.27.2",
},
},
},
"packages": {
"preact": ["preact@10.27.2", "", {}, "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="],
}
}

View File

@@ -1,6 +1,5 @@
import type { JSX } from "preact";
import { createContext, render } from "preact";
import { useCallback, useContext, useEffect, useRef, useState } from "preact/hooks";
import React, { createContext, useContext } from "react";
import { render, unmountComponentAtNode } from "react-dom";
import type {
FallbackMessageContainer,
JSException,
@@ -165,17 +164,17 @@ const maybeBlobFileURL = (filename: string, line?: number, column?: number): str
return srcFileURL(filename, line, column);
};
const openWithoutFlashOfNewTab: JSX.MouseEventHandler<HTMLAnchorElement> = event => {
const target = event.currentTarget as HTMLAnchorElement;
const openWithoutFlashOfNewTab: React.MouseEventHandler<HTMLAnchorElement> = event => {
const target = event.currentTarget;
const href = target.getAttribute("href");
if (!href || event.button !== 0) {
return true;
}
event.preventDefault();
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
event.nativeEvent.preventDefault();
event.nativeEvent.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
const headers = new Headers();
headers.set("Accept", "text/plain");
@@ -318,17 +317,17 @@ const AsyncSourceLines = ({
highlight: number;
highlightColumnStart: number;
highlightColumnEnd: number;
children?: any;
children?: React.ReactNode;
buildURL: (line?: number, column?: number) => string;
sourceLines: SourceLine[];
setSourceLines: (lines: SourceLine[]) => void;
}) => {
const [loadState, setLoadState] = useState(LoadState.pending);
const [loadState, setLoadState] = React.useState(LoadState.pending);
const controller = useRef<AbortController | null>(null);
const url = useRef<string>(buildURL(0, 0));
const controller = React.useRef<AbortController | null>(null);
const url = React.useRef<string>(buildURL(0, 0));
useEffect(() => {
React.useEffect(() => {
controller.current = new AbortController();
var cancelled = false;
fetch(url.current, {
@@ -433,7 +432,7 @@ const SourceLines = ({
highlight: number;
highlightColumnStart: number;
highlightColumnEnd: number;
children?: any;
children?: React.ReactNode;
buildURL: (line?: number, column?: number) => string;
}) => {
let start = sourceLines.length;
@@ -462,7 +461,7 @@ const SourceLines = ({
const leftPad = maxLineNumber.toString(10).length - minLineNumber.toString(10).length;
const _sourceLines = sourceLines.slice(start, end);
const lines = new Array(_sourceLines.length + (Array.isArray(children) ? children.length : children ? 1 : 0));
const lines = new Array(_sourceLines.length + React.Children.count(children));
let highlightI = 0;
for (let i = 0; i < _sourceLines.length; i++) {
@@ -514,7 +513,7 @@ const SourceLines = ({
const BuildErrorSourceLines = ({ location, filename }: { location: Location; filename: string }) => {
const { line, line_text, column } = location;
const sourceLines: SourceLine[] = [{ line, text: line_text }];
const buildURL = useCallback((line, column) => srcFileURL(filename, line, column), [filename]);
const buildURL = React.useCallback((line, column) => srcFileURL(filename, line, column), [srcFileURL, filename]);
return (
<SourceLines
sourceLines={sourceLines}
@@ -670,15 +669,15 @@ const NativeStackTrace = ({
frames: StackFrame[];
sourceLines: SourceLine[];
setSourceLines: (sourceLines: SourceLine[]) => void;
children?: any;
children?: React.ReactNode;
isClient: boolean;
}) => {
const { file = "", position } = frames[0];
const { cwd } = useContext(ErrorGroupContext);
const filename = normalizedFilename(file, cwd);
const urlBuilder = isClient ? clientURL : maybeBlobFileURL;
const ref = useRef<HTMLDivElement>(null);
const buildURL = useCallback((line, column) => urlBuilder(file, line, column), [file, urlBuilder]);
const ref = React.useRef<HTMLDivElement>(null);
const buildURL = React.useCallback((line, column) => urlBuilder(file, line, column), [file, urlBuilder]);
return (
<div ref={ref} className={`BunError-NativeStackTrace`}>
@@ -733,7 +732,7 @@ const Indent = ({ by, children }) => {
const JSException = ({ value, isClient = false }: { value: JSExceptionType; isClient: boolean }) => {
const tag = isClient ? ErrorTagType.client : ErrorTagType.server;
const [sourceLines, _setSourceLines] = useState(value?.stack?.source_lines ?? []);
const [sourceLines, _setSourceLines] = React.useState(value?.stack?.source_lines ?? []);
var message = value.message || "";
var name = value.name || "";
if (!name && !message) {
@@ -1243,7 +1242,7 @@ export function renderRuntimeError(error: Error) {
export function dismissError() {
if (reactRoot) {
render(null, reactRoot);
unmountComponentAtNode(reactRoot);
const root = document.getElementById("__bun__error-root");
if (root) root.remove();
reactRoot = null;

View File

@@ -5,9 +5,14 @@
"license": "MIT",
"private": true,
"scripts": {
"build": "bun build --production --define:process.env.NODE_ENV=\"'production'\" --minify index.tsx bun-error.css --outdir=dist --target=browser --format=esm"
"build": "esbuild --define:process.env.NODE_ENV=\"'production'\" --minify index.tsx bun-error.css --bundle --outdir=dist --platform=browser --format=esm"
},
"dependencies": {
"preact": "^10.27.2"
"esbuild": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@types/react": "^17.0.39"
}
}

View File

@@ -1,11 +1,10 @@
{
"compilerOptions": {
"jsx": "react",
"lib": ["ESNext", "DOM"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"jsx": "react-jsx",
"jsxImportSource": "preact"
"allowSyntheticDefaultImports": true
}
}

View File

@@ -62,7 +62,7 @@ export const platforms: Platform[] = [
},
{
os: "linux",
arch: "arm64",
arch: "aarch64",
abi: "musl",
bin: "bun-linux-aarch64-musl",
exe: "bin/bun",

View File

@@ -636,7 +636,7 @@ declare module "bun" {
* import { YAML } from "bun";
*
* console.log(YAML.parse("123")) // 123
* console.log(YAML.parse("null")) // null
* console.log(YAML.parse("123")) // null
* console.log(YAML.parse("false")) // false
* console.log(YAML.parse("abc")) // "abc"
* console.log(YAML.parse("- abc")) // [ "abc" ]
@@ -653,10 +653,7 @@ declare module "bun" {
*
* @param input The JavaScript value to stringify.
* @param replacer Currently not supported.
* @param space A number for how many spaces each level of indentation gets, or a string used as indentation.
* Without this parameter, outputs flow-style (single-line) YAML.
* With this parameter, outputs block-style (multi-line) YAML.
* The number is clamped between 0 and 10, and the first 10 characters of the string are used.
* @param space A number for how many spaces each level of indentation gets, or a string used as indentation. The number is clamped between 0 and 10, and the first 10 characters of the string are used.
* @returns A string containing the YAML document.
*
* @example
@@ -664,24 +661,19 @@ declare module "bun" {
* import { YAML } from "bun";
*
* const input = {
* abc: "def",
* num: 123
* abc: "def"
* };
*
* // Without space - flow style (single-line)
* console.log(YAML.stringify(input));
* // {abc: def,num: 123}
*
* // With space - block style (multi-line)
* console.log(YAML.stringify(input, null, 2));
* // # output
* // abc: def
* // num: 123
*
* const cycle = {};
* cycle.obj = cycle;
* console.log(YAML.stringify(cycle, null, 2));
* // &1
* // obj: *1
* console.log(YAML.stringify(cycle));
* // # output
* // &root
* // obj:
* // *root
*/
export function stringify(input: unknown, replacer?: undefined | null, space?: string | number): string;
}
@@ -1827,7 +1819,6 @@ declare module "bun" {
whitespace?: boolean;
syntax?: boolean;
identifiers?: boolean;
keepNames?: boolean;
};
/**
@@ -1907,18 +1898,6 @@ declare module "bun" {
*/
tsconfig?: string;
/**
* JSX configuration options
*/
jsx?: {
runtime?: "automatic" | "classic";
importSource?: string;
factory?: string;
fragment?: string;
sideEffects?: boolean;
development?: boolean;
};
outdir?: string;
}
@@ -5047,7 +5026,6 @@ declare module "bun" {
type SupportedCryptoAlgorithms =
| "blake2b256"
| "blake2b512"
| "blake2s256"
| "md4"
| "md5"
| "ripemd160"

View File

@@ -1556,15 +1556,6 @@ declare var URL: Bun.__internal.UseLibDomIfAvailable<
}
>;
/**
* The **`AbortController`** interface represents a controller object that allows you to abort one or more Web requests as and when desired.
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController)
*/
interface AbortController {
readonly signal: AbortSignal;
abort(reason?: any): void;
}
declare var AbortController: Bun.__internal.UseLibDomIfAvailable<
"AbortController",
{

View File

@@ -26,6 +26,6 @@
/// <reference path="./bun.ns.d.ts" />
// Must disable this so it doesn't conflict with the DOM onmessage type, but still
// @ts-ignore Must disable this so it doesn't conflict with the DOM onmessage type, but still
// allows us to declare our own globals that Node's types can "see" and not conflict with
declare var onmessage: Bun.__internal.UseLibDomIfAvailable<"onmessage", never>;
declare var onmessage: never;

View File

@@ -52,25 +52,21 @@ declare module "bun" {
export namespace RedisClient {
type KeyLike = string | ArrayBufferView | Blob;
type StringPubSubListener = (message: string, channel: string) => void;
// Buffer subscriptions are not yet implemented
// type BufferPubSubListener = (message: Uint8Array<ArrayBuffer>, channel: string) => void;
}
export class RedisClient {
/**
* Creates a new Redis client
*
* @param url URL to connect to, defaults to `process.env.VALKEY_URL`,
* `process.env.REDIS_URL`, or `"valkey://localhost:6379"`
* @param url URL to connect to, defaults to process.env.VALKEY_URL, process.env.REDIS_URL, or "valkey://localhost:6379"
* @param options Additional options
*
* @example
* ```ts
* const redis = new RedisClient();
* await redis.set("hello", "world");
* console.log(await redis.get("hello"));
* const valkey = new RedisClient();
*
* await valkey.set("hello", "world");
*
* console.log(await valkey.get("hello"));
* ```
*/
constructor(url?: string, options?: RedisOptions);
@@ -92,14 +88,12 @@ declare module "bun" {
/**
* Callback fired when the client disconnects from the Redis server
*
* @param error The error that caused the disconnection
*/
onclose: ((this: RedisClient, error: Error) => void) | null;
/**
* Connect to the Redis server
*
* @returns A promise that resolves when connected
*/
connect(): Promise<void>;
@@ -158,12 +152,10 @@ declare module "bun" {
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, px: "PX", milliseconds: number): Promise<"OK">;
/**
* Set key to hold the string value with expiration at a specific Unix
* timestamp
* Set key to hold the string value with expiration at a specific Unix timestamp
* @param key The key to set
* @param value The value to set
* @param exat Set the specified Unix time at which the key will expire, in
* seconds
* @param exat Set the specified Unix time at which the key will expire, in seconds
* @returns Promise that resolves with "OK" on success
*/
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, exat: "EXAT", timestampSeconds: number): Promise<"OK">;
@@ -187,8 +179,7 @@ declare module "bun" {
* @param key The key to set
* @param value The value to set
* @param nx Only set the key if it does not already exist
* @returns Promise that resolves with "OK" on success, or null if the key
* already exists
* @returns Promise that resolves with "OK" on success, or null if the key already exists
*/
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, nx: "NX"): Promise<"OK" | null>;
@@ -197,8 +188,7 @@ declare module "bun" {
* @param key The key to set
* @param value The value to set
* @param xx Only set the key if it already exists
* @returns Promise that resolves with "OK" on success, or null if the key
* does not exist
* @returns Promise that resolves with "OK" on success, or null if the key does not exist
*/
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, xx: "XX"): Promise<"OK" | null>;
@@ -206,10 +196,8 @@ declare module "bun" {
* Set key to hold the string value and return the old value
* @param key The key to set
* @param value The value to set
* @param get Return the old string stored at key, or null if key did not
* exist
* @returns Promise that resolves with the old value, or null if key did not
* exist
* @param get Return the old string stored at key, or null if key did not exist
* @returns Promise that resolves with the old value, or null if key did not exist
*/
set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, get: "GET"): Promise<string | null>;
@@ -255,8 +243,7 @@ declare module "bun" {
/**
* Determine if a key exists
* @param key The key to check
* @returns Promise that resolves with true if the key exists, false
* otherwise
* @returns Promise that resolves with true if the key exists, false otherwise
*/
exists(key: RedisClient.KeyLike): Promise<boolean>;
@@ -271,8 +258,7 @@ declare module "bun" {
/**
* Get the time to live for a key in seconds
* @param key The key to get the TTL for
* @returns Promise that resolves with the TTL, -1 if no expiry, or -2 if
* key doesn't exist
* @returns Promise that resolves with the TTL, -1 if no expiry, or -2 if key doesn't exist
*/
ttl(key: RedisClient.KeyLike): Promise<number>;
@@ -284,14 +270,6 @@ declare module "bun" {
*/
hmset(key: RedisClient.KeyLike, fieldValues: string[]): Promise<string>;
/**
* Get the value of a hash field
* @param key The hash key
* @param field The field to get
* @returns Promise that resolves with the field value or null if the field doesn't exist
*/
hget(key: RedisClient.KeyLike, field: RedisClient.KeyLike): Promise<string | null>;
/**
* Get the values of all the given hash fields
* @param key The hash key
@@ -304,8 +282,7 @@ declare module "bun" {
* Check if a value is a member of a set
* @param key The set key
* @param member The member to check
* @returns Promise that resolves with true if the member exists, false
* otherwise
* @returns Promise that resolves with true if the member exists, false otherwise
*/
sismember(key: RedisClient.KeyLike, member: string): Promise<boolean>;
@@ -313,8 +290,7 @@ declare module "bun" {
* Add a member to a set
* @param key The set key
* @param member The member to add
* @returns Promise that resolves with 1 if the member was added, 0 if it
* already existed
* @returns Promise that resolves with 1 if the member was added, 0 if it already existed
*/
sadd(key: RedisClient.KeyLike, member: string): Promise<number>;
@@ -322,8 +298,7 @@ declare module "bun" {
* Remove a member from a set
* @param key The set key
* @param member The member to remove
* @returns Promise that resolves with 1 if the member was removed, 0 if it
* didn't exist
* @returns Promise that resolves with 1 if the member was removed, 0 if it didn't exist
*/
srem(key: RedisClient.KeyLike, member: string): Promise<number>;
@@ -337,16 +312,14 @@ declare module "bun" {
/**
* Get a random member from a set
* @param key The set key
* @returns Promise that resolves with a random member, or null if the set
* is empty
* @returns Promise that resolves with a random member, or null if the set is empty
*/
srandmember(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Remove and return a random member from a set
* @param key The set key
* @returns Promise that resolves with the removed member, or null if the
* set is empty
* @returns Promise that resolves with the removed member, or null if the set is empty
*/
spop(key: RedisClient.KeyLike): Promise<string | null>;
@@ -413,32 +386,28 @@ declare module "bun" {
/**
* Remove and get the first element in a list
* @param key The list key
* @returns Promise that resolves with the first element, or null if the
* list is empty
* @returns Promise that resolves with the first element, or null if the list is empty
*/
lpop(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Remove the expiration from a key
* @param key The key to persist
* @returns Promise that resolves with 1 if the timeout was removed, 0 if
* the key doesn't exist or has no timeout
* @returns Promise that resolves with 1 if the timeout was removed, 0 if the key doesn't exist or has no timeout
*/
persist(key: RedisClient.KeyLike): Promise<number>;
/**
* Get the expiration time of a key as a UNIX timestamp in milliseconds
* @param key The key to check
* @returns Promise that resolves with the timestamp, or -1 if the key has
* no expiration, or -2 if the key doesn't exist
* @returns Promise that resolves with the timestamp, or -1 if the key has no expiration, or -2 if the key doesn't exist
*/
pexpiretime(key: RedisClient.KeyLike): Promise<number>;
/**
* Get the time to live for a key in milliseconds
* @param key The key to check
* @returns Promise that resolves with the TTL in milliseconds, or -1 if the
* key has no expiration, or -2 if the key doesn't exist
* @returns Promise that resolves with the TTL in milliseconds, or -1 if the key has no expiration, or -2 if the key doesn't exist
*/
pttl(key: RedisClient.KeyLike): Promise<number>;
@@ -452,48 +421,42 @@ declare module "bun" {
/**
* Get the number of members in a set
* @param key The set key
* @returns Promise that resolves with the cardinality (number of elements)
* of the set
* @returns Promise that resolves with the cardinality (number of elements) of the set
*/
scard(key: RedisClient.KeyLike): Promise<number>;
/**
* Get the length of the value stored in a key
* @param key The key to check
* @returns Promise that resolves with the length of the string value, or 0
* if the key doesn't exist
* @returns Promise that resolves with the length of the string value, or 0 if the key doesn't exist
*/
strlen(key: RedisClient.KeyLike): Promise<number>;
/**
* Get the number of members in a sorted set
* @param key The sorted set key
* @returns Promise that resolves with the cardinality (number of elements)
* of the sorted set
* @returns Promise that resolves with the cardinality (number of elements) of the sorted set
*/
zcard(key: RedisClient.KeyLike): Promise<number>;
/**
* Remove and return members with the highest scores in a sorted set
* @param key The sorted set key
* @returns Promise that resolves with the removed member and its score, or
* null if the set is empty
* @returns Promise that resolves with the removed member and its score, or null if the set is empty
*/
zpopmax(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Remove and return members with the lowest scores in a sorted set
* @param key The sorted set key
* @returns Promise that resolves with the removed member and its score, or
* null if the set is empty
* @returns Promise that resolves with the removed member and its score, or null if the set is empty
*/
zpopmin(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Get one or multiple random members from a sorted set
* @param key The sorted set key
* @returns Promise that resolves with a random member, or null if the set
* is empty
* @returns Promise that resolves with a random member, or null if the set is empty
*/
zrandmember(key: RedisClient.KeyLike): Promise<string | null>;
@@ -501,8 +464,7 @@ declare module "bun" {
* Append a value to a key
* @param key The key to append to
* @param value The value to append
* @returns Promise that resolves with the length of the string after the
* append operation
* @returns Promise that resolves with the length of the string after the append operation
*/
append(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -510,8 +472,7 @@ declare module "bun" {
* Set the value of a key and return its old value
* @param key The key to set
* @param value The value to set
* @returns Promise that resolves with the old value, or null if the key
* didn't exist
* @returns Promise that resolves with the old value, or null if the key didn't exist
*/
getset(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<string | null>;
@@ -519,8 +480,7 @@ declare module "bun" {
* Prepend one or multiple values to a list
* @param key The list key
* @param value The value to prepend
* @returns Promise that resolves with the length of the list after the push
* operation
* @returns Promise that resolves with the length of the list after the push operation
*/
lpush(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -528,8 +488,7 @@ declare module "bun" {
* Prepend a value to a list, only if the list exists
* @param key The list key
* @param value The value to prepend
* @returns Promise that resolves with the length of the list after the push
* operation, or 0 if the list doesn't exist
* @returns Promise that resolves with the length of the list after the push operation, or 0 if the list doesn't exist
*/
lpushx(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -537,8 +496,7 @@ declare module "bun" {
* Add one or more members to a HyperLogLog
* @param key The HyperLogLog key
* @param element The element to add
* @returns Promise that resolves with 1 if the HyperLogLog was altered, 0
* otherwise
* @returns Promise that resolves with 1 if the HyperLogLog was altered, 0 otherwise
*/
pfadd(key: RedisClient.KeyLike, element: string): Promise<number>;
@@ -546,8 +504,7 @@ declare module "bun" {
* Append one or multiple values to a list
* @param key The list key
* @param value The value to append
* @returns Promise that resolves with the length of the list after the push
* operation
* @returns Promise that resolves with the length of the list after the push operation
*/
rpush(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -555,8 +512,7 @@ declare module "bun" {
* Append a value to a list, only if the list exists
* @param key The list key
* @param value The value to append
* @returns Promise that resolves with the length of the list after the push
* operation, or 0 if the list doesn't exist
* @returns Promise that resolves with the length of the list after the push operation, or 0 if the list doesn't exist
*/
rpushx(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -564,8 +520,7 @@ declare module "bun" {
* Set the value of a key, only if the key does not exist
* @param key The key to set
* @param value The value to set
* @returns Promise that resolves with 1 if the key was set, 0 if the key
* was not set
* @returns Promise that resolves with 1 if the key was set, 0 if the key was not set
*/
setnx(key: RedisClient.KeyLike, value: RedisClient.KeyLike): Promise<number>;
@@ -573,16 +528,14 @@ declare module "bun" {
* Get the score associated with the given member in a sorted set
* @param key The sorted set key
* @param member The member to get the score for
* @returns Promise that resolves with the score of the member as a string,
* or null if the member or key doesn't exist
* @returns Promise that resolves with the score of the member as a string, or null if the member or key doesn't exist
*/
zscore(key: RedisClient.KeyLike, member: string): Promise<string | null>;
/**
* Get the values of all specified keys
* @param keys The keys to get
* @returns Promise that resolves with an array of values, with null for
* keys that don't exist
* @returns Promise that resolves with an array of values, with null for keys that don't exist
*/
mget(...keys: RedisClient.KeyLike[]): Promise<(string | null)[]>;
@@ -596,46 +549,37 @@ declare module "bun" {
/**
* Return a serialized version of the value stored at the specified key
* @param key The key to dump
* @returns Promise that resolves with the serialized value, or null if the
* key doesn't exist
* @returns Promise that resolves with the serialized value, or null if the key doesn't exist
*/
dump(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Get the expiration time of a key as a UNIX timestamp in seconds
*
* @param key The key to check
* @returns Promise that resolves with the timestamp, or -1 if the key has
* no expiration, or -2 if the key doesn't exist
* @returns Promise that resolves with the timestamp, or -1 if the key has no expiration, or -2 if the key doesn't exist
*/
expiretime(key: RedisClient.KeyLike): Promise<number>;
/**
* Get the value of a key and delete the key
*
* @param key The key to get and delete
* @returns Promise that resolves with the value of the key, or null if the
* key doesn't exist
* @returns Promise that resolves with the value of the key, or null if the key doesn't exist
*/
getdel(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Get the value of a key and optionally set its expiration
*
* @param key The key to get
* @returns Promise that resolves with the value of the key, or null if the
* key doesn't exist
* @returns Promise that resolves with the value of the key, or null if the key doesn't exist
*/
getex(key: RedisClient.KeyLike): Promise<string | null>;
/**
* Get the value of a key and set its expiration in seconds
*
* @param key The key to get
* @param ex Set the specified expire time, in seconds
* @param seconds The number of seconds until expiration
* @returns Promise that resolves with the value of the key, or null if the
* key doesn't exist
* @returns Promise that resolves with the value of the key, or null if the key doesn't exist
*/
getex(key: RedisClient.KeyLike, ex: "EX", seconds: number): Promise<string | null>;
@@ -650,7 +594,6 @@ declare module "bun" {
/**
* Get the value of a key and set its expiration at a specific Unix timestamp in seconds
*
* @param key The key to get
* @param exat Set the specified Unix time at which the key will expire, in seconds
* @param timestampSeconds The Unix timestamp in seconds
@@ -660,7 +603,6 @@ declare module "bun" {
/**
* Get the value of a key and set its expiration at a specific Unix timestamp in milliseconds
*
* @param key The key to get
* @param pxat Set the specified Unix time at which the key will expire, in milliseconds
* @param timestampMilliseconds The Unix timestamp in milliseconds
@@ -670,7 +612,6 @@ declare module "bun" {
/**
* Get the value of a key and remove its expiration
*
* @param key The key to get
* @param persist Remove the expiration from the key
* @returns Promise that resolves with the value of the key, or null if the key doesn't exist
@@ -685,133 +626,10 @@ declare module "bun" {
/**
* Ping the server with a message
*
* @param message The message to send to the server
* @returns Promise that resolves with the message if the server is reachable, or throws an error if the server is not reachable
*/
ping(message: RedisClient.KeyLike): Promise<string>;
/**
* Publish a message to a Redis channel.
*
* @param channel The channel to publish to.
* @param message The message to publish.
*
* @returns The number of clients that received the message. Note that in a
* cluster this returns the total number of clients in the same node.
*/
publish(channel: string, message: string): Promise<number>;
/**
* Subscribe to a Redis channel.
*
* Subscribing disables automatic pipelining, so all commands will be
* received immediately.
*
* Subscribing moves the channel to a dedicated subscription state which
* prevents most other commands from being executed until unsubscribed. Only
* {@link ping `.ping()`}, {@link subscribe `.subscribe()`}, and
* {@link unsubscribe `.unsubscribe()`} are legal to invoke in a subscribed
* upon channel.
*
* @param channel The channel to subscribe to.
* @param listener The listener to call when a message is received on the
* channel. The listener will receive the message as the first argument and
* the channel as the second argument.
*
* @example
* ```ts
* await client.subscribe("my-channel", (message, channel) => {
* console.log(`Received message on ${channel}: ${message}`);
* });
* ```
*/
subscribe(channel: string, listener: RedisClient.StringPubSubListener): Promise<number>;
/**
* Subscribe to multiple Redis channels.
*
* Subscribing disables automatic pipelining, so all commands will be
* received immediately.
*
* Subscribing moves the channels to a dedicated subscription state in which
* only a limited set of commands can be executed.
*
* @param channels An array of channels to subscribe to.
* @param listener The listener to call when a message is received on any of
* the subscribed channels. The listener will receive the message as the
* first argument and the channel as the second argument.
*/
subscribe(channels: string[], listener: RedisClient.StringPubSubListener): Promise<number>;
/**
* Unsubscribe from a singular Redis channel.
*
* @param channel The channel to unsubscribe from.
*
* If there are no more channels subscribed to, the client automatically
* re-enables pipelining if it was previously enabled.
*
* Unsubscribing moves the channel back to a normal state out of the
* subscription state if all channels have been unsubscribed from. For
* further details on the subscription state, see
* {@link subscribe `.subscribe()`}.
*/
unsubscribe(channel: string): Promise<void>;
/**
* Remove a listener from a given Redis channel.
*
* If there are no more channels subscribed to, the client automatically
* re-enables pipelining if it was previously enabled.
*
* Unsubscribing moves the channel back to a normal state out of the
* subscription state if all channels have been unsubscribed from. For
* further details on the subscription state, see
* {@link subscribe `.subscribe()`}.
*
* @param channel The channel to unsubscribe from.
* @param listener The listener to remove. This is tested against
* referential equality so you must pass the exact same listener instance as
* when subscribing.
*/
unsubscribe(channel: string, listener: RedisClient.StringPubSubListener): Promise<void>;
/**
* Unsubscribe from all registered Redis channels.
*
* The client will automatically re-enable pipelining if it was previously
* enabled.
*
* Unsubscribing moves the channel back to a normal state out of the
* subscription state if all channels have been unsubscribed from. For
* further details on the subscription state, see
* {@link subscribe `.subscribe()`}.
*/
unsubscribe(): Promise<void>;
/**
* Unsubscribe from multiple Redis channels.
*
* @param channels An array of channels to unsubscribe from.
*
* If there are no more channels subscribed to, the client automatically
* re-enables pipelining if it was previously enabled.
*
* Unsubscribing moves the channel back to a normal state out of the
* subscription state if all channels have been unsubscribed from. For
* further details on the subscription state, see
* {@link subscribe `.subscribe()`}.
*/
unsubscribe(channels: string[]): Promise<void>;
/**
* @brief Create a new RedisClient instance with the same configuration as
* the current instance.
*
* This will open up a new connection to the Redis server.
*/
duplicate(): Promise<RedisClient>;
}
/**

View File

@@ -58,7 +58,7 @@ declare module "bun" {
* // "bun"
* ```
*/
function env(newEnv?: Record<string, string | undefined> | NodeJS.Dict<string> | undefined): $;
function env(newEnv?: Record<string, string | undefined>): $;
/**
*
@@ -106,7 +106,7 @@ declare module "bun" {
* expect(stdout.toString()).toBe("LOL!");
* ```
*/
env(newEnv: Record<string, string | undefined> | NodeJS.Dict<string> | undefined): this;
env(newEnv: Record<string, string> | undefined): this;
/**
* By default, the shell will write to the current process's stdout and stderr, as well as buffering that output.

View File

@@ -12,68 +12,6 @@ declare module "bun" {
release(): void;
}
type ArrayType =
| "BOOLEAN"
| "BYTEA"
| "CHAR"
| "NAME"
| "TEXT"
| "CHAR"
| "VARCHAR"
| "SMALLINT"
| "INT2VECTOR"
| "INTEGER"
| "INT"
| "BIGINT"
| "REAL"
| "DOUBLE PRECISION"
| "NUMERIC"
| "MONEY"
| "OID"
| "TID"
| "XID"
| "CID"
| "JSON"
| "JSONB"
| "JSONPATH"
| "XML"
| "POINT"
| "LSEG"
| "PATH"
| "BOX"
| "POLYGON"
| "LINE"
| "CIRCLE"
| "CIDR"
| "MACADDR"
| "INET"
| "MACADDR8"
| "DATE"
| "TIME"
| "TIMESTAMP"
| "TIMESTAMPTZ"
| "INTERVAL"
| "TIMETZ"
| "BIT"
| "VARBIT"
| "ACLITEM"
| "PG_DATABASE"
| (string & {});
/**
* Represents a SQL array parameter
*/
interface SQLArrayParameter {
/**
* The serialized values of the array parameter
*/
serializedValues: string;
/**
* The type of the array parameter
*/
arrayType: ArrayType;
}
/**
* Represents a client within a transaction context Extends SQL with savepoint
* functionality
@@ -103,22 +41,22 @@ declare module "bun" {
class PostgresError extends SQLError {
public readonly code: string;
public readonly errno?: string | undefined;
public readonly detail?: string | undefined;
public readonly hint?: string | undefined;
public readonly severity?: string | undefined;
public readonly position?: string | undefined;
public readonly internalPosition?: string | undefined;
public readonly internalQuery?: string | undefined;
public readonly where?: string | undefined;
public readonly schema?: string | undefined;
public readonly table?: string | undefined;
public readonly column?: string | undefined;
public readonly dataType?: string | undefined;
public readonly constraint?: string | undefined;
public readonly file?: string | undefined;
public readonly line?: string | undefined;
public readonly routine?: string | undefined;
public readonly errno: string | undefined;
public readonly detail: string | undefined;
public readonly hint: string | undefined;
public readonly severity: string | undefined;
public readonly position: string | undefined;
public readonly internalPosition: string | undefined;
public readonly internalQuery: string | undefined;
public readonly where: string | undefined;
public readonly schema: string | undefined;
public readonly table: string | undefined;
public readonly column: string | undefined;
public readonly dataType: string | undefined;
public readonly constraint: string | undefined;
public readonly file: string | undefined;
public readonly line: string | undefined;
public readonly routine: string | undefined;
constructor(
message: string,
@@ -146,8 +84,8 @@ declare module "bun" {
class MySQLError extends SQLError {
public readonly code: string;
public readonly errno?: number | undefined;
public readonly sqlState?: string | undefined;
public readonly errno: number | undefined;
public readonly sqlState: string | undefined;
constructor(message: string, options: { code: string; errno: number | undefined; sqlState: string | undefined });
}
@@ -205,13 +143,13 @@ declare module "bun" {
/**
* Database server hostname
* @deprecated Prefer {@link hostname}
* @default "localhost"
*/
host?: string | undefined;
/**
* Database server hostname
* Database server hostname (alias for host)
* @deprecated Prefer {@link host}
* @default "localhost"
*/
hostname?: string | undefined;
@@ -326,14 +264,13 @@ declare module "bun" {
* Whether to use TLS/SSL for the connection
* @default false
*/
tls?: Bun.BunFile | TLSOptions | boolean | undefined;
tls?: TLSOptions | boolean | undefined;
/**
* Whether to use TLS/SSL for the connection (alias for tls)
* @deprecated Prefer {@link tls}
* @default false
*/
ssl?: Bun.BunFile | TLSOptions | boolean | undefined;
ssl?: TLSOptions | boolean | undefined;
/**
* Unix domain socket path for connection
@@ -692,21 +629,6 @@ declare module "bun" {
*/
reserve(): Promise<ReservedSQL>;
/**
* Creates a new SQL array parameter
* @param values - The values to create the array parameter from
* @param typeNameOrTypeID - The type name or type ID to create the array parameter from, if omitted it will default to JSON
* @returns A new SQL array parameter
*
* @example
* ```ts
* const array = sql.array([1, 2, 3], "INT");
* await sql`CREATE TABLE users_posts (user_id INT, posts_id INT[])`;
* await sql`INSERT INTO users_posts (user_id, posts_id) VALUES (${user.id}, ${array})`;
* ```
*/
array(values: any[], typeNameOrTypeID?: number | ArrayType): SQLArrayParameter;
/**
* Begins a new transaction.
*

View File

@@ -14,6 +14,11 @@
* ```
*/
declare module "bun:test" {
/**
* -- Mocks --
*
* @category Testing
*/
export type Mock<T extends (...args: any[]) => any> = JestMock.Mock<T>;
export const mock: {
@@ -91,7 +96,6 @@ declare module "bun:test" {
export namespace jest {
function restoreAllMocks(): void;
function clearAllMocks(): void;
function resetAllMocks(): void;
function fn<T extends (...args: any[]) => any>(func?: T): Mock<T>;
function setSystemTime(now?: number | Date): void;
function setTimeout(milliseconds: number): void;
@@ -181,9 +185,6 @@ declare module "bun:test" {
* Clear all mock state (calls, results, etc.) without restoring original implementation
*/
clearAllMocks: typeof jest.clearAllMocks;
resetAllMocks: typeof jest.resetAllMocks;
useFakeTimers: typeof jest.useFakeTimers;
useRealTimers: typeof jest.useRealTimers;
};
interface FunctionLike {
@@ -210,31 +211,31 @@ declare module "bun:test" {
*
* @category Testing
*/
export interface Describe<T extends Readonly<any[]>> {
export interface Describe {
(fn: () => void): void;
(label: DescribeLabel, fn: (...args: T) => void): void;
(label: DescribeLabel, fn: () => void): void;
/**
* Skips all other tests, except this group of tests.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
only: Describe<T>;
only(label: DescribeLabel, fn: () => void): void;
/**
* Skips this group of tests.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
skip: Describe<T>;
skip(label: DescribeLabel, fn: () => void): void;
/**
* Marks this group of tests as to be written or to be fixed.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
todo: Describe<T>;
/**
* Marks this group of tests to be executed concurrently.
*/
concurrent: Describe<T>;
/**
* Marks this group of tests to be executed serially (one after another),
* even when the --concurrent flag is used.
*/
serial: Describe<T>;
todo(label: DescribeLabel, fn?: () => void): void;
/**
* Runs this group of tests, only if `condition` is true.
*
@@ -242,27 +243,37 @@ declare module "bun:test" {
*
* @param condition if these tests should run
*/
if(condition: boolean): Describe<T>;
if(condition: boolean): (label: DescribeLabel, fn: () => void) => void;
/**
* Skips this group of tests, if `condition` is true.
*
* @param condition if these tests should be skipped
*/
skipIf(condition: boolean): Describe<T>;
skipIf(condition: boolean): (label: DescribeLabel, fn: () => void) => void;
/**
* Marks this group of tests as to be written or to be fixed, if `condition` is true.
*
* @param condition if these tests should be skipped
*/
todoIf(condition: boolean): Describe<T>;
todoIf(condition: boolean): (label: DescribeLabel, fn: () => void) => void;
/**
* Returns a function that runs for each item in `table`.
*
* @param table Array of Arrays with the arguments that are passed into the test fn for each row.
*/
each<T extends Readonly<[any, ...any[]]>>(table: readonly T[]): Describe<[...T]>;
each<T extends any[]>(table: readonly T[]): Describe<[...T]>;
each<T>(table: T[]): Describe<[T]>;
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: DescribeLabel, fn: (...args: [...T]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T extends any[]>(
table: readonly T[],
): (
label: DescribeLabel,
fn: (...args: Readonly<T>) => void | Promise<unknown>,
options?: number | TestOptions,
) => void;
each<T>(
table: T[],
): (label: DescribeLabel, fn: (...args: T[]) => void | Promise<unknown>, options?: number | TestOptions) => void;
}
/**
* Describes a group of related tests.
@@ -280,7 +291,7 @@ declare module "bun:test" {
* @param label the label for the tests
* @param fn the function that defines the tests
*/
export const describe: Describe<[]>;
export const describe: Describe;
/**
* Skips a group of related tests.
*
@@ -289,9 +300,7 @@ declare module "bun:test" {
* @param label the label for the tests
* @param fn the function that defines the tests
*/
export const xdescribe: Describe<[]>;
type HookOptions = number | { timeout?: number };
export const xdescribe: Describe;
/**
* Runs a function, once, before all the tests.
*
@@ -308,10 +317,7 @@ declare module "bun:test" {
*
* @param fn the function to run
*/
export function beforeAll(
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: HookOptions,
): void;
export function beforeAll(fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
/**
* Runs a function before each test.
*
@@ -322,10 +328,7 @@ declare module "bun:test" {
*
* @param fn the function to run
*/
export function beforeEach(
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: HookOptions,
): void;
export function beforeEach(fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
/**
* Runs a function, once, after all the tests.
*
@@ -342,10 +345,7 @@ declare module "bun:test" {
*
* @param fn the function to run
*/
export function afterAll(
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: HookOptions,
): void;
export function afterAll(fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
/**
* Runs a function after each test.
*
@@ -354,10 +354,7 @@ declare module "bun:test" {
*
* @param fn the function to run
*/
export function afterEach(
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: HookOptions,
): void;
export function afterEach(fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
/**
* Sets the default timeout for all tests in the current file. If a test specifies a timeout, it will
* override this value. The default timeout is 5000ms (5 seconds).
@@ -390,11 +387,6 @@ declare module "bun:test" {
*/
repeats?: number;
}
type IsTuple<T> = T extends readonly unknown[]
? number extends T["length"]
? false // It's an array with unknown length, not a tuple
: true // It's an array with a fixed length (a tuple)
: false; // Not an array at all
/**
* Runs a test.
*
@@ -418,10 +410,10 @@ declare module "bun:test" {
*
* @category Testing
*/
export interface Test<T extends Readonly<any[]>> {
export interface Test {
(
label: string,
fn: (...args: IsTuple<T> extends true ? [...T, (err?: unknown) => void] : T) => void | Promise<unknown>,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
/**
* - If a `number`, sets the timeout for the test in milliseconds.
* - If an `object`, sets the options for the test.
@@ -432,13 +424,29 @@ declare module "bun:test" {
options?: number | TestOptions,
): void;
/**
* Skips all other tests, except this test.
* Skips all other tests, except this test when run with the `--only` option.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
only: Test<T>;
only(
label: string,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Skips this test.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
skip: Test<T>;
skip(
label: string,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Marks this test as to be written or to be fixed.
*
@@ -446,8 +454,16 @@ declare module "bun:test" {
* if the test passes, the test will be marked as `fail` in the results; you will have to
* remove the `.todo` or check that your test
* is implemented correctly.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
todo: Test<T>;
todo(
label: string,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Marks this test as failing.
*
@@ -458,17 +474,16 @@ declare module "bun:test" {
*
* `test.failing` is very similar to {@link test.todo} except that it always
* runs, regardless of the `--todo` flag.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
failing: Test<T>;
/**
* Runs the test concurrently with other concurrent tests.
*/
concurrent: Test<T>;
/**
* Forces the test to run serially (not in parallel),
* even when the --concurrent flag is used.
*/
serial: Test<T>;
failing(
label: string,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Runs this test, if `condition` is true.
*
@@ -476,46 +491,51 @@ declare module "bun:test" {
*
* @param condition if the test should run
*/
if(condition: boolean): Test<T>;
if(
condition: boolean,
): (
label: string,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Skips this test, if `condition` is true.
*
* @param condition if the test should be skipped
*/
skipIf(condition: boolean): Test<T>;
skipIf(
condition: boolean,
): (
label: string,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Marks this test as to be written or to be fixed, if `condition` is true.
*
* @param condition if the test should be marked TODO
*/
todoIf(condition: boolean): Test<T>;
/**
* Marks this test as failing, if `condition` is true.
*
* @param condition if the test should be marked as failing
*/
failingIf(condition: boolean): Test<T>;
/**
* Runs the test concurrently with other concurrent tests, if `condition` is true.
*
* @param condition if the test should run concurrently
*/
concurrentIf(condition: boolean): Test<T>;
/**
* Forces the test to run serially (not in parallel), if `condition` is true.
* This applies even when the --concurrent flag is used.
*
* @param condition if the test should run serially
*/
serialIf(condition: boolean): Test<T>;
todoIf(
condition: boolean,
): (
label: string,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Returns a function that runs for each item in `table`.
*
* @param table Array of Arrays with the arguments that are passed into the test fn for each row.
*/
each<T extends Readonly<[any, ...any[]]>>(table: readonly T[]): Test<[...T]>;
each<T extends any[]>(table: readonly T[]): Test<[...T]>;
each<T>(table: T[]): Test<[T]>;
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: string, fn: (...args: [...T]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T extends any[]>(
table: readonly T[],
): (label: string, fn: (...args: Readonly<T>) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T>(
table: T[],
): (label: string, fn: (...args: T[]) => void | Promise<unknown>, options?: number | TestOptions) => void;
}
/**
* Runs a test.
@@ -533,7 +553,7 @@ declare module "bun:test" {
* @param label the label for the test
* @param fn the test function
*/
export const test: Test<[]>;
export const test: Test;
export { test as it, xtest as xit };
/**
@@ -544,7 +564,7 @@ declare module "bun:test" {
* @param label the label for the test
* @param fn the test function
*/
export const xtest: Test<[]>;
export const xtest: Test;
/**
* Asserts that a value matches some criteria.
@@ -568,9 +588,7 @@ declare module "bun:test" {
* @param customFailMessage an optional custom message to display if the test fails.
* */
(actual?: never, customFailMessage?: string): Matchers<undefined>;
<T = unknown>(actual: T, customFailMessage?: string): Matchers<T>;
<T = unknown>(actual?: T, customFailMessage?: string): Matchers<T | undefined>;
<T = unknown>(actual?: T, customFailMessage?: string): Matchers<T>;
/**
* Access to negated asymmetric matchers.
@@ -888,7 +906,6 @@ declare module "bun:test" {
* @param message the message to display if the test fails (optional)
*/
pass: (message?: string) => void;
/**
* Assertion which fails.
*
@@ -900,7 +917,6 @@ declare module "bun:test" {
* expect().not.fail("hi");
*/
fail: (message?: string) => void;
/**
* Asserts that a value equals what is expected.
*
@@ -914,15 +930,9 @@ declare module "bun:test" {
* expect([123]).toBe([123]); // fail, use toEqual()
* expect(3 + 0.14).toBe(3.14); // fail, use toBeCloseTo()
*
* // TypeScript errors:
* expect("hello").toBe(3.14); // typescript error + fail
* expect("hello").toBe<number>(3.14); // no typescript error, but still fails
*
* @param expected the expected value
*/
toBe(expected: T): void;
toBe<X = T>(expected: NoInfer<X>): void;
/**
* Asserts that a number is odd.
*
@@ -932,7 +942,6 @@ declare module "bun:test" {
* expect(2).not.toBeOdd();
*/
toBeOdd(): void;
/**
* Asserts that a number is even.
*
@@ -942,7 +951,6 @@ declare module "bun:test" {
* expect(1).not.toBeEven();
*/
toBeEven(): void;
/**
* Asserts that value is close to the expected by floating point precision.
*
@@ -961,7 +969,6 @@ declare module "bun:test" {
* @param numDigits the number of digits to check after the decimal point. Default is `2`
*/
toBeCloseTo(expected: number, numDigits?: number): void;
/**
* Asserts that a value is deeply equal to what is expected.
*
@@ -974,8 +981,6 @@ declare module "bun:test" {
* @param expected the expected value
*/
toEqual(expected: T): void;
toEqual<X = T>(expected: NoInfer<X>): void;
/**
* Asserts that a value is deeply and strictly equal to
* what is expected.
@@ -1000,8 +1005,6 @@ declare module "bun:test" {
* @param expected the expected value
*/
toStrictEqual(expected: T): void;
toStrictEqual<X = T>(expected: NoInfer<X>): void;
/**
* Asserts that the value is deep equal to an element in the expected array.
*
@@ -1014,9 +1017,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toBeOneOf(expected: Iterable<T>): void;
toBeOneOf<X = T>(expected: NoInfer<Iterable<X>>): void;
toBeOneOf(expected: Array<unknown> | Iterable<unknown>): void;
/**
* Asserts that a value contains what is expected.
*
@@ -1030,9 +1031,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContain(expected: T extends Iterable<infer U> ? U : T): void;
toContain<X = T>(expected: NoInfer<X extends Iterable<infer U> ? U : X>): void;
toContain(expected: unknown): void;
/**
* Asserts that an `object` contains a key.
*
@@ -1046,9 +1045,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContainKey(expected: keyof T): void;
toContainKey<X = T>(expected: NoInfer<keyof X>): void;
toContainKey(expected: unknown): void;
/**
* Asserts that an `object` contains all the provided keys.
*
@@ -1063,9 +1060,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContainAllKeys(expected: Array<keyof T>): void;
toContainAllKeys<X = T>(expected: NoInfer<Array<keyof X>>): void;
toContainAllKeys(expected: unknown): void;
/**
* Asserts that an `object` contains at least one of the provided keys.
* Asserts that an `object` contains all the provided keys.
@@ -1080,16 +1075,12 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContainAnyKeys(expected: Array<keyof T>): void;
toContainAnyKeys<X = T>(expected: NoInfer<Array<keyof X>>): void;
toContainAnyKeys(expected: unknown): void;
/**
* Asserts that an `object` contain the provided value.
*
* This method is deep and will look through child properties to find the
* expected value.
*
* The input value must be an object.
* The value must be an object
*
* @example
* const shallow = { hello: "world" };
@@ -1113,16 +1104,11 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
// Contributor note: In theory we could type this better but it would be a
// slow union to compute...
toContainValue(expected: unknown): void;
/**
* Asserts that an `object` contain the provided value.
*
* This is the same as {@link toContainValue}, but accepts an array of
* values instead.
*
* The value must be an object
*
* @example
@@ -1132,7 +1118,7 @@ declare module "bun:test" {
* expect(o).not.toContainValues(['qux', 'foo']);
* @param expected the expected value
*/
toContainValues(expected: Array<unknown>): void;
toContainValues(expected: unknown): void;
/**
* Asserts that an `object` contain all the provided values.
@@ -1146,7 +1132,7 @@ declare module "bun:test" {
* expect(o).not.toContainAllValues(['bar', 'foo']);
* @param expected the expected value
*/
toContainAllValues(expected: Array<unknown>): void;
toContainAllValues(expected: unknown): void;
/**
* Asserts that an `object` contain any provided value.
@@ -1161,7 +1147,7 @@ declare module "bun:test" {
* expect(o).not.toContainAnyValues(['qux']);
* @param expected the expected value
*/
toContainAnyValues(expected: Array<unknown>): void;
toContainAnyValues(expected: unknown): void;
/**
* Asserts that an `object` contains all the provided keys.
@@ -1173,9 +1159,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContainKeys(expected: Array<keyof T>): void;
toContainKeys<X = T>(expected: NoInfer<Array<keyof X>>): void;
toContainKeys(expected: unknown): void;
/**
* Asserts that a value contains and equals what is expected.
*
@@ -1188,9 +1172,7 @@ declare module "bun:test" {
*
* @param expected the expected value
*/
toContainEqual(expected: T extends Iterable<infer U> ? U : T): void;
toContainEqual<X = T>(expected: NoInfer<X extends Iterable<infer U> ? U : X>): void;
toContainEqual(expected: unknown): void;
/**
* Asserts that a value has a `.length` property
* that is equal to the expected length.
@@ -1202,7 +1184,6 @@ declare module "bun:test" {
* @param length the expected length
*/
toHaveLength(length: number): void;
/**
* Asserts that a value has a property with the
* expected name, and value if provided.
@@ -1217,7 +1198,6 @@ declare module "bun:test" {
* @param value the expected property value, if provided
*/
toHaveProperty(keyPath: string | number | Array<string | number>, value?: unknown): void;
/**
* Asserts that a value is "truthy".
*
@@ -1230,7 +1210,6 @@ declare module "bun:test" {
* expect({}).toBeTruthy();
*/
toBeTruthy(): void;
/**
* Asserts that a value is "falsy".
*
@@ -1243,7 +1222,6 @@ declare module "bun:test" {
* expect({}).toBeTruthy();
*/
toBeFalsy(): void;
/**
* Asserts that a value is defined. (e.g. is not `undefined`)
*
@@ -1252,7 +1230,6 @@ declare module "bun:test" {
* expect(undefined).toBeDefined(); // fail
*/
toBeDefined(): void;
/**
* Asserts that the expected value is an instance of value
*
@@ -1261,7 +1238,6 @@ declare module "bun:test" {
* expect(null).toBeInstanceOf(Array); // fail
*/
toBeInstanceOf(value: unknown): void;
/**
* Asserts that a value is `undefined`.
*
@@ -1270,7 +1246,6 @@ declare module "bun:test" {
* expect(null).toBeUndefined(); // fail
*/
toBeUndefined(): void;
/**
* Asserts that a value is `null`.
*
@@ -1279,7 +1254,6 @@ declare module "bun:test" {
* expect(undefined).toBeNull(); // fail
*/
toBeNull(): void;
/**
* Asserts that a value is `NaN`.
*
@@ -1291,7 +1265,6 @@ declare module "bun:test" {
* expect("notanumber").toBeNaN(); // fail
*/
toBeNaN(): void;
/**
* Asserts that a value is a `number` and is greater than the expected value.
*
@@ -1303,7 +1276,6 @@ declare module "bun:test" {
* @param expected the expected number
*/
toBeGreaterThan(expected: number | bigint): void;
/**
* Asserts that a value is a `number` and is greater than or equal to the expected value.
*
@@ -1315,7 +1287,6 @@ declare module "bun:test" {
* @param expected the expected number
*/
toBeGreaterThanOrEqual(expected: number | bigint): void;
/**
* Asserts that a value is a `number` and is less than the expected value.
*
@@ -1327,7 +1298,6 @@ declare module "bun:test" {
* @param expected the expected number
*/
toBeLessThan(expected: number | bigint): void;
/**
* Asserts that a value is a `number` and is less than or equal to the expected value.
*
@@ -1339,7 +1309,6 @@ declare module "bun:test" {
* @param expected the expected number
*/
toBeLessThanOrEqual(expected: number | bigint): void;
/**
* Asserts that a function throws an error.
*
@@ -1360,7 +1329,6 @@ declare module "bun:test" {
* @param expected the expected error, error message, or error pattern
*/
toThrow(expected?: unknown): void;
/**
* Asserts that a function throws an error.
*
@@ -1382,7 +1350,6 @@ declare module "bun:test" {
* @alias toThrow
*/
toThrowError(expected?: unknown): void;
/**
* Asserts that a value matches a regular expression or includes a substring.
*
@@ -1393,7 +1360,6 @@ declare module "bun:test" {
* @param expected the expected substring or pattern.
*/
toMatch(expected: string | RegExp): void;
/**
* Asserts that a value matches the most recent snapshot.
*
@@ -1402,7 +1368,6 @@ declare module "bun:test" {
* @param hint Hint used to identify the snapshot in the snapshot file.
*/
toMatchSnapshot(hint?: string): void;
/**
* Asserts that a value matches the most recent snapshot.
*
@@ -1415,7 +1380,6 @@ declare module "bun:test" {
* @param hint Hint used to identify the snapshot in the snapshot file.
*/
toMatchSnapshot(propertyMatchers?: object, hint?: string): void;
/**
* Asserts that a value matches the most recent inline snapshot.
*
@@ -1426,7 +1390,6 @@ declare module "bun:test" {
* @param value The latest automatically-updated snapshot value.
*/
toMatchInlineSnapshot(value?: string): void;
/**
* Asserts that a value matches the most recent inline snapshot.
*
@@ -1442,7 +1405,6 @@ declare module "bun:test" {
* @param value The latest automatically-updated snapshot value.
*/
toMatchInlineSnapshot(propertyMatchers?: object, value?: string): void;
/**
* Asserts that a function throws an error matching the most recent snapshot.
*
@@ -1456,7 +1418,6 @@ declare module "bun:test" {
* @param value The latest automatically-updated snapshot value.
*/
toThrowErrorMatchingSnapshot(hint?: string): void;
/**
* Asserts that a function throws an error matching the most recent snapshot.
*
@@ -1470,7 +1431,6 @@ declare module "bun:test" {
* @param value The latest automatically-updated snapshot value.
*/
toThrowErrorMatchingInlineSnapshot(value?: string): void;
/**
* Asserts that an object matches a subset of properties.
*
@@ -1481,7 +1441,6 @@ declare module "bun:test" {
* @param subset Subset of properties to match with.
*/
toMatchObject(subset: object): void;
/**
* Asserts that a value is empty.
*
@@ -1492,7 +1451,6 @@ declare module "bun:test" {
* expect(new Set()).toBeEmpty();
*/
toBeEmpty(): void;
/**
* Asserts that a value is an empty `object`.
*
@@ -1501,7 +1459,6 @@ declare module "bun:test" {
* expect({ a: 'hello' }).not.toBeEmptyObject();
*/
toBeEmptyObject(): void;
/**
* Asserts that a value is `null` or `undefined`.
*
@@ -1510,7 +1467,6 @@ declare module "bun:test" {
* expect(undefined).toBeNil();
*/
toBeNil(): void;
/**
* Asserts that a value is a `array`.
*
@@ -1521,7 +1477,6 @@ declare module "bun:test" {
* expect({}).not.toBeArray();
*/
toBeArray(): void;
/**
* Asserts that a value is a `array` of a certain length.
*
@@ -1533,7 +1488,6 @@ declare module "bun:test" {
* expect({}).not.toBeArrayOfSize(0);
*/
toBeArrayOfSize(size: number): void;
/**
* Asserts that a value is a `boolean`.
*
@@ -1544,7 +1498,6 @@ declare module "bun:test" {
* expect(0).not.toBeBoolean();
*/
toBeBoolean(): void;
/**
* Asserts that a value is `true`.
*
@@ -1554,7 +1507,6 @@ declare module "bun:test" {
* expect(1).not.toBeTrue();
*/
toBeTrue(): void;
/**
* Asserts that a value matches a specific type.
*
@@ -1565,7 +1517,6 @@ declare module "bun:test" {
* expect([]).not.toBeTypeOf("boolean");
*/
toBeTypeOf(type: "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"): void;
/**
* Asserts that a value is `false`.
*
@@ -1575,7 +1526,6 @@ declare module "bun:test" {
* expect(0).not.toBeFalse();
*/
toBeFalse(): void;
/**
* Asserts that a value is a `number`.
*
@@ -1586,7 +1536,6 @@ declare module "bun:test" {
* expect(BigInt(1)).not.toBeNumber();
*/
toBeNumber(): void;
/**
* Asserts that a value is a `number`, and is an integer.
*
@@ -1596,7 +1545,6 @@ declare module "bun:test" {
* expect(NaN).not.toBeInteger();
*/
toBeInteger(): void;
/**
* Asserts that a value is an `object`.
*
@@ -1606,7 +1554,6 @@ declare module "bun:test" {
* expect(NaN).not.toBeObject();
*/
toBeObject(): void;
/**
* Asserts that a value is a `number`, and is not `NaN` or `Infinity`.
*
@@ -1617,7 +1564,6 @@ declare module "bun:test" {
* expect(Infinity).not.toBeFinite();
*/
toBeFinite(): void;
/**
* Asserts that a value is a positive `number`.
*
@@ -1627,7 +1573,6 @@ declare module "bun:test" {
* expect(NaN).not.toBePositive();
*/
toBePositive(): void;
/**
* Asserts that a value is a negative `number`.
*
@@ -1637,7 +1582,6 @@ declare module "bun:test" {
* expect(NaN).not.toBeNegative();
*/
toBeNegative(): void;
/**
* Asserts that a value is a number between a start and end value.
*
@@ -1645,7 +1589,6 @@ declare module "bun:test" {
* @param end the end number (exclusive)
*/
toBeWithin(start: number, end: number): void;
/**
* Asserts that a value is equal to the expected string, ignoring any whitespace.
*
@@ -1656,7 +1599,6 @@ declare module "bun:test" {
* @param expected the expected string
*/
toEqualIgnoringWhitespace(expected: string): void;
/**
* Asserts that a value is a `symbol`.
*
@@ -1665,7 +1607,6 @@ declare module "bun:test" {
* expect("foo").not.toBeSymbol();
*/
toBeSymbol(): void;
/**
* Asserts that a value is a `function`.
*
@@ -1673,7 +1614,6 @@ declare module "bun:test" {
* expect(() => {}).toBeFunction();
*/
toBeFunction(): void;
/**
* Asserts that a value is a `Date` object.
*
@@ -1685,7 +1625,6 @@ declare module "bun:test" {
* expect("2020-03-01").not.toBeDate();
*/
toBeDate(): void;
/**
* Asserts that a value is a valid `Date` object.
*
@@ -1695,7 +1634,6 @@ declare module "bun:test" {
* expect("2020-03-01").not.toBeValidDate();
*/
toBeValidDate(): void;
/**
* Asserts that a value is a `string`.
*
@@ -1705,7 +1643,6 @@ declare module "bun:test" {
* expect(123).not.toBeString();
*/
toBeString(): void;
/**
* Asserts that a value includes a `string`.
*
@@ -1714,14 +1651,12 @@ declare module "bun:test" {
* @param expected the expected substring
*/
toInclude(expected: string): void;
/**
* Asserts that a value includes a `string` {times} times.
* @param expected the expected substring
* @param times the number of times the substring should occur
*/
toIncludeRepeated(expected: string, times: number): void;
/**
* Checks whether a value satisfies a custom condition.
* @param {Function} predicate - The custom condition to be satisfied. It should be a function that takes a value as an argument (in this case the value from expect) and returns a boolean.
@@ -1733,21 +1668,18 @@ declare module "bun:test" {
* @link https://jest-extended.jestcommunity.dev/docs/matchers/toSatisfy
*/
toSatisfy(predicate: (value: T) => boolean): void;
/**
* Asserts that a value starts with a `string`.
*
* @param expected the string to start with
*/
toStartWith(expected: string): void;
/**
* Asserts that a value ends with a `string`.
*
* @param expected the string to end with
*/
toEndWith(expected: string): void;
/**
* Ensures that a mock function has returned successfully at least once.
*
@@ -1788,51 +1720,42 @@ declare module "bun:test" {
* Ensures that a mock function is called.
*/
toHaveBeenCalled(): void;
/**
* Ensures that a mock function is called an exact number of times.
* @alias toHaveBeenCalled
*/
toBeCalled(): void;
/**
* Ensures that a mock function is called an exact number of times.
*/
toHaveBeenCalledTimes(expected: number): void;
/**
* Ensure that a mock function is called with specific arguments.
* @alias toHaveBeenCalledTimes
*/
toBeCalledTimes(expected: number): void;
/**
* Ensure that a mock function is called with specific arguments.
*/
toHaveBeenCalledWith(...expected: unknown[]): void;
/**
* Ensure that a mock function is called with specific arguments.
* @alias toHaveBeenCalledWith
*/
toBeCalledWith(...expected: unknown[]): void;
/**
* Ensure that a mock function is called with specific arguments for the last call.
*/
toHaveBeenLastCalledWith(...expected: unknown[]): void;
/**
* Ensure that a mock function is called with specific arguments for the nth call.
* @alias toHaveBeenCalledWith
*/
lastCalledWith(...expected: unknown[]): void;
/**
* Ensure that a mock function is called with specific arguments for the nth call.
*/
toHaveBeenNthCalledWith(n: number, ...expected: unknown[]): void;
/**
* Ensure that a mock function is called with specific arguments for the nth call.
* @alias toHaveBeenCalledWith

View File

@@ -25,23 +25,6 @@
#include <stdio.h>
#include <stdlib.h>
#if BUN_DEBUG
// Debug network traffic logging
static FILE *debug_recv_file = NULL;
static FILE *debug_send_file = NULL;
static int debug_logging_initialized = 0;
static void init_debug_logging() {
if (debug_logging_initialized) return;
debug_logging_initialized = 1;
const char *recv_path = getenv("BUN_RECV");
const char *send_path = getenv("BUN_SEND");
if (recv_path) if (!debug_recv_file) debug_recv_file = fopen(recv_path, "w");
if (send_path) if (!debug_send_file) debug_send_file = fopen(send_path, "w");
}
#endif
#ifndef _WIN32
// Necessary for the stdint include
#ifndef _GNU_SOURCE
@@ -601,7 +584,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol, in
if (UNLIKELY(created_fd == -1)) {
if (err != NULL) {
*err = errno;
*err = LIBUS_ERR;
}
return LIBUS_SOCKET_ERROR;
}
@@ -614,7 +597,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol, in
if (UNLIKELY(created_fd == -1)) {
if (err != NULL) {
*err = errno;
*err = LIBUS_ERR;
}
return LIBUS_SOCKET_ERROR;
}
@@ -738,17 +721,6 @@ ssize_t bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) {
continue;
}
#if BUN_DEBUG
// Debug logging for received data
if (ret > 0) {
init_debug_logging();
if (debug_recv_file) {
fwrite(buf, 1, ret, debug_recv_file);
fflush(debug_recv_file);
}
}
#endif
return ret;
}
}
@@ -770,7 +742,7 @@ ssize_t bsd_recvmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct msghdr *msg, int flags) {
#if !defined(_WIN32)
#include <sys/uio.h>
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) {
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length, int *error) {
struct iovec chunks[2];
chunks[0].iov_base = (char *)header;
@@ -784,15 +756,18 @@ ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_le
if (UNLIKELY(IS_EINTR(written))) {
continue;
}
if (written < 0) {
if (error != NULL) *error = LIBUS_ERR;
}
return written;
}
}
#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 bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length, int *error) {
ssize_t written = bsd_send(fd, header, header_length, error);
if (written == header_length) {
ssize_t second_write = bsd_send(fd, payload, payload_length);
ssize_t second_write = bsd_send(fd, payload, payload_length, error);
if (second_write > 0) {
written += second_write;
}
@@ -801,7 +776,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 *error) {
while (1) {
// MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD)
@@ -815,17 +790,10 @@ ssize_t bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length) {
if (UNLIKELY(IS_EINTR(rc))) {
continue;
}
#if BUN_DEBUG
// Debug logging for sent data
if (rc > 0) {
init_debug_logging();
if (debug_send_file) {
fwrite(buf, 1, rc, debug_send_file);
fflush(debug_send_file);
}
if (rc < 0) {
if (error != NULL) *error = LIBUS_ERR;
return -1;
}
#endif
return rc;
}
@@ -858,16 +826,18 @@ static int us_internal_bind_and_listen(LIBUS_SOCKET_DESCRIPTOR listenFd, struct
do
result = bind(listenFd, listenAddr, listenAddrLength);
while (IS_EINTR(result));
if (result == -1) {
*error = LIBUS_ERR;
if (error != NULL) *error = LIBUS_ERR;
return -1;
}
do
result = listen(listenFd, backlog);
while (IS_EINTR(result));
*error = LIBUS_ERR;
if (result == -1) {
if (error != NULL) *error = LIBUS_ERR;
return -1;
}
return result;
}
@@ -990,7 +960,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int
struct addrinfo *listenAddr;
for (struct addrinfo *a = result; a != NULL; a = a->ai_next) {
if (a->ai_family == AF_INET6) {
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol, NULL);
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol, error);
if (listenFd == LIBUS_SOCKET_ERROR) {
continue;
}
@@ -1007,7 +977,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int
for (struct addrinfo *a = result; a != NULL; a = a->ai_next) {
if (a->ai_family == AF_INET) {
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol, NULL);
listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol, error);
if (listenFd == LIBUS_SOCKET_ERROR) {
continue;
}
@@ -1035,7 +1005,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int
#include <sys/stat.h>
#include <stddef.h>
static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path, size_t path_len, int* dirfd_linux_workaround_for_unix_path_len, struct sockaddr_un *server_address, size_t* addrlen) {
static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path, size_t path_len, int* dirfd_linux_workaround_for_unix_path_len, struct sockaddr_un *server_address, size_t* addrlen, int *error) {
memset(server_address, 0, sizeof(struct sockaddr_un));
server_address->sun_family = AF_UNIX;
@@ -1044,7 +1014,7 @@ static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path,
// simulate ENOENT
SetLastError(ERROR_PATH_NOT_FOUND);
#else
errno = ENOENT;
*error = ENOENT;
#endif
return LIBUS_SOCKET_ERROR;
}
@@ -1063,13 +1033,13 @@ static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path,
// if the path is just a single character, or the path is too long, we cannot use this method
if (dirname_len < 2 || (path_len - dirname_len + 1) >= sizeof(server_address->sun_path)) {
errno = ENAMETOOLONG;
if (error != NULL) *error = EINVAL;
return LIBUS_SOCKET_ERROR;
}
char dirname_buf[4096];
if (dirname_len + 1 > sizeof(dirname_buf)) {
errno = ENAMETOOLONG;
if (error != NULL) *error = EINVAL;
return LIBUS_SOCKET_ERROR;
}
@@ -1078,14 +1048,14 @@ static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path,
int socket_dir_fd = open(dirname_buf, O_CLOEXEC | O_PATH | O_DIRECTORY, 0700);
if (socket_dir_fd == -1) {
errno = ENAMETOOLONG;
if (error != NULL) *error = EINVAL;
return LIBUS_SOCKET_ERROR;
}
int sun_path_len = snprintf(server_address->sun_path, sizeof(server_address->sun_path), "/proc/self/fd/%d/%s", socket_dir_fd, path + dirname_len);
if (sun_path_len >= sizeof(server_address->sun_path) || sun_path_len < 0) {
close(socket_dir_fd);
errno = ENAMETOOLONG;
if (error != NULL) *error = EINVAL;
return LIBUS_SOCKET_ERROR;
}
@@ -1108,7 +1078,10 @@ static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path,
// simulate ENAMETOOLONG
SetLastError(ERROR_FILENAME_EXCED_RANGE);
#else
errno = ENAMETOOLONG;
// was ENAMETOOLONG but changed to EINVAL to align with node's UV_PIPE_NO_TRUNCATE flag
// https://docs.libuv.org/en/v1.x/pipe.html#c.uv_pipe_bind2
// https://github.com/nodejs/node/blob/v24.0.0/src/pipe_wrap.cc#L168
*error = EINVAL;
#endif
return LIBUS_SOCKET_ERROR;
@@ -1121,7 +1094,7 @@ static LIBUS_SOCKET_DESCRIPTOR bsd_create_unix_socket_address(const char *path,
static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_listen_socket_unix(const char* path, int options, struct sockaddr_un* server_address, size_t addrlen, int* error) {
LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR;
listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0, NULL);
listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0, error);
if (listenFd == LIBUS_SOCKET_ERROR) {
return LIBUS_SOCKET_ERROR;
@@ -1153,7 +1126,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, size_t l
int dirfd_linux_workaround_for_unix_path_len = -1;
struct sockaddr_un server_address;
size_t addrlen = 0;
if (bsd_create_unix_socket_address(path, len, &dirfd_linux_workaround_for_unix_path_len, &server_address, &addrlen)) {
if (bsd_create_unix_socket_address(path, len, &dirfd_linux_workaround_for_unix_path_len, &server_address, &addrlen, error)) {
return LIBUS_SOCKET_ERROR;
}
@@ -1264,11 +1237,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port, int op
/* We bind here as well */
if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) {
if (err != NULL) {
#ifdef _WIN32
*err = WSAGetLastError();
#else
*err = errno;
#endif
*err = LIBUS_ERR;
}
bsd_close_socket(listenFd);
freeaddrinfo(result);
@@ -1367,7 +1336,7 @@ int bsd_disconnect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd) {
// return 0; // no ecn defaults to 0
// }
static int bsd_do_connect_raw(LIBUS_SOCKET_DESCRIPTOR fd, struct sockaddr *addr, size_t namelen)
static int bsd_do_connect_raw(LIBUS_SOCKET_DESCRIPTOR fd, struct sockaddr *addr, size_t namelen, int *error)
{
#ifdef _WIN32
while (1) {
@@ -1386,6 +1355,7 @@ static int bsd_do_connect_raw(LIBUS_SOCKET_DESCRIPTOR fd, struct sockaddr *addr,
continue;
}
default: {
if (error != NULL) *error = us_wsa_to_libuv_errno(err);
return err;
}
}
@@ -1406,7 +1376,8 @@ static int bsd_do_connect_raw(LIBUS_SOCKET_DESCRIPTOR fd, struct sockaddr *addr,
return 0;
}
return errno;
if (error != NULL) *error = LIBUS_ERR;
return r;
}
return 0;
@@ -1448,8 +1419,8 @@ static int is_loopback(struct sockaddr_storage *sockaddr) {
}
#endif
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, int options) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(addr->ss_family, SOCK_STREAM, 0, NULL);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, int options, int *error) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(addr->ss_family, SOCK_STREAM, 0, error);
if (fd == LIBUS_SOCKET_ERROR) {
return LIBUS_SOCKET_ERROR;
}
@@ -1486,7 +1457,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr,
}
#endif
int rc = bsd_do_connect_raw(fd, (struct sockaddr*) addr, addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
int rc = bsd_do_connect_raw(fd, (struct sockaddr*) addr, addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), error);
if (rc != 0) {
bsd_close_socket(fd);
@@ -1495,8 +1466,8 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr,
return fd;
}
static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, struct sockaddr_un* server_address, const size_t addrlen) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0, NULL);
static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, struct sockaddr_un* server_address, const size_t addrlen, int *error) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0, error);
if (fd == LIBUS_SOCKET_ERROR) {
return LIBUS_SOCKET_ERROR;
@@ -1504,7 +1475,7 @@ static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const cha
win32_set_nonblocking(fd);
if (bsd_do_connect_raw(fd, (struct sockaddr *)server_address, addrlen) != 0) {
if (bsd_do_connect_raw(fd, (struct sockaddr *)server_address, addrlen, error) != 0) {
bsd_close_socket(fd);
return LIBUS_SOCKET_ERROR;
}
@@ -1512,15 +1483,15 @@ static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const cha
return fd;
}
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t len, int options) {
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, int *error) {
struct sockaddr_un server_address;
size_t addrlen = 0;
int dirfd_linux_workaround_for_unix_path_len = -1;
if (bsd_create_unix_socket_address(server_path, len, &dirfd_linux_workaround_for_unix_path_len, &server_address, &addrlen)) {
if (bsd_create_unix_socket_address(server_path, len, &dirfd_linux_workaround_for_unix_path_len, &server_address, &addrlen, error)) {
return LIBUS_SOCKET_ERROR;
}
LIBUS_SOCKET_DESCRIPTOR fd = internal_bsd_create_connect_socket_unix(server_path, len, options, &server_address, addrlen);
LIBUS_SOCKET_DESCRIPTOR fd = internal_bsd_create_connect_socket_unix(server_path, len, options, &server_address, addrlen, error);
#if defined(__linux__)
if (dirfd_linux_workaround_for_unix_path_len != -1) {

View File

@@ -430,8 +430,8 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock
return ls;
}
struct us_socket_t* us_socket_context_connect_resolved_dns(struct us_socket_context_t *context, struct sockaddr_storage* addr, int options, int socket_ext_size) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(addr, options);
struct us_socket_t* us_socket_context_connect_resolved_dns(struct us_socket_context_t *context, struct sockaddr_storage* addr, int options, int socket_ext_size, int *error) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(addr, options, error);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
return NULL;
}
@@ -501,10 +501,10 @@ static bool try_parse_ip(const char *ip_str, int port, struct sockaddr_storage *
return 0;
}
void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size, int* has_dns_resolved) {
void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size, int* has_dns_resolved, int *error) {
#ifndef LIBUS_NO_SSL
if (ssl == 1) {
return us_internal_ssl_socket_context_connect((struct us_internal_ssl_socket_context_t *) context, host, port, options, socket_ext_size, has_dns_resolved);
return us_internal_ssl_socket_context_connect((struct us_internal_ssl_socket_context_t *) context, host, port, options, socket_ext_size, has_dns_resolved, error);
}
#endif
@@ -514,7 +514,7 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
struct sockaddr_storage addr;
if (try_parse_ip(host, port, &addr)) {
*has_dns_resolved = 1;
return us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size);
return us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size, error);
}
struct addrinfo_request* ai_req;
@@ -534,7 +534,7 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
struct sockaddr_storage addr;
init_addr_with_port(&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);
struct us_socket_t *s = us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size, error);
Bun__addrinfo_freeRequest(ai_req, s == NULL);
return s;
}
@@ -566,7 +566,7 @@ int start_connections(struct us_connecting_socket_t *c, int count) {
for (; c->addrinfo_head != NULL && opened < count; c->addrinfo_head = c->addrinfo_head->ai_next) {
struct sockaddr_storage addr;
init_addr_with_port(c->addrinfo_head, c->port, &addr);
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(&addr, c->options);
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(&addr, c->options, NULL);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
continue;
}
@@ -655,6 +655,8 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
break;
}
default: {
if (error == WSAENOTCONN) error = WSAECONNREFUSED;
error = us_wsa_to_libuv_errno(error);
break;
}
}
@@ -733,14 +735,14 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
}
}
struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, size_t pathlen, int options, int socket_ext_size) {
struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, size_t pathlen, int options, int socket_ext_size, int *error) {
#ifndef LIBUS_NO_SSL
if (ssl) {
return (struct us_socket_t *) us_internal_ssl_socket_context_connect_unix((struct us_internal_ssl_socket_context_t *) context, server_path, pathlen, options, socket_ext_size);
return (struct us_socket_t *) us_internal_ssl_socket_context_connect_unix((struct us_internal_ssl_socket_context_t *) context, server_path, pathlen, options, socket_ext_size, error);
}
#endif
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, pathlen, options);
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, pathlen, options, error);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
return 0;
}

View File

@@ -135,7 +135,7 @@ 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);
int written = us_socket_write(0, loop_ssl_data->ssl_socket, data, length, NULL);
BIO_clear_retry_flags(bio);
if (!written) {
@@ -1608,11 +1608,11 @@ static void us_internal_zero_ssl_data_for_connected_socket_before_onopen(struct
// TODO does this need more changes?
struct us_socket_t *us_internal_ssl_socket_context_connect(
struct us_internal_ssl_socket_context_t *context, const char *host,
int port, int options, int socket_ext_size, int* is_connecting) {
int port, int options, int socket_ext_size, int* is_connecting, int *error) {
struct us_internal_ssl_socket_t *s = (struct us_internal_ssl_socket_t *)us_socket_context_connect(
2, &context->sc, host, port, options,
sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) +
socket_ext_size, is_connecting);
socket_ext_size, is_connecting, error);
if (*is_connecting && s) {
us_internal_zero_ssl_data_for_connected_socket_before_onopen(s);
}
@@ -1621,11 +1621,11 @@ struct us_socket_t *us_internal_ssl_socket_context_connect(
}
struct us_socket_t *us_internal_ssl_socket_context_connect_unix(
struct us_internal_ssl_socket_context_t *context, const char *server_path,
size_t pathlen, int options, int socket_ext_size) {
size_t pathlen, int options, int socket_ext_size, int *error) {
struct us_socket_t *s = (struct us_socket_t *)us_socket_context_connect_unix(
0, &context->sc, server_path, pathlen, options,
sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) +
socket_ext_size);
socket_ext_size, error);
if (s) {
us_internal_zero_ssl_data_for_connected_socket_before_onopen((struct us_internal_ssl_socket_t*) s);
}
@@ -1746,7 +1746,7 @@ int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s,
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, NULL);
}
int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s,

View File

@@ -6,46 +6,10 @@
#include <atomic>
#include <string.h>
#include "./default_ciphers.h"
// System-specific includes for certificate loading
#include "./root_certs_platform.h"
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#else
// Linux/Unix includes
#include <dirent.h>
#include <stdio.h>
#include <limits.h>
#endif
static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]);
extern "C" void BUN__warn__extra_ca_load_failed(const char* filename, const char* error_msg);
// Forward declarations for platform-specific functions
// (Actual implementations are in platform-specific files)
// External variable from Zig CLI arguments
extern "C" bool Bun__Node__UseSystemCA;
// Helper function to check if system CA should be used
// Checks both CLI flag (--use-system-ca) and environment variable (NODE_USE_SYSTEM_CA=1)
static bool us_should_use_system_ca() {
// Check CLI flag first
if (Bun__Node__UseSystemCA) {
return true;
}
// Check environment variable
const char *use_system_ca = getenv("NODE_USE_SYSTEM_CA");
return use_system_ca && strcmp(use_system_ca, "1") == 0;
}
// Platform-specific system certificate loading implementations are separated:
// - macOS: root_certs_darwin.cpp (Security framework with dynamic loading)
// - Windows: root_certs_windows.cpp (Windows CryptoAPI)
// - Linux/Unix: us_load_system_certificates_linux() below
// This callback is used to avoid the default passphrase callback in OpenSSL
// which will typically prompt for the passphrase. The prompting is designed
// for the OpenSSL CLI, but works poorly for this case because it involves
@@ -137,8 +101,7 @@ end:
static void us_internal_init_root_certs(
X509 *root_cert_instances[root_certs_size],
STACK_OF(X509) *&root_extra_cert_instances,
STACK_OF(X509) *&root_system_cert_instances) {
STACK_OF(X509) *&root_extra_cert_instances) {
static std::atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT;
static std::atomic_bool root_cert_instances_initialized = 0;
@@ -160,17 +123,6 @@ static void us_internal_init_root_certs(
if (extra_certs && extra_certs[0]) {
root_extra_cert_instances = us_ssl_ctx_load_all_certs_from_file(extra_certs);
}
// load system certificates if NODE_USE_SYSTEM_CA=1
if (us_should_use_system_ca()) {
#ifdef __APPLE__
us_load_system_certificates_macos(&root_system_cert_instances);
#elif defined(_WIN32)
us_load_system_certificates_windows(&root_system_cert_instances);
#else
us_load_system_certificates_linux(&root_system_cert_instances);
#endif
}
}
atomic_flag_clear_explicit(&root_cert_instances_lock,
@@ -185,15 +137,12 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) {
struct us_default_ca_certificates {
X509 *root_cert_instances[root_certs_size];
STACK_OF(X509) *root_extra_cert_instances;
STACK_OF(X509) *root_system_cert_instances;
};
us_default_ca_certificates* us_get_default_ca_certificates() {
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL, NULL};
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,
default_ca_certificates.root_system_cert_instances);
us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances);
return &default_ca_certificates;
}
@@ -202,33 +151,20 @@ STACK_OF(X509) *us_get_root_extra_cert_instances() {
return us_get_default_ca_certificates()->root_extra_cert_instances;
}
STACK_OF(X509) *us_get_root_system_cert_instances() {
if (!us_should_use_system_ca())
return NULL;
// Ensure single-path initialization via us_internal_init_root_certs
auto certs = us_get_default_ca_certificates();
return certs->root_system_cert_instances;
}
extern "C" X509_STORE *us_get_default_ca_store() {
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
return NULL;
}
// Only load system default paths when NODE_USE_SYSTEM_CA=1
// Otherwise, rely on bundled certificates only (like Node.js behavior)
if (us_should_use_system_ca()) {
if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
}
if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(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;
STACK_OF(X509) *root_system_cert_instances = default_ca_certificates->root_system_cert_instances;
// load all root_cert_instances on the default ca store
for (size_t i = 0; i < root_certs_size; i++) {
@@ -247,59 +183,8 @@ extern "C" X509_STORE *us_get_default_ca_store() {
}
}
if (us_should_use_system_ca() && root_system_cert_instances) {
for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) {
X509 *cert = sk_X509_value(root_system_cert_instances, i);
X509_up_ref(cert);
X509_STORE_add_cert(store, cert);
}
}
return store;
}
extern "C" const char *us_get_default_ciphers() {
return DEFAULT_CIPHER_LIST;
}
// Platform-specific implementations for loading system certificates
#if defined(_WIN32)
// Windows implementation is split to avoid header conflicts:
// - root_certs_windows.cpp loads raw certificate data (uses Windows headers)
// - This file converts raw data to X509* (uses OpenSSL headers)
#include <vector>
struct RawCertificate {
std::vector<unsigned char> data;
};
// Defined in root_certs_windows.cpp - loads raw certificate data
extern void us_load_system_certificates_windows_raw(
std::vector<RawCertificate>& raw_certs);
// Convert raw Windows certificates to OpenSSL X509 format
void us_load_system_certificates_windows(STACK_OF(X509) **system_certs) {
*system_certs = sk_X509_new_null();
if (*system_certs == NULL) {
return;
}
// Load raw certificates from Windows stores
std::vector<RawCertificate> raw_certs;
us_load_system_certificates_windows_raw(raw_certs);
// Convert each raw certificate to X509
for (const auto& raw_cert : raw_certs) {
const unsigned char* data = raw_cert.data.data();
X509* x509_cert = d2i_X509(NULL, &data, raw_cert.data.size());
if (x509_cert != NULL) {
sk_X509_push(*system_certs, x509_cert);
}
}
}
#else
// Linux and other Unix-like systems - implementation is in root_certs_linux.cpp
extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs);
#endif
}

View File

@@ -1,431 +0,0 @@
#ifdef __APPLE__
#include <dlfcn.h>
#include <CoreFoundation/CoreFoundation.h>
#include <atomic>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <stdio.h>
// Security framework types and constants - dynamically loaded
typedef struct OpaqueSecCertificateRef* SecCertificateRef;
typedef struct OpaqueSecTrustRef* SecTrustRef;
typedef struct OpaqueSecPolicyRef* SecPolicyRef;
typedef int32_t OSStatus;
typedef uint32_t SecTrustSettingsDomain;
// Security framework constants
enum {
errSecSuccess = 0,
errSecItemNotFound = -25300,
};
// Trust settings domains
enum {
kSecTrustSettingsDomainUser = 0,
kSecTrustSettingsDomainAdmin = 1,
kSecTrustSettingsDomainSystem = 2,
};
// Trust status enumeration
enum class TrustStatus {
TRUSTED,
DISTRUSTED,
UNSPECIFIED
};
// Dynamic Security framework loader
class SecurityFramework {
public:
void* handle;
void* cf_handle;
// Core Foundation constants
CFStringRef kSecClass;
CFStringRef kSecClassCertificate;
CFStringRef kSecMatchLimit;
CFStringRef kSecMatchLimitAll;
CFStringRef kSecReturnRef;
CFStringRef kSecMatchTrustedOnly;
CFBooleanRef kCFBooleanTrue;
CFAllocatorRef kCFAllocatorDefault;
CFArrayCallBacks* kCFTypeArrayCallBacks;
CFDictionaryKeyCallBacks* kCFTypeDictionaryKeyCallBacks;
CFDictionaryValueCallBacks* kCFTypeDictionaryValueCallBacks;
// Core Foundation function pointers
CFMutableArrayRef (*CFArrayCreateMutable)(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks);
CFArrayRef (*CFArrayCreate)(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks);
void (*CFArraySetValueAtIndex)(CFMutableArrayRef theArray, CFIndex idx, const void *value);
const void* (*CFArrayGetValueAtIndex)(CFArrayRef theArray, CFIndex idx);
CFIndex (*CFArrayGetCount)(CFArrayRef theArray);
void (*CFRelease)(CFTypeRef cf);
CFDictionaryRef (*CFDictionaryCreate)(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks);
const UInt8* (*CFDataGetBytePtr)(CFDataRef theData);
CFIndex (*CFDataGetLength)(CFDataRef theData);
// Security framework function pointers
OSStatus (*SecItemCopyMatching)(CFDictionaryRef query, CFTypeRef *result);
CFDataRef (*SecCertificateCopyData)(SecCertificateRef certificate);
OSStatus (*SecTrustCreateWithCertificates)(CFArrayRef certificates, CFArrayRef policies, SecTrustRef *trust);
SecPolicyRef (*SecPolicyCreateSSL)(Boolean server, CFStringRef hostname);
Boolean (*SecTrustEvaluateWithError)(SecTrustRef trust, CFErrorRef *error);
OSStatus (*SecTrustSettingsCopyTrustSettings)(SecCertificateRef certRef, SecTrustSettingsDomain domain, CFArrayRef *trustSettings);
SecurityFramework() : handle(nullptr), cf_handle(nullptr),
kSecClass(nullptr), kSecClassCertificate(nullptr),
kSecMatchLimit(nullptr), kSecMatchLimitAll(nullptr),
kSecReturnRef(nullptr), kSecMatchTrustedOnly(nullptr), kCFBooleanTrue(nullptr),
kCFAllocatorDefault(nullptr), kCFTypeArrayCallBacks(nullptr),
kCFTypeDictionaryKeyCallBacks(nullptr), kCFTypeDictionaryValueCallBacks(nullptr),
CFArrayCreateMutable(nullptr), CFArrayCreate(nullptr),
CFArraySetValueAtIndex(nullptr), CFArrayGetValueAtIndex(nullptr),
CFArrayGetCount(nullptr), CFRelease(nullptr),
CFDictionaryCreate(nullptr), CFDataGetBytePtr(nullptr), CFDataGetLength(nullptr),
SecItemCopyMatching(nullptr), SecCertificateCopyData(nullptr),
SecTrustCreateWithCertificates(nullptr), SecPolicyCreateSSL(nullptr),
SecTrustEvaluateWithError(nullptr), SecTrustSettingsCopyTrustSettings(nullptr) {}
~SecurityFramework() {
if (handle) {
dlclose(handle);
}
if (cf_handle) {
dlclose(cf_handle);
}
}
bool load() {
if (handle && cf_handle) return true; // Already loaded
// Load CoreFoundation framework
cf_handle = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY | RTLD_LOCAL);
if (!cf_handle) {
fprintf(stderr, "Failed to load CoreFoundation framework: %s\n", dlerror());
return false;
}
// Load Security framework
handle = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
fprintf(stderr, "Failed to load Security framework: %s\n", dlerror());
dlclose(cf_handle);
cf_handle = nullptr;
return false;
}
// Load constants and functions
if (!load_constants()) {
if (handle) {
dlclose(handle);
handle = nullptr;
}
if (cf_handle) {
dlclose(cf_handle);
cf_handle = nullptr;
}
return false;
}
if (!load_functions()) {
if (handle) {
dlclose(handle);
handle = nullptr;
}
if (cf_handle) {
dlclose(cf_handle);
cf_handle = nullptr;
}
return false;
}
return true;
}
private:
bool load_constants() {
// Load Security framework constants
void* ptr = dlsym(handle, "kSecClass");
if (!ptr) { fprintf(stderr, "DEBUG: kSecClass not found\n"); return false; }
kSecClass = *(CFStringRef*)ptr;
ptr = dlsym(handle, "kSecClassCertificate");
if (!ptr) { fprintf(stderr, "DEBUG: kSecClassCertificate not found\n"); return false; }
kSecClassCertificate = *(CFStringRef*)ptr;
ptr = dlsym(handle, "kSecMatchLimit");
if (!ptr) { fprintf(stderr, "DEBUG: kSecMatchLimit not found\n"); return false; }
kSecMatchLimit = *(CFStringRef*)ptr;
ptr = dlsym(handle, "kSecMatchLimitAll");
if (!ptr) { fprintf(stderr, "DEBUG: kSecMatchLimitAll not found\n"); return false; }
kSecMatchLimitAll = *(CFStringRef*)ptr;
ptr = dlsym(handle, "kSecReturnRef");
if (!ptr) { fprintf(stderr, "DEBUG: kSecReturnRef not found\n"); return false; }
kSecReturnRef = *(CFStringRef*)ptr;
ptr = dlsym(handle, "kSecMatchTrustedOnly");
if (!ptr) { fprintf(stderr, "DEBUG: kSecMatchTrustedOnly not found\n"); return false; }
kSecMatchTrustedOnly = *(CFStringRef*)ptr;
// Load CoreFoundation constants
ptr = dlsym(cf_handle, "kCFBooleanTrue");
if (!ptr) { fprintf(stderr, "DEBUG: kCFBooleanTrue not found\n"); return false; }
kCFBooleanTrue = *(CFBooleanRef*)ptr;
ptr = dlsym(cf_handle, "kCFAllocatorDefault");
if (!ptr) { fprintf(stderr, "DEBUG: kCFAllocatorDefault not found\n"); return false; }
kCFAllocatorDefault = *(CFAllocatorRef*)ptr;
ptr = dlsym(cf_handle, "kCFTypeArrayCallBacks");
if (!ptr) { fprintf(stderr, "DEBUG: kCFTypeArrayCallBacks not found\n"); return false; }
kCFTypeArrayCallBacks = (CFArrayCallBacks*)ptr;
ptr = dlsym(cf_handle, "kCFTypeDictionaryKeyCallBacks");
if (!ptr) { fprintf(stderr, "DEBUG: kCFTypeDictionaryKeyCallBacks not found\n"); return false; }
kCFTypeDictionaryKeyCallBacks = (CFDictionaryKeyCallBacks*)ptr;
ptr = dlsym(cf_handle, "kCFTypeDictionaryValueCallBacks");
if (!ptr) { fprintf(stderr, "DEBUG: kCFTypeDictionaryValueCallBacks not found\n"); return false; }
kCFTypeDictionaryValueCallBacks = (CFDictionaryValueCallBacks*)ptr;
return true;
}
bool load_functions() {
// Load CoreFoundation functions
CFArrayCreateMutable = (CFMutableArrayRef (*)(CFAllocatorRef, CFIndex, const CFArrayCallBacks*))dlsym(cf_handle, "CFArrayCreateMutable");
CFArrayCreate = (CFArrayRef (*)(CFAllocatorRef, const void**, CFIndex, const CFArrayCallBacks*))dlsym(cf_handle, "CFArrayCreate");
CFArraySetValueAtIndex = (void (*)(CFMutableArrayRef, CFIndex, const void*))dlsym(cf_handle, "CFArraySetValueAtIndex");
CFArrayGetValueAtIndex = (const void* (*)(CFArrayRef, CFIndex))dlsym(cf_handle, "CFArrayGetValueAtIndex");
CFArrayGetCount = (CFIndex (*)(CFArrayRef))dlsym(cf_handle, "CFArrayGetCount");
CFRelease = (void (*)(CFTypeRef))dlsym(cf_handle, "CFRelease");
CFDictionaryCreate = (CFDictionaryRef (*)(CFAllocatorRef, const void**, const void**, CFIndex, const CFDictionaryKeyCallBacks*, const CFDictionaryValueCallBacks*))dlsym(cf_handle, "CFDictionaryCreate");
CFDataGetBytePtr = (const UInt8* (*)(CFDataRef))dlsym(cf_handle, "CFDataGetBytePtr");
CFDataGetLength = (CFIndex (*)(CFDataRef))dlsym(cf_handle, "CFDataGetLength");
// Load Security framework functions
SecItemCopyMatching = (OSStatus (*)(CFDictionaryRef, CFTypeRef*))dlsym(handle, "SecItemCopyMatching");
SecCertificateCopyData = (CFDataRef (*)(SecCertificateRef))dlsym(handle, "SecCertificateCopyData");
SecTrustCreateWithCertificates = (OSStatus (*)(CFArrayRef, CFArrayRef, SecTrustRef*))dlsym(handle, "SecTrustCreateWithCertificates");
SecPolicyCreateSSL = (SecPolicyRef (*)(Boolean, CFStringRef))dlsym(handle, "SecPolicyCreateSSL");
SecTrustEvaluateWithError = (Boolean (*)(SecTrustRef, CFErrorRef*))dlsym(handle, "SecTrustEvaluateWithError");
SecTrustSettingsCopyTrustSettings = (OSStatus (*)(SecCertificateRef, SecTrustSettingsDomain, CFArrayRef*))dlsym(handle, "SecTrustSettingsCopyTrustSettings");
return CFArrayCreateMutable && CFArrayCreate && CFArraySetValueAtIndex &&
CFArrayGetValueAtIndex && CFArrayGetCount && CFRelease &&
CFDictionaryCreate && CFDataGetBytePtr && CFDataGetLength &&
SecItemCopyMatching && SecCertificateCopyData &&
SecTrustCreateWithCertificates && SecPolicyCreateSSL &&
SecTrustEvaluateWithError && SecTrustSettingsCopyTrustSettings;
}
};
// Global instance for dynamic loading
static std::atomic<SecurityFramework*> g_security_framework{nullptr};
static SecurityFramework* get_security_framework() {
SecurityFramework* framework = g_security_framework.load();
if (!framework) {
SecurityFramework* new_framework = new SecurityFramework();
if (new_framework->load()) {
SecurityFramework* expected = nullptr;
if (g_security_framework.compare_exchange_strong(expected, new_framework)) {
framework = new_framework;
} else {
delete new_framework;
framework = expected;
}
} else {
delete new_framework;
framework = nullptr;
}
}
return framework;
}
// Helper function to determine if a certificate is self-issued
static bool is_certificate_self_issued(X509* cert) {
X509_NAME* subject = X509_get_subject_name(cert);
X509_NAME* issuer = X509_get_issuer_name(cert);
return subject && issuer && X509_NAME_cmp(subject, issuer) == 0;
}
// Validate certificate trust using Security framework
static bool is_certificate_trust_valid(SecurityFramework* security, SecCertificateRef cert_ref) {
CFMutableArrayRef subj_certs = security->CFArrayCreateMutable(nullptr, 1, security->kCFTypeArrayCallBacks);
if (!subj_certs) return false;
security->CFArraySetValueAtIndex(subj_certs, 0, cert_ref);
SecPolicyRef policy = security->SecPolicyCreateSSL(true, nullptr);
if (!policy) {
security->CFRelease(subj_certs);
return false;
}
CFArrayRef policies = security->CFArrayCreate(nullptr, (const void**)&policy, 1, security->kCFTypeArrayCallBacks);
if (!policies) {
security->CFRelease(policy);
security->CFRelease(subj_certs);
return false;
}
SecTrustRef sec_trust = nullptr;
OSStatus ortn = security->SecTrustCreateWithCertificates(subj_certs, policies, &sec_trust);
bool result = false;
if (ortn == errSecSuccess && sec_trust) {
result = security->SecTrustEvaluateWithError(sec_trust, nullptr);
}
// Cleanup
if (sec_trust) security->CFRelease(sec_trust);
security->CFRelease(policies);
security->CFRelease(policy);
security->CFRelease(subj_certs);
return result;
}
// Check trust settings for policy (simplified version)
static TrustStatus is_trust_settings_trusted_for_policy(SecurityFramework* security, CFArrayRef trust_settings, bool is_self_issued) {
if (!trust_settings) {
return TrustStatus::UNSPECIFIED;
}
// Empty trust settings array means "always trust this certificate"
if (security->CFArrayGetCount(trust_settings) == 0) {
return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
}
// For simplicity, we'll do basic checking here
// A full implementation would parse the trust dictionary entries
return TrustStatus::UNSPECIFIED;
}
// Check if certificate is trusted for server auth policy
static bool is_certificate_trusted_for_policy(SecurityFramework* security, X509* cert, SecCertificateRef cert_ref) {
bool is_self_issued = is_certificate_self_issued(cert);
bool trust_evaluated = false;
// Check user trust domain, then admin domain
for (const auto& trust_domain : {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem}) {
CFArrayRef trust_settings = nullptr;
OSStatus err = security->SecTrustSettingsCopyTrustSettings(cert_ref, trust_domain, &trust_settings);
if (err != errSecSuccess && err != errSecItemNotFound) {
continue;
}
if (err == errSecSuccess && trust_settings) {
TrustStatus result = is_trust_settings_trusted_for_policy(security, trust_settings, is_self_issued);
security->CFRelease(trust_settings);
if (result == TrustStatus::TRUSTED) {
return true;
} else if (result == TrustStatus::DISTRUSTED) {
return false;
}
}
// If no trust settings and we haven't evaluated trust yet, check trust validity
if (!trust_settings && !trust_evaluated) {
if (is_certificate_trust_valid(security, cert_ref)) {
return true;
}
trust_evaluated = true;
}
}
return false;
}
// Main function to load system certificates on macOS
extern "C" void us_load_system_certificates_macos(STACK_OF(X509) **system_certs) {
*system_certs = sk_X509_new_null();
if (!*system_certs) {
return;
}
SecurityFramework* security = get_security_framework();
if (!security) {
return; // Fail silently
}
// Create search dictionary for certificates
CFTypeRef search_keys[] = {
security->kSecClass,
security->kSecMatchLimit,
security->kSecReturnRef,
security->kSecMatchTrustedOnly,
};
CFTypeRef search_values[] = {
security->kSecClassCertificate,
security->kSecMatchLimitAll,
security->kCFBooleanTrue,
security->kCFBooleanTrue,
};
CFDictionaryRef search = security->CFDictionaryCreate(
security->kCFAllocatorDefault,
search_keys,
search_values,
4,
security->kCFTypeDictionaryKeyCallBacks,
security->kCFTypeDictionaryValueCallBacks
);
if (!search) {
return;
}
CFArrayRef certificates = nullptr;
OSStatus status = security->SecItemCopyMatching(search, (CFTypeRef*)&certificates);
security->CFRelease(search);
if (status != errSecSuccess || !certificates) {
return;
}
CFIndex count = security->CFArrayGetCount(certificates);
for (CFIndex i = 0; i < count; ++i) {
SecCertificateRef cert_ref = (SecCertificateRef)security->CFArrayGetValueAtIndex(certificates, i);
if (!cert_ref) continue;
// Get certificate data
CFDataRef cert_data = security->SecCertificateCopyData(cert_ref);
if (!cert_data) continue;
// Convert to X509
const unsigned char* data_ptr = security->CFDataGetBytePtr(cert_data);
long data_len = security->CFDataGetLength(cert_data);
X509* x509_cert = d2i_X509(nullptr, &data_ptr, data_len);
security->CFRelease(cert_data);
if (!x509_cert) continue;
// Only consider CA certificates
if (X509_check_ca(x509_cert) == 1 &&
is_certificate_trusted_for_policy(security, x509_cert, cert_ref)) {
sk_X509_push(*system_certs, x509_cert);
} else {
X509_free(x509_cert);
}
}
security->CFRelease(certificates);
}
// Cleanup function for Security framework
extern "C" void us_cleanup_security_framework() {
SecurityFramework* framework = g_security_framework.exchange(nullptr);
if (framework) {
delete framework;
}
}
#endif // __APPLE__

View File

@@ -5,7 +5,6 @@
#define CPPDECL extern "C"
STACK_OF(X509) *us_get_root_extra_cert_instances();
STACK_OF(X509) *us_get_root_system_cert_instances();
#else
#define CPPDECL extern

View File

@@ -1,170 +0,0 @@
#ifndef _WIN32
#ifndef __APPLE__
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
extern "C" void BUN__warn__extra_ca_load_failed(const char* filename, const char* error_msg);
// Helper function to load certificates from a directory
static void load_certs_from_directory(const char* dir_path, STACK_OF(X509)* cert_stack) {
DIR* dir = opendir(dir_path);
if (!dir) {
return;
}
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
// Skip . and ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// Check if file has .crt, .pem, or .cer extension
const char* ext = strrchr(entry->d_name, '.');
if (!ext || (strcmp(ext, ".crt") != 0 && strcmp(ext, ".pem") != 0 && strcmp(ext, ".cer") != 0)) {
continue;
}
// Build full path
char filepath[PATH_MAX];
snprintf(filepath, sizeof(filepath), "%s/%s", dir_path, entry->d_name);
// Try to load certificate
FILE* file = fopen(filepath, "r");
if (file) {
X509* cert = PEM_read_X509(file, NULL, NULL, NULL);
fclose(file);
if (cert) {
if (!sk_X509_push(cert_stack, cert)) {
X509_free(cert);
}
}
}
}
closedir(dir);
}
// Helper function to load certificates from a bundle file
static void load_certs_from_bundle(const char* bundle_path, STACK_OF(X509)* cert_stack) {
FILE* file = fopen(bundle_path, "r");
if (!file) {
return;
}
X509* cert;
while ((cert = PEM_read_X509(file, NULL, NULL, NULL)) != NULL) {
if (!sk_X509_push(cert_stack, cert)) {
X509_free(cert);
break;
}
}
ERR_clear_error();
fclose(file);
}
// Main function to load system certificates on Linux and other Unix-like systems
extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs) {
*system_certs = sk_X509_new_null();
if (*system_certs == NULL) {
return;
}
// First check environment variables (same as Node.js and OpenSSL)
const char* ssl_cert_file = getenv("SSL_CERT_FILE");
const char* ssl_cert_dir = getenv("SSL_CERT_DIR");
// If SSL_CERT_FILE is set, load from it
if (ssl_cert_file && strlen(ssl_cert_file) > 0) {
load_certs_from_bundle(ssl_cert_file, *system_certs);
}
// If SSL_CERT_DIR is set, load from each directory (colon-separated)
if (ssl_cert_dir && strlen(ssl_cert_dir) > 0) {
char* dir_copy = strdup(ssl_cert_dir);
if (dir_copy) {
char* token = strtok(dir_copy, ":");
while (token != NULL) {
// Skip empty tokens
if (strlen(token) > 0) {
load_certs_from_directory(token, *system_certs);
}
token = strtok(NULL, ":");
}
free(dir_copy);
}
}
// If environment variables were set, use only those (even if they yield zero certs)
if (ssl_cert_file || ssl_cert_dir) {
return;
}
// Otherwise, load certificates from standard Linux/Unix paths
// These are the common locations for system certificates
// Common certificate bundle locations (single file with multiple certs)
// These paths are based on common Linux distributions and OpenSSL defaults
static const char* bundle_paths[] = {
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/pki/tls/cert.pem", // Fedora/RHEL 7+
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7+
"/etc/ssl/cert.pem", // Alpine Linux, macOS OpenSSL
"/usr/local/etc/openssl/cert.pem", // Homebrew OpenSSL on macOS
"/usr/local/share/ca-certificates/ca-certificates.crt", // Custom CA installs
NULL
};
// Common certificate directory locations (multiple files)
// Note: OpenSSL expects hashed symlinks in directories (c_rehash format)
static const char* dir_paths[] = {
"/etc/ssl/certs", // Common location (Debian/Ubuntu with hashed links)
"/etc/pki/tls/certs", // RHEL/Fedora
"/usr/share/ca-certificates", // Debian/Ubuntu (original certs, not hashed)
"/usr/local/share/certs", // FreeBSD
"/etc/openssl/certs", // NetBSD
"/var/ssl/certs", // AIX
"/usr/local/etc/openssl/certs", // Homebrew OpenSSL on macOS
"/System/Library/OpenSSL/certs", // macOS system OpenSSL (older versions)
NULL
};
// Try loading from bundle files first
for (const char** path = bundle_paths; *path != NULL; path++) {
load_certs_from_bundle(*path, *system_certs);
}
// Then try loading from directories
for (const char** path = dir_paths; *path != NULL; path++) {
load_certs_from_directory(*path, *system_certs);
}
// Also check NODE_EXTRA_CA_CERTS environment variable
const char* extra_ca_certs = getenv("NODE_EXTRA_CA_CERTS");
if (extra_ca_certs && strlen(extra_ca_certs) > 0) {
FILE* file = fopen(extra_ca_certs, "r");
if (file) {
X509* cert;
while ((cert = PEM_read_X509(file, NULL, NULL, NULL)) != NULL) {
sk_X509_push(*system_certs, cert);
}
fclose(file);
} else {
BUN__warn__extra_ca_load_failed(extra_ca_certs, "Failed to open file");
}
}
}
#endif // !__APPLE__
#endif // !_WIN32

View File

@@ -1,18 +0,0 @@
#pragma once
#include <openssl/x509.h>
// Platform-specific certificate loading functions
extern "C" {
// Load system certificates for the current platform
void us_load_system_certificates_linux(STACK_OF(X509) **system_certs);
void us_load_system_certificates_macos(STACK_OF(X509) **system_certs);
void us_load_system_certificates_windows(STACK_OF(X509) **system_certs);
// Platform-specific cleanup functions
#ifdef __APPLE__
void us_cleanup_security_framework();
#endif
}

View File

@@ -1,53 +0,0 @@
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#include <vector>
#include <cstring>
// Forward declaration to avoid including OpenSSL headers here
// This prevents conflicts with Windows macros like X509_NAME
// Note: We don't use STACK_OF macro here since we don't have OpenSSL headers
// Structure to hold raw certificate data
struct RawCertificate {
std::vector<unsigned char> data;
};
// Helper function to load raw certificates from a Windows certificate store
static void LoadRawCertsFromStore(std::vector<RawCertificate>& raw_certs,
DWORD store_flags,
const wchar_t* store_name) {
HCERTSTORE cert_store = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
0,
0,
store_flags | CERT_STORE_READONLY_FLAG,
store_name
);
if (cert_store == NULL) {
return;
}
PCCERT_CONTEXT cert_context = NULL;
while ((cert_context = CertEnumCertificatesInStore(cert_store, cert_context)) != NULL) {
RawCertificate raw_cert;
raw_cert.data.assign(cert_context->pbCertEncoded,
cert_context->pbCertEncoded + cert_context->cbCertEncoded);
raw_certs.push_back(std::move(raw_cert));
}
CertCloseStore(cert_store, 0);
}
// Main function to load raw system certificates on Windows
// Returns certificates as raw DER data to avoid OpenSSL header conflicts
extern void us_load_system_certificates_windows_raw(
std::vector<RawCertificate>& raw_certs) {
// Load only from ROOT by default
LoadRawCertsFromStore(raw_certs, CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");
LoadRawCertsFromStore(raw_certs, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"ROOT");
}
#endif // _WIN32

View File

@@ -63,8 +63,9 @@ void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop,
#endif
#ifdef _WIN32
int us_wsa_to_libuv_errno(int);
#define IS_EINTR(rc) (rc == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
#define LIBUS_ERR WSAGetLastError()
#define LIBUS_ERR us_wsa_to_libuv_errno(WSAGetLastError())
#else
#include <errno.h>
#define IS_EINTR(rc) (rc == -1 && errno == EINTR)
@@ -410,11 +411,12 @@ struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(
struct us_socket_t *us_internal_ssl_socket_context_connect(
us_internal_ssl_socket_context_r context, const char *host,
int port, int options, int socket_ext_size, int* is_resolved);
int port, int options, int socket_ext_size, int* is_resolved,
int *error);
struct us_socket_t *us_internal_ssl_socket_context_connect_unix(
us_internal_ssl_socket_context_r context, const char *server_path,
size_t pathlen, int options, int socket_ext_size);
size_t pathlen, int options, int socket_ext_size, int *error);
int us_internal_ssl_socket_write(us_internal_ssl_socket_r s,
const char *data, int length);

View File

@@ -210,11 +210,11 @@ 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 *error);
#if !defined(_WIN32)
ssize_t bsd_sendmsg(LIBUS_SOCKET_DESCRIPTOR fd, const struct msghdr *msg, int flags);
#endif
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length);
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length, int *error);
int bsd_would_block();
// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
@@ -228,9 +228,9 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port, int op
int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int port);
int bsd_disconnect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, int options);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, int options, int *error);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t pathlen, int options);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t pathlen, int options, int *error);
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0

View File

@@ -328,10 +328,10 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) nonnull_fn_d
per the happy eyeballs algorithm
*/
void *us_socket_context_connect(int ssl, struct us_socket_context_t * nonnull_arg context,
const char *host, int port, int options, int socket_ext_size, int *is_connecting) __attribute__((nonnull(2)));
const char *host, int port, int options, int socket_ext_size, int *is_connecting, int *error) __attribute__((nonnull(2)));
struct us_socket_t *us_socket_context_connect_unix(int ssl, us_socket_context_r context,
const char *server_path, size_t pathlen, int options, int socket_ext_size) __attribute__((nonnull(2)));
const char *server_path, size_t pathlen, int options, int socket_ext_size, int *error) __attribute__((nonnull(2)));
/* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet.
* Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */
@@ -421,10 +421,10 @@ 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;
int us_socket_write(int ssl, us_socket_r s, const char * nonnull_arg data, int length, int *error);
/* 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;
int us_socket_write2(int ssl, us_socket_r s, const char * nonnull_arg header, int header_length, const char * nonnull_arg payload, int payload_length, int *error);
/* 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 */

View File

@@ -22,16 +22,7 @@
#ifndef WIN32
#include <sys/ioctl.h>
#endif
#if __has_include("wtf/Platform.h")
#include "wtf/Platform.h"
#elif !defined(ASSERT_ENABLED)
#if defined(BUN_DEBUG) || defined(__has_feature) && __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#define ASSERT_ENABLED 1
#else
#define ASSERT_ENABLED 0
#endif
#endif
#if ASSERT_ENABLED
extern const size_t Bun__lock__size;
@@ -316,7 +307,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
/* Both connect and listen sockets are semi-sockets
* but they poll for different events */
if (us_poll_events(p) == LIBUS_SOCKET_WRITABLE) {
us_internal_socket_after_open((struct us_socket_t *) p, error || eof);
us_internal_socket_after_open((struct us_socket_t *) p, (error || eof) ? ECONNREFUSED : 0);
} else {
struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) p;
struct bsd_addr_t addr;

View File

@@ -289,12 +289,12 @@ struct us_socket_t *us_socket_pair(struct us_socket_context_t *ctx, int socket_e
}
/* This is not available for SSL sockets as it makes no sense. */
int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length) {
int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length, int *error) {
if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
return 0;
}
int written = bsd_write2(us_poll_fd(&s->p), header, header_length, payload, payload_length);
int written = bsd_write2(us_poll_fd(&s->p), header, header_length, payload, payload_length, error);
if (written != header_length + payload_length) {
us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
@@ -357,7 +357,7 @@ 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 *error) {
#ifndef LIBUS_NO_SSL
if (ssl) {
return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length);
@@ -367,7 +367,7 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length
return 0;
}
int written = bsd_send(us_poll_fd(&s->p), data, length);
int written = bsd_send(us_poll_fd(&s->p), data, length, error);
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);
@@ -502,7 +502,7 @@ int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int le
}
#endif
// non-TLS is always raw
return us_socket_write(ssl, s, data, length);
return us_socket_write(ssl, s, data, length, NULL);
}
unsigned int us_get_remote_address_info(char *buf, struct us_socket_t *s, const char **dest, int *port, int *is_ipv6)

View File

@@ -627,15 +627,9 @@ public:
return std::move(*this);
}
void setOnSocketClosed(HttpContextData<SSL>::OnSocketClosedCallback onClose) {
void setOnClose(HttpContextData<SSL>::OnSocketClosedCallback onClose) {
httpContext->getSocketContextData()->onSocketClosed = onClose;
}
void setOnSocketDrain(HttpContextData<SSL>::OnSocketDrainCallback onDrain) {
httpContext->getSocketContextData()->onSocketDrain = onDrain;
}
void setOnSocketData(HttpContextData<SSL>::OnSocketDataCallback onData) {
httpContext->getSocketContextData()->onSocketData = onData;
}
void setOnClientError(HttpContextData<SSL>::OnClientErrorCallback onClientError) {
httpContext->getSocketContextData()->onClientError = std::move(onClientError);

View File

@@ -247,7 +247,7 @@ public:
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, NULL);
total_written += written;
/* Check if we couldn't write the entire buffer */
@@ -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, NULL);
/* 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) */
@@ -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, NULL);
/* Did we fail? */
if (written < length) {
@@ -402,4 +402,4 @@ public:
}
#endif // UWS_ASYNCSOCKET_H
#endif // UWS_ASYNCSOCKET_H

View File

@@ -193,32 +193,23 @@ private:
auto *httpResponseData = reinterpret_cast<HttpResponseData<SSL> *>(us_socket_ext(SSL, s));
/* Call filter */
HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
if(httpResponseData && httpResponseData->isConnectRequest) {
if (httpResponseData->socketData && httpContextData->onSocketData) {
httpContextData->onSocketData(httpResponseData->socketData, SSL, s, "", 0, true);
}
if(httpResponseData->inStream) {
httpResponseData->inStream(reinterpret_cast<HttpResponse<SSL> *>(s), "", 0, true, httpResponseData->userData);
httpResponseData->inStream = nullptr;
}
}
for (auto &f : httpContextData->filterHandlers) {
f((HttpResponse<SSL> *) s, -1);
}
if (httpResponseData->socketData && httpContextData->onSocketClosed) {
httpContextData->onSocketClosed(httpResponseData->socketData, SSL, s);
}
/* Signal broken HTTP request only if we have a pending request */
if (httpResponseData->onAborted != nullptr && httpResponseData->userData != nullptr) {
httpResponseData->onAborted((HttpResponse<SSL> *)s, httpResponseData->userData);
}
if (httpResponseData->socketData && httpContextData->onSocketClosed) {
httpContextData->onSocketClosed(httpResponseData->socketData, SSL, s);
}
/* Destruct socket ext */
httpResponseData->~HttpResponseData<SSL>();
@@ -263,9 +254,7 @@ private:
/* The return value is entirely up to us to interpret. The HttpParser cares only for whether the returned value is DIFFERENT from passed user */
auto result = httpResponseData->consumePostPadded(httpContextData->maxHeaderSize, httpResponseData->isConnectRequest, httpContextData->flags.requireHostHeader,httpContextData->flags.useStrictMethodValidation, data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
auto result = httpResponseData->consumePostPadded(httpContextData->maxHeaderSize, httpContextData->flags.requireHostHeader,httpContextData->flags.useStrictMethodValidation, data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
/* For every request we reset the timeout and hang until user makes action */
/* Warning: if we are in shutdown state, resetting the timer is a security issue! */
us_socket_timeout(SSL, (us_socket_t *) s, 0);
@@ -341,12 +330,7 @@ private:
/* Continue parsing */
return s;
}, [httpResponseData, httpContextData](void *user, std::string_view data, bool fin) -> void * {
if (httpResponseData->isConnectRequest && httpResponseData->socketData && httpContextData->onSocketData) {
httpContextData->onSocketData(httpResponseData->socketData, SSL, (struct us_socket_t *) user, data.data(), data.length(), fin);
}
}, [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) {
@@ -396,7 +380,7 @@ 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(), NULL);
us_socket_shutdown(SSL, s);
/* Close any socket on HTTP errors */
us_socket_close(SSL, s, 0, nullptr);
@@ -465,7 +449,7 @@ private:
us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
auto *asyncSocket = reinterpret_cast<AsyncSocket<SSL> *>(s);
auto *httpResponseData = reinterpret_cast<HttpResponseData<SSL> *>(asyncSocket->getAsyncSocketData());
/* Attempt to drain the socket buffer before triggering onWritable callback */
size_t bufferedAmount = asyncSocket->getBufferedAmount();
if (bufferedAmount > 0) {
@@ -486,12 +470,6 @@ private:
*/
}
auto *httpContextData = getSocketContextDataS(s);
if (httpResponseData->isConnectRequest && httpResponseData->socketData && httpContextData->onSocketDrain) {
httpContextData->onSocketDrain(httpResponseData->socketData, SSL, (struct us_socket_t *) s);
}
/* 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 */
@@ -536,7 +514,6 @@ private:
us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
auto *asyncSocket = reinterpret_cast<AsyncSocket<SSL> *>(s);
asyncSocket->uncorkWithoutSending();
/* We do not care for half closed sockets */
return asyncSocket->close();
});

View File

@@ -44,10 +44,7 @@ struct alignas(16) HttpContextData {
private:
std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;
using OnSocketClosedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
using OnSocketDataCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket, const char *data, int length, bool last);
using OnSocketDrainCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
using OnClientErrorCallback = MoveOnlyFunction<void(int is_ssl, struct us_socket_t *rawSocket, uWS::HttpParserError errorCode, char *rawPacket, int rawPacketLength)>;
MoveOnlyFunction<void(const char *hostname)> missingServerNameHandler;
@@ -64,8 +61,6 @@ private:
void *upgradedWebSocket = nullptr;
/* Used to simulate Node.js socket events. */
OnSocketClosedCallback onSocketClosed = nullptr;
OnSocketDrainCallback onSocketDrain = nullptr;
OnSocketDataCallback onSocketData = nullptr;
OnClientErrorCallback onClientError = nullptr;
uint64_t maxHeaderSize = 0; // 0 means no limit

View File

@@ -117,19 +117,18 @@ namespace uWS
struct ConsumeRequestLineResult {
char *position;
bool isAncientHTTP;
bool isConnect;
HTTPHeaderParserError headerParserError;
public:
static ConsumeRequestLineResult error(HTTPHeaderParserError error) {
return ConsumeRequestLineResult{nullptr, false, false, error};
return ConsumeRequestLineResult{nullptr, false, error};
}
static ConsumeRequestLineResult success(char *position, bool isAncientHTTP = false, bool isConnect = false) {
return ConsumeRequestLineResult{position, isAncientHTTP, isConnect, HTTP_HEADER_PARSER_ERROR_NONE};
static ConsumeRequestLineResult success(char *position, bool isAncientHTTP = false) {
return ConsumeRequestLineResult{position, isAncientHTTP, HTTP_HEADER_PARSER_ERROR_NONE};
}
static ConsumeRequestLineResult shortRead(bool isAncientHTTP = false, bool isConnect = false) {
return ConsumeRequestLineResult{nullptr, isAncientHTTP, isConnect, HTTP_HEADER_PARSER_ERROR_NONE};
static ConsumeRequestLineResult shortRead(bool isAncientHTTP = false) {
return ConsumeRequestLineResult{nullptr, isAncientHTTP, HTTP_HEADER_PARSER_ERROR_NONE};
}
bool isErrorOrShortRead() {
@@ -552,10 +551,7 @@ namespace uWS
return ConsumeRequestLineResult::shortRead();
}
bool isHTTPMethod = (__builtin_expect(data[1] == '/', 1));
bool isConnect = !isHTTPMethod && (isHTTPorHTTPSPrefixForProxies(data + 1, end) == 1 || ((data - start) == 7 && memcmp(start, "CONNECT", 7) == 0));
if (isHTTPMethod || isConnect) [[likely]] {
if (data[0] == 32 && (__builtin_expect(data[1] == '/', 1) || isHTTPorHTTPSPrefixForProxies(data + 1, end) == 1)) [[likely]] {
header.key = {start, (size_t) (data - start)};
data++;
if(!isValidMethod(header.key, useStrictMethodValidation)) {
@@ -581,22 +577,22 @@ namespace uWS
if (nextPosition >= end) {
/* Whatever we have must be part of the version string */
if (memcmp(" HTTP/1.1\r\n", data, std::min<unsigned int>(11, (unsigned int) (end - data))) == 0) {
return ConsumeRequestLineResult::shortRead(false, isConnect);
return ConsumeRequestLineResult::shortRead();
} else if (memcmp(" HTTP/1.0\r\n", data, std::min<unsigned int>(11, (unsigned int) (end - data))) == 0) {
/*Indicates that the request line is ancient HTTP*/
return ConsumeRequestLineResult::shortRead(true, isConnect);
return ConsumeRequestLineResult::shortRead(true);
}
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION);
}
if (memcmp(" HTTP/1.1\r\n", data, 11) == 0) {
return ConsumeRequestLineResult::success(nextPosition, false, isConnect);
return ConsumeRequestLineResult::success(nextPosition);
} else if (memcmp(" HTTP/1.0\r\n", data, 11) == 0) {
/*Indicates that the request line is ancient HTTP*/
return ConsumeRequestLineResult::success(nextPosition, true, isConnect);
return ConsumeRequestLineResult::success(nextPosition, true);
}
/* If we stand at the post padded CR, we have fragmented input so try again later */
if (data[0] == '\r') {
return ConsumeRequestLineResult::shortRead(false, isConnect);
return ConsumeRequestLineResult::shortRead();
}
/* This is an error */
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_HTTP_VERSION);
@@ -606,14 +602,14 @@ namespace uWS
/* If we stand at the post padded CR, we have fragmented input so try again later */
if (data[0] == '\r') {
return ConsumeRequestLineResult::shortRead(false, isConnect);
return ConsumeRequestLineResult::shortRead();
}
if (data[0] == 32) {
switch (isHTTPorHTTPSPrefixForProxies(data + 1, end)) {
// If we haven't received enough data to check if it's http:// or https://, let's try again later
case -1:
return ConsumeRequestLineResult::shortRead(false, isConnect);
return ConsumeRequestLineResult::shortRead();
// Otherwise, if it's not http:// or https://, return 400
default:
return ConsumeRequestLineResult::error(HTTP_HEADER_PARSER_ERROR_INVALID_REQUEST);
@@ -639,7 +635,7 @@ namespace uWS
}
/* End is only used for the proxy parser. The HTTP parser recognizes "\ra" as invalid "\r\n" scan and breaks. */
static HttpParserResult getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved, bool &isAncientHTTP, bool &isConnectRequest, bool useStrictMethodValidation, uint64_t maxHeaderSize) {
static HttpParserResult getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved, bool &isAncientHTTP, bool useStrictMethodValidation, uint64_t maxHeaderSize) {
char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer;
#ifdef UWS_WITH_PROXY
/* ProxyParser is passed as reserved parameter */
@@ -693,9 +689,6 @@ namespace uWS
if(requestLineResult.isAncientHTTP) {
isAncientHTTP = true;
}
if(requestLineResult.isConnect) {
isConnectRequest = true;
}
/* No request headers found */
const char * headerStart = (headers[0].key.length() > 0) ? headers[0].key.data() : end;
@@ -805,7 +798,7 @@ namespace uWS
/* This is the only caller of getHeaders and is thus the deepest part of the parser. */
template <bool ConsumeMinimally>
HttpParserResult fenceAndConsumePostPadded(uint64_t maxHeaderSize, bool& isConnectRequest, bool requireHostHeader, bool useStrictMethodValidation, char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction<void *(void *, HttpRequest *)> &requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &dataHandler) {
HttpParserResult fenceAndConsumePostPadded(uint64_t maxHeaderSize, bool requireHostHeader, bool useStrictMethodValidation, char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction<void *(void *, HttpRequest *)> &requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &dataHandler) {
/* How much data we CONSUMED (to throw away) */
unsigned int consumedTotal = 0;
@@ -816,7 +809,7 @@ namespace uWS
data[length + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */
req->ancientHttp = false;
for (;length;) {
auto result = getHeaders(data, data + length, req->headers, reserved, req->ancientHttp, isConnectRequest, useStrictMethodValidation, maxHeaderSize);
auto result = getHeaders(data, data + length, req->headers, reserved, req->ancientHttp, useStrictMethodValidation, maxHeaderSize);
if(result.isError()) {
return result;
}
@@ -923,10 +916,6 @@ namespace uWS
length -= emittable;
consumedTotal += emittable;
}
} else if(isConnectRequest) {
// This only server to mark that the connect request read all headers
// and can starting emitting data
remainingStreamingBytes = STATE_IS_CHUNKED;
} else {
/* If we came here without a body; emit an empty data chunk to signal no data */
dataHandler(user, {}, true);
@@ -942,16 +931,15 @@ namespace uWS
}
public:
HttpParserResult consumePostPadded(uint64_t maxHeaderSize, bool& isConnectRequest, bool requireHostHeader, bool useStrictMethodValidation, char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction<void *(void *, HttpRequest *)> &&requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &&dataHandler) {
HttpParserResult consumePostPadded(uint64_t maxHeaderSize, bool requireHostHeader, bool useStrictMethodValidation, char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction<void *(void *, HttpRequest *)> &&requestHandler, MoveOnlyFunction<void *(void *, std::string_view, bool)> &&dataHandler) {
/* This resets BloomFilter by construction, but later we also reset it again.
* Optimize this to skip resetting twice (req could be made global) */
HttpRequest req;
if (remainingStreamingBytes) {
if (isConnectRequest) {
dataHandler(user, std::string_view(data, length), false);
return HttpParserResult::success(0, user);
} else if (isParsingChunkedEncoding(remainingStreamingBytes)) {
/* It's either chunked or with a content-length */
/* It's either chunked or with a content-length */
if (isParsingChunkedEncoding(remainingStreamingBytes)) {
std::string_view dataToConsume(data, length);
for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) {
dataHandler(user, chunk, chunk.length() == 0);
@@ -962,7 +950,6 @@ public:
data = (char *) dataToConsume.data();
length = (unsigned int) dataToConsume.length();
} else {
// this is exactly the same as below!
// todo: refactor this
if (remainingStreamingBytes >= length) {
@@ -993,7 +980,7 @@ public:
fallback.append(data, maxCopyDistance);
// break here on break
HttpParserResult consumed = fenceAndConsumePostPadded<true>(maxHeaderSize, isConnectRequest, requireHostHeader, useStrictMethodValidation, fallback.data(), (unsigned int) fallback.length(), user, reserved, &req, requestHandler, dataHandler);
HttpParserResult consumed = fenceAndConsumePostPadded<true>(maxHeaderSize, requireHostHeader, useStrictMethodValidation, fallback.data(), (unsigned int) fallback.length(), user, reserved, &req, requestHandler, dataHandler);
/* Return data will be different than user if we are upgraded to WebSocket or have an error */
if (consumed.returnedData != user) {
return consumed;
@@ -1010,11 +997,8 @@ public:
length -= consumedBytes - had;
if (remainingStreamingBytes) {
if(isConnectRequest) {
dataHandler(user, std::string_view(data, length), false);
return HttpParserResult::success(0, user);
} else if (isParsingChunkedEncoding(remainingStreamingBytes)) {
/* It's either chunked or with a content-length */
/* It's either chunked or with a content-length */
if (isParsingChunkedEncoding(remainingStreamingBytes)) {
std::string_view dataToConsume(data, length);
for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) {
dataHandler(user, chunk, chunk.length() == 0);
@@ -1053,7 +1037,7 @@ public:
}
}
HttpParserResult consumed = fenceAndConsumePostPadded<false>(maxHeaderSize, isConnectRequest, requireHostHeader, useStrictMethodValidation, data, length, user, reserved, &req, requestHandler, dataHandler);
HttpParserResult consumed = fenceAndConsumePostPadded<false>(maxHeaderSize, requireHostHeader, useStrictMethodValidation, data, length, user, reserved, &req, requestHandler, dataHandler);
/* Return data will be different than user if we are upgraded to WebSocket or have an error */
if (consumed.returnedData != user) {
return consumed;

View File

@@ -243,7 +243,7 @@ public:
/* Manually upgrade to WebSocket. Typically called in upgrade handler. Immediately calls open handler.
* NOTE: Will invalidate 'this' as socket might change location in memory. Throw away after use. */
template <typename UserData>
us_socket_t *upgrade(UserData&& userData, std::string_view secWebSocketKey, std::string_view secWebSocketProtocol,
us_socket_t *upgrade(UserData &&userData, std::string_view secWebSocketKey, std::string_view secWebSocketProtocol,
std::string_view secWebSocketExtensions,
struct us_socket_context_t *webSocketContext) {
@@ -350,8 +350,7 @@ public:
us_socket_timeout(SSL, (us_socket_t *) webSocket, webSocketContextData->idleTimeoutComponents.first);
/* Move construct the UserData right before calling open handler */
new (webSocket->getUserData()) UserData(std::forward<UserData>(userData));
new (webSocket->getUserData()) UserData(std::move(userData));
/* Emit open event and start the timeout */
if (webSocketContextData->openHandler) {
@@ -742,10 +741,6 @@ public:
return httpResponseData->socketData;
}
bool isConnectRequest() {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
return httpResponseData->isConnectRequest;
}
void setWriteOffset(uint64_t offset) {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData();

View File

@@ -108,7 +108,6 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
uint8_t state = 0;
uint8_t idleTimeout = 10; // default HTTP_TIMEOUT 10 seconds
bool fromAncientRequest = false;
bool isConnectRequest = false;
bool isIdle = true;
bool shouldCloseOnceIdle = false;

View File

@@ -114,7 +114,7 @@ public:
if (message.length() >= 16 * 1024 && !compress && !SSL && !webSocketData->subscriber && getBufferedAmount() == 0 && Super::getLoopData()->getCorkOffset() == 0) {
char header[10];
int header_length = (int) protocol::formatMessage<isServer>(header, "", 0, opCode, message.length(), compress, fin);
int written = us_socket_write2(0, (struct us_socket_t *)this, header, header_length, message.data(), (int) message.length());
int written = us_socket_write2(0, (struct us_socket_t *)this, header, header_length, message.data(), (int) message.length(), NULL);
if (written != header_length + (int) message.length()) {
/* Buffer up backpressure */
@@ -380,4 +380,4 @@ public:
}
#endif // UWS_WEBSOCKET_H
#endif // UWS_WEBSOCKET_H

View File

@@ -22,7 +22,7 @@ At its core is the _Bun runtime_, a fast JavaScript runtime designed as a drop-i
## Features:
- Live in-editor error messages (gif below)
- Vscode test runner support
- Test runner codelens
- Debugger support
- Run scripts from package.json
- Visual lockfile viewer for old binary lockfiles (`bun.lockb`)

View File

@@ -1,6 +1,6 @@
{
"name": "bun-vscode",
"version": "0.0.31",
"version": "0.0.29",
"author": "oven",
"repository": {
"type": "git",
@@ -116,6 +116,20 @@
"category": "Bun",
"enablement": "!inDebugMode && resourceLangId =~ /^(javascript|typescript|javascriptreact|typescriptreact)$/ && !isInDiffEditor && resourceScheme == 'untitled'",
"icon": "$(play-circle)"
},
{
"command": "extension.bun.runTest",
"title": "Run all tests",
"shortTitle": "Run Test",
"category": "Bun",
"icon": "$(play)"
},
{
"command": "extension.bun.watchTest",
"title": "Run all tests in watch mode",
"shortTitle": "Run Test Watch",
"category": "Bun",
"icon": "$(sync)"
}
],
"menus": {

View File

@@ -30,7 +30,7 @@ describe("BunTestController", () => {
const pattern = internal.buildTestNamePattern(mockTests);
expect(pattern).toContain(".*?");
expect(pattern).toBe("(^ ?test with .*?$)|(^ ?test with \\.*?$)");
expect(pattern).toBe("(^ test with .*?$)|(^ test with \\.*?$)");
});
test("should escape % formatters", () => {
@@ -41,7 +41,7 @@ describe("BunTestController", () => {
const pattern = internal.buildTestNamePattern(mockTests);
expect(pattern).toBe("(^ ?test with .*?$)|(^ ?test with .*?$)");
expect(pattern).toBe("(^ test with .*?$)|(^ test with .*?$)");
});
test("should join multiple patterns with |", () => {
@@ -53,7 +53,7 @@ describe("BunTestController", () => {
const pattern = internal.buildTestNamePattern(mockTests);
expect(pattern).toBe("(^ ?test 1$)|(^ ?test 2$)|(^ ?test 3$)");
expect(pattern).toBe("(^ test 1$)|(^ test 2$)|(^ test 3$)");
});
test("should handle describe blocks differently", () => {
@@ -61,7 +61,7 @@ describe("BunTestController", () => {
const pattern = internal.buildTestNamePattern(mockTests);
expect(pattern).toBe("(^ ?describe block )");
expect(pattern).toBe("(^ describe block )");
});
test("should handle complex nested test names", () => {

View File

@@ -1339,9 +1339,9 @@ export class BunTestController implements vscode.Disposable {
t = t.replaceAll(/\$[\w\.\[\]]+/g, ".*?");
if (test?.tags?.some(tag => tag.id === "test" || tag.id === "it")) {
testNames.push(`^ ?${t}$`);
testNames.push(`^ ${t}$`);
} else if (test?.tags?.some(tag => tag.id === "describe")) {
testNames.push(`^ ?${t} `);
testNames.push(`^ ${t} `);
} else {
testNames.push(t);
}

View File

@@ -1,5 +1,5 @@
#!/bin/sh
# Version: 19
# Version: 18
# A script that installs the dependencies needed to build and test Bun.
# This should work on macOS and Linux with a POSIX shell.
@@ -685,8 +685,6 @@ install_common_software() {
apt-transport-https \
software-properties-common
fi
install_packages \
libc6-dbg
;;
dnf)
install_packages \
@@ -1195,7 +1193,7 @@ install_docker() {
execute_sudo amazon-linux-extras install docker
;;
amzn-* | alpine-*)
install_packages docker docker-cli-compose
install_packages docker
;;
*)
sh="$(require sh)"
@@ -1210,17 +1208,10 @@ install_docker() {
if [ -f "$systemctl" ]; then
execute_sudo "$systemctl" enable docker
fi
if [ "$os" = "linux" ] && [ "$distro" = "alpine" ]; then
execute doas rc-update add docker default
execute doas rc-service docker start
fi
getent="$(which getent)"
if [ -n "$("$getent" group docker)" ]; then
usermod="$(which usermod)"
if [ -z "$usermod" ]; then
usermod="$(sudo which usermod)"
fi
if [ -f "$usermod" ]; then
execute_sudo "$usermod" -aG docker "$user"
fi

View File

@@ -117,7 +117,7 @@ async function countReactions(issueNumbers: number[], verbose = false): Promise<
}
// Small delay to avoid rate limiting
await Bun.sleep(1);
await Bun.sleep(50);
}
return totalReactions;

View File

@@ -1,72 +0,0 @@
#!/usr/bin/env bun
const body = process.env.GITHUB_ISSUE_BODY || "";
const title = process.env.GITHUB_ISSUE_TITLE || "";
const issueNumber = process.env.GITHUB_ISSUE_NUMBER;
if (!issueNumber) {
throw new Error("GITHUB_ISSUE_NUMBER must be set");
}
interface CloseAction {
reason: "not_planned" | "completed";
comment: string;
}
let closeAction: CloseAction | null = null;
// Check for workers_terminated
if (body.includes("workers_terminated")) {
closeAction = {
reason: "not_planned",
comment: `Duplicate of #15964
We are tracking worker stability issues in https://github.com/oven-sh/bun/issues/15964. For now, I recommend against terminating workers when possible.`,
};
}
// Check for better-sqlite3 with RunCommand or AutoCommand
else if (body.includes("better-sqlite3") && (body.includes("[RunCommand]") || body.includes("[AutoCommand]"))) {
closeAction = {
reason: "not_planned",
comment: `Duplicate of #4290.
better-sqlite3 is not supported yet in Bun due to missing V8 C++ APIs. For now, you can try [bun:sqlite](https://bun.com/docs/api/sqlite) for an almost drop-in replacement.`,
};
}
// Check for CPU architecture issues (Segmentation Fault/Illegal Instruction with no_avx)
else if (
(body.includes("Segmentation Fault") ||
body.includes("Illegal Instruction") ||
body.includes("IllegalInstruction")) &&
body.includes("no_avx")
) {
let comment = `Bun requires a CPU with the micro-architecture [\`nehalem\`](https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)) or later (released in 2008). If you're using a CPU emulator like qemu, then try enabling x86-64-v2.`;
// Check if it's macOS
const platformMatch = body.match(/Platform:\s*([^\n]+)/i) || body.match(/on\s+(macos|darwin)/i);
const isMacOS =
platformMatch &&
(platformMatch[1]?.toLowerCase().includes("darwin") || platformMatch[1]?.toLowerCase().includes("macos"));
if (isMacOS) {
comment += `\n\nIf you're on a macOS silicon device, you're running Bun via the Rosetta CPU emulator and your best option is to run Bun natively instead.`;
}
closeAction = {
reason: "not_planned",
comment,
};
}
if (closeAction) {
// Output the action to take
console.write(
JSON.stringify({
close: true,
reason: closeAction.reason,
comment: closeAction.comment,
}),
);
} else {
console.write(JSON.stringify({ close: false }));
}

View File

@@ -6,9 +6,6 @@ if (!body) {
const latest = (await Bun.file(join(import.meta.dir, "..", "LATEST")).text()).trim();
// Check if this is a standalone executable
const isStandalone = body.includes("standalone_executable");
const lines = body.split("\n").reverse();
for (let line of lines) {
@@ -42,11 +39,6 @@ for (let line of lines) {
await Bun.write("is-outdated.txt", "true");
await Bun.write("outdated.txt", version);
// Write flag for standalone executables
if (isStandalone) {
await Bun.write("is-standalone.txt", "true");
}
const isVeryOutdated =
major !== latestMajor || minor !== latestMinor || (latestPatch > patch && latestPatch - patch > 3);

View File

@@ -72,7 +72,6 @@ const cwd = import.meta.dirname ? dirname(import.meta.dirname) : process.cwd();
const testsPath = join(cwd, "test");
const spawnTimeout = 5_000;
const spawnBunTimeout = 20_000; // when running with ASAN/LSAN bun can take a bit longer to exit, not a bug.
const testTimeout = 3 * 60_000;
const integrationTimeout = 5 * 60_000;
@@ -80,7 +79,7 @@ function getNodeParallelTestTimeout(testPath) {
if (testPath.includes("test-dns")) {
return 90_000;
}
return 20_000;
return 10_000;
}
process.on("SIGTRAP", () => {
@@ -299,7 +298,7 @@ function getTestExpectations() {
return expectations;
}
const skipsForExceptionValidation = (() => {
const skipArray = (() => {
const path = join(cwd, "test/no-validate-exceptions.txt");
if (!existsSync(path)) {
return [];
@@ -310,32 +309,13 @@ const skipsForExceptionValidation = (() => {
.filter(line => !line.startsWith("#") && line.length > 0);
})();
const skipsForLeaksan = (() => {
const path = join(cwd, "test/no-validate-leaksan.txt");
if (!existsSync(path)) {
return [];
}
return readFileSync(path, "utf-8")
.split("\n")
.filter(line => !line.startsWith("#") && line.length > 0);
})();
/**
* Returns whether we should validate exception checks running the given test
* @param {string} test
* @returns {boolean}
*/
const shouldValidateExceptions = test => {
return !(skipsForExceptionValidation.includes(test) || skipsForExceptionValidation.includes("test/" + test));
};
/**
* Returns whether we should validate exception checks running the given test
* @param {string} test
* @returns {boolean}
*/
const shouldValidateLeakSan = test => {
return !(skipsForLeaksan.includes(test) || skipsForLeaksan.includes("test/" + test));
return !(skipArray.includes(test) || skipArray.includes("test/" + test));
};
/**
@@ -420,9 +400,7 @@ async function runTests() {
const okResults = [];
const flakyResults = [];
const flakyResultsTitles = [];
const failedResults = [];
const failedResultsTitles = [];
const maxAttempts = 1 + (parseInt(options["retries"]) || 0);
const parallelism = options["parallel"] ? availableParallelism() : 1;
@@ -458,7 +436,6 @@ async function runTests() {
if (ok) {
if (failure) {
flakyResults.push(failure);
flakyResultsTitles.push(title);
} else {
okResults.push(result);
}
@@ -478,7 +455,6 @@ async function runTests() {
if (attempt >= maxAttempts || isAlwaysFailure(error)) {
flaky = false;
failedResults.push(failure);
failedResultsTitles.push(title);
break;
}
}
@@ -591,12 +567,6 @@ async function runTests() {
env.BUN_JSC_validateExceptionChecks = "1";
env.BUN_JSC_dumpSimulatedThrows = "1";
}
if ((basename(execPath).includes("asan") || !isCI) && shouldValidateLeakSan(testPath)) {
env.BUN_DESTRUCT_VM_ON_EXIT = "1";
env.ASAN_OPTIONS = "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=1:abort_on_error=1";
// prettier-ignore
env.LSAN_OPTIONS = `malloc_context_size=100:print_suppressions=0:suppressions=${process.cwd()}/test/leaksan.supp`;
}
return runTest(title, async () => {
const { ok, error, stdout, crashes } = await spawnBun(execPath, {
cwd: cwd,
@@ -654,16 +624,6 @@ async function runTests() {
throw new Error(`Unsupported package manager: ${packageManager}`);
}
// build
const buildResult = await spawnBun(execPath, {
cwd: vendorPath,
args: ["run", "build"],
timeout: 60_000,
});
if (!buildResult.ok) {
throw new Error(`Failed to build vendor: ${buildResult.error}`);
}
for (const testPath of testPaths) {
const title = join(relative(cwd, vendorPath), testPath).replace(/\\/g, "/");
@@ -685,9 +645,6 @@ async function runTests() {
}
}
// tests are all over, close the group from the final test. any further output should print ungrouped.
startGroup("End");
if (isGithubAction) {
reportOutputToGitHubAction("failing_tests_count", failedResults.length);
const markdown = formatTestToMarkdown(failedResults, false, 0);
@@ -852,14 +809,14 @@ async function runTests() {
if (failedResults.length) {
console.log(`${getAnsi("red")}Failing Tests:${getAnsi("reset")}`);
for (const testPath of failedResultsTitles) {
for (const { testPath } of failedResults) {
console.log(`${getAnsi("red")}- ${testPath}${getAnsi("reset")}`);
}
}
if (flakyResults.length) {
console.log(`${getAnsi("yellow")}Flaky Tests:${getAnsi("reset")}`);
for (const testPath of flakyResultsTitles) {
for (const { testPath } of flakyResults) {
console.log(`${getAnsi("yellow")}- ${testPath}${getAnsi("reset")}`);
}
}
@@ -1137,6 +1094,10 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
: { BUN_ENABLE_CRASH_REPORTING: "0" }),
};
if (basename(execPath).includes("asan")) {
bunEnv.ASAN_OPTIONS = "allow_user_segv_handler=1:disable_coredump=0";
}
if (isWindows && bunEnv.Path) {
delete bunEnv.Path;
}
@@ -1153,9 +1114,6 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
}
bunEnv["TEMP"] = tmpdirPath;
}
if (timeout === undefined) {
timeout = spawnBunTimeout;
}
try {
const existingCores = options["coredump-upload"] ? readdirSync(coresDir) : [];
const result = await spawnSafe({
@@ -1292,17 +1250,17 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
*
* @param {string} execPath
* @param {string} testPath
* @param {object} [opts]
* @param {string} [opts.cwd]
* @param {string[]} [opts.args]
* @param {object} [options]
* @param {string} [options.cwd]
* @param {string[]} [options.args]
* @returns {Promise<TestResult>}
*/
async function spawnBunTest(execPath, testPath, opts = { cwd }) {
async function spawnBunTest(execPath, testPath, options = { cwd }) {
const timeout = getTestTimeout(testPath);
const perTestTimeout = Math.ceil(timeout / 2);
const absPath = join(opts["cwd"], testPath);
const absPath = join(options["cwd"], testPath);
const isReallyTest = isTestStrict(testPath) || absPath.includes("vendor");
const args = opts["args"] ?? [];
const args = options["args"] ?? [];
const testArgs = ["test", ...args, `--timeout=${perTestTimeout}`];
@@ -1333,16 +1291,10 @@ async function spawnBunTest(execPath, testPath, opts = { cwd }) {
env.BUN_JSC_validateExceptionChecks = "1";
env.BUN_JSC_dumpSimulatedThrows = "1";
}
if ((basename(execPath).includes("asan") || !isCI) && shouldValidateLeakSan(relative(cwd, absPath))) {
env.BUN_DESTRUCT_VM_ON_EXIT = "1";
env.ASAN_OPTIONS = "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=1:abort_on_error=1";
// prettier-ignore
env.LSAN_OPTIONS = `malloc_context_size=100:print_suppressions=0:suppressions=${process.cwd()}/test/leaksan.supp`;
}
const { ok, error, stdout, crashes } = await spawnBun(execPath, {
args: isReallyTest ? testArgs : [...args, absPath],
cwd: opts["cwd"],
cwd: options["cwd"],
timeout: isReallyTest ? timeout : 30_000,
env,
stdout: options.stdout,
@@ -1576,11 +1528,7 @@ function isNodeTest(path) {
return false;
}
const unixPath = path.replaceAll(sep, "/");
return (
unixPath.includes("js/node/test/parallel/") ||
unixPath.includes("js/node/test/sequential/") ||
unixPath.includes("js/bun/test/parallel/")
);
return unixPath.includes("js/node/test/parallel/") || unixPath.includes("js/node/test/sequential/");
}
/**
@@ -2269,7 +2217,7 @@ function getExitCode(outcome) {
return 1;
}
// A flaky segfault, sigtrap, or sigkill must never be ignored.
// A flaky segfault, sigtrap, or sigill must never be ignored.
// If it happens in CI, it will happen to our users.
// Flaky AddressSanitizer errors cannot be ignored since they still represent real bugs.
function isAlwaysFailure(error) {
@@ -2278,7 +2226,6 @@ function isAlwaysFailure(error) {
error.includes("segmentation fault") ||
error.includes("illegal instruction") ||
error.includes("sigtrap") ||
error.includes("sigkill") ||
error.includes("error: addresssanitizer") ||
error.includes("internal assertion failure") ||
error.includes("core dumped") ||

View File

@@ -2808,8 +2808,6 @@ export function endGroup() {
} else {
console.groupEnd();
}
// when a file exits with an ASAN error, there is no trailing newline so we add one here to make sure `console.group()` detection doesn't get broken in CI.
console.log();
}
export function printEnvironment() {
@@ -2866,12 +2864,6 @@ export function printEnvironment() {
spawnSync([shell, "-c", "free -m -w"], { stdio: "inherit" });
}
});
startGroup("Docker", () => {
const shell = which(["sh", "bash"]);
if (shell) {
spawnSync([shell, "-c", "docker ps"], { stdio: "inherit" });
}
});
}
if (isWindows) {
startGroup("Disk (win)", () => {

35
scripts/write-versions.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
set -exo pipefail
WEBKIT_VERSION=$(grep 'set(WEBKIT_TAG' "CMakeLists.txt" | awk '{print $2}' | cut -f 1 -d ')')
MIMALLOC_VERSION=$(git rev-parse HEAD:./src/deps/mimalloc)
LIBARCHIVE_VERSION=$(git rev-parse HEAD:./src/deps/libarchive)
PICOHTTPPARSER_VERSION=$(git rev-parse HEAD:./src/deps/picohttpparser)
BORINGSSL_VERSION=$(git rev-parse HEAD:./src/deps/boringssl)
ZLIB_VERSION=$(git rev-parse HEAD:./src/deps/zlib)
LOLHTML=$(git rev-parse HEAD:./src/deps/lol-html)
TINYCC=$(git rev-parse HEAD:./src/deps/tinycc)
C_ARES=$(git rev-parse HEAD:./src/deps/c-ares)
ZSTD=$(git rev-parse HEAD:./src/deps/zstd)
LSHPACK=$(git rev-parse HEAD:./src/deps/ls-hpack)
LIBDEFLATE=$(git rev-parse HEAD:./src/deps/libdeflate)
rm -rf src/generated_versions_list.zig
echo "// AUTO-GENERATED FILE. Created via .scripts/write-versions.sh" >src/generated_versions_list.zig
echo "" >>src/generated_versions_list.zig
echo "pub const boringssl = \"$BORINGSSL_VERSION\";" >>src/generated_versions_list.zig
echo "pub const libarchive = \"$LIBARCHIVE_VERSION\";" >>src/generated_versions_list.zig
echo "pub const mimalloc = \"$MIMALLOC_VERSION\";" >>src/generated_versions_list.zig
echo "pub const picohttpparser = \"$PICOHTTPPARSER_VERSION\";" >>src/generated_versions_list.zig
echo "pub const webkit = \"$WEBKIT_VERSION\";" >>src/generated_versions_list.zig
echo "pub const zig = @import(\"std\").fmt.comptimePrint(\"{}\", .{@import(\"builtin\").zig_version});" >>src/generated_versions_list.zig
echo "pub const zlib = \"$ZLIB_VERSION\";" >>src/generated_versions_list.zig
echo "pub const tinycc = \"$TINYCC\";" >>src/generated_versions_list.zig
echo "pub const lolhtml = \"$LOLHTML\";" >>src/generated_versions_list.zig
echo "pub const c_ares = \"$C_ARES\";" >>src/generated_versions_list.zig
echo "pub const libdeflate = \"$LIBDEFLATE\";" >>src/generated_versions_list.zig
echo "pub const zstd = \"$ZSTD\";" >>src/generated_versions_list.zig
echo "pub const lshpack = \"$LSHPACK\";" >>src/generated_versions_list.zig
echo "" >>src/generated_versions_list.zig
zig fmt src/generated_versions_list.zig

View File

@@ -121,10 +121,6 @@ pub fn exit(code: u32) noreturn {
std.os.windows.kernel32.ExitProcess(code);
},
else => {
if (Environment.enable_asan) {
std.c.exit(@bitCast(code));
std.c.abort(); // exit should be noreturn
}
bun.c.quick_exit(@bitCast(code));
std.c.abort(); // quick_exit should be noreturn
},
@@ -163,7 +159,7 @@ pub inline fn mimalloc_cleanup(force: bool) void {
Mimalloc.mi_collect(force);
}
}
// Versions are now handled by CMake-generated header (bun_dependency_versions.h)
pub const versions = @import("./generated_versions_list.zig");
// Enabling huge pages slows down bun by 8x or so
// Keeping this code for:

View File

@@ -18,7 +18,7 @@ pub fn deinit(this: *HTMLScanner) void {
for (this.import_records.slice()) |*record| {
this.allocator.free(record.path.text);
}
this.import_records.deinit(this.allocator);
this.import_records.deinitWithAllocator(this.allocator);
}
fn createImportRecord(this: *HTMLScanner, input_path: []const u8, kind: ImportKind) !void {
@@ -44,7 +44,7 @@ fn createImportRecord(this: *HTMLScanner, input_path: []const u8, kind: ImportKi
.range = logger.Range.None,
};
try this.import_records.append(this.allocator, record);
try this.import_records.push(this.allocator, record);
}
const debug = bun.Output.scoped(.HTMLScanner, .hidden);

View File

@@ -199,6 +199,7 @@ pub const StandaloneModuleGraph = struct {
store.ref();
const b = bun.webcore.Blob.initWithStore(store, globalObject).new();
b.allocator = bun.default_allocator;
if (bun.http.MimeType.byExtensionNoDefault(bun.strings.trimLeadingChar(std.fs.path.extension(this.name), '.'))) |mime| {
store.mime_type = mime;
@@ -723,8 +724,7 @@ pub const StandaloneModuleGraph = struct {
return bun.invalid_fd;
};
defer pe_file.deinit();
// Always strip authenticode when adding .bun section for --compile
pe_file.addBunSection(bytes, .strip_always) catch |err| {
pe_file.addBunSection(bytes) catch |err| {
Output.prettyErrorln("Error adding Bun section to PE file: {}", .{err});
cleanup(zname, cloned_executable_fd);
return bun.invalid_fd;

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