From 5763a8e533ab14b1765840a0c1a0d0949f358637 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 12 Jun 2025 13:20:28 -0800 Subject: [PATCH 001/133] node:zlib: add zstd (#20313) Co-authored-by: nektro <5464072+nektro@users.noreply.github.com> --- .github/workflows/update-zstd.yml | 10 +- build.zig | 2 + cmake/sources/ZigSources.txt | 1 + cmake/targets/BuildBun.cmake | 40 +-- cmake/targets/BuildZstd.cmake | 11 +- cmake/targets/CloneZstd.cmake | 8 + src/bun.js/api.zig | 1 + src/bun.js/api/zlib.classes.ts | 2 +- src/bun.js/bindings/ErrorCode.cpp | 8 + src/bun.js/bindings/ErrorCode.ts | 1 + .../bindings/ProcessBindingConstants.cpp | 66 +++++ .../bindings/generated_classes_list.zig | 1 + src/bun.js/event_loop/Task.zig | 6 + src/bun.js/node/node_zlib_binding.zig | 19 +- src/bun.js/node/zlib/NativeZstd.zig | 266 ++++++++++++++++++ src/c-headers-for-zig.h | 8 +- src/deps/zstd.zig | 208 +------------- src/js/node/zlib.ts | 105 ++++++- src/zlib.zig | 2 + test/js/node/test/fixtures/person.jpg.zst | Bin 0 -> 45404 bytes .../parallel/test-zlib-convenience-methods.js | 2 + .../test/parallel/test-zlib-empty-buffer.js | 2 + .../test/parallel/test-zlib-invalid-input.js | 1 + .../parallel/test-zlib-random-byte-pipes.js | 1 + .../parallel/test-zlib-write-after-flush.js | 1 + .../node/test/parallel/test-zlib-zero-byte.js | 11 +- .../test/parallel/test-zlib-zstd-flush.js | 28 ++ .../parallel/test-zlib-zstd-from-string.js | 38 +++ .../test/parallel/test-zlib-zstd-from-zstd.js | 34 +++ .../test-zlib-zstd-kmaxlength-rangeerror.js | 29 ++ .../test-zlib-zstd-pledged-src-size.js | 37 +++ test/js/node/test/parallel/test-zlib-zstd.js | 134 +++++++++ test/js/node/test/parallel/test-zlib.js | 1 + test/js/node/zlib/leak.test.ts | 53 +++- test/js/node/zlib/zlib.test.js | 173 +++++++++++- test/js/web/fetch/fetch.stream.test.ts | 7 +- 36 files changed, 1049 insertions(+), 268 deletions(-) create mode 100644 cmake/targets/CloneZstd.cmake create mode 100644 src/bun.js/node/zlib/NativeZstd.zig create mode 100644 test/js/node/test/fixtures/person.jpg.zst create mode 100644 test/js/node/test/parallel/test-zlib-zstd-flush.js create mode 100644 test/js/node/test/parallel/test-zlib-zstd-from-string.js create mode 100644 test/js/node/test/parallel/test-zlib-zstd-from-zstd.js create mode 100644 test/js/node/test/parallel/test-zlib-zstd-kmaxlength-rangeerror.js create mode 100644 test/js/node/test/parallel/test-zlib-zstd-pledged-src-size.js create mode 100644 test/js/node/test/parallel/test-zlib-zstd.js diff --git a/.github/workflows/update-zstd.yml b/.github/workflows/update-zstd.yml index a48020021c..6787eed141 100644 --- a/.github/workflows/update-zstd.yml +++ b/.github/workflows/update-zstd.yml @@ -21,16 +21,16 @@ jobs: set -euo pipefail # Extract the commit hash from the line after COMMIT - CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildZstd.cmake) + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/CloneZstd.cmake) if [ -z "$CURRENT_VERSION" ]; then - echo "Error: Could not find COMMIT line in BuildZstd.cmake" + echo "Error: Could not find COMMIT line in CloneZstd.cmake" exit 1 fi # Validate that it looks like a git hash if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then - echo "Error: Invalid git hash format in BuildZstd.cmake" + echo "Error: Invalid git hash format in CloneZstd.cmake" echo "Found: $CURRENT_VERSION" echo "Expected: 40 character hexadecimal string" exit 1 @@ -76,7 +76,7 @@ jobs: run: | set -euo pipefail # Handle multi-line format where COMMIT and its value are on separate lines - sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildZstd.cmake + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/CloneZstd.cmake - name: Create Pull Request if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest @@ -84,7 +84,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | - cmake/targets/BuildZstd.cmake + cmake/targets/CloneZstd.cmake commit-message: "deps: update zstd to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" title: "deps: update zstd to ${{ steps.check-version.outputs.tag }}" delete-branch: true diff --git a/build.zig b/build.zig index d3b78178e5..23459193ab 100644 --- a/build.zig +++ b/build.zig @@ -513,6 +513,8 @@ fn getTranslateC(b: *Build, initial_target: std.Build.ResolvedTarget, optimize: translate_c.defineCMacroRaw(b.fmt("{s}={d}", .{ str, @intFromBool(value) })); } + translate_c.addIncludePath(b.path("vendor/zstd/lib")); + if (target.result.os.tag == .windows) { // translate-c is unable to translate the unsuffixed windows functions // like `SetCurrentDirectory` since they are defined with an odd macro diff --git a/cmake/sources/ZigSources.txt b/cmake/sources/ZigSources.txt index 4ce90b84c6..26b0cb9eb8 100644 --- a/cmake/sources/ZigSources.txt +++ b/cmake/sources/ZigSources.txt @@ -199,6 +199,7 @@ src/bun.js/node/util/parse_args_utils.zig src/bun.js/node/util/parse_args.zig src/bun.js/node/util/validators.zig src/bun.js/node/win_watcher.zig +src/bun.js/node/zlib/NativeZstd.zig src/bun.js/ProcessAutoKiller.zig src/bun.js/rare_data.zig src/bun.js/ResolveMessage.zig diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index c68aa5d99a..055ceb3452 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -42,6 +42,29 @@ else() set(CONFIGURE_DEPENDS "") endif() +# --- Dependencies --- + +set(BUN_DEPENDENCIES + BoringSSL + Brotli + Cares + Highway + LibDeflate + LolHtml + Lshpack + Mimalloc + TinyCC + Zlib + LibArchive # must be loaded after zlib + HdrHistogram # must be loaded after zlib + Zstd +) + +include(CloneZstd) +# foreach(dependency ${BUN_DEPENDENCIES}) +# include(Clone${dependency}) +# endforeach() + # --- Codegen --- set(BUN_ERROR_SOURCE ${CWD}/packages/bun-error) @@ -582,6 +605,7 @@ register_command( ${BUN_ZIG_OUTPUT} TARGETS clone-zig + clone-zstd SOURCES ${BUN_ZIG_SOURCES} ${BUN_ZIG_GENERATED_SOURCES} @@ -1054,22 +1078,6 @@ endif() # --- Dependencies --- -set(BUN_DEPENDENCIES - BoringSSL - Brotli - Cares - Highway - LibDeflate - LolHtml - Lshpack - Mimalloc - TinyCC - Zlib - LibArchive # must be loaded after zlib - HdrHistogram # must be loaded after zlib - Zstd -) - if(WIN32) list(APPEND BUN_DEPENDENCIES Libuv) endif() diff --git a/cmake/targets/BuildZstd.cmake b/cmake/targets/BuildZstd.cmake index 2204c04884..697481c3d5 100644 --- a/cmake/targets/BuildZstd.cmake +++ b/cmake/targets/BuildZstd.cmake @@ -1,12 +1,3 @@ -register_repository( - NAME - zstd - REPOSITORY - facebook/zstd - COMMIT - f8745da6ff1ad1e7bab384bd1f9d742439278e99 -) - register_cmake_command( TARGET zstd @@ -23,4 +14,6 @@ register_cmake_command( LIBRARIES zstd_static WIN32 zstd UNIX + INCLUDES + lib ) diff --git a/cmake/targets/CloneZstd.cmake b/cmake/targets/CloneZstd.cmake new file mode 100644 index 0000000000..7edc95a4da --- /dev/null +++ b/cmake/targets/CloneZstd.cmake @@ -0,0 +1,8 @@ +register_repository( + NAME + zstd + REPOSITORY + facebook/zstd + COMMIT + f8745da6ff1ad1e7bab384bd1f9d742439278e99 +) diff --git a/src/bun.js/api.zig b/src/bun.js/api.zig index fadc6f249e..94c96ab4c2 100644 --- a/src/bun.js/api.zig +++ b/src/bun.js/api.zig @@ -44,6 +44,7 @@ pub const SocketHandlers = @import("api/bun/socket.zig").Handlers; pub const UDPSocket = @import("api/bun/udp_socket.zig").UDPSocket; pub const Valkey = @import("../valkey/js_valkey.zig").JSValkeyClient; pub const BlockList = @import("./node/net/BlockList.zig"); +pub const NativeZstd = @import("./node/zlib/NativeZstd.zig"); pub const napi = @import("../napi/napi.zig"); diff --git a/src/bun.js/api/zlib.classes.ts b/src/bun.js/api/zlib.classes.ts index 3a0b9b59b5..0475f1d2b4 100644 --- a/src/bun.js/api/zlib.classes.ts +++ b/src/bun.js/api/zlib.classes.ts @@ -24,4 +24,4 @@ function generate(name: string) { }); } -export default [generate("NativeZlib"), generate("NativeBrotli")]; +export default [generate("NativeZlib"), generate("NativeBrotli"), generate("NativeZstd")]; diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index c466e6a2d3..168a98f3e1 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -2263,6 +2263,14 @@ JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject return JSC::JSValue::encode(error); } + case Bun::ErrorCode::ERR_ZSTD_INVALID_PARAM: { + auto arg0 = callFrame->argument(1); + auto str0 = arg0.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto message = makeString(str0, " is not a valid zstd parameter"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_ZSTD_INVALID_PARAM, message)); + } + case ErrorCode::ERR_IPC_DISCONNECTED: return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_DISCONNECTED, "IPC channel is already disconnected"_s)); case ErrorCode::ERR_SERVER_NOT_RUNNING: diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts index 18283da02f..4b98659f03 100644 --- a/src/bun.js/bindings/ErrorCode.ts +++ b/src/bun.js/bindings/ErrorCode.ts @@ -254,6 +254,7 @@ const errors: ErrorCodeMapping = [ ["ERR_UNKNOWN_CREDENTIAL", Error], ["ERR_UNKNOWN_ENCODING", TypeError], ["ERR_UNKNOWN_SIGNAL", TypeError], + ["ERR_ZSTD_INVALID_PARAM", RangeError], ["ERR_USE_AFTER_CLOSE", Error], ["ERR_WASI_NOT_STARTED", Error], ["ERR_WORKER_INIT_FAILED", Error], diff --git a/src/bun.js/bindings/ProcessBindingConstants.cpp b/src/bun.js/bindings/ProcessBindingConstants.cpp index 789ba863ff..cf67373d32 100644 --- a/src/bun.js/bindings/ProcessBindingConstants.cpp +++ b/src/bun.js/bindings/ProcessBindingConstants.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1016,6 +1017,8 @@ static JSValue processBindingConstantsGetZlib(VM& vm, JSObject* bindingObject) object->putDirect(vm, PropertyName(Identifier::fromString(vm, "UNZIP"_s)), jsNumber(7)); object->putDirect(vm, PropertyName(Identifier::fromString(vm, "BROTLI_DECODE"_s)), jsNumber(8)); object->putDirect(vm, PropertyName(Identifier::fromString(vm, "BROTLI_ENCODE"_s)), jsNumber(9)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_COMPRESS"_s)), jsNumber(10)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_DECOMPRESS"_s)), jsNumber(11)); object->putDirect(vm, PropertyName(Identifier::fromString(vm, "Z_MIN_WINDOWBITS"_s)), jsNumber(8)); object->putDirect(vm, PropertyName(Identifier::fromString(vm, "Z_MAX_WINDOWBITS"_s)), jsNumber(15)); @@ -1092,6 +1095,69 @@ static JSValue processBindingConstantsGetZlib(VM& vm, JSObject* bindingObject) object->putDirect(vm, PropertyName(Identifier::fromString(vm, "BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES"_s)), jsNumber(BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES)); object->putDirect(vm, PropertyName(Identifier::fromString(vm, "BROTLI_DECODER_ERROR_UNREACHABLE"_s)), jsNumber(BROTLI_DECODER_ERROR_UNREACHABLE)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_e_continue"_s)), jsNumber(ZSTD_e_continue)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_e_flush"_s)), jsNumber(ZSTD_e_flush)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_e_end"_s)), jsNumber(ZSTD_e_end)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_fast"_s)), jsNumber(ZSTD_fast)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_dfast"_s)), jsNumber(ZSTD_dfast)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_greedy"_s)), jsNumber(ZSTD_greedy)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_lazy"_s)), jsNumber(ZSTD_lazy)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_lazy2"_s)), jsNumber(ZSTD_lazy2)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_btlazy2"_s)), jsNumber(ZSTD_btlazy2)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_btopt"_s)), jsNumber(ZSTD_btopt)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_btultra"_s)), jsNumber(ZSTD_btultra)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_btultra2"_s)), jsNumber(ZSTD_btultra2)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_compressionLevel"_s)), jsNumber(ZSTD_c_compressionLevel)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_windowLog"_s)), jsNumber(ZSTD_c_windowLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_hashLog"_s)), jsNumber(ZSTD_c_hashLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_chainLog"_s)), jsNumber(ZSTD_c_chainLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_searchLog"_s)), jsNumber(ZSTD_c_searchLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_minMatch"_s)), jsNumber(ZSTD_c_minMatch)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_targetLength"_s)), jsNumber(ZSTD_c_targetLength)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_strategy"_s)), jsNumber(ZSTD_c_strategy)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_enableLongDistanceMatching"_s)), jsNumber(ZSTD_c_enableLongDistanceMatching)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_ldmHashLog"_s)), jsNumber(ZSTD_c_ldmHashLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_ldmMinMatch"_s)), jsNumber(ZSTD_c_ldmMinMatch)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_ldmBucketSizeLog"_s)), jsNumber(ZSTD_c_ldmBucketSizeLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_ldmHashRateLog"_s)), jsNumber(ZSTD_c_ldmHashRateLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_contentSizeFlag"_s)), jsNumber(ZSTD_c_contentSizeFlag)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_checksumFlag"_s)), jsNumber(ZSTD_c_checksumFlag)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_dictIDFlag"_s)), jsNumber(ZSTD_c_dictIDFlag)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_nbWorkers"_s)), jsNumber(ZSTD_c_nbWorkers)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_jobSize"_s)), jsNumber(ZSTD_c_jobSize)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_c_overlapLog"_s)), jsNumber(ZSTD_c_overlapLog)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_d_windowLogMax"_s)), jsNumber(ZSTD_d_windowLogMax)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_CLEVEL_DEFAULT"_s)), jsNumber(ZSTD_CLEVEL_DEFAULT)); + + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_no_error"_s)), jsNumber(ZSTD_error_no_error)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_GENERIC"_s)), jsNumber(ZSTD_error_GENERIC)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_prefix_unknown"_s)), jsNumber(ZSTD_error_prefix_unknown)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_version_unsupported"_s)), jsNumber(ZSTD_error_version_unsupported)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_frameParameter_unsupported"_s)), jsNumber(ZSTD_error_frameParameter_unsupported)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_frameParameter_windowTooLarge"_s)), jsNumber(ZSTD_error_frameParameter_windowTooLarge)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_corruption_detected"_s)), jsNumber(ZSTD_error_corruption_detected)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_checksum_wrong"_s)), jsNumber(ZSTD_error_checksum_wrong)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_literals_headerWrong"_s)), jsNumber(ZSTD_error_literals_headerWrong)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_dictionary_corrupted"_s)), jsNumber(ZSTD_error_dictionary_corrupted)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_dictionary_wrong"_s)), jsNumber(ZSTD_error_dictionary_wrong)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_dictionaryCreation_failed"_s)), jsNumber(ZSTD_error_dictionaryCreation_failed)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_parameter_unsupported"_s)), jsNumber(ZSTD_error_parameter_unsupported)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_parameter_combination_unsupported"_s)), jsNumber(ZSTD_error_parameter_combination_unsupported)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_parameter_outOfBound"_s)), jsNumber(ZSTD_error_parameter_outOfBound)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_tableLog_tooLarge"_s)), jsNumber(ZSTD_error_tableLog_tooLarge)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_maxSymbolValue_tooLarge"_s)), jsNumber(ZSTD_error_maxSymbolValue_tooLarge)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_maxSymbolValue_tooSmall"_s)), jsNumber(ZSTD_error_maxSymbolValue_tooSmall)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_stabilityCondition_notRespected"_s)), jsNumber(ZSTD_error_stabilityCondition_notRespected)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_stage_wrong"_s)), jsNumber(ZSTD_error_stage_wrong)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_init_missing"_s)), jsNumber(ZSTD_error_init_missing)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_memory_allocation"_s)), jsNumber(ZSTD_error_memory_allocation)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_workSpace_tooSmall"_s)), jsNumber(ZSTD_error_workSpace_tooSmall)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_dstSize_tooSmall"_s)), jsNumber(ZSTD_error_dstSize_tooSmall)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_srcSize_wrong"_s)), jsNumber(ZSTD_error_srcSize_wrong)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_dstBuffer_null"_s)), jsNumber(ZSTD_error_dstBuffer_null)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_noForwardProgress_destFull"_s)), jsNumber(ZSTD_error_noForwardProgress_destFull)); + object->putDirect(vm, PropertyName(Identifier::fromString(vm, "ZSTD_error_noForwardProgress_inputEmpty"_s)), jsNumber(ZSTD_error_noForwardProgress_inputEmpty)); + return object; } diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig index 09f3f0f708..2d2639aaab 100644 --- a/src/bun.js/bindings/generated_classes_list.zig +++ b/src/bun.js/bindings/generated_classes_list.zig @@ -86,4 +86,5 @@ pub const Classes = struct { pub const HTMLBundle = api.HTMLBundle; pub const RedisClient = api.Valkey; pub const BlockList = api.BlockList; + pub const NativeZstd = api.NativeZstd; }; diff --git a/src/bun.js/event_loop/Task.zig b/src/bun.js/event_loop/Task.zig index 7e0ae01373..c29a78de2a 100644 --- a/src/bun.js/event_loop/Task.zig +++ b/src/bun.js/event_loop/Task.zig @@ -42,6 +42,7 @@ pub const Task = TaggedPointerUnion(.{ NapiFinalizerTask, NativeBrotli, NativeZlib, + NativeZstd, Open, PollPendingModulesTask, PosixSignalTask, @@ -448,6 +449,10 @@ pub fn tickQueueWithCount(this: *EventLoop, virtual_machine: *VirtualMachine) u3 var any: *NativeBrotli = task.get(NativeBrotli).?; any.runFromJSThread(); }, + @field(Task.Tag, @typeName(NativeZstd)) => { + var any: *NativeZstd = task.get(NativeZstd).?; + any.runFromJSThread(); + }, @field(Task.Tag, @typeName(ProcessWaiterThreadTask)) => { bun.markPosixOnly(); var any: *ProcessWaiterThreadTask = task.get(ProcessWaiterThreadTask).?; @@ -558,6 +563,7 @@ const StatFS = AsyncFS.statfs; const Unlink = AsyncFS.unlink; const NativeZlib = JSC.API.NativeZlib; const NativeBrotli = JSC.API.NativeBrotli; +const NativeZstd = JSC.API.NativeZstd; const ShellGlobTask = shell.interpret.Interpreter.Expansion.ShellGlobTask; const ShellRmTask = shell.Interpreter.Builtin.Rm.ShellRmTask; diff --git a/src/bun.js/node/node_zlib_binding.zig b/src/bun.js/node/node_zlib_binding.zig index 9b5f02b76b..d5cfd08fe1 100644 --- a/src/bun.js/node/node_zlib_binding.zig +++ b/src/bun.js/node/node_zlib_binding.zig @@ -264,7 +264,7 @@ pub fn CompressionStream(comptime T: type) type { return false; } - fn emitError(this: *T, globalThis: *JSC.JSGlobalObject, this_value: JSC.JSValue, err_: Error) !void { + pub fn emitError(this: *T, globalThis: *JSC.JSGlobalObject, this_value: JSC.JSValue, err_: Error) !void { var msg_str = bun.String.createFormat("{s}", .{std.mem.sliceTo(err_.msg, 0) orelse ""}) catch bun.outOfMemory(); const msg_value = msg_str.transferToJS(globalThis); const err_value = JSC.jsNumber(err_.err); @@ -287,7 +287,7 @@ pub fn CompressionStream(comptime T: type) type { pub const NativeZlib = JSC.Codegen.JSNativeZlib.getConstructor; -const CountedKeepAlive = struct { +pub const CountedKeepAlive = struct { keep_alive: bun.Async.KeepAlive = .{}, ref_count: u32 = 0, @@ -428,7 +428,7 @@ pub const SNativeZlib = struct { } }; -const Error = struct { +pub const Error = struct { msg: ?[*:0]const u8, err: c_int, code: ?[*:0]const u8, @@ -471,6 +471,7 @@ const ZlibContext = struct { .UNZIP => windowBits + 32, .DEFLATERAW, .INFLATERAW => windowBits * -1, .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, }; this.dictionary = dictionary orelse ""; @@ -479,8 +480,8 @@ const ZlibContext = struct { .NONE => unreachable, .DEFLATE, .GZIP, .DEFLATERAW => this.err = c.deflateInit2_(&this.state, level, 8, windowBitsActual, memLevel, strategy, c.zlibVersion(), @sizeOf(c.z_stream)), .INFLATE, .GUNZIP, .UNZIP, .INFLATERAW => this.err = c.inflateInit2_(&this.state, windowBitsActual, c.zlibVersion(), @sizeOf(c.z_stream)), - .BROTLI_DECODE => @panic("TODO"), - .BROTLI_ENCODE => @panic("TODO"), + .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, } if (this.err != .Ok) { this.mode = .NONE; @@ -619,6 +620,7 @@ const ZlibContext = struct { }, .NONE => {}, .BROTLI_ENCODE, .BROTLI_DECODE => {}, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, } } @@ -684,6 +686,7 @@ const ZlibContext = struct { }, .NONE => {}, .BROTLI_ENCODE, .BROTLI_DECODE => {}, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, } bun.assert(status == .Ok or status == .DataError); this.mode = .NONE; @@ -722,9 +725,7 @@ pub const SNativeBrotli = struct { write_in_progress: bool = false, pending_close: bool = false, closed: bool = false, - task: JSC.WorkPoolTask = .{ - .callback = undefined, - }, + task: JSC.WorkPoolTask = .{ .callback = undefined }, pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { const arguments = callframe.argumentsUndef(1).ptr; @@ -960,3 +961,5 @@ const BrotliContext = struct { unreachable; } }; + +pub const NativeZstd = JSC.Codegen.JSNativeZstd.getConstructor; diff --git a/src/bun.js/node/zlib/NativeZstd.zig b/src/bun.js/node/zlib/NativeZstd.zig new file mode 100644 index 0000000000..f95ced09b5 --- /dev/null +++ b/src/bun.js/node/zlib/NativeZstd.zig @@ -0,0 +1,266 @@ +const std = @import("std"); +const bun = @import("bun"); +const JSC = bun.JSC; +const CompressionStream = @import("./../node_zlib_binding.zig").CompressionStream; +const CountedKeepAlive = @import("./../node_zlib_binding.zig").CountedKeepAlive; +const Error = @import("./../node_zlib_binding.zig").Error; +const validators = @import("./../util/validators.zig"); + +const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinit, .{}); +pub const ref = RefCount.ref; +pub const deref = RefCount.deref; + +pub const js = JSC.Codegen.JSNativeZstd; +pub const toJS = js.toJS; +pub const fromJS = js.fromJS; +pub const fromJSDirect = js.fromJSDirect; + +const impl = CompressionStream(@This()); +pub const write = impl.write; +pub const runFromJSThread = impl.runFromJSThread; +pub const writeSync = impl.writeSync; +pub const reset = impl.reset; +pub const close = impl.close; +pub const setOnError = impl.setOnError; +pub const getOnError = impl.getOnError; +pub const finalize = impl.finalize; + +ref_count: RefCount, +mode: bun.zlib.NodeMode, +globalThis: *JSC.JSGlobalObject, +stream: Context = .{}, +write_result: ?[*]u32 = null, +poll_ref: CountedKeepAlive = .{}, +this_value: JSC.Strong.Optional = .empty, +write_in_progress: bool = false, +pending_close: bool = false, +closed: bool = false, +task: JSC.WorkPoolTask = .{ .callback = undefined }, + +pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { + const arguments = callframe.argumentsAsArray(1); + + var mode = arguments[0]; + if (!mode.isNumber()) { + return globalThis.throwInvalidArgumentTypeValue("mode", "number", mode); + } + const mode_double = mode.asNumber(); + if (@mod(mode_double, 1.0) != 0.0) { + return globalThis.throwInvalidArgumentTypeValue("mode", "integer", mode); + } + const mode_int: i64 = @intFromFloat(mode_double); + if (mode_int < 10 or mode_int > 11) { + return globalThis.throwRangeError(mode_int, .{ .field_name = "mode", .min = 10, .max = 11 }); + } + + const ptr = bun.new(@This(), .{ + .ref_count = .init(), + .mode = @enumFromInt(mode_int), + .globalThis = globalThis, + }); + ptr.stream.mode = ptr.mode; + ptr.stream.mode_ = ptr.mode; + return ptr; +} + +pub fn estimatedSize(this: *const @This()) usize { + return @sizeOf(@This()) + switch (this.stream.mode_) { + .ZSTD_COMPRESS => bun.c.ZSTD_sizeof_CCtx(@ptrCast(this.stream.state)), + .ZSTD_DECOMPRESS => bun.c.ZSTD_sizeof_DCtx(@ptrCast(this.stream.state)), + else => 0, + }; +} + +pub fn init(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const arguments = callframe.argumentsAsArray(4); + const this_value = callframe.this(); + if (callframe.argumentsCount() != 4) return globalThis.ERR(.MISSING_ARGS, "init(initParamsArray, pledgedSrcSize, writeState, processCallback)", .{}).throw(); + + const initParamsArray_value = arguments[0]; + const pledgedSrcSize_value = arguments[1]; + const writeState_value = arguments[2]; + const processCallback_value = arguments[3]; + + const writeState = writeState_value.asArrayBuffer(globalThis) orelse return globalThis.throwInvalidArgumentTypeValue("writeState", "Uint32Array", writeState_value); + if (writeState.typed_array_type != .Uint32Array) return globalThis.throwInvalidArgumentTypeValue("writeState", "Uint32Array", writeState_value); + this.write_result = writeState.asU32().ptr; + + const write_js_callback = try validators.validateFunction(globalThis, "processCallback", processCallback_value); + js.writeCallbackSetCached(this_value, globalThis, write_js_callback); + + var pledged_src_size: u64 = std.math.maxInt(u64); + if (pledgedSrcSize_value.isNumber()) { + pledged_src_size = try validators.validateUint32(globalThis, pledgedSrcSize_value, "pledgedSrcSize", .{}, false); + } + + var err = this.stream.init(pledged_src_size); + if (err.isError()) { + try impl.emitError(this, globalThis, this_value, err); + return .jsBoolean(false); + } + + const params_ = initParamsArray_value.asArrayBuffer(globalThis) orelse return globalThis.throwInvalidArgumentTypeValue("initParamsArray", "Uint32Array", initParamsArray_value); + if (params_.typed_array_type != .Uint32Array) return globalThis.throwInvalidArgumentTypeValue("initParamsArray", "Uint32Array", initParamsArray_value); + for (params_.asU32(), 0..) |x, i| { + if (x == std.math.maxInt(u32)) continue; + const err_ = this.stream.setParams(@intCast(i), x); + if (err_.isError()) return globalThis.ERR(.ZLIB_INITIALIZATION_FAILED, "{s}", .{std.mem.sliceTo(err_.msg.?, 0)}).throw(); + } + + return .jsBoolean(true); +} + +pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + // intentionally left empty + return .undefined; +} + +fn deinit(this: *@This()) void { + this.poll_ref.deinit(); + switch (this.stream.mode) { + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => this.stream.close(), + else => {}, + } + bun.destroy(this); +} + +const Context = struct { + const c = bun.c; + + mode: bun.zlib.NodeMode = .NONE, + mode_: bun.zlib.NodeMode = .NONE, + state: ?*anyopaque = null, + flush: c_int = c.ZSTD_e_continue, + input: c.ZSTD_inBuffer = .{ .src = null, .size = 0, .pos = 0 }, + output: c.ZSTD_outBuffer = .{ .dst = null, .size = 0, .pos = 0 }, + pledged_src_size: u64 = std.math.maxInt(u64), + remaining: u64 = 0, + + pub fn init(this: *Context, pledged_src_size: u64) Error { + switch (this.mode_) { + .ZSTD_COMPRESS => { + this.pledged_src_size = pledged_src_size; + const state = c.ZSTD_createCCtx(); + if (state == null) return .init("Could not initialize zstd instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); + this.state = state.?; + const result = c.ZSTD_CCtx_setPledgedSrcSize(state, pledged_src_size); + if (c.ZSTD_isError(result) > 0) return .init("Could not set pledged src size", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); + return .ok; + }, + .ZSTD_DECOMPRESS => { + const state = c.ZSTD_createDCtx(); + if (state == null) return .init("Could not initialize zstd instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); + this.state = state.?; + return .ok; + }, + else => @panic("unreachable"), + } + } + + pub fn setParams(this: *Context, key: c_uint, value: u32) Error { + switch (this.mode_) { + .ZSTD_COMPRESS => { + const result = c.ZSTD_CCtx_setParameter(@ptrCast(this.state), key, @bitCast(value)); + if (c.ZSTD_isError(result) > 0) return .init("Setting parameter failed", -1, "ERR_ZSTD_PARAM_SET_FAILED"); + return .ok; + }, + .ZSTD_DECOMPRESS => { + const result = c.ZSTD_DCtx_setParameter(@ptrCast(this.state), key, @bitCast(value)); + if (c.ZSTD_isError(result) > 0) return .init("Setting parameter failed", -1, "ERR_ZSTD_PARAM_SET_FAILED"); + return .ok; + }, + else => @panic("unreachable"), + } + } + + pub fn reset(this: *Context) Error { + return this.init(this.pledged_src_size); + } + + pub fn setBuffers(this: *Context, in: ?[]const u8, out: ?[]u8) void { + this.input.src = if (in) |p| p.ptr else null; + this.input.size = if (in) |p| p.len else 0; + this.input.pos = 0; + this.output.dst = if (out) |p| p.ptr else null; + this.output.size = if (out) |p| p.len else 0; + this.output.pos = 0; + } + + pub fn setFlush(this: *Context, flush: c_int) void { + this.flush = flush; + } + + pub fn doWork(this: *Context) void { + this.remaining = switch (this.mode_) { + .ZSTD_COMPRESS => c.ZSTD_compressStream2(@ptrCast(this.state), &this.output, &this.input, @intCast(this.flush)), + .ZSTD_DECOMPRESS => c.ZSTD_decompressStream(@ptrCast(this.state), &this.output, &this.input), + else => @panic("unreachable"), + }; + } + + pub fn updateWriteResult(this: *Context, avail_in: *u32, avail_out: *u32) void { + avail_in.* = @intCast(this.input.size - this.input.pos); + avail_out.* = @intCast(this.output.size - this.output.pos); + } + + pub fn getErrorInfo(this: *Context) Error { + defer this.remaining = 0; + const err = c.ZSTD_getErrorCode(this.remaining); + if (err == 0) { + return .ok; + } + return Error{ + .err = @intCast(err), + .msg = c.ZSTD_getErrorString(err), + .code = switch (err) { + c.ZSTD_error_no_error => "ZSTD_error_no_error", + c.ZSTD_error_GENERIC => "ZSTD_error_GENERIC", + c.ZSTD_error_prefix_unknown => "ZSTD_error_prefix_unknown", + c.ZSTD_error_version_unsupported => "ZSTD_error_version_unsupported", + c.ZSTD_error_frameParameter_unsupported => "ZSTD_error_frameParameter_unsupported", + c.ZSTD_error_frameParameter_windowTooLarge => "ZSTD_error_frameParameter_windowTooLarge", + c.ZSTD_error_corruption_detected => "ZSTD_error_corruption_detected", + c.ZSTD_error_checksum_wrong => "ZSTD_error_checksum_wrong", + c.ZSTD_error_literals_headerWrong => "ZSTD_error_literals_headerWrong", + c.ZSTD_error_dictionary_corrupted => "ZSTD_error_dictionary_corrupted", + c.ZSTD_error_dictionary_wrong => "ZSTD_error_dictionary_wrong", + c.ZSTD_error_dictionaryCreation_failed => "ZSTD_error_dictionaryCreation_failed", + c.ZSTD_error_parameter_unsupported => "ZSTD_error_parameter_unsupported", + c.ZSTD_error_parameter_combination_unsupported => "ZSTD_error_parameter_combination_unsupported", + c.ZSTD_error_parameter_outOfBound => "ZSTD_error_parameter_outOfBound", + c.ZSTD_error_tableLog_tooLarge => "ZSTD_error_tableLog_tooLarge", + c.ZSTD_error_maxSymbolValue_tooLarge => "ZSTD_error_maxSymbolValue_tooLarge", + c.ZSTD_error_maxSymbolValue_tooSmall => "ZSTD_error_maxSymbolValue_tooSmall", + c.ZSTD_error_stabilityCondition_notRespected => "ZSTD_error_stabilityCondition_notRespected", + c.ZSTD_error_stage_wrong => "ZSTD_error_stage_wrong", + c.ZSTD_error_init_missing => "ZSTD_error_init_missing", + c.ZSTD_error_memory_allocation => "ZSTD_error_memory_allocation", + c.ZSTD_error_workSpace_tooSmall => "ZSTD_error_workSpace_tooSmall", + c.ZSTD_error_dstSize_tooSmall => "ZSTD_error_dstSize_tooSmall", + c.ZSTD_error_srcSize_wrong => "ZSTD_error_srcSize_wrong", + c.ZSTD_error_dstBuffer_null => "ZSTD_error_dstBuffer_null", + c.ZSTD_error_noForwardProgress_destFull => "ZSTD_error_noForwardProgress_destFull", + c.ZSTD_error_noForwardProgress_inputEmpty => "ZSTD_error_noForwardProgress_inputEmpty", + else => "ZSTD_error_GENERIC", + }, + }; + } + + pub fn close(this: *Context) void { + _ = switch (this.mode_) { + .ZSTD_COMPRESS => c.ZSTD_CCtx_reset(@ptrCast(this.state), c.ZSTD_reset_session_and_parameters), + .ZSTD_DECOMPRESS => c.ZSTD_DCtx_reset(@ptrCast(this.state), c.ZSTD_reset_session_and_parameters), + else => unreachable, + }; + _ = switch (this.mode_) { + .ZSTD_COMPRESS => c.ZSTD_freeCCtx(@ptrCast(this.state)), + .ZSTD_DECOMPRESS => c.ZSTD_freeDCtx(@ptrCast(this.state)), + else => unreachable, + }; + this.mode = .NONE; + this.state = null; + } +}; diff --git a/src/c-headers-for-zig.h b/src/c-headers-for-zig.h index 90e2998161..daf0ead276 100644 --- a/src/c-headers-for-zig.h +++ b/src/c-headers-for-zig.h @@ -61,11 +61,9 @@ #include #endif -#if WINDOWS -#include -#include -#endif - #undef lstat #undef fstat #undef stat + +#include +#include diff --git a/src/deps/zstd.zig b/src/deps/zstd.zig index a708342bcc..3ef2e6c65d 100644 --- a/src/deps/zstd.zig +++ b/src/deps/zstd.zig @@ -1,186 +1,6 @@ const std = @import("std"); const bun = @import("bun"); -pub extern fn ZSTD_versionNumber() c_uint; -pub extern fn ZSTD_versionString() [*c]const u8; -pub extern fn ZSTD_compress(dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, compressionLevel: c_int) usize; -pub extern fn ZSTD_decompress(dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, compressedSize: usize) usize; -pub extern fn ZSTD_getFrameContentSize(src: ?*const anyopaque, srcSize: usize) c_ulonglong; -pub extern fn ZSTD_getDecompressedSize(src: ?*const anyopaque, srcSize: usize) c_ulonglong; -pub extern fn ZSTD_findFrameCompressedSize(src: ?*const anyopaque, srcSize: usize) usize; -pub extern fn ZSTD_compressBound(srcSize: usize) usize; -pub extern fn ZSTD_isError(code: usize) c_uint; -pub extern fn ZSTD_getErrorName(code: usize) [*:0]const u8; -pub extern fn ZSTD_minCLevel() c_int; -pub extern fn ZSTD_maxCLevel() c_int; -pub extern fn ZSTD_defaultCLevel() c_int; -pub const struct_ZSTD_CCtx_s = opaque {}; -pub const ZSTD_CCtx = struct_ZSTD_CCtx_s; -pub extern fn ZSTD_createCCtx() ?*ZSTD_CCtx; -pub extern fn ZSTD_freeCCtx(cctx: ?*ZSTD_CCtx) usize; -pub extern fn ZSTD_compressCCtx(cctx: ?*ZSTD_CCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, compressionLevel: c_int) usize; -pub const struct_ZSTD_DCtx_s = opaque {}; -pub const ZSTD_DCtx = struct_ZSTD_DCtx_s; -pub extern fn ZSTD_createDCtx() ?*ZSTD_DCtx; -pub extern fn ZSTD_freeDCtx(dctx: ?*ZSTD_DCtx) usize; -pub extern fn ZSTD_decompressDCtx(dctx: ?*ZSTD_DCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize) usize; -pub const ZSTD_fast: c_int = 1; -pub const ZSTD_dfast: c_int = 2; -pub const ZSTD_greedy: c_int = 3; -pub const ZSTD_lazy: c_int = 4; -pub const ZSTD_lazy2: c_int = 5; -pub const ZSTD_btlazy2: c_int = 6; -pub const ZSTD_btopt: c_int = 7; -pub const ZSTD_btultra: c_int = 8; -pub const ZSTD_btultra2: c_int = 9; -pub const ZSTD_strategy = c_uint; -pub const ZSTD_c_compressionLevel: c_int = 100; -pub const ZSTD_c_windowLog: c_int = 101; -pub const ZSTD_c_hashLog: c_int = 102; -pub const ZSTD_c_chainLog: c_int = 103; -pub const ZSTD_c_searchLog: c_int = 104; -pub const ZSTD_c_minMatch: c_int = 105; -pub const ZSTD_c_targetLength: c_int = 106; -pub const ZSTD_c_strategy: c_int = 107; -pub const ZSTD_c_enableLongDistanceMatching: c_int = 160; -pub const ZSTD_c_ldmHashLog: c_int = 161; -pub const ZSTD_c_ldmMinMatch: c_int = 162; -pub const ZSTD_c_ldmBucketSizeLog: c_int = 163; -pub const ZSTD_c_ldmHashRateLog: c_int = 164; -pub const ZSTD_c_contentSizeFlag: c_int = 200; -pub const ZSTD_c_checksumFlag: c_int = 201; -pub const ZSTD_c_dictIDFlag: c_int = 202; -pub const ZSTD_c_nbWorkers: c_int = 400; -pub const ZSTD_c_jobSize: c_int = 401; -pub const ZSTD_c_overlapLog: c_int = 402; -pub const ZSTD_c_experimentalParam1: c_int = 500; -pub const ZSTD_c_experimentalParam2: c_int = 10; -pub const ZSTD_c_experimentalParam3: c_int = 1000; -pub const ZSTD_c_experimentalParam4: c_int = 1001; -pub const ZSTD_c_experimentalParam5: c_int = 1002; -pub const ZSTD_c_experimentalParam6: c_int = 1003; -pub const ZSTD_c_experimentalParam7: c_int = 1004; -pub const ZSTD_c_experimentalParam8: c_int = 1005; -pub const ZSTD_c_experimentalParam9: c_int = 1006; -pub const ZSTD_c_experimentalParam10: c_int = 1007; -pub const ZSTD_c_experimentalParam11: c_int = 1008; -pub const ZSTD_c_experimentalParam12: c_int = 1009; -pub const ZSTD_c_experimentalParam13: c_int = 1010; -pub const ZSTD_c_experimentalParam14: c_int = 1011; -pub const ZSTD_c_experimentalParam15: c_int = 1012; -pub const ZSTD_c_experimentalParam16: c_int = 1013; -pub const ZSTD_c_experimentalParam17: c_int = 1014; -pub const ZSTD_c_experimentalParam18: c_int = 1015; -pub const ZSTD_c_experimentalParam19: c_int = 1016; -pub const ZSTD_cParameter = c_uint; -pub const ZSTD_bounds = extern struct { - @"error": usize, - lowerBound: c_int, - upperBound: c_int, -}; -pub extern fn ZSTD_cParam_getBounds(cParam: ZSTD_cParameter) ZSTD_bounds; -pub extern fn ZSTD_CCtx_setParameter(cctx: ?*ZSTD_CCtx, param: ZSTD_cParameter, value: c_int) usize; -pub extern fn ZSTD_CCtx_setPledgedSrcSize(cctx: ?*ZSTD_CCtx, pledgedSrcSize: c_ulonglong) usize; -pub const ZSTD_reset_session_only: c_int = 1; -pub const ZSTD_reset_parameters: c_int = 2; -pub const ZSTD_reset_session_and_parameters: c_int = 3; -pub const ZSTD_ResetDirective = c_uint; -pub extern fn ZSTD_CCtx_reset(cctx: ?*ZSTD_CCtx, reset: ZSTD_ResetDirective) usize; -pub extern fn ZSTD_compress2(cctx: ?*ZSTD_CCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize) usize; -pub const ZSTD_d_windowLogMax: c_int = 100; -pub const ZSTD_d_experimentalParam1: c_int = 1000; -pub const ZSTD_d_experimentalParam2: c_int = 1001; -pub const ZSTD_d_experimentalParam3: c_int = 1002; -pub const ZSTD_d_experimentalParam4: c_int = 1003; -pub const ZSTD_d_experimentalParam5: c_int = 1004; -pub const ZSTD_dParameter = c_uint; -pub extern fn ZSTD_dParam_getBounds(dParam: ZSTD_dParameter) ZSTD_bounds; -pub extern fn ZSTD_DCtx_setParameter(dctx: ?*ZSTD_DCtx, param: ZSTD_dParameter, value: c_int) usize; -pub extern fn ZSTD_DCtx_reset(dctx: ?*ZSTD_DCtx, reset: ZSTD_ResetDirective) usize; -pub const struct_ZSTD_inBuffer_s = extern struct { - src: ?*const anyopaque, - size: usize, - pos: usize, -}; -pub const ZSTD_inBuffer = struct_ZSTD_inBuffer_s; -pub const struct_ZSTD_outBuffer_s = extern struct { - dst: ?*anyopaque, - size: usize, - pos: usize, -}; -pub const ZSTD_outBuffer = struct_ZSTD_outBuffer_s; -pub const ZSTD_CStream = ZSTD_CCtx; -pub extern fn ZSTD_createCStream() ?*ZSTD_CStream; -pub extern fn ZSTD_freeCStream(zcs: ?*ZSTD_CStream) usize; -pub const ZSTD_e_continue: c_int = 0; -pub const ZSTD_e_flush: c_int = 1; -pub const ZSTD_e_end: c_int = 2; -pub const ZSTD_EndDirective = c_uint; -pub extern fn ZSTD_compressStream2(cctx: ?*ZSTD_CCtx, output: [*c]ZSTD_outBuffer, input: [*c]ZSTD_inBuffer, endOp: ZSTD_EndDirective) usize; -pub extern fn ZSTD_CStreamInSize() usize; -pub extern fn ZSTD_CStreamOutSize() usize; -pub extern fn ZSTD_initCStream(zcs: ?*ZSTD_CStream, compressionLevel: c_int) usize; -pub extern fn ZSTD_compressStream(zcs: ?*ZSTD_CStream, output: [*c]ZSTD_outBuffer, input: [*c]ZSTD_inBuffer) usize; -pub extern fn ZSTD_flushStream(zcs: ?*ZSTD_CStream, output: [*c]ZSTD_outBuffer) usize; -pub extern fn ZSTD_endStream(zcs: ?*ZSTD_CStream, output: [*c]ZSTD_outBuffer) usize; -pub const ZSTD_DStream = ZSTD_DCtx; -pub extern fn ZSTD_createDStream() ?*ZSTD_DStream; -pub extern fn ZSTD_freeDStream(zds: ?*ZSTD_DStream) usize; -pub extern fn ZSTD_initDStream(zds: ?*ZSTD_DStream) usize; -pub extern fn ZSTD_decompressStream(zds: ?*ZSTD_DStream, output: [*c]ZSTD_outBuffer, input: [*c]ZSTD_inBuffer) usize; -pub extern fn ZSTD_DStreamInSize() usize; -pub extern fn ZSTD_DStreamOutSize() usize; -pub extern fn ZSTD_compress_usingDict(ctx: ?*ZSTD_CCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, dict: ?*const anyopaque, dictSize: usize, compressionLevel: c_int) usize; -pub extern fn ZSTD_decompress_usingDict(dctx: ?*ZSTD_DCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, dict: ?*const anyopaque, dictSize: usize) usize; -pub const struct_ZSTD_CDict_s = opaque {}; -pub const ZSTD_CDict = struct_ZSTD_CDict_s; -pub extern fn ZSTD_createCDict(dictBuffer: ?*const anyopaque, dictSize: usize, compressionLevel: c_int) ?*ZSTD_CDict; -pub extern fn ZSTD_freeCDict(CDict: ?*ZSTD_CDict) usize; -pub extern fn ZSTD_compress_usingCDict(cctx: ?*ZSTD_CCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, cdict: ?*const ZSTD_CDict) usize; -pub const struct_ZSTD_DDict_s = opaque {}; -pub const ZSTD_DDict = struct_ZSTD_DDict_s; -pub extern fn ZSTD_createDDict(dictBuffer: ?*const anyopaque, dictSize: usize) ?*ZSTD_DDict; -pub extern fn ZSTD_freeDDict(ddict: ?*ZSTD_DDict) usize; -pub extern fn ZSTD_decompress_usingDDict(dctx: ?*ZSTD_DCtx, dst: ?*anyopaque, dstCapacity: usize, src: ?*const anyopaque, srcSize: usize, ddict: ?*const ZSTD_DDict) usize; -pub extern fn ZSTD_getDictID_fromDict(dict: ?*const anyopaque, dictSize: usize) c_uint; -pub extern fn ZSTD_getDictID_fromCDict(cdict: ?*const ZSTD_CDict) c_uint; -pub extern fn ZSTD_getDictID_fromDDict(ddict: ?*const ZSTD_DDict) c_uint; -pub extern fn ZSTD_getDictID_fromFrame(src: ?*const anyopaque, srcSize: usize) c_uint; -pub extern fn ZSTD_CCtx_loadDictionary(cctx: ?*ZSTD_CCtx, dict: ?*const anyopaque, dictSize: usize) usize; -pub extern fn ZSTD_CCtx_refCDict(cctx: ?*ZSTD_CCtx, cdict: ?*const ZSTD_CDict) usize; -pub extern fn ZSTD_CCtx_refPrefix(cctx: ?*ZSTD_CCtx, prefix: ?*const anyopaque, prefixSize: usize) usize; -pub extern fn ZSTD_DCtx_loadDictionary(dctx: ?*ZSTD_DCtx, dict: ?*const anyopaque, dictSize: usize) usize; -pub extern fn ZSTD_DCtx_refDDict(dctx: ?*ZSTD_DCtx, ddict: ?*const ZSTD_DDict) usize; -pub extern fn ZSTD_DCtx_refPrefix(dctx: ?*ZSTD_DCtx, prefix: ?*const anyopaque, prefixSize: usize) usize; -pub extern fn ZSTD_sizeof_CCtx(cctx: ?*const ZSTD_CCtx) usize; -pub extern fn ZSTD_sizeof_DCtx(dctx: ?*const ZSTD_DCtx) usize; -pub extern fn ZSTD_sizeof_CStream(zcs: ?*const ZSTD_CStream) usize; -pub extern fn ZSTD_sizeof_DStream(zds: ?*const ZSTD_DStream) usize; -pub extern fn ZSTD_sizeof_CDict(cdict: ?*const ZSTD_CDict) usize; -pub extern fn ZSTD_sizeof_DDict(ddict: ?*const ZSTD_DDict) usize; -pub const ZSTD_VERSION_MAJOR = @as(c_int, 1); -pub const ZSTD_VERSION_MINOR = @as(c_int, 5); -pub const ZSTD_VERSION_RELEASE = @as(c_int, 5); -pub const ZSTD_VERSION_NUMBER = (((ZSTD_VERSION_MAJOR * @as(c_int, 100)) * @as(c_int, 100)) + (ZSTD_VERSION_MINOR * @as(c_int, 100))) + ZSTD_VERSION_RELEASE; -pub const ZSTD_LIB_VERSION = ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE; -pub const ZSTD_CLEVEL_DEFAULT = @as(c_int, 3); -pub const ZSTD_MAGICNUMBER = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xFD2FB528, .hex); -pub const ZSTD_MAGIC_DICTIONARY = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xEC30A437, .hex); -pub const ZSTD_MAGIC_SKIPPABLE_START = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x184D2A50, .hex); -pub const ZSTD_MAGIC_SKIPPABLE_MASK = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xFFFFFFF0, .hex); -pub const ZSTD_BLOCKSIZELOG_MAX = @as(c_int, 17); -pub const ZSTD_BLOCKSIZE_MAX = @as(c_int, 1) << ZSTD_BLOCKSIZELOG_MAX; -pub const ZSTD_CONTENTSIZE_UNKNOWN = @as(c_ulonglong, 0) - @as(c_int, 1); -pub const ZSTD_CONTENTSIZE_ERROR = @as(c_ulonglong, 0) - @as(c_int, 2); -pub const ZSTD_MAX_INPUT_SIZE = if (@import("std").zig.c_translation.sizeof(usize) == @as(c_int, 8)) @as(c_ulonglong, 0xFF00FF00FF00FF00) else @import("std").zig.c_translation.promoteIntLiteral(c_uint, 0xFF00FF00, .hex); -pub inline fn ZSTD_COMPRESSBOUND(srcSize: anytype) @TypeOf(if (@import("std").zig.c_translation.cast(usize, srcSize) >= ZSTD_MAX_INPUT_SIZE) @as(c_int, 0) else (srcSize + (srcSize >> @as(c_int, 8))) + (if (srcSize < (@as(c_int, 128) << @as(c_int, 10))) ((@as(c_int, 128) << @as(c_int, 10)) - srcSize) >> @as(c_int, 11) else @as(c_int, 0))) { - return if (@import("std").zig.c_translation.cast(usize, srcSize) >= ZSTD_MAX_INPUT_SIZE) @as(c_int, 0) else (srcSize + (srcSize >> @as(c_int, 8))) + (if (srcSize < (@as(c_int, 128) << @as(c_int, 10))) ((@as(c_int, 128) << @as(c_int, 10)) - srcSize) >> @as(c_int, 11) else @as(c_int, 0)); -} -pub const ZSTD_CCtx_s = struct_ZSTD_CCtx_s; -pub const ZSTD_DCtx_s = struct_ZSTD_DCtx_s; -pub const ZSTD_inBuffer_s = struct_ZSTD_inBuffer_s; -pub const ZSTD_outBuffer_s = struct_ZSTD_outBuffer_s; -pub const ZSTD_CDict_s = struct_ZSTD_CDict_s; -pub const ZSTD_DDict_s = struct_ZSTD_DDict_s; +const c = bun.c; // ----------------------------------- @@ -194,13 +14,13 @@ pub const ZSTD_DDict_s = struct_ZSTD_DDict_s; // const void* src, size_t srcSize, // int compressionLevel); pub fn compress(dest: []u8, src: []const u8, level: ?i32) Result { - const result = ZSTD_compress(dest.ptr, dest.len, src.ptr, src.len, level orelse ZSTD_defaultCLevel()); - if (ZSTD_isError(result) != 0) return .{ .err = bun.sliceTo(ZSTD_getErrorName(result), 0) }; + const result = c.ZSTD_compress(dest.ptr, dest.len, src.ptr, src.len, level orelse c.ZSTD_defaultCLevel()); + if (c.ZSTD_isError(result) != 0) return .{ .err = bun.sliceTo(c.ZSTD_getErrorName(result), 0) }; return .{ .success = result }; } pub fn compressBound(srcSize: usize) usize { - return ZSTD_compressBound(srcSize); + return c.ZSTD_compressBound(srcSize); } /// ZSTD_decompress() : @@ -212,8 +32,8 @@ pub fn compressBound(srcSize: usize) usize { // ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, // const void* src, size_t compressedSize); pub fn decompress(dest: []u8, src: []const u8) Result { - const result = ZSTD_decompress(dest.ptr, dest.len, src.ptr, src.len); - if (ZSTD_isError(result) != 0) return .{ .err = bun.sliceTo(ZSTD_getErrorName(result), 0) }; + const result = c.ZSTD_decompress(dest.ptr, dest.len, src.ptr, src.len); + if (c.ZSTD_isError(result) != 0) return .{ .err = bun.sliceTo(c.ZSTD_getErrorName(result), 0) }; return .{ .success = result }; } @@ -262,7 +82,7 @@ pub const ZstdReaderArrayList = struct { list_allocator: std.mem.Allocator, list_ptr: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, - zstd: *ZSTD_DStream, + zstd: *c.ZSTD_DStream, state: State = State.Uninitialized, total_out: usize = 0, total_in: usize = 0, @@ -293,17 +113,17 @@ pub const ZstdReaderArrayList = struct { .zstd = undefined, }; - reader.zstd = ZSTD_createDStream() orelse { + reader.zstd = c.ZSTD_createDStream() orelse { allocator.destroy(reader); return error.ZstdFailedToCreateInstance; }; - _ = ZSTD_initDStream(reader.zstd); + _ = c.ZSTD_initDStream(reader.zstd); return reader; } pub fn end(this: *ZstdReaderArrayList) void { if (this.state != .End) { - _ = ZSTD_freeDStream(this.zstd); + _ = c.ZSTD_freeDStream(this.zstd); this.state = .End; } } @@ -327,19 +147,19 @@ pub const ZstdReaderArrayList = struct { } const next_in = this.input[this.total_in..]; - var in_buf = ZSTD_inBuffer{ + var in_buf: c.ZSTD_inBuffer = .{ .src = if (next_in.len > 0) next_in.ptr else null, .size = next_in.len, .pos = 0, }; - var out_buf = ZSTD_outBuffer{ + var out_buf: c.ZSTD_outBuffer = .{ .dst = if (unused.len > 0) unused.ptr else null, .size = unused.len, .pos = 0, }; - const rc = ZSTD_decompressStream(this.zstd, &out_buf, &in_buf); - if (ZSTD_isError(rc) != 0) { + const rc = c.ZSTD_decompressStream(this.zstd, &out_buf, &in_buf); + if (c.ZSTD_isError(rc) != 0) { this.state = .Error; return error.ZstdDecompressionError; } diff --git a/src/js/node/zlib.ts b/src/js/node/zlib.ts index 9b966dbce2..57902037a0 100644 --- a/src/js/node/zlib.ts +++ b/src/js/node/zlib.ts @@ -6,6 +6,7 @@ const BufferModule = require("node:buffer"); const crc32 = $newZigFunction("node_zlib_binding.zig", "crc32", 1); const NativeZlib = $zig("node_zlib_binding.zig", "NativeZlib"); const NativeBrotli = $zig("node_zlib_binding.zig", "NativeBrotli"); +const NativeZstd = $zig("node_zlib_binding.zig", "NativeZstd"); const ObjectKeys = Object.keys; const ArrayPrototypePush = Array.prototype.push; @@ -15,6 +16,7 @@ const ObjectFreeze = Object.freeze; const TypedArrayPrototypeFill = Uint8Array.prototype.fill; const ArrayPrototypeForEach = Array.prototype.forEach; const NumberIsNaN = Number.isNaN; +const MathMax = Math.max; const ArrayBufferIsView = ArrayBuffer.isView; const isArrayBufferView = ArrayBufferIsView; @@ -37,9 +39,11 @@ const { Z_MIN_CHUNK, Z_MIN_WINDOWBITS, Z_MAX_WINDOWBITS, Z_MIN_LEVEL, Z_MAX_LEVEL, Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_CHUNK, Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY, Z_DEFAULT_WINDOWBITS, Z_DEFAULT_MEMLEVEL, Z_FIXED, // Node's compression stream modes (node_zlib_mode) - DEFLATE, DEFLATERAW, INFLATE, INFLATERAW, GZIP, GUNZIP, UNZIP, BROTLI_DECODE, BROTLI_ENCODE, + DEFLATE, DEFLATERAW, INFLATE, INFLATERAW, GZIP, GUNZIP, UNZIP, BROTLI_DECODE, BROTLI_ENCODE, ZSTD_COMPRESS, ZSTD_DECOMPRESS, // Brotli operations (~flush levels) BROTLI_OPERATION_PROCESS, BROTLI_OPERATION_FLUSH, BROTLI_OPERATION_FINISH, BROTLI_OPERATION_EMIT_METADATA, + // Zstd end directives (~flush levels) + ZSTD_e_continue, ZSTD_e_flush, ZSTD_e_end, } = constants; // Translation table for return codes. @@ -132,9 +136,11 @@ function zlibOnError(message, errno, code) { const FLUSH_BOUND = [ [Z_NO_FLUSH, Z_BLOCK], [BROTLI_OPERATION_PROCESS, BROTLI_OPERATION_EMIT_METADATA], + [ZSTD_e_continue, ZSTD_e_end], ]; const FLUSH_BOUND_IDX_NORMAL = 0; const FLUSH_BOUND_IDX_BROTLI = 1; +const FLUSH_BOUND_IDX_ZSTD = 2; // The base class for all Zlib-style streams. function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { @@ -142,13 +148,15 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { let maxOutputLength = kMaxLength; // The ZlibBase class is not exported to user land, the mode should only be passed in by us. assert(typeof mode === "number"); - assert(mode >= DEFLATE && mode <= BROTLI_ENCODE); + assert(mode >= DEFLATE && mode <= ZSTD_DECOMPRESS); let flushBoundIdx; - if (mode !== BROTLI_ENCODE && mode !== BROTLI_DECODE) { - flushBoundIdx = FLUSH_BOUND_IDX_NORMAL; - } else { + if (mode === BROTLI_ENCODE || mode === BROTLI_DECODE) { flushBoundIdx = FLUSH_BOUND_IDX_BROTLI; + } else if (mode === ZSTD_COMPRESS || mode === ZSTD_DECOMPRESS) { + flushBoundIdx = FLUSH_BOUND_IDX_ZSTD; + } else { + flushBoundIdx = FLUSH_BOUND_IDX_NORMAL; } if (opts) { @@ -592,44 +600,44 @@ Zlib.prototype.params = function params(level, strategy, callback) { } }; -function Deflate(opts) { +function Deflate(opts): void { if (!(this instanceof Deflate)) return new Deflate(opts); Zlib.$apply(this, [opts, DEFLATE]); } $toClass(Deflate, "Deflate", Zlib); -function Inflate(opts) { +function Inflate(opts): void { if (!(this instanceof Inflate)) return new Inflate(opts); Zlib.$apply(this, [opts, INFLATE]); } $toClass(Inflate, "Inflate", Zlib); -function Gzip(opts) { +function Gzip(opts): void { if (!(this instanceof Gzip)) return new Gzip(opts); Zlib.$apply(this, [opts, GZIP]); } $toClass(Gzip, "Gzip", Zlib); -function Gunzip(opts) { +function Gunzip(opts): void { if (!(this instanceof Gunzip)) return new Gunzip(opts); Zlib.$apply(this, [opts, GUNZIP]); } $toClass(Gunzip, "Gunzip", Zlib); -function DeflateRaw(opts) { +function DeflateRaw(opts): void { if (opts && opts.windowBits === 8) opts.windowBits = 9; if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); Zlib.$apply(this, [opts, DEFLATERAW]); } $toClass(DeflateRaw, "DeflateRaw", Zlib); -function InflateRaw(opts) { +function InflateRaw(opts): void { if (!(this instanceof InflateRaw)) return new InflateRaw(opts); Zlib.$apply(this, [opts, INFLATERAW]); } $toClass(InflateRaw, "InflateRaw", Zlib); -function Unzip(opts) { +function Unzip(opts): void { if (!(this instanceof Unzip)) return new Unzip(opts); Zlib.$apply(this, [opts, UNZIP]); } @@ -694,18 +702,75 @@ function Brotli(opts, mode) { } $toClass(Brotli, "Brotli", Zlib); -function BrotliCompress(opts) { +function BrotliCompress(opts): void { if (!(this instanceof BrotliCompress)) return new BrotliCompress(opts); Brotli.$apply(this, [opts, BROTLI_ENCODE]); } $toClass(BrotliCompress, "BrotliCompress", Brotli); -function BrotliDecompress(opts) { +function BrotliDecompress(opts): void { if (!(this instanceof BrotliDecompress)) return new BrotliDecompress(opts); Brotli.$apply(this, [opts, BROTLI_DECODE]); } $toClass(BrotliDecompress, "BrotliDecompress", Brotli); +const zstdDefaultOpts = { + flush: ZSTD_e_continue, + finishFlush: ZSTD_e_end, + fullFlush: ZSTD_e_flush, +}; + +class Zstd extends ZlibBase { + constructor(opts, mode, initParamsArray, maxParam) { + assert(mode === ZSTD_COMPRESS || mode === ZSTD_DECOMPRESS); + + initParamsArray.fill(-1); + if (opts?.params) { + ObjectKeys(opts.params).forEach(origKey => { + const key = +origKey; + if (NumberIsNaN(key) || key < 0 || key > maxParam || (initParamsArray[key] | 0) !== -1) { + throw $ERR_ZSTD_INVALID_PARAM(origKey); + } + + const value = opts.params[origKey]; + if (typeof value !== "number" && typeof value !== "boolean") { + throw $ERR_INVALID_ARG_TYPE("options.params[key]", "number", opts.params[origKey]); + } + initParamsArray[key] = value; + }); + } + + const handle = new NativeZstd(mode); + + const pledgedSrcSize = opts?.pledgedSrcSize ?? undefined; + + const writeState = new Uint32Array(2); + handle.init(initParamsArray, pledgedSrcSize, writeState, processCallback); + super(opts, mode, handle, zstdDefaultOpts); + this._writeState = writeState; + } +} + +const kMaxZstdCParam = MathMax(...ObjectKeys(constants).map(key => (key.startsWith("ZSTD_c_") ? constants[key] : 0))); + +const zstdInitCParamsArray = new Uint32Array(kMaxZstdCParam + 1); + +class ZstdCompress extends Zstd { + constructor(opts) { + super(opts, ZSTD_COMPRESS, zstdInitCParamsArray, kMaxZstdCParam); + } +} + +const kMaxZstdDParam = MathMax(...ObjectKeys(constants).map(key => (key.startsWith("ZSTD_d_") ? constants[key] : 0))); + +const zstdInitDParamsArray = new Uint32Array(kMaxZstdDParam + 1); + +class ZstdDecompress extends Zstd { + constructor(opts) { + super(opts, ZSTD_DECOMPRESS, zstdInitDParamsArray, kMaxZstdDParam); + } +} + // Legacy alias on the C++ wrapper object. ObjectDefineProperty(NativeZlib.prototype, "jsref", { __proto__: null, @@ -728,6 +793,8 @@ const zlib = { Unzip, BrotliCompress, BrotliDecompress, + ZstdCompress, + ZstdDecompress, deflate: createConvenienceMethod(Deflate, false, "deflate"), deflateSync: createConvenienceMethod(Deflate, true, "deflateSync"), @@ -747,6 +814,10 @@ const zlib = { brotliCompressSync: createConvenienceMethod(BrotliCompress, true, "brotliCompressSync"), brotliDecompress: createConvenienceMethod(BrotliDecompress, false, "brotliDecompress"), brotliDecompressSync: createConvenienceMethod(BrotliDecompress, true, "brotliDecompressSync"), + zstdCompress: createConvenienceMethod(ZstdCompress, false, "zstdCompress"), + zstdCompressSync: createConvenienceMethod(ZstdCompress, true, "zstdCompressSync"), + zstdDecompress: createConvenienceMethod(ZstdDecompress, false, "zstdDecompress"), + zstdDecompressSync: createConvenienceMethod(ZstdDecompress, true, "zstdDecompressSync"), createDeflate: function (options) { return new Deflate(options); @@ -775,6 +846,12 @@ const zlib = { createBrotliDecompress: function (options) { return new BrotliDecompress(options); }, + createZstdCompress: function (options) { + return new ZstdCompress(options); + }, + createZstdDecompress: function (options) { + return new ZstdDecompress(options); + }, }; ObjectDefineProperties(zlib, { diff --git a/src/zlib.zig b/src/zlib.zig index 04269c63b9..98a2ee3a84 100644 --- a/src/zlib.zig +++ b/src/zlib.zig @@ -737,6 +737,8 @@ pub const NodeMode = enum(u8) { UNZIP = 7, BROTLI_DECODE = 8, BROTLI_ENCODE = 9, + ZSTD_COMPRESS = 10, + ZSTD_DECOMPRESS = 11, }; /// Not for streaming! diff --git a/test/js/node/test/fixtures/person.jpg.zst b/test/js/node/test/fixtures/person.jpg.zst new file mode 100644 index 0000000000000000000000000000000000000000..ea2df5c9a6668aa6056fce470902d13214545e79 GIT binary patch literal 45404 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 z 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 literal 0 HcmV?d00001 diff --git a/test/js/node/test/parallel/test-zlib-convenience-methods.js b/test/js/node/test/parallel/test-zlib-convenience-methods.js index 01ec7e211b..f938a1f6a7 100644 --- a/test/js/node/test/parallel/test-zlib-convenience-methods.js +++ b/test/js/node/test/parallel/test-zlib-convenience-methods.js @@ -54,6 +54,8 @@ for (const [type, expect] of [ ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], ['brotliCompress', 'brotliDecompress', 'BrotliCompress', 'BrotliDecompress'], + ['zstdCompress', 'zstdDecompress', + 'ZstdCompress', 'ZstdDecompress'], ]) { zlib[method[0]](expect, opts, common.mustCall((err, result) => { zlib[method[1]](result, opts, common.mustCall((err, result) => { diff --git a/test/js/node/test/parallel/test-zlib-empty-buffer.js b/test/js/node/test/parallel/test-zlib-empty-buffer.js index 27fd1340fd..af53b3013e 100644 --- a/test/js/node/test/parallel/test-zlib-empty-buffer.js +++ b/test/js/node/test/parallel/test-zlib-empty-buffer.js @@ -11,10 +11,12 @@ const emptyBuffer = Buffer.alloc(0); [ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ], [ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ], [ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ], + [ zlib.zstdCompressSync, zlib.zstdDecompressSync, 'zstd sync' ], [ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ], [ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ], [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ], [ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ], + [ promisify(zlib.zstdCompress), promisify(zlib.zstdDecompress), 'zstd' ], ]) { const compressed = await compress(emptyBuffer); const decompressed = await decompress(compressed); diff --git a/test/js/node/test/parallel/test-zlib-invalid-input.js b/test/js/node/test/parallel/test-zlib-invalid-input.js index 7aa44dfe70..3ec7a15e0b 100644 --- a/test/js/node/test/parallel/test-zlib-invalid-input.js +++ b/test/js/node/test/parallel/test-zlib-invalid-input.js @@ -40,6 +40,7 @@ const unzips = [ zlib.Inflate(), zlib.InflateRaw(), zlib.BrotliDecompress(), + new zlib.ZstdDecompress(), ]; nonStringInputs.forEach(common.mustCall((input) => { diff --git a/test/js/node/test/parallel/test-zlib-random-byte-pipes.js b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js index d8d039a6d6..6a7d7c505e 100644 --- a/test/js/node/test/parallel/test-zlib-random-byte-pipes.js +++ b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js @@ -144,6 +144,7 @@ class HashStream extends Stream { for (const [ createCompress, createDecompress ] of [ [ zlib.createGzip, zlib.createGunzip ], [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], + [ zlib.createZstdCompress, zlib.createZstdDecompress ], ]) { const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); const out = new HashStream(); diff --git a/test/js/node/test/parallel/test-zlib-write-after-flush.js b/test/js/node/test/parallel/test-zlib-write-after-flush.js index 6edcae2e2f..57988191f8 100644 --- a/test/js/node/test/parallel/test-zlib-write-after-flush.js +++ b/test/js/node/test/parallel/test-zlib-write-after-flush.js @@ -27,6 +27,7 @@ const zlib = require('zlib'); for (const [ createCompress, createDecompress ] of [ [ zlib.createGzip, zlib.createGunzip ], [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], + [ zlib.createZstdCompress, zlib.createZstdDecompress ], ]) { const gzip = createCompress(); const gunz = createDecompress(); diff --git a/test/js/node/test/parallel/test-zlib-zero-byte.js b/test/js/node/test/parallel/test-zlib-zero-byte.js index fc57960f1e..d61750912b 100644 --- a/test/js/node/test/parallel/test-zlib-zero-byte.js +++ b/test/js/node/test/parallel/test-zlib-zero-byte.js @@ -24,8 +24,14 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) { - const gz = Compressor(); +const compressors = [ + [zlib.Gzip, 20], + [zlib.BrotliCompress, 1], + [zlib.ZstdCompress, 9], +]; + +for (const [Compressor, expected] of compressors) { + const gz = new Compressor(); const emptyBuffer = Buffer.alloc(0); let received = 0; gz.on('data', function(c) { @@ -33,7 +39,6 @@ for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) { }); gz.on('end', common.mustCall(function() { - const expected = Compressor === zlib.Gzip ? 20 : 1; assert.strictEqual(received, expected, `${received}, ${expected}, ${Compressor.name}`); })); diff --git a/test/js/node/test/parallel/test-zlib-zstd-flush.js b/test/js/node/test/parallel/test-zlib-zstd-flush.js new file mode 100644 index 0000000000..bde202867b --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd-flush.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const compress = new zlib.ZstdCompress(); + +const chunk = file.slice(0, chunkSize); +const expectedFull = Buffer.from('KLUv/QBYgAAA/9j/4AAQSkZJRgABAQEASA==', 'base64'); +let actualFull; + +compress.write(chunk, function() { + compress.flush(function() { + const bufs = []; + let buf; + while ((buf = compress.read()) !== null) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualFull.toString('base64'), expectedFull.toString('base64')); + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/js/node/test/parallel/test-zlib-zstd-from-string.js b/test/js/node/test/parallel/test-zlib-zstd-from-string.js new file mode 100644 index 0000000000..478e3be826 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd-from-string.js @@ -0,0 +1,38 @@ +'use strict'; +// Test compressing and uncompressing a string with zstd + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const compressedString = 'KLUv/QRYRQkA9tc9H6AlhTb/z/7/gbTI3kaWLKnbCtkZu/hXm0j' + + 'FpNz/VQM2ADMANQBHTuQOpIYzfVv7XGwXrpoIfgXNAB98xW4wV3' + + 'vnCF2bjcvWZF2wIZ1vr1mSHHvPHU0TgMGBwUFrF0xqReWcWPO8z' + + 'Ny6wMwFUilN+Lg987Zvs2GSRMy6uYvtovK9Uuhgst6l9FQrXLnA' + + '5gpZL7PdI8bO9sDH3tHm73XBzaUK+LjSPNKRmzQ3ZMYEPozdof1' + + '2KcZGfIcLa0PTsdkYqhGcAx/E9mWa8EGEeq0Qou2LTmzgg3YJz/' + + '21OuXSF+TOd662d60Qyb04xC5dOF4b8JFH8mpHAxAAELu3tg1oa' + + 'bBEIWaRHdE0l/+0RdEWWIVMAku8TgbiX/4bU+OpLo4UuY1FKDR8' + + 'RgBc'; + +zlib.zstdCompress(inputString, common.mustCall((err, buffer) => { + assert(inputString.length > buffer.length); + + zlib.zstdDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); + })); +})); + +const buffer = Buffer.from(compressedString, 'base64'); +zlib.zstdDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/js/node/test/parallel/test-zlib-zstd-from-zstd.js b/test/js/node/test/parallel/test-zlib-zstd-from-zstd.js new file mode 100644 index 0000000000..628dbefb25 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd-from-zstd.js @@ -0,0 +1,34 @@ +'use strict'; +// Test unzipping a file that was created with a non-node zstd lib, +// piped in as fast as possible. +// +// The compressed fixture was created using the reference CLI: +// $ zstd -19 test/fixtures/person.jpg -o test/fixtures/person.jpg.zst + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const decompress = new zlib.ZstdDecompress(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.zst'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = tmpdir.resolve('person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(decompress).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/js/node/test/parallel/test-zlib-zstd-kmaxlength-rangeerror.js b/test/js/node/test/parallel/test-zlib-zstd-kmaxlength-rangeerror.js new file mode 100644 index 0000000000..58ad8ff2c9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd-kmaxlength-rangeerror.js @@ -0,0 +1,29 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 64; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +// "a".repeat(128), compressed using zstd. +const encoded = Buffer.from('KLUv/SCARQAAEGFhAQA7BVg=', 'base64'); + +// Async +zlib.zstdDecompress(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.zstdDecompressSync(encoded); +}, RangeError); diff --git a/test/js/node/test/parallel/test-zlib-zstd-pledged-src-size.js b/test/js/node/test/parallel/test-zlib-zstd-pledged-src-size.js new file mode 100644 index 0000000000..b1e32e14ae --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd-pledged-src-size.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +function compressWithPledgedSrcSize({ pledgedSrcSize, actualSrcSize }) { + return new Promise((resolve, reject) => { + const compressor = zlib.createZstdCompress({ pledgedSrcSize }); + compressor.on('error', (e) => { + reject(e); + }); + compressor.on('end', resolve); + compressor.write('x'.repeat(actualSrcSize), () => { + compressor.end(); + compressor.resume(); + }); + }).then(() => { + // Compression should only succeed if sizes match + assert.strictEqual(pledgedSrcSize, actualSrcSize); + }, (error) => { + assert.strictEqual(error.code, 'ZSTD_error_srcSize_wrong'); + // Size error should only happen when sizes do not match + assert.notStrictEqual(pledgedSrcSize, actualSrcSize); + }).then(common.mustCall()); +} + +compressWithPledgedSrcSize({ pledgedSrcSize: 0, actualSrcSize: 0 }); + +compressWithPledgedSrcSize({ pledgedSrcSize: 0, actualSrcSize: 42 }); + +compressWithPledgedSrcSize({ pledgedSrcSize: 13, actualSrcSize: 42 }); + +compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 0 }); + +compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 13 }); + +compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 42 }); diff --git a/test/js/node/test/parallel/test-zlib-zstd.js b/test/js/node/test/parallel/test-zlib-zstd.js new file mode 100644 index 0000000000..194ed98bf9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zstd.js @@ -0,0 +1,134 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Test some zstd-specific properties of the zstd streams that can not +// be easily covered through expanding zlib-only tests. + +const sampleBuffer = fixtures.readSync('/pss-vectors.json'); + +{ + // Test setting the quality parameter at stream creation: + const sizes = []; + for (let quality = 1; + quality <= 22; + quality++) { + const encoded = zlib.zstdCompressSync(sampleBuffer, { + params: { + [zlib.constants.ZSTD_c_compressionLevel]: quality + } + }); + sizes.push(encoded.length); + } + + // Increasing quality should roughly correspond to decreasing compressed size: + for (let i = 0; i < sizes.length - 1; i++) { + assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error. + } + assert(sizes[0] > sizes[sizes.length - 1], sizes); +} + +{ + // Test that setting out-of-bounds option values or keys fails. + assert.throws(() => { + zlib.createZstdCompress({ + params: { + 10000: 0 + } + }); + }, { + code: 'ERR_ZSTD_INVALID_PARAM', + name: 'RangeError', + message: '10000 is not a valid zstd parameter' + }); + + // Test that accidentally using duplicate keys fails. + assert.throws(() => { + zlib.createZstdCompress({ + params: { + '0': 0, + '00': 0 + } + }); + }, { + code: 'ERR_ZSTD_INVALID_PARAM', + name: 'RangeError', + message: '00 is not a valid zstd parameter' + }); + + assert.throws(() => { + zlib.createZstdCompress({ + params: { + // This param must be a valid ZSTD_strategy value. + [zlib.constants.ZSTD_c_strategy]: 130 + } + }); + }, { + code: 'ERR_ZLIB_INITIALIZATION_FAILED', + name: 'Error', + message: 'Setting parameter failed' + }); + + // Test that setting out-of-bounds option values or keys fails. + assert.throws(() => { + zlib.createZstdDecompress({ + params: { + 10000: 0 + } + }); + }, { + code: 'ERR_ZSTD_INVALID_PARAM', + name: 'RangeError', + message: '10000 is not a valid zstd parameter' + }); + + // Test that accidentally using duplicate keys fails. + assert.throws(() => { + zlib.createZstdDecompress({ + params: { + '0': 0, + '00': 0 + } + }); + }, { + code: 'ERR_ZSTD_INVALID_PARAM', + name: 'RangeError', + message: '00 is not a valid zstd parameter' + }); + + assert.throws(() => { + zlib.createZstdDecompress({ + params: { + // This param must be >= 10 (ZSTD_WINDOWLOG_ABSOLUTEMIN). + [zlib.constants.ZSTD_d_windowLogMax]: 1 + } + }); + }, { + code: 'ERR_ZLIB_INITIALIZATION_FAILED', + name: 'Error', + message: 'Setting parameter failed' + }); +} + +{ + // Test options.flush range + assert.throws(() => { + zlib.zstdCompressSync('', { flush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.flush" is out of range. It must be >= 0 ' + + 'and <= 2. Received 4', + }); + + assert.throws(() => { + zlib.zstdCompressSync('', { finishFlush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.finishFlush" is out of range. It must be ' + + '>= 0 and <= 2. Received 4', + }); +} diff --git a/test/js/node/test/parallel/test-zlib.js b/test/js/node/test/parallel/test-zlib.js index 65050b85a0..3b8878d915 100644 --- a/test/js/node/test/parallel/test-zlib.js +++ b/test/js/node/test/parallel/test-zlib.js @@ -42,6 +42,7 @@ let zlibPairs = [ [zlib.Gzip, zlib.Unzip], [zlib.DeflateRaw, zlib.InflateRaw], [zlib.BrotliCompress, zlib.BrotliDecompress], + [zlib.ZstdCompress, zlib.ZstdDecompress], ]; // How fast to trickle through the slowstream diff --git a/test/js/node/zlib/leak.test.ts b/test/js/node/zlib/leak.test.ts index 4055bb02fa..7468cdb3fe 100644 --- a/test/js/node/zlib/leak.test.ts +++ b/test/js/node/zlib/leak.test.ts @@ -1,10 +1,13 @@ import { beforeAll, describe, expect, test } from "bun:test"; +import { isASAN } from "harness"; import { promisify } from "node:util"; import zlib from "node:zlib"; const input = Buffer.alloc(50000); for (let i = 0; i < input.length; i++) input[i] = Math.random(); +const upper = 1024 * 1024 * (isASAN ? 15 : 10); + describe("zlib compression does not leak memory", () => { beforeAll(() => { for (let index = 0; index < 10_000; index++) { @@ -30,8 +33,8 @@ describe("zlib compression does not leak memory", () => { const after = process.memoryUsage.rss(); console.log(after); console.log("-", after - baseline); - console.log("-", 1024 * 1024 * 10); - expect(after - baseline).toBeLessThan(1024 * 1024 * 10); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); }, 0, ); @@ -53,8 +56,8 @@ describe("zlib compression does not leak memory", () => { const after = process.memoryUsage.rss(); console.log(after); console.log("-", after - baseline); - console.log("-", 1024 * 1024 * 10); - expect(after - baseline).toBeLessThan(1024 * 1024 * 10); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); }, 0, ); @@ -73,8 +76,8 @@ describe("zlib compression does not leak memory", () => { const after = process.memoryUsage.rss(); console.log(after); console.log("-", after - baseline); - console.log("-", 1024 * 1024 * 10); - expect(after - baseline).toBeLessThan(1024 * 1024 * 10); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); }, 0); test("brotliCompressSync", async () => { @@ -90,7 +93,41 @@ describe("zlib compression does not leak memory", () => { const after = process.memoryUsage.rss(); console.log(after); console.log("-", after - baseline); - console.log("-", 1024 * 1024 * 10); - expect(after - baseline).toBeLessThan(1024 * 1024 * 10); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); + }, 0); + + test("zstdCompress", async () => { + for (let index = 0; index < 1_000; index++) { + await promisify(zlib.zstdCompress)(input); + } + const baseline = process.memoryUsage.rss(); + console.log(baseline); + for (let index = 0; index < 1_000; index++) { + await promisify(zlib.zstdCompress)(input); + } + Bun.gc(true); + const after = process.memoryUsage.rss(); + console.log(after); + console.log("-", after - baseline); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); + }, 0); + + test("zstdCompressSync", async () => { + for (let index = 0; index < 1_000; index++) { + zlib.zstdCompressSync(input); + } + const baseline = process.memoryUsage.rss(); + console.log(baseline); + for (let index = 0; index < 1_000; index++) { + zlib.zstdCompressSync(input); + } + Bun.gc(true); + const after = process.memoryUsage.rss(); + console.log(after); + console.log("-", after - baseline); + console.log("-", upper); + expect(after - baseline).toBeLessThan(upper); }, 0); }); diff --git a/test/js/node/zlib/zlib.test.js b/test/js/node/zlib/zlib.test.js index 102bb91461..7280089f36 100644 --- a/test/js/node/zlib/zlib.test.js +++ b/test/js/node/zlib/zlib.test.js @@ -51,6 +51,26 @@ describe("prototype and name and constructor", () => { }); }); } + + for (let [name, Class] of [ + ["ZstdCompress", zlib.ZstdCompress], + ["ZstdDecompress", zlib.ZstdDecompress], + ]) { + describe(`${name}`, () => { + it(`${name}.prototype should be instanceof ${name}.__proto__`, () => { + expect(Class.prototype).toBeInstanceOf(Class.__proto__); + }); + it(`${name}.prototype.constructor should be ${name}`, () => { + expect(Class.prototype.constructor).toBe(Class); + }); + it(`${name}.name should be ${name}`, () => { + expect(Class.name).toBe(name); + }); + it(`${name}.prototype.__proto__.constructor.name should be Zstd`, () => { + expect(Class.prototype.__proto__.constructor.name).toBe("Zstd"); + }); + }); + } }); describe("zlib", () => { @@ -198,21 +218,23 @@ describe("zlib.brotli", () => { const out_path_d = resolve(x_dir, "this.js"); { - const { resolve, promise } = Promise.withResolvers(); + const { resolve, reject, promise } = Promise.withResolvers(); const readStream = fs.createReadStream(import.meta.filename); const writeStream = fs.createWriteStream(out_path_c); const brStream = zlib.createBrotliCompress(); const the_stream = readStream.pipe(brStream).pipe(writeStream); the_stream.on("finish", resolve); + the_stream.on("error", reject); await promise; } { - const { resolve, promise } = Promise.withResolvers(); + const { resolve, reject, promise } = Promise.withResolvers(); const readStream = fs.createReadStream(out_path_c); const writeStream = fs.createWriteStream(out_path_d); const brStream = zlib.createBrotliDecompress(); const the_stream = readStream.pipe(brStream).pipe(writeStream); the_stream.on("finish", resolve); + the_stream.on("error", reject); await promise; } { @@ -232,8 +254,10 @@ describe("zlib.brotli", () => { const rand = createPRNG(1); let all = []; + const { promise, resolve, reject } = Promise.withResolvers(); brotliStream.on("data", chunk => all.push(chunk.length)); - brotliStream.on("end", () => expect(all).toEqual([11180, 13, 14, 13, 13, 13, 14])); + brotliStream.on("end", resolve); + brotliStream.on("error", reject); for (let i = 0; i < 50; i++) { let buf = Buffer.alloc(1024 * 1024); @@ -242,6 +266,8 @@ describe("zlib.brotli", () => { } readStream.push(null); readStream.pipe(brotliStream); + await promise; + expect(all.length).toBeGreaterThanOrEqual(7); }, 15_000); it("should accept params", async () => { @@ -337,6 +363,7 @@ for (const [compress, decompressor] of [ [zlib.deflateRawSync, zlib.createInflateRaw], [zlib.deflateSync, zlib.createInflate], [zlib.brotliCompressSync, zlib.createBrotliDecompress], + [zlib.zstdCompressSync, zlib.createZstdDecompress], // [zlib.gzipSync, zlib.createGunzip], // [zlib.gzipSync, zlib.createUnzip], ]) { @@ -364,7 +391,11 @@ for (const [compress, decompressor] of [ }, ]; for (const i in variants) { - it(`premature end handles bytesWritten properly: ${compress.name} + ${decompressor.name}: variant ${i}`, async () => { + let should_skip = false; + if (decompressor === zlib.createZstdDecompress && i == 1) should_skip = true; // fails in node too + if (decompressor === zlib.createZstdDecompress && i == 2) should_skip = true; // fails in node too + // prettier-ignore + it.skipIf(should_skip)(`premature end handles bytesWritten properly: ${compress.name} + ${decompressor.name}: variant ${i}`, async () => { const variant = variants[i]; const { promise, resolve, reject } = Promise.withResolvers(); let output = ""; @@ -469,3 +500,137 @@ for (const C of [zlib.BrotliCompress, zlib.BrotliDecompress]) { }); } } + +describe("zlib.zstd", () => { + const inputString = + "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + + "t. Morbi faucibus, purus at gravida dictum, libero arcu " + + "convallis lacus, in commodo libero metus eu nisi. Nullam" + + " commodo, neque nec porta placerat, nisi est fermentum a" + + "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + + "n non diam orci. Proin quis elit turpis. Suspendisse non" + + " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + + "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + + "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; + const compressedString = + "KLUv/WD5AF0JAGbXPCCgJUkH/8+rqgA3KaVsW+6LfK3JLcnP+I/" + + "Gy1/3Qv9XDTQAMwA0AK+Ch9LCub6tnT62C7QuwrHQHDhhNPcCQl" + + "tMWOrafGy3KO2D79QZ95omy09vwp/TFEAkEIlHOO99cOlZmfRiz" + + "XQ79GvDoY9TxrTgBBfR+77Nd7LkOWlHaGW+aEwd2rSeegWaj9Ns" + + "WAJJ0253u1jQpe3ByWLS5i+24QhTAZygaf4UlqNER3XoAk7QYar" + + "9tjHHV4yHj+tC108zuqMBJ+X2hlpwUqX6vE3r3N7q5QYntVvn3N" + + "8zVDb9UfCMCW1790yV3A88pgvkvQAniSWvFxMAELvECFu0tC1R9" + + "Ijsri5bt2kE/2mLoi2wCpkElnidDMS//DemxlNdHClyl6KeNTCugmAG"; + const compressedBuffer = Buffer.from(compressedString, "base64"); + + it("zstdDecompress", async () => { + const roundtrip = await util.promisify(zlib.zstdDecompress)(compressedBuffer); + expect(roundtrip.toString()).toEqual(inputString); + }); + + it("zstdCompressSync", () => { + const compressed = zlib.zstdCompressSync(inputString); + expect(compressed.toString("base64")).toEqual(compressedString); + }); + + it("zstdDecompressSync", () => { + const roundtrip = zlib.zstdDecompressSync(compressedBuffer); + expect(roundtrip.toString()).toEqual(inputString); + }); + + it("can compress streaming", async () => { + const encoder = zlib.createZstdCompress(); + for (const chunk of window(inputString, 55)) { + encoder.push(chunk); + } + encoder.push(null); + const buf = await new Response(encoder).text(); + expect(buf).toEqual(inputString); + }); + + it("can decompress streaming", async () => { + const decoder = zlib.createZstdDecompress(); + for (const chunk of window(compressedBuffer, 10)) { + decoder.push(chunk); + } + decoder.push(null); + const buf = await new Response(decoder).bytes(); + expect(buf).toEqual(compressedBuffer); + }); + + it("can roundtrip an empty string", async () => { + const input = ""; + const compressed = await util.promisify(zlib.zstdCompress)(input); + const roundtrip = await util.promisify(zlib.zstdDecompress)(compressed); + expect(roundtrip.toString()).toEqual(input); + }); + + it("can compress streaming big", async () => { + const encoder = zlib.createZstdCompress(); + const input = inputString + inputString + inputString + inputString; + for (const chunk of window(input, 65)) { + encoder.push(chunk); + } + encoder.push(null); + const buf = await new Response(encoder).text(); + expect(buf).toEqual(input); + }); + + it("fully works as a stream.Transform", async () => { + const x_dir = tmpdirSync(); + const out_path_c = resolve(x_dir, "this.js.br"); + const out_path_d = resolve(x_dir, "this.js"); + + { + const { resolve, reject, promise } = Promise.withResolvers(); + const readStream = fs.createReadStream(import.meta.filename); + const writeStream = fs.createWriteStream(out_path_c); + const brStream = zlib.createZstdCompress(); + const the_stream = readStream.pipe(brStream).pipe(writeStream); + the_stream.on("finish", resolve); + the_stream.on("error", reject); + await promise; + } + { + const { resolve, reject, promise } = Promise.withResolvers(); + const readStream = fs.createReadStream(out_path_c); + const writeStream = fs.createWriteStream(out_path_d); + const brStream = zlib.createZstdDecompress(); + const the_stream = readStream.pipe(brStream).pipe(writeStream); + the_stream.on("finish", resolve); + the_stream.on("error", reject); + await promise; + } + { + const expected = await Bun.file(import.meta.filename).text(); + const actual = await Bun.file(out_path_d).text(); + expect(actual).toEqual(expected); + } + }); + + it("streaming encode doesn't wait for entire input", async () => { + const createPRNG = seed => { + let state = seed ?? Math.floor(Math.random() * 0x7fffffff); + return () => (state = (1103515245 * state + 12345) % 0x80000000) / 0x7fffffff; + }; + const readStream = new stream.Readable(); + const zstdStream = zlib.createZstdCompress(); + const rand = createPRNG(1); + let all = []; + + const { promise, resolve, reject } = Promise.withResolvers(); + zstdStream.on("data", chunk => all.push(chunk.length)); + zstdStream.on("end", resolve); + zstdStream.on("error", reject); + + for (let i = 0; i < 50; i++) { + let buf = Buffer.alloc(1024 * 1024); + for (let j = 0; j < buf.length; j++) buf[j] = (rand() * 256) | 0; + readStream.push(buf); + } + readStream.push(null); + readStream.pipe(zstdStream); + await promise; + expect(all.length).toBeGreaterThanOrEqual(7); + }, 15_000); +}); diff --git a/test/js/web/fetch/fetch.stream.test.ts b/test/js/web/fetch/fetch.stream.test.ts index 92dce147b7..1dc27432fe 100644 --- a/test/js/web/fetch/fetch.stream.test.ts +++ b/test/js/web/fetch/fetch.stream.test.ts @@ -657,6 +657,7 @@ describe("fetch() with streaming", () => { { headers: { "Content-Encoding": "deflate" }, compression: "deflate-libdeflate" }, { headers: { "Content-Encoding": "deflate" }, compression: "deflate_with_headers" }, { headers: { "Content-Encoding": "br" }, compression: "br" }, + { headers: { "Content-Encoding": "zstd" }, compression: "zstd" }, ] as const; function compress(compression, data: Uint8Array) { @@ -685,6 +686,8 @@ describe("fetch() with streaming", () => { [zlib.constants.BROTLI_PARAM_SIZE_HINT]: 0, }, }); + case "zstd": + return zlib.zstdCompressSync(data, {}); default: return data; } @@ -1241,9 +1244,11 @@ describe("fetch() with streaming", () => { expect((err as Error).name).toBe("Error"); expect((err as Error).code).toBe("BrotliDecompressionError"); } else if (compression === "deflate-libdeflate") { - // Since the compressed data is different, the error ends up different. expect((err as Error).name).toBe("Error"); expect((err as Error).code).toBe("ShortRead"); + } else if (compression === "zstd") { + expect((err as Error).name).toBe("Error"); + expect((err as Error).code).toBe("ZstdDecompressionError"); } else { expect((err as Error).name).toBe("Error"); expect((err as Error).code).toBe("ZlibError"); From cf00cb495c6e266d01c84c77920c5dd2a1af5d6f Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 12 Jun 2025 14:26:17 -0700 Subject: [PATCH 002/133] fix main zig build --- src/bun.js/node/zlib/NativeZstd.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bun.js/node/zlib/NativeZstd.zig b/src/bun.js/node/zlib/NativeZstd.zig index f95ced09b5..9bd8789b4f 100644 --- a/src/bun.js/node/zlib/NativeZstd.zig +++ b/src/bun.js/node/zlib/NativeZstd.zig @@ -115,7 +115,7 @@ pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.C _ = globalThis; _ = callframe; // intentionally left empty - return .undefined; + return .jsUndefined(); } fn deinit(this: *@This()) void { From 12a4b95b34587330984616bdbe04d3cc57e3ddca Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 12 Jun 2025 15:35:52 -0800 Subject: [PATCH 003/133] zig: make uws Response and WebSocket opaque again (#20355) --- src/deps/uws/Response.zig | 6 ++---- src/deps/uws/WebSocket.zig | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/deps/uws/Response.zig b/src/deps/uws/Response.zig index f6d7744696..db9a64add3 100644 --- a/src/deps/uws/Response.zig +++ b/src/deps/uws/Response.zig @@ -10,10 +10,8 @@ /// - Offers safe casting between Zig and C representations /// - Maintains zero-cost abstractions over the underlying µWebSockets API pub fn NewResponse(ssl_flag: i32) type { - // making this opaque crashes Zig 0.14.0 when built in Debug or ReleaseSafe - // TODO: change to opaque when we have https://github.com/ziglang/zig/pull/23197 - return struct { - const Response = @This(); + return opaque { + const Response = NewResponse(ssl_flag); const ssl = ssl_flag == 1; pub inline fn castRes(res: *c.uws_res) *Response { diff --git a/src/deps/uws/WebSocket.zig b/src/deps/uws/WebSocket.zig index eeda19652e..19925c47e1 100644 --- a/src/deps/uws/WebSocket.zig +++ b/src/deps/uws/WebSocket.zig @@ -1,7 +1,6 @@ pub fn NewWebSocket(comptime ssl_flag: c_int) type { - // TODO: change to opaque when we have https://github.com/ziglang/zig/pull/23197 - return struct { - const WebSocket = @This(); + return opaque { + const WebSocket = NewWebSocket(ssl_flag); pub fn raw(this: *WebSocket) *RawWebSocket { return @as(*RawWebSocket, @ptrCast(this)); From 4329a66a1d91a4691f26f8f8972ada727fa79160 Mon Sep 17 00:00:00 2001 From: Kuba Ellwart <57842793+pxseu@users.noreply.github.com> Date: Fri, 13 Jun 2025 02:06:51 +0200 Subject: [PATCH 004/133] fix(types): make the RedisClient.del accept spread keys by default (#20348) --- packages/bun-types/redis.d.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/bun-types/redis.d.ts b/packages/bun-types/redis.d.ts index 498f80f42a..fb5947ed12 100644 --- a/packages/bun-types/redis.d.ts +++ b/packages/bun-types/redis.d.ts @@ -220,12 +220,11 @@ declare module "bun" { set(key: RedisClient.KeyLike, value: RedisClient.KeyLike, ...options: string[]): Promise<"OK" | string | null>; /** - * Delete a key - * @param key The key to delete - * @param keys Additional keys to delete + * Delete a key(s) + * @param keys The keys to delete * @returns Promise that resolves with the number of keys removed */ - del(key: RedisClient.KeyLike, ...keys: RedisClient.KeyLike[]): Promise; + del(...keys: RedisClient.KeyLike[]): Promise; /** * Increment the integer value of a key by one From 9c5797e2f58aef9646823cff6c94b8d226cefce1 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Thu, 12 Jun 2025 17:35:32 -0700 Subject: [PATCH 005/133] Update expectations.txt (#20350) --- test/expectations.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/expectations.txt b/test/expectations.txt index c253b89097..48caa8b14e 100644 --- a/test/expectations.txt +++ b/test/expectations.txt @@ -3,7 +3,6 @@ # Tests that are broken test/cli/create/create-jsx.test.ts [ FAIL ] # false > react spa (no tailwind) > build -test/integration/bun-types/bun-types.test.ts [ FAIL ] # @types/bun integration test > checks without lib.dom.d.ts test/bundler/native-plugin.test.ts [ FAIL ] # prints name when plugin crashes test/cli/install/bun-run.test.ts [ FAIL ] # should pass arguments correctly in scripts test/cli/run/run-crash-handler.test.ts [ FAIL ] # automatic crash reporter > segfault should report From f53aff093583222aa6b6d4537dd8af62b16ed092 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Fri, 13 Jun 2025 10:30:52 -0700 Subject: [PATCH 006/133] Fix TypeScript non-null assertion with new operator (#20363) Co-authored-by: Claude Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Dylan Conway --- src/js_parser.zig | 9 ------ test/bundler/transpiler/transpiler.test.js | 34 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/js_parser.zig b/src/js_parser.zig index 6261b6fa77..dad2a9eb5e 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -14180,10 +14180,6 @@ fn NewParser_( return error.SyntaxError; } - if (level.gte(.postfix)) { - return left; - } - try p.lexer.next(); optional_chain = old_optional_chain; }, @@ -15052,11 +15048,6 @@ fn NewParser_( var args = ExprNodeList{}; if (comptime is_typescript_enabled) { - // Skip over TypeScript non-null assertions - if (p.lexer.token == .t_exclamation and !p.lexer.has_newline_before) { - try p.lexer.next(); - } - // Skip over TypeScript type arguments here if there are any if (p.lexer.token == .t_less_than) { _ = p.trySkipTypeScriptTypeArgumentsWithBacktracking(); diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index 530fef1425..c4172df446 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -727,6 +727,40 @@ describe("Bun.Transpiler", () => { // err("async () => {}", "Unexpected const"); }); + it("non-null assertion with new operator", () => { + const exp = ts.expectPrinted_; + + // Basic non-null assertion with new operator on nested class + exp( + "const obj = { a: class Abc {} }; const instance = new obj!.a();", + "const obj = { a: class Abc {\n} };\nconst instance = new obj.a", + ); + + // with constructor + exp( + "const obj = { a: class Abc { constructor(x) { this.x = x; }} }; const instance = new obj!.a(1);", + "const obj = { a: class Abc {\n constructor(x) {\n this.x = x;\n }\n} };\nconst instance = new obj.a(1)", + ); + + // Non-null assertion with new operator on nested property + exp( + "const obj = { nested: { Class: class NestedClass {} } }; const instance = new obj!.nested.Class();", + "const obj = { nested: { Class: class NestedClass {\n} } };\nconst instance = new obj.nested.Class", + ); + + // Multiple non-null assertions in new expression + exp( + "const obj = { a: { b: class DeepClass {} } }; const instance = new obj!.a!.b();", + "const obj = { a: { b: class DeepClass {\n} } };\nconst instance = new obj.a.b", + ); + + // Non-null assertion with new operator and method call + exp( + "const obj = { getClass() { return class MyClass {}; } }; const C = obj!.getClass(); const instance = new C();", + "const obj = { getClass() {\n return class MyClass {\n };\n} };\nconst C = obj.getClass();\nconst instance = new C", + ); + }); + it("modifiers", () => { const exp = ts.expectPrinted_; From 62794850fa673743a50ad359afa3ae9e3346ac6d Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 13 Jun 2025 09:43:35 -0800 Subject: [PATCH 007/133] zig: node:zlib: tidy (#20362) Co-authored-by: nektro <5464072+nektro@users.noreply.github.com> --- cmake/sources/ZigSources.txt | 2 + src/bun.js/api.zig | 4 +- src/bun.js/node/node_zlib_binding.zig | 639 +------------------------- src/bun.js/node/zlib/NativeBrotli.zig | 268 +++++++++++ src/bun.js/node/zlib/NativeZlib.zig | 366 +++++++++++++++ src/bun.js/node/zlib/NativeZstd.zig | 18 +- test/internal/ban-words.test.ts | 2 +- 7 files changed, 650 insertions(+), 649 deletions(-) create mode 100644 src/bun.js/node/zlib/NativeBrotli.zig create mode 100644 src/bun.js/node/zlib/NativeZlib.zig diff --git a/cmake/sources/ZigSources.txt b/cmake/sources/ZigSources.txt index 26b0cb9eb8..cc477b670e 100644 --- a/cmake/sources/ZigSources.txt +++ b/cmake/sources/ZigSources.txt @@ -199,6 +199,8 @@ src/bun.js/node/util/parse_args_utils.zig src/bun.js/node/util/parse_args.zig src/bun.js/node/util/validators.zig src/bun.js/node/win_watcher.zig +src/bun.js/node/zlib/NativeBrotli.zig +src/bun.js/node/zlib/NativeZlib.zig src/bun.js/node/zlib/NativeZstd.zig src/bun.js/ProcessAutoKiller.zig src/bun.js/rare_data.zig diff --git a/src/bun.js/api.zig b/src/bun.js/api.zig index 94c96ab4c2..060135afd5 100644 --- a/src/bun.js/api.zig +++ b/src/bun.js/api.zig @@ -28,8 +28,8 @@ pub const JSBundler = @import("api/JSBundler.zig").JSBundler; pub const JSTranspiler = @import("api/JSTranspiler.zig"); pub const Listener = @import("api/bun/socket.zig").Listener; pub const MatchedRoute = @import("api/filesystem_router.zig").MatchedRoute; -pub const NativeBrotli = @import("node/node_zlib_binding.zig").SNativeBrotli; -pub const NativeZlib = @import("node/node_zlib_binding.zig").SNativeZlib; +pub const NativeBrotli = @import("node/zlib/NativeBrotli.zig"); +pub const NativeZlib = @import("node/zlib/NativeZlib.zig"); pub const NodeHTTPResponse = @import("api/server.zig").NodeHTTPResponse; pub const Postgres = @import("../sql/postgres.zig"); pub const ResolveMessage = @import("ResolveMessage.zig").ResolveMessage; diff --git a/src/bun.js/node/node_zlib_binding.zig b/src/bun.js/node/node_zlib_binding.zig index d5cfd08fe1..0b998c99ab 100644 --- a/src/bun.js/node/node_zlib_binding.zig +++ b/src/bun.js/node/node_zlib_binding.zig @@ -4,7 +4,6 @@ const JSC = bun.JSC; const string = bun.string; const Output = bun.Output; const ZigString = JSC.ZigString; -const validators = @import("./util/validators.zig"); const debug = bun.Output.scoped(.zlib, true); const Buffer = bun.api.node.Buffer; @@ -287,6 +286,10 @@ pub fn CompressionStream(comptime T: type) type { pub const NativeZlib = JSC.Codegen.JSNativeZlib.getConstructor; +pub const NativeBrotli = JSC.Codegen.JSNativeBrotli.getConstructor; + +pub const NativeZstd = JSC.Codegen.JSNativeZstd.getConstructor; + pub const CountedKeepAlive = struct { keep_alive: bun.Async.KeepAlive = .{}, ref_count: u32 = 0, @@ -310,124 +313,6 @@ pub const CountedKeepAlive = struct { } }; -pub const SNativeZlib = struct { - const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinit, .{}); - pub const ref = RefCount.ref; - pub const deref = RefCount.deref; - - pub const js = JSC.Codegen.JSNativeZlib; - pub const toJS = js.toJS; - pub const fromJS = js.fromJS; - pub const fromJSDirect = js.fromJSDirect; - - const impl = CompressionStream(@This()); - pub const write = impl.write; - pub const runFromJSThread = impl.runFromJSThread; - pub const writeSync = impl.writeSync; - pub const reset = impl.reset; - pub const close = impl.close; - pub const setOnError = impl.setOnError; - pub const getOnError = impl.getOnError; - pub const finalize = impl.finalize; - - ref_count: RefCount, - mode: bun.zlib.NodeMode, - globalThis: *JSC.JSGlobalObject, - stream: ZlibContext = .{}, - write_result: ?[*]u32 = null, - poll_ref: CountedKeepAlive = .{}, - this_value: JSC.Strong.Optional = .empty, - write_in_progress: bool = false, - pending_close: bool = false, - closed: bool = false, - task: JSC.WorkPoolTask = .{ .callback = undefined }, - - pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { - const arguments = callframe.argumentsUndef(4).ptr; - - var mode = arguments[0]; - if (!mode.isNumber()) { - return globalThis.throwInvalidArgumentTypeValue("mode", "number", mode); - } - const mode_double = mode.asNumber(); - if (@mod(mode_double, 1.0) != 0.0) { - return globalThis.throwInvalidArgumentTypeValue("mode", "integer", mode); - } - const mode_int: i64 = @intFromFloat(mode_double); - if (mode_int < 1 or mode_int > 7) { - return globalThis.throwRangeError(mode_int, .{ .field_name = "mode", .min = 1, .max = 7 }); - } - - const ptr = bun.new(SNativeZlib, .{ - .ref_count = .init(), - .mode = @enumFromInt(mode_int), - .globalThis = globalThis, - }); - ptr.stream.mode = ptr.mode; - return ptr; - } - - //// adding this didnt help much but leaving it here to compare the number with later - pub fn estimatedSize(_: *const SNativeZlib) usize { - const internal_state_size = 3309; // @sizeOf(@cImport(@cInclude("deflate.h")).internal_state) @ cloudflare/zlib @ 92530568d2c128b4432467b76a3b54d93d6350bd - return @sizeOf(SNativeZlib) + internal_state_size; - } - - pub fn init(this: *SNativeZlib, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const arguments = callframe.argumentsUndef(7).slice(); - const this_value = callframe.this(); - - if (arguments.len != 7) { - return globalThis.ERR(.MISSING_ARGS, "init(windowBits, level, memLevel, strategy, writeResult, writeCallback, dictionary)", .{}).throw(); - } - - const windowBits = try validators.validateInt32(globalThis, arguments[0], "windowBits", .{}, null, null); - const level = try validators.validateInt32(globalThis, arguments[1], "level", .{}, null, null); - const memLevel = try validators.validateInt32(globalThis, arguments[2], "memLevel", .{}, null, null); - const strategy = try validators.validateInt32(globalThis, arguments[3], "strategy", .{}, null, null); - // this does not get gc'd because it is stored in the JS object's `this._writeState`. and the JS object is tied to the native handle as `_handle[owner_symbol]`. - const writeResult = arguments[4].asArrayBuffer(globalThis).?.asU32().ptr; - const writeCallback = try validators.validateFunction(globalThis, "writeCallback", arguments[5]); - const dictionary = if (arguments[6].isUndefined()) null else arguments[6].asArrayBuffer(globalThis).?.byteSlice(); - - this.write_result = writeResult; - js.writeCallbackSetCached(this_value, globalThis, writeCallback); - - // Keep the dictionary alive by keeping a reference to it in the JS object. - if (dictionary != null) { - js.dictionarySetCached(this_value, globalThis, arguments[6]); - } - - this.stream.init(level, windowBits, memLevel, strategy, dictionary); - - return .jsUndefined(); - } - - pub fn params(this: *SNativeZlib, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const arguments = callframe.argumentsUndef(2).slice(); - - if (arguments.len != 2) { - return globalThis.ERR(.MISSING_ARGS, "params(level, strategy)", .{}).throw(); - } - - const level = try validators.validateInt32(globalThis, arguments[0], "level", .{}, null, null); - const strategy = try validators.validateInt32(globalThis, arguments[1], "strategy", .{}, null, null); - - const err = this.stream.setParams(level, strategy); - if (err.isError()) { - try impl.emitError(this, globalThis, callframe.this(), err); - } - return .jsUndefined(); - } - - fn deinit(this: *@This()) void { - this.this_value.deinit(); - this.poll_ref.deinit(); - this.stream.close(); - bun.destroy(this); - } -}; - pub const Error = struct { msg: ?[*:0]const u8, err: c_int, @@ -447,519 +332,3 @@ pub const Error = struct { return this.msg != null; } }; - -const ZlibContext = struct { - const c = bun.zlib; - const GZIP_HEADER_ID1: u8 = 0x1f; - const GZIP_HEADER_ID2: u8 = 0x8b; - - mode: c.NodeMode = .NONE, - state: c.z_stream = std.mem.zeroes(c.z_stream), - err: c.ReturnCode = .Ok, - flush: c.FlushValue = .NoFlush, - dictionary: []const u8 = "", - gzip_id_bytes_read: u8 = 0, - - pub fn init(this: *ZlibContext, level: c_int, windowBits: c_int, memLevel: c_int, strategy: c_int, dictionary: ?[]const u8) void { - this.flush = .NoFlush; - this.err = .Ok; - - const windowBitsActual = switch (this.mode) { - .NONE => unreachable, - .DEFLATE, .INFLATE => windowBits, - .GZIP, .GUNZIP => windowBits + 16, - .UNZIP => windowBits + 32, - .DEFLATERAW, .INFLATERAW => windowBits * -1, - .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, - .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, - }; - - this.dictionary = dictionary orelse ""; - - switch (this.mode) { - .NONE => unreachable, - .DEFLATE, .GZIP, .DEFLATERAW => this.err = c.deflateInit2_(&this.state, level, 8, windowBitsActual, memLevel, strategy, c.zlibVersion(), @sizeOf(c.z_stream)), - .INFLATE, .GUNZIP, .UNZIP, .INFLATERAW => this.err = c.inflateInit2_(&this.state, windowBitsActual, c.zlibVersion(), @sizeOf(c.z_stream)), - .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, - .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, - } - if (this.err != .Ok) { - this.mode = .NONE; - return; - } - - _ = this.setDictionary(); - } - - pub fn setDictionary(this: *ZlibContext) Error { - const dict = this.dictionary; - if (dict.len == 0) return Error.ok; - this.err = .Ok; - switch (this.mode) { - .DEFLATE, .DEFLATERAW => { - this.err = c.deflateSetDictionary(&this.state, dict.ptr, @intCast(dict.len)); - }, - .INFLATERAW => { - this.err = c.inflateSetDictionary(&this.state, dict.ptr, @intCast(dict.len)); - }, - else => {}, - } - if (this.err != .Ok) { - return this.error_for_message("Failed to set dictionary"); - } - return Error.ok; - } - - pub fn setParams(this: *ZlibContext, level: c_int, strategy: c_int) Error { - this.err = .Ok; - switch (this.mode) { - .DEFLATE, .DEFLATERAW => { - this.err = c.deflateParams(&this.state, level, strategy); - }, - else => {}, - } - if (this.err != .Ok and this.err != .BufError) { - return this.error_for_message("Failed to set parameters"); - } - return Error.ok; - } - - fn error_for_message(this: *ZlibContext, default: [*:0]const u8) Error { - var message = default; - if (this.state.err_msg) |msg| message = msg; - return .{ - .msg = message, - .err = @intFromEnum(this.err), - .code = switch (this.err) { - .Ok => "Z_OK", - .StreamEnd => "Z_STREAM_END", - .NeedDict => "Z_NEED_DICT", - .ErrNo => "Z_ERRNO", - .StreamError => "Z_STREAM_ERROR", - .DataError => "Z_DATA_ERROR", - .MemError => "Z_MEM_ERROR", - .BufError => "Z_BUF_ERROR", - .VersionError => "Z_VERSION_ERROR", - }, - }; - } - - pub fn reset(this: *ZlibContext) Error { - this.err = .Ok; - switch (this.mode) { - .DEFLATE, .DEFLATERAW, .GZIP => { - this.err = c.deflateReset(&this.state); - }, - .INFLATE, .INFLATERAW, .GUNZIP => { - this.err = c.inflateReset(&this.state); - }, - else => {}, - } - if (this.err != .Ok) { - return this.error_for_message("Failed to reset stream"); - } - return this.setDictionary(); - } - - pub fn setBuffers(this: *ZlibContext, in: ?[]const u8, out: ?[]u8) void { - this.state.avail_in = if (in) |p| @intCast(p.len) else 0; - this.state.next_in = if (in) |p| p.ptr else null; - this.state.avail_out = if (out) |p| @intCast(p.len) else 0; - this.state.next_out = if (out) |p| p.ptr else null; - } - - pub fn setFlush(this: *ZlibContext, flush: c_int) void { - this.flush = @enumFromInt(flush); - } - - pub fn doWork(this: *ZlibContext) void { - var next_expected_header_byte: ?[*]const u8 = null; - - // If the avail_out is left at 0, then it means that it ran out - // of room. If there was avail_out left over, then it means - // that all of the input was consumed. - switch (this.mode) { - .DEFLATE, .GZIP, .DEFLATERAW => { - return this.doWorkDeflate(); - }, - .UNZIP => { - if (this.state.avail_in > 0) { - next_expected_header_byte = this.state.next_in.?; - } - if (this.gzip_id_bytes_read == 0) { - if (next_expected_header_byte == null) { - return this.doWorkInflate(); - } - if (next_expected_header_byte.?[0] == GZIP_HEADER_ID1) { - this.gzip_id_bytes_read = 1; - next_expected_header_byte.? += 1; - if (this.state.avail_in == 1) { // The only available byte was already read. - return this.doWorkInflate(); - } - } else { - this.mode = .INFLATE; - return this.doWorkInflate(); - } - } - if (this.gzip_id_bytes_read == 1) { - if (next_expected_header_byte == null) { - return this.doWorkInflate(); - } - if (next_expected_header_byte.?[0] == GZIP_HEADER_ID2) { - this.gzip_id_bytes_read = 2; - this.mode = .GUNZIP; - } else { - this.mode = .INFLATE; - } - return this.doWorkInflate(); - } - bun.assert(false); // invalid number of gzip magic number bytes read - }, - .INFLATE, .GUNZIP, .INFLATERAW => { - return this.doWorkInflate(); - }, - .NONE => {}, - .BROTLI_ENCODE, .BROTLI_DECODE => {}, - .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, - } - } - - fn doWorkDeflate(this: *ZlibContext) void { - this.err = c.deflate(&this.state, this.flush); - } - - fn doWorkInflate(this: *ZlibContext) void { - this.err = c.inflate(&this.state, this.flush); - - if (this.mode != .INFLATERAW and this.err == .NeedDict and this.dictionary.len > 0) { - this.err = c.inflateSetDictionary(&this.state, this.dictionary.ptr, @intCast(this.dictionary.len)); - - if (this.err == .Ok) { - this.err = c.inflate(&this.state, this.flush); - } else if (this.err == .DataError) { - this.err = .NeedDict; - } - } - while (this.state.avail_in > 0 and this.mode == .GUNZIP and this.err == .StreamEnd and this.state.next_in.?[0] != 0) { - // Bytes remain in input buffer. Perhaps this is another compressed member in the same archive, or just trailing garbage. - // Trailing zero bytes are okay, though, since they are frequently used for padding. - _ = this.reset(); - this.err = c.inflate(&this.state, this.flush); - } - } - - pub fn updateWriteResult(this: *ZlibContext, avail_in: *u32, avail_out: *u32) void { - avail_in.* = this.state.avail_in; - avail_out.* = this.state.avail_out; - } - - pub fn getErrorInfo(this: *ZlibContext) Error { - switch (this.err) { - .Ok, .BufError => { - if (this.state.avail_out != 0 and this.flush == .Finish) { - return this.error_for_message("unexpected end of file"); - } - }, - .StreamEnd => {}, - .NeedDict => { - if (this.dictionary.len == 0) { - return this.error_for_message("Missing dictionary"); - } else { - return this.error_for_message("Bad dictionary"); - } - }, - else => { - return this.error_for_message("Zlib error"); - }, - } - return Error.ok; - } - - pub fn close(this: *ZlibContext) void { - var status = c.ReturnCode.Ok; - switch (this.mode) { - .DEFLATE, .DEFLATERAW, .GZIP => { - status = c.deflateEnd(&this.state); - }, - .INFLATE, .INFLATERAW, .GUNZIP, .UNZIP => { - status = c.inflateEnd(&this.state); - }, - .NONE => {}, - .BROTLI_ENCODE, .BROTLI_DECODE => {}, - .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, - } - bun.assert(status == .Ok or status == .DataError); - this.mode = .NONE; - } -}; - -pub const NativeBrotli = JSC.Codegen.JSNativeBrotli.getConstructor; - -pub const SNativeBrotli = struct { - const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinit, .{}); - pub const ref = RefCount.ref; - pub const deref = RefCount.deref; - - pub const js = JSC.Codegen.JSNativeBrotli; - pub const toJS = js.toJS; - pub const fromJS = js.fromJS; - pub const fromJSDirect = js.fromJSDirect; - - const impl = CompressionStream(@This()); - pub const write = impl.write; - pub const runFromJSThread = impl.runFromJSThread; - pub const writeSync = impl.writeSync; - pub const reset = impl.reset; - pub const close = impl.close; - pub const setOnError = impl.setOnError; - pub const getOnError = impl.getOnError; - pub const finalize = impl.finalize; - - ref_count: RefCount, - mode: bun.zlib.NodeMode, - globalThis: *JSC.JSGlobalObject, - stream: BrotliContext = .{}, - write_result: ?[*]u32 = null, - poll_ref: CountedKeepAlive = .{}, - this_value: JSC.Strong.Optional = .empty, - write_in_progress: bool = false, - pending_close: bool = false, - closed: bool = false, - task: JSC.WorkPoolTask = .{ .callback = undefined }, - - pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { - const arguments = callframe.argumentsUndef(1).ptr; - - var mode = arguments[0]; - if (!mode.isNumber()) { - return globalThis.throwInvalidArgumentTypeValue("mode", "number", mode); - } - const mode_double = mode.asNumber(); - if (@mod(mode_double, 1.0) != 0.0) { - return globalThis.throwInvalidArgumentTypeValue("mode", "integer", mode); - } - const mode_int: i64 = @intFromFloat(mode_double); - if (mode_int < 8 or mode_int > 9) { - return globalThis.throwRangeError(mode_int, .{ .field_name = "mode", .min = 8, .max = 9 }); - } - - const ptr = bun.new(@This(), .{ - .ref_count = .init(), - .mode = @enumFromInt(mode_int), - .globalThis = globalThis, - }); - ptr.stream.mode = ptr.mode; - ptr.stream.mode_ = ptr.mode; - return ptr; - } - - pub fn estimatedSize(this: *const SNativeBrotli) usize { - const encoder_state_size: usize = 5143; // @sizeOf(@cImport(@cInclude("brotli/encode.h")).BrotliEncoderStateStruct) - const decoder_state_size: usize = 855; // @sizeOf(@cImport(@cInclude("brotli/decode.h")).BrotliDecoderStateStruct) - return @sizeOf(SNativeBrotli) + switch (this.mode) { - .BROTLI_ENCODE => encoder_state_size, - .BROTLI_DECODE => decoder_state_size, - else => 0, - }; - } - - pub fn init(this: *SNativeBrotli, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const arguments = callframe.argumentsUndef(3).slice(); - const this_value = callframe.this(); - if (arguments.len != 3) { - return globalThis.ERR(.MISSING_ARGS, "init(params, writeResult, writeCallback)", .{}).throw(); - } - - // this does not get gc'd because it is stored in the JS object's `this._writeState`. and the JS object is tied to the native handle as `_handle[owner_symbol]`. - const writeResult = arguments[1].asArrayBuffer(globalThis).?.asU32().ptr; - const writeCallback = try validators.validateFunction(globalThis, "writeCallback", arguments[2]); - - this.write_result = writeResult; - - js.writeCallbackSetCached(this_value, globalThis, writeCallback); - - var err = this.stream.init(); - if (err.isError()) { - try impl.emitError(this, globalThis, this_value, err); - return JSC.jsBoolean(false); - } - - const params_ = arguments[0].asArrayBuffer(globalThis).?.asU32(); - - for (params_, 0..) |d, i| { - // (d == -1) { - if (d == std.math.maxInt(u32)) { - continue; - } - err = this.stream.setParams(@intCast(i), d); - if (err.isError()) { - // try impl.emitError(this, globalThis, this_value, err); //XXX: onerror isn't set yet - return JSC.jsBoolean(false); - } - } - return JSC.jsBoolean(true); - } - - pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { - _ = this; - _ = globalThis; - _ = callframe; - // intentionally left empty - return .jsUndefined(); - } - - fn deinit(this: *@This()) void { - this.this_value.deinit(); - this.poll_ref.deinit(); - switch (this.stream.mode) { - .BROTLI_ENCODE, .BROTLI_DECODE => this.stream.close(), - else => {}, - } - bun.destroy(this); - } -}; - -const BrotliContext = struct { - const c = bun.brotli.c; - const Op = bun.brotli.c.BrotliEncoder.Operation; - - mode: bun.zlib.NodeMode = .NONE, - mode_: bun.zlib.NodeMode = .NONE, - state: *anyopaque = undefined, - - next_in: ?[*]const u8 = null, - next_out: ?[*]u8 = null, - avail_in: usize = 0, - avail_out: usize = 0, - - flush: Op = .process, - - last_result: extern union { e: c_int, d: c.BrotliDecoderResult } = @bitCast(@as(u32, 0)), - error_: c.BrotliDecoderErrorCode2 = .NO_ERROR, - - pub fn init(this: *BrotliContext) Error { - switch (this.mode_) { - .BROTLI_ENCODE => { - const alloc = &bun.brotli.BrotliAllocator.alloc; - const free = &bun.brotli.BrotliAllocator.free; - const state = c.BrotliEncoderCreateInstance(alloc, free, null); - if (state == null) { - return Error.init("Could not initialize Brotli instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); - } - this.state = @ptrCast(state.?); - return Error.ok; - }, - .BROTLI_DECODE => { - const alloc = &bun.brotli.BrotliAllocator.alloc; - const free = &bun.brotli.BrotliAllocator.free; - const state = c.BrotliDecoderCreateInstance(alloc, free, null); - if (state == null) { - return Error.init("Could not initialize Brotli instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); - } - this.state = @ptrCast(state.?); - return Error.ok; - }, - else => unreachable, - } - } - - pub fn setParams(this: *BrotliContext, key: c_uint, value: u32) Error { - switch (this.mode_) { - .BROTLI_ENCODE => { - if (c.BrotliEncoderSetParameter(@ptrCast(this.state), key, value) == 0) { - return Error.init("Setting parameter failed", -1, "ERR_BROTLI_PARAM_SET_FAILED"); - } - return Error.ok; - }, - .BROTLI_DECODE => { - if (c.BrotliDecoderSetParameter(@ptrCast(this.state), key, value) == 0) { - return Error.init("Setting parameter failed", -1, "ERR_BROTLI_PARAM_SET_FAILED"); - } - return Error.ok; - }, - else => unreachable, - } - } - - pub fn reset(this: *BrotliContext) Error { - return this.init(); - } - - pub fn setBuffers(this: *BrotliContext, in: ?[]const u8, out: ?[]u8) void { - this.next_in = if (in) |p| p.ptr else null; - this.next_out = if (out) |p| p.ptr else null; - this.avail_in = if (in) |p| p.len else 0; - this.avail_out = if (out) |p| p.len else 0; - } - - pub fn setFlush(this: *BrotliContext, flush: c_int) void { - this.flush = @enumFromInt(flush); - } - - pub fn doWork(this: *BrotliContext) void { - switch (this.mode_) { - .BROTLI_ENCODE => { - var next_in = this.next_in; - this.last_result.e = c.BrotliEncoderCompressStream(@ptrCast(this.state), this.flush, &this.avail_in, &next_in, &this.avail_out, &this.next_out, null); - this.next_in.? += @intFromPtr(next_in.?) - @intFromPtr(this.next_in.?); - }, - .BROTLI_DECODE => { - var next_in = this.next_in; - this.last_result.d = c.BrotliDecoderDecompressStream(@ptrCast(this.state), &this.avail_in, &next_in, &this.avail_out, &this.next_out, null); - this.next_in.? += @intFromPtr(next_in.?) - @intFromPtr(this.next_in.?); - if (this.last_result.d == .err) { - this.error_ = c.BrotliDecoderGetErrorCode(@ptrCast(this.state)); - } - }, - else => unreachable, - } - } - - pub fn updateWriteResult(this: *BrotliContext, avail_in: *u32, avail_out: *u32) void { - avail_in.* = @intCast(this.avail_in); - avail_out.* = @intCast(this.avail_out); - } - - pub fn getErrorInfo(this: *BrotliContext) Error { - switch (this.mode_) { - .BROTLI_ENCODE => { - if (this.last_result.e == 0) { - return Error.init("Compression failed", -1, "ERR_BROTLI_COMPRESSION_FAILED"); - } - return Error.ok; - }, - .BROTLI_DECODE => { - if (this.error_ != .NO_ERROR) { - return Error.init("Decompression failed", @intFromEnum(this.error_), code_for_error(this.error_)); - } else if (this.flush == .finish and this.last_result.d == .needs_more_input) { - return Error.init("unexpected end of file", @intFromEnum(bun.zlib.ReturnCode.BufError), "Z_BUF_ERROR"); - } - return Error.ok; - }, - else => unreachable, - } - } - - pub fn close(this: *BrotliContext) void { - switch (this.mode_) { - .BROTLI_ENCODE => c.BrotliEncoderDestroyInstance(@ptrCast(@alignCast(this.state))), - .BROTLI_DECODE => c.BrotliDecoderDestroyInstance(@ptrCast(@alignCast(this.state))), - else => unreachable, - } - this.mode = .NONE; - } - - fn code_for_error(err: c.BrotliDecoderErrorCode2) [:0]const u8 { - const E = c.BrotliDecoderErrorCode2; - const names = comptime std.meta.fieldNames(E); - const values = comptime std.enums.values(E); - inline for (names, values) |n, v| { - if (err == v) { - return "ERR_BROTLI_DECODER_" ++ n; - } - } - unreachable; - } -}; - -pub const NativeZstd = JSC.Codegen.JSNativeZstd.getConstructor; diff --git a/src/bun.js/node/zlib/NativeBrotli.zig b/src/bun.js/node/zlib/NativeBrotli.zig new file mode 100644 index 0000000000..495831e4b5 --- /dev/null +++ b/src/bun.js/node/zlib/NativeBrotli.zig @@ -0,0 +1,268 @@ +const std = @import("std"); +const bun = @import("bun"); +const JSC = bun.JSC; +const CompressionStream = @import("./../node_zlib_binding.zig").CompressionStream; +const CountedKeepAlive = @import("./../node_zlib_binding.zig").CountedKeepAlive; +const Error = @import("./../node_zlib_binding.zig").Error; +const validators = @import("./../util/validators.zig"); + +const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinit, .{}); +pub const ref = RefCount.ref; +pub const deref = RefCount.deref; + +pub const js = JSC.Codegen.JSNativeBrotli; +pub const toJS = js.toJS; +pub const fromJS = js.fromJS; +pub const fromJSDirect = js.fromJSDirect; + +const impl = CompressionStream(@This()); +pub const write = impl.write; +pub const runFromJSThread = impl.runFromJSThread; +pub const writeSync = impl.writeSync; +pub const reset = impl.reset; +pub const close = impl.close; +pub const setOnError = impl.setOnError; +pub const getOnError = impl.getOnError; +pub const finalize = impl.finalize; + +ref_count: RefCount, +globalThis: *JSC.JSGlobalObject, +stream: Context = .{}, +write_result: ?[*]u32 = null, +poll_ref: CountedKeepAlive = .{}, +this_value: JSC.Strong.Optional = .empty, +write_in_progress: bool = false, +pending_close: bool = false, +closed: bool = false, +task: JSC.WorkPoolTask = .{ .callback = undefined }, + +pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { + const arguments = callframe.argumentsUndef(1).ptr; + + var mode = arguments[0]; + if (!mode.isNumber()) { + return globalThis.throwInvalidArgumentTypeValue("mode", "number", mode); + } + const mode_double = mode.asNumber(); + if (@mod(mode_double, 1.0) != 0.0) { + return globalThis.throwInvalidArgumentTypeValue("mode", "integer", mode); + } + const mode_int: i64 = @intFromFloat(mode_double); + if (mode_int < 8 or mode_int > 9) { + return globalThis.throwRangeError(mode_int, .{ .field_name = "mode", .min = 8, .max = 9 }); + } + + const ptr = bun.new(@This(), .{ + .ref_count = .init(), + .globalThis = globalThis, + }); + ptr.stream.mode = @enumFromInt(mode_int); + return ptr; +} + +pub fn estimatedSize(this: *const @This()) usize { + const encoder_state_size: usize = 5143; // @sizeOf(@cImport(@cInclude("brotli/encode.h")).BrotliEncoderStateStruct) + const decoder_state_size: usize = 855; // @sizeOf(@cImport(@cInclude("brotli/decode.h")).BrotliDecoderStateStruct) + return @sizeOf(@This()) + switch (this.stream.mode) { + .BROTLI_ENCODE => encoder_state_size, + .BROTLI_DECODE => decoder_state_size, + else => 0, + }; +} + +pub fn init(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const arguments = callframe.argumentsUndef(3).slice(); + const this_value = callframe.this(); + if (arguments.len != 3) { + return globalThis.ERR(.MISSING_ARGS, "init(params, writeResult, writeCallback)", .{}).throw(); + } + + // this does not get gc'd because it is stored in the JS object's `this._writeState`. and the JS object is tied to the native handle as `_handle[owner_symbol]`. + const writeResult = arguments[1].asArrayBuffer(globalThis).?.asU32().ptr; + const writeCallback = try validators.validateFunction(globalThis, "writeCallback", arguments[2]); + + this.write_result = writeResult; + + js.writeCallbackSetCached(this_value, globalThis, writeCallback); + + var err = this.stream.init(); + if (err.isError()) { + try impl.emitError(this, globalThis, this_value, err); + return JSC.jsBoolean(false); + } + + const params_ = arguments[0].asArrayBuffer(globalThis).?.asU32(); + + for (params_, 0..) |d, i| { + // (d == -1) { + if (d == std.math.maxInt(u32)) { + continue; + } + err = this.stream.setParams(@intCast(i), d); + if (err.isError()) { + // try impl.emitError(this, globalThis, this_value, err); //XXX: onerror isn't set yet + return JSC.jsBoolean(false); + } + } + return JSC.jsBoolean(true); +} + +pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + _ = this; + _ = globalThis; + _ = callframe; + // intentionally left empty + return .jsUndefined(); +} + +fn deinit(this: *@This()) void { + this.this_value.deinit(); + this.poll_ref.deinit(); + switch (this.stream.mode) { + .BROTLI_ENCODE, .BROTLI_DECODE => this.stream.close(), + else => {}, + } + bun.destroy(this); +} + +const Context = struct { + const c = bun.brotli.c; + const Op = bun.brotli.c.BrotliEncoder.Operation; + + mode: bun.zlib.NodeMode = .NONE, + state: ?*anyopaque = null, + + next_in: ?[*]const u8 = null, + next_out: ?[*]u8 = null, + avail_in: usize = 0, + avail_out: usize = 0, + + flush: Op = .process, + + last_result: extern union { e: c_int, d: c.BrotliDecoderResult } = @bitCast(@as(u32, 0)), + error_: c.BrotliDecoderErrorCode2 = .NO_ERROR, + + pub fn init(this: *Context) Error { + switch (this.mode) { + .BROTLI_ENCODE => { + const alloc = &bun.brotli.BrotliAllocator.alloc; + const free = &bun.brotli.BrotliAllocator.free; + const state = c.BrotliEncoderCreateInstance(alloc, free, null); + if (state == null) { + return Error.init("Could not initialize Brotli instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); + } + this.state = @ptrCast(state.?); + return Error.ok; + }, + .BROTLI_DECODE => { + const alloc = &bun.brotli.BrotliAllocator.alloc; + const free = &bun.brotli.BrotliAllocator.free; + const state = c.BrotliDecoderCreateInstance(alloc, free, null); + if (state == null) { + return Error.init("Could not initialize Brotli instance", -1, "ERR_ZLIB_INITIALIZATION_FAILED"); + } + this.state = @ptrCast(state.?); + return Error.ok; + }, + else => unreachable, + } + } + + pub fn setParams(this: *Context, key: c_uint, value: u32) Error { + switch (this.mode) { + .BROTLI_ENCODE => { + if (c.BrotliEncoderSetParameter(@ptrCast(this.state), key, value) == 0) { + return Error.init("Setting parameter failed", -1, "ERR_BROTLI_PARAM_SET_FAILED"); + } + return Error.ok; + }, + .BROTLI_DECODE => { + if (c.BrotliDecoderSetParameter(@ptrCast(this.state), key, value) == 0) { + return Error.init("Setting parameter failed", -1, "ERR_BROTLI_PARAM_SET_FAILED"); + } + return Error.ok; + }, + else => unreachable, + } + } + + pub fn reset(this: *Context) Error { + return this.init(); + } + + pub fn setBuffers(this: *Context, in: ?[]const u8, out: ?[]u8) void { + this.next_in = if (in) |p| p.ptr else null; + this.next_out = if (out) |p| p.ptr else null; + this.avail_in = if (in) |p| p.len else 0; + this.avail_out = if (out) |p| p.len else 0; + } + + pub fn setFlush(this: *Context, flush: c_int) void { + this.flush = @enumFromInt(flush); + } + + pub fn doWork(this: *Context) void { + switch (this.mode) { + .BROTLI_ENCODE => { + var next_in = this.next_in; + this.last_result.e = c.BrotliEncoderCompressStream(@ptrCast(this.state), this.flush, &this.avail_in, &next_in, &this.avail_out, &this.next_out, null); + this.next_in.? += @intFromPtr(next_in.?) - @intFromPtr(this.next_in.?); + }, + .BROTLI_DECODE => { + var next_in = this.next_in; + this.last_result.d = c.BrotliDecoderDecompressStream(@ptrCast(this.state), &this.avail_in, &next_in, &this.avail_out, &this.next_out, null); + this.next_in.? += @intFromPtr(next_in.?) - @intFromPtr(this.next_in.?); + if (this.last_result.d == .err) { + this.error_ = c.BrotliDecoderGetErrorCode(@ptrCast(this.state)); + } + }, + else => unreachable, + } + } + + pub fn updateWriteResult(this: *Context, avail_in: *u32, avail_out: *u32) void { + avail_in.* = @intCast(this.avail_in); + avail_out.* = @intCast(this.avail_out); + } + + pub fn getErrorInfo(this: *Context) Error { + switch (this.mode) { + .BROTLI_ENCODE => { + if (this.last_result.e == 0) { + return Error.init("Compression failed", -1, "ERR_BROTLI_COMPRESSION_FAILED"); + } + return Error.ok; + }, + .BROTLI_DECODE => { + if (this.error_ != .NO_ERROR) { + return Error.init("Decompression failed", @intFromEnum(this.error_), code_for_error(this.error_)); + } else if (this.flush == .finish and this.last_result.d == .needs_more_input) { + return Error.init("unexpected end of file", @intFromEnum(bun.zlib.ReturnCode.BufError), "Z_BUF_ERROR"); + } + return Error.ok; + }, + else => unreachable, + } + } + + pub fn close(this: *Context) void { + switch (this.mode) { + .BROTLI_ENCODE => c.BrotliEncoderDestroyInstance(@ptrCast(@alignCast(this.state))), + .BROTLI_DECODE => c.BrotliDecoderDestroyInstance(@ptrCast(@alignCast(this.state))), + else => unreachable, + } + this.mode = .NONE; + } + + fn code_for_error(err: c.BrotliDecoderErrorCode2) [:0]const u8 { + const E = c.BrotliDecoderErrorCode2; + const names = comptime std.meta.fieldNames(E); + const values = comptime std.enums.values(E); + inline for (names, values) |n, v| { + if (err == v) { + return "ERR_BROTLI_DECODER_" ++ n; + } + } + unreachable; + } +}; diff --git a/src/bun.js/node/zlib/NativeZlib.zig b/src/bun.js/node/zlib/NativeZlib.zig new file mode 100644 index 0000000000..fab1a8f020 --- /dev/null +++ b/src/bun.js/node/zlib/NativeZlib.zig @@ -0,0 +1,366 @@ +const std = @import("std"); +const bun = @import("bun"); +const JSC = bun.JSC; +const CompressionStream = @import("./../node_zlib_binding.zig").CompressionStream; +const CountedKeepAlive = @import("./../node_zlib_binding.zig").CountedKeepAlive; +const Error = @import("./../node_zlib_binding.zig").Error; +const validators = @import("./../util/validators.zig"); + +const RefCount = bun.ptr.RefCount(@This(), "ref_count", deinit, .{}); +pub const ref = RefCount.ref; +pub const deref = RefCount.deref; + +pub const js = JSC.Codegen.JSNativeZlib; +pub const toJS = js.toJS; +pub const fromJS = js.fromJS; +pub const fromJSDirect = js.fromJSDirect; + +const impl = CompressionStream(@This()); +pub const write = impl.write; +pub const runFromJSThread = impl.runFromJSThread; +pub const writeSync = impl.writeSync; +pub const reset = impl.reset; +pub const close = impl.close; +pub const setOnError = impl.setOnError; +pub const getOnError = impl.getOnError; +pub const finalize = impl.finalize; + +ref_count: RefCount, +globalThis: *JSC.JSGlobalObject, +stream: Context = .{}, +write_result: ?[*]u32 = null, +poll_ref: CountedKeepAlive = .{}, +this_value: JSC.Strong.Optional = .empty, +write_in_progress: bool = false, +pending_close: bool = false, +closed: bool = false, +task: JSC.WorkPoolTask = .{ .callback = undefined }, + +pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!*@This() { + const arguments = callframe.argumentsUndef(4).ptr; + + var mode = arguments[0]; + if (!mode.isNumber()) { + return globalThis.throwInvalidArgumentTypeValue("mode", "number", mode); + } + const mode_double = mode.asNumber(); + if (@mod(mode_double, 1.0) != 0.0) { + return globalThis.throwInvalidArgumentTypeValue("mode", "integer", mode); + } + const mode_int: i64 = @intFromFloat(mode_double); + if (mode_int < 1 or mode_int > 7) { + return globalThis.throwRangeError(mode_int, .{ .field_name = "mode", .min = 1, .max = 7 }); + } + + const ptr = bun.new(@This(), .{ + .ref_count = .init(), + .globalThis = globalThis, + }); + ptr.stream.mode = @enumFromInt(mode_int); + return ptr; +} + +//// adding this didnt help much but leaving it here to compare the number with later +pub fn estimatedSize(_: *const @This()) usize { + const internal_state_size = 3309; // @sizeOf(@cImport(@cInclude("deflate.h")).internal_state) @ cloudflare/zlib @ 92530568d2c128b4432467b76a3b54d93d6350bd + return @sizeOf(@This()) + internal_state_size; +} + +pub fn init(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const arguments = callframe.argumentsUndef(7).slice(); + const this_value = callframe.this(); + + if (arguments.len != 7) { + return globalThis.ERR(.MISSING_ARGS, "init(windowBits, level, memLevel, strategy, writeResult, writeCallback, dictionary)", .{}).throw(); + } + + const windowBits = try validators.validateInt32(globalThis, arguments[0], "windowBits", .{}, null, null); + const level = try validators.validateInt32(globalThis, arguments[1], "level", .{}, null, null); + const memLevel = try validators.validateInt32(globalThis, arguments[2], "memLevel", .{}, null, null); + const strategy = try validators.validateInt32(globalThis, arguments[3], "strategy", .{}, null, null); + // this does not get gc'd because it is stored in the JS object's `this._writeState`. and the JS object is tied to the native handle as `_handle[owner_symbol]`. + const writeResult = arguments[4].asArrayBuffer(globalThis).?.asU32().ptr; + const writeCallback = try validators.validateFunction(globalThis, "writeCallback", arguments[5]); + const dictionary = if (arguments[6].isUndefined()) null else arguments[6].asArrayBuffer(globalThis).?.byteSlice(); + + this.write_result = writeResult; + js.writeCallbackSetCached(this_value, globalThis, writeCallback); + + // Keep the dictionary alive by keeping a reference to it in the JS object. + if (dictionary != null) { + js.dictionarySetCached(this_value, globalThis, arguments[6]); + } + + this.stream.init(level, windowBits, memLevel, strategy, dictionary); + + return .jsUndefined(); +} + +pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { + const arguments = callframe.argumentsUndef(2).slice(); + + if (arguments.len != 2) { + return globalThis.ERR(.MISSING_ARGS, "params(level, strategy)", .{}).throw(); + } + + const level = try validators.validateInt32(globalThis, arguments[0], "level", .{}, null, null); + const strategy = try validators.validateInt32(globalThis, arguments[1], "strategy", .{}, null, null); + + const err = this.stream.setParams(level, strategy); + if (err.isError()) { + try impl.emitError(this, globalThis, callframe.this(), err); + } + return .jsUndefined(); +} + +fn deinit(this: *@This()) void { + this.this_value.deinit(); + this.poll_ref.deinit(); + this.stream.close(); + bun.destroy(this); +} + +const Context = struct { + const c = bun.zlib; + const GZIP_HEADER_ID1: u8 = 0x1f; + const GZIP_HEADER_ID2: u8 = 0x8b; + + mode: c.NodeMode = .NONE, + state: c.z_stream = std.mem.zeroes(c.z_stream), + err: c.ReturnCode = .Ok, + flush: c.FlushValue = .NoFlush, + dictionary: []const u8 = "", + gzip_id_bytes_read: u8 = 0, + + pub fn init(this: *Context, level: c_int, windowBits: c_int, memLevel: c_int, strategy: c_int, dictionary: ?[]const u8) void { + this.flush = .NoFlush; + this.err = .Ok; + + const windowBitsActual = switch (this.mode) { + .NONE => unreachable, + .DEFLATE, .INFLATE => windowBits, + .GZIP, .GUNZIP => windowBits + 16, + .UNZIP => windowBits + 32, + .DEFLATERAW, .INFLATERAW => windowBits * -1, + .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, + }; + + this.dictionary = dictionary orelse ""; + + switch (this.mode) { + .NONE => unreachable, + .DEFLATE, .GZIP, .DEFLATERAW => this.err = c.deflateInit2_(&this.state, level, 8, windowBitsActual, memLevel, strategy, c.zlibVersion(), @sizeOf(c.z_stream)), + .INFLATE, .GUNZIP, .UNZIP, .INFLATERAW => this.err = c.inflateInit2_(&this.state, windowBitsActual, c.zlibVersion(), @sizeOf(c.z_stream)), + .BROTLI_DECODE, .BROTLI_ENCODE => unreachable, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => unreachable, + } + if (this.err != .Ok) { + this.mode = .NONE; + return; + } + + _ = this.setDictionary(); + } + + pub fn setDictionary(this: *Context) Error { + const dict = this.dictionary; + if (dict.len == 0) return Error.ok; + this.err = .Ok; + switch (this.mode) { + .DEFLATE, .DEFLATERAW => { + this.err = c.deflateSetDictionary(&this.state, dict.ptr, @intCast(dict.len)); + }, + .INFLATERAW => { + this.err = c.inflateSetDictionary(&this.state, dict.ptr, @intCast(dict.len)); + }, + else => {}, + } + if (this.err != .Ok) { + return this.error_for_message("Failed to set dictionary"); + } + return Error.ok; + } + + pub fn setParams(this: *Context, level: c_int, strategy: c_int) Error { + this.err = .Ok; + switch (this.mode) { + .DEFLATE, .DEFLATERAW => { + this.err = c.deflateParams(&this.state, level, strategy); + }, + else => {}, + } + if (this.err != .Ok and this.err != .BufError) { + return this.error_for_message("Failed to set parameters"); + } + return Error.ok; + } + + fn error_for_message(this: *Context, default: [*:0]const u8) Error { + var message = default; + if (this.state.err_msg) |msg| message = msg; + return .{ + .msg = message, + .err = @intFromEnum(this.err), + .code = switch (this.err) { + .Ok => "Z_OK", + .StreamEnd => "Z_STREAM_END", + .NeedDict => "Z_NEED_DICT", + .ErrNo => "Z_ERRNO", + .StreamError => "Z_STREAM_ERROR", + .DataError => "Z_DATA_ERROR", + .MemError => "Z_MEM_ERROR", + .BufError => "Z_BUF_ERROR", + .VersionError => "Z_VERSION_ERROR", + }, + }; + } + + pub fn reset(this: *Context) Error { + this.err = .Ok; + switch (this.mode) { + .DEFLATE, .DEFLATERAW, .GZIP => { + this.err = c.deflateReset(&this.state); + }, + .INFLATE, .INFLATERAW, .GUNZIP => { + this.err = c.inflateReset(&this.state); + }, + else => {}, + } + if (this.err != .Ok) { + return this.error_for_message("Failed to reset stream"); + } + return this.setDictionary(); + } + + pub fn setBuffers(this: *Context, in: ?[]const u8, out: ?[]u8) void { + this.state.avail_in = if (in) |p| @intCast(p.len) else 0; + this.state.next_in = if (in) |p| p.ptr else null; + this.state.avail_out = if (out) |p| @intCast(p.len) else 0; + this.state.next_out = if (out) |p| p.ptr else null; + } + + pub fn setFlush(this: *Context, flush: c_int) void { + this.flush = @enumFromInt(flush); + } + + pub fn doWork(this: *Context) void { + var next_expected_header_byte: ?[*]const u8 = null; + + // If the avail_out is left at 0, then it means that it ran out + // of room. If there was avail_out left over, then it means + // that all of the input was consumed. + switch (this.mode) { + .DEFLATE, .GZIP, .DEFLATERAW => { + return this.doWorkDeflate(); + }, + .UNZIP => { + if (this.state.avail_in > 0) { + next_expected_header_byte = this.state.next_in.?; + } + if (this.gzip_id_bytes_read == 0) { + if (next_expected_header_byte == null) { + return this.doWorkInflate(); + } + if (next_expected_header_byte.?[0] == GZIP_HEADER_ID1) { + this.gzip_id_bytes_read = 1; + next_expected_header_byte.? += 1; + if (this.state.avail_in == 1) { // The only available byte was already read. + return this.doWorkInflate(); + } + } else { + this.mode = .INFLATE; + return this.doWorkInflate(); + } + } + if (this.gzip_id_bytes_read == 1) { + if (next_expected_header_byte == null) { + return this.doWorkInflate(); + } + if (next_expected_header_byte.?[0] == GZIP_HEADER_ID2) { + this.gzip_id_bytes_read = 2; + this.mode = .GUNZIP; + } else { + this.mode = .INFLATE; + } + return this.doWorkInflate(); + } + bun.assert(false); // invalid number of gzip magic number bytes read + }, + .INFLATE, .GUNZIP, .INFLATERAW => { + return this.doWorkInflate(); + }, + .NONE => {}, + .BROTLI_ENCODE, .BROTLI_DECODE => {}, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, + } + } + + fn doWorkDeflate(this: *Context) void { + this.err = c.deflate(&this.state, this.flush); + } + + fn doWorkInflate(this: *Context) void { + this.err = c.inflate(&this.state, this.flush); + + if (this.mode != .INFLATERAW and this.err == .NeedDict and this.dictionary.len > 0) { + this.err = c.inflateSetDictionary(&this.state, this.dictionary.ptr, @intCast(this.dictionary.len)); + + if (this.err == .Ok) { + this.err = c.inflate(&this.state, this.flush); + } else if (this.err == .DataError) { + this.err = .NeedDict; + } + } + while (this.state.avail_in > 0 and this.mode == .GUNZIP and this.err == .StreamEnd and this.state.next_in.?[0] != 0) { + // Bytes remain in input buffer. Perhaps this is another compressed member in the same archive, or just trailing garbage. + // Trailing zero bytes are okay, though, since they are frequently used for padding. + _ = this.reset(); + this.err = c.inflate(&this.state, this.flush); + } + } + + pub fn updateWriteResult(this: *Context, avail_in: *u32, avail_out: *u32) void { + avail_in.* = this.state.avail_in; + avail_out.* = this.state.avail_out; + } + + pub fn getErrorInfo(this: *Context) Error { + switch (this.err) { + .Ok, .BufError => { + if (this.state.avail_out != 0 and this.flush == .Finish) { + return this.error_for_message("unexpected end of file"); + } + }, + .StreamEnd => {}, + .NeedDict => { + if (this.dictionary.len == 0) { + return this.error_for_message("Missing dictionary"); + } else { + return this.error_for_message("Bad dictionary"); + } + }, + else => { + return this.error_for_message("Zlib error"); + }, + } + return Error.ok; + } + + pub fn close(this: *Context) void { + var status = c.ReturnCode.Ok; + switch (this.mode) { + .DEFLATE, .DEFLATERAW, .GZIP => { + status = c.deflateEnd(&this.state); + }, + .INFLATE, .INFLATERAW, .GUNZIP, .UNZIP => { + status = c.inflateEnd(&this.state); + }, + .NONE => {}, + .BROTLI_ENCODE, .BROTLI_DECODE => {}, + .ZSTD_COMPRESS, .ZSTD_DECOMPRESS => {}, + } + bun.assert(status == .Ok or status == .DataError); + this.mode = .NONE; + } +}; diff --git a/src/bun.js/node/zlib/NativeZstd.zig b/src/bun.js/node/zlib/NativeZstd.zig index 9bd8789b4f..b0b6019aeb 100644 --- a/src/bun.js/node/zlib/NativeZstd.zig +++ b/src/bun.js/node/zlib/NativeZstd.zig @@ -26,7 +26,6 @@ pub const getOnError = impl.getOnError; pub const finalize = impl.finalize; ref_count: RefCount, -mode: bun.zlib.NodeMode, globalThis: *JSC.JSGlobalObject, stream: Context = .{}, write_result: ?[*]u32 = null, @@ -55,16 +54,14 @@ pub fn constructor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) b const ptr = bun.new(@This(), .{ .ref_count = .init(), - .mode = @enumFromInt(mode_int), .globalThis = globalThis, }); - ptr.stream.mode = ptr.mode; - ptr.stream.mode_ = ptr.mode; + ptr.stream.mode = @enumFromInt(mode_int); return ptr; } pub fn estimatedSize(this: *const @This()) usize { - return @sizeOf(@This()) + switch (this.stream.mode_) { + return @sizeOf(@This()) + switch (this.stream.mode) { .ZSTD_COMPRESS => bun.c.ZSTD_sizeof_CCtx(@ptrCast(this.stream.state)), .ZSTD_DECOMPRESS => bun.c.ZSTD_sizeof_DCtx(@ptrCast(this.stream.state)), else => 0, @@ -131,7 +128,6 @@ const Context = struct { const c = bun.c; mode: bun.zlib.NodeMode = .NONE, - mode_: bun.zlib.NodeMode = .NONE, state: ?*anyopaque = null, flush: c_int = c.ZSTD_e_continue, input: c.ZSTD_inBuffer = .{ .src = null, .size = 0, .pos = 0 }, @@ -140,7 +136,7 @@ const Context = struct { remaining: u64 = 0, pub fn init(this: *Context, pledged_src_size: u64) Error { - switch (this.mode_) { + switch (this.mode) { .ZSTD_COMPRESS => { this.pledged_src_size = pledged_src_size; const state = c.ZSTD_createCCtx(); @@ -161,7 +157,7 @@ const Context = struct { } pub fn setParams(this: *Context, key: c_uint, value: u32) Error { - switch (this.mode_) { + switch (this.mode) { .ZSTD_COMPRESS => { const result = c.ZSTD_CCtx_setParameter(@ptrCast(this.state), key, @bitCast(value)); if (c.ZSTD_isError(result) > 0) return .init("Setting parameter failed", -1, "ERR_ZSTD_PARAM_SET_FAILED"); @@ -194,7 +190,7 @@ const Context = struct { } pub fn doWork(this: *Context) void { - this.remaining = switch (this.mode_) { + this.remaining = switch (this.mode) { .ZSTD_COMPRESS => c.ZSTD_compressStream2(@ptrCast(this.state), &this.output, &this.input, @intCast(this.flush)), .ZSTD_DECOMPRESS => c.ZSTD_decompressStream(@ptrCast(this.state), &this.output, &this.input), else => @panic("unreachable"), @@ -250,12 +246,12 @@ const Context = struct { } pub fn close(this: *Context) void { - _ = switch (this.mode_) { + _ = switch (this.mode) { .ZSTD_COMPRESS => c.ZSTD_CCtx_reset(@ptrCast(this.state), c.ZSTD_reset_session_and_parameters), .ZSTD_DECOMPRESS => c.ZSTD_DCtx_reset(@ptrCast(this.state), c.ZSTD_reset_session_and_parameters), else => unreachable, }; - _ = switch (this.mode_) { + _ = switch (this.mode) { .ZSTD_COMPRESS => c.ZSTD_freeCCtx(@ptrCast(this.state)), .ZSTD_DECOMPRESS => c.ZSTD_freeDCtx(@ptrCast(this.state)), else => unreachable, diff --git a/test/internal/ban-words.test.ts b/test/internal/ban-words.test.ts index 07ba00f66c..e78bd7bf13 100644 --- a/test/internal/ban-words.test.ts +++ b/test/internal/ban-words.test.ts @@ -32,7 +32,7 @@ const words: Record "== alloc.ptr": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, "!= alloc.ptr": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, - [String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 243, regex: true }, + [String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 242, regex: true }, "usingnamespace": { reason: "Zig 0.15 will remove `usingnamespace`" }, "catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1857 }, From f3bca62a7757b477e49d095266ceb30fe7d93287 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Fri, 13 Jun 2025 19:43:55 +0200 Subject: [PATCH 008/133] Fix proxy handling in bun audit (#20295) Co-authored-by: Cursor Agent Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/cli/audit_command.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/audit_command.zig b/src/cli/audit_command.zig index ce796ef9a6..e8f3b29f1a 100644 --- a/src/cli/audit_command.zig +++ b/src/cli/audit_command.zig @@ -334,6 +334,8 @@ fn sendAuditRequest(allocator: std.mem.Allocator, pm: *PackageManager, body: []c defer allocator.free(url_str); const url = URL.parse(url_str); + const http_proxy = pm.env.getHttpProxyFor(url); + var response_buf = try MutableString.init(allocator, 1024); var req = http.AsyncHTTP.initSync( allocator, @@ -343,7 +345,7 @@ fn sendAuditRequest(allocator: std.mem.Allocator, pm: *PackageManager, body: []c headers.content.ptr.?[0..headers.content.len], &response_buf, final_compressed_body, - null, + http_proxy, null, .follow, ); From c6deb4527c2ad0252056b3a47a1c72b14162bdb5 Mon Sep 17 00:00:00 2001 From: 190n Date: Fri, 13 Jun 2025 10:46:26 -0700 Subject: [PATCH 009/133] Fix crash when FFI pointers are encoded as ints (#20351) Co-authored-by: 190n <7763597+190n@users.noreply.github.com> Co-authored-by: Ciro Spaciari --- src/bun.js/api/FFIObject.zig | 2 +- src/bun.js/api/ffi.zig | 2 +- src/bun.js/bindings/JSValue.zig | 53 ++++++++++++-------------------- src/bun.js/bindings/bindings.cpp | 9 ------ src/bun.js/bindings/headers.h | 1 - test/js/bun/ffi/addr32.c | 24 +++++++++++++++ test/js/bun/ffi/addr32.test.ts | 23 ++++++++++++++ 7 files changed, 69 insertions(+), 45 deletions(-) create mode 100644 test/js/bun/ffi/addr32.c create mode 100644 test/js/bun/ffi/addr32.test.ts diff --git a/src/bun.js/api/FFIObject.zig b/src/bun.js/api/FFIObject.zig index 54e2e1aa32..dcd733ac58 100644 --- a/src/bun.js/api/FFIObject.zig +++ b/src/bun.js/api/FFIObject.zig @@ -413,7 +413,7 @@ const ValueOrError = union(enum) { }; pub fn getPtrSlice(globalThis: *JSGlobalObject, value: JSValue, byteOffset: ?JSValue, byteLength: ?JSValue) ValueOrError { - if (!value.isNumber()) { + if (!value.isNumber() or value.asNumber() < 0 or value.asNumber() > std.math.maxInt(usize)) { return .{ .err = globalThis.toInvalidArguments("ptr must be a number.", .{}) }; } diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index bb8c24f763..11cdc52d42 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -805,7 +805,7 @@ pub const FFI = struct { } pub fn closeCallback(globalThis: *JSGlobalObject, ctx: JSValue) JSValue { - var function = ctx.asPtr(Function); + var function: *Function = @ptrFromInt(ctx.asPtrAddress()); function.deinit(globalThis); return JSValue.jsUndefined(); } diff --git a/src/bun.js/bindings/JSValue.zig b/src/bun.js/bindings/JSValue.zig index 66ca74d22a..00efc6aea1 100644 --- a/src/bun.js/bindings/JSValue.zig +++ b/src/bun.js/bindings/JSValue.zig @@ -545,8 +545,9 @@ pub const JSValue = enum(i64) { return JSC__JSValue__createObject2(global, key1, key2, value1, value2); } + /// this must have been created by fromPtrAddress() pub fn asPromisePtr(this: JSValue, comptime T: type) *T { - return asPtr(this, T); + return @ptrFromInt(this.asPtrAddress()); } extern fn JSC__JSValue__createRopeString(this: JSValue, rhs: JSValue, globalThis: *JSC.JSGlobalObject) JSValue; @@ -624,7 +625,7 @@ pub const JSValue = enum(i64) { return switch (comptime Number) { JSValue => number, u0 => jsNumberFromInt32(0), - f32, f64 => jsNumberFromDouble(@as(f64, number)), + f32, f64 => jsDoubleNumber(@as(f64, number)), u31, c_ushort, u8, i16, i32, c_int, i8, u16 => jsNumberFromInt32(@as(i32, @intCast(number))), c_long, u32, u52, c_uint, i64, isize => jsNumberFromInt64(@as(i64, @intCast(number))), usize, u64 => jsNumberFromUint64(@as(u64, @intCast(number))), @@ -680,9 +681,6 @@ pub const JSValue = enum(i64) { } extern fn JSC__JSValue__jsDoubleNumber(i: f64) JSValue; - pub fn jsDoubleNumber(i: f64) JSValue { - return JSC__JSValue__jsDoubleNumber(i); - } extern fn JSC__JSValue__jsEmptyString(globalThis: *JSGlobalObject) JSValue; pub inline fn jsEmptyString(globalThis: *JSGlobalObject) JSValue { @@ -797,7 +795,7 @@ pub const JSValue = enum(i64) { return JSArrayIterator.init(this, global); } - pub fn jsNumberFromDouble(i: f64) JSValue { + pub fn jsDoubleNumber(i: f64) JSValue { return FFI.DOUBLE_TO_JSVALUE(i).asJSValue; } extern fn JSC__JSValue__jsNumberFromChar(i: u8) JSValue; @@ -817,7 +815,7 @@ pub const JSValue = enum(i64) { return jsNumberFromInt32(@as(i32, @intCast(i))); } - return jsNumberFromDouble(@floatFromInt(i)); + return jsDoubleNumber(@floatFromInt(i)); } pub inline fn toJS(this: JSValue, _: *const JSGlobalObject) JSValue { @@ -833,7 +831,7 @@ pub const JSValue = enum(i64) { } pub fn jsNumberFromPtrSize(i: usize) JSValue { - return jsNumberFromDouble(@floatFromInt(i)); + return jsDoubleNumber(@floatFromInt(i)); } fn coerceJSValueDoubleTruncatingT(comptime T: type, num: f64) T { @@ -1435,9 +1433,7 @@ pub const JSValue = enum(i64) { } pub fn then(this: JSValue, global: *JSGlobalObject, ctx: ?*anyopaque, resolve: JSC.JSHostFnZig, reject: JSC.JSHostFnZig) void { - if (comptime bun.Environment.allow_assert) - bun.assert(JSValue.fromPtr(ctx).asPtr(anyopaque) == ctx.?); - return this._then(global, JSValue.fromPtr(ctx), resolve, reject); + return this._then(global, JSValue.fromPtrAddress(@intFromPtr(ctx)), resolve, reject); } pub fn getDescription(this: JSValue, global: *JSGlobalObject) ZigString { @@ -2011,24 +2007,20 @@ pub const JSValue = enum(i64) { return null; } - extern fn JSC__JSValue__asNumber(this: JSValue) f64; + /// Asserts this is a number, undefined, null, or a boolean pub fn asNumber(this: JSValue) f64 { + bun.assert(this.isNumber() or this.isUndefinedOrNull() or this.isBoolean()); if (this.isInt32()) { - return @as(f64, @floatFromInt(this.asInt32())); - } - - if (isNumber(this)) { + return @floatFromInt(this.asInt32()); + } else if (isNumber(this)) { // Don't need to check for !isInt32() because above - return asDouble(this); - } - - if (this.isUndefinedOrNull()) { + return this.asDouble(); + } else if (this.isUndefinedOrNull()) { return 0.0; } else if (this.isBoolean()) { - return if (asBoolean(this)) 1.0 else 0.0; + return @floatFromInt(@intFromBool(this.asBoolean())); } - - return JSC__JSValue__asNumber(this); + return std.math.nan(f64); // unreachable in assertion builds } pub fn asDouble(this: JSValue) f64 { @@ -2036,21 +2028,16 @@ pub const JSValue = enum(i64) { return FFI.JSVALUE_TO_DOUBLE(.{ .asJSValue = this }); } - pub fn asPtr(this: JSValue, comptime Pointer: type) *Pointer { - return @as(*Pointer, @ptrFromInt(this.asPtrAddress())); - } - - pub fn fromPtrAddress(addr: anytype) JSValue { - return jsNumber(@as(f64, @floatFromInt(@as(usize, @bitCast(@as(usize, addr)))))); + /// Encodes addr as a double. Resulting value can be passed to asPtrAddress. + pub fn fromPtrAddress(addr: usize) JSValue { + return jsDoubleNumber(@floatFromInt(addr)); } + /// Interprets a numeric JSValue as a pointer address. Use on values returned by fromPtrAddress. pub fn asPtrAddress(this: JSValue) usize { - return @as(usize, @bitCast(@as(usize, @intFromFloat(this.asDouble())))); + return @intFromFloat(this.asNumber()); } - pub fn fromPtr(addr: anytype) JSValue { - return fromPtrAddress(@intFromPtr(addr)); - } extern fn JSC__JSValue__toBoolean(this: JSValue) bool; /// Equivalent to the `!!` operator pub fn toBoolean(this: JSValue) bool { diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index cad985f22e..a0a6b00a93 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3554,11 +3554,6 @@ JSC::JSCell* JSC__JSValue__asCell(JSC::EncodedJSValue JSValue0) auto value = JSC::JSValue::decode(JSValue0); return value.asCell(); } -double JSC__JSValue__asNumber(JSC::EncodedJSValue JSValue0) -{ - auto value = JSC::JSValue::decode(JSValue0); - return value.asNumber(); -}; JSC::JSString* JSC__JSValue__asString(JSC::EncodedJSValue JSValue0) { @@ -3726,10 +3721,6 @@ JSC::EncodedJSValue JSC__JSValue__jsBoolean(bool arg0) { return JSC::JSValue::encode(JSC::jsBoolean(arg0)); } -JSC::EncodedJSValue JSC__JSValue__jsDoubleNumber(double arg0) -{ - return JSC::JSValue::encode(JSC::jsNumber(arg0)); -} JSC::EncodedJSValue JSC__JSValue__jsEmptyString(JSC::JSGlobalObject* arg0) { diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 093e7b0f70..9abc67f3b4 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -197,7 +197,6 @@ CPP_DECL bool JSC__JSValue__asArrayBuffer_(JSC::EncodedJSValue JSValue0, JSC::JS CPP_DECL unsigned char JSC__JSValue__asBigIntCompare(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1, JSC::EncodedJSValue JSValue2); CPP_DECL JSC::JSCell* JSC__JSValue__asCell(JSC::EncodedJSValue JSValue0); CPP_DECL JSC::JSInternalPromise* JSC__JSValue__asInternalPromise(JSC::EncodedJSValue JSValue0); -CPP_DECL double JSC__JSValue__asNumber(JSC::EncodedJSValue JSValue0); CPP_DECL JSC::JSPromise* JSC__JSValue__asPromise(JSC::EncodedJSValue JSValue0); CPP_DECL JSC::JSString* JSC__JSValue__asString(JSC::EncodedJSValue JSValue0); CPP_DECL double JSC__JSValue__coerceToDouble(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1); diff --git a/test/js/bun/ffi/addr32.c b/test/js/bun/ffi/addr32.c new file mode 100644 index 0000000000..380499622d --- /dev/null +++ b/test/js/bun/ffi/addr32.c @@ -0,0 +1,24 @@ +#include +#include +#include + +// Return a string pointer in the first 2 GiB of address space. +// Linux only. +char *addr32(void) { + size_t pagesize = getpagesize(); + char *attempt = (char *)(1 << 20); + void *mapping = MAP_FAILED; + // try a few times without clobbering any existing mapping + for (int i = 0; i < 400 && mapping == MAP_FAILED; + i++, attempt += 64 * pagesize) { + mapping = mmap((void *)attempt, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); + } + if (mapping == MAP_FAILED) { + return NULL; + } else { + const char *string = "hello world"; + memcpy(mapping, string, strlen(string)); + return mapping; + } +} diff --git a/test/js/bun/ffi/addr32.test.ts b/test/js/bun/ffi/addr32.test.ts new file mode 100644 index 0000000000..3454a127f1 --- /dev/null +++ b/test/js/bun/ffi/addr32.test.ts @@ -0,0 +1,23 @@ +import { CString, dlopen, FFIType } from "bun:ffi"; +import { jscDescribe } from "bun:jsc"; +import { expect, test } from "bun:test"; +import { join } from "node:path"; +import { isLinux } from "../../../harness"; + +// Only runs on Linux because that is where we can most reliably allocate a 32-bit pointer. +test.skipIf(!isLinux)("can use addresses encoded as int32s", async () => { + const compiler = Bun.spawn(["cc", "-shared", "-o", "libaddr32.so", "addr32.c"], { + cwd: __dirname, + }); + await compiler.exited; + expect(compiler.exitCode).toBe(0); + + const { symbols } = dlopen(join(__dirname, "libaddr32.so"), { addr32: { args: [], returns: FFIType.pointer } }); + const addr = symbols.addr32()!; + expect(addr).toBeGreaterThan(0); + expect(addr).toBeLessThan(2 ** 31); + const addrIntEncoded = addr | 0; + expect(jscDescribe(addrIntEncoded)).toContain("Int32"); + // @ts-expect-error + expect(new CString(addrIntEncoded).toString()).toBe("hello world"); +}); From 4d905123fa15c8763b00348d5eeb45c49a1bf0b7 Mon Sep 17 00:00:00 2001 From: 190n Date: Fri, 13 Jun 2025 10:47:33 -0700 Subject: [PATCH 010/133] Always use correct Zig version for `bun watch` (#20309) Co-authored-by: 190n <7763597+190n@users.noreply.github.com> --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7273f89e71..764e813718 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ }, "scripts": { "build": "bun run build:debug", - "watch": "zig build check --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib -Doverride-no-export-cpp-apis=true", - "watch-windows": "zig build check-windows --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib", + "watch": "bun run zig build check --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib -Doverride-no-export-cpp-apis=true", + "watch-windows": "bun run zig build check-windows --watch -fincremental --prominent-compile-errors --global-cache-dir build/debug/zig-check-cache --zig-lib-dir vendor/zig/lib", "bd:v": "(bun run --silent build:debug &> /tmp/bun.debug.build.log || (cat /tmp/bun.debug.build.log && rm -rf /tmp/bun.debug.build.log && exit 1)) && rm -f /tmp/bun.debug.build.log && ./build/debug/bun-debug", "bd": "BUN_DEBUG_QUIET_LOGS=1 bun bd:v", "build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug", From 82b34bbbddd227c386cebe285e3487a362dd7503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Rish=C3=B8j?= Date: Fri, 13 Jun 2025 19:48:37 +0200 Subject: [PATCH 011/133] feat(sqlite): Add `columnTypes` and `declaredTypes` to `Statement` (#20232) Co-authored-by: Jarred Sumner --- packages/bun-types/sqlite.d.ts | 79 +++++ src/bun.js/bindings/sqlite/JSSQLStatement.cpp | 125 ++++++++ src/js/bun/sqlite.ts | 8 + test/js/bun/sqlite/column-types.test.js | 278 ++++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100644 test/js/bun/sqlite/column-types.test.js diff --git a/packages/bun-types/sqlite.d.ts b/packages/bun-types/sqlite.d.ts index 5c2dcaaebc..c894084b8c 100644 --- a/packages/bun-types/sqlite.d.ts +++ b/packages/bun-types/sqlite.d.ts @@ -764,6 +764,79 @@ declare module "bun:sqlite" { */ readonly paramsCount: number; + /** + * The actual SQLite column types from the first row of the result set. + * Useful for expressions and computed columns, which are not covered by `declaredTypes` + * + * Returns an array of SQLite type constants as uppercase strings: + * - `"INTEGER"` for integer values + * - `"FLOAT"` for floating-point values + * - `"TEXT"` for text values + * - `"BLOB"` for binary data + * - `"NULL"` for null values + * - `null` for unknown/unsupported types + * + * **Requirements:** + * - Only available for read-only statements (SELECT queries) + * - For non-read-only statements, throws an error + * + * **Behavior:** + * - Uses `sqlite3_column_type()` to get actual data types from the first row + * - Returns `null` for columns with unknown SQLite type constants + * + * @example + * ```ts + * const stmt = db.prepare("SELECT id, name, age FROM users WHERE id = 1"); + * + * console.log(stmt.columnTypes); + * // => ["INTEGER", "TEXT", "INTEGER"] + * + * // For expressions: + * const exprStmt = db.prepare("SELECT length('bun') AS str_length"); + * console.log(exprStmt.columnTypes); + * // => ["INTEGER"] + * ``` + * + * @throws Error if statement is not read-only (INSERT, UPDATE, DELETE, etc.) + * @since Bun v1.2.13 + */ + readonly columnTypes: Array<"INTEGER" | "FLOAT" | "TEXT" | "BLOB" | "NULL" | null>; + + /** + * The declared column types from the table schema. + * + * Returns an array of declared type strings from `sqlite3_column_decltype()`: + * - Raw type strings as declared in the CREATE TABLE statement + * - `null` for columns without declared types (e.g., expressions, computed columns) + * + * **Requirements:** + * - Statement must be executed at least once before accessing this property + * - Available for both read-only and read-write statements + * + * **Behavior:** + * - Uses `sqlite3_column_decltype()` to get schema-declared types + * - Returns the exact type string from the table definition + * + * @example + * ```ts + * // For table columns: + * const stmt = db.prepare("SELECT id, name, weight FROM products"); + * stmt.get(); + * console.log(stmt.declaredTypes); + * // => ["INTEGER", "TEXT", "REAL"] + * + * // For expressions (no declared types): + * const exprStmt = db.prepare("SELECT length('bun') AS str_length"); + * exprStmt.get(); + * console.log(exprStmt.declaredTypes); + * // => [null] + * ``` + * + * @throws Error if statement hasn't been executed + * @since Bun v1.2.13 + */ + readonly declaredTypes: Array; + /** * Finalize the prepared statement, freeing the resources used by the * statement and preventing it from being executed again. @@ -840,6 +913,12 @@ declare module "bun:sqlite" { * Native object representing the underlying `sqlite3_stmt` * * This is left untyped because the ABI of the native bindings may change at any time. + * + * For stable, typed access to statement metadata, use the typed properties on the Statement class: + * - {@link columnNames} for column names + * - {@link paramsCount} for parameter count + * - {@link columnTypes} for actual data types from the first row + * - {@link declaredTypes} for schema-declared column types */ readonly native: any; } diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp index ad399bf868..33c98eb9dc 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp @@ -294,6 +294,8 @@ JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnNames); JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnCount); JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetParamCount); +JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnTypes); +JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnDeclaredTypes); JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetSafeIntegers); JSC_DECLARE_CUSTOM_SETTER(jsSqlStatementSetSafeIntegers); @@ -545,6 +547,8 @@ static const HashTableValue JSSQLStatementPrototypeTableValues[] = { { "columns"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetColumnNames, 0 } }, { "columnsCount"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetColumnCount, 0 } }, { "paramsCount"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetParamCount, 0 } }, + { "columnTypes"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetColumnTypes, 0 } }, + { "declaredTypes"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetColumnDeclaredTypes, 0 } }, { "safeIntegers"_s, static_cast(JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsSqlStatementGetSafeIntegers, jsSqlStatementSetSafeIntegers } }, }; @@ -2362,6 +2366,127 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetParamCount, (JSGlobalObject * lexicalG RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(sqlite3_bind_parameter_count(castedThis->stmt)))); } +JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnTypes, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + JSSQLStatement* castedThis = jsDynamicCast(JSValue::decode(thisValue)); + auto scope = DECLARE_THROW_SCOPE(vm); + CHECK_THIS + CHECK_PREPARED + + int count = sqlite3_column_count(castedThis->stmt); + + // We need to reset and step the statement to get fresh types, + // but only do this for read-only statements to avoid side effects + bool isReadOnly = sqlite3_stmt_readonly(castedThis->stmt) != 0; + + if (! isReadOnly) { + // For non-read-only statements, throw an error since column types don't make sense + throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "columnTypes is not available for non-read-only statements (INSERT, UPDATE, DELETE, etc.)"_s)); + return { }; + } + + // Reset the statement (safe for read-only statements) + int resetStatus = sqlite3_reset(castedThis->stmt); + if (resetStatus != SQLITE_OK) { + throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); + return { }; + } + + MarkedArgumentBuffer args; + + // Step once to get to the first row (safe for read-only statements) + int stepStatus = sqlite3_step(castedThis->stmt); + + // If we got a row, get types from it + if (stepStatus == SQLITE_ROW) { + for (int i = 0; i < count; i++) { + JSC::JSValue typeValue; + + // Get the actual column type from the current row + int columnType = sqlite3_column_type(castedThis->stmt, i); + + switch (columnType) { + case SQLITE_INTEGER: + typeValue = JSC::jsString(vm, makeAtomString("INTEGER"_s)); + break; + case SQLITE_FLOAT: + typeValue = JSC::jsString(vm, makeAtomString("FLOAT"_s)); + break; + case SQLITE3_TEXT: + typeValue = JSC::jsString(vm, makeAtomString("TEXT"_s)); + break; + case SQLITE_BLOB: + typeValue = JSC::jsString(vm, makeAtomString("BLOB"_s)); + break; + case SQLITE_NULL: + typeValue = JSC::jsString(vm, makeAtomString("NULL"_s)); + break; + default: + typeValue = JSC::jsNull(); + break; + } + + args.append(typeValue); + } + } else if (stepStatus == SQLITE_DONE) { + // No data rows to read, return 'NULL' for all columns + JSC::JSValue typeValue = JSC::jsString(vm, makeAtomString("NULL"_s)); + for (int i = 0; i < count; i++) { + args.append(typeValue); + } + } else { + // If there was an error stepping, throw it + throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); + sqlite3_reset(castedThis->stmt); + return { }; + } + + // Reset the statement back to its original state + sqlite3_reset(castedThis->stmt); + + JSC::JSArray* array = constructArray(lexicalGlobalObject, static_cast(nullptr), args); + RETURN_IF_EXCEPTION(scope, {}); + + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(array)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnDeclaredTypes, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + JSSQLStatement* castedThis = jsDynamicCast(JSValue::decode(thisValue)); + auto scope = DECLARE_THROW_SCOPE(vm); + CHECK_THIS + CHECK_PREPARED + + // Ensure the statement has been executed at least once + if (!castedThis->hasExecuted) { + throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Statement must be executed before accessing declaredTypes"_s)); + return { }; + } + + int count = sqlite3_column_count(castedThis->stmt); + JSC::JSArray* array = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, count); + + // Use declared types only - return raw strings as-is, null when no declared type + for (int i = 0; i < count; i++) { + const char* declType = sqlite3_column_decltype(castedThis->stmt, i); + JSC::JSValue typeValue; + + if (declType != nullptr) { + String typeStr = String::fromUTF8(declType); + typeValue = JSC::jsNontrivialString(vm, typeStr); + } else { + // If no declared type (e.g., for expressions or results of functions) + typeValue = JSC::jsNull(); + } + + array->putDirectIndex(lexicalGlobalObject, i, typeValue); + } + + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(array)); +} + JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetSafeIntegers, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) { auto& vm = JSC::getVM(lexicalGlobalObject); diff --git a/src/js/bun/sqlite.ts b/src/js/bun/sqlite.ts index 6253fdf19f..3fbc4a61a3 100644 --- a/src/js/bun/sqlite.ts +++ b/src/js/bun/sqlite.ts @@ -267,6 +267,14 @@ class Statement { return this.#raw.columns; } + get columnTypes() { + return this.#raw.columnTypes; + } + + get declaredTypes() { + return this.#raw.declaredTypes; + } + get paramsCount() { return this.#raw.paramsCount; } diff --git a/test/js/bun/sqlite/column-types.test.js b/test/js/bun/sqlite/column-types.test.js new file mode 100644 index 0000000000..58ade55b88 --- /dev/null +++ b/test/js/bun/sqlite/column-types.test.js @@ -0,0 +1,278 @@ +import { describe, it, expect } from "bun:test"; +import { Database } from "bun:sqlite"; + +describe("SQLite Statement column types", () => { + it("reports correct column types for a variety of data types", () => { + // Create a test database + const db = new Database(":memory:"); + + // Create a table with different column types + db.run(` + CREATE TABLE test_types ( + id INTEGER PRIMARY KEY, + name TEXT, + weight REAL, + image BLOB, + is_active INTEGER + ) + `); + + // Insert a row + db.run(` + INSERT INTO test_types (id, name, weight, image, is_active) + VALUES (1, 'test', 72.5, X'DEADBEEF', 1) + `); + + // Prepare a statement that selects all columns + const stmt = db.prepare("SELECT * FROM test_types"); + + // Execute the statement to get column types + const row = stmt.get(); + + // Verify column metadata + expect(stmt.native.columns).toEqual(["id", "name", "weight", "image", "is_active"]); + expect(stmt.native.columnsCount).toBe(5); + + // Test the columnTypes property (uses actual data types from sqlite3_column_type) + expect(stmt.columnTypes).toBeDefined(); + expect(Array.isArray(stmt.columnTypes)).toBe(true); + expect(stmt.columnTypes.length).toBe(5); + expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT', 'FLOAT', 'BLOB', 'INTEGER']); + + // Test the declaredTypes property (uses declared types from sqlite3_column_decltype) + expect(stmt.declaredTypes).toBeDefined(); + expect(Array.isArray(stmt.declaredTypes)).toBe(true); + expect(stmt.declaredTypes.length).toBe(5); + expect(stmt.declaredTypes).toEqual(['INTEGER', 'TEXT', 'REAL', 'BLOB', 'INTEGER']); + }); + + it("handles NULL values correctly", () => { + const db = new Database(":memory:"); + + db.run(` + CREATE TABLE nulls_test ( + id INTEGER PRIMARY KEY, + nullable TEXT + ) + `); + + db.run(`INSERT INTO nulls_test (id, nullable) VALUES (1, NULL)`); + + const stmt = db.prepare("SELECT * FROM nulls_test"); + + // Execute the statement to get column types + const row = stmt.get(); + + // columnTypes now returns actual data types - NULL values are reported as 'NULL' + expect(stmt.columnTypes).toEqual(['INTEGER', 'NULL']); + + // declaredTypes still shows the declared table schema + expect(stmt.declaredTypes).toEqual(['INTEGER', 'TEXT']); + }); + + it("reports actual column types based on data values", () => { + const db = new Database(":memory:"); + + db.run(` + CREATE TABLE dynamic_types ( + id INTEGER PRIMARY KEY, + value ANY + ) + `); + + // SQLite can store various types in the same column + db.run(`INSERT INTO dynamic_types VALUES (1, 42)`); + + let stmt = db.prepare("SELECT * FROM dynamic_types"); + + // Execute the statement to get column types + let row = stmt.get(); + + // We should get the actual type of the value (integer) + expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER']); + + // Update to a text value + db.run(`UPDATE dynamic_types SET value = 'text' WHERE id = 1`); + + // Re-prepare to get fresh column type information + stmt = db.prepare("SELECT * FROM dynamic_types"); + row = stmt.get(); + + // We should get the actual type of the value (text) + expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT']); + + // Update to a float value + db.run(`UPDATE dynamic_types SET value = 3.14 WHERE id = 1`); + + // Re-prepare to get fresh column type information + stmt = db.prepare("SELECT * FROM dynamic_types"); + row = stmt.get(); + + // We should get the actual type of the value (float) + expect(stmt.columnTypes).toEqual(['INTEGER', 'FLOAT']); + }); + + it("reports actual types for columns from expressions", () => { + // Create a database + const db = new Database(":memory:"); + + // Test with an expression + const stmt = db.prepare("SELECT length('bun') AS str_length, 42 AS magic_number, 'hello' AS greeting"); + const row = stmt.get(); + + // Check the row data is as expected + expect(row).toEqual({ + str_length: 3, + magic_number: 42, + greeting: "hello" + }); + + // Check columns are correctly identified + expect(stmt.native.columns).toEqual(['str_length', 'magic_number', 'greeting']); + + // For expressions, expect the actual data types + expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER', 'TEXT']); + }); + + it("handles multiple different expressions and functions", () => { + const db = new Database(":memory:"); + + // Test with multiple different expressions + const stmt = db.prepare(` + SELECT + 123 AS int_val, + 3.14 AS float_val, + 'text' AS text_val, + x'DEADBEEF' AS blob_val, + NULL AS null_val, + length('bun') AS func_result, + CURRENT_TIMESTAMP AS timestamp + `); + + const row = stmt.get(); + + // Verify we have the expected columns + expect(stmt.native.columns).toEqual([ + 'int_val', + 'float_val', + 'text_val', + 'blob_val', + 'null_val', + 'func_result', + 'timestamp' + ]); + + // Expression columns should be reported with their actual types + expect(stmt.columnTypes).toEqual([ + 'INTEGER', 'FLOAT', 'TEXT', 'BLOB', 'NULL', 'INTEGER', 'TEXT' + ]); + + // Verify data types were correctly identified at runtime + expect(typeof row.int_val).toBe('number'); + expect(typeof row.float_val).toBe('number'); + expect(typeof row.text_val).toBe('string'); + expect(row.blob_val instanceof Uint8Array).toBe(true); + expect(row.null_val).toBe(null); + expect(typeof row.func_result).toBe('number'); + expect(typeof row.timestamp).toBe('string'); + }); + + it("shows difference between columnTypes and declaredTypes for expressions", () => { + const db = new Database(":memory:"); + + // Test with expressions where declared types differ from actual types + const stmt = db.prepare("SELECT length('bun') AS str_length, 42 AS magic_number, 'hello' AS greeting"); + const row = stmt.get(); + + // columnTypes shows actual data types based on the values + expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER', 'TEXT']); + + // declaredTypes shows declared types (which are null for expressions without explicit declarations) + expect(stmt.declaredTypes).toEqual([null, null, null]); + }); + + it("shows difference for dynamic column types", () => { + const db = new Database(":memory:"); + + db.run(` + CREATE TABLE dynamic_types ( + id INTEGER PRIMARY KEY, + value ANY + ) + `); + + // Insert an integer value + db.run(`INSERT INTO dynamic_types VALUES (1, 42)`); + + let stmt = db.prepare("SELECT * FROM dynamic_types"); + let row = stmt.get(); + + // columnTypes shows actual type (integer) for the current value + expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER']); + + // declaredTypes shows the declared table schema + expect(stmt.declaredTypes).toEqual(['INTEGER', 'ANY']); + + // Update to a text value + db.run(`UPDATE dynamic_types SET value = 'text' WHERE id = 1`); + + stmt = db.prepare("SELECT * FROM dynamic_types"); + row = stmt.get(); + + // columnTypes now shows text for the current value + expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT']); + + // declaredTypes still shows the declared table schema + expect(stmt.declaredTypes).toEqual(['INTEGER', 'ANY']); + }); + + it("throws an error when accessing columnTypes before statement execution", () => { + const db = new Database(":memory:"); + db.run(`CREATE TABLE test (id INTEGER, name TEXT)`); + + // Prepare statement but don't execute it + const stmt = db.prepare("SELECT * FROM test"); + + // Accessing columnTypes before executing is fine (implicitly executes the statement) + expect(stmt.columnTypes).toBeArray(); + + // Accessing declaredTypes before executing should throw + expect(() => { + stmt.declaredTypes; + }).toThrow("Statement must be executed before accessing declaredTypes"); + }); + + it("throws an error when accessing columnTypes on non-read-only statements", () => { + const db = new Database(":memory:"); + db.run(`CREATE TABLE test (id INTEGER, name TEXT)`); + + // Test INSERT statement + const insertStmt = db.prepare("INSERT INTO test (id, name) VALUES (?, ?)"); + insertStmt.run(1, "test"); + + expect(() => { + insertStmt.columnTypes; + }).toThrow("columnTypes is not available for non-read-only statements"); + + // Test UPDATE statement + const updateStmt = db.prepare("UPDATE test SET name = ? WHERE id = ?"); + updateStmt.run("updated", 1); + + expect(() => { + updateStmt.columnTypes; + }).toThrow("columnTypes is not available for non-read-only statements"); + + // Test DELETE statement + const deleteStmt = db.prepare("DELETE FROM test WHERE id = ?"); + deleteStmt.run(1); + + expect(() => { + deleteStmt.columnTypes; + }).toThrow("columnTypes is not available for non-read-only statements"); + + // declaredTypes should still work for these statements + expect(() => { + insertStmt.declaredTypes; + }).not.toThrow(); + }); +}); From a445b45e55fa9c86b908415b036d80d49697e7cc Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 13 Jun 2025 14:18:28 -0800 Subject: [PATCH 012/133] empty commit to trigger formatting ci (#20378) Co-authored-by: nektro <5464072+nektro@users.noreply.github.com> --- packages/bun-types/sqlite.d.ts | 32 +++--- src/bun.js/bindings/sqlite/JSSQLStatement.cpp | 22 ++-- test/js/bun/sqlite/column-types.test.js | 104 +++++++++--------- 3 files changed, 78 insertions(+), 80 deletions(-) diff --git a/packages/bun-types/sqlite.d.ts b/packages/bun-types/sqlite.d.ts index c894084b8c..94cbca0b95 100644 --- a/packages/bun-types/sqlite.d.ts +++ b/packages/bun-types/sqlite.d.ts @@ -767,36 +767,36 @@ declare module "bun:sqlite" { /** * The actual SQLite column types from the first row of the result set. * Useful for expressions and computed columns, which are not covered by `declaredTypes` - * + * * Returns an array of SQLite type constants as uppercase strings: * - `"INTEGER"` for integer values - * - `"FLOAT"` for floating-point values + * - `"FLOAT"` for floating-point values * - `"TEXT"` for text values * - `"BLOB"` for binary data * - `"NULL"` for null values * - `null` for unknown/unsupported types - * + * * **Requirements:** * - Only available for read-only statements (SELECT queries) * - For non-read-only statements, throws an error - * + * * **Behavior:** * - Uses `sqlite3_column_type()` to get actual data types from the first row * - Returns `null` for columns with unknown SQLite type constants - * + * * @example * ```ts * const stmt = db.prepare("SELECT id, name, age FROM users WHERE id = 1"); - * + * * console.log(stmt.columnTypes); * // => ["INTEGER", "TEXT", "INTEGER"] - * + * * // For expressions: * const exprStmt = db.prepare("SELECT length('bun') AS str_length"); * console.log(exprStmt.columnTypes); * // => ["INTEGER"] * ``` - * + * * @throws Error if statement is not read-only (INSERT, UPDATE, DELETE, etc.) * @since Bun v1.2.13 */ @@ -804,19 +804,19 @@ declare module "bun:sqlite" { /** * The declared column types from the table schema. - * + * * Returns an array of declared type strings from `sqlite3_column_decltype()`: * - Raw type strings as declared in the CREATE TABLE statement * - `null` for columns without declared types (e.g., expressions, computed columns) - * + * * **Requirements:** * - Statement must be executed at least once before accessing this property * - Available for both read-only and read-write statements - * + * * **Behavior:** * - Uses `sqlite3_column_decltype()` to get schema-declared types * - Returns the exact type string from the table definition - * + * * @example * ```ts * // For table columns: @@ -824,14 +824,14 @@ declare module "bun:sqlite" { * stmt.get(); * console.log(stmt.declaredTypes); * // => ["INTEGER", "TEXT", "REAL"] - * + * * // For expressions (no declared types): * const exprStmt = db.prepare("SELECT length('bun') AS str_length"); * exprStmt.get(); * console.log(exprStmt.declaredTypes); * // => [null] * ``` - * + * * @throws Error if statement hasn't been executed * @since Bun v1.2.13 */ @@ -913,10 +913,10 @@ declare module "bun:sqlite" { * Native object representing the underlying `sqlite3_stmt` * * This is left untyped because the ABI of the native bindings may change at any time. - * + * * For stable, typed access to statement metadata, use the typed properties on the Statement class: * - {@link columnNames} for column names - * - {@link paramsCount} for parameter count + * - {@link paramsCount} for parameter count * - {@link columnTypes} for actual data types from the first row * - {@link declaredTypes} for schema-declared column types */ diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp index 33c98eb9dc..7c580590ef 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp @@ -2375,22 +2375,22 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnTypes, (JSGlobalObject * lexical CHECK_PREPARED int count = sqlite3_column_count(castedThis->stmt); - + // We need to reset and step the statement to get fresh types, // but only do this for read-only statements to avoid side effects bool isReadOnly = sqlite3_stmt_readonly(castedThis->stmt) != 0; - if (! isReadOnly) { - // For non-read-only statements, throw an error since column types don't make sense - throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "columnTypes is not available for non-read-only statements (INSERT, UPDATE, DELETE, etc.)"_s)); - return { }; + if (!isReadOnly) { + // For non-read-only statements, throw an error since column types don't make sense + throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "columnTypes is not available for non-read-only statements (INSERT, UPDATE, DELETE, etc.)"_s)); + return {}; } // Reset the statement (safe for read-only statements) int resetStatus = sqlite3_reset(castedThis->stmt); if (resetStatus != SQLITE_OK) { throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); - return { }; + return {}; } MarkedArgumentBuffer args; @@ -2439,7 +2439,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnTypes, (JSGlobalObject * lexical // If there was an error stepping, throw it throwException(lexicalGlobalObject, scope, createSQLiteError(lexicalGlobalObject, castedThis->version_db->db)); sqlite3_reset(castedThis->stmt); - return { }; + return {}; } // Reset the statement back to its original state @@ -2462,7 +2462,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnDeclaredTypes, (JSGlobalObject * // Ensure the statement has been executed at least once if (!castedThis->hasExecuted) { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Statement must be executed before accessing declaredTypes"_s)); - return { }; + return {}; } int count = sqlite3_column_count(castedThis->stmt); @@ -2472,7 +2472,7 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnDeclaredTypes, (JSGlobalObject * for (int i = 0; i < count; i++) { const char* declType = sqlite3_column_decltype(castedThis->stmt, i); JSC::JSValue typeValue; - + if (declType != nullptr) { String typeStr = String::fromUTF8(declType); typeValue = JSC::jsNontrivialString(vm, typeStr); @@ -2480,10 +2480,10 @@ JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnDeclaredTypes, (JSGlobalObject * // If no declared type (e.g., for expressions or results of functions) typeValue = JSC::jsNull(); } - + array->putDirectIndex(lexicalGlobalObject, i, typeValue); } - + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(array)); } diff --git a/test/js/bun/sqlite/column-types.test.js b/test/js/bun/sqlite/column-types.test.js index 58ade55b88..1d93a845ad 100644 --- a/test/js/bun/sqlite/column-types.test.js +++ b/test/js/bun/sqlite/column-types.test.js @@ -1,5 +1,5 @@ -import { describe, it, expect } from "bun:test"; import { Database } from "bun:sqlite"; +import { describe, expect, it } from "bun:test"; describe("SQLite Statement column types", () => { it("reports correct column types for a variety of data types", () => { @@ -25,25 +25,25 @@ describe("SQLite Statement column types", () => { // Prepare a statement that selects all columns const stmt = db.prepare("SELECT * FROM test_types"); - + // Execute the statement to get column types const row = stmt.get(); // Verify column metadata expect(stmt.native.columns).toEqual(["id", "name", "weight", "image", "is_active"]); expect(stmt.native.columnsCount).toBe(5); - + // Test the columnTypes property (uses actual data types from sqlite3_column_type) expect(stmt.columnTypes).toBeDefined(); expect(Array.isArray(stmt.columnTypes)).toBe(true); expect(stmt.columnTypes.length).toBe(5); - expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT', 'FLOAT', 'BLOB', 'INTEGER']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "TEXT", "FLOAT", "BLOB", "INTEGER"]); + // Test the declaredTypes property (uses declared types from sqlite3_column_decltype) expect(stmt.declaredTypes).toBeDefined(); expect(Array.isArray(stmt.declaredTypes)).toBe(true); expect(stmt.declaredTypes.length).toBe(5); - expect(stmt.declaredTypes).toEqual(['INTEGER', 'TEXT', 'REAL', 'BLOB', 'INTEGER']); + expect(stmt.declaredTypes).toEqual(["INTEGER", "TEXT", "REAL", "BLOB", "INTEGER"]); }); it("handles NULL values correctly", () => { @@ -59,15 +59,15 @@ describe("SQLite Statement column types", () => { db.run(`INSERT INTO nulls_test (id, nullable) VALUES (1, NULL)`); const stmt = db.prepare("SELECT * FROM nulls_test"); - + // Execute the statement to get column types const row = stmt.get(); // columnTypes now returns actual data types - NULL values are reported as 'NULL' - expect(stmt.columnTypes).toEqual(['INTEGER', 'NULL']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "NULL"]); + // declaredTypes still shows the declared table schema - expect(stmt.declaredTypes).toEqual(['INTEGER', 'TEXT']); + expect(stmt.declaredTypes).toEqual(["INTEGER", "TEXT"]); }); it("reports actual column types based on data values", () => { @@ -82,24 +82,24 @@ describe("SQLite Statement column types", () => { // SQLite can store various types in the same column db.run(`INSERT INTO dynamic_types VALUES (1, 42)`); - + let stmt = db.prepare("SELECT * FROM dynamic_types"); - + // Execute the statement to get column types let row = stmt.get(); - + // We should get the actual type of the value (integer) - expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "INTEGER"]); + // Update to a text value db.run(`UPDATE dynamic_types SET value = 'text' WHERE id = 1`); - + // Re-prepare to get fresh column type information stmt = db.prepare("SELECT * FROM dynamic_types"); row = stmt.get(); - + // We should get the actual type of the value (text) - expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT']); + expect(stmt.columnTypes).toEqual(["INTEGER", "TEXT"]); // Update to a float value db.run(`UPDATE dynamic_types SET value = 3.14 WHERE id = 1`); @@ -109,7 +109,7 @@ describe("SQLite Statement column types", () => { row = stmt.get(); // We should get the actual type of the value (float) - expect(stmt.columnTypes).toEqual(['INTEGER', 'FLOAT']); + expect(stmt.columnTypes).toEqual(["INTEGER", "FLOAT"]); }); it("reports actual types for columns from expressions", () => { @@ -124,14 +124,14 @@ describe("SQLite Statement column types", () => { expect(row).toEqual({ str_length: 3, magic_number: 42, - greeting: "hello" + greeting: "hello", }); // Check columns are correctly identified - expect(stmt.native.columns).toEqual(['str_length', 'magic_number', 'greeting']); - + expect(stmt.native.columns).toEqual(["str_length", "magic_number", "greeting"]); + // For expressions, expect the actual data types - expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER', 'TEXT']); + expect(stmt.columnTypes).toEqual(["INTEGER", "INTEGER", "TEXT"]); }); it("handles multiple different expressions and functions", () => { @@ -148,33 +148,31 @@ describe("SQLite Statement column types", () => { length('bun') AS func_result, CURRENT_TIMESTAMP AS timestamp `); - + const row = stmt.get(); // Verify we have the expected columns expect(stmt.native.columns).toEqual([ - 'int_val', - 'float_val', - 'text_val', - 'blob_val', - 'null_val', - 'func_result', - 'timestamp' + "int_val", + "float_val", + "text_val", + "blob_val", + "null_val", + "func_result", + "timestamp", ]); // Expression columns should be reported with their actual types - expect(stmt.columnTypes).toEqual([ - 'INTEGER', 'FLOAT', 'TEXT', 'BLOB', 'NULL', 'INTEGER', 'TEXT' - ]); + expect(stmt.columnTypes).toEqual(["INTEGER", "FLOAT", "TEXT", "BLOB", "NULL", "INTEGER", "TEXT"]); // Verify data types were correctly identified at runtime - expect(typeof row.int_val).toBe('number'); - expect(typeof row.float_val).toBe('number'); - expect(typeof row.text_val).toBe('string'); + expect(typeof row.int_val).toBe("number"); + expect(typeof row.float_val).toBe("number"); + expect(typeof row.text_val).toBe("string"); expect(row.blob_val instanceof Uint8Array).toBe(true); expect(row.null_val).toBe(null); - expect(typeof row.func_result).toBe('number'); - expect(typeof row.timestamp).toBe('string'); + expect(typeof row.func_result).toBe("number"); + expect(typeof row.timestamp).toBe("string"); }); it("shows difference between columnTypes and declaredTypes for expressions", () => { @@ -185,8 +183,8 @@ describe("SQLite Statement column types", () => { const row = stmt.get(); // columnTypes shows actual data types based on the values - expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER', 'TEXT']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "INTEGER", "TEXT"]); + // declaredTypes shows declared types (which are null for expressions without explicit declarations) expect(stmt.declaredTypes).toEqual([null, null, null]); }); @@ -203,27 +201,27 @@ describe("SQLite Statement column types", () => { // Insert an integer value db.run(`INSERT INTO dynamic_types VALUES (1, 42)`); - + let stmt = db.prepare("SELECT * FROM dynamic_types"); let row = stmt.get(); - + // columnTypes shows actual type (integer) for the current value - expect(stmt.columnTypes).toEqual(['INTEGER', 'INTEGER']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "INTEGER"]); + // declaredTypes shows the declared table schema - expect(stmt.declaredTypes).toEqual(['INTEGER', 'ANY']); - + expect(stmt.declaredTypes).toEqual(["INTEGER", "ANY"]); + // Update to a text value db.run(`UPDATE dynamic_types SET value = 'text' WHERE id = 1`); - + stmt = db.prepare("SELECT * FROM dynamic_types"); row = stmt.get(); - + // columnTypes now shows text for the current value - expect(stmt.columnTypes).toEqual(['INTEGER', 'TEXT']); - + expect(stmt.columnTypes).toEqual(["INTEGER", "TEXT"]); + // declaredTypes still shows the declared table schema - expect(stmt.declaredTypes).toEqual(['INTEGER', 'ANY']); + expect(stmt.declaredTypes).toEqual(["INTEGER", "ANY"]); }); it("throws an error when accessing columnTypes before statement execution", () => { @@ -235,7 +233,7 @@ describe("SQLite Statement column types", () => { // Accessing columnTypes before executing is fine (implicitly executes the statement) expect(stmt.columnTypes).toBeArray(); - + // Accessing declaredTypes before executing should throw expect(() => { stmt.declaredTypes; From 6b4662ff55f58247cc2fd22e85b4f9805b0950a5 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 13 Jun 2025 15:14:57 -0800 Subject: [PATCH 013/133] zig: .jsUndefined -> .js_undefined (#20377) Co-authored-by: nektro <5464072+nektro@users.noreply.github.com> --- src/OutputFile.zig | 2 +- src/bake/FrameworkRouter.zig | 2 +- src/bun.js/ConsoleObject.zig | 10 +- src/bun.js/ResolveMessage.zig | 2 +- src/bun.js/VirtualMachine.zig | 4 +- src/bun.js/api/BunObject.zig | 28 +-- src/bun.js/api/JSBundler.zig | 2 +- src/bun.js/api/Timer.zig | 20 +- src/bun.js/api/UnsafeObject.zig | 2 +- src/bun.js/api/bun/dns_resolver.zig | 14 +- src/bun.js/api/bun/h2_frame_parser.zig | 64 +++--- src/bun.js/api/bun/socket.zig | 216 +++++++++--------- src/bun.js/api/bun/socket/SocketAddress.zig | 6 +- src/bun.js/api/bun/subprocess.zig | 26 +-- src/bun.js/api/bun/udp_socket.zig | 22 +- src/bun.js/api/ffi.zig | 10 +- src/bun.js/api/glob.zig | 4 +- src/bun.js/api/html_rewriter.zig | 48 ++-- src/bun.js/api/server.zig | 32 +-- src/bun.js/api/server/NodeHTTPResponse.zig | 68 +++--- src/bun.js/api/server/RequestContext.zig | 12 +- src/bun.js/api/server/ServerWebSocket.zig | 24 +- .../api/server/WebSocketServerContext.zig | 2 +- src/bun.js/bindings/CallFrame.zig | 8 +- src/bun.js/bindings/JSGlobalObject.zig | 2 +- src/bun.js/bindings/JSPromise.zig | 2 +- src/bun.js/bindings/JSValue.zig | 28 +-- src/bun.js/ipc.zig | 24 +- src/bun.js/node.zig | 2 +- src/bun.js/node/net/BlockList.zig | 6 +- src/bun.js/node/node_cluster_binding.zig | 10 +- src/bun.js/node/node_crypto_binding.zig | 26 +-- src/bun.js/node/node_fs.zig | 42 ++-- src/bun.js/node/node_fs_binding.zig | 2 +- src/bun.js/node/node_fs_stat_watcher.zig | 12 +- src/bun.js/node/node_fs_watcher.zig | 10 +- src/bun.js/node/node_util_binding.zig | 4 +- src/bun.js/node/node_zlib_binding.zig | 12 +- src/bun.js/node/path.zig | 20 +- src/bun.js/node/types.zig | 4 +- src/bun.js/node/util/parse_args.zig | 18 +- src/bun.js/node/zlib/NativeBrotli.zig | 2 +- src/bun.js/node/zlib/NativeZlib.zig | 4 +- src/bun.js/node/zlib/NativeZstd.zig | 2 +- src/bun.js/test/expect.zig | 188 +++++++-------- src/bun.js/test/jest.zig | 32 +-- src/bun.js/test/pretty_format.zig | 6 +- src/bun.js/virtual_machine_exports.zig | 2 +- src/bun.js/webcore/Blob.zig | 10 +- src/bun.js/webcore/Body.zig | 8 +- src/bun.js/webcore/Crypto.zig | 4 +- src/bun.js/webcore/FileReader.zig | 2 +- src/bun.js/webcore/FileSink.zig | 2 +- src/bun.js/webcore/ObjectURLRegistry.zig | 12 +- src/bun.js/webcore/ReadableStream.zig | 16 +- src/bun.js/webcore/Request.zig | 6 +- src/bun.js/webcore/Response.zig | 10 +- src/bun.js/webcore/S3File.zig | 2 +- src/bun.js/webcore/Sink.zig | 6 +- src/bun.js/webcore/TextEncoder.zig | 2 +- src/bun.js/webcore/fetch.zig | 8 +- src/bun.js/webcore/prompt.zig | 6 +- src/bun.js/webcore/streams.zig | 4 +- src/bun_js.zig | 8 +- src/cli/upgrade_command.zig | 10 +- src/crash_handler.zig | 6 +- src/css/css_internals.zig | 2 +- src/deps/c_ares.zig | 14 +- src/deps/uws/UpgradedDuplex.zig | 8 +- src/install/dependency.zig | 14 +- src/install/install.zig | 6 +- src/js_ast.zig | 2 +- src/logger.zig | 2 +- src/napi/napi.zig | 10 +- src/patch.zig | 10 +- src/s3/client.zig | 4 +- src/shell/ParsedShellScript.zig | 6 +- src/shell/interpreter.zig | 18 +- src/shell/subproc.zig | 6 +- src/sql/postgres.zig | 42 ++-- src/valkey/js_valkey.zig | 12 +- 81 files changed, 680 insertions(+), 686 deletions(-) diff --git a/src/OutputFile.zig b/src/OutputFile.zig index 00c269d7df..1b58c6ef24 100644 --- a/src/OutputFile.zig +++ b/src/OutputFile.zig @@ -321,7 +321,7 @@ pub fn toJS( ) bun.JSC.JSValue { return switch (this.value) { .move, .pending => @panic("Unexpected pending output file"), - .noop => .jsUndefined(), + .noop => .js_undefined, .copy => |copy| brk: { const file_blob = JSC.WebCore.Blob.Store.initFile( if (copy.fd.isValid()) diff --git a/src/bake/FrameworkRouter.zig b/src/bake/FrameworkRouter.zig index 6e5f92a95a..4ff15e5f33 100644 --- a/src/bake/FrameworkRouter.zig +++ b/src/bake/FrameworkRouter.zig @@ -1110,7 +1110,7 @@ pub const JSFrameworkRouter = struct { return global.throwInvalidArguments("Missing options.root", .{}); defer root.deinit(); - var style = try Style.fromJS(try opts.getOptional(global, "style", JSValue) orelse .jsUndefined(), global); + var style = try Style.fromJS(try opts.getOptional(global, "style", JSValue) orelse .js_undefined, global); errdefer style.deinit(); const abs_root = try bun.default_allocator.dupe(u8, bun.strings.withoutTrailingSlash( diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index ebff5fa752..70fa6654e7 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -184,7 +184,7 @@ fn messageWithTypeAndLevel_( // if value is not an object/array/iterable, don't print a table and just print it var tabular_data = vals[0]; if (tabular_data.isObject()) { - const properties: JSValue = if (len >= 2 and vals[1].jsType().isArray()) vals[1] else .jsUndefined(); + const properties: JSValue = if (len >= 2 and vals[1].jsType().isArray()) vals[1] else .js_undefined; var table_printer = TablePrinter.init( global, level, @@ -2606,7 +2606,7 @@ pub const Formatter = struct { } // this case should never happen - return try this.printAs(.Undefined, Writer, writer_, .jsUndefined(), .Cell, enable_ansi_colors); + return try this.printAs(.Undefined, Writer, writer_, .js_undefined, .Cell, enable_ansi_colors); } else if (value.as(bun.api.Timer.TimeoutObject)) |timer| { this.addForNewLine("Timeout(# ) ".len + bun.fmt.fastDigitCount(@as(u64, @intCast(@max(timer.internals.id, 0))))); if (timer.internals.flags.kind == .setInterval) { @@ -2896,12 +2896,12 @@ pub const Formatter = struct { }, .Event => { const event_type_value: JSValue = brk: { - const value_ = value.get_unsafe(this.globalThis, "type") orelse break :brk .jsUndefined(); + const value_ = value.get_unsafe(this.globalThis, "type") orelse break :brk .js_undefined; if (value_.isString()) { break :brk value_; } - break :brk .jsUndefined(); + break :brk .js_undefined; }; const event_type = switch (try EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { @@ -2972,7 +2972,7 @@ pub const Formatter = struct { comptime Output.prettyFmt("data: ", enable_ansi_colors), .{}, ); - const data: JSValue = (try value.fastGet(this.globalThis, .data)) orelse .jsUndefined(); + const data: JSValue = (try value.fastGet(this.globalThis, .data)) orelse .js_undefined; const tag = Tag.getAdvanced(data, this.globalThis, .{ .hide_global = true, .disable_inspect_custom = this.disable_inspect_custom, diff --git a/src/bun.js/ResolveMessage.zig b/src/bun.js/ResolveMessage.zig index 8d059517db..be15e7e263 100644 --- a/src/bun.js/ResolveMessage.zig +++ b/src/bun.js/ResolveMessage.zig @@ -61,7 +61,7 @@ pub const ResolveMessage = struct { defer atom.deref(); return atom.toJS(globalObject); }, - else => return .jsUndefined(), + else => return .js_undefined, } } diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index ea609ac8c1..17988dc036 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -1686,7 +1686,7 @@ pub const main_file_name: string = "bun:main"; pub export fn Bun__drainMicrotasksFromJS(globalObject: *JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSValue { _ = callframe; // autofix globalObject.bunVM().drainMicrotasks(); - return .jsUndefined(); + return .js_undefined; } pub fn drainMicrotasks(this: *VirtualMachine) void { @@ -2345,7 +2345,7 @@ fn printErrorFromMaybePrivateData( pub fn reportUncaughtException(globalObject: *JSGlobalObject, exception: *Exception) JSValue { var jsc_vm = globalObject.bunVM(); _ = jsc_vm.uncaughtException(globalObject, exception.value(), false); - return .jsUndefined(); + return .js_undefined; } pub fn printStackTrace(comptime Writer: type, writer: Writer, trace: ZigStackTrace, comptime allow_ansi_colors: bool) !void { diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index be6a5d5a07..22901fde80 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -356,7 +356,7 @@ pub fn inspectTable(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) if (!arguments[1].isArray()) { arguments[2] = arguments[1]; - arguments[1] = .jsUndefined(); + arguments[1] = .js_undefined; } var formatOptions = ConsoleObject.FormatOptions{ @@ -380,7 +380,7 @@ pub fn inspectTable(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) const writer = buffered_writer.writer(); const Writer = @TypeOf(writer); - const properties: JSValue = if (arguments[1].jsType().isArray()) arguments[1] else .jsUndefined(); + const properties: JSValue = if (arguments[1].jsType().isArray()) arguments[1] else .js_undefined; var table_printer = ConsoleObject.TablePrinter.init( globalThis, .Log, @@ -527,7 +527,7 @@ pub fn registerMacro(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFram arguments[1].protect(); get_or_put_result.value_ptr.* = arguments[1].asObjectRef(); - return .jsUndefined(); + return .js_undefined; } pub fn getCWD(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue { @@ -694,7 +694,7 @@ pub fn openInEditor(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) return globalThis.throw("Opening editor failed {s}", .{@errorName(err)}); }; - return .jsUndefined(); + return .js_undefined; } pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void { @@ -768,7 +768,7 @@ pub fn sleepSync(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) b } std.time.sleep(@as(u64, @intCast(milliseconds)) * std.time.ns_per_ms); - return .jsUndefined(); + return .js_undefined; } pub fn gc(vm: *JSC.VirtualMachine, sync: bool) usize { @@ -780,7 +780,7 @@ export fn Bun__gc(vm: *JSC.VirtualMachine, sync: bool) callconv(.C) usize { pub fn shrink(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { globalObject.vm().shrinkFootprint(); - return .jsUndefined(); + return .js_undefined; } fn doResolve(globalThis: *JSC.JSGlobalObject, arguments: []const JSValue) bun.JSError!JSC.JSValue { @@ -1043,22 +1043,22 @@ pub fn serve(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.J @field(@TypeOf(entry.tag()), @typeName(JSC.API.HTTPServer)) => { var server: *JSC.API.HTTPServer = entry.as(JSC.API.HTTPServer); server.onReloadFromZig(&config, globalObject); - return server.js_value.get() orelse .jsUndefined(); + return server.js_value.get() orelse .js_undefined; }, @field(@TypeOf(entry.tag()), @typeName(JSC.API.DebugHTTPServer)) => { var server: *JSC.API.DebugHTTPServer = entry.as(JSC.API.DebugHTTPServer); server.onReloadFromZig(&config, globalObject); - return server.js_value.get() orelse .jsUndefined(); + return server.js_value.get() orelse .js_undefined; }, @field(@TypeOf(entry.tag()), @typeName(JSC.API.DebugHTTPSServer)) => { var server: *JSC.API.DebugHTTPSServer = entry.as(JSC.API.DebugHTTPSServer); server.onReloadFromZig(&config, globalObject); - return server.js_value.get() orelse .jsUndefined(); + return server.js_value.get() orelse .js_undefined; }, @field(@TypeOf(entry.tag()), @typeName(JSC.API.HTTPSServer)) => { var server: *JSC.API.HTTPSServer = entry.as(JSC.API.HTTPSServer); server.onReloadFromZig(&config, globalObject); - return server.js_value.get() orelse .jsUndefined(); + return server.js_value.get() orelse .js_undefined; }, else => {}, } @@ -1288,7 +1288,7 @@ pub fn getS3DefaultClient(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC } pub fn getValkeyDefaultClient(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue { - const valkey = JSC.API.Valkey.create(globalThis, &.{.jsUndefined()}) 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; @@ -1459,7 +1459,7 @@ pub const JSZlib = struct { // This has to be `inline` due to the callframe. inline fn getOptions(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!struct { JSC.Node.StringOrBuffer, ?JSValue } { const arguments = callframe.arguments_old(2).slice(); - const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); + const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; const options_val: ?JSValue = if (arguments.len > 1 and arguments[1].isObject()) arguments[1] @@ -1731,7 +1731,7 @@ pub const JSZstd = struct { inline fn getOptions(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!struct { JSC.Node.StringOrBuffer, ?JSValue } { const arguments = callframe.arguments(); - const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); + const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; const options_val: ?JSValue = if (arguments.len > 1 and arguments[1].isObject()) arguments[1] @@ -1765,7 +1765,7 @@ pub const JSZstd = struct { inline fn getOptionsAsync(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!struct { JSC.Node.StringOrBuffer, ?JSValue, i32 } { const arguments = callframe.arguments(); - const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); + const buffer_value: JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; const options_val: ?JSValue = if (arguments.len > 1 and arguments[1].isObject()) arguments[1] diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index eed081bb05..66da89ea28 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -88,7 +88,7 @@ pub const JSBundler = struct { if (try config.getArray(globalThis, "plugins")) |array| { const length = array.getLength(globalThis); var iter = array.arrayIterator(globalThis); - var onstart_promise_array: JSValue = .jsUndefined(); + var onstart_promise_array: JSValue = .js_undefined; var i: usize = 0; while (iter.next()) |plugin| : (i += 1) { if (!plugin.isObject()) { diff --git a/src/bun.js/api/Timer.zig b/src/bun.js/api/Timer.zig index 41429661c7..7e21663a13 100644 --- a/src/bun.js/api/Timer.zig +++ b/src/bun.js/api/Timer.zig @@ -330,8 +330,8 @@ pub const All = struct { globalThis.emitWarning( warning_string.transferToJS(globalThis), warning_type_string.transferToJS(globalThis), - .jsUndefined(), - .jsUndefined(), + .js_undefined, + .js_undefined, ) catch unreachable; } @@ -390,7 +390,7 @@ pub const All = struct { const countdown_int = try vm.timer.jsValueToCountdown(global, countdown, .clamp, true); const wrapped_promise = promise.withAsyncContextIfNeeded(global); - return TimeoutObject.init(global, id, .setTimeout, countdown_int, wrapped_promise, .jsUndefined()); + return TimeoutObject.init(global, id, .setTimeout, countdown_int, wrapped_promise, .js_undefined); } pub fn setImmediate( @@ -517,7 +517,7 @@ pub const All = struct { ) JSError!JSValue { JSC.markBinding(@src()); try clearTimer(id, globalThis, .setImmediate); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn clearTimeout( globalThis: *JSGlobalObject, @@ -525,7 +525,7 @@ pub const All = struct { ) JSError!JSValue { JSC.markBinding(@src()); try clearTimer(id, globalThis, .setTimeout); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn clearInterval( globalThis: *JSGlobalObject, @@ -533,7 +533,7 @@ pub const All = struct { ) JSError!JSValue { JSC.markBinding(@src()); try clearTimer(id, globalThis, .setInterval); - return JSValue.jsUndefined(); + return .js_undefined; } comptime { @@ -671,7 +671,7 @@ pub const TimeoutObject = struct { pub fn dispose(this: *TimeoutObject, globalThis: *JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.internals.cancel(globalThis.bunVM()); - return .jsUndefined(); + return .js_undefined; } }; @@ -766,7 +766,7 @@ pub const ImmediateObject = struct { pub fn dispose(this: *ImmediateObject, globalThis: *JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.internals.cancel(globalThis.bunVM()); - return .jsUndefined(); + return .js_undefined; } }; @@ -891,8 +891,8 @@ pub const TimerObjectInternals = struct { .setImmediate => .{ ImmediateObject.js.callbackGetCached(this_object).?, ImmediateObject.js.argumentsGetCached(this_object).?, - .jsUndefined(), - .jsUndefined(), + .js_undefined, + .js_undefined, }, .setTimeout, .setInterval => .{ TimeoutObject.js.callbackGetCached(this_object).?, diff --git a/src/bun.js/api/UnsafeObject.zig b/src/bun.js/api/UnsafeObject.zig index 1f7dcddaef..a9f66fb954 100644 --- a/src/bun.js/api/UnsafeObject.zig +++ b/src/bun.js/api/UnsafeObject.zig @@ -64,7 +64,7 @@ fn dump_mimalloc(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSErr if (bun.heap_breakdown.enabled) { dump_zone_malloc_stats(); } - return .jsUndefined(); + return .js_undefined; } const JSC = bun.JSC; diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index 4b071ae9f4..f7bfc86f29 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -1787,7 +1787,7 @@ pub const InternalDNS = struct { }; prefetch(JSC.VirtualMachine.get().uwsLoop(), hostname_z, port); - return .jsUndefined(); + return .js_undefined; } pub fn prefetch(loop: *bun.uws.Loop, hostname: ?[:0]const u8, port: u16) void { @@ -2762,7 +2762,7 @@ pub const DNSResolver = struct { options = GetAddrInfo.Options.fromJS(optionsObject, globalThis) catch |err| { return switch (err) { - error.InvalidFlags => globalThis.throwInvalidArgumentValue("flags", try optionsObject.getTruthy(globalThis, "flags") orelse .jsUndefined()), + error.InvalidFlags => globalThis.throwInvalidArgumentValue("flags", try optionsObject.getTruthy(globalThis, "flags") orelse .js_undefined), error.JSError => |exception| exception, error.OutOfMemory => |oom| oom, @@ -3245,13 +3245,13 @@ pub const DNSResolver = struct { const first_af = try setChannelLocalAddress(channel, globalThis, arguments[0]); if (arguments.len < 2 or arguments[1].isUndefined()) { - return .jsUndefined(); + return .js_undefined; } const second_af = try setChannelLocalAddress(channel, globalThis, arguments[1]); if (first_af != second_af) { - return .jsUndefined(); + return .js_undefined; } switch (first_af) { @@ -3311,7 +3311,7 @@ pub const DNSResolver = struct { const err = c_ares.Error.get(r).?; return globalThis.throwValue(globalThis.createErrorInstance("ares_set_servers_ports error: {s}", .{err.label()})); } - return .jsUndefined(); + return .js_undefined; } const allocator = bun.default_allocator; @@ -3370,7 +3370,7 @@ pub const DNSResolver = struct { return globalThis.throwValue(globalThis.createErrorInstance("ares_set_servers_ports error: {s}", .{err.label()})); } - return .jsUndefined(); + return .js_undefined; } pub fn setGlobalServers(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -3402,7 +3402,7 @@ pub const DNSResolver = struct { _ = callframe; const channel = try this.getChannelOrError(globalThis); c_ares.ares_cancel(channel); - return .jsUndefined(); + return .js_undefined; } // Resolves the given address and port into a host name and service using the operating system's underlying getnameinfo implementation. diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index 40362f34c2..5e6e4305d7 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -419,7 +419,7 @@ pub fn jsAssertSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallF } } } - return .jsUndefined(); + return .js_undefined; } pub fn jsGetPackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -556,7 +556,7 @@ const Handlers = struct { pub fn callWriteCallback(this: *Handlers, callback: JSC.JSValue, data: []const JSValue) bool { if (!callback.isCallable()) return false; - this.vm.eventLoop().runCallback(callback, this.globalObject, .jsUndefined(), data); + this.vm.eventLoop().runCallback(callback, this.globalObject, .js_undefined, data); return true; } @@ -1888,7 +1888,7 @@ pub const H2FrameParser = struct { const headers = try JSC.JSValue.createEmptyArray(globalObject, 0); headers.ensureStillAlive(); - var sensitiveHeaders = JSC.JSValue.jsUndefined(); + var sensitiveHeaders: JSValue = .js_undefined; var count: usize = 0; while (true) { @@ -2075,7 +2075,7 @@ pub const H2FrameParser = struct { } if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| { var payload = content.data; - var originValue = JSC.JSValue.jsUndefined(); + var originValue: JSValue = .js_undefined; var count: usize = 0; this.readBuffer.reset(); @@ -2609,7 +2609,7 @@ pub const H2FrameParser = struct { return globalObject.throwValue(err); }; - return .jsUndefined(); + return .js_undefined; } pub fn loadSettingsFromJSValue(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, options: JSC.JSValue) bun.JSError!void { @@ -2739,7 +2739,7 @@ pub const H2FrameParser = struct { } stream.windowSize = windowSizeValue; } - return .jsUndefined(); + return .js_undefined; } pub fn getCurrentState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { @@ -2794,14 +2794,14 @@ pub const H2FrameParser = struct { if (opaque_data_arg.asArrayBuffer(globalObject)) |array_buffer| { const slice = array_buffer.byteSlice(); this.sendGoAway(0, @enumFromInt(errorCode), slice, lastStreamID, false); - return .jsUndefined(); + return .js_undefined; } } } } this.sendGoAway(0, @enumFromInt(errorCode), "", lastStreamID, false); - return .jsUndefined(); + return .js_undefined; } pub fn ping(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -2819,7 +2819,7 @@ pub const H2FrameParser = struct { if (args_list.ptr[0].asArrayBuffer(globalObject)) |array_buffer| { const slice = array_buffer.slice(); this.sendPing(false, slice); - return .jsUndefined(); + return .js_undefined; } return globalObject.throw("Expected payload to be a Buffer", .{}); @@ -2843,7 +2843,7 @@ pub const H2FrameParser = struct { }; _ = frame.write(@TypeOf(writer), writer); _ = this.write(&buffer); - return .jsUndefined(); + return .js_undefined; } if (origin_arg.isString()) { @@ -2909,7 +2909,7 @@ pub const H2FrameParser = struct { _ = frame.write(@TypeOf(writer), writer); _ = this.write(buffer[0..total_length]); } - return .jsUndefined(); + return .js_undefined; } pub fn altsvc(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -2957,11 +2957,11 @@ pub const H2FrameParser = struct { if (stream_id > 0) { // dont error but dont send frame to invalid stream id _ = this.streams.getPtr(stream_id) orelse { - return .jsUndefined(); + return .js_undefined; }; } this.sendAltSvc(stream_id, origin_str, value_str); - return .jsUndefined(); + return .js_undefined; } pub fn getEndAfterHeaders(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -3271,7 +3271,7 @@ pub const H2FrameParser = struct { enqueued = true; // write the full frame in memory and queue the frame // the callback will only be called after the last frame is sended - stream.queueFrame(this, slice, if (offset >= payload.len) callback else JSC.JSValue.jsUndefined(), offset >= payload.len and close); + stream.queueFrame(this, slice, if (offset >= payload.len) callback else .js_undefined, offset >= payload.len and close); } else { const padding = stream.getPadding(size, max_size - 1); const payload_size = size + (if (padding != 0) padding + 1 else 0); @@ -3321,7 +3321,7 @@ pub const H2FrameParser = struct { }; stream.waitForTrailers = false; - this.sendData(stream, "", true, JSC.JSValue.jsUndefined()); + this.sendData(stream, "", true, .js_undefined); const identifier = stream.getIdentifier(); identifier.ensureStillAlive(); @@ -3332,7 +3332,7 @@ pub const H2FrameParser = struct { stream.state = .HALF_CLOSED_LOCAL; } this.dispatchWithExtra(.onStreamEnd, identifier, JSC.JSValue.jsNumber(@intFromEnum(stream.state))); - return .jsUndefined(); + return .js_undefined; } /// validate header name and convert to lowecase if needed fn toValidHeaderName(in: []const u8, out: []u8) ![]const u8 { @@ -3488,7 +3488,7 @@ pub const H2FrameParser = struct { JSC.JSValue.jsNumber(@intFromEnum(ErrorCode.FRAME_SIZE_ERROR)), ); this.dispatchWithExtra(.onStreamError, identifier, JSC.JSValue.jsNumber(stream.rstCode)); - return .jsUndefined(); + return .js_undefined; }; } } else { @@ -3527,7 +3527,7 @@ pub const H2FrameParser = struct { this.dispatchWithExtra(.onStreamError, identifier, JSC.JSValue.jsNumber(stream.rstCode)); - return .jsUndefined(); + return .js_undefined; }; } } @@ -3552,7 +3552,7 @@ pub const H2FrameParser = struct { stream.state = .HALF_CLOSED_LOCAL; } this.dispatchWithExtra(.onStreamEnd, identifier, JSC.JSValue.jsNumber(@intFromEnum(stream.state))); - return .jsUndefined(); + return .js_undefined; } pub fn writeStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); @@ -3649,7 +3649,7 @@ pub const H2FrameParser = struct { this.lastStreamID -= 2; } } - return .jsUndefined(); + return .js_undefined; } pub fn hasNativeRead(this: *H2FrameParser, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { @@ -3686,7 +3686,7 @@ pub const H2FrameParser = struct { return globalObject.throw("Invalid stream id", .{}); }; - return stream.jsContext.get() orelse .jsUndefined(); + return stream.jsContext.get() orelse .js_undefined; } pub fn setStreamContext(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -3709,17 +3709,17 @@ pub const H2FrameParser = struct { } stream.setContext(context_arg, globalObject); - return .jsUndefined(); + return .js_undefined; } pub fn forEachStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { JSC.markBinding(@src()); const args = callframe.arguments(); if (args.len < 1 or !args[0].isCallable()) { - return .jsUndefined(); + return .js_undefined; } const callback = args[0]; - const thisValue: JSValue = if (args.len > 1) args[1] else .jsUndefined(); + const thisValue: JSValue = if (args.len > 1) args[1] else .js_undefined; var count: u32 = 0; var it = this.streams.valueIterator(); while (it.next()) |stream| { @@ -3727,7 +3727,7 @@ pub const H2FrameParser = struct { this.handlers.vm.eventLoop().runCallback(callback, globalObject, thisValue, &[_]JSC.JSValue{value}); count += 1; } - return .jsUndefined(); + return .js_undefined; } pub fn emitAbortToAllStreams(this: *H2FrameParser, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -3746,10 +3746,10 @@ pub const H2FrameParser = struct { const identifier = stream.getIdentifier(); identifier.ensureStillAlive(); stream.freeResources(this, false); - this.dispatchWith2Extra(.onAborted, identifier, .jsUndefined(), JSC.JSValue.jsNumber(@intFromEnum(old_state))); + this.dispatchWith2Extra(.onAborted, identifier, .js_undefined, JSC.JSValue.jsNumber(@intFromEnum(old_state))); } } - return .jsUndefined(); + return .js_undefined; } pub fn emitErrorToAllStreams(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -3774,7 +3774,7 @@ pub const H2FrameParser = struct { this.dispatchWithExtra(.onStreamError, identifier, args_list.ptr[0]); } } - return .jsUndefined(); + return .js_undefined; } pub fn flushFromJS(this: *H2FrameParser, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { @@ -3932,7 +3932,7 @@ pub const H2FrameParser = struct { stream.state = .CLOSED; stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR); this.dispatchWithExtra(.onStreamError, stream.getIdentifier(), JSC.JSValue.jsNumber(stream.rstCode)); - return .jsUndefined(); + return .js_undefined; }; } } else if (!js_value.isEmptyOrUndefinedOrNull()) { @@ -4205,7 +4205,7 @@ pub const H2FrameParser = struct { const result = try this.readBytes(bytes); bytes = bytes[result..]; } - return .jsUndefined(); + return .js_undefined; } return globalObject.throw("Expected data to be a Buffer or ArrayBuffer", .{}); } @@ -4266,7 +4266,7 @@ pub const H2FrameParser = struct { // if we started with non native and go to native we now control the backpressure internally this.has_nonnative_backpressure = false; } - return .jsUndefined(); + return .js_undefined; } pub fn detachNativeSocket(this: *H2FrameParser) void { @@ -4441,7 +4441,7 @@ pub const H2FrameParser = struct { stream.freeResources(this, false); } this.detach(); - return .jsUndefined(); + return .js_undefined; } /// be careful when calling detach be sure that the socket is closed and the parser not accesible anymore /// this function can be called multiple times, it will erase stream info diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 28e8cf32f3..e7b7e533f0 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -538,7 +538,7 @@ pub const Listener = struct { pub fn getData(this: *Listener, _: *JSC.JSGlobalObject) JSValue { log("getData()", .{}); - return this.strong_data.get() orelse JSValue.jsUndefined(); + return this.strong_data.get() orelse .js_undefined; } pub fn setData(this: *Listener, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { @@ -609,7 +609,7 @@ pub const Listener = struct { this.handlers = handlers; // TODO: this is a memory leak this.handlers.protect(); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn listen(globalObject: *JSC.JSGlobalObject, opts: JSValue) bun.JSError!JSValue { @@ -921,12 +921,12 @@ pub const Listener = struct { this.socket_context.?.addServerName(true, server_name, ssl_config.asUSockets()); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn dispose(this: *Listener, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.doStop(true); - return .jsUndefined(); + return .js_undefined; } pub fn stop(this: *Listener, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -935,7 +935,7 @@ pub const Listener = struct { this.doStop(if (arguments.len > 0 and arguments.ptr[0].isBoolean()) arguments.ptr[0].toBoolean() else false); - return .jsUndefined(); + return .js_undefined; } fn doStop(this: *Listener, force_close: bool) void { @@ -1015,7 +1015,7 @@ pub const Listener = struct { pub fn getUnix(this: *Listener, globalObject: *JSC.JSGlobalObject) JSValue { if (this.connection != .unix) { - return JSValue.jsUndefined(); + return .js_undefined; } return ZigString.init(this.connection.unix).withEncoding().toJS(globalObject); @@ -1023,24 +1023,24 @@ pub const Listener = struct { pub fn getHostname(this: *Listener, globalObject: *JSC.JSGlobalObject) JSValue { if (this.connection != .host) { - return JSValue.jsUndefined(); + return .js_undefined; } return ZigString.init(this.connection.host.host).withEncoding().toJS(globalObject); } pub fn getPort(this: *Listener, _: *JSC.JSGlobalObject) JSValue { if (this.connection != .host) { - return JSValue.jsUndefined(); + return .js_undefined; } return JSValue.jsNumber(this.connection.host.port); } pub fn ref(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const this_value = callframe.this(); - if (this.listener == .none) return JSValue.jsUndefined(); + if (this.listener == .none) return .js_undefined; this.poll_ref.ref(globalObject.bunVM()); this.strong_self.set(globalObject, this_value); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn unref(this: *Listener, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { @@ -1048,7 +1048,7 @@ pub const Listener = struct { if (this.handlers.active_connections == 0) { this.strong_self.clearWithoutDeallocation(); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn connect(globalObject: *JSC.JSGlobalObject, opts: JSValue) bun.JSError!JSValue { @@ -1290,7 +1290,7 @@ pub const Listener = struct { pub fn getsockname(this: *Listener, globalThis: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSValue { if (this.listener != .uws) { - return .jsUndefined(); + return .js_undefined; } const out = callFrame.argumentsAsArray(1)[0]; @@ -1298,16 +1298,16 @@ pub const Listener = struct { var buf: [64]u8 = [_]u8{0} ** 64; var text_buf: [512]u8 = undefined; - const address_bytes: []const u8 = socket.getLocalAddress(this.ssl, &buf) catch return .jsUndefined(); + const address_bytes: []const u8 = socket.getLocalAddress(this.ssl, &buf) catch return .js_undefined; const address_zig: std.net.Address = switch (address_bytes.len) { 4 => std.net.Address.initIp4(address_bytes[0..4].*, 0), 16 => std.net.Address.initIp6(address_bytes[0..16].*, 0, 0, 0), - else => return .jsUndefined(), + else => return .js_undefined, }; const family_js = switch (address_bytes.len) { 4 => bun.String.static("IPv4").toJS(globalThis), 16 => bun.String.static("IPv6").toJS(globalThis), - else => return .jsUndefined(), + else => return .js_undefined, }; const address_js = ZigString.init(bun.fmt.formatIp(address_zig, &text_buf) catch unreachable).toJS(globalThis); const port_js: JSValue = .jsNumber(socket.getLocalPort(this.ssl)); @@ -1315,7 +1315,7 @@ pub const Listener = struct { out.put(globalThis, bun.String.static("family"), family_js); out.put(globalThis, bun.String.static("address"), address_js); out.put(globalThis, bun.String.static("port"), port_js); - return .jsUndefined(); + return .js_undefined; } }; @@ -1501,19 +1501,19 @@ fn NewSocket(comptime ssl: bool) type { pub fn resumeFromJS(this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); - if (this.socket.isDetached()) return .jsUndefined(); + if (this.socket.isDetached()) return .js_undefined; log("resume", .{}); // we should not allow pausing/resuming a wrapped socket because a wrapped socket is 2 sockets and this can cause issues if (this.wrapped == .none and this.flags.is_paused) { this.flags.is_paused = !this.socket.resumeStream(); } - return .jsUndefined(); + return .js_undefined; } pub fn pauseFromJS(this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); - if (this.socket.isDetached()) return .jsUndefined(); + if (this.socket.isDetached()) return .js_undefined; log("pause", .{}); // we should not allow pausing/resuming a wrapped socket because a wrapped socket is 2 sockets and this can cause issues @@ -1521,7 +1521,7 @@ fn NewSocket(comptime ssl: bool) type { this.flags.is_paused = this.socket.pauseStream(); } - return .jsUndefined(); + return .js_undefined; } pub fn setKeepAlive(this: *This, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -1972,7 +1972,7 @@ fn NewSocket(comptime ssl: bool) type { const globalObject = handlers.globalObject; const this_value = this.getThisValue(globalObject); - var js_error: JSValue = .jsUndefined(); + var js_error: JSValue = .js_undefined; if (err != 0) { // errors here are always a read error js_error = bun.sys.Error.fromCodeInt(err, .read).toJSC(globalObject); @@ -2019,7 +2019,7 @@ fn NewSocket(comptime ssl: bool) type { pub fn getData(_: *This, _: *JSC.JSGlobalObject) JSValue { log("getData()", .{}); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn setData(this: *This, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { @@ -2029,11 +2029,11 @@ fn NewSocket(comptime ssl: bool) type { pub fn getListener(this: *This, _: *JSC.JSGlobalObject) JSValue { if (!this.handlers.is_server or this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } const l: *Listener = @fieldParentPtr("handlers", this.handlers); - return l.strong_self.get() orelse JSValue.jsUndefined(); + return l.strong_self.get() orelse .js_undefined; } pub fn getReadyState(this: *This, _: *JSC.JSGlobalObject) JSValue { @@ -2058,7 +2058,7 @@ fn NewSocket(comptime ssl: bool) type { pub fn timeout(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); const args = callframe.arguments_old(1); - if (this.socket.isDetached()) return JSValue.jsUndefined(); + if (this.socket.isDetached()) return .js_undefined; if (args.len == 0) { return globalObject.throw("Expected 1 argument, got 0", .{}); } @@ -2070,7 +2070,7 @@ fn NewSocket(comptime ssl: bool) type { this.socket.setTimeout(@as(c_uint, @intCast(t))); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getAuthorizationError(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { @@ -2116,31 +2116,31 @@ fn NewSocket(comptime ssl: bool) type { pub fn getLocalFamily(this: *This, globalThis: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } var buf: [64]u8 = [_]u8{0} ** 64; - const address_bytes: []const u8 = this.socket.localAddress(&buf) orelse return JSValue.jsUndefined(); + const address_bytes: []const u8 = this.socket.localAddress(&buf) orelse return .js_undefined; return switch (address_bytes.len) { 4 => bun.String.static("IPv4").toJS(globalThis), 16 => bun.String.static("IPv6").toJS(globalThis), - else => return JSValue.jsUndefined(), + else => return .js_undefined, }; } pub fn getLocalAddress(this: *This, globalThis: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } var buf: [64]u8 = [_]u8{0} ** 64; var text_buf: [512]u8 = undefined; - const address_bytes: []const u8 = this.socket.localAddress(&buf) orelse return JSValue.jsUndefined(); + const address_bytes: []const u8 = this.socket.localAddress(&buf) orelse return .js_undefined; const address: std.net.Address = switch (address_bytes.len) { 4 => std.net.Address.initIp4(address_bytes[0..4].*, 0), 16 => std.net.Address.initIp6(address_bytes[0..16].*, 0, 0, 0), - else => return JSValue.jsUndefined(), + else => return .js_undefined, }; const text = bun.fmt.formatIp(address, &text_buf) catch unreachable; @@ -2149,7 +2149,7 @@ fn NewSocket(comptime ssl: bool) type { pub fn getLocalPort(this: *This, _: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } return JSValue.jsNumber(this.socket.localPort()); @@ -2157,31 +2157,31 @@ fn NewSocket(comptime ssl: bool) type { pub fn getRemoteFamily(this: *This, globalThis: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } var buf: [64]u8 = [_]u8{0} ** 64; - const address_bytes: []const u8 = this.socket.remoteAddress(&buf) orelse return JSValue.jsUndefined(); + const address_bytes: []const u8 = this.socket.remoteAddress(&buf) orelse return .js_undefined; return switch (address_bytes.len) { 4 => bun.String.static("IPv4").toJS(globalThis), 16 => bun.String.static("IPv6").toJS(globalThis), - else => return JSValue.jsUndefined(), + else => return .js_undefined, }; } pub fn getRemoteAddress(this: *This, globalThis: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } var buf: [64]u8 = [_]u8{0} ** 64; var text_buf: [512]u8 = undefined; - const address_bytes: []const u8 = this.socket.remoteAddress(&buf) orelse return JSValue.jsUndefined(); + const address_bytes: []const u8 = this.socket.remoteAddress(&buf) orelse return .js_undefined; const address: std.net.Address = switch (address_bytes.len) { 4 => std.net.Address.initIp4(address_bytes[0..4].*, 0), 16 => std.net.Address.initIp6(address_bytes[0..16].*, 0, 0, 0), - else => return JSValue.jsUndefined(), + else => return .js_undefined, }; const text = bun.fmt.formatIp(address, &text_buf) catch unreachable; @@ -2190,7 +2190,7 @@ fn NewSocket(comptime ssl: bool) type { pub fn getRemotePort(this: *This, _: *JSC.JSGlobalObject) JSValue { if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } return JSValue.jsNumber(this.socket.remotePort()); @@ -2257,7 +2257,7 @@ fn NewSocket(comptime ssl: bool) type { fn writeOrEndBuffered(this: *This, globalObject: *JSC.JSGlobalObject, data_value: JSC.JSValue, encoding_value: JSC.JSValue, comptime is_end: bool) WriteResult { if (this.buffered_data_for_node_net.len == 0) { - var values = [4]JSC.JSValue{ data_value, .jsUndefined(), .jsUndefined(), encoding_value }; + var values = [4]JSC.JSValue{ data_value, .js_undefined, .js_undefined, encoding_value }; return this.writeOrEnd(globalObject, &values, true, is_end); } @@ -2387,10 +2387,10 @@ fn NewSocket(comptime ssl: bool) type { var encoding_value: JSC.JSValue = args[3]; if (args[2].isString()) { encoding_value = args[2]; - args[2] = .jsUndefined(); + args[2] = .js_undefined; } else if (args[1].isString()) { encoding_value = args[1]; - args[1] = .jsUndefined(); + args[1] = .js_undefined; } const offset_value = args[1]; @@ -2552,13 +2552,13 @@ fn NewSocket(comptime ssl: bool) type { pub fn flush(this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); this.internalFlush(); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn terminate(this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); this.closeAndDetach(.failure); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn shutdown(this: *This, _: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -2570,7 +2570,7 @@ fn NewSocket(comptime ssl: bool) type { this.socket.shutdown(); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn close(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -2579,7 +2579,7 @@ fn NewSocket(comptime ssl: bool) type { this.socket.close(.normal); this.socket.detach(); this.poll_ref.unref(globalObject.bunVM()); - return .jsUndefined(); + return .js_undefined; } pub fn end(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -2609,16 +2609,16 @@ fn NewSocket(comptime ssl: bool) type { pub fn jsRef(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); if (this.socket.isDetached()) this.ref_pollref_on_connect = true; - if (this.socket.isDetached()) return JSValue.jsUndefined(); + if (this.socket.isDetached()) return .js_undefined; this.poll_ref.ref(globalObject.bunVM()); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn jsUnref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); if (this.socket.isDetached()) this.ref_pollref_on_connect = false; this.poll_ref.unref(globalObject.bunVM()); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn deinit(this: *This) void { @@ -2670,7 +2670,7 @@ fn NewSocket(comptime ssl: bool) type { } if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } const opts = args.ptr[0]; @@ -2689,24 +2689,24 @@ fn NewSocket(comptime ssl: bool) type { this.handlers.* = handlers; // TODO: this is a memory leak this.handlers.protect(); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn disableRenegotiation(this: *This, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; BoringSSL.SSL_set_renegotiate_mode(ssl_ptr, BoringSSL.ssl_renegotiate_never); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn setVerifyMode(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } const args = callframe.arguments_old(2); @@ -2733,36 +2733,36 @@ fn NewSocket(comptime ssl: bool) type { const ssl_ptr = this.socket.ssl(); // we always allow and check the SSL certificate after the handshake or renegotiation BoringSSL.SSL_set_verify(ssl_ptr, verify_mode, alwaysAllowSSLVerifyCallback); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn renegotiate(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; BoringSSL.ERR_clear_error(); if (BoringSSL.SSL_renegotiate(ssl_ptr) != 1) { return globalObject.throwValue(getSSLException(globalObject, "SSL_renegotiate error")); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getTLSTicket(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); - const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; + const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return .js_undefined; var ticket: [*c]const u8 = undefined; var length: usize = 0; //The pointer is only valid while the connection is in use so we need to copy it BoringSSL.SSL_SESSION_get0_ticket(session, @as([*c][*c]const u8, @ptrCast(&ticket)), &length); if (ticket == null or length == 0) { - return JSValue.jsUndefined(); + return .js_undefined; } return JSC.ArrayBuffer.createBuffer(globalObject, ticket[0..length]); @@ -2770,11 +2770,11 @@ fn NewSocket(comptime ssl: bool) type { pub fn setSession(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } const args = callframe.arguments_old(1); @@ -2792,11 +2792,11 @@ fn NewSocket(comptime ssl: bool) type { const session_slice = sb.slice(); const ssl_ptr = this.socket.ssl(); var tmp = @as([*c]const u8, @ptrCast(session_slice.ptr)); - const session = BoringSSL.d2i_SSL_SESSION(null, &tmp, @as(c_long, @intCast(session_slice.len))) orelse return JSValue.jsUndefined(); + const session = BoringSSL.d2i_SSL_SESSION(null, &tmp, @as(c_long, @intCast(session_slice.len))) orelse return .js_undefined; if (BoringSSL.SSL_set_session(ssl_ptr, session) != 1) { return globalObject.throwValue(getSSLException(globalObject, "SSL_set_session error")); } - return JSValue.jsUndefined(); + return .js_undefined; } else { return globalObject.throw("Expected session to be a string, Buffer or TypedArray", .{}); } @@ -2804,14 +2804,14 @@ fn NewSocket(comptime ssl: bool) type { pub fn getSession(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); - const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; + const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return .js_undefined; const size = BoringSSL.i2d_SSL_SESSION(session, null); if (size <= 0) { - return JSValue.jsUndefined(); + return .js_undefined; } const buffer_size = @as(usize, @intCast(size)); @@ -2854,11 +2854,11 @@ fn NewSocket(comptime ssl: bool) type { pub fn exportKeyingMaterial(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } if (this.socket.isDetached()) { - return JSValue.jsUndefined(); + return .js_undefined; } const args = callframe.arguments_old(3); @@ -2884,7 +2884,7 @@ fn NewSocket(comptime ssl: bool) type { defer label.deinit(); const label_slice = label.slice(); - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; if (args.len > 2) { const context_arg = args.ptr[2]; @@ -2985,10 +2985,10 @@ fn NewSocket(comptime ssl: bool) type { pub fn getCipher(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; const cipher = BoringSSL.SSL_get_current_cipher(ssl_ptr); var result = JSValue.createEmptyObject(globalObject, 3); @@ -3025,10 +3025,10 @@ fn NewSocket(comptime ssl: bool) type { pub fn getTLSPeerFinishedMessage(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; // We cannot just pass nullptr to SSL_get_peer_finished() // because it would further be propagated to memcpy(), // where the standard requirements as described in ISO/IEC 9899:2011 @@ -3036,7 +3036,7 @@ fn NewSocket(comptime ssl: bool) type { // Thus, we use a dummy byte. var dummy: [1]u8 = undefined; const size = BoringSSL.SSL_get_peer_finished(ssl_ptr, @as(*anyopaque, @ptrCast(&dummy)), @sizeOf(@TypeOf(dummy))); - if (size == 0) return JSValue.jsUndefined(); + if (size == 0) return .js_undefined; const buffer_size = @as(usize, @intCast(size)); var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); @@ -3049,10 +3049,10 @@ fn NewSocket(comptime ssl: bool) type { pub fn getTLSFinishedMessage(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; // We cannot just pass nullptr to SSL_get_finished() // because it would further be propagated to memcpy(), // where the standard requirements as described in ISO/IEC 9899:2011 @@ -3060,7 +3060,7 @@ fn NewSocket(comptime ssl: bool) type { // Thus, we use a dummy byte. var dummy: [1]u8 = undefined; const size = BoringSSL.SSL_get_finished(ssl_ptr, @as(*anyopaque, @ptrCast(&dummy)), @sizeOf(@TypeOf(dummy))); - if (size == 0) return JSValue.jsUndefined(); + if (size == 0) return .js_undefined; const buffer_size = @as(usize, @intCast(size)); var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); @@ -3201,7 +3201,7 @@ fn NewSocket(comptime ssl: bool) type { pub fn getPeerCertificate(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { JSC.markBinding(@src()); if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } const args = callframe.arguments_old(1); @@ -3214,7 +3214,7 @@ fn NewSocket(comptime ssl: bool) type { abbreviated = arg.toBoolean(); } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; if (abbreviated) { if (this.handlers.is_server) { @@ -3224,8 +3224,8 @@ fn NewSocket(comptime ssl: bool) type { } } - const cert_chain = BoringSSL.SSL_get_peer_cert_chain(ssl_ptr) orelse return JSValue.jsUndefined(); - const cert = BoringSSL.sk_X509_value(cert_chain, 0) orelse return JSValue.jsUndefined(); + const cert_chain = BoringSSL.SSL_get_peer_cert_chain(ssl_ptr) orelse return .js_undefined; + const cert = BoringSSL.sk_X509_value(cert_chain, 0) orelse return .js_undefined; return X509.toJS(cert, globalObject); } var cert: ?*BoringSSL.X509 = null; @@ -3237,67 +3237,67 @@ fn NewSocket(comptime ssl: bool) type { const first_cert = if (cert) |c| c else if (cert_chain) |cc| BoringSSL.sk_X509_value(cc, 0) else null; if (first_cert == null) { - return JSValue.jsUndefined(); + return .js_undefined; } // TODO: we need to support the non abbreviated version of this - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getCertificate(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; const cert = BoringSSL.SSL_get_certificate(ssl_ptr); if (cert) |x509| { return X509.toJS(x509, globalObject); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getPeerX509Certificate(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; const cert = BoringSSL.SSL_get_peer_certificate(ssl_ptr); if (cert) |x509| { return X509.toJSObject(x509, globalObject); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getX509Certificate(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } - const ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + const ssl_ptr = this.socket.ssl() orelse return .js_undefined; const cert = BoringSSL.SSL_get_certificate(ssl_ptr); if (cert) |x509| { return X509.toJSObject(x509.ref(), globalObject); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn getServername(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } const ssl_ptr = this.socket.ssl(); const servername = BoringSSL.SSL_get_servername(ssl_ptr, BoringSSL.TLSEXT_NAMETYPE_host_name); if (servername == null) { - return JSValue.jsUndefined(); + return .js_undefined; } return ZigString.fromUTF8(servername[0..bun.len(servername)]).toJS(globalObject); } pub fn setServername(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { if (comptime ssl == false) { - return JSValue.jsUndefined(); + return .js_undefined; } if (this.handlers.is_server) { @@ -3324,7 +3324,7 @@ fn NewSocket(comptime ssl: bool) type { const host = normalizeHost(@as([]const u8, slice)); if (host.len > 0) { - var ssl_ptr = this.socket.ssl() orelse return JSValue.jsUndefined(); + var ssl_ptr = this.socket.ssl() orelse return .js_undefined; if (ssl_ptr.isInitFinished()) { // match node.js exceptions @@ -3335,7 +3335,7 @@ fn NewSocket(comptime ssl: bool) type { ssl_ptr.setHostname(host__); } - return JSValue.jsUndefined(); + return .js_undefined; } // this invalidates the current socket returning 2 new sockets @@ -3346,10 +3346,10 @@ fn NewSocket(comptime ssl: bool) type { const this_js = callframe.this(); if (comptime ssl) { - return JSValue.jsUndefined(); + return .js_undefined; } if (this.socket.isDetached() or this.socket.isNamedPipe()) { - return JSValue.jsUndefined(); + return .js_undefined; } const args = callframe.arguments_old(1); @@ -3485,7 +3485,7 @@ fn NewSocket(comptime ssl: bool) type { return globalObject.throw("Failed to upgrade socket from TCP -> TLS. Is the TLS config correct?", .{}); } - return JSValue.jsUndefined(); + return .js_undefined; }; // Do not create the JS Wrapper object until _after_ we've validated the TLS config. @@ -4404,5 +4404,5 @@ pub fn jsSetSocketOptions(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame } } - return JSC.JSValue.jsUndefined(); + return .js_undefined; } diff --git a/src/bun.js/api/bun/socket/SocketAddress.zig b/src/bun.js/api/bun/socket/SocketAddress.zig index 5291152eee..71cd9a4fe4 100644 --- a/src/bun.js/api/bun/socket/SocketAddress.zig +++ b/src/bun.js/api/bun/socket/SocketAddress.zig @@ -114,7 +114,7 @@ pub fn parse(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError }; defer url_str.deref(); - const url = JSC.URL.fromString(url_str) orelse return JSValue.jsUndefined(); + const url = JSC.URL.fromString(url_str) orelse return .js_undefined; defer url.deinit(); const host = url.host(); const port_: u16 = blk: { @@ -129,10 +129,10 @@ pub fn parse(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError // - "0x.0x.0" -> "0.0.0.0" const paddr = host.latin1(); // presentation address const addr = if (paddr[0] == '[' and paddr[paddr.len - 1] == ']') v6: { - const v6 = net.Ip6Address.parse(paddr[1 .. paddr.len - 1], port_) catch return JSValue.jsUndefined(); + const v6 = net.Ip6Address.parse(paddr[1 .. paddr.len - 1], port_) catch return .js_undefined; break :v6 SocketAddress{ ._addr = .{ .sin6 = v6.sa } }; } else v4: { - const v4 = net.Ip4Address.parse(paddr, port_) catch return JSValue.jsUndefined(); + const v4 = net.Ip4Address.parse(paddr, port_) catch return .js_undefined; break :v4 SocketAddress{ ._addr = .{ .sin = v4.sa } }; }; diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index 676463d32c..fcb4307457 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -237,7 +237,7 @@ pub fn createResourceUsageObject(this: *Subprocess, globalObject: *JSGlobalObjec } } - return JSValue.jsUndefined(); + return .js_undefined; }; const resource_usage = ResourceUsage{ @@ -520,7 +520,7 @@ const Readable = union(enum) { return JSC.WebCore.ReadableStream.fromOwnedSlice(globalThis, own, 0); }, else => { - return JSValue.jsUndefined(); + return .js_undefined; }, } } @@ -551,7 +551,7 @@ const Readable = union(enum) { return JSC.MarkedArrayBuffer.fromBytes(own, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); }, else => { - return JSValue.jsUndefined(); + return .js_undefined; }, } } @@ -591,7 +591,7 @@ pub fn asyncDispose( ) bun.JSError!JSValue { if (this.process.hasExited()) { // rely on GC to clean everything up in this case - return .jsUndefined(); + return .js_undefined; } const this_jsvalue = callframe.this(); @@ -699,7 +699,7 @@ pub fn kill( }, } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn hasKilled(this: *const Subprocess) bool { @@ -726,12 +726,12 @@ fn closeProcess(this: *Subprocess) void { pub fn doRef(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.jsRef(); - return .jsUndefined(); + return .js_undefined; } pub fn doUnref(this: *Subprocess, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.jsUnref(); - return .jsUndefined(); + return .js_undefined; } pub fn onStdinDestroyed(this: *Subprocess) void { @@ -761,7 +761,7 @@ pub fn disconnect(this: *Subprocess, globalThis: *JSGlobalObject, callframe: *JS _ = globalThis; _ = callframe; this.disconnectIPC(true); - return .jsUndefined(); + return .js_undefined; } pub fn getConnected(this: *Subprocess, globalThis: *JSGlobalObject) JSValue { @@ -1133,7 +1133,7 @@ pub const PipeReader = struct { return JSC.MarkedArrayBuffer.fromBytes(bytes, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); }, else => { - return .jsUndefined(); + return .js_undefined; }, } } @@ -1391,8 +1391,8 @@ const Writable = union(enum) { pub fn toJS(this: *Writable, globalThis: *JSC.JSGlobalObject, subprocess: *Subprocess) JSValue { return switch (this.*) { .fd => |fd| fd.toJS(globalThis), - .memfd, .ignore => JSValue.jsUndefined(), - .buffer, .inherit => JSValue.jsUndefined(), + .memfd, .ignore => .js_undefined, + .buffer, .inherit => .js_undefined, .pipe => |pipe| { this.* = .{ .ignore = {} }; if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) { @@ -1597,9 +1597,9 @@ pub fn onProcessExit(this: *Subprocess, process: *Process, status: bun.spawn.Sta if (status == .err) status.err.toJSC(globalThis) else - .jsUndefined(); + .js_undefined; - const this_value: JSValue = if (this_jsvalue.isEmptyOrUndefinedOrNull()) .jsUndefined() else this_jsvalue; + const this_value: JSValue = if (this_jsvalue.isEmptyOrUndefinedOrNull()) .js_undefined else this_jsvalue; this_value.ensureStillAlive(); const args = [_]JSValue{ diff --git a/src/bun.js/api/bun/udp_socket.zig b/src/bun.js/api/bun/udp_socket.zig index 47a15e7266..ca7f196f4f 100644 --- a/src/bun.js/api/bun/udp_socket.zig +++ b/src/bun.js/api/bun/udp_socket.zig @@ -801,13 +801,13 @@ pub const UDPSocket = struct { this.poll_ref.ref(globalThis.bunVM()); } - return .jsUndefined(); + return .js_undefined; } pub fn unref(this: *This, globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.poll_ref.unref(globalThis.bunVM()); - return .jsUndefined(); + return .js_undefined; } pub fn close( @@ -817,7 +817,7 @@ pub const UDPSocket = struct { ) bun.JSError!JSValue { if (!this.closed) this.socket.close(); - return .jsUndefined(); + return .js_undefined; } pub fn reload(this: *This, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { @@ -835,7 +835,7 @@ pub const UDPSocket = struct { previous_config.unprotect(); this.config = config; - return .jsUndefined(); + return .js_undefined; } pub fn getClosed(this: *This, _: *JSGlobalObject) JSValue { @@ -848,17 +848,17 @@ pub const UDPSocket = struct { } pub fn getPort(this: *This, _: *JSGlobalObject) JSValue { - if (this.closed) return .jsUndefined(); + if (this.closed) return .js_undefined; return JSValue.jsNumber(this.socket.boundPort()); } fn createSockAddr(globalThis: *JSGlobalObject, address_bytes: []const u8, port: u16) JSValue { - var sockaddr = SocketAddress.init(address_bytes, port) catch return .jsUndefined(); + var sockaddr = SocketAddress.init(address_bytes, port) catch return .js_undefined; return sockaddr.intoDTO(globalThis); } pub fn getAddress(this: *This, globalThis: *JSGlobalObject) JSValue { - if (this.closed) return .jsUndefined(); + if (this.closed) return .js_undefined; var buf: [64]u8 = [_]u8{0} ** 64; var length: i32 = 64; this.socket.boundIp(&buf, &length); @@ -869,8 +869,8 @@ pub const UDPSocket = struct { } pub fn getRemoteAddress(this: *This, globalThis: *JSC.JSGlobalObject) JSC.JSValue { - if (this.closed) return .jsUndefined(); - const connect_info = this.connect_info orelse return .jsUndefined(); + if (this.closed) return .js_undefined; + const connect_info = this.connect_info orelse return .js_undefined; var buf: [64]u8 = [_]u8{0} ** 64; var length: i32 = 64; this.socket.remoteIp(&buf, &length); @@ -948,7 +948,7 @@ pub const UDPSocket = struct { js.addressSetCached(callFrame.this(), globalThis, .zero); js.remoteAddressSetCached(callFrame.this(), globalThis, .zero); - return .jsUndefined(); + return .js_undefined; } pub fn jsDisconnect(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -969,6 +969,6 @@ pub const UDPSocket = struct { } this.connect_info = null; - return .jsUndefined(); + return .js_undefined; } }; diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 11cdc52d42..ff6e0075ad 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -595,7 +595,7 @@ pub const FFI = struct { } } - const symbols_object: JSValue = object.getOwn(globalThis, "symbols") orelse .jsUndefined(); + const symbols_object: JSValue = object.getOwn(globalThis, "symbols") orelse .js_undefined; if (!globalThis.hasException() and (symbols_object == .zero or !symbols_object.isObject())) { return globalThis.throwInvalidArgumentTypeValue("symbols", "object", symbols_object); } @@ -807,7 +807,7 @@ pub const FFI = struct { pub fn closeCallback(globalThis: *JSGlobalObject, ctx: JSValue) JSValue { var function: *Function = @ptrFromInt(ctx.asPtrAddress()); function.deinit(globalThis); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn callback(globalThis: *JSGlobalObject, interface: JSC.JSValue, js_callback: JSC.JSValue) JSValue { @@ -866,7 +866,7 @@ pub const FFI = struct { ) bun.JSError!JSValue { JSC.markBinding(@src()); if (this.closed) { - return .jsUndefined(); + return .js_undefined; } this.closed = true; if (this.dylib) |*dylib| { @@ -894,7 +894,7 @@ pub const FFI = struct { // bun.default_allocator.free(relocated_bytes_to_free); // } - return .jsUndefined(); + return .js_undefined; } pub fn printCallback(global: *JSGlobalObject, object: JSC.JSValue) JSValue { @@ -1143,7 +1143,7 @@ pub const FFI = struct { pub fn getSymbols(_: *FFI, _: *JSC.JSGlobalObject) JSC.JSValue { // This shouldn't be called. The cachedValue is what should be called. - return .jsUndefined(); + return .js_undefined; } pub fn linkSymbols(global: *JSGlobalObject, object_value: JSC.JSValue) JSC.JSValue { diff --git a/src/bun.js/api/glob.zig b/src/bun.js/api/glob.zig index aa063d5d45..8d49134623 100644 --- a/src/bun.js/api/glob.zig +++ b/src/bun.js/api/glob.zig @@ -331,7 +331,7 @@ pub fn __scan(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.CallFram var arena = std.heap.ArenaAllocator.init(alloc); const globWalker = try this.makeGlobWalker(globalThis, &arguments, "scan", alloc, &arena) orelse { arena.deinit(); - return .jsUndefined(); + return .js_undefined; }; incrPendingActivityFlag(&this.has_pending_activity); @@ -354,7 +354,7 @@ pub fn __scanSync(this: *Glob, globalThis: *JSGlobalObject, callframe: *JSC.Call var arena = std.heap.ArenaAllocator.init(alloc); var globWalker = try this.makeGlobWalker(globalThis, &arguments, "scanSync", alloc, &arena) orelse { arena.deinit(); - return .jsUndefined(); + return .js_undefined; }; defer globWalker.deinit(true); diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig index 2e3578c882..c5ef322882 100644 --- a/src/bun.js/api/html_rewriter.zig +++ b/src/bun.js/api/html_rewriter.zig @@ -1093,7 +1093,7 @@ pub const TextChunk = struct { fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { if (this.text_chunk == null) - return .jsUndefined(); + return .js_undefined; var content_slice = content.toSlice(bun.default_allocator); defer content_slice.deinit(); @@ -1146,7 +1146,7 @@ pub const TextChunk = struct { callFrame: *JSC.CallFrame, ) bun.JSError!JSValue { if (this.text_chunk == null) - return JSValue.jsUndefined(); + return .js_undefined; this.text_chunk.?.remove(); return callFrame.this(); } @@ -1156,7 +1156,7 @@ pub const TextChunk = struct { global: *JSGlobalObject, ) JSValue { if (this.text_chunk == null) - return JSValue.jsUndefined(); + return .js_undefined; return ZigString.init(this.text_chunk.?.getContent().slice()).withEncoding().toJS(global); } @@ -1213,7 +1213,7 @@ pub const DocType = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.doctype == null) - return JSValue.jsUndefined(); + return .js_undefined; const str = this.doctype.?.getName().slice(); if (str.len == 0) return JSValue.jsNull(); @@ -1225,7 +1225,7 @@ pub const DocType = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.doctype == null) - return JSValue.jsUndefined(); + return .js_undefined; const str = this.doctype.?.getSystemId().slice(); if (str.len == 0) @@ -1238,7 +1238,7 @@ pub const DocType = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.doctype == null) - return JSValue.jsUndefined(); + return .js_undefined; const str = this.doctype.?.getPublicId().slice(); if (str.len == 0) @@ -1252,7 +1252,7 @@ pub const DocType = struct { callFrame: *JSC.CallFrame, ) bun.JSError!JSValue { if (this.doctype == null) - return JSValue.jsUndefined(); + return .js_undefined; this.doctype.?.remove(); return callFrame.this(); } @@ -1262,7 +1262,7 @@ pub const DocType = struct { _: *JSGlobalObject, ) JSValue { if (this.doctype == null) - return JSValue.jsUndefined(); + return .js_undefined; return JSValue.jsBoolean(this.doctype.?.isRemoved()); } }; @@ -1433,7 +1433,7 @@ pub const Comment = struct { _: *JSGlobalObject, ) JSValue { if (this.comment == null) - return JSValue.jsUndefined(); + return .js_undefined; return JSValue.jsBoolean(this.comment.?.isRemoved()); } @@ -1547,7 +1547,7 @@ pub const EndTag = struct { callFrame: *JSC.CallFrame, ) bun.JSError!JSValue { if (this.end_tag == null) - return JSValue.jsUndefined(); + return .js_undefined; this.end_tag.?.remove(); return callFrame.this(); @@ -1558,7 +1558,7 @@ pub const EndTag = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.end_tag == null) - return JSValue.jsUndefined(); + return .js_undefined; return this.end_tag.?.getName().toJS(globalObject); } @@ -1620,13 +1620,13 @@ pub const AttributeIterator = struct { const value_label = JSC.ZigString.static("value"); if (this.iterator == null) { - return JSValue.createObject2(globalObject, done_label, value_label, JSValue.jsBoolean(true), JSValue.jsUndefined()); + return JSValue.createObject2(globalObject, done_label, value_label, JSValue.jsBoolean(true), .js_undefined); } var attribute = this.iterator.?.next() orelse { this.iterator.?.deinit(); this.iterator = null; - return JSValue.createObject2(globalObject, done_label, value_label, JSValue.jsBoolean(true), JSValue.jsUndefined()); + return JSValue.createObject2(globalObject, done_label, value_label, JSValue.jsBoolean(true), .js_undefined); }; const value = attribute.value(); @@ -1729,7 +1729,7 @@ pub const Element = struct { /// Sets an attribute to a provided value, creating the attribute if it does not exist. pub fn setAttribute_(this: *Element, callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, name_: ZigString, value_: ZigString) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; var name_slice = name_.toSlice(bun.default_allocator); defer name_slice.deinit(); @@ -1743,7 +1743,7 @@ pub const Element = struct { /// Removes the attribute. pub fn removeAttribute_(this: *Element, callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, name: ZigString) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; var name_slice = name.toSlice(bun.default_allocator); defer name_slice.deinit(); @@ -1762,7 +1762,7 @@ pub const Element = struct { fn contentHandler(this: *Element, comptime Callback: (fn (*LOLHTML.Element, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; var content_slice = content.toSlice(bun.default_allocator); defer content_slice.deinit(); @@ -1862,7 +1862,7 @@ pub const Element = struct { callFrame: *JSC.CallFrame, ) bun.JSError!JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; this.element.?.remove(); return callFrame.this(); @@ -1875,14 +1875,14 @@ pub const Element = struct { callFrame: *JSC.CallFrame, ) bun.JSError!JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; this.element.?.removeAndKeepContent(); return callFrame.this(); } pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; return htmlStringValue(this.element.?.tagName(), globalObject); } @@ -1907,7 +1907,7 @@ pub const Element = struct { _: *JSGlobalObject, ) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; return JSValue.jsBoolean(this.element.?.isRemoved()); } @@ -1916,7 +1916,7 @@ pub const Element = struct { _: *JSGlobalObject, ) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; return JSValue.jsBoolean(this.element.?.isSelfClosing()); } @@ -1925,7 +1925,7 @@ pub const Element = struct { _: *JSGlobalObject, ) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; return JSValue.jsBoolean(this.element.?.canHaveContent()); } @@ -1934,7 +1934,7 @@ pub const Element = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; return bun.String.createUTF8ForJS(globalObject, std.mem.span(this.element.?.namespaceURI())); } @@ -1943,7 +1943,7 @@ pub const Element = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.element == null) - return JSValue.jsUndefined(); + return .js_undefined; const iter = this.element.?.attributes() orelse return createLOLHTMLError(globalObject); var attr_iter = bun.new(AttributeIterator, .{ diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index ed925c30fd..50b3bbbaf0 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -361,7 +361,7 @@ const ServePlugins = struct { handleOnResolve(plugins); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn handleOnResolve(this: *ServePlugins) void { @@ -392,7 +392,7 @@ const ServePlugins = struct { const plugins = plugin_js.asPromisePtr(ServePlugins); handleOnReject(plugins, globalThis, error_js); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn handleOnReject(this: *ServePlugins, global: *JSC.JSGlobalObject, err: JSValue) void { @@ -552,7 +552,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d bun.debugAssert(server.listener != null); // this assertion is only valid while listening return server.js_value.get() orelse brk: { bun.debugAssert(false); - break :brk .jsUndefined(); // safe-ish + break :brk .js_undefined; // safe-ish }; } @@ -594,7 +594,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d return this.globalThis.throwInvalidArguments("timeout() requires a Request object", .{}); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn setIdleTimeout(this: *ThisServer, seconds: c_uint) void { @@ -1048,7 +1048,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d this.onReloadFromZig(&new_config, globalThis); - return this.js_value.get() orelse .jsUndefined(); + return this.js_value.get() orelse .js_undefined; } pub fn onFetch( @@ -1196,7 +1196,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d this.stop(true); } - return .jsUndefined(); + return .js_undefined; } pub fn getPort( @@ -1204,7 +1204,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d _: *JSC.JSGlobalObject, ) JSC.JSValue { switch (this.config.address) { - .unix => return .jsUndefined(), + .unix => return .js_undefined, else => {}, } @@ -1303,7 +1303,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d pub fn getHostname(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { switch (this.config.address) { - .unix => return .jsUndefined(), + .unix => return .js_undefined, else => {}, } @@ -1376,7 +1376,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d pub fn getAllClosedPromise(this: *ThisServer, globalThis: *JSC.JSGlobalObject) JSC.JSValue { if (this.listener == null and this.pending_requests == 0) { - return JSC.JSPromise.resolvedPromise(globalThis, .jsUndefined()).toJS(); + return JSC.JSPromise.resolvedPromise(globalThis, .js_undefined).toJS(); } const prom = &this.all_closed_promise; if (prom.strong.has()) { @@ -1789,7 +1789,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d resp.timeout(this.config.idleTimeout); const globalThis = this.globalThis; - const thisObject: JSValue = this.js_value.get() orelse .jsUndefined(); + const thisObject: JSValue = this.js_value.get() orelse .js_undefined; const vm = this.vm; var node_http_response: ?*NodeHTTPResponse = null; @@ -1810,7 +1810,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d if (bun.http.Method.find(req.method())) |method| method.toJS(globalThis) else - .jsUndefined(), + .js_undefined, req, resp, upgrade_ctx, @@ -2795,7 +2795,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d const loop = this.globalThis.bunVM().eventLoop(); loop.enter(); defer loop.exit(); - _ = callback.call(this.globalThis, .jsUndefined(), &.{ JSValue.jsBoolean(is_ssl), node_socket, error_code_value, raw_packet_value }) catch |err| { + _ = callback.call(this.globalThis, .js_undefined, &.{ JSValue.jsBoolean(is_ssl), node_socket, error_code_value, raw_packet_value }) catch |err| { this.globalThis.reportActiveExceptionAsUnhandled(err); }; } @@ -2843,7 +2843,7 @@ pub const ServerAllConnectionsClosedTask = struct { bun.destroy(this); if (!vm.isShuttingDown()) { - promise.resolve(globalObject, .jsUndefined()); + promise.resolve(globalObject, .js_undefined); } } }; @@ -3215,7 +3215,7 @@ pub fn Server__setOnClientError_(globalThis: *JSC.JSGlobalObject, server: JSC.JS } else { bun.debugAssert(false); } - return .jsUndefined(); + return .js_undefined; } pub fn Server__setAppFlags_(globalThis: *JSC.JSGlobalObject, server: JSC.JSValue, require_host_header: bool, use_strict_method_validation: bool) bun.JSError!JSC.JSValue { @@ -3234,7 +3234,7 @@ pub fn Server__setAppFlags_(globalThis: *JSC.JSGlobalObject, server: JSC.JSValue } else { return globalThis.throw("Failed to set timeout: The 'this' value is not a Server.", .{}); } - return .jsUndefined(); + return .js_undefined; } pub fn Server__setMaxHTTPHeaderSize_(globalThis: *JSC.JSGlobalObject, server: JSC.JSValue, max_header_size: u64) bun.JSError!JSC.JSValue { @@ -3253,7 +3253,7 @@ pub fn Server__setMaxHTTPHeaderSize_(globalThis: *JSC.JSGlobalObject, server: JS } else { return globalThis.throw("Failed to set maxHeaderSize: The 'this' value is not a Server.", .{}); } - return .jsUndefined(); + return .js_undefined; } comptime { _ = Server__setIdleTimeout; diff --git a/src/bun.js/api/server/NodeHTTPResponse.zig b/src/bun.js/api/server/NodeHTTPResponse.zig index 8862dc9e18..50dffeb601 100644 --- a/src/bun.js/api/server/NodeHTTPResponse.zig +++ b/src/bun.js/api/server/NodeHTTPResponse.zig @@ -259,7 +259,7 @@ pub fn dumpRequestBody(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObjec this.clearOnDataCallback(thisValue, globalObject); } - return .jsUndefined(); + return .js_undefined; } fn markRequestAsDone(this: *NodeHTTPResponse) void { @@ -394,14 +394,14 @@ pub fn jsRef(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, _: *JSC if (!this.isDone()) { this.js_ref.ref(globalObject.bunVM()); } - return .jsUndefined(); + return .js_undefined; } pub fn jsUnref(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { if (!this.isDone()) { this.js_ref.unref(globalObject.bunVM()); } - return .jsUndefined(); + return .js_undefined; } fn handleEndedIfNecessary(state: uws.State, globalObject: *JSC.JSGlobalObject) bun.JSError!void { @@ -436,9 +436,9 @@ pub fn writeHead(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, cal const state = this.raw_response.state(); try handleEndedIfNecessary(state, globalObject); - const status_code_value: JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); - const status_message_value: JSValue = if (arguments.len > 1 and arguments[1] != .null) arguments[1] else .jsUndefined(); - const headers_object_value: JSValue = if (arguments.len > 2 and arguments[2] != .null) arguments[2] else .jsUndefined(); + const status_code_value: JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; + const status_message_value: JSValue = if (arguments.len > 1 and arguments[1] != .null) arguments[1] else .js_undefined; + const headers_object_value: JSValue = if (arguments.len > 2 and arguments[2] != .null) arguments[2] else .js_undefined; const status_code: i32 = brk: { if (!status_code_value.isUndefined()) { @@ -483,7 +483,7 @@ pub fn writeHead(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, cal break :do_it; } - return .jsUndefined(); + return .js_undefined; } fn writeHeadInternal(response: uws.AnyResponse, globalObject: *JSC.JSGlobalObject, status_message: []const u8, headers: JSC.JSValue) void { @@ -496,14 +496,14 @@ fn writeHeadInternal(response: uws.AnyResponse, globalObject: *JSC.JSGlobalObjec pub fn writeContinue(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { if (this.isDone()) { - return .jsUndefined(); + return .js_undefined; } const state = this.raw_response.state(); try handleEndedIfNecessary(state, globalObject); this.raw_response.writeContinue(); - return .jsUndefined(); + return .js_undefined; } pub const AbortEvent = enum(u8) { @@ -575,7 +575,7 @@ pub fn doPause(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, _: *JSC.CallFram } pub fn drainRequestBody(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { - return this.drainBufferedRequestBodyFromPause(globalObject) orelse .jsUndefined(); + return this.drainBufferedRequestBodyFromPause(globalObject) orelse .js_undefined; } fn drainBufferedRequestBodyFromPause(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject) ?JSC.JSValue { @@ -640,7 +640,7 @@ pub export fn Bun__NodeHTTPRequest__onResolve(globalObject: *JSC.JSGlobalObject, this.onRequestComplete(); } - return .jsUndefined(); + return .js_undefined; } pub export fn Bun__NodeHTTPRequest__onReject(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) JSC.JSValue { @@ -668,18 +668,18 @@ pub export fn Bun__NodeHTTPRequest__onReject(globalObject: *JSC.JSGlobalObject, } _ = globalObject.bunVM().uncaughtException(globalObject, err, true); - return .jsUndefined(); + return .js_undefined; } pub fn abort(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { if (this.isDone()) { - return .jsUndefined(); + return .js_undefined; } this.flags.socket_closed = true; const state = this.raw_response.state(); if (state.isHttpEndCalled()) { - return .jsUndefined(); + return .js_undefined; } resumeSocket(this); this.raw_response.clearOnData(); @@ -687,7 +687,7 @@ pub fn abort(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) this.raw_response.clearTimeout(); this.raw_response.endWithoutBody(true); this.onRequestComplete(); - return .jsUndefined(); + return .js_undefined; } fn onBufferRequestBodyWhilePaused(this: *NodeHTTPResponse, chunk: []const u8, last: bool) void { @@ -746,10 +746,10 @@ fn onDataOrAborted(this: *NodeHTTPResponse, chunk: []const u8, last: bool, event if (chunk.len > 0) { break :brk JSC.ArrayBuffer.createBuffer(globalThis, chunk); } - break :brk .jsUndefined(); + break :brk .js_undefined; }; - event_loop.runCallback(callback, globalThis, .jsUndefined(), &.{ + event_loop.runCallback(callback, globalThis, .js_undefined, &.{ bytes, JSC.JSValue.jsBoolean(last), JSC.JSValue.jsNumber(@intFromEnum(event)), @@ -775,10 +775,10 @@ fn onDrain(this: *NodeHTTPResponse, offset: u64, response: uws.AnyResponse) bool const thisValue = this.getThisValue(); const on_writable = js.onWritableGetCached(thisValue) orelse return false; const globalThis = JSC.VirtualMachine.get().global; - js.onWritableSetCached(thisValue, globalThis, .jsUndefined()); // TODO(@heimskr): is this necessary? + js.onWritableSetCached(thisValue, globalThis, .js_undefined); // TODO(@heimskr): is this necessary? const vm = globalThis.bunVM(); - response.corked(JSC.EventLoop.runCallback, .{ vm.eventLoop(), on_writable, globalThis, .jsUndefined(), &.{JSC.JSValue.jsNumberFromUint64(offset)} }); + response.corked(JSC.EventLoop.runCallback, .{ vm.eventLoop(), on_writable, globalThis, .js_undefined, &.{JSC.JSValue.jsNumberFromUint64(offset)} }); // return true means we may have something to drain return true; } @@ -799,11 +799,11 @@ fn writeOrEnd( return globalObject.ERR(.STREAM_WRITE_AFTER_END, "Stream already ended", .{}).throw(); } - const input_value: JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); - var encoding_value: JSValue = if (arguments.len > 1) arguments[1] else .jsUndefined(); + const input_value: JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; + var encoding_value: JSValue = if (arguments.len > 1) arguments[1] else .js_undefined; const callback_value: JSValue = brk: { if (!encoding_value.isUndefinedOrNull() and encoding_value.isCallable()) { - encoding_value = .jsUndefined(); + encoding_value = .js_undefined; break :brk arguments[1]; } @@ -815,7 +815,7 @@ fn writeOrEnd( break :brk arguments[2]; } - break :brk .jsUndefined(); + break :brk .js_undefined; }; const strict_content_length: ?u64 = brk: { @@ -903,7 +903,7 @@ fn writeOrEnd( switch (this.raw_response.write(bytes)) { .want_more => |written| { this.raw_response.clearOnWritable(); - js.onWritableSetCached(js_this, globalObject, .jsUndefined()); + js.onWritableSetCached(js_this, globalObject, .js_undefined); return JSC.JSValue.jsNumberFromUint64(written); }, .backpressure => |written| { @@ -920,21 +920,21 @@ fn writeOrEnd( pub fn setOnWritable(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSValue) void { if (this.isDone() or value.isUndefined()) { - js.onWritableSetCached(thisValue, globalObject, .jsUndefined()); + js.onWritableSetCached(thisValue, globalObject, .js_undefined); } else { js.onWritableSetCached(thisValue, globalObject, value.withAsyncContextIfNeeded(globalObject)); } } pub fn getOnWritable(_: *NodeHTTPResponse, thisValue: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { - return js.onWritableGetCached(thisValue) orelse .jsUndefined(); + return js.onWritableGetCached(thisValue) orelse .js_undefined; } pub fn getOnAbort(this: *NodeHTTPResponse, thisValue: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { if (this.flags.socket_closed) { - return .jsUndefined(); + return .js_undefined; } - return js.onAbortedGetCached(thisValue) orelse .jsUndefined(); + return js.onAbortedGetCached(thisValue) orelse .js_undefined; } pub fn setOnAbort(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSValue) void { @@ -950,7 +950,7 @@ pub fn setOnAbort(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalObject: } pub fn getOnData(_: *NodeHTTPResponse, thisValue: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { - return js.onDataGetCached(thisValue) orelse .jsUndefined(); + return js.onDataGetCached(thisValue) orelse .js_undefined; } pub fn getHasCustomOnData(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject) JSC.JSValue { @@ -968,7 +968,7 @@ pub fn setHasCustomOnData(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, value fn clearOnDataCallback(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject) void { if (this.body_read_state != .none) { if (thisValue != .zero) { - js.onDataSetCached(thisValue, globalObject, .jsUndefined()); + js.onDataSetCached(thisValue, globalObject, .js_undefined); } if (!this.flags.socket_closed) this.raw_response.clearOnData(); @@ -980,7 +980,7 @@ fn clearOnDataCallback(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalOb pub fn setOnData(this: *NodeHTTPResponse, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSValue) void { if (value.isUndefined() or this.flags.ended or this.flags.socket_closed or this.body_read_state == .none or this.flags.is_data_buffered_during_pause_last) { - js.onDataSetCached(thisValue, globalObject, .jsUndefined()); + js.onDataSetCached(thisValue, globalObject, .js_undefined); defer { if (this.body_read_ref.has) { this.body_read_ref.unref(globalObject.bunVM()); @@ -1017,7 +1017,7 @@ pub fn write(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, callfra pub fn flushHeaders(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { this.raw_response.flushHeaders(); - return .jsUndefined(); + return .js_undefined; } pub fn end(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -1032,7 +1032,7 @@ pub fn getBytesWritten(this: *NodeHTTPResponse, _: *JSC.JSGlobalObject, _: *JSC. } fn handleCorked(globalObject: *JSC.JSGlobalObject, function: JSC.JSValue, result: *JSValue, is_exception: *bool) void { - result.* = function.call(globalObject, .jsUndefined(), &.{}) catch |err| { + result.* = function.call(globalObject, .js_undefined, &.{}) catch |err| { result.* = globalObject.takeException(err); is_exception.* = true; return; @@ -1091,7 +1091,7 @@ pub fn cork(this: *NodeHTTPResponse, globalObject: *JSC.JSGlobalObject, callfram } if (result == .zero) { - return .jsUndefined(); + return .js_undefined; } return result; diff --git a/src/bun.js/api/server/RequestContext.zig b/src/bun.js/api/server/RequestContext.zig index c9de993852..da894d68eb 100644 --- a/src/bun.js/api/server/RequestContext.zig +++ b/src/bun.js/api/server/RequestContext.zig @@ -105,7 +105,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, result.ensureStillAlive(); handleResolve(ctx, result); - return JSValue.jsUndefined(); + return .js_undefined; } fn renderMissingInvalidResponse(ctx: *RequestContext, value: JSC.JSValue) void { @@ -254,8 +254,8 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, const ctx = arguments.ptr[1].asPromisePtr(@This()); const err = arguments.ptr[0]; defer ctx.deref(); - handleReject(ctx, if (!err.isEmptyOrUndefinedOrNull()) err else .jsUndefined()); - return JSValue.jsUndefined(); + handleReject(ctx, if (!err.isEmptyOrUndefinedOrNull()) err else .js_undefined); + return .js_undefined; } fn handleReject(ctx: *RequestContext, value: JSC.JSValue) void { @@ -1622,7 +1622,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, var req: *@This() = args.ptr[args.len - 1].asPromisePtr(@This()); defer req.deref(); req.handleResolveStream(); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn onRejectStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { streamLog("onRejectStream", .{}); @@ -1632,7 +1632,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, defer req.deref(); req.handleRejectStream(globalThis, err); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn handleRejectStream(req: *@This(), globalThis: *JSC.JSGlobalObject, err: JSValue) void { @@ -1978,7 +1978,7 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, this.flags.has_called_error_handler = true; const result = server.config.onError.call( server.globalThis, - server.js_value.get() orelse .jsUndefined(), + server.js_value.get() orelse .js_undefined, &.{value}, ) catch |err| server.globalThis.takeException(err); defer result.ensureStillAlive(); diff --git a/src/bun.js/api/server/ServerWebSocket.zig b/src/bun.js/api/server/ServerWebSocket.zig index fed7652abd..ab6bc051c6 100644 --- a/src/bun.js/api/server/ServerWebSocket.zig +++ b/src/bun.js/api/server/ServerWebSocket.zig @@ -243,7 +243,7 @@ pub fn onPing(this: *ServerWebSocket, _: uws.AnyWebSocket, data: []const u8) voi _ = cb.call( globalThis, - .jsUndefined(), + .js_undefined, &[_]JSC.JSValue{ this.getThisValue(), this.binaryToJS(globalThis, data) }, ) catch |e| { const err = globalThis.takeException(e); @@ -271,7 +271,7 @@ pub fn onPong(this: *ServerWebSocket, _: uws.AnyWebSocket, data: []const u8) voi _ = cb.call( globalThis, - .jsUndefined(), + .js_undefined, &[_]JSC.JSValue{ this.getThisValue(), this.binaryToJS(globalThis, data) }, ) catch |e| { const err = globalThis.takeException(e); @@ -324,7 +324,7 @@ pub fn onClose(this: *ServerWebSocket, _: uws.AnyWebSocket, code: i32, message: _ = handler.onClose.call( globalObject, - .jsUndefined(), + .js_undefined, &[_]JSC.JSValue{ this.getThisValue(), JSValue.jsNumber(code), bun.String.createUTF8ForJS(globalObject, message) }, ) catch |e| { const err = globalObject.takeException(e); @@ -674,7 +674,7 @@ pub fn cork( } if (this.isClosed()) { - return JSValue.jsUndefined(); + return .js_undefined; } var corker = Corker{ @@ -1027,7 +1027,7 @@ pub fn getData( _: *JSC.JSGlobalObject, ) JSValue { log("getData()", .{}); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn setData( @@ -1064,7 +1064,7 @@ pub fn close( this.this_value = this_value; if (this.isClosed()) { - return .jsUndefined(); + return .js_undefined; } const code = brk: { @@ -1089,7 +1089,7 @@ pub fn close( this.flags.closed = true; this.websocket().end(code, message_value.slice()); - return .jsUndefined(); + return .js_undefined; } pub fn terminate( @@ -1107,14 +1107,14 @@ pub fn terminate( this.this_value = this_value; if (this.isClosed()) { - return .jsUndefined(); + return .js_undefined; } this.flags.closed = true; this.this_value.unprotect(); this.websocket().close(); - return .jsUndefined(); + return .js_undefined; } pub fn getBinaryType( @@ -1244,7 +1244,7 @@ pub fn getRemoteAddress( globalThis: *JSC.JSGlobalObject, ) JSValue { if (this.isClosed()) { - return JSValue.jsUndefined(); + return .js_undefined; } var buf: [64]u8 = [_]u8{0} ** 64; @@ -1254,7 +1254,7 @@ pub fn getRemoteAddress( const address: std.net.Address = switch (address_bytes.len) { 4 => std.net.Address.initIp4(address_bytes[0..4].*, 0), 16 => std.net.Address.initIp6(address_bytes[0..16].*, 0, 0, 0), - else => return JSValue.jsUndefined(), + else => return .js_undefined, }; const text = bun.fmt.formatIp(address, &text_buf) catch unreachable; @@ -1272,7 +1272,7 @@ const Corker = struct { const this_value = this.this_value; this.result = this.callback.call( this.globalObject, - if (this_value == .zero) .jsUndefined() else this_value, + if (this_value == .zero) .js_undefined else this_value, this.args, ) catch |err| this.globalObject.takeException(err); } diff --git a/src/bun.js/api/server/WebSocketServerContext.zig b/src/bun.js/api/server/WebSocketServerContext.zig index fcd21dbe96..bb4e9ba231 100644 --- a/src/bun.js/api/server/WebSocketServerContext.zig +++ b/src/bun.js/api/server/WebSocketServerContext.zig @@ -37,7 +37,7 @@ pub const Handler = struct { pub fn runErrorCallback(this: *const Handler, vm: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, error_value: JSC.JSValue) void { const onError = this.onError; if (!onError.isEmptyOrUndefinedOrNull()) { - _ = onError.call(globalObject, .jsUndefined(), &.{error_value}) catch |err| + _ = onError.call(globalObject, .js_undefined, &.{error_value}) catch |err| this.globalObject.reportActiveExceptionAsUnhandled(err); return; } diff --git a/src/bun.js/bindings/CallFrame.zig b/src/bun.js/bindings/CallFrame.zig index 8723874caa..dba857fce8 100644 --- a/src/bun.js/bindings/CallFrame.zig +++ b/src/bun.js/bindings/CallFrame.zig @@ -17,7 +17,7 @@ pub const CallFrame = opaque { /// Usage: `const arg1, const arg2 = call_frame.argumentsAsArray(2);` pub fn argumentsAsArray(call_frame: *const CallFrame, comptime count: usize) [count]JSValue { const slice = call_frame.arguments(); - var value: [count]JSValue = @splat(.jsUndefined()); + var value: [count]JSValue = @splat(.js_undefined); const n = @min(call_frame.argumentsCount(), count); @memcpy(value[0..n], slice[0..n]); return value; @@ -25,7 +25,7 @@ pub const CallFrame = opaque { /// This function protects out-of-bounds access by returning undefined pub fn argument(self: *const CallFrame, i: usize) JSC.JSValue { - return if (self.argumentsCount() > i) self.arguments()[i] else .jsUndefined(); + return if (self.argumentsCount() > i) self.arguments()[i] else .js_undefined; } pub fn argumentsCount(self: *const CallFrame) u32 { @@ -134,7 +134,7 @@ pub const CallFrame = opaque { } pub inline fn initUndef(comptime i: usize, ptr: [*]const JSC.JSValue) @This() { - var args: [max]JSC.JSValue = @splat(.jsUndefined()); + var args: [max]JSC.JSValue = @splat(.js_undefined); args[0..i].* = ptr[0..i].*; return @This(){ .ptr = args, .len = i }; } @@ -168,7 +168,7 @@ pub const CallFrame = opaque { const slice = self.arguments(); comptime bun.assert(max <= 9); return switch (@as(u4, @min(slice.len, max))) { - 0 => .{ .ptr = @splat(.jsUndefined()), .len = 0 }, + 0 => .{ .ptr = @splat(.js_undefined), .len = 0 }, inline 1...9 => |count| Arguments(max).initUndef(@min(count, max), slice.ptr), else => unreachable, }; diff --git a/src/bun.js/bindings/JSGlobalObject.zig b/src/bun.js/bindings/JSGlobalObject.zig index aa8dc11bb0..723b633af8 100644 --- a/src/bun.js/bindings/JSGlobalObject.zig +++ b/src/bun.js/bindings/JSGlobalObject.zig @@ -482,7 +482,7 @@ pub const JSGlobalObject = opaque { error_array: JSValue, ) JSValue { if (bun.Environment.allow_assert) bun.assert(error_array.isArray()); - return JSC__JSGlobalObject__createAggregateErrorWithArray(globalObject, error_array, message, .jsUndefined()); + return JSC__JSGlobalObject__createAggregateErrorWithArray(globalObject, error_array, message, .js_undefined); } extern fn JSC__JSGlobalObject__generateHeapSnapshot(*JSGlobalObject) JSValue; diff --git a/src/bun.js/bindings/JSPromise.zig b/src/bun.js/bindings/JSPromise.zig index 69bf262144..5cb0765641 100644 --- a/src/bun.js/bindings/JSPromise.zig +++ b/src/bun.js/bindings/JSPromise.zig @@ -199,7 +199,7 @@ pub const JSPromise = opaque { pub fn wrapValue(globalObject: *JSGlobalObject, value: JSValue) JSValue { if (value == .zero) { - return resolvedPromiseValue(globalObject, JSValue.jsUndefined()); + return resolvedPromiseValue(globalObject, .js_undefined); } else if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) { return resolvedPromiseValue(globalObject, value); } diff --git a/src/bun.js/bindings/JSValue.zig b/src/bun.js/bindings/JSValue.zig index 00efc6aea1..218f07800c 100644 --- a/src/bun.js/bindings/JSValue.zig +++ b/src/bun.js/bindings/JSValue.zig @@ -1,6 +1,8 @@ /// ABI-compatible with EncodedJSValue /// In the future, this type will exclude `zero`, encoding it as `error.JSError` instead. pub const JSValue = enum(i64) { + // fields here are prefixed so they're not accidentally mixed up with Zig's undefined/null/etc. + js_undefined = 0xa, null = 0x2, true = FFI.TrueI64, false = 0x6, @@ -20,10 +22,6 @@ pub const JSValue = enum(i64) { property_does_not_exist_on_object = 0x4, _, - /// not `pub` on purpose. - /// use .jsUndefined() so as to not be accidentally confused/typo'd with Zig undefined. - const @"undefined": JSValue = @enumFromInt(0xa); - /// When JavaScriptCore throws something, it returns a null cell (0). The /// exception is set on the global object. ABI-compatible with EncodedJSValue. pub const MaybeException = enum(backing_int) { @@ -700,10 +698,6 @@ pub const JSValue = enum(i64) { return JSC__JSValue__jsTDZValue(); } - pub inline fn jsUndefined() JSValue { - return @enumFromInt(0xa); - } - pub fn className(this: JSValue, globalThis: *JSGlobalObject) ZigString { var str = ZigString.init(""); this.getClassName(globalThis, &str); @@ -1095,7 +1089,7 @@ pub const JSValue = enum(i64) { pub inline fn isCell(this: JSValue) bool { return switch (this) { - .zero, JSValue.undefined, .null, .true, .false => false, + .zero, .js_undefined, .null, .true, .false => false, else => (@as(u64, @bitCast(@intFromEnum(this))) & FFI.NotCellMask) == 0, }; } @@ -1371,7 +1365,7 @@ pub const JSValue = enum(i64) { return switch (JSC__JSValue__fastGet(this, global, @intFromEnum(builtin_name))) { .zero => error.JSError, - JSValue.undefined, .property_does_not_exist_on_object => null, + .js_undefined, .property_does_not_exist_on_object => null, else => |val| val, }; } @@ -1454,7 +1448,7 @@ pub const JSValue = enum(i64) { } return switch (JSC__JSValue__getIfPropertyExistsImpl(this, global, property.ptr, @intCast(property.len))) { - JSValue.undefined, .zero, .property_does_not_exist_on_object => null, + .js_undefined, .zero, .property_does_not_exist_on_object => null, else => |val| val, }; } @@ -1490,7 +1484,7 @@ pub const JSValue = enum(i64) { // since there are false positives, the better path is to make them // negatives, as the number of places that desire throwing on // existing undefined is extremely small, but non-zero. - JSValue.undefined => null, + .js_undefined => null, else => |val| val, }; } @@ -1510,7 +1504,7 @@ pub const JSValue = enum(i64) { return switch (JSC__JSValue__getPropertyValue(target, global, property_name.ptr, @intCast(property_name.len))) { .zero => error.JSError, .property_does_not_exist_on_object => null, - JSValue.undefined => null, + .js_undefined => null, else => |val| val, }; } @@ -1563,7 +1557,7 @@ pub const JSValue = enum(i64) { .zero => unreachable, // Treat undefined and null as unspecified - .null, JSValue.undefined => null, + .null, .js_undefined => null, // false, 0, are deliberately not included in this list. // That would prevent you from passing `0` or `false` to various Bun APIs. @@ -1613,7 +1607,7 @@ pub const JSValue = enum(i64) { /// Returns null when the value is: /// - JSValue.null /// - JSValue.false - /// - JSValue.undefined + /// - .js_undefined /// - an empty string pub fn getStringish(this: JSValue, global: *JSGlobalObject, property: []const u8) bun.JSError!?bun.String { const prop = try get(this, global, property) orelse return null; @@ -1809,7 +1803,7 @@ pub const JSValue = enum(i64) { const prop = try this.get(global, property_name) orelse return null; return switch (prop) { - JSValue.undefined => null, + .js_undefined => null, .false, .true => prop == .true, else => { return JSC.Node.validators.throwErrInvalidArgType(global, property_name, .{}, "boolean", prop); @@ -2336,7 +2330,7 @@ pub const JSValue = enum(i64) { } switch (comptime Type) { - void => return .jsUndefined(), + void => return .js_undefined, bool => return JSC.JSValue.jsBoolean(if (comptime Type != T) value.* else value), *JSC.JSGlobalObject => return value.toJSValue(), []const u8, [:0]const u8, [*:0]const u8, []u8, [:0]u8, [*:0]u8 => { diff --git a/src/bun.js/ipc.zig b/src/bun.js/ipc.zig index d62e9c0529..3e43f8151f 100644 --- a/src/bun.js/ipc.zig +++ b/src/bun.js/ipc.zig @@ -644,8 +644,8 @@ pub const SendQueue = struct { global.emitWarning( warning.transferToJS(global), warning_name.transferToJS(global), - .jsUndefined(), - .jsUndefined(), + .js_undefined, + .js_undefined, ) catch |e| { _ = global.takeException(e); }; @@ -927,7 +927,7 @@ const MAX_HANDLE_RETRANSMISSIONS = 3; fn emitProcessErrorEvent(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const ex = callframe.argumentsAsArray(1)[0]; JSC.VirtualMachine.Process__emitErrorEvent(globalThis, ex); - return .jsUndefined(); + return .js_undefined; } const FromEnum = enum { subprocess_exited, subprocess, process }; fn doSendErr(globalObject: *JSC.JSGlobalObject, callback: JSC.JSValue, ex: JSC.JSValue, from: FromEnum) bun.JSError!JSC.JSValue { @@ -948,11 +948,11 @@ pub fn doSend(ipc: ?*SendQueue, globalObject: *JSC.JSGlobalObject, callFrame: *J if (handle.isCallable()) { callback = handle; - handle = .jsUndefined(); - options_ = .jsUndefined(); + handle = .js_undefined; + options_ = .js_undefined; } else if (options_.isCallable()) { callback = options_; - options_ = .jsUndefined(); + options_ = .js_undefined; } else if (!options_.isUndefined()) { try globalObject.validateObject("options", options_, .{}); } @@ -979,7 +979,7 @@ pub fn doSend(ipc: ?*SendQueue, globalObject: *JSC.JSGlobalObject, callFrame: *J if (!handle.isUndefinedOrNull()) { const serialized_array: JSC.JSValue = try ipcSerialize(globalObject, message, handle); if (serialized_array.isUndefinedOrNull()) { - handle = .jsUndefined(); + handle = .js_undefined; } else { const serialized_handle = serialized_array.getIndex(globalObject, 0); const serialized_message = serialized_array.getIndex(globalObject, 1); @@ -1023,14 +1023,14 @@ pub fn doSend(ipc: ?*SendQueue, globalObject: *JSC.JSGlobalObject, callFrame: *J pub fn emitHandleIPCMessage(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const target, const message, const handle = callframe.argumentsAsArray(3); if (target.isNull()) { - const ipc = globalThis.bunVM().getIPCInstance() orelse return .jsUndefined(); + const ipc = globalThis.bunVM().getIPCInstance() orelse return .js_undefined; ipc.handleIPCMessage(.{ .data = message }, handle); } else { - if (!target.isCell()) return .jsUndefined(); - const subprocess = bun.JSC.Subprocess.fromJSDirect(target) orelse return .jsUndefined(); + if (!target.isCell()) return .js_undefined; + const subprocess = bun.JSC.Subprocess.fromJSDirect(target) orelse return .js_undefined; subprocess.handleIPCMessage(.{ .data = message }, handle); } - return .jsUndefined(); + return .js_undefined; } const IPCCommand = union(enum) { @@ -1131,7 +1131,7 @@ fn handleIPCMessage(send_queue: *SendQueue, message: DecodedIPCMessage, globalTh } else { switch (send_queue.owner) { inline else => |owner| { - owner.handleIPCMessage(message, .jsUndefined()); + owner.handleIPCMessage(message, .js_undefined); }, } } diff --git a/src/bun.js/node.zig b/src/bun.js/node.zig index 55182c946b..0c12e67007 100644 --- a/src/bun.js/node.zig +++ b/src/bun.js/node.zig @@ -198,7 +198,7 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type { .result => |r| switch (ReturnType) { JSC.JSValue => r, - void => .jsUndefined(), + void => .js_undefined, bool => JSC.JSValue.jsBoolean(r), JSC.ArrayBuffer => r.toJS(globalObject, null), diff --git a/src/bun.js/node/net/BlockList.zig b/src/bun.js/node/net/BlockList.zig index b61bd270de..f14da10a81 100644 --- a/src/bun.js/node/net/BlockList.zig +++ b/src/bun.js/node/net/BlockList.zig @@ -61,7 +61,7 @@ pub fn addAddress(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *J break :blk (try SocketAddress.initFromAddrFamily(globalThis, address_js, family_js))._addr; }; try this.da_rules.insert(0, .{ .addr = address }); - return .jsUndefined(); + return .js_undefined; } pub fn addRange(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -86,7 +86,7 @@ pub fn addRange(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC } } try this.da_rules.insert(0, .{ .range = .{ .start = start, .end = end } }); - return .jsUndefined(); + return .js_undefined; } pub fn addSubnet(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -107,7 +107,7 @@ pub fn addSubnet(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JS else => {}, } try this.da_rules.insert(0, .{ .subnet = .{ .network = network, .prefix = prefix } }); - return .jsUndefined(); + return .js_undefined; } pub fn check(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/bun.js/node/node_cluster_binding.zig b/src/bun.js/node/node_cluster_binding.zig index 1aa1c72e6a..8e220584dd 100644 --- a/src/bun.js/node/node_cluster_binding.zig +++ b/src/bun.js/node/node_cluster_binding.zig @@ -60,7 +60,7 @@ pub fn sendHelperChild(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFram const arguments_ = callframe_.arguments_old(1).slice(); const ex = arguments_[0]; Process__emitErrorEvent(globalThis_, ex.toError() orelse ex); - return .jsUndefined(); + return .js_undefined; } }; @@ -84,7 +84,7 @@ pub fn onInternalMessageChild(globalThis: *JSC.JSGlobalObject, callframe: *JSC.C child_singleton.worker = .create(arguments[0], globalThis); child_singleton.cb = .create(arguments[1], globalThis); try child_singleton.flush(globalThis); - return .jsUndefined(); + return .js_undefined; } pub fn handleInternalMessageChild(globalThis: *JSC.JSGlobalObject, message: JSC.JSValue) bun.JSError!void { @@ -216,11 +216,11 @@ pub fn sendHelperPrimary(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFr pub fn onInternalMessagePrimary(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.arguments_old(3).ptr; const subprocess = arguments[0].as(bun.JSC.Subprocess).?; - const ipc_data = subprocess.ipc() orelse return .jsUndefined(); + const ipc_data = subprocess.ipc() orelse return .js_undefined; // TODO: remove these strongs. ipc_data.internal_msg_queue.worker = .create(arguments[1], globalThis); ipc_data.internal_msg_queue.cb = .create(arguments[2], globalThis); - return .jsUndefined(); + return .js_undefined; } pub fn handleInternalMessagePrimary(globalThis: *JSC.JSGlobalObject, subprocess: *JSC.Subprocess, message: JSC.JSValue) bun.JSError!void { @@ -275,7 +275,7 @@ pub fn setRef(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun. } else { vm.channel_ref.unref(vm); } - return .jsUndefined(); + return .js_undefined; } export fn Bun__refChannelUnlessOverridden(globalObject: *JSC.JSGlobalObject) void { diff --git a/src/bun.js/node/node_crypto_binding.zig b/src/bun.js/node/node_crypto_binding.zig index 28f6d1dbfb..4931d46ed1 100644 --- a/src/bun.js/node/node_crypto_binding.zig +++ b/src/bun.js/node/node_crypto_binding.zig @@ -211,7 +211,7 @@ const random = struct { fn runFromJS(this: *JobCtx, global: *JSGlobalObject, callback: JSValue) void { const vm = global.bunVM(); - vm.eventLoop().runCallback(callback, global, .jsUndefined(), &.{ .null, this.value }); + vm.eventLoop().runCallback(callback, global, .js_undefined, &.{ .null, this.value }); } fn deinit(this: *JobCtx) void { @@ -266,8 +266,8 @@ const random = struct { const res = std.crypto.random.intRangeLessThan(i64, min, max); if (!callback.isUndefined()) { - callback.callNextTick(global, [2]JSValue{ .jsUndefined(), JSValue.jsNumber(res) }); - return JSValue.jsUndefined(); + callback.callNextTick(global, [2]JSValue{ .js_undefined, JSValue.jsNumber(res) }); + return .js_undefined; } return JSValue.jsNumber(res); @@ -351,7 +351,7 @@ const random = struct { }; try Job.initAndSchedule(global, callback, &ctx); - return .jsUndefined(); + return .js_undefined; } fn randomFillSync(global: *JSGlobalObject, callFrame: *JSC.CallFrame) JSError!JSValue { @@ -414,8 +414,8 @@ const random = struct { try assertSize(global, size_value, element_size, offset, buf.byte_len); if (size == 0) { - _ = try callback.call(global, .jsUndefined(), &.{ .null, JSValue.jsNumber(0) }); - return .jsUndefined(); + _ = try callback.call(global, .js_undefined, &.{ .null, JSValue.jsNumber(0) }); + return .js_undefined; } const ctx: JobCtx = .{ @@ -426,7 +426,7 @@ const random = struct { }; try Job.initAndSchedule(global, callback, &ctx); - return .jsUndefined(); + return .js_undefined; } }; @@ -481,7 +481,7 @@ pub fn timingSafeEqual(global: *JSGlobalObject, callFrame: *JSC.CallFrame) JSErr } pub fn secureHeapUsed(_: *JSGlobalObject, _: *JSC.CallFrame) JSError!JSValue { - return .jsUndefined(); + return .js_undefined; } pub fn getFips(_: *JSGlobalObject, _: *JSC.CallFrame) JSError!JSValue { @@ -489,7 +489,7 @@ pub fn getFips(_: *JSGlobalObject, _: *JSC.CallFrame) JSError!JSValue { } pub fn setFips(_: *JSGlobalObject, _: *JSC.CallFrame) JSError!JSValue { - return .jsUndefined(); + return .js_undefined; } pub fn setEngine(global: *JSGlobalObject, _: *JSC.CallFrame) JSError!JSValue { @@ -718,17 +718,17 @@ const Scrypt = struct { var buf: [256]u8 = undefined; const msg = BoringSSL.ERR_error_string_n(err, &buf, buf.len); const exception = global.ERR(.CRYPTO_OPERATION_FAILED, "Scrypt failed: {s}", .{msg}).toJS(); - vm.eventLoop().runCallback(callback, global, .jsUndefined(), &.{exception}); + vm.eventLoop().runCallback(callback, global, .js_undefined, &.{exception}); return; } const exception = global.ERR(.CRYPTO_OPERATION_FAILED, "Scrypt failed", .{}).toJS(); - vm.eventLoop().runCallback(callback, global, .jsUndefined(), &.{exception}); + vm.eventLoop().runCallback(callback, global, .js_undefined, &.{exception}); return; } const buf = this.buf.swap(); - vm.eventLoop().runCallback(callback, global, .jsUndefined(), &.{ .jsUndefined(), buf }); + vm.eventLoop().runCallback(callback, global, .js_undefined, &.{ .js_undefined, buf }); } fn deinit(this: *Scrypt) void { @@ -739,7 +739,7 @@ const Scrypt = struct { fn scrypt(global: *JSGlobalObject, callFrame: *JSC.CallFrame) JSError!JSValue { const ctx, const callback = try Scrypt.fromJS(global, callFrame, true); try Scrypt.Job.initAndSchedule(global, callback, &ctx); - return .jsUndefined(); + return .js_undefined; } fn scryptSync(global: *JSGlobalObject, callFrame: *JSC.CallFrame) JSError!JSValue { diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 24fc6688c7..3bbe2deca5 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -1286,11 +1286,11 @@ pub const Arguments = struct { pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Rename { const old_path = try PathLike.fromJS(ctx, arguments) orelse { - return ctx.throwInvalidArgumentTypeValue("oldPath", "string or an instance of Buffer or URL", arguments.next() orelse .jsUndefined()); + return ctx.throwInvalidArgumentTypeValue("oldPath", "string or an instance of Buffer or URL", arguments.next() orelse .js_undefined); }; const new_path = try PathLike.fromJS(ctx, arguments) orelse { - return ctx.throwInvalidArgumentTypeValue("newPath", "string or an instance of Buffer or URL", arguments.next() orelse .jsUndefined()); + return ctx.throwInvalidArgumentTypeValue("newPath", "string or an instance of Buffer or URL", arguments.next() orelse .js_undefined); }; return Rename{ .old_path = old_path, .new_path = new_path }; @@ -1350,7 +1350,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Writev { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1404,7 +1404,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Readv { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1450,7 +1450,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!FTruncate { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1521,7 +1521,7 @@ pub const Arguments = struct { pub fn toThreadSafe(_: *const @This()) void {} pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Fchown { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1619,7 +1619,7 @@ pub const Arguments = struct { }; errdefer path.deinit(); - const mode_arg: JSC.JSValue = arguments.next() orelse .jsUndefined(); + const mode_arg: JSC.JSValue = arguments.next() orelse .js_undefined; const mode: Mode = try JSC.Node.modeFromJS(ctx, mode_arg) orelse { return JSC.Node.validators.throwErrInvalidArgType(ctx, "mode", .{}, "number", mode_arg); }; @@ -1639,12 +1639,12 @@ pub const Arguments = struct { pub fn toThreadSafe(_: *const @This()) void {} pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!FChmod { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; - const mode_arg: JSC.JSValue = arguments.next() orelse .jsUndefined(); + const mode_arg: JSC.JSValue = arguments.next() orelse .js_undefined; const mode: Mode = try JSC.Node.modeFromJS(ctx, mode_arg) orelse { return JSC.Node.validators.throwErrInvalidArgType(ctx, "mode", .{}, "number", mode_arg); }; @@ -1753,7 +1753,7 @@ pub const Arguments = struct { pub fn toThreadSafe(_: *@This()) void {} pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Fstat { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2160,7 +2160,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!MkdirTemp { const prefix = try PathLike.fromJS(ctx, arguments) orelse { - return ctx.throwInvalidArgumentTypeValue("prefix", "string, Buffer, or URL", arguments.next() orelse .jsUndefined()); + return ctx.throwInvalidArgumentTypeValue("prefix", "string, Buffer, or URL", arguments.next() orelse .js_undefined); }; errdefer prefix.deinit(); @@ -2268,7 +2268,7 @@ pub const Arguments = struct { pub fn toThreadSafe(_: Close) void {} pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Close { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2355,7 +2355,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Futimes { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2427,7 +2427,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Write { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2532,7 +2532,7 @@ pub const Arguments = struct { // fs_binding.read(fd, buffer, offset, length, position) // fd = getValidatedFd(fd); - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2977,7 +2977,7 @@ pub const Arguments = struct { } pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!FdataSync { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -3127,7 +3127,7 @@ pub const Arguments = struct { pub fn toThreadSafe(_: *const @This()) void {} pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice) bun.JSError!Fsync { - const fd_value: JSC.JSValue = arguments.nextEat() orelse .jsUndefined(); + const fd_value: JSC.JSValue = arguments.nextEat() orelse .js_undefined; const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -3144,14 +3144,14 @@ pub const StatOrNotFound = union(enum) { pub fn toJS(this: *StatOrNotFound, globalObject: *JSC.JSGlobalObject) JSC.JSValue { return switch (this.*) { .stats => this.stats.toJS(globalObject), - .not_found => .jsUndefined(), + .not_found => .js_undefined, }; } pub fn toJSNewlyCreated(this: *const StatOrNotFound, globalObject: *JSC.JSGlobalObject) JSC.JSValue { return switch (this.*) { .stats => this.stats.toJSNewlyCreated(globalObject), - .not_found => .jsUndefined(), + .not_found => .js_undefined, }; } }; @@ -3163,7 +3163,7 @@ pub const StringOrUndefined = union(enum) { pub fn toJS(this: *const StringOrUndefined, globalObject: *JSC.JSGlobalObject) JSC.JSValue { return switch (this.*) { .string => this.string.toJS(globalObject), - .none => .jsUndefined(), + .none => .js_undefined, }; } }; @@ -5876,7 +5876,7 @@ pub const NodeFS = struct { .code = bun.String.init(@errorName(err)), .path = bun.String.init(args.path.slice()), }).toErrorInstance(args.global_this)) catch {}; - return Maybe(Return.Watch){ .result = .jsUndefined() }; + return Maybe(Return.Watch){ .result = .js_undefined }; }; return Maybe(Return.Watch){ .result = watcher }; } diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig index ee9a788f27..7d8a7f17b1 100644 --- a/src/bun.js/node/node_fs_binding.zig +++ b/src/bun.js/node/node_fs_binding.zig @@ -218,7 +218,7 @@ pub fn createMemfdForTesting(globalObject: *JSC.JSGlobalObject, callFrame: *JSC. const arguments = callFrame.arguments_old(1); if (arguments.len < 1) { - return .jsUndefined(); + return .js_undefined; } if (comptime !bun.Environment.isLinux) { diff --git a/src/bun.js/node/node_fs_stat_watcher.zig b/src/bun.js/node/node_fs_stat_watcher.zig index a3ff4453c6..a58cb8aacf 100644 --- a/src/bun.js/node/node_fs_stat_watcher.zig +++ b/src/bun.js/node/node_fs_stat_watcher.zig @@ -309,7 +309,7 @@ pub const StatWatcher = struct { if (obj.js_this != .zero) { return obj.js_this; } - return .jsUndefined(); + return .js_undefined; } }; @@ -318,7 +318,7 @@ pub const StatWatcher = struct { this.persistent = true; this.poll_ref.ref(this.ctx); } - return .jsUndefined(); + return .js_undefined; } pub fn doUnref(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -326,7 +326,7 @@ pub const StatWatcher = struct { this.persistent = false; this.poll_ref.unref(this.ctx); } - return .jsUndefined(); + return .js_undefined; } /// Stops file watching but does not free the instance. @@ -341,7 +341,7 @@ pub const StatWatcher = struct { pub fn doClose(this: *StatWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { this.close(); - return .jsUndefined(); + return .js_undefined; } /// If the scheduler is not using this, free instantly, otherwise mark for being freed. @@ -408,7 +408,7 @@ pub const StatWatcher = struct { _ = js.listenerGetCached(this.js_this).?.call( this.globalThis, - .jsUndefined(), + .js_undefined, &[2]JSC.JSValue{ jsvalue, jsvalue, @@ -444,7 +444,7 @@ pub const StatWatcher = struct { _ = js.listenerGetCached(this.js_this).?.call( this.globalThis, - .jsUndefined(), + .js_undefined, &[2]JSC.JSValue{ current_jsvalue, prev_jsvalue, diff --git a/src/bun.js/node/node_fs_watcher.zig b/src/bun.js/node/node_fs_watcher.zig index 72c8b9d5e5..b5df617bf7 100644 --- a/src/bun.js/node/node_fs_watcher.zig +++ b/src/bun.js/node/node_fs_watcher.zig @@ -524,7 +524,7 @@ pub const FSWatcher = struct { if (js_this == .zero) return; const listener = js.listenerGetCached(js_this) orelse return; const globalObject = this.globalThis; - var filename: JSC.JSValue = .jsUndefined(); + var filename: JSC.JSValue = .js_undefined; if (file_name.len > 0) { if (this.encoding == .buffer) filename = JSC.ArrayBuffer.createBuffer(globalObject, file_name) @@ -556,7 +556,7 @@ pub const FSWatcher = struct { this.persistent = true; this.poll_ref.ref(this.ctx); } - return .jsUndefined(); + return .js_undefined; } pub fn doUnref(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -564,7 +564,7 @@ pub const FSWatcher = struct { this.persistent = false; this.poll_ref.unref(this.ctx); } - return .jsUndefined(); + return .js_undefined; } pub fn hasRef(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -604,7 +604,7 @@ pub const FSWatcher = struct { if (FSWatcher.js.listenerGetCached(js_this)) |listener| { _ = this.refTask(); log("emit('close')", .{}); - emitJS(listener, this.globalThis, .jsUndefined(), .close); + emitJS(listener, this.globalThis, .js_undefined, .close); this.unrefTask(); } } @@ -637,7 +637,7 @@ pub const FSWatcher = struct { pub fn doClose(this: *FSWatcher, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { this.close(); - return .jsUndefined(); + return .js_undefined; } pub fn init(args: Arguments) bun.JSC.Maybe(*FSWatcher) { diff --git a/src/bun.js/node/node_util_binding.zig b/src/bun.js/node/node_util_binding.zig index 904dc93abd..bc580c85e8 100644 --- a/src/bun.js/node/node_util_binding.zig +++ b/src/bun.js/node/node_util_binding.zig @@ -135,7 +135,7 @@ pub fn extractedSplitNewLinesFastPathStringsOnly(globalThis: *JSC.JSGlobalObject .utf8 => if (bun.strings.isAllASCII(str.byteSlice())) return split(.utf8, globalThis, bun.default_allocator, &str) else - return JSC.JSValue.jsUndefined(), + return .js_undefined, }; } @@ -209,7 +209,7 @@ pub fn normalizeEncoding(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFr defer str.deref(); if (str.length() == 0) return JSC.Node.Encoding.utf8.toJS(globalThis); if (str.inMapCaseInsensitive(JSC.Node.Encoding.map)) |enc| return enc.toJS(globalThis); - return JSC.JSValue.jsUndefined(); + return .js_undefined; } pub fn parseEnv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/bun.js/node/node_zlib_binding.zig b/src/bun.js/node/node_zlib_binding.zig index 0b998c99ab..ff2dfc8e10 100644 --- a/src/bun.js/node/node_zlib_binding.zig +++ b/src/bun.js/node/node_zlib_binding.zig @@ -14,7 +14,7 @@ pub fn crc32(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSE const data: JSC.JSValue = arguments[0]; if (data == .zero) { - return globalThis.throwInvalidArgumentTypeValue("data", "string or an instance of Buffer, TypedArray, or DataView", .jsUndefined()); + return globalThis.throwInvalidArgumentTypeValue("data", "string or an instance of Buffer, TypedArray, or DataView", .js_undefined); } if (data.isString()) { break :blk data.asString().toSlice(globalThis, bun.default_allocator); @@ -113,7 +113,7 @@ pub fn CompressionStream(comptime T: type) type { this.poll_ref.ref(vm); JSC.WorkPool.schedule(&this.task); - return .jsUndefined(); + return .js_undefined; } const AsyncJob = struct { @@ -216,7 +216,7 @@ pub fn CompressionStream(comptime T: type) type { } this.deref(); - return .jsUndefined(); + return .js_undefined; } pub fn reset(this: *T, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -224,14 +224,14 @@ pub fn CompressionStream(comptime T: type) type { if (err.isError()) { try emitError(this, globalThis, callframe.this(), err); } - return .jsUndefined(); + return .js_undefined; } pub fn close(this: *T, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { _ = globalThis; _ = callframe; closeInternal(this); - return .jsUndefined(); + return .js_undefined; } fn closeInternal(this: *T) void { @@ -252,7 +252,7 @@ pub fn CompressionStream(comptime T: type) type { } pub fn getOnError(_: *T, this_value: JSC.JSValue, _: *JSC.JSGlobalObject) JSC.JSValue { - return T.js.errorCallbackGetCached(this_value) orelse .jsUndefined(); + return T.js.errorCallbackGetCached(this_value) orelse .js_undefined; } /// returns true if no error was detected/emitted diff --git a/src/bun.js/node/path.zig b/src/bun.js/node/path.zig index aca01dadec..59d67d9501 100644 --- a/src/bun.js/node/path.zig +++ b/src/bun.js/node/path.zig @@ -443,7 +443,7 @@ pub fn basename(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [* }; } - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { return .zero; @@ -634,7 +634,7 @@ pub inline fn dirnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, i } pub fn dirname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -833,7 +833,7 @@ pub inline fn extnameJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, i } pub fn extname(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -947,7 +947,7 @@ pub fn formatJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocator } pub fn format(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) bun.JSError!JSC.JSValue { - const pathObject_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const pathObject_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateObject(globalObject, pathObject_ptr, "pathObject", .{}, .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -1040,7 +1040,7 @@ pub fn isAbsoluteWindowsZigString(pathZStr: JSC.ZigString) bool { } pub fn isAbsolute(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -1664,7 +1664,7 @@ pub fn normalizeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, alloca } pub fn normalize(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -1987,7 +1987,7 @@ pub inline fn parseJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, isW } pub fn parse(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const path_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, path_ptr, "path", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. @@ -2348,13 +2348,13 @@ pub fn relativeJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, allocat } pub fn relative(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - const from_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .jsUndefined(); + const from_ptr: JSC.JSValue = if (args_len > 0) args_ptr[0] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, from_ptr, "from", .{}) catch { // Returning .zero translates to a nullprt JSC.JSValue. return .zero; }; - const to_ptr: JSC.JSValue = if (args_len > 1) args_ptr[1] else .jsUndefined(); + const to_ptr: JSC.JSValue = if (args_len > 1) args_ptr[1] else .js_undefined; // Supress exeption in zig. It does globalThis.vm().throwError() in JS land. validateString(globalObject, to_ptr, "to", .{}) catch { return .zero; @@ -2940,7 +2940,7 @@ pub fn toNamespacedPathJS_T(comptime T: type, globalObject: *JSC.JSGlobalObject, } pub fn toNamespacedPath(globalObject: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(JSC.conv) JSC.JSValue { - if (args_len == 0) return .jsUndefined(); + if (args_len == 0) return .js_undefined; var path_ptr = args_ptr[0]; // Based on Node v21.6.1 path.win32.toNamespacedPath and path.posix.toNamespacedPath: diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 5de1f07220..7df8e8d663 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -477,7 +477,7 @@ pub const Encoding = enum(u8) { pub fn jsAssertEncodingValid(global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) bun.JSError!JSC.JSValue { const value = call_frame.argument(0); _ = try Encoding.assert(value, global, .utf8); - return .jsUndefined(); + return .js_undefined; } const PathOrBuffer = union(Tag) { @@ -1174,7 +1174,7 @@ pub const PathOrBlob = union(enum) { } const arg = args.nextEat() orelse { - return ctx.throwInvalidArgumentTypeValue("destination", "path, file descriptor, or Blob", .jsUndefined()); + return ctx.throwInvalidArgumentTypeValue("destination", "path, file descriptor, or Blob", .js_undefined); }; if (arg.as(Blob)) |blob| { return PathOrBlob{ diff --git a/src/bun.js/node/util/parse_args.zig b/src/bun.js/node/util/parse_args.zig index f14dab555b..be9f380498 100644 --- a/src/bun.js/node/util/parse_args.zig +++ b/src/bun.js/node/util/parse_args.zig @@ -175,7 +175,7 @@ fn getDefaultArgs(globalThis: *JSGlobalObject) !ArgsSlice { } return .{ - .array = .jsUndefined(), + .array = .js_undefined, .start = 0, .end = 0, }; @@ -316,7 +316,7 @@ fn parseOptionDefinitions(globalThis: *JSGlobalObject, options_obj: JSValue, opt try validators.validateObject(globalThis, obj, "options.{s}", .{option.long_name}, .{}); // type field is required - const option_type: JSValue = obj.getOwn(globalThis, "type") orelse .jsUndefined(); + const option_type: JSValue = obj.getOwn(globalThis, "type") orelse .js_undefined; option.type = try validators.validateStringEnum(OptionValueType, globalThis, option_type, "options.{s}.type", .{option.long_name}); if (obj.getOwn(globalThis, "short")) |short_option| { @@ -413,7 +413,7 @@ fn tokenizeArgs( const short_option = arg.substringWithLen(1, 2); const option_idx = findOptionByShortName(short_option, options); const option_type: OptionValueType = if (option_idx) |idx| options[idx].type else .boolean; - var value = ValueRef{ .jsvalue = .jsUndefined() }; + var value = ValueRef{ .jsvalue = .js_undefined }; var has_inline_value = true; if (option_type == .string and index + 1 < num_args) { // e.g. '-f', "bar" @@ -447,7 +447,7 @@ fn tokenizeArgs( // Boolean option, or last short in group. Well formed. // Immediately process as a lone_short_option (e.g. from input -abc, process -a -b -c) - var value = ValueRef{ .jsvalue = .jsUndefined() }; + var value = ValueRef{ .jsvalue = .js_undefined }; var has_inline_value = true; if (option_type == .string and index + 1 < num_args) { // e.g. '-f', "bar" @@ -526,7 +526,7 @@ fn tokenizeArgs( try ctx.handleToken(.{ .option = .{ .index = index, - .value = ValueRef{ .jsvalue = value orelse JSValue.jsUndefined() }, + .value = ValueRef{ .jsvalue = value orelse .js_undefined }, .inline_value = (value == null), .name = ValueRef{ .bunstr = long_option }, .parse_type = .lone_long_option, @@ -635,7 +635,7 @@ const ParseArgsState = struct { // value exists only for string options, otherwise the property exists with "undefined" as value var value = token.value.asJSValue(globalThis); obj.put(globalThis, ZigString.static("value"), value); - obj.put(globalThis, ZigString.static("inlineValue"), if (value.isUndefined()) .jsUndefined() else JSValue.jsBoolean(token.inline_value)); + obj.put(globalThis, ZigString.static("inlineValue"), if (value.isUndefined()) .js_undefined else JSValue.jsBoolean(token.inline_value)); }, .positional => |token| { obj.put(globalThis, ZigString.static("index"), JSValue.jsNumber(token.index)); @@ -665,7 +665,7 @@ pub fn parseArgs(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSE const config = if (config_value.isUndefined()) null else config_value; // Phase 0.A: Get and validate type of input args - const config_args: JSValue = if (config) |c| c.getOwn(globalThis, "args") orelse .jsUndefined() else .jsUndefined(); + const config_args: JSValue = if (config) |c| c.getOwn(globalThis, "args") orelse .js_undefined else .js_undefined; const args: ArgsSlice = if (!config_args.isUndefinedOrNull()) args: { try validators.validateArray(globalThis, config_args, "args", .{}, null); break :args .{ @@ -681,7 +681,7 @@ pub fn parseArgs(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSE var config_allow_positionals: JSValue = if (config) |c| c.getOwn(globalThis, "allowPositionals") orelse JSC.jsBoolean(!config_strict.toBoolean()) else JSC.jsBoolean(!config_strict.toBoolean()); const config_return_tokens: JSValue = (if (config) |c| c.getOwn(globalThis, "tokens") else null) orelse JSValue.jsBoolean(false); const config_allow_negative: JSValue = if (config) |c| c.getOwn(globalThis, "allowNegative") orelse .false else .false; - const config_options: JSValue = if (config) |c| c.getOwn(globalThis, "options") orelse .jsUndefined() else .jsUndefined(); + const config_options: JSValue = if (config) |c| c.getOwn(globalThis, "options") orelse .js_undefined else .js_undefined; const strict = try validators.validateBoolean(globalThis, config_strict, "strict", .{}); @@ -714,7 +714,7 @@ pub fn parseArgs(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSE // note that "values" needs to have a null prototype instead of Object, to avoid issues such as "values.toString"` being defined const values = JSValue.createEmptyObjectWithNullPrototype(globalThis); const positionals = try JSC.JSValue.createEmptyArray(globalThis, 0); - const tokens: JSValue = if (return_tokens) try JSC.JSValue.createEmptyArray(globalThis, 0) else .jsUndefined(); + const tokens: JSValue = if (return_tokens) try JSC.JSValue.createEmptyArray(globalThis, 0) else .js_undefined; var state = ParseArgsState{ .globalThis = globalThis, diff --git a/src/bun.js/node/zlib/NativeBrotli.zig b/src/bun.js/node/zlib/NativeBrotli.zig index 495831e4b5..71ea321d95 100644 --- a/src/bun.js/node/zlib/NativeBrotli.zig +++ b/src/bun.js/node/zlib/NativeBrotli.zig @@ -112,7 +112,7 @@ pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.C _ = globalThis; _ = callframe; // intentionally left empty - return .jsUndefined(); + return .js_undefined; } fn deinit(this: *@This()) void { diff --git a/src/bun.js/node/zlib/NativeZlib.zig b/src/bun.js/node/zlib/NativeZlib.zig index fab1a8f020..945136e59a 100644 --- a/src/bun.js/node/zlib/NativeZlib.zig +++ b/src/bun.js/node/zlib/NativeZlib.zig @@ -93,7 +93,7 @@ pub fn init(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.Cal this.stream.init(level, windowBits, memLevel, strategy, dictionary); - return .jsUndefined(); + return .js_undefined; } pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -110,7 +110,7 @@ pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.C if (err.isError()) { try impl.emitError(this, globalThis, callframe.this(), err); } - return .jsUndefined(); + return .js_undefined; } fn deinit(this: *@This()) void { diff --git a/src/bun.js/node/zlib/NativeZstd.zig b/src/bun.js/node/zlib/NativeZstd.zig index b0b6019aeb..0d9756bf81 100644 --- a/src/bun.js/node/zlib/NativeZstd.zig +++ b/src/bun.js/node/zlib/NativeZstd.zig @@ -112,7 +112,7 @@ pub fn params(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.C _ = globalThis; _ = callframe; // intentionally left empty - return .jsUndefined(); + return .js_undefined; } fn deinit(this: *@This()) void { diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 65f76eb456..a04e1fd0cd 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -356,7 +356,7 @@ pub const Expect = struct { pub fn call(globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { const arguments = callframe.arguments_old(2).slice(); - const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) .js_undefined else arguments[0]; var custom_label = bun.String.empty; if (arguments.len > 1) { @@ -438,7 +438,7 @@ pub const Expect = struct { var pass = true; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var msg = _msg.toSlice(default_allocator); defer msg.deinit(); @@ -483,7 +483,7 @@ pub const Expect = struct { var pass = false; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var msg = _msg.toSlice(default_allocator); defer msg.deinit(); @@ -516,7 +516,7 @@ pub const Expect = struct { var pass = right.isSameValue(left, globalThis); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -608,7 +608,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure if (not) { @@ -686,7 +686,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -781,7 +781,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1424,7 +1424,7 @@ pub const Expect = struct { if (truthy) pass = true; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1453,7 +1453,7 @@ pub const Expect = struct { if (value.isUndefined()) pass = true; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1486,7 +1486,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1514,7 +1514,7 @@ pub const Expect = struct { const not = this.flags.not; var pass = value.isNull(); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1542,7 +1542,7 @@ pub const Expect = struct { const not = this.flags.not; var pass = !value.isUndefined(); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1575,7 +1575,7 @@ pub const Expect = struct { if (!truthy) pass = true; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1612,7 +1612,7 @@ pub const Expect = struct { var pass = try value.jestDeepEquals(expected, globalThis); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure const diff_formatter = DiffFormatter{ @@ -1651,7 +1651,7 @@ pub const Expect = struct { var pass = try value.jestStrictDeepEquals(expected, globalThis); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure const diff_formatter = DiffFormatter{ .received = value, .expected = expected, .globalThis = globalThis, .not = not }; @@ -1706,7 +1706,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1790,7 +1790,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1847,7 +1847,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1907,7 +1907,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -1967,7 +1967,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2027,7 +2027,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2090,7 +2090,7 @@ pub const Expect = struct { } if (std.math.isPositiveInf(expected) and std.math.isPositiveInf(received)) { - return .jsUndefined(); + return .js_undefined; } const expected_diff = bun.pow(10, -precision) / 2; @@ -2100,7 +2100,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -2159,7 +2159,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2217,7 +2217,7 @@ pub const Expect = struct { if (not) { const signature = comptime getSignature("toThrow", "expected", true); - if (!did_throw) return .jsUndefined(); + if (!did_throw) return .js_undefined; const result: JSValue = result_.?; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2226,8 +2226,8 @@ pub const Expect = struct { if (expected_value == .zero or expected_value.isUndefined()) { const signature_no_args = comptime getSignature("toThrow", "", true); if (result.toError()) |err| { - const name: JSValue = try err.getTruthyComptime(globalThis, "name") orelse .jsUndefined(); - const message: JSValue = try err.getTruthyComptime(globalThis, "message") orelse .jsUndefined(); + const name: JSValue = try err.getTruthyComptime(globalThis, "name") orelse .js_undefined; + const message: JSValue = try err.getTruthyComptime(globalThis, "message") orelse .js_undefined; const fmt = signature_no_args ++ "\n\nError name: {any}\nError message: {any}\n"; return globalThis.throwPretty(fmt, .{ name.toFmt(&formatter), @@ -2244,7 +2244,7 @@ pub const Expect = struct { const received_message: JSValue = (if (result.isObject()) try result.fastGet(globalThis, .message) else - JSValue.fromCell(try result.toJSString(globalThis))) orelse .jsUndefined(); + JSValue.fromCell(try result.toJSString(globalThis))) orelse .js_undefined; if (globalThis.hasException()) return .zero; // TODO: remove this allocation @@ -2254,7 +2254,7 @@ pub const Expect = struct { defer expected_slice.deinit(); const received_slice = try received_message.toSliceOrNull(globalThis); defer received_slice.deinit(); - if (!strings.contains(received_slice.slice(), expected_slice.slice())) return .jsUndefined(); + if (!strings.contains(received_slice.slice(), expected_slice.slice())) return .js_undefined; } return this.throw(globalThis, signature, "\n\nExpected substring: not {any}\nReceived message: {any}\n", .{ @@ -2267,13 +2267,13 @@ pub const Expect = struct { const received_message: JSValue = (if (result.isObject()) try result.fastGet(globalThis, .message) else - JSValue.fromCell(try result.toJSString(globalThis))) orelse .jsUndefined(); + JSValue.fromCell(try result.toJSString(globalThis))) orelse .js_undefined; if (globalThis.hasException()) return .zero; // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (try expected_value.get(globalThis, "test")) |test_fn| { const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err); - if (!matches.toBoolean()) return .jsUndefined(); + if (!matches.toBoolean()) return .js_undefined; } return this.throw(globalThis, signature, "\n\nExpected pattern: not {any}\nReceived message: {any}\n", .{ @@ -2286,25 +2286,25 @@ pub const Expect = struct { const received_message: JSValue = (if (result.isObject()) try result.fastGet(globalThis, .message) else - JSValue.fromCell(try result.toJSString(globalThis))) orelse .jsUndefined(); + JSValue.fromCell(try result.toJSString(globalThis))) orelse .js_undefined; if (globalThis.hasException()) return .zero; // no partial match for this case - if (!expected_message.isSameValue(received_message, globalThis)) return .jsUndefined(); + if (!expected_message.isSameValue(received_message, globalThis)) return .js_undefined; return this.throw(globalThis, signature, "\n\nExpected message: not {any}\n", .{expected_message.toFmt(&formatter)}); } - if (!result.isInstanceOf(globalThis, expected_value)) return .jsUndefined(); + if (!result.isInstanceOf(globalThis, expected_value)) return .js_undefined; var expected_class = ZigString.Empty; expected_value.getClassName(globalThis, &expected_class); - const received_message: JSValue = (try result.fastGet(globalThis, .message)) orelse .jsUndefined(); + const received_message: JSValue = (try result.fastGet(globalThis, .message)) orelse .js_undefined; return this.throw(globalThis, signature, "\n\nExpected constructor: not {s}\n\nReceived message: {any}\n", .{ expected_class, received_message.toFmt(&formatter) }); } if (did_throw) { - if (expected_value == .zero or expected_value.isUndefined()) return .jsUndefined(); + if (expected_value == .zero or expected_value.isUndefined()) return .js_undefined; const result: JSValue = if (result_.?.toError()) |r| r @@ -2324,7 +2324,7 @@ pub const Expect = struct { defer expected_slice.deinit(); const received_slice = try received_message.toSlice(globalThis, globalThis.allocator()); defer received_slice.deinit(); - if (strings.contains(received_slice.slice(), expected_slice.slice())) return .jsUndefined(); + if (strings.contains(received_slice.slice(), expected_slice.slice())) return .js_undefined; } // error: message from received error does not match expected string @@ -2349,7 +2349,7 @@ pub const Expect = struct { // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (try expected_value.get(globalThis, "test")) |test_fn| { const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err); - if (matches.toBoolean()) return .jsUndefined(); + if (matches.toBoolean()) return .js_undefined; } } @@ -2380,7 +2380,7 @@ pub const Expect = struct { } if (is_equal) { - return .jsUndefined(); + return .js_undefined; } var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2397,7 +2397,7 @@ pub const Expect = struct { const signature = comptime getSignature("toThrow", "expected", false); if (_received_message) |received_message| { - if (received_message.isSameValue(expected_message, globalThis)) return .jsUndefined(); + if (received_message.isSameValue(expected_message, globalThis)) return .js_undefined; } // error: message from received error does not match expected error message. @@ -2415,7 +2415,7 @@ pub const Expect = struct { return this.throw(globalThis, signature, "\n\nExpected message: {any}\nReceived value: {any}\n", .{ expected_fmt, received_fmt }); } - if (result.isInstanceOf(globalThis, expected_value)) return .jsUndefined(); + if (result.isInstanceOf(globalThis, expected_value)) return .js_undefined; // error: received error not instance of received error constructor var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -2503,7 +2503,7 @@ pub const Expect = struct { const prev_unhandled_pending_rejection_to_capture = vm.unhandled_pending_rejection_to_capture; vm.unhandled_pending_rejection_to_capture = &return_value; vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue; - return_value_from_function = value.call(globalThis, .jsUndefined(), &.{}) catch |err| globalThis.takeException(err); + return_value_from_function = value.call(globalThis, .js_undefined, &.{}) catch |err| globalThis.takeException(err); vm.unhandled_pending_rejection_to_capture = prev_unhandled_pending_rejection_to_capture; vm.global.handleRejectedPromises(); @@ -2584,10 +2584,10 @@ pub const Expect = struct { var err_value_res = err_value orelse return null; if (err_value_res.isAnyError()) { - const message: JSValue = try err_value_res.getTruthyComptime(globalThis, "message") orelse .jsUndefined(); + const message: JSValue = try err_value_res.getTruthyComptime(globalThis, "message") orelse .js_undefined; err_value_res = message; } else { - err_value_res = .jsUndefined(); + err_value_res = .js_undefined; } return err_value_res; } @@ -2787,7 +2787,7 @@ pub const Expect = struct { if (strings.eqlLong(pretty_value.slice(), trim_res.trimmed, true)) { Jest.runner.?.snapshots.passed += 1; - return .jsUndefined(); + return .js_undefined; } else if (update) { Jest.runner.?.snapshots.passed += 1; needs_write = true; @@ -2850,7 +2850,7 @@ pub const Expect = struct { }); } - return .jsUndefined(); + return .js_undefined; } pub fn toMatchSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { defer this.postMatch(globalThis); @@ -2955,7 +2955,7 @@ pub const Expect = struct { if (existing_value) |saved_value| { if (strings.eqlLong(pretty_value.slice(), saved_value, true)) { Jest.runner.?.snapshots.passed += 1; - return .jsUndefined(); + return .js_undefined; } Jest.runner.?.snapshots.failed += 1; @@ -2970,7 +2970,7 @@ pub const Expect = struct { return globalThis.throwPretty(fmt, .{diff_format}); } - return .jsUndefined(); + return .js_undefined; } pub fn toBeEmpty(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { @@ -3036,7 +3036,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; if (not) { const signature = comptime getSignature("toBeEmpty", "", true); @@ -3089,7 +3089,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isUndefinedOrNull() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3115,7 +3115,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.jsType().isArray() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3156,7 +3156,7 @@ pub const Expect = struct { var pass = value.jsType().isArray() and @as(i32, @intCast(value.getLength(globalThis))) == size.toInt32(); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3182,7 +3182,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isBoolean() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3253,7 +3253,7 @@ pub const Expect = struct { pass = strings.eql(typeof, whatIsTheType); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3280,7 +3280,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = (value.isBoolean() and value.toBoolean()) != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3306,7 +3306,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = (value.isBoolean() and !value.toBoolean()) != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3332,7 +3332,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isNumber() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3358,7 +3358,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isAnyInt() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3416,7 +3416,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3448,7 +3448,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3480,7 +3480,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3533,7 +3533,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3616,7 +3616,7 @@ pub const Expect = struct { } if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -3644,7 +3644,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isSymbol() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3670,7 +3670,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isCallable() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3696,7 +3696,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isDate() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3749,7 +3749,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = value.isString() != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3798,7 +3798,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3879,7 +3879,7 @@ pub const Expect = struct { pass = std.mem.containsAtLeast(u8, expectStringAsStr, countAsNum, subStringAsStr); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -3945,7 +3945,7 @@ pub const Expect = struct { }; value.ensureStillAlive(); - const result = predicate.call(globalThis, .jsUndefined(), &.{value}) catch |e| { + const result = predicate.call(globalThis, .js_undefined, &.{value}) catch |e| { const err = globalThis.takeException(e); const fmt = ZigString.init("toSatisfy() predicate threw an exception"); return globalThis.throwValue(globalThis.createAggregateError(&.{err}, &fmt)); @@ -3954,7 +3954,7 @@ pub const Expect = struct { const not = this.flags.not; const pass = (result.isBoolean() and result.toBoolean()) != not; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -4006,7 +4006,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -4060,7 +4060,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; defer formatter.deinit(); @@ -4106,7 +4106,7 @@ pub const Expect = struct { const not = this.flags.not; var pass = value.isInstanceOf(globalThis, expected_value); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure const expected_fmt = expected_value.toFmt(&formatter); @@ -4165,7 +4165,7 @@ pub const Expect = struct { }; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure const expected_fmt = expected_value.toFmt(&formatter); @@ -4202,7 +4202,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure if (not) { @@ -4233,7 +4233,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure if (not) { @@ -4272,7 +4272,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure if (not) { @@ -4323,7 +4323,7 @@ pub const Expect = struct { var pass = received_object.jestDeepMatch(property_matchers, globalThis, true); if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure const diff_formatter = DiffFormatter{ @@ -4389,7 +4389,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure if (not) { @@ -4444,7 +4444,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -4508,7 +4508,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; // handle failure var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true }; @@ -4593,7 +4593,7 @@ pub const Expect = struct { const not = this.flags.not; if (not) pass = !pass; - if (pass) return .jsUndefined(); + if (pass) return .js_undefined; if (!pass and return_status == ReturnStatus.throw) { const signature = comptime getSignature(name, "expected", false); @@ -4707,7 +4707,7 @@ pub const Expect = struct { globalThis.bunVM().autoGarbageCollect(); - return .jsUndefined(); + return .js_undefined; } const CustomMatcherParamsFormatter = struct { @@ -4823,7 +4823,7 @@ pub const Expect = struct { } message = message_value; } else { - message = .jsUndefined(); + message = .js_undefined; } break :valid true; @@ -4868,7 +4868,7 @@ pub const Expect = struct { // retrieve the user-provided matcher function (matcher_fn) const func: JSValue = callFrame.callee(); - var matcher_fn: JSValue = getCustomMatcherFn(func, globalThis) orelse .jsUndefined(); + var matcher_fn: JSValue = getCustomMatcherFn(func, globalThis) orelse .js_undefined; if (!matcher_fn.jsType().isFunction()) { return globalThis.throw("Internal consistency error: failed to retrieve the matcher function for a custom matcher!", .{}); } @@ -4922,7 +4922,7 @@ pub const Expect = struct { is_expecting_assertions = true; - return .jsUndefined(); + return .js_undefined; } pub fn assertions(globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { @@ -4953,7 +4953,7 @@ pub const Expect = struct { is_expecting_assertions_count = true; active_test_expectation_counter.expected = unsigned_expected_assertions; - return .jsUndefined(); + return .js_undefined; } pub fn notImplementedJSCFn(_: *Expect, globalThis: *JSGlobalObject, _: *CallFrame) bun.JSError!JSValue { @@ -5178,7 +5178,7 @@ pub const ExpectCloseTo = struct { } const number_value = args[0]; - var precision_value: JSValue = if (args.len > 1) args[1] else .jsUndefined(); + var precision_value: JSValue = if (args.len > 1) args[1] else .js_undefined; if (precision_value.isUndefined()) { precision_value = JSValue.jsNumberFromInt32(2); // default value from jest } @@ -5461,7 +5461,7 @@ pub const ExpectCustomAsymmetricMatcher = struct { pub fn asymmetricMatch(this: *ExpectCustomAsymmetricMatcher, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { const arguments = callframe.arguments_old(1).slice(); - const received_value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; + const received_value = if (arguments.len < 1) .js_undefined else arguments[0]; const matched = execute(this, callframe.this(), globalThis, received_value); return JSValue.jsBoolean(matched); } @@ -5616,19 +5616,19 @@ pub const ExpectMatcherUtils = struct { pub fn stringify(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { const arguments = callframe.arguments_old(1).slice(); - const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) .js_undefined else arguments[0]; return printValueCatched(globalThis, value, null); } pub fn printExpected(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { const arguments = callframe.arguments_old(1).slice(); - const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) .js_undefined else arguments[0]; return printValueCatched(globalThis, value, ""); } pub fn printReceived(_: *ExpectMatcherUtils, globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue { const arguments = callframe.arguments_old(1).slice(); - const value = if (arguments.len < 1) JSValue.jsUndefined() else arguments[0]; + const value = if (arguments.len < 1) .js_undefined else arguments[0]; return printValueCatched(globalThis, value, ""); } @@ -5643,7 +5643,7 @@ pub const ExpectMatcherUtils = struct { const received = if (arguments.len > 1) arguments[1] else bun.String.static("received").toJS(globalThis); const expected = if (arguments.len > 2) arguments[2] else bun.String.static("expected").toJS(globalThis); - const options = if (arguments.len > 3) arguments[3] else JSValue.jsUndefined(); + const options = if (arguments.len > 3) arguments[3] else .js_undefined; var is_not = false; var comment: ?*JSC.JSString = null; // TODO support diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index e5e16aac96..95c5e6d358 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -285,7 +285,7 @@ pub const Jest = struct { function.protect(); @field(the_runner.global_callbacks, name).append(bun.default_allocator, function) catch unreachable; - return .jsUndefined(); + return .js_undefined; } }.appendGlobalFunctionCallback; } @@ -505,7 +505,7 @@ pub const Jest = struct { test_runner.default_timeout_override = timeout_ms; } - return .jsUndefined(); + return .js_undefined; } comptime { @@ -592,7 +592,7 @@ pub const TestScope = struct { var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); task.handleResult(.{ .fail = expect.active_test_expectation_counter.actual }, .promise); globalThis.bunVM().autoGarbageCollect(); - return JSValue.jsUndefined(); + return .js_undefined; } const jsOnReject = JSC.toJSHostFn(onReject); @@ -602,7 +602,7 @@ pub const TestScope = struct { var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); task.handleResult(.{ .pass = expect.active_test_expectation_counter.actual }, .promise); globalThis.bunVM().autoGarbageCollect(); - return JSValue.jsUndefined(); + return .js_undefined; } const jsOnResolve = JSC.toJSHostFn(onResolve); @@ -648,7 +648,7 @@ pub const TestScope = struct { } } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn run( @@ -905,7 +905,7 @@ pub const DescribeScope = struct { scope.done = true; } - return JSValue.jsUndefined(); + return .js_undefined; } pub const afterAll = createCallback(.afterAll); @@ -1083,7 +1083,7 @@ pub const DescribeScope = struct { if (callback == .zero) { this.runTests(globalObject); - return .jsUndefined(); + return .js_undefined; } { @@ -1096,17 +1096,17 @@ pub const DescribeScope = struct { .fulfilled => {}, else => { _ = globalObject.bunVM().unhandledRejection(globalObject, prom.result(globalObject.vm()), prom.asValue()); - return .jsUndefined(); + return .js_undefined; }, } } else if (result.toError()) |err| { _ = globalObject.bunVM().uncaughtException(globalObject, err, true); - return .jsUndefined(); + return .js_undefined; } } this.runTests(globalObject); - return .jsUndefined(); + return .js_undefined; } pub fn runTests(this: *DescribeScope, globalObject: *JSGlobalObject) void { @@ -1805,7 +1805,7 @@ inline fn createScope( Jest.runner.?.setOnly(); tag_to_use = .only; } else if (is_test and Jest.runner.?.only and parent.tag != .only) { - return .jsUndefined(); + return .js_undefined; } var is_skip = tag == .skip or @@ -2052,14 +2052,14 @@ fn eachBind(globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSVa const allocator = bun.default_allocator; const each_data = bun.cast(*EachData, data); JSC.host_fn.setFunctionData(callee, null); - const array = each_data.*.strong.get() orelse return .jsUndefined(); + const array = each_data.*.strong.get() orelse return .js_undefined; defer { each_data.*.strong.deinit(); allocator.destroy(each_data); } if (array.isUndefinedOrNull() or !array.jsType().isArray()) { - return .jsUndefined(); + return .js_undefined; } var iter = array.arrayIterator(globalThis); @@ -2138,7 +2138,7 @@ fn eachBind(globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSVa allocator.free(formattedLabel); } else if (each_data.is_test) { if (Jest.runner.?.only and tag != .only) { - return .jsUndefined(); + return .js_undefined; } else { function.protect(); parent.tests.append(allocator, TestScope{ @@ -2168,7 +2168,7 @@ fn eachBind(globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSVa } } - return .jsUndefined(); + return .js_undefined; } inline fn createEach( @@ -2207,7 +2207,7 @@ fn callJSFunctionForTestRunner(vm: *JSC.VirtualMachine, globalObject: *JSGlobalO defer vm.eventLoop().exit(); globalObject.clearTerminationException(); - return function.call(globalObject, .jsUndefined(), args) catch |err| globalObject.takeException(err); + return function.call(globalObject, .js_undefined, args) catch |err| globalObject.takeException(err); } const assert = bun.assert; diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index 129fe2e123..daa4c316f1 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -1421,12 +1421,12 @@ pub const JestPrettyFormat = struct { }, .Event => { const event_type_value: JSValue = brk: { - const value_: JSValue = value.get_unsafe(this.globalThis, "type") orelse break :brk .jsUndefined(); + const value_: JSValue = value.get_unsafe(this.globalThis, "type") orelse break :brk .js_undefined; if (value_.isString()) { break :brk value_; } - break :brk .jsUndefined(); + break :brk .js_undefined; }; const event_type = switch (try EventType.map.fromJS(this.globalThis, event_type_value) orelse .unknown) { @@ -1478,7 +1478,7 @@ pub const JestPrettyFormat = struct { comptime Output.prettyFmt("data: ", enable_ansi_colors), .{}, ); - const data: JSValue = (try value.fastGet(this.globalThis, .data)) orelse .jsUndefined(); + const data: JSValue = (try value.fastGet(this.globalThis, .data)) orelse .js_undefined; const tag = Tag.get(data, this.globalThis); if (tag.cell.isStringLike()) { diff --git a/src/bun.js/virtual_machine_exports.zig b/src/bun.js/virtual_machine_exports.zig index a6e52110d0..c061cc4371 100644 --- a/src/bun.js/virtual_machine_exports.zig +++ b/src/bun.js/virtual_machine_exports.zig @@ -91,7 +91,7 @@ pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JS if (!value.isTerminationException(vm.jsc)) { _ = vm.uncaughtException(globalObject, value, false); } - return .jsUndefined(); + return .js_undefined; } /// This function is called on another thread diff --git a/src/bun.js/webcore/Blob.zig b/src/bun.js/webcore/Blob.zig index 95b0f5fad9..9f9beceac5 100644 --- a/src/bun.js/webcore/Blob.zig +++ b/src/bun.js/webcore/Blob.zig @@ -2329,7 +2329,7 @@ pub fn onFileStreamResolveRequestStream(globalThis: *JSC.JSGlobalObject, callfra stream.done(globalThis); } this.promise.resolve(globalThis, JSC.JSValue.jsNumber(0)); - return .jsUndefined(); + return .js_undefined; } pub fn onFileStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -2347,7 +2347,7 @@ pub fn onFileStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callfram if (strong.get(globalThis)) |stream| { stream.cancel(globalThis); } - return .jsUndefined(); + return .js_undefined; } comptime { const jsonResolveRequestStream = JSC.toJSHostFn(onFileStreamResolveRequestStream); @@ -2911,7 +2911,7 @@ pub fn getName( _: JSC.JSValue, globalThis: *JSC.JSGlobalObject, ) JSValue { - return if (this.getNameString()) |name| name.toJS(globalThis) else .jsUndefined(); + return if (this.getNameString()) |name| name.toJS(globalThis) else .js_undefined; } pub fn setName( @@ -3017,7 +3017,7 @@ export fn Bun__Blob__getSizeForBindings(this: *Blob) callconv(.C) u64 { } pub fn getStat(this: *Blob, globalThis: *JSC.JSGlobalObject, callback: *JSC.CallFrame) bun.JSError!JSC.JSValue { - const store = this.store orelse return JSC.JSValue.jsUndefined(); + const store = this.store orelse return .js_undefined; // TODO: make this async for files return switch (store.data) { .file => |*file| { @@ -3037,7 +3037,7 @@ pub fn getStat(this: *Blob, globalThis: *JSC.JSGlobalObject, callback: *JSC.Call }; }, .s3 => S3File.getStat(this, globalThis, callback), - else => JSC.JSValue.jsUndefined(), + else => .js_undefined, }; } pub fn getSize(this: *Blob, _: *JSC.JSGlobalObject) JSValue { diff --git a/src/bun.js/webcore/Body.zig b/src/bun.js/webcore/Body.zig index 3283602367..4684c06575 100644 --- a/src/bun.js/webcore/Body.zig +++ b/src/bun.js/webcore/Body.zig @@ -175,7 +175,7 @@ pub const PendingValue = struct { break :brk globalThis.readableStreamToFormData(readable.value, switch (form_data.?.encoding) { .Multipart => |multipart| bun.String.init(multipart).toJS(globalThis), - .URLEncoded => .jsUndefined(), + .URLEncoded => .js_undefined, }); }, else => unreachable, @@ -306,7 +306,7 @@ pub const Value = union(Tag) { .SystemError => |system_error| system_error.toErrorInstance(globalObject), .Message => |message| message.toErrorInstance(globalObject), // do a early return in this case we don't need to create a new Strong - .JSValue => |js_value| return js_value.get() orelse JSC.JSValue.jsUndefined(), + .JSValue => |js_value| return js_value.get() orelse .js_undefined, }; this.* = .{ .JSValue = .create(js_value, globalObject) }; return js_value; @@ -1503,7 +1503,7 @@ pub const ValueBufferer = struct { var args = callframe.arguments_old(2); var sink: *@This() = args.ptr[args.len - 1].asPromisePtr(@This()); sink.handleResolveStream(true); - return JSValue.jsUndefined(); + return .js_undefined; } pub fn onRejectStream(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -1511,7 +1511,7 @@ pub const ValueBufferer = struct { var sink = args.ptr[args.len - 1].asPromisePtr(@This()); const err = args.ptr[0]; sink.handleRejectStream(err, true); - return JSValue.jsUndefined(); + return .js_undefined; } fn handleRejectStream(sink: *@This(), err: JSValue, is_async: bool) void { diff --git a/src/bun.js/webcore/Crypto.zig b/src/bun.js/webcore/Crypto.zig index bafe2e3290..2c0577cd06 100644 --- a/src/bun.js/webcore/Crypto.zig +++ b/src/bun.js/webcore/Crypto.zig @@ -114,7 +114,7 @@ comptime { pub fn Bun__randomUUIDv7_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.argumentsUndef(2).slice(); - var encoding_value: JSC.JSValue = .jsUndefined(); + var encoding_value: JSC.JSValue = .js_undefined; const encoding: JSC.Node.Encoding = brk: { if (arguments.len > 0) { @@ -137,7 +137,7 @@ pub fn Bun__randomUUIDv7_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallF else if (arguments.len == 1 and encoding_value.isUndefined()) arguments[0] else - .jsUndefined(); + .js_undefined; if (!timestamp_value.isUndefined()) { if (timestamp_value.isDate()) { diff --git a/src/bun.js/webcore/FileReader.zig b/src/bun.js/webcore/FileReader.zig index 3e13641f63..bf4bbe747b 100644 --- a/src/bun.js/webcore/FileReader.zig +++ b/src/bun.js/webcore/FileReader.zig @@ -611,7 +611,7 @@ pub fn onReaderDone(this: *FileReader) void { this.eventLoop().js.runCallback( cb, globalThis, - .jsUndefined(), + .js_undefined, &.{ JSC.ArrayBuffer.fromBytes( buffered.items, diff --git a/src/bun.js/webcore/FileSink.zig b/src/bun.js/webcore/FileSink.zig index 1a121eba42..8230ac6bb5 100644 --- a/src/bun.js/webcore/FileSink.zig +++ b/src/bun.js/webcore/FileSink.zig @@ -434,7 +434,7 @@ pub fn flushFromJS(this: *FileSink, globalThis: *JSGlobalObject, wait: bool) JSC } if (this.done) { - return .initResult(.jsUndefined()); + return .initResult(.js_undefined); } const rc = this.writer.flush(); diff --git a/src/bun.js/webcore/ObjectURLRegistry.zig b/src/bun.js/webcore/ObjectURLRegistry.zig index e9a6268a59..16e891aabb 100644 --- a/src/bun.js/webcore/ObjectURLRegistry.zig +++ b/src/bun.js/webcore/ObjectURLRegistry.zig @@ -120,7 +120,7 @@ fn Bun__revokeObjectURL_(globalObject: *JSC.JSGlobalObject, callframe: *JSC.Call } const str = arguments.ptr[0].toBunString(globalObject) catch @panic("unreachable"); if (!str.hasPrefixComptime("blob:")) { - return .jsUndefined(); + return .js_undefined; } const slice = str.toUTF8WithoutRef(bun.default_allocator); @@ -129,10 +129,10 @@ fn Bun__revokeObjectURL_(globalObject: *JSC.JSGlobalObject, callframe: *JSC.Call const sliced = slice.slice(); if (sliced.len < "blob:".len + UUID.stringLength) { - return .jsUndefined(); + return .js_undefined; } ObjectURLRegistry.singleton().revoke(sliced["blob:".len..]); - return .jsUndefined(); + return .js_undefined; } comptime { @@ -146,7 +146,7 @@ fn jsFunctionResolveObjectURL_(globalObject: *JSC.JSGlobalObject, callframe: *JS // Not thrown. // https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/lib/internal/blob.js#L441 if (arguments.len < 1) { - return .jsUndefined(); + return .js_undefined; } const str = try arguments.ptr[0].toBunString(globalObject); defer str.deref(); @@ -156,7 +156,7 @@ fn jsFunctionResolveObjectURL_(globalObject: *JSC.JSGlobalObject, callframe: *JS } if (!str.hasPrefixComptime("blob:") or str.length() < specifier_len) { - return .jsUndefined(); + return .js_undefined; } const slice = str.toUTF8WithoutRef(bun.default_allocator); @@ -165,7 +165,7 @@ fn jsFunctionResolveObjectURL_(globalObject: *JSC.JSGlobalObject, callframe: *JS const registry = ObjectURLRegistry.singleton(); const blob = registry.resolveAndDupeToJS(sliced["blob:".len..], globalObject); - return blob orelse .jsUndefined(); + return blob orelse .js_undefined; } pub const specifier_len = "blob:".len + UUID.stringLength; diff --git a/src/bun.js/webcore/ReadableStream.zig b/src/bun.js/webcore/ReadableStream.zig index 32a8d3c805..7913cc8a2c 100644 --- a/src/bun.js/webcore/ReadableStream.zig +++ b/src/bun.js/webcore/ReadableStream.zig @@ -570,7 +570,7 @@ pub fn NewSource( bun.assert(flag.isBoolean()); } return switch (this.context.setRawMode(flag == .true)) { - .result => .jsUndefined(), + .result => .js_undefined, .err => |e| e.toJSC(global), }; } @@ -618,7 +618,7 @@ pub fn NewSource( const view = arguments.ptr[0]; view.ensureStillAlive(); this.this_jsvalue = this_jsvalue; - var buffer = view.asArrayBuffer(globalThis) orelse return .jsUndefined(); + var buffer = view.asArrayBuffer(globalThis) orelse return .js_undefined; return processResult( this_jsvalue, globalThis, @@ -679,7 +679,7 @@ pub fn NewSource( JSC.markBinding(@src()); this.this_jsvalue = callFrame.this(); this.cancel(); - return .jsUndefined(); + return .js_undefined; } pub fn setOnCloseFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) bun.JSError!void { @@ -704,7 +704,7 @@ pub fn NewSource( this.globalThis = globalObject; if (value.isUndefined()) { - js.onDrainCallbackSetCached(this.this_jsvalue, globalObject, .jsUndefined()); + js.onDrainCallbackSetCached(this.this_jsvalue, globalObject, .js_undefined); return; } @@ -720,7 +720,7 @@ pub fn NewSource( JSC.markBinding(@src()); - return this.close_jsvalue.get() orelse .jsUndefined(); + return this.close_jsvalue.get() orelse .js_undefined; } pub fn getOnDrainFromJS(this: *ReadableStreamSourceType, globalObject: *JSC.JSGlobalObject) JSC.JSValue { @@ -732,7 +732,7 @@ pub fn NewSource( return val; } - return .jsUndefined(); + return .js_undefined; } pub fn updateRef(this: *ReadableStreamSourceType, globalObject: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -742,7 +742,7 @@ pub fn NewSource( const ref_or_unref = callFrame.argument(0).toBoolean(); this.setRef(ref_or_unref); - return .jsUndefined(); + return .js_undefined; } fn onClose(ptr: ?*anyopaque) void { @@ -768,7 +768,7 @@ pub fn NewSource( if (list.len > 0) { return JSC.ArrayBuffer.fromBytes(list.slice(), .Uint8Array).toJS(globalThis, null); } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn text(this: *ReadableStreamSourceType, globalThis: *JSGlobalObject, callFrame: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/bun.js/webcore/Request.zig b/src/bun.js/webcore/Request.zig index e7c69edea5..4728da2b98 100644 --- a/src/bun.js/webcore/Request.zig +++ b/src/bun.js/webcore/Request.zig @@ -94,7 +94,7 @@ pub const InternalJSEventCallback = struct { pub fn trigger(this: *InternalJSEventCallback, eventType: EventType, globalThis: *JSC.JSGlobalObject) bool { if (this.function.get()) |callback| { - _ = callback.call(globalThis, JSC.JSValue.jsUndefined(), &.{JSC.JSValue.jsNumber( + _ = callback.call(globalThis, .js_undefined, &.{JSC.JSValue.jsNumber( @intFromEnum(eventType), )}) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); return true; @@ -555,10 +555,10 @@ pub fn constructInto(globalThis: *JSC.JSGlobalObject, arguments: []const JSC.JSV if (arguments.len > 1 and arguments[1].isObject()) arguments[1] else if (is_first_argument_a_url) - .jsUndefined() + .js_undefined else url_or_object, - if (is_first_argument_a_url) .jsUndefined() else url_or_object, + if (is_first_argument_a_url) .js_undefined else url_or_object, }; const values_to_try = values_to_try_[0 .. @as(usize, @intFromBool(!is_first_argument_a_url)) + @as(usize, @intFromBool(arguments.len > 1 and arguments[1].isObject()))]; diff --git a/src/bun.js/webcore/Response.zig b/src/bun.js/webcore/Response.zig index ca92028ea1..6767cc8e10 100644 --- a/src/bun.js/webcore/Response.zig +++ b/src/bun.js/webcore/Response.zig @@ -77,7 +77,7 @@ pub export fn jsFunctionGetCompleteRequestOrResponseBodyValueAsArrayBuffer(globa const arguments = callframe.arguments_old(1); const this_value = arguments.ptr[0]; if (this_value.isEmptyOrUndefinedOrNull()) { - return .jsUndefined(); + return .js_undefined; } const body: *Body.Value = brk: { @@ -87,15 +87,15 @@ pub export fn jsFunctionGetCompleteRequestOrResponseBodyValueAsArrayBuffer(globa break :brk &request.body.value; } - return .jsUndefined(); + return .js_undefined; }; // Get the body if it's available synchronously. switch (body.*) { - .Used, .Empty, .Null => return .jsUndefined(), + .Used, .Empty, .Null => return .js_undefined, .Blob => |*blob| { if (blob.isBunFile()) { - return .jsUndefined(); + return .js_undefined; } defer body.* = .{ .Used = {} }; return blob.toArrayBuffer(globalObject, .transfer) catch return .zero; @@ -104,7 +104,7 @@ pub export fn jsFunctionGetCompleteRequestOrResponseBodyValueAsArrayBuffer(globa var any_blob = body.useAsAnyBlob(); return any_blob.toArrayBufferTransfer(globalObject) catch return .zero; }, - .Error, .Locked => return .jsUndefined(), + .Error, .Locked => return .js_undefined, } } diff --git a/src/bun.js/webcore/S3File.zig b/src/bun.js/webcore/S3File.zig index 988734f58c..12fd263ac8 100644 --- a/src/bun.js/webcore/S3File.zig +++ b/src/bun.js/webcore/S3File.zig @@ -542,7 +542,7 @@ pub fn getBucket(this: *Blob, globalThis: *JSC.JSGlobalObject) callconv(JSC.conv if (getBucketName(this)) |name| { return bun.String.createUTF8ForJS(globalThis, name); } - return .jsUndefined(); + return .js_undefined; } pub fn getPresignUrl(this: *Blob, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const args = callframe.arguments_old(1); diff --git a/src/bun.js/webcore/Sink.zig b/src/bun.js/webcore/Sink.zig index 3d46767777..e46bed8c48 100644 --- a/src/bun.js/webcore/Sink.zig +++ b/src/bun.js/webcore/Sink.zig @@ -228,11 +228,11 @@ pub fn JSSink(comptime SinkType: type, comptime abi_name: []const u8) type { } pub fn close(this: *@This(), _: ?Syscall.Error) void { - onClose(@as(SinkSignal, @bitCast(@intFromPtr(this))).cpp, JSValue.jsUndefined()); + onClose(@as(SinkSignal, @bitCast(@intFromPtr(this))).cpp, .js_undefined); } pub fn ready(this: *@This(), _: ?Blob.SizeType, _: ?Blob.SizeType) void { - onReady(@as(SinkSignal, @bitCast(@intFromPtr(this))).cpp, JSValue.jsUndefined(), JSValue.jsUndefined()); + onReady(@as(SinkSignal, @bitCast(@intFromPtr(this))).cpp, .js_undefined, .js_undefined); } pub fn start(_: *@This()) void {} @@ -463,7 +463,7 @@ pub fn JSSink(comptime SinkType: type, comptime abi_name: []const u8) type { pub fn close(globalThis: *JSGlobalObject, sink_ptr: ?*anyopaque) callconv(.C) JSValue { JSC.markBinding(@src()); - const this: *ThisSink = @ptrCast(@alignCast(sink_ptr orelse return .jsUndefined())); + const this: *ThisSink = @ptrCast(@alignCast(sink_ptr orelse return .js_undefined)); if (comptime @hasDecl(SinkType, "getPendingError")) { if (this.sink.getPendingError()) |err| { diff --git a/src/bun.js/webcore/TextEncoder.zig b/src/bun.js/webcore/TextEncoder.zig index 587db73801..e6845f3b88 100644 --- a/src/bun.js/webcore/TextEncoder.zig +++ b/src/bun.js/webcore/TextEncoder.zig @@ -200,7 +200,7 @@ pub export fn TextEncoder__encodeRopeString( array.ensureStillAlive(); if (encoder.any_non_ascii) { - return .jsUndefined(); + return .js_undefined; } if (array == .zero) { diff --git a/src/bun.js/webcore/fetch.zig b/src/bun.js/webcore/fetch.zig index d4a6b35115..3c5177bd79 100644 --- a/src/bun.js/webcore/fetch.zig +++ b/src/bun.js/webcore/fetch.zig @@ -353,7 +353,7 @@ pub const FetchTasklet = struct { } } - return JSValue.jsUndefined(); + return .js_undefined; } pub fn onRejectRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -372,7 +372,7 @@ pub const FetchTasklet = struct { } this.abortListener(err); - return JSValue.jsUndefined(); + return .js_undefined; } comptime { const jsonResolveRequestStream = JSC.toJSHostFn(onResolveRequestStream); @@ -794,7 +794,7 @@ pub const FetchTasklet = struct { const js_hostname = hostname.toJS(globalObject); js_hostname.ensureStillAlive(); js_cert.ensureStillAlive(); - const check_result = check_server_identity.call(globalObject, .jsUndefined(), &.{ js_hostname, js_cert }) catch |err| globalObject.takeException(err); + const check_result = check_server_identity.call(globalObject, .js_undefined, &.{ js_hostname, js_cert }) catch |err| globalObject.takeException(err); // > Returns object [...] on failure if (check_result.isAnyError()) { @@ -1499,7 +1499,7 @@ pub fn Bun__fetchPreconnect_( } bun.http.AsyncHTTP.preconnect(url, true); - return .jsUndefined(); + return .js_undefined; } const StringOrURL = struct { diff --git a/src/bun.js/webcore/prompt.zig b/src/bun.js/webcore/prompt.zig index e17eac62be..0719b10864 100644 --- a/src/bun.js/webcore/prompt.zig +++ b/src/bun.js/webcore/prompt.zig @@ -31,14 +31,14 @@ fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSErr // 5. Show message to the user, treating U+000A LF as a line break. output.writeAll(message.slice()) catch { // 1. If we cannot show simple dialogs for this, then return. - return .jsUndefined(); + return .js_undefined; }; } } output.writeAll(if (has_message) " [Enter] " else "Alert [Enter] ") catch { // 1. If we cannot show simple dialogs for this, then return. - return .jsUndefined(); + return .js_undefined; }; // 6. Invoke WebDriver BiDi user prompt opened with this, "alert", and message. @@ -56,7 +56,7 @@ fn alert(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSErr // 8. Invoke WebDriver BiDi user prompt closed with this and true. // * Again, not necessary in a server context. - return .jsUndefined(); + return .js_undefined; } fn confirm(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 101ab14b21..43f8e8f508 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -32,7 +32,7 @@ pub const Start = union(Tag) { pub fn toJS(this: Start, globalThis: *JSGlobalObject) JSC.JSValue { switch (this) { .empty, .ready => { - return .jsUndefined(); + return .js_undefined; }, .chunk_size => |chunk| { return JSC.JSValue.jsNumber(@as(Blob.SizeType, @intCast(chunk))); @@ -47,7 +47,7 @@ pub const Start = union(Tag) { return JSC.ArrayBuffer.create(globalThis, list.slice(), .Uint8Array); }, else => { - return .jsUndefined(); + return .js_undefined; }, } } diff --git a/src/bun_js.zig b/src/bun_js.zig index 85bd025122..b9ee1001a7 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -413,11 +413,11 @@ pub const Run = struct { if (this.ctx.runtime_options.eval.eval_and_print) { const to_print = brk: { - const result: JSC.JSValue = vm.entry_point_result.value.get() orelse .jsUndefined(); + const result: JSC.JSValue = vm.entry_point_result.value.get() orelse .js_undefined; if (result.asAnyPromise()) |promise| { switch (promise.status(vm.jsc)) { .pending => { - result._then2(vm.global, .jsUndefined(), Bun__onResolveEntryPointResult, Bun__onRejectEntryPointResult); + result._then2(vm.global, .js_undefined, Bun__onResolveEntryPointResult, Bun__onRejectEntryPointResult); vm.tick(); vm.eventLoop().autoTickActive(); @@ -490,7 +490,7 @@ pub export fn Bun__onResolveEntryPointResult(global: *JSC.JSGlobalObject, callfr const result = arguments[0]; result.print(global, .Log, .Log); Global.exit(global.bunVM().exit_handler.exit_code); - return .jsUndefined(); + return .js_undefined; } pub export fn Bun__onRejectEntryPointResult(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(JSC.conv) noreturn { @@ -498,7 +498,7 @@ pub export fn Bun__onRejectEntryPointResult(global: *JSC.JSGlobalObject, callfra const result = arguments[0]; result.print(global, .Log, .Log); Global.exit(global.bunVM().exit_handler.exit_code); - return .jsUndefined(); + return .js_undefined; } noinline fn dumpBuildError(vm: *JSC.VirtualMachine) void { diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index f4ec3d51ae..701c111158 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -939,13 +939,13 @@ pub const upgrade_js_bindings = struct { /// For testing upgrades when the temp directory has an open handle without FILE_SHARE_DELETE. /// Windows only pub fn jsOpenTempDirWithoutSharingDelete(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!bun.JSC.JSValue { - if (comptime !Environment.isWindows) return .jsUndefined(); + if (comptime !Environment.isWindows) return .js_undefined; const w = std.os.windows; var buf: bun.WPathBuffer = undefined; const tmpdir_path = fs.FileSystem.RealFS.getDefaultTempDir(); const path = switch (bun.sys.normalizePathWindows(u8, bun.invalid_fd, tmpdir_path, &buf, .{})) { - .err => return .jsUndefined(), + .err => return .js_undefined, .result => |norm| norm, }; @@ -989,17 +989,17 @@ pub const upgrade_js_bindings = struct { else => {}, } - return .jsUndefined(); + return .js_undefined; } pub fn jsCloseTempDirHandle(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { - if (comptime !Environment.isWindows) return .jsUndefined(); + if (comptime !Environment.isWindows) return .js_undefined; if (tempdir_fd) |fd| { fd.close(); } - return .jsUndefined(); + return .js_undefined; } }; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index e805f6545f..edbd263bfb 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -1779,9 +1779,9 @@ pub const js_bindings = struct { } pub fn jsGetMachOImageZeroOffset(_: *bun.JSC.JSGlobalObject, _: *bun.JSC.CallFrame) bun.JSError!JSValue { - if (!bun.Environment.isMac) return .jsUndefined(); + if (!bun.Environment.isMac) return .js_undefined; - const header = std.c._dyld_get_image_header(0) orelse return .jsUndefined(); + const header = std.c._dyld_get_image_header(0) orelse return .js_undefined; const base_address = @intFromPtr(header); const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(0); @@ -1793,7 +1793,7 @@ pub const js_bindings = struct { const ptr: [*]align(1) u64 = @ptrFromInt(0xDEADBEEF); ptr[0] = 0xDEADBEEF; std.mem.doNotOptimizeAway(&ptr); - return .jsUndefined(); + return .js_undefined; } pub fn jsPanic(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { diff --git a/src/css/css_internals.zig b/src/css/css_internals.zig index 53b076c09a..47bd0e5833 100644 --- a/src/css/css_internals.zig +++ b/src/css/css_internals.zig @@ -320,7 +320,7 @@ pub fn attrTest(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun. .initOutsideOfBundler(&import_records), ) catch |e| { bun.handleErrorReturnTrace(e, @errorReturnTrace()); - return .jsUndefined(); + return .js_undefined; }; return bun.String.fromBytes(result.code).toJS(globalThis); diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index 8265028723..9dccf83abf 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -332,7 +332,7 @@ pub const hostent_with_ttls = struct { bun.dns.addressToJS(&std.net.Address.initIp4(addr[0..4].*, 0), globalThis)) catch return globalThis.throwOutOfMemoryValue(); const ttl: ?c_int = if (count < this.ttls.len) this.ttls[count] else null; - const resultObject = JSC.JSValue.createObject2(globalThis, &addressKey, &ttlKey, addrString, if (ttl) |val| JSC.jsNumber(val) else .jsUndefined()); + const resultObject = JSC.JSValue.createObject2(globalThis, &addressKey, &ttlKey, addrString, if (ttl) |val| JSC.jsNumber(val) else .js_undefined); array.putIndex(globalThis, count, resultObject); } @@ -439,7 +439,7 @@ pub const struct_nameinfo = extern struct { const node_slice = this.node[0..node_len]; array.putIndex(globalThis, 0, JSC.ZigString.fromUTF8(node_slice).toJS(globalThis)); } else { - array.putIndex(globalThis, 0, .jsUndefined()); + array.putIndex(globalThis, 0, .js_undefined); } if (this.service != null) { @@ -447,7 +447,7 @@ pub const struct_nameinfo = extern struct { const service_slice = this.service[0..service_len]; array.putIndex(globalThis, 1, JSC.ZigString.fromUTF8(service_slice).toJS(globalThis)); } else { - array.putIndex(globalThis, 1, .jsUndefined()); + array.putIndex(globalThis, 1, .js_undefined); } return array; @@ -2021,9 +2021,9 @@ pub fn Bun__canonicalizeIP_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.Cal const addr_slice = addr.toSlice(bun.default_allocator); const addr_str = addr_slice.slice(); if (addr_str.len >= INET6_ADDRSTRLEN) { - return .jsUndefined(); + return .js_undefined; } - for (addr_str) |char| if (char == '/') return .jsUndefined(); // CIDR not allowed + for (addr_str) |char| if (char == '/') return .js_undefined; // CIDR not allowed var ip_std_text: [INET6_ADDRSTRLEN + 1]u8 = undefined; // we need a null terminated string as input @@ -2036,12 +2036,12 @@ pub fn Bun__canonicalizeIP_(globalThis: *JSC.JSGlobalObject, callframe: *JSC.Cal if (ares_inet_pton(af, &ip_addr, &ip_std_text) != 1) { af = AF.INET6; if (ares_inet_pton(af, &ip_addr, &ip_std_text) != 1) { - return .jsUndefined(); + return .js_undefined; } } // ip_addr will contain the null-terminated string of the cannonicalized IP if (ares_inet_ntop(af, &ip_std_text, &ip_addr, @sizeOf(@TypeOf(ip_addr))) == null) { - return .jsUndefined(); + return .js_undefined; } // use the null-terminated size to return the string const size = bun.len(bun.cast([*:0]u8, &ip_addr)); diff --git a/src/deps/uws/UpgradedDuplex.zig b/src/deps/uws/UpgradedDuplex.zig index d05fa1e247..31a0896828 100644 --- a/src/deps/uws/UpgradedDuplex.zig +++ b/src/deps/uws/UpgradedDuplex.zig @@ -149,7 +149,7 @@ fn onReceivedData( const data_arg = args.ptr[0]; if (this.origin.has()) { if (data_arg.isEmptyOrUndefinedOrNull()) { - return JSC.JSValue.jsUndefined(); + return .js_undefined; } if (data_arg.asArrayBuffer(globalObject)) |array_buffer| { // yay we can read the data @@ -164,7 +164,7 @@ fn onReceivedData( } } } - return JSC.JSValue.jsUndefined(); + return .js_undefined; } fn onEnd( @@ -203,7 +203,7 @@ fn onWritable( this.handlers.onWritable(this.handlers.ctx); } - return JSC.JSValue.jsUndefined(); + return .js_undefined; } fn onCloseJS( @@ -223,7 +223,7 @@ fn onCloseJS( } } - return JSC.JSValue.jsUndefined(); + return .js_undefined; } pub fn onTimeout(this: *UpgradedDuplex) EventLoopTimer.Arm { diff --git a/src/install/dependency.zig b/src/install/dependency.zig index da371b5723..217160edb7 100644 --- a/src/install/dependency.zig +++ b/src/install/dependency.zig @@ -783,10 +783,10 @@ pub const Version = struct { pub fn inferFromJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { const arguments = callframe.arguments_old(1).slice(); if (arguments.len == 0 or !arguments[0].isString()) { - return .jsUndefined(); + return .js_undefined; } - const tag = try Tag.fromJS(globalObject, arguments[0]) orelse return .jsUndefined(); + const tag = try Tag.fromJS(globalObject, arguments[0]) orelse return .js_undefined; var str = bun.String.init(@tagName(tag)); return str.transferToJS(globalObject); } @@ -1284,19 +1284,19 @@ pub fn fromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JS var stack = std.heap.stackFallback(1024, arena.allocator()); const allocator = stack.get(); - const alias_value: JSC.JSValue = if (arguments.len > 0) arguments[0] else .jsUndefined(); + const alias_value: JSC.JSValue = if (arguments.len > 0) arguments[0] else .js_undefined; if (!alias_value.isString()) { - return .jsUndefined(); + return .js_undefined; } const alias_slice = try alias_value.toSlice(globalThis, allocator); defer alias_slice.deinit(); if (alias_slice.len == 0) { - return .jsUndefined(); + return .js_undefined; } - const name_value: JSC.JSValue = if (arguments.len > 1) arguments[1] else .jsUndefined(); + const name_value: JSC.JSValue = if (arguments.len > 1) arguments[1] else .js_undefined; const name_slice = try name_value.toSlice(globalThis, allocator); defer name_slice.deinit(); @@ -1320,7 +1320,7 @@ pub fn fromJS(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JS return globalThis.throwValue(try log.toJS(globalThis, bun.default_allocator, "Failed to parse dependency")); } - return .jsUndefined(); + return .js_undefined; }; if (log.msgs.items.len > 0) { diff --git a/src/install/install.zig b/src/install/install.zig index 7febf9b40b..04a4e50838 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -8267,11 +8267,11 @@ pub const PackageManager = struct { } if (globalThis.hasException()) return .zero; } else { - return .jsUndefined(); + return .js_undefined; } if (all_positionals.items.len == 0) { - return .jsUndefined(); + return .js_undefined; } var array = Array{}; @@ -8279,7 +8279,7 @@ pub const PackageManager = struct { const update_requests = parseWithError(allocator, null, &log, all_positionals.items, &array, .add, false) catch { return globalThis.throwValue(try log.toJS(globalThis, bun.default_allocator, "Failed to parse dependencies")); }; - if (update_requests.len == 0) return .jsUndefined(); + if (update_requests.len == 0) return .js_undefined; if (log.msgs.items.len > 0) { return globalThis.throwValue(try log.toJS(globalThis, bun.default_allocator, "Failed to parse dependencies")); diff --git a/src/js_ast.zig b/src/js_ast.zig index 42b4f9b1b4..b2f5021920 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -6342,7 +6342,7 @@ pub const Expr = struct { .e_object => |e| e.toJS(allocator, globalObject), .e_string => |e| e.toJS(allocator, globalObject), .e_null => JSC.JSValue.null, - .e_undefined => .jsUndefined(), + .e_undefined => .js_undefined, .e_boolean => |boolean| if (boolean.value) JSC.JSValue.true else diff --git a/src/logger.zig b/src/logger.zig index dfd532afb9..0e69ad5fe7 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -748,7 +748,7 @@ pub const Log = struct { const count = @as(u16, @intCast(@min(msgs.len, errors_stack.len))); switch (count) { - 0 => return .jsUndefined(), + 0 => return .js_undefined, 1 => { const msg = msgs[0]; return switch (msg.metadata) { diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 98c3d92e7f..334b96c173 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -270,7 +270,7 @@ pub export fn napi_get_undefined(env_: napi_env, result_: ?*napi_value) napi_sta const result = result_ orelse { return env.invalidArg(); }; - result.set(env, JSValue.jsUndefined()); + result.set(env, .js_undefined); return env.ok(); } pub export fn napi_get_null(env_: napi_env, result_: ?*napi_value) napi_status { @@ -674,7 +674,7 @@ pub export fn napi_make_callback(env_: napi_env, _: *anyopaque, recv_: napi_valu if (recv != .zero) recv else - .jsUndefined(), + .js_undefined, if (arg_count > 0 and args != null) @as([*]const JSC.JSValue, @ptrCast(args.?))[0..arg_count] else @@ -1616,16 +1616,16 @@ pub const ThreadSafeFunction = struct { switch (this.callback) { .js => |strong| { - const js: JSValue = strong.get() orelse .jsUndefined(); + const js: JSValue = strong.get() orelse .js_undefined; if (js.isEmptyOrUndefinedOrNull()) { return; } - _ = js.call(globalObject, .jsUndefined(), &.{}) catch |err| + _ = js.call(globalObject, .js_undefined, &.{}) catch |err| globalObject.reportActiveExceptionAsUnhandled(err); }, .c => |cb| { - const js: JSValue = cb.js.get() orelse .jsUndefined(); + const js: JSValue = cb.js.get() orelse .js_undefined; const handle_scope = NapiHandleScope.open(env, false); defer if (handle_scope) |scope| scope.close(env); diff --git a/src/patch.zig b/src/patch.zig index 48aba3295c..027bdd0258 100644 --- a/src/patch.zig +++ b/src/patch.zig @@ -1168,11 +1168,11 @@ pub const TestingAPIs = struct { const patchfile_js = arguments.nextEat() orelse { globalThis.throw("apply: expected at least 1 argument, got 0", .{}) catch {}; - return .initErr(.jsUndefined()); + return .initErr(.js_undefined); }; const dir_fd = if (arguments.nextEat()) |dir_js| brk: { - var bunstr = dir_js.toBunString(globalThis) catch return .initErr(.jsUndefined()); + var bunstr = dir_js.toBunString(globalThis) catch return .initErr(.js_undefined); defer bunstr.deref(); const path = bunstr.toOwnedSliceZ(bun.default_allocator) catch unreachable; defer bun.default_allocator.free(path); @@ -1180,13 +1180,13 @@ pub const TestingAPIs = struct { break :brk switch (bun.sys.open(path, bun.O.DIRECTORY | bun.O.RDONLY, 0)) { .err => |e| { globalThis.throwValue(e.withPath(path).toJSC(globalThis)) catch {}; - return .initErr(.jsUndefined()); + return .initErr(.js_undefined); }, .result => |fd| fd, }; } else bun.FileDescriptor.cwd(); - const patchfile_bunstr = patchfile_js.toBunString(globalThis) catch return .initErr(.jsUndefined()); + const patchfile_bunstr = patchfile_js.toBunString(globalThis) catch return .initErr(.js_undefined); defer patchfile_bunstr.deref(); const patchfile_src = patchfile_bunstr.toUTF8(bun.default_allocator); @@ -1198,7 +1198,7 @@ pub const TestingAPIs = struct { patchfile_src.deinit(); globalThis.throwError(e, "failed to parse patchfile") catch {}; - return .initErr(.jsUndefined()); + return .initErr(.js_undefined); }; return .{ diff --git a/src/s3/client.zig b/src/s3/client.zig index 32689279c7..7f6eb6c76f 100644 --- a/src/s3/client.zig +++ b/src/s3/client.zig @@ -376,7 +376,7 @@ pub fn onUploadStreamResolveRequestStream(globalThis: *JSC.JSGlobalObject, callf this.readable_stream_ref.deinit(); this.task.continueStream(); - return .jsUndefined(); + return .js_undefined; } pub fn onUploadStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -403,7 +403,7 @@ pub fn onUploadStreamRejectRequestStream(globalThis: *JSC.JSGlobalObject, callfr } this.task.continueStream(); - return .jsUndefined(); + return .js_undefined; } comptime { const jsonResolveRequestStream = JSC.toJSHostFn(onUploadStreamResolveRequestStream); diff --git a/src/shell/ParsedShellScript.zig b/src/shell/ParsedShellScript.zig index e9c4a04790..f48092dc6c 100644 --- a/src/shell/ParsedShellScript.zig +++ b/src/shell/ParsedShellScript.zig @@ -54,12 +54,12 @@ pub fn setCwd(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: }; const str = try bun.String.fromJS(str_js, globalThis); this.cwd = str; - return .jsUndefined(); + return .js_undefined; } pub fn setQuiet(this: *ParsedShellScript, _: *JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { this.quiet = true; - return .jsUndefined(); + return .js_undefined; } pub fn setEnv(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -97,7 +97,7 @@ pub fn setEnv(this: *ParsedShellScript, globalThis: *JSGlobalObject, callframe: previous.deinit(); } this.export_env = env; - return .jsUndefined(); + return .js_undefined; } pub fn createParsedShellScript(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index b4ebb4f0e5..38cdde94e0 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -1137,7 +1137,7 @@ pub const Interpreter = struct { root.start(); if (globalThis.hasException()) return error.JSError; - return .jsUndefined(); + return .js_undefined; } fn ioToJSValue(globalThis: *JSGlobalObject, buf: *bun.ByteList) JSValue { @@ -1185,13 +1185,13 @@ pub const Interpreter = struct { const loop = this.event_loop.js; this.keep_alive.disable(); loop.enter(); - _ = resolve.call(globalThis, .jsUndefined(), &.{ + _ = resolve.call(globalThis, .js_undefined, &.{ JSValue.jsNumberFromU16(exit_code), this.getBufferedStdout(globalThis), this.getBufferedStderr(globalThis), }) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); - JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .jsUndefined()); - JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .jsUndefined()); + JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .js_undefined); + JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .js_undefined); loop.exit(); } } @@ -1220,8 +1220,8 @@ pub const Interpreter = struct { this.getBufferedStdout(globalThis), this.getBufferedStderr(globalThis), }) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); - JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .jsUndefined()); - JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .jsUndefined()); + JSC.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .js_undefined); + JSC.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .js_undefined); loop.exit(); } @@ -1269,7 +1269,7 @@ pub const Interpreter = struct { pub fn setQuiet(this: *ThisInterpreter, _: *JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { log("Interpreter(0x{x}) setQuiet()", .{@intFromPtr(this)}); this.flags.quiet = true; - return .jsUndefined(); + return .js_undefined; } pub fn setCwd(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -1284,7 +1284,7 @@ pub const Interpreter = struct { }, .result => {}, } - return .jsUndefined(); + return .js_undefined; } pub fn setEnv(this: *ThisInterpreter, globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue { @@ -1320,7 +1320,7 @@ pub const Interpreter = struct { this.root_shell.export_env.insert(keyref, valueref); } - return .jsUndefined(); + return .js_undefined; } pub fn isRunning( diff --git a/src/shell/subproc.zig b/src/shell/subproc.zig index dbde2688f8..ed36bc17cf 100644 --- a/src/shell/subproc.zig +++ b/src/shell/subproc.zig @@ -241,8 +241,8 @@ pub const ShellSubprocess = struct { pub fn toJS(this: *Writable, globalThis: *JSC.JSGlobalObject, subprocess: *Subprocess) JSValue { return switch (this.*) { .fd => |fd| JSValue.jsNumber(fd), - .memfd, .ignore => JSValue.jsUndefined(), - .buffer, .inherit => JSValue.jsUndefined(), + .memfd, .ignore => .js_undefined, + .buffer, .inherit => .js_undefined, .pipe => |pipe| { this.* = .{ .ignore = {} }; if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) { @@ -1295,7 +1295,7 @@ pub const PipeReader = struct { return JSC.MarkedArrayBuffer.fromBytes(bytes, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis); }, else => { - return .jsUndefined(); + return .js_undefined; }, } } diff --git a/src/sql/postgres.zig b/src/sql/postgres.zig index 18374c608b..df0664368a 100644 --- a/src/sql/postgres.zig +++ b/src/sql/postgres.zig @@ -245,7 +245,7 @@ pub const PostgresSQLContext = struct { ctx.onQueryResolveFn.set(globalObject, callframe.argument(0)); ctx.onQueryRejectFn.set(globalObject, callframe.argument(1)); - return .jsUndefined(); + return .js_undefined; } comptime { @@ -541,10 +541,10 @@ pub const PostgresSQLQuery = struct { event_loop.runCallback(function, globalObject, thisValue, &.{ targetValue, - consumePendingValue(thisValue, globalObject) orelse .jsUndefined(), + consumePendingValue(thisValue, globalObject) orelse .js_undefined, tag.toJSTag(globalObject), tag.toJSNumber(), - if (connection == .zero) .jsUndefined() else PostgresSQLConnection.js.queriesGetCached(connection) orelse .jsUndefined(), + if (connection == .zero) .js_undefined else PostgresSQLConnection.js.queriesGetCached(connection) orelse .js_undefined, JSValue.jsBoolean(is_last), }); } @@ -578,8 +578,8 @@ pub const PostgresSQLQuery = struct { return globalThis.throw("values must be an array", .{}); } - const pending_value: JSValue = args.nextEat() orelse .jsUndefined(); - const columns: JSValue = args.nextEat() orelse .jsUndefined(); + const pending_value: JSValue = args.nextEat() orelse .js_undefined; + const columns: JSValue = args.nextEat() orelse .js_undefined; const js_bigint: JSValue = args.nextEat() orelse .false; const js_simple: JSValue = args.nextEat() orelse .false; @@ -628,12 +628,12 @@ pub const PostgresSQLQuery = struct { pub fn doDone(this: *@This(), globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { _ = globalObject; this.flags.is_done = true; - return .jsUndefined(); + return .js_undefined; } pub fn setPendingValue(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const result = callframe.argument(0); js.pendingValueSetCached(this.thisValue.get(), globalObject, result); - return .jsUndefined(); + return .js_undefined; } pub fn setMode(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { const js_mode = callframe.argument(0); @@ -645,7 +645,7 @@ pub const PostgresSQLQuery = struct { this.flags.result_mode = std.meta.intToEnum(PostgresSQLQueryResultMode, mode) catch { return globalObject.throwInvalidArgumentTypeValue("mode", "Number", js_mode); }; - return .jsUndefined(); + return .js_undefined; } pub fn doRun(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -704,10 +704,10 @@ pub const PostgresSQLQuery = struct { } else { connection.resetConnectionTimeout(); } - return .jsUndefined(); + return .js_undefined; } - const columns_value: JSValue = js.columnsGetCached(this_value) orelse .jsUndefined(); + const columns_value: JSValue = js.columnsGetCached(this_value) orelse .js_undefined; var signature = Signature.generate(globalObject, query_str.slice(), binding_value, columns_value, connection.prepared_statement_id, connection.flags.use_unnamed_prepared_statements) catch |err| { if (!globalObject.hasException()) @@ -821,7 +821,7 @@ pub const PostgresSQLQuery = struct { } else { connection.resetConnectionTimeout(); } - return .jsUndefined(); + return .js_undefined; } pub fn doCancel(this: *PostgresSQLQuery, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue { @@ -829,7 +829,7 @@ pub const PostgresSQLQuery = struct { _ = globalObject; _ = this; - return .jsUndefined(); + return .js_undefined; } comptime { @@ -1182,7 +1182,7 @@ pub const PostgresSQLConnection = struct { statements: PreparedStatementsMap, prepared_statement_id: u64 = 0, pending_activity_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - js_value: JSValue = .jsUndefined(), + js_value: JSValue = .js_undefined, backend_parameters: bun.StringMap = bun.StringMap.init(bun.default_allocator, true), backend_key_data: protocol.BackendKeyData = .{}, @@ -1410,7 +1410,7 @@ pub const PostgresSQLConnection = struct { return value; } - return .jsUndefined(); + return .js_undefined; } pub fn setOnConnect(_: *PostgresSQLConnection, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { @@ -1422,7 +1422,7 @@ pub const PostgresSQLConnection = struct { return value; } - return .jsUndefined(); + return .js_undefined; } pub fn setOnClose(_: *PostgresSQLConnection, thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { @@ -2046,17 +2046,17 @@ pub const PostgresSQLConnection = struct { pub fn doRef(this: *@This(), _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.poll_ref.ref(this.globalObject.bunVM()); this.updateHasPendingActivity(); - return .jsUndefined(); + return .js_undefined; } pub fn doUnref(this: *@This(), _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { this.poll_ref.unref(this.globalObject.bunVM()); this.updateHasPendingActivity(); - return .jsUndefined(); + return .js_undefined; } pub fn doFlush(this: *PostgresSQLConnection, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue { this.flushData(); - return .jsUndefined(); + return .js_undefined; } pub fn deref(this: *@This()) void { @@ -2074,7 +2074,7 @@ pub const PostgresSQLConnection = struct { this.disconnect(); this.write_buffer.deinit(bun.default_allocator); - return .jsUndefined(); + return .js_undefined; } pub fn stopTimers(this: *PostgresSQLConnection) void { @@ -2378,13 +2378,13 @@ pub const PostgresSQLConnection = struct { .DataRow => { const request = this.current() orelse return error.ExpectedRequest; var statement = request.statement orelse return error.ExpectedStatement; - var structure: JSValue = .jsUndefined(); + var structure: JSValue = .js_undefined; var cached_structure: ?PostgresCachedStructure = null; // explicit use switch without else so if new modes are added, we don't forget to check for duplicate fields switch (request.flags.result_mode) { .objects => { cached_structure = statement.structure(this.js_value, this.globalObject); - structure = cached_structure.?.jsValue() orelse .jsUndefined(); + structure = cached_structure.?.jsValue() orelse .js_undefined; }, .raw, .values => { // no need to check for duplicate fields or structure diff --git a/src/valkey/js_valkey.zig b/src/valkey/js_valkey.zig index dd614e8fa5..0513723634 100644 --- a/src/valkey/js_valkey.zig +++ b/src/valkey/js_valkey.zig @@ -165,7 +165,7 @@ pub const JSValkeyClient = struct { // If already connected, resolve immediately if (this.client.status == .connected) { - return JSC.JSPromise.resolvedPromiseValue(globalObject, js.helloGetCached(this_value) orelse .jsUndefined()); + return JSC.JSPromise.resolvedPromiseValue(globalObject, js.helloGetCached(this_value) orelse .js_undefined); } if (js.connectionPromiseGetCached(this_value)) |promise| { @@ -219,17 +219,17 @@ pub const JSValkeyClient = struct { pub fn jsDisconnect(this: *JSValkeyClient, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSValue { if (this.client.status == .disconnected) { - return .jsUndefined(); + return .js_undefined; } this.client.disconnect(); - return .jsUndefined(); + return .js_undefined; } pub fn getOnConnect(_: *JSValkeyClient, thisValue: JSValue, _: *JSC.JSGlobalObject) JSValue { if (js.onconnectGetCached(thisValue)) |value| { return value; } - return .jsUndefined(); + return .js_undefined; } pub fn setOnConnect(_: *JSValkeyClient, thisValue: JSValue, globalObject: *JSC.JSGlobalObject, value: JSValue) void { @@ -240,7 +240,7 @@ pub const JSValkeyClient = struct { if (js.oncloseGetCached(thisValue)) |value| { return value; } - return .jsUndefined(); + return .js_undefined; } pub fn setOnClose(_: *JSValkeyClient, thisValue: JSValue, globalObject: *JSC.JSGlobalObject, value: JSValue) void { @@ -406,7 +406,7 @@ pub const JSValkeyClient = struct { defer event_loop.exit(); if (this.this_value.tryGet()) |this_value| { - const hello_value: JSValue = value.toJS(globalObject) catch .jsUndefined(); + const hello_value: JSValue = value.toJS(globalObject) catch .js_undefined; js.helloSetCached(this_value, globalObject, hello_value); // Call onConnect callback if defined by the user if (js.onconnectGetCached(this_value)) |on_connect| { From 9499f215182fbc471a62b05f34235c5ccaa6f12a Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 13 Jun 2025 16:16:14 -0700 Subject: [PATCH 014/133] Update .git-blame-ignore-revs --- .git-blame-ignore-revs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 62e969a32f..597a1a7fe7 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,4 +5,6 @@ # # git config blame.ignoreRevsFile .git-blame-ignore-revs # -4ec410e0d7c5f6a712c323444edbf56b48d432d8 # make @import("bun") work in zig (#19096) \ No newline at end of file +4ec410e0d7c5f6a712c323444edbf56b48d432d8 # make @import("bun") work in zig (#19096) +dedd433cbf2e2fe38e51bc166e08fbcc601ad42b # JSValue.undefined -> .jsUndefined() +6b4662ff55f58247cc2fd22e85b4f9805b0950a5 # JSValue.jsUndefined() -> .js_undefined From e0924ef226696f34de5feab381323a5e02449a99 Mon Sep 17 00:00:00 2001 From: pfg Date: Fri, 13 Jun 2025 17:30:04 -0700 Subject: [PATCH 015/133] Implement `require('tls').getCACertificates()` (#20364) Co-authored-by: pfgithub <6010774+pfgithub@users.noreply.github.com> --- packages/bun-usockets/src/crypto/openssl.c | 5 +- .../bun-usockets/src/crypto/root_certs.cpp | 27 ++++++-- .../src/crypto/root_certs_header.h | 14 ++++ src/bun.js/bindings/NodeTLS.cpp | 55 +++++++++++++--- src/bun.js/bindings/NodeTLS.h | 4 +- src/js/internal-for-testing.ts | 2 +- src/js/node/tls.ts | 64 ++++++++++++++++++- test/js/node/test/common/tls.js | 13 ++++ .../tls-check-extra-ca-certificates.js | 17 +++++ .../test/fixtures/tls-get-ca-certificates.js | 11 ++++ ...-tls-get-ca-certificates-bundled-subset.js | 17 +++++ .../test-tls-get-ca-certificates-bundled.js | 20 ++++++ .../test-tls-get-ca-certificates-default.js | 20 ++++++ .../test-tls-get-ca-certificates-error.js | 20 ++++++ ...est-tls-get-ca-certificates-extra-empty.js | 29 +++++++++ ...st-tls-get-ca-certificates-extra-subset.js | 16 +++++ .../test-tls-get-ca-certificates-extra.js | 29 +++++++++ test/js/node/tls/node-tls-internals.test.ts | 5 +- 18 files changed, 341 insertions(+), 27 deletions(-) create mode 100644 packages/bun-usockets/src/crypto/root_certs_header.h create mode 100644 test/js/node/test/fixtures/tls-check-extra-ca-certificates.js create mode 100644 test/js/node/test/fixtures/tls-get-ca-certificates.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-default.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-error.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c index 49b535d818..dff3399fb6 100644 --- a/packages/bun-usockets/src/crypto/openssl.c +++ b/packages/bun-usockets/src/crypto/openssl.c @@ -44,10 +44,7 @@ void *sni_find(void *sni, const char *hostname); #include #endif -#include "./root_certs.h" - -/* These are in root_certs.cpp */ -extern X509_STORE *us_get_default_ca_store(); +#include "./root_certs_header.h" struct loop_ssl_data { char *ssl_read_input, *ssl_read_output; diff --git a/packages/bun-usockets/src/crypto/root_certs.cpp b/packages/bun-usockets/src/crypto/root_certs.cpp index f218ad7c5a..877685a8c8 100644 --- a/packages/bun-usockets/src/crypto/root_certs.cpp +++ b/packages/bun-usockets/src/crypto/root_certs.cpp @@ -1,10 +1,9 @@ // MSVC doesn't support C11 stdatomic.h propertly yet. // so we use C++ std::atomic instead. #include "./root_certs.h" +#include "./root_certs_header.h" #include "./internal/internal.h" #include -#include -#include #include static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]); @@ -134,6 +133,23 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) { return root_certs_size; } +struct us_default_ca_certificates { + X509 *root_cert_instances[root_certs_size]; + STACK_OF(X509) *root_extra_cert_instances; +}; + +us_default_ca_certificates* us_get_default_ca_certificates() { + static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL}; + + us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances); + + return &default_ca_certificates; +} + +STACK_OF(X509) *us_get_root_extra_cert_instances() { + return us_get_default_ca_certificates()->root_extra_cert_instances; +} + extern "C" X509_STORE *us_get_default_ca_store() { X509_STORE *store = X509_STORE_new(); if (store == NULL) { @@ -145,10 +161,9 @@ extern "C" X509_STORE *us_get_default_ca_store() { return NULL; } - static X509 *root_cert_instances[root_certs_size] = {NULL}; - static STACK_OF(X509) *root_extra_cert_instances = NULL; - - us_internal_init_root_certs(root_cert_instances, root_extra_cert_instances); + 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; // load all root_cert_instances on the default ca store for (size_t i = 0; i < root_certs_size; i++) { diff --git a/packages/bun-usockets/src/crypto/root_certs_header.h b/packages/bun-usockets/src/crypto/root_certs_header.h new file mode 100644 index 0000000000..92515fcbfe --- /dev/null +++ b/packages/bun-usockets/src/crypto/root_certs_header.h @@ -0,0 +1,14 @@ +#include +#include + +#ifdef __cplusplus +#define CPPDECL extern "C" + +STACK_OF(X509) *us_get_root_extra_cert_instances(); + +#else +#define CPPDECL extern +#endif + +CPPDECL X509_STORE *us_get_default_ca_store(); + diff --git a/src/bun.js/bindings/NodeTLS.cpp b/src/bun.js/bindings/NodeTLS.cpp index 4c4102180f..36ef165e22 100644 --- a/src/bun.js/bindings/NodeTLS.cpp +++ b/src/bun.js/bindings/NodeTLS.cpp @@ -6,22 +6,23 @@ #include "libusockets.h" #include "ZigGlobalObject.h" +#include "ErrorCode.h" +#include "openssl/base.h" +#include "openssl/bio.h" +#include "../../packages/bun-usockets/src/crypto/root_certs_header.h" namespace Bun { using namespace JSC; -BUN_DECLARE_HOST_FUNCTION(Bun__canonicalizeIP); - -JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) +JSC_DEFINE_HOST_FUNCTION(getBundledRootCertificates, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { VM& vm = globalObject->vm(); - auto* obj = constructEmptyObject(globalObject); struct us_cert_string_t* out; auto size = us_raw_root_certs(&out); if (size < 0) { - return jsUndefined(); + return JSValue::encode(jsUndefined()); } auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); for (auto i = 0; i < size; i++) { @@ -29,12 +30,46 @@ JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) auto str = WTF::String::fromUTF8(std::span { raw.str, raw.len }); rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); } - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "rootCertificates"_s)), JSC::objectConstructorFreeze(globalObject, rootCertificates), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "canonicalizeIP"_s)), JSC::JSFunction::create(vm, globalObject, 1, "canonicalizeIP"_s, Bun__canonicalizeIP, ImplementationVisibility::Public, NoIntrinsic), 0); - return obj; + return JSValue::encode(JSC::objectConstructorFreeze(globalObject, rootCertificates)); +} + +JSC_DEFINE_HOST_FUNCTION(getExtraCACertificates, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + VM& vm = globalObject->vm(); + + STACK_OF(X509)* root_extra_cert_instances = us_get_root_extra_cert_instances(); + + auto size = sk_X509_num(root_extra_cert_instances); + if (size < 0) size = 0; // root_extra_cert_instances is nullptr + + auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); + for (auto i = 0; i < size; i++) { + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) { + throwOutOfMemoryError(globalObject, scope); + return {}; + } + + if (PEM_write_bio_X509(bio, sk_X509_value(root_extra_cert_instances, i)) != 1) { + BIO_free(bio); + return throwError(globalObject, scope, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "X509 to PEM conversion"_str); + } + + char* bioData = nullptr; + long bioLen = BIO_get_mem_data(bio, &bioData); + if (bioLen <= 0 || !bioData) { + BIO_free(bio); + return throwError(globalObject, scope, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "Reading PEM data"_str); + } + + auto str = WTF::String::fromUTF8(std::span { bioData, static_cast(bioLen) }); + rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); + BIO_free(bio); + } + + return JSValue::encode(JSC::objectConstructorFreeze(globalObject, rootCertificates)); } } // namespace Bun diff --git a/src/bun.js/bindings/NodeTLS.h b/src/bun.js/bindings/NodeTLS.h index 2650f0cfaf..296c28c76a 100644 --- a/src/bun.js/bindings/NodeTLS.h +++ b/src/bun.js/bindings/NodeTLS.h @@ -3,6 +3,8 @@ namespace Bun { -JSC::JSValue createNodeTLSBinding(Zig::GlobalObject*); +BUN_DECLARE_HOST_FUNCTION(Bun__canonicalizeIP); +JSC_DECLARE_HOST_FUNCTION(getBundledRootCertificates); +JSC_DECLARE_HOST_FUNCTION(getExtraCACertificates); } diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index fa784a3ae9..d922de05b7 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -12,7 +12,7 @@ const fmtBinding = $bindgenFn("fmt.bind.ts", "fmtString"); export const highlightJavaScript = (code: string) => fmtBinding(code, "highlight-javascript"); export const escapePowershell = (code: string) => fmtBinding(code, "escape-powershell"); -export const TLSBinding = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); +export const canonicalizeIP = $newCppFunction("NodeTLS.cpp", "Bun__canonicalizeIP", 1); export const SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); diff --git a/src/js/node/tls.ts b/src/js/node/tls.ts index 946e4e2b7e..06f7a81e44 100644 --- a/src/js/node/tls.ts +++ b/src/js/node/tls.ts @@ -5,10 +5,13 @@ const { Duplex } = require("node:stream"); const addServerName = $newZigFunction("socket.zig", "jsAddServerName", 3); const { throwNotImplemented } = require("internal/shared"); const { throwOnInvalidTLSArray, DEFAULT_CIPHERS, validateCiphers } = require("internal/tls"); +const { validateString } = require("internal/validators"); const { Server: NetServer, Socket: NetSocket } = net; -const { rootCertificates, canonicalizeIP } = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); +const getBundledRootCertificates = $newCppFunction("NodeTLS.cpp", "getBundledRootCertificates", 1); +const getExtraCACertificates = $newCppFunction("NodeTLS.cpp", "getExtraCACertificates", 1); +const canonicalizeIP = $newCppFunction("NodeTLS.cpp", "Bun__canonicalizeIP", 1); const SymbolReplace = Symbol.replace; const RegExpPrototypeSymbolReplace = RegExp.prototype[SymbolReplace]; @@ -31,6 +34,9 @@ const ArrayPrototypeForEach = Array.prototype.forEach; const ArrayPrototypePush = Array.prototype.push; const ArrayPrototypeSome = Array.prototype.some; const ArrayPrototypeReduce = Array.prototype.reduce; + +const ObjectFreeze = Object.freeze; + function parseCertString() { // Removed since JAN 2022 Node v18.0.0+ https://github.com/nodejs/node/pull/41479 throwNotImplemented("Not implemented"); @@ -724,6 +730,59 @@ function convertALPNProtocols(protocols, out) { } } +let bundledRootCertificates: string[] | undefined; +function cacheBundledRootCertificates(): string[] { + bundledRootCertificates ||= getBundledRootCertificates() as string[]; + return bundledRootCertificates; +} +let defaultCACertificates: string[] | undefined; +function cacheDefaultCACertificates() { + if (defaultCACertificates) return defaultCACertificates; + defaultCACertificates = []; + + const bundled = cacheBundledRootCertificates(); + for (let i = 0; i < bundled.length; ++i) { + ArrayPrototypePush.$call(defaultCACertificates, bundled[i]); + } + + if (process.env.NODE_EXTRA_CA_CERTS) { + const extra = cacheExtraCACertificates(); + for (let i = 0; i < extra.length; ++i) { + ArrayPrototypePush.$call(defaultCACertificates, extra[i]); + } + } + + ObjectFreeze(defaultCACertificates); + return defaultCACertificates; +} + +function cacheSystemCACertificates(): string[] { + throw new Error("getCACertificates('system') is not yet implemented in Bun"); +} + +let extraCACertificates: string[] | undefined; +function cacheExtraCACertificates(): string[] { + extraCACertificates ||= getExtraCACertificates() as string[]; + return extraCACertificates; +} + +function getCACertificates(type = "default") { + validateString(type, "type"); + + switch (type) { + case "default": + return cacheDefaultCACertificates(); + case "bundled": + return cacheBundledRootCertificates(); + case "system": + return cacheSystemCACertificates(); + case "extra": + return cacheExtraCACertificates(); + default: + throw $ERR_INVALID_ARG_VALUE("type", type); + } +} + export default { CLIENT_RENEG_LIMIT, CLIENT_RENEG_WINDOW, @@ -742,6 +801,7 @@ export default { TLSSocket, checkServerIdentity, get rootCertificates() { - return rootCertificates; + return cacheBundledRootCertificates(); }, + getCACertificates, } as any as typeof import("node:tls"); diff --git a/test/js/node/test/common/tls.js b/test/js/node/test/common/tls.js index d212fbe076..bbc37df19c 100644 --- a/test/js/node/test/common/tls.js +++ b/test/js/node/test/common/tls.js @@ -3,6 +3,7 @@ 'use strict'; const crypto = require('crypto'); const net = require('net'); +const assert = require('assert'); exports.ccs = Buffer.from('140303000101', 'hex'); @@ -173,4 +174,16 @@ function P_hash(algo, secret, seed, size) { return result; } +exports.assertIsCAArray = function assertIsCAArray(certs) { + assert(Array.isArray(certs)); + assert(certs.length > 0); + + // The certificates looks PEM-encoded. + for (const cert of certs) { + const trimmed = cert.trim(); + assert.match(trimmed, /^-----BEGIN CERTIFICATE-----/); + assert.match(trimmed, /-----END CERTIFICATE-----$/); + } +}; + exports.TestTLSSocket = TestTLSSocket; diff --git a/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js b/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js new file mode 100644 index 0000000000..ee12992604 --- /dev/null +++ b/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js @@ -0,0 +1,17 @@ +'use strict'; + +const tls = require('tls'); +const assert = require('assert'); + +const defaultSet = new Set(tls.getCACertificates('default')); +const extraSet = new Set(tls.getCACertificates('extra')); +console.log(defaultSet.size, 'default certificates'); +console.log(extraSet.size, 'extra certificates') + +// Parent process is supposed to call this with +// NODE_EXTRA_CA_CERTS set to test/fixtures/keys/ca1-cert.pem. +assert.strictEqual(extraSet.size, 1); + +// Check that default set is a super set of extra set. +assert.deepStrictEqual(defaultSet.intersection(extraSet), + extraSet); diff --git a/test/js/node/test/fixtures/tls-get-ca-certificates.js b/test/js/node/test/fixtures/tls-get-ca-certificates.js new file mode 100644 index 0000000000..e21f06b81b --- /dev/null +++ b/test/js/node/test/fixtures/tls-get-ca-certificates.js @@ -0,0 +1,11 @@ +'use strict'; +// This fixture just writes tls.getCACertificates() outputs to process.env.CA_OUT +const tls = require('tls'); +const fs = require('fs'); +const assert = require('assert'); +assert(process.env.CA_TYPE); +assert(process.env.CA_OUT); + +const certs = tls.getCACertificates(process.env.CA_TYPE); + +fs.writeFileSync(process.env.CA_OUT, JSON.stringify(certs), 'utf8'); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js new file mode 100644 index 0000000000..b9fe43b965 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js @@ -0,0 +1,17 @@ +'use strict'; +// Flags: --no-use-openssl-ca +// This tests that tls.getCACertificates() returns the bundled +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +const defaultSet = new Set(tls.getCACertificates('default')); +const bundledSet = new Set(tls.getCACertificates('bundled')); + +// When --use-openssl-ca is false (i.e. bundled CA is sued), +// default is a superset of bundled certificates. +assert.deepStrictEqual(defaultSet.intersection(bundledSet), bundledSet); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js new file mode 100644 index 0000000000..5dbe254bca --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js @@ -0,0 +1,20 @@ +'use strict'; +// This tests that tls.getCACertificates() returns the bundled +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const { assertIsCAArray } = require('../common/tls'); + +const certs = tls.getCACertificates('bundled'); +assertIsCAArray(certs); + +// It's the same as tls.rootCertificates - both are +// Mozilla CA stores across platform. +assert.strictEqual(certs, tls.rootCertificates); + +// It's cached on subsequent accesses. +assert.strictEqual(certs, tls.getCACertificates('bundled')); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js new file mode 100644 index 0000000000..29fb2a29a8 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js @@ -0,0 +1,20 @@ +'use strict'; + +// This tests that tls.getCACertificates() returns the default +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const { assertIsCAArray } = require('../common/tls'); + +const certs = tls.getCACertificates(); +assertIsCAArray(certs); + +const certs2 = tls.getCACertificates('default'); +assert.strictEqual(certs, certs2); + +// It's cached on subsequent accesses. +assert.strictEqual(certs, tls.getCACertificates('default')); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js new file mode 100644 index 0000000000..da40b8fdf4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js @@ -0,0 +1,20 @@ +'use strict'; + +// This tests that tls.getCACertificates() throws error when being +// passed an invalid argument. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +for (const invalid of [1, null, () => {}, true]) { + assert.throws(() => tls.getCACertificates(invalid), { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +assert.throws(() => tls.getCACertificates('test'), { + code: 'ERR_INVALID_ARG_VALUE' +}); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js new file mode 100644 index 0000000000..f099a03962 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js @@ -0,0 +1,29 @@ +'use strict'; +// This tests that tls.getCACertificates('extra') returns an empty +// array if NODE_EXTRA_CA_CERTS is empty. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +const assert = require('assert'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const certsJSON = tmpdir.resolve('certs.json'); + +// If NODE_EXTRA_CA_CERTS is not set, it should be an empty array. +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-get-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: undefined, + CA_TYPE: 'extra', + CA_OUT: certsJSON, + } +}); + +const parsed = JSON.parse(fs.readFileSync(certsJSON, 'utf-8')); +assert.deepStrictEqual(parsed, []); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js new file mode 100644 index 0000000000..5fd3da47bd --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js @@ -0,0 +1,16 @@ +'use strict'; +// This tests that tls.getCACertificates('defulat') returns a superset +// of tls.getCACertificates('extra') when NODE_EXTRA_CA_CERTS is used. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-check-extra-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem'), + } +}); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js new file mode 100644 index 0000000000..1ff6a51079 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js @@ -0,0 +1,29 @@ +'use strict'; +// This tests that tls.getCACertificates('extra') returns the extra +// certificates from NODE_EXTRA_CA_CERTS correctly. + +const common = require('../common'); + +if (!common.hasCrypto) common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); +const assert = require('assert'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const certsJSON = tmpdir.resolve('certs.json'); + +// If NODE_EXTRA_CA_CERTS is set, it should contain a list of certificates. +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-get-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem'), + CA_TYPE: 'extra', + CA_OUT: certsJSON, + } +}); + +const parsed = JSON.parse(fs.readFileSync(certsJSON, 'utf-8')); +assert.deepStrictEqual(parsed, [fixtures.readKey('ca1-cert.pem', 'utf8')]); diff --git a/test/js/node/tls/node-tls-internals.test.ts b/test/js/node/tls/node-tls-internals.test.ts index 9b342cd22b..8a7e021504 100644 --- a/test/js/node/tls/node-tls-internals.test.ts +++ b/test/js/node/tls/node-tls-internals.test.ts @@ -1,9 +1,8 @@ -import { TLSBinding } from "bun:internal-for-testing"; +import { canonicalizeIP } from "bun:internal-for-testing"; import { createTest } from "node-harness"; +import { rootCertificates } from "tls"; const { describe, expect } = createTest(import.meta.path); -const { canonicalizeIP, rootCertificates } = TLSBinding; - describe("NodeTLS.cpp", () => { test("canonicalizeIP", () => { expect(canonicalizeIP("127.0.0.1")).toBe("127.0.0.1"); From c44515eaaf0d9bd8c0de7096c75184f467fe253f Mon Sep 17 00:00:00 2001 From: pfg Date: Fri, 13 Jun 2025 19:05:05 -0700 Subject: [PATCH 016/133] Support `--unhandled-rejections` flag and `rejectionHandled` event (#19874) Co-authored-by: pfgithub <6010774+pfgithub@users.noreply.github.com> --- src/api/schema.zig | 20 + src/bun.js/VirtualMachine.zig | 89 ++- src/bun.js/bindings/BunProcess.cpp | 125 +++- src/bun.js/bindings/ErrorCode.ts | 1 + src/bun.js/bindings/ZigGlobalObject.cpp | 7 +- src/bun.js/virtual_machine_exports.zig | 18 + src/bun.zig | 2 +- src/cli.zig | 9 + test/internal/ban-words.test.ts | 2 +- test/js/node/promise/reject-tostring.js | 18 + test/js/node/promise/reject-tostring.test.ts | 17 + .../test-promise-unhandled-default.js | 58 ++ .../parallel/test-promise-unhandled-error.js | 53 ++ .../parallel/test-promise-unhandled-flag.js | 18 + .../test-promise-unhandled-silent-no-hook.js | 20 + .../parallel/test-promise-unhandled-throw.js | 54 ++ .../test-promise-unhandled-warn-no-hook.js | 20 + ...est-promises-unhandled-proxy-rejections.js | 28 + .../test-promises-unhandled-rejections.js | 705 ++++++++++++++++++ ...st-promises-unhandled-symbol-rejections.js | 29 + ...promises-warning-on-unhandled-rejection.js | 50 ++ 21 files changed, 1329 insertions(+), 14 deletions(-) create mode 100644 test/js/node/promise/reject-tostring.js create mode 100644 test/js/node/promise/reject-tostring.test.ts create mode 100644 test/js/node/test/parallel/test-promise-unhandled-default.js create mode 100644 test/js/node/test/parallel/test-promise-unhandled-error.js create mode 100644 test/js/node/test/parallel/test-promise-unhandled-flag.js create mode 100644 test/js/node/test/parallel/test-promise-unhandled-silent-no-hook.js create mode 100644 test/js/node/test/parallel/test-promise-unhandled-throw.js create mode 100644 test/js/node/test/parallel/test-promise-unhandled-warn-no-hook.js create mode 100644 test/js/node/test/parallel/test-promises-unhandled-proxy-rejections.js create mode 100644 test/js/node/test/parallel/test-promises-unhandled-rejections.js create mode 100644 test/js/node/test/parallel/test-promises-unhandled-symbol-rejections.js create mode 100644 test/js/node/test/parallel/test-promises-warning-on-unhandled-rejection.js diff --git a/src/api/schema.zig b/src/api/schema.zig index fca846537f..78949927ce 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1612,6 +1612,24 @@ pub const Api = struct { } }; + pub const UnhandledRejections = enum(u8) { + strict = 0, + throw = 1, + warn = 2, + none = 3, + warn_with_error_code = 4, + bun = 5, + + pub const map = bun.ComptimeStringMap(UnhandledRejections, .{ + .{ "strict", .strict }, + .{ "throw", .throw }, + .{ "warn", .warn }, + .{ "none", .none }, + .{ "warn-with-error-code", .warn_with_error_code }, + .{ "bun", .bun }, + }); + }; + pub const TransformOptions = struct { /// jsx jsx: ?Jsx = null, @@ -1709,6 +1727,8 @@ pub const Api = struct { // from --no-addons. null == true allow_addons: ?bool = null, + /// from --unhandled-rejections, default is 'bun' + unhandled_rejections: ?UnhandledRejections = null, bunfig_path: []const u8, diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 17988dc036..6de742e3bc 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -18,6 +18,7 @@ comptime { @export(&specifierIsEvalEntryPoint, .{ .name = "Bun__VM__specifierIsEvalEntryPoint" }); @export(&string_allocation_limit, .{ .name = "Bun__stringSyntheticAllocationLimit" }); @export(&allowAddons, .{ .name = "Bun__VM__allowAddons" }); + @export(&allowRejectionHandledWarning, .{ .name = "Bun__VM__allowRejectionHandledWarning" }); } global: *JSGlobalObject, @@ -196,6 +197,12 @@ pub const OnException = fn (*ZigException) void; pub fn allowAddons(this: *VirtualMachine) callconv(.c) bool { return if (this.transpiler.options.transform_options.allow_addons) |allow_addons| allow_addons else true; } +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; +} pub fn initRequestBodyValue(this: *VirtualMachine, body: JSC.WebCore.Body.Value) !*Body.Value.HiveRef { return .init(body, &this.body_value_hive_allocator); @@ -496,6 +503,34 @@ pub fn loadExtraEnvAndSourceCodePrinter(this: *VirtualMachine) void { extern fn Bun__handleUncaughtException(*JSGlobalObject, err: JSValue, is_rejection: c_int) c_int; extern fn Bun__handleUnhandledRejection(*JSGlobalObject, reason: JSValue, promise: JSValue) c_int; +extern fn Bun__wrapUnhandledRejectionErrorForUncaughtException(*JSGlobalObject, reason: JSValue) JSValue; +extern fn Bun__emitHandledPromiseEvent(*JSGlobalObject, promise: JSValue) bool; +extern fn Bun__promises__isErrorLike(*JSGlobalObject, reason: JSValue) bool; +extern fn Bun__promises__emitUnhandledRejectionWarning(*JSGlobalObject, reason: JSValue, promise: JSValue) void; +extern fn Bun__noSideEffectsToString(vm: *JSC.VM, globalObject: *JSGlobalObject, reason: JSValue) JSValue; + +fn isErrorLike(globalObject: *JSGlobalObject, reason: JSValue) bun.JSError!bool { + const result = Bun__promises__isErrorLike(globalObject, reason); + if (globalObject.hasException()) return error.JSError; + return result; +} + +fn wrapUnhandledRejectionErrorForUncaughtException(globalObject: *JSGlobalObject, reason: JSValue) JSValue { + if (isErrorLike(globalObject, reason) catch blk: { + if (globalObject.hasException()) globalObject.clearException(); + break :blk false; + }) return reason; + const reasonStr = Bun__noSideEffectsToString(globalObject.vm(), globalObject, reason); + if (globalObject.hasException()) globalObject.clearException(); + const msg = "This error originated either by throwing inside of an async function without a catch block, " ++ + "or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason \"" ++ + "{s}" ++ + "\"."; + if (reasonStr.isString()) { + return globalObject.ERR(.UNHANDLED_REJECTION, msg, .{reasonStr.asString().view(globalObject)}).toJS(); + } + return globalObject.ERR(.UNHANDLED_REJECTION, msg, .{"undefined"}).toJS(); +} pub fn unhandledRejection(this: *JSC.VirtualMachine, globalObject: *JSGlobalObject, reason: JSValue, promise: JSValue) bool { if (this.isShuttingDown()) { @@ -509,12 +544,56 @@ pub fn unhandledRejection(this: *JSC.VirtualMachine, globalObject: *JSGlobalObje return true; } - const handled = Bun__handleUnhandledRejection(globalObject, reason, promise) > 0; - if (!handled) { - this.unhandled_error_counter += 1; - this.onUnhandledRejection(this, globalObject, reason); + switch (this.unhandledRejectionsMode()) { + .bun => { + if (Bun__handleUnhandledRejection(globalObject, reason, promise) > 0) return true; + // continue to default handler + }, + .none => { + defer this.eventLoop().drainMicrotasks(); + if (Bun__handleUnhandledRejection(globalObject, reason, promise) > 0) return true; + return true; // ignore the unhandled rejection + }, + .warn => { + defer this.eventLoop().drainMicrotasks(); + _ = Bun__handleUnhandledRejection(globalObject, reason, promise); + Bun__promises__emitUnhandledRejectionWarning(globalObject, reason, promise); + return true; + }, + .warn_with_error_code => { + defer this.eventLoop().drainMicrotasks(); + if (Bun__handleUnhandledRejection(globalObject, reason, promise) > 0) return true; + Bun__promises__emitUnhandledRejectionWarning(globalObject, reason, promise); + this.exit_handler.exit_code = 1; + return true; + }, + .strict => { + defer this.eventLoop().drainMicrotasks(); + const wrapped_reason = wrapUnhandledRejectionErrorForUncaughtException(globalObject, reason); + _ = this.uncaughtException(globalObject, wrapped_reason, true); + if (Bun__handleUnhandledRejection(globalObject, reason, promise) > 0) return true; + Bun__promises__emitUnhandledRejectionWarning(globalObject, reason, promise); + return true; + }, + .throw => { + defer this.eventLoop().drainMicrotasks(); + if (Bun__handleUnhandledRejection(globalObject, reason, promise) > 0) return true; + const wrapped_reason = wrapUnhandledRejectionErrorForUncaughtException(globalObject, reason); + if (this.uncaughtException(globalObject, wrapped_reason, true)) return true; + // continue to default handler + }, } - return handled; + this.unhandled_error_counter += 1; + this.onUnhandledRejection(this, globalObject, reason); + return false; +} + +pub fn handledPromise(this: *JSC.VirtualMachine, globalObject: *JSGlobalObject, promise: JSValue) bool { + if (this.isShuttingDown()) { + return true; + } + + return Bun__emitHandledPromiseEvent(globalObject, promise); } pub fn uncaughtException(this: *JSC.VirtualMachine, globalObject: *JSGlobalObject, err: JSValue, is_rejection: bool) bool { diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 0af9842bcd..9698e6ffb4 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -1112,6 +1112,75 @@ extern "C" int Bun__handleUncaughtException(JSC::JSGlobalObject* lexicalGlobalOb return true; } +extern "C" bool Bun__promises__isErrorLike(JSC::JSGlobalObject* globalObject, JSC::JSValue obj) +{ + // return typeof obj === 'object' && + // obj !== null && + // ObjectPrototypeHasOwnProperty(obj, 'stack'); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (!obj.isObject()) return false; + + auto* object = JSC::jsCast(obj); + const bool result = JSC::objectPrototypeHasOwnProperty(globalObject, object, vm.propertyNames->stack); + RELEASE_AND_RETURN(scope, result); +} + +extern "C" JSC::EncodedJSValue Bun__noSideEffectsToString(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue reason) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + auto decodedReason = JSValue::decode(reason); + if (decodedReason.isSymbol()) { + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsNontrivialString(globalObject->vm(), asSymbol(decodedReason)->descriptiveString()))); + } + + if (decodedReason.isInt32()) + return JSC::JSValue::encode(jsString(vm, decodedReason.toWTFString(globalObject))); + if (decodedReason.isDouble()) + return JSC::JSValue::encode(jsString(vm, decodedReason.toWTFString(globalObject))); + if (decodedReason.isTrue()) + return JSC::JSValue::encode(vm.smallStrings.trueString()); + if (decodedReason.isFalse()) + return JSC::JSValue::encode(vm.smallStrings.falseString()); + if (decodedReason.isNull()) + return JSC::JSValue::encode(vm.smallStrings.nullString()); + if (decodedReason.isUndefined()) + return JSC::JSValue::encode(vm.smallStrings.undefinedString()); + if (decodedReason.isString()) + return JSC::JSValue::encode(decodedReason); + if (decodedReason.isBigInt()) + return JSC::JSValue::encode(jsString(vm, decodedReason.toWTFString(globalObject))); + return JSC::JSValue::encode(vm.smallStrings.objectObjectString()); +} + +extern "C" void Bun__promises__emitUnhandledRejectionWarning(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue reason, JSC::EncodedJSValue promise) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto warning = JSC::createError(globalObject, "Unhandled promise rejection. This error originated either by " + "throwing inside of an async function without a catch block, " + "or by rejecting a promise which was not handled with .catch(). " + "To terminate the bun process on unhandled promise " + "rejection, use the CLI flag `--unhandled-rejections=strict`."_s); + warning->putDirect(vm, Identifier::fromString(vm, "name"_s), jsString(vm, "UnhandledPromiseRejectionWarning"_str), JSC::PropertyAttribute::DontEnum | 0); + + JSValue reasonStack {}; + if (Bun__promises__isErrorLike(globalObject, JSValue::decode(reason))) { + reasonStack = JSValue::decode(reason).get(globalObject, vm.propertyNames->stack); + if (scope.exception()) scope.clearException(); + warning->putDirect(vm, vm.propertyNames->stack, reasonStack); + } + if (!reasonStack) { + reasonStack = JSValue::decode(Bun__noSideEffectsToString(vm, globalObject, reason)); + if (scope.exception()) scope.clearException(); + } + if (!reasonStack) reasonStack = jsUndefined(); + + Process::emitWarning(globalObject, reasonStack, jsString(globalObject->vm(), "UnhandledPromiseRejectionWarning"_str), jsUndefined(), jsUndefined()); + if (scope.exception()) scope.clearException(); + Process::emitWarningErrorInstance(globalObject, warning); + if (scope.exception()) scope.clearException(); +} extern "C" int Bun__handleUnhandledRejection(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue reason, JSC::JSValue promise) { @@ -1119,17 +1188,44 @@ extern "C" int Bun__handleUnhandledRejection(JSC::JSGlobalObject* lexicalGlobalO return false; auto* globalObject = jsCast(lexicalGlobalObject); auto* process = globalObject->processObject(); - MarkedArgumentBuffer args; - args.append(reason); - args.append(promise); + auto eventType = Identifier::fromString(JSC::getVM(globalObject), "unhandledRejection"_s); auto& wrapped = process->wrapped(); if (wrapped.listenerCount(eventType) > 0) { + MarkedArgumentBuffer args; + args.append(reason); + args.append(promise); wrapped.emit(eventType, args); return true; - } else { - return false; } + + return false; +} + +extern "C" bool Bun__VM__allowRejectionHandledWarning(void* vm); + +extern "C" bool Bun__emitHandledPromiseEvent(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue promise) +{ + auto scope = DECLARE_CATCH_SCOPE(JSC::getVM(lexicalGlobalObject)); + if (!lexicalGlobalObject->inherits(Zig::GlobalObject::info())) + return false; + auto* globalObject = jsCast(lexicalGlobalObject); + auto* process = jsCast(globalObject->processObject()); + + auto eventType = Identifier::fromString(JSC::getVM(globalObject), "rejectionHandled"_s); + + if (Bun__VM__allowRejectionHandledWarning(globalObject->bunVM())) { + Process::emitWarning(globalObject, jsString(globalObject->vm(), String("Promise rejection was handled asynchronously"_s)), jsString(globalObject->vm(), String("PromiseRejectionHandledWarning"_s)), jsUndefined(), jsUndefined()); + } + auto& wrapped = process->wrapped(); + if (wrapped.listenerCount(eventType) > 0) { + MarkedArgumentBuffer args; + args.append(promise); + wrapped.emit(eventType, args); + return true; + } + + return false; } extern "C" void Bun__refChannelUnlessOverridden(JSC::JSGlobalObject* globalObject); @@ -1488,7 +1584,24 @@ JSValue Process::emitWarning(JSC::JSGlobalObject* lexicalGlobalObject, JSValue w if (!code.isUndefined()) errorInstance->putDirect(vm, builtinNames(vm).codePublicName(), code, JSC::PropertyAttribute::DontEnum | 0); if (!detail.isUndefined()) errorInstance->putDirect(vm, vm.propertyNames->detail, detail, JSC::PropertyAttribute::DontEnum | 0); - // ErrorCaptureStackTrace(warning, ctor || process.emitWarning); + + /* + // TODO: ErrorCaptureStackTrace(warning, ctor || process.emitWarning); + // This doesn't work, getStackTrace does not get any stack frames. + Vector stackTrace; + const size_t framesToSkip = 1; + JSValue caller; + if (ctor.toBoolean(globalObject)) { + caller = ctor; + } else { + auto* globalObject = jsCast(lexicalGlobalObject); + auto* process = globalObject->processObject(); + caller = process->get(globalObject, Identifier::fromString(vm, String("emitWarning"_s))); + RETURN_IF_EXCEPTION(scope, {}); + } + vm.interpreter.getStackTrace(errorInstance, stackTrace, framesToSkip, globalObject->stackTraceLimit().value_or(0), caller.isCallable() ? caller.asCell() : nullptr); + errorInstance->putDirect(vm, vm.propertyNames->stack, jsString(vm, Interpreter::stackTraceAsString(vm, stackTrace)), static_cast(PropertyAttribute::DontEnum)); + */ RELEASE_AND_RETURN(scope, emitWarningErrorInstance(lexicalGlobalObject, errorInstance)); } diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts index 4b98659f03..ecd518b692 100644 --- a/src/bun.js/bindings/ErrorCode.ts +++ b/src/bun.js/bindings/ErrorCode.ts @@ -204,6 +204,7 @@ const errors: ErrorCodeMapping = [ ["ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT", TypeError, "PostgresError"], ["ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE", TypeError, "PostgresError"], ["ERR_POSTGRES_UNSUPPORTED_NUMERIC_FORMAT", TypeError, "PostgresError"], + ["ERR_UNHANDLED_REJECTION", Error, "UnhandledPromiseRejection"], ["ERR_REQUIRE_ASYNC_MODULE", Error], ["ERR_S3_INVALID_ENDPOINT", Error], ["ERR_S3_INVALID_METHOD", Error], diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 6f574d440e..6b3615dc3c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1315,6 +1315,8 @@ void GlobalObject::reportUncaughtExceptionAtEventLoop(JSGlobalObject* globalObje Bun__reportUnhandledError(globalObject, JSValue::encode(JSValue(exception))); } +extern "C" void Bun__handleHandledPromise(Zig::GlobalObject* JSGlobalObject, JSC::JSPromise* promise); + void GlobalObject::promiseRejectionTracker(JSGlobalObject* obj, JSC::JSPromise* promise, JSC::JSPromiseRejectionOperation operation) { @@ -1328,9 +1330,12 @@ void GlobalObject::promiseRejectionTracker(JSGlobalObject* obj, JSC::JSPromise* globalObj->m_aboutToBeNotifiedRejectedPromises.append(JSC::Strong(obj->vm(), promise)); break; case JSPromiseRejectionOperation::Handle: - globalObj->m_aboutToBeNotifiedRejectedPromises.removeFirstMatching([&](Strong& unhandledPromise) { + bool removed = globalObj->m_aboutToBeNotifiedRejectedPromises.removeFirstMatching([&](Strong& unhandledPromise) { return unhandledPromise.get() == promise; }); + if (removed) break; + // The promise rejection has already been notified, now we need to queue it for the rejectionHandled event + Bun__handleHandledPromise(globalObj, promise); break; } } diff --git a/src/bun.js/virtual_machine_exports.zig b/src/bun.js/virtual_machine_exports.zig index c061cc4371..6122e926cc 100644 --- a/src/bun.js/virtual_machine_exports.zig +++ b/src/bun.js/virtual_machine_exports.zig @@ -119,6 +119,24 @@ pub export fn Bun__handleRejectedPromise(global: *JSGlobalObject, promise: *JSC. jsc_vm.autoGarbageCollect(); } +pub export fn Bun__handleHandledPromise(global: *JSGlobalObject, promise: *JSC.JSPromise) void { + const Context = struct { + globalThis: *JSC.JSGlobalObject, + promise: JSC.JSValue, + pub fn callback(context: *@This()) void { + _ = context.globalThis.bunVM().handledPromise(context.globalThis, context.promise); + context.promise.unprotect(); + bun.default_allocator.destroy(context); + } + }; + JSC.markBinding(@src()); + const promise_js = promise.toJS(); + promise_js.protect(); + const context = bun.default_allocator.create(Context) catch bun.outOfMemory(); + context.* = .{ .globalThis = global, .promise = promise_js }; + global.bunVM().eventLoop().enqueueTask(JSC.ManagedTask.New(Context, Context.callback).init(context)); +} + pub export fn Bun__onDidAppendPlugin(jsc_vm: *VirtualMachine, globalObject: *JSGlobalObject) void { if (jsc_vm.plugin_runner != null) { return; diff --git a/src/bun.zig b/src/bun.zig index 0fb24bf626..5715dd301a 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -26,7 +26,7 @@ pub const callmod_inline: std.builtin.CallModifier = if (builtin.mode == .Debug) pub const callconv_inline: std.builtin.CallingConvention = if (builtin.mode == .Debug) .Unspecified else .Inline; /// In debug builds, this will catch memory leaks. In release builds, it is mimalloc. -pub const debug_allocator: std.mem.Allocator = if (Environment.isDebug) +pub const debug_allocator: std.mem.Allocator = if (Environment.isDebug or Environment.enable_asan) debug_allocator_data.allocator else default_allocator; diff --git a/src/cli.zig b/src/cli.zig index 562a34fac2..cc474e186e 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -237,6 +237,7 @@ pub const Arguments = struct { clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable, clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable, clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable, + clap.parseParam("--unhandled-rejections One of \"strict\", \"throw\", \"warn\", \"none\", \"warn-with-error-code\", or \"bun\" (default)") catch unreachable, }; const auto_or_run_params = [_]ParamType{ @@ -712,6 +713,14 @@ pub const Arguments = struct { opts.allow_addons = false; } + if (args.option("--unhandled-rejections")) |unhandled_rejections| { + const resolved = Api.UnhandledRejections.map.get(unhandled_rejections) orelse { + Output.errGeneric("Invalid value for --unhandled-rejections: \"{s}\". Must be one of \"strict\", \"throw\", \"warn\", \"none\", \"warn-with-error-code\", or \"bun\"\n", .{unhandled_rejections}); + Global.exit(1); + }; + opts.unhandled_rejections = resolved; + } + if (args.option("--port")) |port_str| { if (comptime cmd == .RunAsNodeCommand) { // TODO: prevent `node --port + + + `, + "/styles.css": /* css */ ` + body { + background: blue; + } + `, + "/app.js": /* js */ ` + console.log("Client app loaded"); + `, + }, + run: { + stdout: "Status: 200\nContent-Type: text/html;charset=utf-8\nHas HTML tag: true\nHas h1: true", + }, + }); + + itBundled("compile/HTMLServerMultipleRoutes", { + compile: true, + files: { + "/entry.ts": /* js */ ` + import home from "./home.html"; + import about from "./about.html"; + + using server = Bun.serve({ + port: 0, + routes: { + "/": home, + "/about": about, + }, + }); + + // Test home route + const homeRes = await fetch(server.url); + console.log("Home status:", homeRes.status); + const homeHtml = await homeRes.text(); + console.log("Home has content:", homeHtml.includes("Home Page")); + + // Test about route + const aboutRes = await fetch(server.url + "about"); + console.log("About status:", aboutRes.status); + const aboutHtml = await aboutRes.text(); + console.log("About has content:", aboutHtml.includes("About Page")); + `, + "/home.html": /* html */ ` + + + + Home + + + +

Home Page

+ + + + `, + "/about.html": /* html */ ` + + + + About + + + +

About Page

+ + + + `, + "/styles.css": /* css */ ` + body { + margin: 0; + font-family: sans-serif; + } + `, + "/app.js": /* js */ ` + console.log("App loaded"); + `, + }, + run: { + stdout: "Home status: 200\nHome has content: true\nAbout status: 200\nAbout has content: true", + }, + }); +}); diff --git a/test/internal/ban-words.test.ts b/test/internal/ban-words.test.ts index f840f0987b..ac05275e59 100644 --- a/test/internal/ban-words.test.ts +++ b/test/internal/ban-words.test.ts @@ -44,7 +44,7 @@ const words: Record ".arguments_old(": { reason: "Please migrate to .argumentsAsArray() or another argument API", limit: 284 }, "// autofix": { reason: "Evaluate if this variable should be deleted entirely or explicitly discarded.", limit: 175 }, - "global.hasException": { reason: "Incompatible with strict exception checks. Use a CatchScope instead.", limit: 28 }, + "global.hasException": { reason: "Incompatible with strict exception checks. Use a CatchScope instead.", limit: 29 }, "globalObject.hasException": { reason: "Incompatible with strict exception checks. Use a CatchScope instead.", limit: 49 }, "globalThis.hasException": { reason: "Incompatible with strict exception checks. Use a CatchScope instead.", limit: 139 }, }; diff --git a/test/js/bun/http/bun-serve-html-manifest.test.ts b/test/js/bun/http/bun-serve-html-manifest.test.ts new file mode 100644 index 0000000000..939489eefe --- /dev/null +++ b/test/js/bun/http/bun-serve-html-manifest.test.ts @@ -0,0 +1,372 @@ +import { describe, expect, it } from "bun:test"; +import { bunEnv, bunExe, rmScope, tempDirWithFiles } from "harness"; +import { join } from "node:path"; +import { StringDecoder } from "node:string_decoder"; + +describe("Bun.serve HTML manifest", () => { + it("serves HTML import with manifest", async () => { + const dir = tempDirWithFiles("serve-html", { + "server.ts": ` + import index from "./index.html"; + + const server = Bun.serve({ + port: 0, + routes: { + "/": index, + }, + }); + + console.log("PORT=" + server.port); + + // Test the manifest structure + console.log("Manifest type:", typeof index); + console.log("Has index:", "index" in index); + console.log("Has files:", "files" in index); + if (index.files) { + console.log("File count:", index.files.length); + } + `, + "index.html": ` + + + Test + + + +

Hello World

+ + +`, + "styles.css": `body { background: red; }`, + "app.js": `console.log("Hello from app");`, + }); + + using cleanup = { [Symbol.dispose]: () => rmScope(dir) }; + + const proc = Bun.spawn({ + cmd: [bunExe(), "run", join(dir, "server.ts")], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + const { stdout, stderr, exited } = proc; + + // Read stdout line by line until we get the PORT + let port: number | undefined; + const reader = stdout.getReader(); + const decoder = new StringDecoder("utf8"); + let buffer = ""; + + while (!port) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.write(value); + const lines = buffer.split("\n"); + buffer = lines.pop() || ""; + + for (const line of lines) { + const portMatch = line.match(/PORT=(\d+)/); + if (portMatch) { + port = parseInt(portMatch[1]); + break; + } + } + } + + reader.releaseLock(); + expect(port).toBeDefined(); + + if (port) { + // Test the server + const res = await fetch(`http://localhost:${port}/`); + expect(res.status).toBe(200); + expect(res.headers.get("content-type")).toContain("text/html"); + + const html = await res.text(); + expect(html).toContain("Hello World"); + expect(html).toContain(" { + const dir = tempDirWithFiles("serve-html-bundled", { + "build.ts": ` + const result = await Bun.build({ + entrypoints: ["./server.ts"], + target: "bun", + outdir: "./dist", + }); + + if (!result.success) { + console.error("Build failed"); + process.exit(1); + } + + console.log("Build complete"); + `, + "server.ts": ` + import index from "./index.html"; + import about from "./about.html"; + + const server = Bun.serve({ + port: 0, + routes: { + "/": index, + "/about": about, + }, + }); + + console.log("PORT=" + server.port); + `, + "index.html": ` + + + Home + + + +

Home Page

+ + +`, + "about.html": ` + + + About + + + +

About Page

+ + +`, + "shared.css": `body { margin: 0; }`, + "app.js": `console.log("App loaded");`, + }); + + using cleanup = { [Symbol.dispose]: () => rmScope(dir) }; + + // Build first + const buildProc = Bun.spawn({ + cmd: [bunExe(), "run", join(dir, "build.ts")], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + await buildProc.exited; + expect(buildProc.exitCode).toBe(0); + + // Run the built server + const serverProc = Bun.spawn({ + cmd: [bunExe(), "run", join(dir, "dist", "server.js")], + cwd: join(dir, "dist"), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + // Read stdout line by line until we get the PORT + let port: number | undefined; + const reader = serverProc.stdout.getReader(); + const decoder = new StringDecoder("utf8"); + let buffer = ""; + + while (!port) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.write(value); + const lines = buffer.split("\n"); + buffer = lines.pop() || ""; + + for (const line of lines) { + const portMatch = line.match(/PORT=(\d+)/); + if (portMatch) { + port = parseInt(portMatch[1]); + break; + } + } + } + + reader.releaseLock(); + expect(port).toBeDefined(); + + if (port) { + // Test both routes + const homeRes = await fetch(`http://localhost:${port}/`); + expect(homeRes.status).toBe(200); + const homeHtml = await homeRes.text(); + expect(homeHtml).toContain("Home Page"); + + const aboutRes = await fetch(`http://localhost:${port}/about`); + expect(aboutRes.status).toBe(200); + const aboutHtml = await aboutRes.text(); + expect(aboutHtml).toContain("About Page"); + } + + serverProc.kill(); + await serverProc.exited; + }); + + it("validates manifest files exist", async () => { + const dir = tempDirWithFiles("serve-html-validate", { + "test.ts": ` + // Create a fake manifest + const fakeManifest = { + index: "./index.html", + files: [ + { + input: "index.html", + path: "./does-not-exist.html", + loader: "html", + isEntry: true, + headers: { + etag: "test123", + "content-type": "text/html;charset=utf-8" + } + } + ] + }; + + try { + const server = Bun.serve({ + port: 0, + routes: { + "/": fakeManifest, + }, + }); + console.log("ERROR: Server started when it should have failed"); + server.stop(); + } catch (error) { + console.log("SUCCESS: Manifest validation failed as expected"); + } + `, + }); + + using cleanup = { [Symbol.dispose]: () => rmScope(dir) }; + + const proc = Bun.spawn({ + cmd: [bunExe(), "run", join(dir, "test.ts")], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + const out = await new Response(proc.stdout).text(); + await proc.exited; + + expect(out).toContain("SUCCESS: Manifest validation failed as expected"); + }); + + it("serves manifest with proper headers", async () => { + const dir = tempDirWithFiles("serve-html-headers", { + "server.ts": ` + import index from "./index.html"; + + const server = Bun.serve({ + port: 0, + routes: { + "/": index, + }, + }); + + console.log("PORT=" + server.port); + + // Check manifest structure + if (index.files) { + for (const file of index.files) { + console.log("File:", file.path, "Loader:", file.loader); + if (file.headers) { + console.log(" Content-Type:", file.headers["content-type"]); + console.log(" Has ETag:", !!file.headers.etag); + } + } + } + `, + "index.html": ` + + + Test + + + +

Test

+ +`, + "test.css": `h1 { color: red; }`, + }); + + using cleanup = { [Symbol.dispose]: () => rmScope(dir) }; + + // Build first to generate the manifest + const buildProc = Bun.spawn({ + cmd: [bunExe(), "build", join(dir, "server.ts"), "--outdir", join(dir, "dist"), "--target", "bun"], + cwd: dir, + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + await buildProc.exited; + expect(buildProc.exitCode).toBe(0); + + // Run the built server + const proc = Bun.spawn({ + cmd: [bunExe(), "run", join(dir, "dist", "server.js")], + cwd: join(dir, "dist"), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); + + // Read stdout line by line to collect all output + const reader = proc.stdout.getReader(); + const decoder = new StringDecoder("utf8"); + let buffer = ""; + let output = ""; + let etagCount = 0; + const expectedEtagLines = 2; // One for HTML, one for CSS + + while (etagCount < expectedEtagLines) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.write(value); + const lines = buffer.split("\n"); + buffer = lines.pop() || ""; + + for (const line of lines) { + output += line + "\n"; + if (line.includes("Has ETag:")) { + etagCount++; + } + } + } + + reader.releaseLock(); + + // Should have proper content types + expect(output).toContain("text/html"); + expect(output).toContain("text/css"); + expect(output).toContain("Has ETag:"); + + proc.kill(); + await proc.exited; + }); +}); diff --git a/test/no-validate-exceptions.txt b/test/no-validate-exceptions.txt index b313ae40dc..39d038dc19 100644 --- a/test/no-validate-exceptions.txt +++ b/test/no-validate-exceptions.txt @@ -29,6 +29,8 @@ test/bundler/css/wpt/relative_color_out_of_gamut.test.ts test/bundler/esbuild/css.test.ts test/bundler/esbuild/dce.test.ts test/bundler/esbuild/extra.test.ts +test/bundler/bundler_html_server.test.ts +test/js/bun/http/bun-serve-html-manifest.test.ts test/bundler/esbuild/importstar_ts.test.ts test/bundler/esbuild/importstar.test.ts test/bundler/esbuild/loader.test.ts From fd91e3de0df6ef925d8fa4ddc840441c10dfab43 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Sat, 21 Jun 2025 10:57:36 +0800 Subject: [PATCH 052/133] fix typo (#20449) --- docs/bundler/vs-esbuild.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bundler/vs-esbuild.md b/docs/bundler/vs-esbuild.md index 35bb62f6f7..1a57c136ca 100644 --- a/docs/bundler/vs-esbuild.md +++ b/docs/bundler/vs-esbuild.md @@ -125,7 +125,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot - `--target` - n/a -- No supported. Bun's bundler performs no syntactic down-leveling at this time. +- Not supported. Bun's bundler performs no syntactic down-leveling at this time. --- From 282dda62c8ea35ac5ba9482a90c95bfb592d9513 Mon Sep 17 00:00:00 2001 From: Michael H Date: Sat, 21 Jun 2025 12:58:54 +1000 Subject: [PATCH 053/133] Add `--import` as alias to `--preload` for nodejs compat (#20523) --- src/cli/Arguments.zig | 40 +++++------ .../bunfig/fixtures/preload/many/index.ts | 1 + .../bunfig/fixtures/preload/many/preload1.ts | 1 + .../bunfig/fixtures/preload/many/preload2.ts | 1 + .../bunfig/fixtures/preload/many/preload3.ts | 1 + test/config/bunfig/preload.test.ts | 70 ++++++++++++++++++- test/internal/ban-words.test.ts | 2 +- 7 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 test/config/bunfig/fixtures/preload/many/index.ts create mode 100644 test/config/bunfig/fixtures/preload/many/preload1.ts create mode 100644 test/config/bunfig/fixtures/preload/many/preload2.ts create mode 100644 test/config/bunfig/fixtures/preload/many/preload3.ts diff --git a/src/cli/Arguments.zig b/src/cli/Arguments.zig index d29edde371..e6e6659a9f 100644 --- a/src/cli/Arguments.zig +++ b/src/cli/Arguments.zig @@ -82,6 +82,7 @@ pub const runtime_params_ = [_]ParamType{ clap.parseParam("--smol Use less memory, but run garbage collection more often") catch unreachable, clap.parseParam("-r, --preload ... Import a module before other modules are loaded") catch unreachable, clap.parseParam("--require ... Alias of --preload, for Node.js compatibility") catch unreachable, + clap.parseParam("--import ... Alias of --preload, for Node.js compatibility") catch unreachable, clap.parseParam("--inspect ? Activate Bun's debugger") catch unreachable, clap.parseParam("--inspect-wait ? Activate Bun's debugger, wait for a connection before executing") catch unreachable, clap.parseParam("--inspect-brk ? Activate Bun's debugger, set breakpoint on first line of code and wait") catch unreachable, @@ -542,13 +543,23 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C // runtime commands if (cmd == .AutoCommand or cmd == .RunCommand or cmd == .TestCommand or cmd == .RunAsNodeCommand) { - var preloads = args.options("--preload"); - if (preloads.len == 0) { - if (bun.getenvZ("BUN_INSPECT_PRELOAD")) |preload| { - preloads = bun.default_allocator.dupe([]const u8, &.{preload}) catch unreachable; + { + const preloads = args.options("--preload"); + const preloads2 = args.options("--require"); + const preloads3 = args.options("--import"); + const preload4 = bun.getenvZ("BUN_INSPECT_PRELOAD"); + + const total_preloads = ctx.preloads.len + preloads.len + preloads2.len + preloads3.len + (if (preload4 != null) @as(usize, 1) else @as(usize, 0)); + if (total_preloads > 0) { + var all = std.ArrayList(string).initCapacity(ctx.allocator, total_preloads) catch unreachable; + if (ctx.preloads.len > 0) all.appendSliceAssumeCapacity(ctx.preloads); + if (preloads.len > 0) all.appendSliceAssumeCapacity(preloads); + if (preloads2.len > 0) all.appendSliceAssumeCapacity(preloads2); + if (preloads3.len > 0) all.appendSliceAssumeCapacity(preloads3); + if (preload4) |p| all.appendAssumeCapacity(p); + ctx.preloads = all.items; } } - const preloads2 = args.options("--require"); if (args.flag("--hot")) { ctx.debug.hot_reload = .hot; @@ -645,25 +656,6 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C } } - if (ctx.preloads.len > 0 and (preloads.len > 0 or preloads2.len > 0)) { - var all = std.ArrayList(string).initCapacity(ctx.allocator, ctx.preloads.len + preloads.len + preloads2.len) catch unreachable; - all.appendSliceAssumeCapacity(ctx.preloads); - all.appendSliceAssumeCapacity(preloads); - all.appendSliceAssumeCapacity(preloads2); - ctx.preloads = all.items; - } else if (preloads.len > 0) { - if (preloads2.len > 0) { - var all = std.ArrayList(string).initCapacity(ctx.allocator, preloads.len + preloads2.len) catch unreachable; - all.appendSliceAssumeCapacity(preloads); - all.appendSliceAssumeCapacity(preloads2); - ctx.preloads = all.items; - } else { - ctx.preloads = preloads; - } - } else if (preloads2.len > 0) { - ctx.preloads = preloads2; - } - if (args.option("--print")) |script| { ctx.runtime_options.eval.script = script; ctx.runtime_options.eval.eval_and_print = true; diff --git a/test/config/bunfig/fixtures/preload/many/index.ts b/test/config/bunfig/fixtures/preload/many/index.ts new file mode 100644 index 0000000000..8f356d1362 --- /dev/null +++ b/test/config/bunfig/fixtures/preload/many/index.ts @@ -0,0 +1 @@ +console.log(globalThis.preload); \ No newline at end of file diff --git a/test/config/bunfig/fixtures/preload/many/preload1.ts b/test/config/bunfig/fixtures/preload/many/preload1.ts new file mode 100644 index 0000000000..63141e2cd2 --- /dev/null +++ b/test/config/bunfig/fixtures/preload/many/preload1.ts @@ -0,0 +1 @@ +(globalThis.preload ??= []).push("multi/preload1.ts"); diff --git a/test/config/bunfig/fixtures/preload/many/preload2.ts b/test/config/bunfig/fixtures/preload/many/preload2.ts new file mode 100644 index 0000000000..59d054a998 --- /dev/null +++ b/test/config/bunfig/fixtures/preload/many/preload2.ts @@ -0,0 +1 @@ +(globalThis.preload ??= []).push("multi/preload2.ts"); diff --git a/test/config/bunfig/fixtures/preload/many/preload3.ts b/test/config/bunfig/fixtures/preload/many/preload3.ts new file mode 100644 index 0000000000..c5da4a3366 --- /dev/null +++ b/test/config/bunfig/fixtures/preload/many/preload3.ts @@ -0,0 +1 @@ +(globalThis.preload ??= []).push("multi/preload3.ts"); diff --git a/test/config/bunfig/preload.test.ts b/test/config/bunfig/preload.test.ts index 895be5ea57..44daf6c91d 100644 --- a/test/config/bunfig/preload.test.ts +++ b/test/config/bunfig/preload.test.ts @@ -7,13 +7,17 @@ const fixturePath = (...segs: string[]) => resolve(import.meta.dirname, "fixture type Opts = { args?: string[]; cwd?: string; + env?: Record; }; type Out = [stdout: string, stderr: string, exitCode: number]; -const run = (file: string, { args = [], cwd }: Opts = {}): Promise => { +const run = (file: string, { args = [], cwd, env = {} }: Opts = {}): Promise => { const res = Bun.spawn([bunExe(), ...args, file], { cwd, stdio: ["ignore", "pipe", "pipe"], - env: bunEnv, + env: { + ...env, + ...bunEnv, + }, } satisfies SpawnOptions.OptionsObject<"ignore", "pipe", "pipe">); return Promise.all([ @@ -134,3 +138,65 @@ describe("Given a `bunfig.toml` file with a relative path without a leading './' expect(code).toBe(0); }); }); // + +describe("Test that all the aliases for --preload work", () => { + const dir = fixturePath("many"); + + it.each(["--preload=./preload1.ts", "--require=./preload1.ts", "--import=./preload1.ts"])( + "When `bun run` is run with %s, the preload is executed", + async flag => { + const [out, err, code] = await run("index.ts", { args: [flag], cwd: dir }); + expect(err).toBeEmpty(); + expect(out).toBe('[ "multi/preload1.ts" ]'); + expect(code).toBe(0); + }, + ); + + it.each(["1", "2", "3", "4"])( + "When multiple preload flags are used, they execute in order: --preload, --require, --import (#%s)", + async i => { + let args: string[] = []; + if (i === "1") args = ["--preload", "./preload1.ts", "--require", "./preload2.ts", "--import", "./preload3.ts"]; + if (i === "2") args = ["--import", "./preload3.ts", "--preload=./preload1.ts", "--require", "./preload2.ts"]; + if (i === "3") args = ["--require", "./preload2.ts", "--import", "./preload3.ts", "--preload", "./preload1.ts"]; + if (i === "4") args = ["--require", "./preload1.ts", "--import", "./preload3.ts", "--require", "./preload2.ts"]; + const [out, err, code] = await run("index.ts", { args, cwd: dir }); + expect(err).toBeEmpty(); + expect(out).toBe('[ "multi/preload1.ts", "multi/preload2.ts", "multi/preload3.ts" ]'); + expect(code).toBe(0); + }, + ); + + it("Duplicate preload flags are only executed once", async () => { + const args = ["--preload", "./preload1.ts", "--require", "./preload1.ts", "--import", "./preload1.ts"]; + const [out, err, code] = await run("index.ts", { args, cwd: dir }); + expect(err).toBeEmpty(); + expect(out).toBe('[ "multi/preload1.ts" ]'); + expect(code).toBe(0); + }); + + it("Test double preload flags", async () => { + const dir = fixturePath("many"); + const args = [ + "--preload", + "./preload1.ts", + "--preload=./preload2.ts", + "--preload", + "./preload3.ts", + "-r", + "./preload3.ts", + ]; + const [out, err, code] = await run("index.ts", { args, cwd: dir }); + expect(err).toBeEmpty(); + expect(out).toMatchInlineSnapshot(`"[ "multi/preload1.ts", "multi/preload2.ts", "multi/preload3.ts" ]"`); + expect(code).toBe(0); + }); +}); // + +test("Test BUN_INSPECT_PRELOAD is used to set preloads", async () => { + const dir = fixturePath("many"); + const [out, err, code] = await run("index.ts", { args: [], cwd: dir, env: { BUN_INSPECT_PRELOAD: "./preload1.ts" } }); + expect(err).toBeEmpty(); + expect(out).toMatchInlineSnapshot(`"[ "multi/preload1.ts" ]"`); + expect(code).toBe(0); +}); // diff --git a/test/internal/ban-words.test.ts b/test/internal/ban-words.test.ts index ac05275e59..3ac65c6498 100644 --- a/test/internal/ban-words.test.ts +++ b/test/internal/ban-words.test.ts @@ -34,7 +34,7 @@ const words: Record [String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 242, regex: true }, "usingnamespace": { reason: "Zig 0.15 will remove `usingnamespace`" }, - "catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1860 }, + "catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1859 }, "std.fs.Dir": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 179 }, "std.fs.cwd": { reason: "Prefer bun.FD.cwd()", limit: 102 }, From 0b5363099b3a01057106b6582937c465611d539c Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 21 Jun 2025 01:00:48 -0700 Subject: [PATCH 054/133] Some docs --- docs/api/http.md | 10 +++-- docs/bundler/executables.md | 77 +++++++++++++++++++++++++++++++- docs/bundler/fullstack.md | 89 +++++++++++++++++++++++++++++++++++-- docs/bundler/index.md | 3 +- docs/bundler/loaders.md | 14 ++++++ 5 files changed, 185 insertions(+), 8 deletions(-) diff --git a/docs/api/http.md b/docs/api/http.md index 4e4fd43e62..0a0c959e48 100644 --- a/docs/api/http.md +++ b/docs/api/http.md @@ -326,7 +326,11 @@ Bun.serve({ ### HTML imports -To add a client-side single-page app, you can use an HTML import: +Bun supports importing HTML files directly into your server code, enabling full-stack applications with both server-side and client-side code. HTML imports work in two modes: + +**Development (`bun --hot`):** Assets are bundled on-demand at runtime, enabling hot module replacement (HMR) for a fast, iterative development experience. When you change your frontend code, the browser automatically updates without a full page reload. + +**Production (`bun build`):** When building with `bun build --target=bun`, the `import index from "./index.html"` statement resolves to a pre-built manifest object containing all bundled client assets. `Bun.serve` consumes this manifest to serve optimized assets with zero runtime bundling overhead. This is ideal for deploying to production. ```ts import myReactSinglePageApp from "./index.html"; @@ -338,9 +342,9 @@ Bun.serve({ }); ``` -HTML imports don't just serve HTML. It's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.sh/docs/bundler), JavaScript transpiler and CSS parser. +HTML imports don't just serve HTML — it's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.sh/docs/bundler), JavaScript transpiler and CSS parser. You can use this to build full-featured frontends with React, TypeScript, Tailwind CSS, and more. -You can use this to build a full-featured frontend with React, TypeScript, Tailwind CSS, and more. Check out [/docs/bundler/fullstack](https://bun.sh/docs/bundler/fullstack) to learn more. +For a complete guide on building full-stack applications with HTML imports, including detailed examples and best practices, see [/docs/bundler/fullstack](https://bun.sh/docs/bundler/fullstack). ### Practical example: REST API diff --git a/docs/bundler/executables.md b/docs/bundler/executables.md index 2cdad23f40..76d37954cf 100644 --- a/docs/bundler/executables.md +++ b/docs/bundler/executables.md @@ -126,6 +126,81 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time. +## Full-stack executables + +{% note %} + +New in Bun v1.2.17 + +{% /note %} + +Bun's `--compile` flag can create standalone executables that contain both server and client code, making it ideal for full-stack applications. When you import an HTML file in your server code, Bun automatically bundles all frontend assets (JavaScript, CSS, etc.) and embeds them into the executable. When Bun sees the HTML import on the server, it kicks off a frontend build process to bundle JavaScript, CSS, and other assets. + +{% codetabs %} + +```ts#server.ts +import { serve } from "bun"; +import index from "./index.html"; + +const server = serve({ + routes: { + "/": index, + "/api/hello": { GET: () => Response.json({ message: "Hello from API" }) }, + }, +}); + +console.log(`Server running at http://localhost:${server.port}`); +``` + +```html#index.html + + + + My App + + + +

Hello World

+ + + +``` + +```js#app.js +console.log("Hello from the client!"); +``` + +```css#styles.css +body { + background-color: #f0f0f0; +} +``` + +{% /codetabs %} + +To build this into a single executable: + +```sh +bun build --compile ./server.ts --outfile myapp +``` + +This creates a self-contained binary that includes: + +- Your server code +- The Bun runtime +- All frontend assets (HTML, CSS, JavaScript) +- Any npm packages used by your server + +The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run: + +```sh +./myapp +``` + +Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets. + +For more details on building full-stack applications with Bun, see the [full-stack guide](/docs/bundler/fullstack). + ## Worker To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments: @@ -174,7 +249,7 @@ $ ./hello Standalone executables support embedding files. -To embed files into an executable with `bun build --compile`, import the file in your code +To embed files into an executable with `bun build --compile`, import the file in your code. ```ts // this becomes an internal file path diff --git a/docs/bundler/fullstack.md b/docs/bundler/fullstack.md index e89b29af2e..f46c1aef58 100644 --- a/docs/bundler/fullstack.md +++ b/docs/bundler/fullstack.md @@ -1,5 +1,3 @@ -Using `Bun.serve()`'s `routes` option, you can run your frontend and backend in the same app with no extra steps. - To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`. ```ts @@ -234,7 +232,92 @@ When `console: true` is set, Bun will stream console logs from the browser to th #### Production mode -When serving your app in production, set `development: false` in `Bun.serve()`. +Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible. + +##### Ahead of time bundling (recommended) + +As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time. + +```sh +$ bun build --target=bun --production --outdir=dist ./src/index.ts +``` + +When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that Bun.serve() can use to serve the assets. + +```ts +import { serve } from "bun"; +import index from "./index.html"; + +serve({ + routes: { "/": index }, +}); +``` + +{% details summary="Internally, the `index` variable is a manifest object that looks something like this" %} + +```json +{ + "index": "./index.html", + "files": [ + { + "input": "index.html", + "path": "./index-f2me3qnf.js", + "loader": "js", + "isEntry": true, + "headers": { + "etag": "eet6gn75", + "content-type": "text/javascript;charset=utf-8" + } + }, + { + "input": "index.html", + "path": "./index.html", + "loader": "html", + "isEntry": true, + "headers": { + "etag": "r9njjakd", + "content-type": "text/html;charset=utf-8" + } + }, + { + "input": "index.html", + "path": "./index-gysa5fmk.css", + "loader": "css", + "isEntry": true, + "headers": { + "etag": "50zb7x61", + "content-type": "text/css;charset=utf-8" + } + }, + { + "input": "logo.svg", + "path": "./logo-kygw735p.svg", + "loader": "file", + "isEntry": false, + "headers": { + "etag": "kygw735p", + "content-type": "application/octet-stream" + } + }, + { + "input": "react.svg", + "path": "./react-ck11dneg.svg", + "loader": "file", + "isEntry": false, + "headers": { + "etag": "ck11dneg", + "content-type": "application/octet-stream" + } + } + ] +} +``` + +{% /details %} + +##### Runtime bundling + +When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`. - Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts. - Enables `Cache-Control` headers and `ETag` headers diff --git a/docs/bundler/index.md b/docs/bundler/index.md index 73987cc472..a889343eee 100644 --- a/docs/bundler/index.md +++ b/docs/bundler/index.md @@ -26,6 +26,7 @@ The bundler is a key piece of infrastructure in the JavaScript ecosystem. As a b - **Reducing HTTP requests.** A single package in `node_modules` may consist of hundreds of files, and large applications may have dozens of such dependencies. Loading each of these files with a separate HTTP request becomes untenable very quickly, so bundlers are used to convert our application source code into a smaller number of self-contained "bundles" that can be loaded with a single request. - **Code transforms.** Modern apps are commonly built with languages or tools like TypeScript, JSX, and CSS modules, all of which must be converted into plain JavaScript and CSS before they can be consumed by a browser. The bundler is the natural place to configure these transformations. - **Framework features.** Frameworks rely on bundler plugins & code transformations to implement common patterns like file-system routing, client-server code co-location (think `getServerSideProps` or Remix loaders), and server components. +- **Full-stack Applications.** Bun's bundler can handle both server and client code in a single command, enabling optimized production builds and single-file executables. With build-time HTML imports, you can bundle your entire application — frontend assets and backend server — into a single deployable unit. Let's jump into the bundler API. @@ -324,7 +325,7 @@ Depending on the target, Bun will apply different module resolution rules and op --- - `bun` -- For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance. +- For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance. This is the target to use for building full-stack applications with build-time HTML imports, where both server and client code are bundled together. All bundles generated with `target: "bun"` are marked with a special `// @bun` pragma, which indicates to the Bun runtime that there's no need to re-transpile the file before execution. diff --git a/docs/bundler/loaders.md b/docs/bundler/loaders.md index 6fac8cdaa6..440e579274 100644 --- a/docs/bundler/loaders.md +++ b/docs/bundler/loaders.md @@ -262,6 +262,20 @@ Currently, the list of selectors is: - `video[poster]` - `video[src]` +{% callout %} + +**HTML Loader Behavior in Different Contexts** + +The `html` loader behaves differently depending on how it's used: + +1. **Static Build:** When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed. + +2. **Runtime:** When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement. + +3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production. + +{% /callout %} + ### `sh` loader **Bun Shell loader**. Default for `.sh` files From 29dd4166f21221b59b25d6669c465d4d58a68348 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Sat, 21 Jun 2025 01:48:09 -0700 Subject: [PATCH 055/133] Bump --- LATEST | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LATEST b/LATEST index a96f385f15..21344eb17a 100644 --- a/LATEST +++ b/LATEST @@ -1 +1 @@ -1.2.16 \ No newline at end of file +1.2.17 \ No newline at end of file diff --git a/package.json b/package.json index 06acf99b10..a360184bbe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "bun", - "version": "1.2.17", + "version": "1.2.18", "workspaces": [ "./packages/bun-types", "./packages/@types/bun" From c40468ea39299b63032f2bf70268eb102f8641a1 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Sat, 21 Jun 2025 18:53:33 -0800 Subject: [PATCH 056/133] install: fix crash researching #5949 (#20461) --- src/install/install.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/install/install.zig b/src/install/install.zig index 555513b573..e9e3e1d2c1 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -799,6 +799,9 @@ pub const Task = struct { return; } + this.err = err; + this.status = Status.fail; + this.data = .{ .git_clone = bun.invalid_fd }; attempt += 1; break :brk null; }; @@ -816,12 +819,12 @@ pub const Task = struct { this.err = err; this.status = Status.fail; this.data = .{ .git_clone = bun.invalid_fd }; - return; } else { return; }; + this.err = null; this.data = .{ .git_clone = .fromStdDir(dir) }; this.status = Status.success; }, From 37505ad955da9f5b2ebba168154e6d31b8ecb674 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 21 Jun 2025 23:43:55 -0700 Subject: [PATCH 057/133] Deflake test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts on Windows --- test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts b/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts index 891591e408..dbfb71c7c8 100644 --- a/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts +++ b/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts @@ -33,6 +33,6 @@ if (numAbortSignalObjects > 10) { } const rss = (process.memoryUsage().rss / 1024 / 1024) | 0; -if (rss > 170) { +if (rss > 200) { throw new Error(`Memory leak detected: ${rss} MB, expected < 170 MB`); } From 064d7bb56e41254c1163f120498f7024114eb89d Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 21 Jun 2025 23:44:28 -0700 Subject: [PATCH 058/133] Fixes #10675 (#20553) --- src/bun.js/bindings/ZigGlobalObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index ef26ff8471..48da391232 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2106,7 +2106,7 @@ extern "C" int32_t ReadableStreamTag__tagged(Zig::GlobalObject* globalObject, JS JSValue target = object; JSValue fn = JSValue(); auto* function = jsDynamicCast(object); - if (function && function->jsExecutable() && function->jsExecutable()->isAsyncGenerator()) { + if (function && !function->isHostFunction() && function->jsExecutable() && function->jsExecutable()->isAsyncGenerator()) { fn = object; target = jsUndefined(); } else if (auto iterable = object->getIfPropertyExists(globalObject, vm.propertyNames->asyncIteratorSymbol)) { From 2cbb196f293fa5429ac01167d7d89b7fd0d0262c Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:44:59 -0700 Subject: [PATCH 059/133] Fix crash with garbage environment variables (#20527) Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Zack Radisic --- .../bindings/JSEnvironmentVariableMap.cpp | 11 +- test/cli/run/garbage-env.c | 133 ++++++++++++++++++ test/cli/run/garbage-env.test.ts | 25 ++++ test/no-validate-exceptions.txt | 1 + 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 test/cli/run/garbage-env.c create mode 100644 test/cli/run/garbage-env.test.ts diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp index d3f1baafb9..7c1781298f 100644 --- a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp @@ -305,10 +305,13 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject) bool hasNodeTLSRejectUnauthorized = false; bool hasBunConfigVerboseFetch = false; + auto* cached_getter_setter = JSC::CustomGetterSetter::create(vm, jsGetterEnvironmentVariable, nullptr); + for (size_t i = 0; i < count; i++) { unsigned char* chars; size_t len = Bun__getEnvKey(list, i, &chars); - auto name = String::fromUTF8(std::span { chars, len }); + // We can't really trust that the OS gives us valid UTF-8 + auto name = String::fromUTF8ReplacingInvalidSequences(std::span { chars, len }); #if OS(WINDOWS) keyArray->putByIndexInline(globalObject, (unsigned)i, jsString(vm, name), false); #endif @@ -347,7 +350,11 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject) } } - object->putDirectCustomAccessor(vm, identifier, JSC::CustomGetterSetter::create(vm, jsGetterEnvironmentVariable, jsSetterEnvironmentVariable), JSC::PropertyAttribute::CustomAccessor | 0); + // JSC::PropertyAttribute::CustomValue calls the getter ONCE (the first + // time) and then sets it onto the object, subsequent calls to the + // getter will not go through the getter and instead will just do the + // property lookup. + object->putDirectCustomAccessor(vm, identifier, cached_getter_setter, JSC::PropertyAttribute::CustomValue | 0); } unsigned int TZAttrs = JSC::PropertyAttribute::CustomAccessor | 0; diff --git a/test/cli/run/garbage-env.c b/test/cli/run/garbage-env.c new file mode 100644 index 0000000000..de651b305b --- /dev/null +++ b/test/cli/run/garbage-env.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include + +int main() { + int stdout_pipe[2], stderr_pipe[2]; + pid_t pid; + int status; + char stdout_buffer[4096] = {0}; + char stderr_buffer[4096] = {0}; + + // Create pipes for stdout and stderr + if (pipe(stdout_pipe) == -1 || pipe(stderr_pipe) == -1) { + perror("pipe"); + return 1; + } + + // Create garbage environment variables with stack buffers containing + // arbitrary bytes + char garbage1[64]; + char garbage2[64]; + char garbage3[64]; + char garbage4[64]; + char garbage5[64]; + + // Fill with arbitrary non-ASCII/UTF-8 bytes + for (int i = 0; i < 63; i++) { + garbage1[i] = (char)(0x80 + (i % 128)); // Invalid UTF-8 start bytes + garbage2[i] = (char)(0xFF - (i % 256)); // High bytes + garbage3[i] = (char)(i * 3 + 128); // Mixed garbage + garbage4[i] = (char)(0xC0 | (i & 0x1F)); // Invalid UTF-8 sequences + } + garbage1[63] = '\0'; + garbage2[63] = '\0'; + garbage3[63] = '\0'; + garbage4[63] = '\0'; + + for (int i = 0; i < 10; i++) { + garbage5[i] = (char)(0x80 + (i % 128)); + } + garbage5[10] = '='; + garbage5[11] = 0x81; + garbage5[12] = 0xF5; + garbage5[13] = 0xC1; + garbage5[14] = 0xC2; + + char *garbage_env[] = { + garbage5, + // garbage1, + // garbage2, + // garbage3, + // garbage4, + "PATH=/usr/bin:/bin", // Keep PATH so we can find commands + "BUN_DEBUG_QUIET_LOGS=1", "OOGA=booga", "OOGA=laskdjflsdf", NULL}; + + pid = fork(); + + if (pid == -1) { + perror("fork"); + return 1; + } + + if (pid == 0) { + // Child process + close(stdout_pipe[0]); // Close read end + close(stderr_pipe[0]); // Close read end + + // Redirect stdout and stderr to pipes + dup2(stdout_pipe[1], STDOUT_FILENO); + dup2(stderr_pipe[1], STDERR_FILENO); + + close(stdout_pipe[1]); + close(stderr_pipe[1]); + + char *BUN_PATH = getenv("BUN_PATH"); + if (BUN_PATH == NULL) { + fprintf(stderr, "Missing BUN_PATH!\n"); + fflush(stderr); + exit(1); + } + execve(BUN_PATH, + (char *[]){"bun-debug", "-e", "console.log(process.env)", NULL}, + garbage_env); + + // If both fail, exit with error + perror("execve"); + exit(127); + } else { + // Parent process + close(stdout_pipe[1]); // Close write end + close(stderr_pipe[1]); // Close write end + + // Read from stdout pipe + ssize_t stdout_bytes = + read(stdout_pipe[0], stdout_buffer, sizeof(stdout_buffer) - 1); + if (stdout_bytes > 0) { + stdout_buffer[stdout_bytes] = '\0'; + } + + // Read from stderr pipe + ssize_t stderr_bytes = + read(stderr_pipe[0], stderr_buffer, sizeof(stderr_buffer) - 1); + if (stderr_bytes > 0) { + stderr_buffer[stderr_bytes] = '\0'; + } + + close(stdout_pipe[0]); + close(stderr_pipe[0]); + + // Wait for child process + waitpid(pid, &status, 0); + + // Print results + printf("=== PROCESS OUTPUT ===\n"); + printf("Exit code: %d\n", WEXITSTATUS(status)); + + printf("\n=== STDOUT ===\n"); + printf("%s", stdout_buffer); + fflush(stdout); + + if (stderr_bytes > 0) { + fprintf(stderr, "\n=== STDERR ===\n"); + fprintf(stderr, "%s", stderr_buffer); + fflush(stderr); + } + exit(status); + } + + return 0; +} \ No newline at end of file diff --git a/test/cli/run/garbage-env.test.ts b/test/cli/run/garbage-env.test.ts new file mode 100644 index 0000000000..299e0f213b --- /dev/null +++ b/test/cli/run/garbage-env.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, test } from "bun:test"; +import { bunExe, isPosix } from "harness"; +import path from "path"; + +describe.if(isPosix)("garbage env", () => { + test("garbage env", async () => { + const cfile = path.join(import.meta.dirname, "garbage-env.c"); + { + const cc = Bun.which("clang") || Bun.which("gcc") || Bun.which("cc"); + const { exitCode, stderr } = await Bun.$`${cc} -o garbage-env ${cfile}`; + const stderrText = stderr.toString(); + if (stderrText.length > 0) { + console.error(stderrText); + } + expect(exitCode).toBe(0); + } + + const { exitCode, stderr } = await Bun.$`./garbage-env`.env({ BUN_PATH: bunExe() }); + const stderrText = stderr.toString(); + if (stderrText.length > 0) { + console.error(stderrText); + } + expect(exitCode).toBe(0); + }); +}); diff --git a/test/no-validate-exceptions.txt b/test/no-validate-exceptions.txt index 39d038dc19..ebd4d546bb 100644 --- a/test/no-validate-exceptions.txt +++ b/test/no-validate-exceptions.txt @@ -71,6 +71,7 @@ test/cli/install/catalogs.test.ts test/cli/install/npmrc.test.ts test/cli/install/overrides.test.ts test/cli/run/env.test.ts +test/cli/run/garbage-env.test.ts test/cli/run/esm-defineProperty.test.ts test/cli/run/jsx-namespaced-attributes.test.ts test/cli/run/log-test.test.ts From 25dbe5cf3f05102d6107f088000c0f173e91e779 Mon Sep 17 00:00:00 2001 From: Michael H Date: Sun, 22 Jun 2025 16:45:38 +1000 Subject: [PATCH 060/133] `fs.glob` set `onlyFiles: false` so it can match folders (#20510) --- src/js/internal/fs/glob.ts | 2 ++ test/js/node/fs/glob.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/js/internal/fs/glob.ts b/src/js/internal/fs/glob.ts index 03d2601f94..d78b419bc6 100644 --- a/src/js/internal/fs/glob.ts +++ b/src/js/internal/fs/glob.ts @@ -65,6 +65,8 @@ function mapOptions(options: GlobOptions): ExtendedGlobOptions { cwd: options?.cwd ?? process.cwd(), // https://github.com/nodejs/node/blob/a9546024975d0bfb0a8ae47da323b10fb5cbb88b/lib/internal/fs/glob.js#L655 followSymlinks: true, + // https://github.com/oven-sh/bun/issues/20507 + onlyFiles: false, exclude, }; } diff --git a/test/js/node/fs/glob.test.ts b/test/js/node/fs/glob.test.ts index dc5d70a243..0383efae19 100644 --- a/test/js/node/fs/glob.test.ts +++ b/test/js/node/fs/glob.test.ts @@ -14,6 +14,10 @@ beforeAll(() => { "bar.txt": "bar", "baz.js": "baz", }, + "folder.test": { + "file.txt": "content", + "another-folder": {}, + }, }); }); @@ -61,6 +65,11 @@ describe("fs.glob", () => { expect(() => fs.glob("*.txt", { cwd: tmp }, undefined)).toThrow(TypeError); }); }); + + it("matches directories", () => { + const paths = fs.globSync("*.test", { cwd: tmp }); + expect(paths).toContain("folder.test"); + }); }); // describe("fs.globSync", () => { @@ -120,6 +129,11 @@ describe("fs.globSync", () => { expect(() => fs.globSync(["*.txt"])).toThrow(TypeError); }); }); + + it("matches directories", () => { + const paths = fs.globSync("*.test", { cwd: tmp }); + expect(paths).toContain("folder.test"); + }); }); // describe("fs.promises.glob", () => { @@ -160,4 +174,15 @@ describe("fs.promises.glob", () => { process.cwd = oldProcessCwd; } }); + + it("matches directories", async () => { + const iter = fs.promises.glob("*.test", { cwd: tmp }); + expect(iter[Symbol.asyncIterator]).toBeDefined(); + let count = 0; + for await (const path of iter) { + expect(path).toBe("folder.test"); + count++; + } + expect(count).toBe(1); + }); }); // From 653c45966018bd81ec7823411a2201c0bbdf0551 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 21 Jun 2025 23:56:40 -0700 Subject: [PATCH 061/133] Fix asan failure in napi_async_work caused by accessing napi_async_work after freed (#20554) --- src/bun.js/event_loop/Task.zig | 2 +- src/napi/napi.zig | 67 ++++++++++++++++++---------------- test/napi/napi.test.ts | 5 ++- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/bun.js/event_loop/Task.zig b/src/bun.js/event_loop/Task.zig index 533c9ac879..9f6e5d7104 100644 --- a/src/bun.js/event_loop/Task.zig +++ b/src/bun.js/event_loop/Task.zig @@ -223,7 +223,7 @@ pub fn tickQueueWithCount(this: *EventLoop, virtual_machine: *VirtualMachine) u3 }, @field(Task.Tag, @typeName(bun.api.napi.napi_async_work)) => { const transform_task: *bun.api.napi.napi_async_work = task.get(bun.api.napi.napi_async_work).?; - transform_task.*.runFromJS(); + transform_task.runFromJS(virtual_machine, global); }, @field(Task.Tag, @typeName(ThreadSafeFunction)) => { var transform_task: *ThreadSafeFunction = task.as(ThreadSafeFunction); diff --git a/src/napi/napi.zig b/src/napi/napi.zig index c54cd8d883..5b25a597b5 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -1047,7 +1047,12 @@ pub const napi_async_work = struct { data: ?*anyopaque = null, status: std.atomic.Value(Status) = .init(.pending), scheduled: bool = false, - ref: Async.KeepAlive = .{}, + poll_ref: Async.KeepAlive = .{}, + ref_count: RefCount, + + const RefCount = bun.ptr.ThreadSafeRefCount(@This(), "ref_count", destroy, .{}); + pub const ref = RefCount.ref; + pub const deref = RefCount.deref; pub const Status = enum(u32) { pending = 0, @@ -1057,29 +1062,37 @@ pub const napi_async_work = struct { }; pub fn new(env: *NapiEnv, execute: napi_async_execute_callback, complete: ?napi_async_complete_callback, data: ?*anyopaque) !*napi_async_work { - const work = try bun.default_allocator.create(napi_async_work); const global = env.toJS(); - work.* = .{ + + const work = bun.new(napi_async_work, .{ .global = global, .env = env, .execute = execute, .event_loop = global.bunVM().eventLoop(), .complete = complete, .data = data, - }; + .ref_count = .initExactRefs(1), + }); return work; } pub fn destroy(this: *napi_async_work) void { - bun.default_allocator.destroy(this); + bun.debugAssert(!this.poll_ref.isActive()); // we must always have unref'd it. + bun.destroy(this); + } + + pub fn schedule(this: *napi_async_work) void { + if (this.scheduled) return; + this.scheduled = true; + this.poll_ref.ref(this.global.bunVM()); + WorkPool.schedule(&this.task); } pub fn runFromThreadPool(task: *WorkPoolTask) void { var this: *napi_async_work = @fieldParentPtr("task", task); - this.run(); } - pub fn run(this: *napi_async_work) void { + fn run(this: *napi_async_work) void { if (this.status.cmpxchgStrong(.pending, .started, .seq_cst, .seq_cst)) |state| { if (state == .cancelled) { this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); @@ -1092,26 +1105,15 @@ pub const napi_async_work = struct { this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); } - pub fn schedule(this: *napi_async_work) void { - if (this.scheduled) return; - this.scheduled = true; - this.ref.ref(this.global.bunVM()); - WorkPool.schedule(&this.task); - } - pub fn cancel(this: *napi_async_work) bool { return this.status.cmpxchgStrong(.pending, .cancelled, .seq_cst, .seq_cst) == null; } - fn runFromJSWithError(this: *napi_async_work) bun.JSError!void { - - // likely `complete` will call `napi_delete_async_work`, so take a copy - // of `ref` beforehand - var ref = this.ref; - const env = this.env; + fn runFromJSWithError(this: *napi_async_work, vm: *JSC.VirtualMachine, global: *JSC.JSGlobalObject) bun.JSError!void { + this.ref(); defer { - const global = env.toJS(); - ref.unref(global.bunVM()); + this.poll_ref.unref(vm); + this.deref(); } // https://github.com/nodejs/node/blob/a2de5b9150da60c77144bb5333371eaca3fab936/src/node_api.cc#L1201 @@ -1119,7 +1121,8 @@ pub const napi_async_work = struct { return; }; - const handle_scope = NapiHandleScope.open(this.env, false); + const env = this.env; + const handle_scope = NapiHandleScope.open(env, false); defer if (handle_scope) |scope| scope.close(env); const status: NapiStatus = if (this.status.load(.seq_cst) == .cancelled) @@ -1128,20 +1131,20 @@ pub const napi_async_work = struct { .ok; complete( - this.env, + env, @intFromEnum(status), this.data, ); - const global = env.toJS(); if (global.hasException()) { return error.JSError; } } - pub fn runFromJS(this: *napi_async_work) void { - this.runFromJSWithError() catch |e| { - this.global.reportActiveExceptionAsUnhandled(e); + pub fn runFromJS(this: *napi_async_work, vm: *JSC.VirtualMachine, global: *JSC.JSGlobalObject) void { + this.runFromJSWithError(vm, global) catch |e| { + // Note: the "this" value here may already be freed. + global.reportActiveExceptionAsUnhandled(e); }; } }; @@ -1306,8 +1309,8 @@ pub export fn napi_delete_async_work(env_: napi_env, work_: ?*napi_async_work) n const work = work_ orelse { return env.invalidArg(); }; - bun.assert(env.toJS() == work.global); - work.destroy(); + if (comptime bun.Environment.allow_assert) bun.assert(env.toJS() == work.global); + work.deref(); return env.ok(); } pub export fn napi_queue_async_work(env_: napi_env, work_: ?*napi_async_work) napi_status { @@ -1318,7 +1321,7 @@ pub export fn napi_queue_async_work(env_: napi_env, work_: ?*napi_async_work) na const work = work_ orelse { return env.invalidArg(); }; - bun.assert(env.toJS() == work.global); + if (comptime bun.Environment.allow_assert) bun.assert(env.toJS() == work.global); work.schedule(); return env.ok(); } @@ -1330,7 +1333,7 @@ pub export fn napi_cancel_async_work(env_: napi_env, work_: ?*napi_async_work) n const work = work_ orelse { return env.invalidArg(); }; - bun.assert(env.toJS() == work.global); + if (comptime bun.Environment.allow_assert) bun.assert(env.toJS() == work.global); if (work.cancel()) { return env.ok(); } diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index c5c2169f37..02c7d78d1c 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -294,8 +294,9 @@ describe("napi", () => { checkSameOutput("test_get_exception", [5]); checkSameOutput("test_get_exception", [{ foo: "bar" }]); }); - it("can throw an exception from an async_complete_callback", () => { - checkSameOutput("create_promise", [true]); + it("can throw an exception from an async_complete_callback", async () => { + const count = 10; + await Promise.all(Array.from({ length: count }, () => checkSameOutput("create_promise", [true]))); }); }); From 197c7abe7ddfc9ee1bf7365fa7c27d0e58afd05e Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 21 Jun 2025 23:57:04 -0700 Subject: [PATCH 062/133] Make the napi/v8 tests compile faster (#20555) --- test/napi/napi-app/package.json | 4 ++-- test/napi/napi.test.ts | 2 ++ test/napi/node-napi.test.ts | 2 +- test/v8/v8.test.ts | 13 +++++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/test/napi/napi-app/package.json b/test/napi/napi-app/package.json index ebc48bd9e2..5f2c942861 100644 --- a/test/napi/napi-app/package.json +++ b/test/napi/napi-app/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "gypfile": true, "scripts": { - "install": "node-gyp rebuild --debug", - "build": "node-gyp rebuild --debug", + "install": "node-gyp rebuild --debug -j max", + "build": "node-gyp rebuild --debug -j max", "clean": "node-gyp clean" }, "devDependencies": { diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 02c7d78d1c..4143cf0d7d 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -7,6 +7,7 @@ import { join } from "path"; describe("napi", () => { beforeAll(() => { // build gyp + console.time("Building node-gyp"); const install = spawnSync({ cmd: [bunExe(), "install", "--verbose"], cwd: join(__dirname, "napi-app"), @@ -19,6 +20,7 @@ describe("napi", () => { console.error("build failed, bailing out!"); process.exit(1); } + console.timeEnd("Building node-gyp"); }); describe.each(["esm", "cjs"])("bundle .node files to %s via", format => { diff --git a/test/napi/node-napi.test.ts b/test/napi/node-napi.test.ts index 5f043e98ef..5efeb52154 100644 --- a/test/napi/node-napi.test.ts +++ b/test/napi/node-napi.test.ts @@ -98,7 +98,7 @@ beforeAll(async () => { async function buildOne(dir: string) { const child = spawn({ - cmd: [bunExe(), "x", "node-gyp", "rebuild", "--debug"], + cmd: [bunExe(), "x", "node-gyp", "rebuild", "--debug", "-j", "max"], cwd: dir, stderr: "pipe", stdout: "ignore", diff --git a/test/v8/v8.test.ts b/test/v8/v8.test.ts index 0cdcc276e1..353bcdab87 100644 --- a/test/v8/v8.test.ts +++ b/test/v8/v8.test.ts @@ -62,8 +62,17 @@ async function build( const build = spawn({ cmd: runtime == Runtime.bun - ? [bunExe(), "x", "--bun", "node-gyp", "rebuild", buildMode == BuildMode.debug ? "--debug" : "--release"] - : [bunExe(), "x", "node-gyp", "rebuild", "--release"], // for node.js we don't bother with debug mode + ? [ + bunExe(), + "x", + "--bun", + "node-gyp", + "rebuild", + buildMode == BuildMode.debug ? "--debug" : "--release", + "-j", + "max", + ] + : [bunExe(), "x", "node-gyp", "rebuild", "--release", "-j", "max"], // for node.js we don't bother with debug mode cwd: tmpDir, env: bunEnv, stdin: "inherit", From 444b9d188384732a574c5b376b3e5a9c4f24a7b4 Mon Sep 17 00:00:00 2001 From: Grigory Date: Sun, 22 Jun 2025 17:33:15 +0500 Subject: [PATCH 063/133] build(windows/resources): specify company name (#20534) --- src/windows-app-info.rc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/windows-app-info.rc b/src/windows-app-info.rc index ed6473fbeb..1e50bc1a0c 100644 --- a/src/windows-app-info.rc +++ b/src/windows-app-info.rc @@ -18,7 +18,8 @@ BEGIN VALUE "InternalName", "bun\0" VALUE "OriginalFilename", "bun.exe\0" VALUE "ProductName", "Bun\0" - VALUE "ProductVersion", "@Bun_VERSION_WITH_TAG@\0" + VALUE "ProductVersion", "@Bun_VERSION_WITH_TAG@\0", + VALUE "CompanyName", "Oven\0" VALUE "LegalCopyright", "https://bun.sh/docs/project/licensing\0" END END From c7b1e5c709a14dc53422a02de59ff63aa836e86c Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 22 Jun 2025 18:22:41 -0700 Subject: [PATCH 064/133] Fix fs.watchFile ignoring atime (#20575) Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/node/node_fs_stat_watcher.zig | 12 +++++++++++- test/js/node/watch/fs.watchFile.test.ts | 14 +++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/bun.js/node/node_fs_stat_watcher.zig b/src/bun.js/node/node_fs_stat_watcher.zig index a58cb8aacf..8fcb3fe33a 100644 --- a/src/bun.js/node/node_fs_stat_watcher.zig +++ b/src/bun.js/node/node_fs_stat_watcher.zig @@ -430,7 +430,17 @@ pub const StatWatcher = struct { .err => std.mem.zeroes(bun.Stat), }; - if (std.mem.eql(u8, std.mem.asBytes(&res), std.mem.asBytes(&this.last_stat))) return; + var compare = res; + const StatT = @TypeOf(compare); + if (@hasField(StatT, "st_atim")) { + compare.st_atim = this.last_stat.st_atim; + } else if (@hasField(StatT, "st_atimespec")) { + compare.st_atimespec = this.last_stat.st_atimespec; + } else if (@hasField(StatT, "atim")) { + compare.atim = this.last_stat.atim; + } + + if (std.mem.eql(u8, std.mem.asBytes(&compare), std.mem.asBytes(&this.last_stat))) return; this.last_stat = res; this.enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, swapAndCallListenerOnMainThread)); diff --git a/test/js/node/watch/fs.watchFile.test.ts b/test/js/node/watch/fs.watchFile.test.ts index ec7166259c..dc84364d92 100644 --- a/test/js/node/watch/fs.watchFile.test.ts +++ b/test/js/node/watch/fs.watchFile.test.ts @@ -1,4 +1,4 @@ -import { tempDirWithFiles } from "harness"; +import { isWindows, tempDirWithFiles } from "harness"; import fs from "node:fs"; import path from "path"; @@ -113,6 +113,18 @@ describe("fs.watchFile", () => { expect(typeof entries[0][0].mtimeMs === "bigint").toBe(true); }); + test.if(isWindows)("does not fire on atime-only update", async () => { + let called = false; + const file = path.join(testDir, "watch.txt"); + fs.watchFile(file, { interval: 50 }, () => { + called = true; + }); + fs.readFileSync(file); + await Bun.sleep(100); + fs.unwatchFile(file); + expect(called).toBe(false); + }); + test("StatWatcherScheduler stress test (1000 watchers with random times)", async () => { const EventEmitter = require("events"); let defaultMaxListeners = EventEmitter.defaultMaxListeners; From 5416155449be83fbc61d123d1b72ce49ca06c6ae Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 22 Jun 2025 19:23:15 -0700 Subject: [PATCH 065/133] Enable Math.sumPrecise (#20569) --- src/bun.js/bindings/ZigGlobalObject.cpp | 1 + test/js/web/web-globals.test.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 48da391232..615d9486de 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -295,6 +295,7 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c JSC::Options::useJITCage() = false; JSC::Options::useShadowRealm() = true; JSC::Options::useV8DateParser() = true; + JSC::Options::useMathSumPreciseMethod() = true; JSC::Options::evalMode() = evalMode; JSC::Options::heapGrowthSteepnessFactor() = 1.0; JSC::Options::heapGrowthMaxIncrease() = 2.0; diff --git a/test/js/web/web-globals.test.js b/test/js/web/web-globals.test.js index 1270c0a810..551578d925 100644 --- a/test/js/web/web-globals.test.js +++ b/test/js/web/web-globals.test.js @@ -30,6 +30,7 @@ test("exists", () => { expect(typeof PerformanceResourceTiming !== "undefined").toBe(true); expect(typeof PerformanceServerTiming !== "undefined").toBe(true); expect(typeof PerformanceTiming !== "undefined").toBe(true); + expect(typeof Math.sumPrecise !== "undefined").toBe(true); }); const globalSetters = [ From 4cc61a1b8c681537cf7cc8ae47ea426d514cb42a Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 22 Jun 2025 20:51:45 -0700 Subject: [PATCH 066/133] Fix NODE_PATH for bun build (#20576) Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- docs/bundler/fullstack.md | 5 -- docs/cli/bun-create.md | 3 - docs/test/mocks.md | 2 - packages/bun-types/authoring.md | 2 - src/bundler/bundle_v2.zig | 4 +- src/bundler/linker_context/README.md | 3 - src/cli/build_command.zig | 3 + src/js/internal/streams/native-readable.ts | 3 +- src/js/node/wasi.ts | 2 +- src/node-fallbacks/util.js | 2 +- src/runtime.js | 2 +- test/bundler/bun-build-api.test.ts | 64 ++++++++++++++++++++++ test/bundler/bundler_regressions.test.ts | 20 +++++++ test/bundler/cli.test.ts | 2 +- test/bundler/esbuild/default.test.ts | 4 +- test/bundler/expectBundled.ts | 7 ++- test/cli/install/bun-add.test.ts | 4 +- test/cli/install/bun-install.test.ts | 4 +- test/js/bun/shell/bunshell.test.ts | 56 +++++++++---------- test/js/bun/shell/commands/cp.test.ts | 2 +- test/js/bun/test/expect.test.js | 4 +- test/js/bun/util/fuzzy-wuzzy.test.ts | 26 ++++----- 22 files changed, 150 insertions(+), 74 deletions(-) diff --git a/docs/bundler/fullstack.md b/docs/bundler/fullstack.md index f46c1aef58..587afe8c60 100644 --- a/docs/bundler/fullstack.md +++ b/docs/bundler/fullstack.md @@ -381,7 +381,6 @@ Note: this is currently in `bunfig.toml` to make it possible to know statically Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `