mirror of
https://github.com/oven-sh/bun
synced 2026-02-05 08:28:55 +00:00
Compare commits
3 Commits
patch-1
...
claude/htt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e752b645a | ||
|
|
6edfdb6589 | ||
|
|
66bce3d6da |
@@ -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`,
|
||||
@@ -616,9 +601,6 @@ function getTestBunStep(platform, options, testOptions = {}) {
|
||||
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",
|
||||
},
|
||||
command:
|
||||
os === "windows"
|
||||
? `node .\\scripts\\runner.node.mjs ${args.join(" ")}`
|
||||
|
||||
2
.github/actions/bump/action.yml
vendored
2
.github/actions/bump/action.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/update-cares.yml
vendored
2
.github/workflows/update-cares.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/update-hdrhistogram.yml
vendored
4
.github/workflows/update-hdrhistogram.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/update-highway.yml
vendored
4
.github/workflows/update-highway.yml
vendored
@@ -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: |
|
||||
|
||||
2
.github/workflows/update-libarchive.yml
vendored
2
.github/workflows/update-libarchive.yml
vendored
@@ -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: |
|
||||
|
||||
2
.github/workflows/update-libdeflate.yml
vendored
2
.github/workflows/update-libdeflate.yml
vendored
@@ -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: |
|
||||
|
||||
6
.github/workflows/update-lolhtml.yml
vendored
6
.github/workflows/update-lolhtml.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/update-lshpack.yml
vendored
4
.github/workflows/update-lshpack.yml
vendored
@@ -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: |
|
||||
|
||||
2
.github/workflows/update-sqlite3.yml
vendored
2
.github/workflows/update-sqlite3.yml
vendored
@@ -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: |
|
||||
|
||||
79
.github/workflows/update-vendor.yml
vendored
79
.github/workflows/update-vendor.yml
vendored
@@ -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)
|
||||
2
.github/workflows/update-zstd.yml
vendored
2
.github/workflows/update-zstd.yml
vendored
@@ -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: |
|
||||
|
||||
5
.github/workflows/vscode-release.yml
vendored
5
.github/workflows/vscode-release.yml
vendored
@@ -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
11
.vscode/launch.json
generated
vendored
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
21
build.zig
21
build.zig
@@ -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 });
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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}
|
||||
@@ -889,8 +886,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 +930,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 +969,6 @@ if(WIN32)
|
||||
/delayload:WSOCK32.dll
|
||||
/delayload:ADVAPI32.dll
|
||||
/delayload:IPHLPAPI.dll
|
||||
/delayload:CRYPT32.dll
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1006,7 +1010,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 +1061,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
|
||||
)
|
||||
@@ -1185,7 +1188,6 @@ if(WIN32)
|
||||
ntdll
|
||||
userenv
|
||||
dbghelp
|
||||
crypt32
|
||||
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
|
||||
delayimp.lib
|
||||
)
|
||||
@@ -1361,20 +1363,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)
|
||||
|
||||
@@ -4,8 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
libuv/libuv
|
||||
COMMIT
|
||||
# Corresponds to v1.51.0
|
||||
5152db2cbfeb5582e9c27c5ea1dba2cd9e10759b
|
||||
da527d8d2a908b824def74382761566371439003
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -181,23 +181,12 @@ function(generate_dependency_versions_header)
|
||||
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
|
||||
|
||||
# Write the header file
|
||||
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()
|
||||
file(WRITE "${OUTPUT_FILE}" "${HEADER_CONTENT}")
|
||||
|
||||
message(STATUS "Generated dependency versions header: ${OUTPUT_FILE}")
|
||||
|
||||
# Also create a more detailed version for debugging
|
||||
set(DEBUG_OUTPUT_FILE "${CMAKE_BINARY_DIR}/bun_dependency_versions_debug.txt")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 2d2e8dd5b020cc165e2bc1d284461b4504d624e5)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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' \
|
||||
|
||||
@@ -184,7 +184,6 @@ Bun.hash.rapidhash("data", 1234);
|
||||
|
||||
- `"blake2b256"`
|
||||
- `"blake2b512"`
|
||||
- `"blake2s256"`
|
||||
- `"md4"`
|
||||
- `"md5"`
|
||||
- `"ripemd160"`
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
211
docs/api/yaml.md
211
docs/api/yaml.md
@@ -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
|
||||
|
||||
109
docs/cli/test.md
109
docs/cli/test.md
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.2.24",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=="],
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export const platforms: Platform[] = [
|
||||
},
|
||||
{
|
||||
os: "linux",
|
||||
arch: "arm64",
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
bin: "bun-linux-aarch64-musl",
|
||||
exe: "bin/bun",
|
||||
|
||||
39
packages/bun-types/bun.d.ts
vendored
39
packages/bun-types/bun.d.ts
vendored
@@ -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;
|
||||
}
|
||||
@@ -1907,18 +1899,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 +5027,6 @@ declare module "bun" {
|
||||
type SupportedCryptoAlgorithms =
|
||||
| "blake2b256"
|
||||
| "blake2b512"
|
||||
| "blake2s256"
|
||||
| "md4"
|
||||
| "md5"
|
||||
| "ripemd160"
|
||||
|
||||
262
packages/bun-types/redis.d.ts
vendored
262
packages/bun-types/redis.d.ts
vendored
@@ -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>;
|
||||
|
||||
@@ -304,8 +290,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 +298,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 +306,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 +320,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 +394,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 +429,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 +472,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 +480,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 +488,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 +496,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 +504,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 +512,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 +520,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 +528,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 +536,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 +557,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 +602,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 +611,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 +620,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 +634,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>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
77
packages/bun-types/sql.d.ts
vendored
77
packages/bun-types/sql.d.ts
vendored
@@ -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
|
||||
@@ -692,21 +630,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.
|
||||
*
|
||||
|
||||
199
packages/bun-types/test.d.ts
vendored
199
packages/bun-types/test.d.ts
vendored
@@ -91,7 +91,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 +180,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 +206,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 +238,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 +286,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 +295,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 +312,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 +323,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 +340,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 +349,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 +382,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 +405,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 +419,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 +449,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 +469,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 +486,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 +548,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 +559,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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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__
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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") ||
|
||||
|
||||
@@ -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)", () => {
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -229,11 +229,6 @@ pub fn BSSList(comptime ValueType: type, comptime _count: anytype) type {
|
||||
this.data[index] = item;
|
||||
return &this.data[index];
|
||||
}
|
||||
|
||||
pub fn deinit(this: *OverflowBlock) void {
|
||||
if (this.prev) |p| p.deinit();
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
@@ -269,12 +264,6 @@ pub fn BSSList(comptime ValueType: type, comptime _count: anytype) type {
|
||||
return instance;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.head.deinit();
|
||||
bun.default_allocator.destroy(instance);
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
pub fn isOverflowing() bool {
|
||||
return instance.used >= @as(u16, count);
|
||||
}
|
||||
@@ -361,12 +350,6 @@ pub fn BSSStringList(comptime _count: usize, comptime _item_length: usize) type
|
||||
return instance;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const Self) void {
|
||||
_ = self;
|
||||
bun.default_allocator.destroy(instance);
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
pub inline fn isOverflowing() bool {
|
||||
return instance.slice_buf_used >= @as(u16, count);
|
||||
}
|
||||
@@ -547,12 +530,6 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
return instance;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.index.deinit(self.allocator);
|
||||
bun.default_allocator.destroy(instance);
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
pub fn isOverflowing() bool {
|
||||
return instance.backing_buf_used >= @as(u16, count);
|
||||
}
|
||||
@@ -676,10 +653,6 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
pub fn values(self: *Self) []ValueType {
|
||||
return (&self.backing_buf)[0..self.backing_buf_used];
|
||||
}
|
||||
};
|
||||
if (!store_keys) {
|
||||
return BSSMapType;
|
||||
@@ -711,12 +684,6 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
return instance;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.map.deinit();
|
||||
bun.default_allocator.destroy(instance);
|
||||
instance_loaded = false;
|
||||
}
|
||||
|
||||
pub fn isOverflowing() bool {
|
||||
return instance.map.backing_buf_used >= count;
|
||||
}
|
||||
@@ -919,8 +886,6 @@ pub const Default = struct {
|
||||
_ = self;
|
||||
return c_allocator;
|
||||
}
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
const basic = if (bun.use_mimalloc)
|
||||
|
||||
@@ -94,8 +94,6 @@ const BorrowedHeap = if (safety_checks) *DebugHeap else *mimalloc.Heap;
|
||||
const DebugHeap = struct {
|
||||
inner: *mimalloc.Heap,
|
||||
thread_lock: bun.safety.ThreadLock,
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
threadlocal var thread_heap: if (safety_checks) ?DebugHeap else void = if (safety_checks) null;
|
||||
@@ -126,7 +124,6 @@ pub fn borrow(self: Self) Borrowed {
|
||||
/// It uses pthread_getspecific to do that.
|
||||
/// We can save those extra calls if we just do it once in here
|
||||
pub fn getThreadLocalDefault() std.mem.Allocator {
|
||||
if (bun.Environment.enable_asan) return bun.default_allocator;
|
||||
return Borrowed.getDefault().allocator();
|
||||
}
|
||||
|
||||
|
||||
@@ -186,10 +186,10 @@ const State = struct {
|
||||
self.history.unlock();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
fn deinit(self: *Self) void {
|
||||
defer self.* = undefined;
|
||||
var history = self.history.intoUnprotected();
|
||||
defer history.deinit(self.parent);
|
||||
defer history.deinit();
|
||||
|
||||
const count = history.allocations.count();
|
||||
if (count == 0) return;
|
||||
|
||||
@@ -112,7 +112,6 @@ pub const Features = struct {
|
||||
pub var unsupported_uv_function: usize = 0;
|
||||
pub var exited: usize = 0;
|
||||
pub var yarn_migration: usize = 0;
|
||||
pub var pnpm_migration: usize = 0;
|
||||
pub var yaml_parse: usize = 0;
|
||||
|
||||
comptime {
|
||||
|
||||
@@ -752,7 +752,7 @@ pub const Object = struct {
|
||||
pub fn hasProperty(obj: *const Object, name: string) bool {
|
||||
for (obj.properties.slice()) |prop| {
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -762,7 +762,7 @@ pub const Object = struct {
|
||||
for (obj.properties.slice(), 0..) |prop, i| {
|
||||
const value = prop.value orelse continue;
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
const key_str = key.data.e_string;
|
||||
if (key_str.eql(string, name)) {
|
||||
return Expr.Query{
|
||||
|
||||
@@ -132,14 +132,14 @@ pub fn isEmpty(expr: Expr) bool {
|
||||
pub const Query = struct { expr: Expr, loc: logger.Loc, i: u32 = 0 };
|
||||
|
||||
pub fn hasAnyPropertyNamed(expr: *const Expr, comptime names: []const string) bool {
|
||||
if (expr.data != .e_object) return false;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return false;
|
||||
const obj = expr.data.e_object;
|
||||
if (obj.properties.len == 0) return false;
|
||||
|
||||
for (obj.properties.slice()) |prop| {
|
||||
if (prop.value == null) continue;
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
const key_str = key.data.e_string;
|
||||
if (strings.eqlAnyComptime(key_str.data, names)) return true;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ pub fn set(expr: *Expr, allocator: std.mem.Allocator, name: string, value: Expr)
|
||||
for (0..expr.data.e_object.properties.len) |i| {
|
||||
const prop = &expr.data.e_object.properties.ptr[i];
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) {
|
||||
prop.value = value;
|
||||
return;
|
||||
@@ -288,7 +288,7 @@ pub fn setString(expr: *Expr, allocator: std.mem.Allocator, name: string, value:
|
||||
for (0..expr.data.e_object.properties.len) |i| {
|
||||
const prop = &expr.data.e_object.properties.ptr[i];
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) {
|
||||
prop.value = Expr.init(E.String, .{ .data = value }, logger.Loc.Empty);
|
||||
return;
|
||||
@@ -310,15 +310,6 @@ pub fn getObject(expr: *const Expr, name: string) ?Expr {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getBoolean(expr: *const Expr, name: string) ?bool {
|
||||
if (expr.asProperty(name)) |query| {
|
||||
if (query.expr.data == .e_boolean) {
|
||||
return query.expr.data.e_boolean.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getString(expr: *const Expr, allocator: std.mem.Allocator, name: string) OOM!?struct { string, logger.Loc } {
|
||||
if (asProperty(expr, name)) |q| {
|
||||
if (q.expr.asString(allocator)) |str| {
|
||||
@@ -394,7 +385,7 @@ pub fn getRope(self: *const Expr, rope: *const E.Object.Rope) ?E.Object.RopeQuer
|
||||
|
||||
// Making this comptime bloats the binary and doesn't seem to impact runtime performance.
|
||||
pub fn asProperty(expr: *const Expr, name: string) ?Query {
|
||||
if (expr.data != .e_object) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return null;
|
||||
const obj = expr.data.e_object;
|
||||
if (obj.properties.len == 0) return null;
|
||||
|
||||
@@ -402,7 +393,7 @@ pub fn asProperty(expr: *const Expr, name: string) ?Query {
|
||||
}
|
||||
|
||||
pub fn asPropertyStringMap(expr: *const Expr, name: string, allocator: std.mem.Allocator) ?*bun.StringArrayHashMap(string) {
|
||||
if (expr.data != .e_object) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return null;
|
||||
const obj_ = expr.data.e_object;
|
||||
if (obj_.properties.len == 0) return null;
|
||||
const query = obj_.asProperty(name) orelse return null;
|
||||
@@ -448,7 +439,7 @@ pub const ArrayIterator = struct {
|
||||
};
|
||||
|
||||
pub fn asArray(expr: *const Expr) ?ArrayIterator {
|
||||
if (expr.data != .e_array) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_array) return null;
|
||||
const array = expr.data.e_array;
|
||||
if (array.items.len == 0) return null;
|
||||
|
||||
@@ -464,7 +455,7 @@ pub inline fn asUtf8StringLiteral(expr: *const Expr) ?string {
|
||||
}
|
||||
|
||||
pub inline fn asStringLiteral(expr: *const Expr, allocator: std.mem.Allocator) ?string {
|
||||
if (expr.data != .e_string) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_string) return null;
|
||||
return expr.data.e_string.string(allocator) catch null;
|
||||
}
|
||||
|
||||
@@ -510,7 +501,7 @@ pub inline fn asStringZ(expr: *const Expr, allocator: std.mem.Allocator) OOM!?st
|
||||
pub fn asBool(
|
||||
expr: *const Expr,
|
||||
) ?bool {
|
||||
if (expr.data != .e_boolean) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_boolean) return null;
|
||||
|
||||
return expr.data.e_boolean.value;
|
||||
}
|
||||
@@ -531,7 +522,7 @@ const Serializable = struct {
|
||||
};
|
||||
|
||||
pub fn isMissing(a: *const Expr) bool {
|
||||
return a.data == Expr.Tag.e_missing;
|
||||
return std.meta.activeTag(a.data) == Expr.Tag.e_missing;
|
||||
}
|
||||
|
||||
// The goal of this function is to "rotate" the AST if it's possible to use the
|
||||
|
||||
@@ -109,9 +109,6 @@ pub const KnownGlobal = enum {
|
||||
return js_ast.Expr.init(E.Array, .{ .items = e.args }, loc);
|
||||
},
|
||||
.number => {
|
||||
if (arg.data != .e_number) {
|
||||
return callFromNew(e, loc);
|
||||
}
|
||||
const val = arg.data.e_number.value;
|
||||
if (
|
||||
// only want this with whitespace minification
|
||||
|
||||
@@ -325,7 +325,7 @@ pub const Runner = struct {
|
||||
return _entry.value_ptr.*;
|
||||
}
|
||||
|
||||
var blob_: ?*const jsc.WebCore.Blob = null;
|
||||
var blob_: ?jsc.WebCore.Blob = null;
|
||||
const mime_type: ?MimeType = null;
|
||||
|
||||
if (value.jsType() == .DOMWrapper) {
|
||||
@@ -334,23 +334,30 @@ pub const Runner = struct {
|
||||
} else if (value.as(jsc.WebCore.Request)) |resp| {
|
||||
return this.run(try resp.getBlobWithoutCallFrame(this.global));
|
||||
} else if (value.as(jsc.WebCore.Blob)) |resp| {
|
||||
blob_ = resp;
|
||||
blob_ = resp.*;
|
||||
blob_.?.allocator = null;
|
||||
} else if (value.as(bun.api.ResolveMessage) != null or value.as(bun.api.BuildMessage) != null) {
|
||||
_ = this.macro.vm.uncaughtException(this.global, value, false);
|
||||
return error.MacroFailed;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_) |blob| {
|
||||
return Expr.fromBlob(
|
||||
if (blob_) |*blob| {
|
||||
const out_expr = Expr.fromBlob(
|
||||
blob,
|
||||
this.allocator,
|
||||
mime_type,
|
||||
this.log,
|
||||
this.caller.loc,
|
||||
) catch {
|
||||
blob.deinit();
|
||||
return error.MacroFailed;
|
||||
};
|
||||
if (out_expr.data == .e_string) {
|
||||
blob.deinit();
|
||||
}
|
||||
|
||||
return out_expr;
|
||||
}
|
||||
|
||||
return Expr.init(E.String, E.String.empty, this.caller.loc);
|
||||
@@ -364,20 +371,41 @@ pub const Runner = struct {
|
||||
|
||||
const _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
|
||||
if (_entry.found_existing) {
|
||||
switch (_entry.value_ptr.*.data) {
|
||||
.e_object, .e_array => {
|
||||
this.log.addErrorFmt(this.source, this.caller.loc, this.allocator, "converting circular structure to Bun AST is not implemented yet", .{}) catch unreachable;
|
||||
return error.MacroFailed;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return _entry.value_ptr.*;
|
||||
}
|
||||
|
||||
var iter = try jsc.JSArrayIterator.init(value, this.global);
|
||||
|
||||
// Process all array items
|
||||
if (iter.len == 0) {
|
||||
const result = Expr.init(
|
||||
E.Array,
|
||||
E.Array{
|
||||
.items = ExprNodeList.empty,
|
||||
.was_originally_macro = true,
|
||||
},
|
||||
this.caller.loc,
|
||||
);
|
||||
_entry.value_ptr.* = result;
|
||||
return result;
|
||||
}
|
||||
var array = this.allocator.alloc(Expr, iter.len) catch unreachable;
|
||||
errdefer this.allocator.free(array);
|
||||
const expr = Expr.init(
|
||||
var out = Expr.init(
|
||||
E.Array,
|
||||
E.Array{ .items = ExprNodeList.empty, .was_originally_macro = true },
|
||||
E.Array{
|
||||
.items = ExprNodeList.empty,
|
||||
.was_originally_macro = true,
|
||||
},
|
||||
this.caller.loc,
|
||||
);
|
||||
_entry.value_ptr.* = expr;
|
||||
_entry.value_ptr.* = out;
|
||||
|
||||
errdefer this.allocator.free(array);
|
||||
var i: usize = 0;
|
||||
while (try iter.next()) |item| {
|
||||
array[i] = try this.run(item);
|
||||
@@ -385,27 +413,24 @@ pub const Runner = struct {
|
||||
continue;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
expr.data.e_array.items = ExprNodeList.fromOwnedSlice(array);
|
||||
expr.data.e_array.items.len = @truncate(i);
|
||||
return expr;
|
||||
out.data.e_array.items = ExprNodeList.fromOwnedSlice(array);
|
||||
_entry.value_ptr.* = out;
|
||||
return out;
|
||||
},
|
||||
// TODO: optimize this
|
||||
jsc.ConsoleObject.Formatter.Tag.Object => {
|
||||
this.is_top_level = false;
|
||||
const _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
|
||||
if (_entry.found_existing) {
|
||||
switch (_entry.value_ptr.*.data) {
|
||||
.e_object, .e_array => {
|
||||
this.log.addErrorFmt(this.source, this.caller.loc, this.allocator, "converting circular structure to Bun AST is not implemented yet", .{}) catch unreachable;
|
||||
return error.MacroFailed;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return _entry.value_ptr.*;
|
||||
}
|
||||
|
||||
// Reserve a placeholder to break cycles.
|
||||
const expr = Expr.init(
|
||||
E.Object,
|
||||
E.Object{ .properties = G.Property.List{}, .was_originally_macro = true },
|
||||
this.caller.loc,
|
||||
);
|
||||
_entry.value_ptr.* = expr;
|
||||
|
||||
// SAFETY: tag ensures `value` is an object.
|
||||
const obj = value.getObject() orelse unreachable;
|
||||
var object_iter = try jsc.JSPropertyIterator(.{
|
||||
@@ -414,28 +439,36 @@ pub const Runner = struct {
|
||||
}).init(this.global, obj);
|
||||
defer object_iter.deinit();
|
||||
|
||||
// Build properties list
|
||||
var properties = bun.handleOom(
|
||||
G.Property.List.initCapacity(this.allocator, object_iter.len),
|
||||
const out = _entry.value_ptr;
|
||||
out.* = Expr.init(
|
||||
E.Object,
|
||||
E.Object{
|
||||
.properties = bun.handleOom(
|
||||
G.Property.List.initCapacity(this.allocator, object_iter.len),
|
||||
),
|
||||
.was_originally_macro = true,
|
||||
},
|
||||
this.caller.loc,
|
||||
);
|
||||
const properties = &out.data.e_object.properties;
|
||||
errdefer properties.clearAndFree(this.allocator);
|
||||
|
||||
while (try object_iter.next()) |prop| {
|
||||
const object_value = try this.run(object_iter.value);
|
||||
|
||||
properties.append(this.allocator, G.Property{
|
||||
bun.assertf(
|
||||
object_iter.i == properties.len,
|
||||
"`properties` unexpectedly modified (length {d}, expected {d})",
|
||||
.{ properties.len, object_iter.i },
|
||||
);
|
||||
properties.appendAssumeCapacity(G.Property{
|
||||
.key = Expr.init(
|
||||
E.String,
|
||||
E.String.init(prop.toOwnedSlice(this.allocator) catch unreachable),
|
||||
this.caller.loc,
|
||||
),
|
||||
.value = object_value,
|
||||
}) catch |err| bun.handleOom(err);
|
||||
.value = try this.run(object_iter.value),
|
||||
});
|
||||
}
|
||||
|
||||
expr.data.e_object.properties = properties;
|
||||
|
||||
return expr;
|
||||
return out.*;
|
||||
},
|
||||
|
||||
.JSON => {
|
||||
|
||||
@@ -954,7 +954,7 @@ pub fn NewParser_(
|
||||
switch (call.target.data) {
|
||||
.e_identifier => |ident| {
|
||||
// is this a require("something")
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) {
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) {
|
||||
_ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable);
|
||||
}
|
||||
},
|
||||
@@ -970,7 +970,7 @@ pub fn NewParser_(
|
||||
switch (call.target.data) {
|
||||
.e_identifier => |ident| {
|
||||
// is this a require("something")
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) {
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) {
|
||||
_ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn VisitExpr(
|
||||
}
|
||||
pub fn e_import_meta(p: *P, expr: Expr, in: ExprIn) Expr {
|
||||
// TODO: delete import.meta might not work
|
||||
const is_delete_target = p.delete_target == .e_import_meta;
|
||||
const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta;
|
||||
|
||||
if (p.define.dots.get("meta")) |meta| {
|
||||
for (meta) |define| {
|
||||
|
||||
@@ -69,8 +69,8 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) !
|
||||
.function_name = .init(function_name),
|
||||
.source_url = .init(file_name),
|
||||
.position = if (line > 0) .{
|
||||
.line = .fromOneBased(line),
|
||||
.column = if (column < 1) .invalid else .fromOneBased(column),
|
||||
.line = .fromOneBased(line + 1),
|
||||
.column = .fromOneBased(@max(1, column)),
|
||||
.line_start_byte = 0,
|
||||
} else .{
|
||||
.line = .invalid,
|
||||
@@ -147,10 +147,10 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) !
|
||||
|
||||
// Remap the frame
|
||||
const remapped = result.mappings.find(
|
||||
frame.position.line,
|
||||
frame.position.column,
|
||||
frame.position.line.oneBased(),
|
||||
frame.position.column.zeroBased(),
|
||||
);
|
||||
if (remapped) |*remapped_position| {
|
||||
if (remapped) |remapped_position| {
|
||||
frame.position = .{
|
||||
.line = .fromZeroBased(remapped_position.originalLine()),
|
||||
.column = .fromZeroBased(remapped_position.originalColumn()),
|
||||
|
||||
@@ -207,9 +207,8 @@ pub const Entry = struct {
|
||||
.original_column = 0,
|
||||
};
|
||||
|
||||
// The runtime.line_count counts newlines (e.g., 2941 for a 2942-line file).
|
||||
// The runtime ends at line 2942 with })({ so modules start after that.
|
||||
var lines_between: u32 = runtime.line_count;
|
||||
// +2 because the magic fairy in my dreams said it would align the source maps.
|
||||
var lines_between: u32 = runtime.line_count + 2;
|
||||
|
||||
// Join all of the mappings together.
|
||||
for (0..map_files.len) |i| switch (map_files.get(i)) {
|
||||
|
||||
@@ -251,8 +251,8 @@ export async function onRuntimeError(err: any, fatal = false, async = false) {
|
||||
writer.stringWithLength(browserUrl);
|
||||
writer.u32(parsed.length);
|
||||
for (const frame of parsed) {
|
||||
writer.i32(frame.line ?? 0);
|
||||
writer.i32(frame.col ?? 0);
|
||||
writer.u32(frame.line ?? 0);
|
||||
writer.u32(frame.col ?? 0);
|
||||
writer.stringWithLength(frame.fn ?? "");
|
||||
const fileName = frame.file;
|
||||
if (fileName) {
|
||||
|
||||
@@ -102,7 +102,6 @@ pub fn buildCommand(ctx: bun.cli.Command.Context) !void {
|
||||
if (vm.exit_handler.exit_code == 0) {
|
||||
vm.exit_handler.exit_code = 1;
|
||||
}
|
||||
vm.onExit();
|
||||
vm.globalExit();
|
||||
},
|
||||
else => |e| return e,
|
||||
|
||||
@@ -47,7 +47,7 @@ pub const Run = struct {
|
||||
vm.preload = ctx.preloads;
|
||||
vm.argv = ctx.passthrough;
|
||||
vm.arena = &run.arena;
|
||||
vm.allocator = vm.arena.allocator();
|
||||
vm.allocator = arena.allocator();
|
||||
|
||||
b.options.install = ctx.install;
|
||||
b.resolver.opts.install = ctx.install;
|
||||
@@ -135,7 +135,7 @@ pub const Run = struct {
|
||||
null,
|
||||
);
|
||||
try bundle.runEnvLoader(false);
|
||||
const mini = jsc.MiniEventLoop.initGlobal(bundle.env, null);
|
||||
const mini = jsc.MiniEventLoop.initGlobal(bundle.env);
|
||||
mini.top_level_dir = ctx.args.absolute_working_dir orelse "";
|
||||
return bun.shell.Interpreter.initAndRunFromFile(ctx, mini, entry_path);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ pub const Run = struct {
|
||||
vm.preload = ctx.preloads;
|
||||
vm.argv = ctx.passthrough;
|
||||
vm.arena = &run.arena;
|
||||
vm.allocator = vm.arena.allocator();
|
||||
vm.allocator = arena.allocator();
|
||||
|
||||
if (ctx.runtime_options.eval.script.len > 0) {
|
||||
const script_source = try bun.default_allocator.create(logger.Source);
|
||||
|
||||
@@ -2278,13 +2278,6 @@ pub const Formatter = struct {
|
||||
}
|
||||
},
|
||||
.Error => {
|
||||
// Temporarily remove from the visited map to allow printErrorlikeObject to process it
|
||||
// The circular reference check is already done in printAs, so we know it's safe
|
||||
const was_in_map = if (this.map_node != null) this.map.remove(value) else false;
|
||||
defer if (was_in_map) {
|
||||
_ = this.map.put(value, {}) catch {};
|
||||
};
|
||||
|
||||
VirtualMachine.get().printErrorlikeObject(
|
||||
value,
|
||||
null,
|
||||
|
||||
@@ -26,7 +26,7 @@ pub const log = Output.scoped(.debugger, .visible);
|
||||
extern "c" fn Bun__createJSDebugger(*JSGlobalObject) u32;
|
||||
extern "c" fn Bun__ensureDebugger(u32, bool) void;
|
||||
extern "c" fn Bun__startJSDebuggerThread(*JSGlobalObject, u32, *bun.String, c_int, bool) void;
|
||||
var futex_atomic: std.atomic.Value(u32) = .init(0);
|
||||
var futex_atomic: std.atomic.Value(u32) = undefined;
|
||||
|
||||
pub fn waitForDebuggerIfNecessary(this: *VirtualMachine) void {
|
||||
const debugger = &(this.debugger orelse return);
|
||||
@@ -127,6 +127,7 @@ pub fn create(this: *VirtualMachine, globalObject: *JSGlobalObject) !void {
|
||||
debugger.script_execution_context_id = Bun__createJSDebugger(globalObject);
|
||||
if (!this.has_started_debugger) {
|
||||
this.has_started_debugger = true;
|
||||
futex_atomic = std.atomic.Value(u32).init(0);
|
||||
var thread = try std.Thread.spawn(.{}, startJSDebuggerThread, .{this});
|
||||
thread.detach();
|
||||
}
|
||||
@@ -298,7 +299,6 @@ pub const TestReporterAgent = struct {
|
||||
handle: ?*Handle = null,
|
||||
const debug = Output.scoped(.TestReporterAgent, .visible);
|
||||
|
||||
/// this enum is kept in sync with c++ InspectorTestReporterAgent.cpp `enum class BunTestStatus`
|
||||
pub const TestStatus = enum(u8) {
|
||||
pass,
|
||||
fail,
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
#raw: jsc.JSValue,
|
||||
#safety: Safety,
|
||||
const Safety = if (enable_safety) ?struct { ptr: *Strong, gpa: std.mem.Allocator, ref_count: u32 } else void;
|
||||
pub fn initNonCell(non_cell: jsc.JSValue) Strong {
|
||||
bun.assert(!non_cell.isCell());
|
||||
const safety: Safety = if (enable_safety) null;
|
||||
return .{ .#raw = non_cell, .#safety = safety };
|
||||
}
|
||||
pub fn init(safety_gpa: std.mem.Allocator, value: jsc.JSValue) Strong {
|
||||
value.protect();
|
||||
const safety: Safety = if (enable_safety) .{ .ptr = bun.create(safety_gpa, Strong, .{ .#raw = @enumFromInt(0xAEBCFA), .#safety = null }), .gpa = safety_gpa, .ref_count = 1 };
|
||||
return .{ .#raw = value, .#safety = safety };
|
||||
}
|
||||
pub fn deinit(this: *Strong) void {
|
||||
this.#raw.unprotect();
|
||||
if (enable_safety) if (this.#safety) |safety| {
|
||||
bun.assert(@intFromEnum(safety.ptr.*.#raw) == 0xAEBCFA);
|
||||
safety.ptr.*.#raw = @enumFromInt(0xFFFFFF);
|
||||
bun.assert(safety.ref_count == 1);
|
||||
safety.gpa.destroy(safety.ptr);
|
||||
};
|
||||
}
|
||||
pub fn get(this: Strong) jsc.JSValue {
|
||||
return this.#raw;
|
||||
}
|
||||
pub fn swap(this: *Strong, safety_gpa: std.mem.Allocator, next: jsc.JSValue) jsc.JSValue {
|
||||
const prev = this.#raw;
|
||||
this.deinit();
|
||||
this.* = .init(safety_gpa, next);
|
||||
return prev;
|
||||
}
|
||||
pub fn dupe(this: Strong, gpa: std.mem.Allocator) Strong {
|
||||
return .init(gpa, this.get());
|
||||
}
|
||||
pub fn ref(this: *Strong) void {
|
||||
this.#raw.protect();
|
||||
if (enable_safety) if (this.#safety) |safety| {
|
||||
safety.ref_count += 1;
|
||||
};
|
||||
}
|
||||
pub fn unref(this: *Strong) void {
|
||||
this.#raw.unprotect();
|
||||
if (enable_safety) if (this.#safety) |safety| {
|
||||
if (safety.ref_count == 1) {
|
||||
bun.assert(@intFromEnum(safety.ptr.*.#raw) == 0xAEBCFA);
|
||||
safety.ptr.*.#raw = @enumFromInt(0xFFFFFF);
|
||||
safety.gpa.destroy(safety.ptr);
|
||||
return;
|
||||
}
|
||||
safety.ref_count -= 1;
|
||||
};
|
||||
}
|
||||
|
||||
pub const Optional = struct {
|
||||
#backing: Strong,
|
||||
pub const empty: Optional = .initNonCell(null);
|
||||
pub fn initNonCell(non_cell: ?jsc.JSValue) Optional {
|
||||
return .{ .#backing = .initNonCell(non_cell orelse .zero) };
|
||||
}
|
||||
pub fn init(safety_gpa: std.mem.Allocator, value: ?jsc.JSValue) Optional {
|
||||
return .{ .#backing = .init(safety_gpa, value orelse .zero) };
|
||||
}
|
||||
pub fn deinit(this: *Optional) void {
|
||||
this.#backing.deinit();
|
||||
}
|
||||
pub fn get(this: Optional) ?jsc.JSValue {
|
||||
const result = this.#backing.get();
|
||||
if (result == .zero) return null;
|
||||
return result;
|
||||
}
|
||||
pub fn swap(this: *Optional, safety_gpa: std.mem.Allocator, next: ?jsc.JSValue) ?jsc.JSValue {
|
||||
const result = this.#backing.swap(safety_gpa, next orelse .zero);
|
||||
if (result == .zero) return null;
|
||||
return result;
|
||||
}
|
||||
pub fn dupe(this: Optional, gpa: std.mem.Allocator) Optional {
|
||||
return .{ .#backing = this.#backing.dupe(gpa) };
|
||||
}
|
||||
pub fn has(this: Optional) bool {
|
||||
return this.#backing.get() != .zero;
|
||||
}
|
||||
pub fn ref(this: *Optional) void {
|
||||
this.#backing.ref();
|
||||
}
|
||||
pub fn unref(this: *Optional) void {
|
||||
this.#backing.unref();
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
const jsc = bun.jsc;
|
||||
const enable_safety = bun.Environment.ci_assert;
|
||||
const Strong = jsc.Strong.Deprecated;
|
||||
@@ -17,6 +17,18 @@ pub fn disable(this: *ProcessAutoKiller) void {
|
||||
|
||||
pub const Result = struct {
|
||||
processes: u32 = 0,
|
||||
|
||||
pub fn format(self: @This(), comptime _: []const u8, _: anytype, writer: anytype) !void {
|
||||
switch (self.processes) {
|
||||
0 => {},
|
||||
1 => {
|
||||
try writer.writeAll("killed 1 dangling process");
|
||||
},
|
||||
else => {
|
||||
try std.fmt.format(writer, "killed {d} dangling processes", .{self.processes});
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn kill(this: *ProcessAutoKiller) Result {
|
||||
|
||||
@@ -298,13 +298,13 @@ pub fn get(this: *SavedSourceMap, path: string) ?*ParsedSourceMap {
|
||||
pub fn resolveMapping(
|
||||
this: *SavedSourceMap,
|
||||
path: []const u8,
|
||||
line: bun.Ordinal,
|
||||
column: bun.Ordinal,
|
||||
line: i32,
|
||||
column: i32,
|
||||
source_handling: SourceMap.SourceContentHandling,
|
||||
) ?SourceMap.Mapping.Lookup {
|
||||
const parse = this.getWithContent(path, switch (source_handling) {
|
||||
.no_source_contents => .mappings_only,
|
||||
.source_contents => .{ .all = .{ .line = @max(line.zeroBased(), 0), .column = @max(column.zeroBased(), 0) } },
|
||||
.source_contents => .{ .all = .{ .line = line, .column = column } },
|
||||
});
|
||||
const map = parse.map orelse return null;
|
||||
|
||||
|
||||
@@ -147,7 +147,5 @@ const Impl = opaque {
|
||||
extern fn Bun__StrongRef__clear(this: *Impl) void;
|
||||
};
|
||||
|
||||
pub const Deprecated = @import("./DeprecatedStrong.zig");
|
||||
|
||||
const bun = @import("bun");
|
||||
const jsc = bun.jsc;
|
||||
|
||||
@@ -61,6 +61,7 @@ is_printing_plugin: bool = false,
|
||||
is_shutting_down: bool = false,
|
||||
plugin_runner: ?PluginRunner = null,
|
||||
is_main_thread: bool = false,
|
||||
last_reported_error_for_dedupe: JSValue = .zero,
|
||||
exit_handler: ExitHandler = .{},
|
||||
|
||||
default_tls_reject_unauthorized: ?bool = null,
|
||||
@@ -201,7 +202,10 @@ pub fn allowRejectionHandledWarning(this: *VirtualMachine) callconv(.C) bool {
|
||||
return this.unhandledRejectionsMode() != .bun;
|
||||
}
|
||||
pub fn unhandledRejectionsMode(this: *VirtualMachine) api.UnhandledRejections {
|
||||
return this.transpiler.options.transform_options.unhandled_rejections orelse .bun;
|
||||
return this.transpiler.options.transform_options.unhandled_rejections orelse switch (bun.FeatureFlags.breaking_changes_1_3) {
|
||||
false => .bun,
|
||||
true => .throw,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initRequestBodyValue(this: *VirtualMachine, body: jsc.WebCore.Body.Value) !*Body.Value.HiveRef {
|
||||
@@ -833,12 +837,8 @@ pub fn onExit(this: *VirtualMachine) void {
|
||||
extern fn Zig__GlobalObject__destructOnExit(*JSGlobalObject) void;
|
||||
|
||||
pub fn globalExit(this: *VirtualMachine) noreturn {
|
||||
bun.assert(this.isShuttingDown());
|
||||
if (this.shouldDestructMainThreadOnExit()) {
|
||||
if (this.eventLoop().forever_timer) |t| t.deinit(true);
|
||||
Zig__GlobalObject__destructOnExit(this.global);
|
||||
this.transpiler.deinit();
|
||||
this.gc_controller.deinit();
|
||||
this.deinit();
|
||||
}
|
||||
bun.Global.exit(this.exit_handler.exit_code);
|
||||
@@ -1915,6 +1915,7 @@ pub fn processFetchLog(globalThis: *JSGlobalObject, specifier: bun.String, refer
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
pub fn deinit(this: *VirtualMachine) void {
|
||||
this.auto_killer.deinit();
|
||||
|
||||
@@ -1954,8 +1955,17 @@ pub fn printException(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runErrorHandlerWithDedupe(this: *VirtualMachine, result: JSValue, exception_list: ?*ExceptionList) void {
|
||||
if (this.last_reported_error_for_dedupe == result and !this.last_reported_error_for_dedupe.isEmptyOrUndefinedOrNull())
|
||||
return;
|
||||
|
||||
this.runErrorHandler(result, exception_list);
|
||||
}
|
||||
|
||||
pub noinline fn runErrorHandler(this: *VirtualMachine, result: JSValue, exception_list: ?*ExceptionList) void {
|
||||
@branchHint(.cold);
|
||||
if (!result.isEmptyOrUndefinedOrNull())
|
||||
this.last_reported_error_for_dedupe = result;
|
||||
|
||||
const prev_had_errors = this.had_errors;
|
||||
this.had_errors = false;
|
||||
@@ -2308,7 +2318,7 @@ pub fn loadMacroEntryPoint(this: *VirtualMachine, entry_path: string, function_n
|
||||
/// We cannot hold it from Zig code because it relies on C++ ARIA to automatically release the lock
|
||||
/// and it is not safe to copy the lock itself
|
||||
/// So we have to wrap entry points to & from JavaScript with an API lock that calls out to C++
|
||||
pub fn runWithAPILock(this: *VirtualMachine, comptime Context: type, ctx: *Context, comptime function: fn (ctx: *Context) void) void {
|
||||
pub inline fn runWithAPILock(this: *VirtualMachine, comptime Context: type, ctx: *Context, comptime function: fn (ctx: *Context) void) void {
|
||||
this.global.vm().holdAPILock(ctx, jsc.OpaqueWrap(Context, function));
|
||||
}
|
||||
|
||||
@@ -2596,8 +2606,8 @@ pub fn remapStackFramePositions(this: *VirtualMachine, frames: [*]jsc.ZigStackFr
|
||||
|
||||
if (this.resolveSourceMapping(
|
||||
sourceURL.slice(),
|
||||
frame.position.line,
|
||||
frame.position.column,
|
||||
@max(frame.position.line.zeroBased(), 0),
|
||||
@max(frame.position.column.zeroBased(), 0),
|
||||
.no_source_contents,
|
||||
)) |lookup| {
|
||||
const source_map = lookup.source_map;
|
||||
@@ -2735,8 +2745,8 @@ pub fn remapZigException(
|
||||
else
|
||||
this.resolveSourceMapping(
|
||||
top_source_url.slice(),
|
||||
top.position.line,
|
||||
top.position.column,
|
||||
@max(top.position.line.zeroBased(), 0),
|
||||
@max(top.position.column.zeroBased(), 0),
|
||||
.source_contents,
|
||||
);
|
||||
|
||||
@@ -2824,8 +2834,8 @@ pub fn remapZigException(
|
||||
defer source_url.deinit();
|
||||
if (this.resolveSourceMapping(
|
||||
source_url.slice(),
|
||||
frame.position.line,
|
||||
frame.position.column,
|
||||
@max(frame.position.line.zeroBased(), 0),
|
||||
@max(frame.position.column.zeroBased(), 0),
|
||||
.no_source_contents,
|
||||
)) |lookup| {
|
||||
defer if (lookup.source_map) |map| map.deref();
|
||||
@@ -3236,23 +3246,8 @@ fn printErrorInstance(
|
||||
}
|
||||
|
||||
for (errors_to_append.items) |err| {
|
||||
// Check for circular references to prevent infinite recursion in cause chains
|
||||
if (formatter.map_node == null) {
|
||||
formatter.map_node = ConsoleObject.Formatter.Visited.Pool.get(default_allocator);
|
||||
formatter.map_node.?.data.clearRetainingCapacity();
|
||||
formatter.map = formatter.map_node.?.data;
|
||||
}
|
||||
|
||||
const entry = formatter.map.getOrPut(err) catch unreachable;
|
||||
if (entry.found_existing) {
|
||||
try writer.writeAll("\n");
|
||||
try writer.writeAll(comptime Output.prettyFmt("<r><cyan>[Circular]<r>", allow_ansi_color));
|
||||
continue;
|
||||
}
|
||||
|
||||
try writer.writeAll("\n");
|
||||
try this.printErrorInstance(.js, err, exception_list, formatter, Writer, writer, allow_ansi_color, allow_side_effects);
|
||||
_ = formatter.map.remove(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3445,8 +3440,8 @@ pub noinline fn printGithubAnnotation(exception: *ZigException) void {
|
||||
pub fn resolveSourceMapping(
|
||||
this: *VirtualMachine,
|
||||
path: []const u8,
|
||||
line: Ordinal,
|
||||
column: Ordinal,
|
||||
line: i32,
|
||||
column: i32,
|
||||
source_handling: SourceMap.SourceContentHandling,
|
||||
) ?SourceMap.Mapping.Lookup {
|
||||
return this.source_mappings.resolveMapping(path, line, column, source_handling) orelse {
|
||||
|
||||
@@ -1294,9 +1294,7 @@ pub fn setTLSDefaultCiphers(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject, c
|
||||
}
|
||||
|
||||
pub fn getValkeyDefaultClient(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue {
|
||||
const SubscriptionCtx = @import("../../valkey/js_valkey.zig").SubscriptionCtx;
|
||||
|
||||
var valkey = jsc.API.Valkey.createNoJsNoPubsub(globalThis, &.{.js_undefined}) catch |err| {
|
||||
const valkey = jsc.API.Valkey.create(globalThis, &.{.js_undefined}) catch |err| {
|
||||
if (err != error.JSError) {
|
||||
_ = globalThis.throwError(err, "Failed to create Redis client") catch {};
|
||||
return .zero;
|
||||
@@ -1304,18 +1302,7 @@ pub fn getValkeyDefaultClient(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject)
|
||||
return .zero;
|
||||
};
|
||||
|
||||
const as_js = valkey.toJS(globalThis);
|
||||
|
||||
valkey.this_value = jsc.JSRef.initWeak(as_js);
|
||||
valkey._subscription_ctx = SubscriptionCtx.init(valkey) catch |err| {
|
||||
if (err != error.JSError) {
|
||||
_ = globalThis.throwError(err, "Failed to create Redis client") catch {};
|
||||
return .zero;
|
||||
}
|
||||
return .zero;
|
||||
};
|
||||
|
||||
return as_js;
|
||||
return valkey.toJS(globalThis);
|
||||
}
|
||||
|
||||
pub fn getValkeyClientConstructor(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue {
|
||||
@@ -1346,6 +1333,7 @@ pub fn getEmbeddedFiles(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) bun.J
|
||||
// We call .dupe() on this to ensure that we don't return a blob that might get freed later.
|
||||
const input_blob = file.blob(globalThis);
|
||||
const blob = jsc.WebCore.Blob.new(input_blob.dupeWithContentType(true));
|
||||
blob.allocator = bun.default_allocator;
|
||||
blob.name = input_blob.name.dupeRef();
|
||||
try array.putIndex(globalThis, i, blob.toJS(globalThis));
|
||||
i += 1;
|
||||
@@ -1826,7 +1814,7 @@ pub const JSZstd = struct {
|
||||
output = try allocator.realloc(output, compressed_size);
|
||||
}
|
||||
|
||||
return jsc.JSValue.createBuffer(globalThis, output);
|
||||
return jsc.JSValue.createBuffer(globalThis, output, bun.default_allocator);
|
||||
}
|
||||
|
||||
pub fn decompressSync(globalThis: *JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
@@ -1861,7 +1849,7 @@ pub const JSZstd = struct {
|
||||
// mimalloc doesn't care about the self-reported size of the slice.
|
||||
output.len = actual_size;
|
||||
|
||||
return jsc.JSValue.createBuffer(globalThis, output);
|
||||
return jsc.JSValue.createBuffer(globalThis, output, bun.default_allocator);
|
||||
}
|
||||
|
||||
// --- Async versions ---
|
||||
@@ -1950,7 +1938,6 @@ pub const JSZstd = struct {
|
||||
|
||||
pub fn runFromJS(this: *ZstdJob) void {
|
||||
defer this.deinit();
|
||||
|
||||
if (this.vm.isShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
@@ -1964,7 +1951,7 @@ pub const JSZstd = struct {
|
||||
}
|
||||
|
||||
const output_slice = this.output;
|
||||
const buffer_value = jsc.JSValue.createBuffer(globalThis, output_slice);
|
||||
const buffer_value = jsc.JSValue.createBuffer(globalThis, output_slice, bun.default_allocator);
|
||||
this.output = &[_]u8{};
|
||||
promise.resolve(globalThis, buffer_value);
|
||||
}
|
||||
@@ -2056,6 +2043,7 @@ pub fn createBunStdin(globalThis: *jsc.JSGlobalObject) callconv(.C) jsc.JSValue
|
||||
var blob = jsc.WebCore.Blob.new(
|
||||
jsc.WebCore.Blob.initWithStore(store, globalThis),
|
||||
);
|
||||
blob.allocator = bun.default_allocator;
|
||||
return blob.toJS(globalThis);
|
||||
}
|
||||
|
||||
@@ -2066,6 +2054,7 @@ pub fn createBunStderr(globalThis: *jsc.JSGlobalObject) callconv(.C) jsc.JSValue
|
||||
var blob = jsc.WebCore.Blob.new(
|
||||
jsc.WebCore.Blob.initWithStore(store, globalThis),
|
||||
);
|
||||
blob.allocator = bun.default_allocator;
|
||||
return blob.toJS(globalThis);
|
||||
}
|
||||
|
||||
@@ -2076,6 +2065,7 @@ pub fn createBunStdout(globalThis: *jsc.JSGlobalObject) callconv(.C) jsc.JSValue
|
||||
var blob = jsc.WebCore.Blob.new(
|
||||
jsc.WebCore.Blob.initWithStore(store, globalThis),
|
||||
);
|
||||
blob.allocator = bun.default_allocator;
|
||||
return blob.toJS(globalThis);
|
||||
}
|
||||
|
||||
|
||||
@@ -582,7 +582,7 @@ pub fn toBuffer(
|
||||
return jsc.JSValue.createBufferWithCtx(globalThis, slice, ctx, callback);
|
||||
}
|
||||
|
||||
return jsc.JSValue.createBuffer(globalThis, slice);
|
||||
return jsc.JSValue.createBuffer(globalThis, slice, null);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,7 @@ pub const JSBundler = struct {
|
||||
outdir: OwnedString = OwnedString.initEmpty(bun.default_allocator),
|
||||
rootdir: OwnedString = OwnedString.initEmpty(bun.default_allocator),
|
||||
serve: Serve = .{},
|
||||
jsx: api.Jsx = .{
|
||||
.factory = "",
|
||||
.fragment = "",
|
||||
.runtime = .automatic,
|
||||
.import_source = "",
|
||||
.development = true, // Default to development mode like old Pragma
|
||||
},
|
||||
jsx: options.JSX.Pragma = .{},
|
||||
force_node_env: options.BundleOptions.ForceNodeEnv = .unspecified,
|
||||
code_splitting: bool = false,
|
||||
minify: Minify = .{},
|
||||
@@ -387,51 +381,6 @@ pub const JSBundler = struct {
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
// Parse JSX configuration
|
||||
if (try config.getTruthy(globalThis, "jsx")) |jsx_value| {
|
||||
if (!jsx_value.isObject()) {
|
||||
return globalThis.throwInvalidArguments("jsx must be an object", .{});
|
||||
}
|
||||
|
||||
if (try jsx_value.getOptional(globalThis, "runtime", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
var str_lower: [128]u8 = undefined;
|
||||
const len = @min(slice.len, str_lower.len);
|
||||
_ = strings.copyLowercase(slice.slice()[0..len], str_lower[0..len]);
|
||||
if (options.JSX.RuntimeMap.get(str_lower[0..len])) |runtime| {
|
||||
this.jsx.runtime = runtime.runtime;
|
||||
if (runtime.development) |dev| {
|
||||
this.jsx.development = dev;
|
||||
}
|
||||
} else {
|
||||
return globalThis.throwInvalidArguments("Invalid jsx.runtime: '{s}'. Must be one of: 'classic', 'automatic', 'react', 'react-jsx', or 'react-jsxdev'", .{slice.slice()});
|
||||
}
|
||||
}
|
||||
|
||||
if (try jsx_value.getOptional(globalThis, "factory", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
this.jsx.factory = try allocator.dupe(u8, slice.slice());
|
||||
}
|
||||
|
||||
if (try jsx_value.getOptional(globalThis, "fragment", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
this.jsx.fragment = try allocator.dupe(u8, slice.slice());
|
||||
}
|
||||
|
||||
if (try jsx_value.getOptional(globalThis, "importSource", ZigString.Slice)) |slice| {
|
||||
defer slice.deinit();
|
||||
this.jsx.import_source = try allocator.dupe(u8, slice.slice());
|
||||
}
|
||||
|
||||
if (try jsx_value.getBooleanLoose(globalThis, "development")) |dev| {
|
||||
this.jsx.development = dev;
|
||||
}
|
||||
|
||||
if (try jsx_value.getBooleanLoose(globalThis, "sideEffects")) |val| {
|
||||
this.jsx.side_effects = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (try config.getOptionalEnum(globalThis, "format", options.Format)) |format| {
|
||||
this.format = format;
|
||||
|
||||
@@ -769,19 +718,6 @@ pub const JSBundler = struct {
|
||||
bun.default_allocator.free(loaders.loaders);
|
||||
bun.default_allocator.free(loaders.extensions);
|
||||
}
|
||||
// Free JSX allocated strings
|
||||
if (self.jsx.factory.len > 0) {
|
||||
allocator.free(self.jsx.factory);
|
||||
self.jsx.factory = "";
|
||||
}
|
||||
if (self.jsx.fragment.len > 0) {
|
||||
allocator.free(self.jsx.fragment);
|
||||
self.jsx.fragment = "";
|
||||
}
|
||||
if (self.jsx.import_source.len > 0) {
|
||||
allocator.free(self.jsx.import_source);
|
||||
self.jsx.import_source = "";
|
||||
}
|
||||
self.names.deinit();
|
||||
self.outdir.deinit();
|
||||
self.rootdir.deinit();
|
||||
|
||||
@@ -1014,7 +1014,7 @@ fn namedExportsToJS(global: *JSGlobalObject, named_exports: *JSAst.Ast.NamedExpo
|
||||
});
|
||||
var i: usize = 0;
|
||||
while (named_exports_iter.next()) |entry| {
|
||||
names[i] = bun.String.fromBytes(entry.key_ptr.*);
|
||||
names[i] = bun.String.cloneUTF8(entry.key_ptr.*);
|
||||
i += 1;
|
||||
}
|
||||
return bun.String.toJSArray(global, names);
|
||||
|
||||
@@ -51,6 +51,7 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
TimerCallback,
|
||||
TimeoutObject,
|
||||
ImmediateObject,
|
||||
TestRunner,
|
||||
StatWatcherScheduler,
|
||||
UpgradedDuplex,
|
||||
DNSResolver,
|
||||
@@ -67,7 +68,6 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
DevServerMemoryVisualizerTick,
|
||||
AbortSignalTimeout,
|
||||
DateHeaderTimer,
|
||||
BunTest,
|
||||
EventLoopDelayMonitor,
|
||||
|
||||
pub fn Type(comptime T: Tag) type {
|
||||
@@ -75,6 +75,7 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
.TimerCallback => TimerCallback,
|
||||
.TimeoutObject => TimeoutObject,
|
||||
.ImmediateObject => ImmediateObject,
|
||||
.TestRunner => jsc.Jest.TestRunner,
|
||||
.StatWatcherScheduler => StatWatcherScheduler,
|
||||
.UpgradedDuplex => uws.UpgradedDuplex,
|
||||
.DNSResolver => DNSResolver,
|
||||
@@ -92,7 +93,6 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
=> bun.bake.DevServer,
|
||||
.AbortSignalTimeout => jsc.WebCore.AbortSignal.Timeout,
|
||||
.DateHeaderTimer => jsc.API.Timer.DateHeaderTimer,
|
||||
.BunTest => jsc.Jest.bun_test.BunTest,
|
||||
.EventLoopDelayMonitor => jsc.API.Timer.EventLoopDelayMonitor,
|
||||
};
|
||||
}
|
||||
@@ -100,6 +100,7 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
TimerCallback,
|
||||
TimeoutObject,
|
||||
ImmediateObject,
|
||||
TestRunner,
|
||||
StatWatcherScheduler,
|
||||
UpgradedDuplex,
|
||||
WTFTimer,
|
||||
@@ -115,7 +116,6 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
DevServerMemoryVisualizerTick,
|
||||
AbortSignalTimeout,
|
||||
DateHeaderTimer,
|
||||
BunTest,
|
||||
EventLoopDelayMonitor,
|
||||
|
||||
pub fn Type(comptime T: Tag) type {
|
||||
@@ -123,6 +123,7 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
.TimerCallback => TimerCallback,
|
||||
.TimeoutObject => TimeoutObject,
|
||||
.ImmediateObject => ImmediateObject,
|
||||
.TestRunner => jsc.Jest.TestRunner,
|
||||
.StatWatcherScheduler => StatWatcherScheduler,
|
||||
.UpgradedDuplex => uws.UpgradedDuplex,
|
||||
.WTFTimer => WTFTimer,
|
||||
@@ -139,7 +140,6 @@ pub const Tag = if (Environment.isWindows) enum {
|
||||
=> bun.bake.DevServer,
|
||||
.AbortSignalTimeout => jsc.WebCore.AbortSignal.Timeout,
|
||||
.DateHeaderTimer => jsc.API.Timer.DateHeaderTimer,
|
||||
.BunTest => jsc.Jest.bun_test.BunTest,
|
||||
.EventLoopDelayMonitor => jsc.API.Timer.EventLoopDelayMonitor,
|
||||
};
|
||||
}
|
||||
@@ -217,11 +217,6 @@ pub fn fire(self: *Self, now: *const timespec, vm: *VirtualMachine) Arm {
|
||||
date_header_timer.run(vm);
|
||||
return .disarm;
|
||||
},
|
||||
.BunTest => {
|
||||
var container_strong = jsc.Jest.bun_test.BunTestPtr.cloneFromRawUnsafe(@fieldParentPtr("timer", self));
|
||||
defer container_strong.deinit();
|
||||
return jsc.Jest.bun_test.BunTest.bunTestTimeoutCallback(container_strong, now, vm);
|
||||
},
|
||||
.EventLoopDelayMonitor => {
|
||||
const monitor = @as(*jsc.API.Timer.EventLoopDelayMonitor, @fieldParentPtr("event_loop_timer", self));
|
||||
monitor.onFire(vm, now);
|
||||
@@ -252,6 +247,11 @@ pub fn fire(self: *Self, now: *const timespec, vm: *VirtualMachine) Arm {
|
||||
}
|
||||
}
|
||||
|
||||
if (comptime t.Type() == jsc.Jest.TestRunner) {
|
||||
container.onTestTimeout(now, vm);
|
||||
return .disarm;
|
||||
}
|
||||
|
||||
if (comptime t.Type() == DNSResolver) {
|
||||
return container.checkTimeouts(now, vm);
|
||||
}
|
||||
@@ -265,6 +265,8 @@ pub fn fire(self: *Self, now: *const timespec, vm: *VirtualMachine) Arm {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(_: *Self) void {}
|
||||
|
||||
/// A timer created by WTF code and invoked by Bun's event loop
|
||||
const WTFTimer = bun.api.Timer.WTFTimer;
|
||||
|
||||
|
||||
@@ -925,14 +925,7 @@ pub fn parse(
|
||||
const root = bun.interchange.yaml.YAML.parse(source, &log, arena.allocator()) catch |err| return switch (err) {
|
||||
error.OutOfMemory => |oom| oom,
|
||||
error.StackOverflow => global.throwStackOverflow(),
|
||||
else => {
|
||||
if (log.msgs.items.len > 0) {
|
||||
const first_msg = log.msgs.items[0];
|
||||
const error_text = first_msg.data.text;
|
||||
return global.throwValue(global.createSyntaxErrorInstance("YAML Parse error: {s}", .{error_text}));
|
||||
}
|
||||
return global.throwValue(global.createSyntaxErrorInstance("YAML Parse error: Unable to parse YAML string", .{}));
|
||||
},
|
||||
else => global.throwValue(try log.toJS(global, bun.default_allocator, "Failed to parse YAML")),
|
||||
};
|
||||
|
||||
var ctx: ParserCtx = .{
|
||||
@@ -1030,7 +1023,7 @@ const ParserCtx = struct {
|
||||
const key_str = try key.toBunString(ctx.global);
|
||||
defer key_str.deref();
|
||||
|
||||
try obj.putMayBeIndex(ctx.global, &key_str, value);
|
||||
obj.putMayBeIndex(ctx.global, &key_str, value);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
@@ -4270,8 +4270,8 @@ pub const H2FrameParser = struct {
|
||||
}
|
||||
|
||||
pub fn detachNativeSocket(this: *H2FrameParser) void {
|
||||
const native_socket = this.native_socket;
|
||||
this.native_socket = .{ .none = {} };
|
||||
const native_socket = this.native_socket;
|
||||
|
||||
switch (native_socket) {
|
||||
inline .tcp, .tls => |socket| {
|
||||
|
||||
@@ -111,7 +111,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn doConnect(this: *This, connection: Listener.UnixOrHost) !void {
|
||||
bun.assert(this.socket_context != null);
|
||||
this.ref();
|
||||
defer this.deref();
|
||||
errdefer this.deref();
|
||||
|
||||
switch (connection) {
|
||||
.host => |c| {
|
||||
@@ -830,7 +830,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getRemoteAddress(this: *This, globalThis: *jsc.JSGlobalObject) bun.JSError!JSValue {
|
||||
pub fn getRemoteAddress(this: *This, globalThis: *jsc.JSGlobalObject) JSValue {
|
||||
if (this.socket.isDetached()) {
|
||||
return .js_undefined;
|
||||
}
|
||||
@@ -846,7 +846,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
};
|
||||
|
||||
const text = bun.fmt.formatIp(address, &text_buf) catch unreachable;
|
||||
return bun.String.createUTF8ForJS(globalThis, text);
|
||||
return ZigString.init(text).toJS(globalThis);
|
||||
}
|
||||
|
||||
pub fn getRemotePort(this: *This, _: *jsc.JSGlobalObject) JSValue {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user