From 00545065389566853a8a6feeeda3a41e6aef8cb3 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Wed, 19 Nov 2025 18:54:41 -0800 Subject: [PATCH 01/20] fix(windows): close worker libuv loop (#24811) ### What does this PR do? We need to call `uv_loop_close` in order to remove the threadlocal loop from a list in libuv so it won't be used later. This explains the crash reports because they have `workers_terminated` in features. Fixes #24804 Closes BUN-3NV Closes ENG-21523 ### How did you verify your code works? Manually. I'm not sure how to write a test yet other than manually clicking sleep --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Zack Radisic --- src/bun.js/web_worker.zig | 12 +++++++++++- src/deps/libuv.zig | 31 ++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 4fb209e832..d74686cfaa 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -611,10 +611,19 @@ pub fn exitAndDeinit(this: *WebWorker) noreturn { loop_.internal_loop_data.jsc_vm = null; } + if (vm_to_deinit) |vm| { + // this deinit needs to happen before `Loop.shutdown` + // in order to not call uv_close on the gc timer twice. + vm.gc_controller.deinit(); + } + + if (comptime Environment.isWindows) { + bun.windows.libuv.Loop.shutdown(); + } + this.deinit(); if (vm_to_deinit) |vm| { - vm.gc_controller.deinit(); vm.deinit(); // NOTE: deinit here isn't implemented, so freeing workers will leak the vm. } bun.deleteAllPoolsForThreadExit(); @@ -637,6 +646,7 @@ const WTFStringImpl = @import("../string.zig").WTFStringImpl; const bun = @import("bun"); const Async = bun.Async; +const Environment = bun.Environment; const Output = bun.Output; const assert = bun.assert; diff --git a/src/deps/libuv.zig b/src/deps/libuv.zig index f09aa3c0c2..7e8db2d0ab 100644 --- a/src/deps/libuv.zig +++ b/src/deps/libuv.zig @@ -654,19 +654,28 @@ pub const Loop = extern struct { return null; } - pub fn close(ptr: *Loop) void { - _ = uv_loop_close(ptr); + fn closeWalkCb(handle: ?*uv_handle_t, data: ?*anyopaque) callconv(.c) void { + _ = data; + const h = handle.?; + if (uv_is_closing(h) == 0) { + uv_close(h, null); + } } - pub fn new() ?*Loop { - const ptr = bun.default_allocator.create(Loop) catch return null; - if (init(ptr) != null) return null; - return ptr; - } + pub fn shutdown() void { + const loop = threadlocal_loop orelse { + return; + }; - pub fn delete(ptr: *Loop) void { - close(ptr); - bun.default_allocator.destroy(ptr); + if (uv_loop_close(loop).errEnum()) |err| { + if (err == .BUSY) { + uv_walk(loop, &closeWalkCb, null); + _ = uv_run(loop, .default); + bun.debugAssert(uv_loop_close(loop) == .zero); + } + } + + threadlocal_loop = null; } threadlocal var threadlocal_loop_data: Loop = undefined; @@ -2120,7 +2129,7 @@ pub const uv_free_func = ?*const fn (?*anyopaque) callconv(.c) void; pub extern fn uv_library_shutdown() void; pub extern fn uv_replace_allocator(malloc_func: uv_malloc_func, realloc_func: uv_realloc_func, calloc_func: uv_calloc_func, free_func: uv_free_func) c_int; pub extern fn uv_loop_init(loop: *uv_loop_t) ReturnCode; -pub extern fn uv_loop_close(loop: *uv_loop_t) c_int; +pub extern fn uv_loop_close(loop: *uv_loop_t) ReturnCode; pub extern fn uv_loop_new() *uv_loop_t; pub extern fn uv_loop_delete(*uv_loop_t) void; pub extern fn uv_loop_size() usize; From b55462666204cdd74d5d2ff446746c7880b4929d Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Wed, 19 Nov 2025 20:31:37 -0800 Subject: [PATCH 02/20] fix ENG-21528 (#24865) ### What does this PR do? Makes sure we are creating error messages with an allocator that will not `deinit` at the end of function scope on error. fixes ENG-21528 ### How did you verify your code works? Added a test --- src/bun.js/api/JSTranspiler.zig | 6 +++--- test/bundler/transpiler/transpiler.test.js | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index fd76a26a97..d5f004113f 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -666,7 +666,7 @@ pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) b } if ((config.log.warnings + config.log.errors) > 0) { - return globalThis.throwValue(try config.log.toJS(globalThis, allocator, "Failed to create transpiler")); + return globalThis.throwValue(try config.log.toJS(globalThis, bun.default_allocator, "Failed to create transpiler")); } const log = &config.log; @@ -677,7 +677,7 @@ pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) b jsc.VirtualMachine.get().transpiler.env, ) catch |err| { if ((log.warnings + log.errors) > 0) { - return globalThis.throwValue(try log.toJS(globalThis, allocator, "Failed to create transpiler")); + return globalThis.throwValue(try log.toJS(globalThis, bun.default_allocator, "Failed to create transpiler")); } return globalThis.throwError(err, "Error creating transpiler"); @@ -688,7 +688,7 @@ pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) b transpiler.options.env.behavior = .disable; transpiler.configureDefines() catch |err| { if ((log.warnings + log.errors) > 0) { - return globalThis.throwValue(try log.toJS(globalThis, allocator, "Failed to load define")); + return globalThis.throwValue(try log.toJS(globalThis, bun.default_allocator, "Failed to load define")); } return globalThis.throwError(err, "Failed to load define"); }; diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index b8f57e7f0b..609d0e4d89 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -101,6 +101,12 @@ describe("Bun.Transpiler", () => { hideFromStackTrace(ts.expectPrinted); hideFromStackTrace(ts.expectParseError); + it("handles errors when parsing macros", () => { + expect(() => { + new Bun.Transpiler({ macro: "hi" }); + }).toThrow("Unexpected hi"); + }); + it("normalizes \\r\\n", () => { ts.expectPrinted_("console.log(`\r\n\r\n\r\n`)", "console.log(`\n\n\n`);\n"); }); From 9189fc4fa14c21f5e7bcc064183e58079b655758 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 19 Nov 2025 21:17:51 -0800 Subject: [PATCH 03/20] Fixes #24817 (#24864) ### What does this PR do? Fixes #24817 ### How did you verify your code works? Test --------- Co-authored-by: taylor.fish --- src/bun.js/api/server/StaticRoute.zig | 11 ++++- src/bun.js/webcore/Body.zig | 8 ++++ src/http/MimeType.zig | 1 - test/regression/issue/24817.test.ts | 62 +++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 test/regression/issue/24817.test.ts diff --git a/src/bun.js/api/server/StaticRoute.zig b/src/bun.js/api/server/StaticRoute.zig index f996168236..585f6ca6ae 100644 --- a/src/bun.js/api/server/StaticRoute.zig +++ b/src/bun.js/api/server/StaticRoute.zig @@ -29,9 +29,11 @@ pub const InitFromBytesOptions = struct { /// Ownership of `blob` is transferred to this function. pub fn initFromAnyBlob(blob: *const AnyBlob, options: InitFromBytesOptions) *StaticRoute { var headers = bun.handleOom(Headers.from(options.headers, bun.default_allocator, .{ .body = blob })); - if (options.mime_type) |mime_type| { - if (headers.getContentType() == null) { + if (headers.getContentType() == null) { + if (options.mime_type) |mime_type| { bun.handleOom(headers.append("Content-Type", mime_type.value)); + } else if (blob.hasContentTypeFromUser()) { + bun.handleOom(headers.append("Content-Type", blob.contentType())); } } @@ -92,6 +94,7 @@ pub fn fromJS(globalThis: *jsc.JSGlobalObject, argument: jsc.JSValue) bun.JSErro // The user may want to pass in the same Response object multiple endpoints // Let's let them do that. const bodyValue = response.getBodyValue(); + const was_string = bodyValue.wasString(); bodyValue.toBlobIfPossible(); const blob: AnyBlob = brk: { @@ -151,6 +154,10 @@ pub fn fromJS(globalThis: *jsc.JSGlobalObject, argument: jsc.JSValue) bun.JSErro .allocator = bun.default_allocator, }; + if (was_string and headers.getContentType() == null) { + bun.handleOom(headers.append("Content-Type", bun.http.MimeType.Table.@"text/plain; charset=utf-8".slice())); + } + // Generate ETag if not already present if (headers.get("etag") == null) { if (blob.slice().len > 0) { diff --git a/src/bun.js/webcore/Body.zig b/src/bun.js/webcore/Body.zig index e949b1fcfb..34b7da8595 100644 --- a/src/bun.js/webcore/Body.zig +++ b/src/bun.js/webcore/Body.zig @@ -272,6 +272,14 @@ pub const Value = union(Tag) { }; } + pub fn wasString(this: *const Value) bool { + return switch (this.*) { + .InternalBlob => |*blob| blob.was_string, + .WTFStringImpl => true, + else => false, + }; + } + pub const heap_breakdown_label = "BodyValue"; pub const ValueError = union(enum) { AbortReason: jsc.CommonAbortReason, diff --git a/src/http/MimeType.zig b/src/http/MimeType.zig index dcb643a445..b051b16c8d 100644 --- a/src/http/MimeType.zig +++ b/src/http/MimeType.zig @@ -246,7 +246,6 @@ pub const html = MimeType.initComptime("text/html;charset=utf-8", .html); pub const json = MimeType.initComptime("application/json;charset=utf-8", .json); pub const transpiled_json = javascript; pub const text = MimeType.initComptime("text/plain;charset=utf-8", .html); -pub const text_plain = MimeType.initComptime("text/plain;charset=utf-8", .text); pub const wasm = MimeType.initComptime( "application/wasm", .wasm, diff --git a/test/regression/issue/24817.test.ts b/test/regression/issue/24817.test.ts new file mode 100644 index 0000000000..5c8aac5b22 --- /dev/null +++ b/test/regression/issue/24817.test.ts @@ -0,0 +1,62 @@ +// https://github.com/oven-sh/bun/issues/24817 +// Unicode not working with static route +import { expect, test } from "bun:test"; + +test("static routes should handle unicode correctly", async () => { + using server = Bun.serve({ + port: 0, + routes: { + "/dynamic": () => new Response("▲"), + "/static": new Response("▲"), + "/unicode-string": new Response("こんにちは世界"), + "/emoji": new Response("🎉🚀✨"), + }, + }); + + const baseUrl = server.url.href; + + // Test basic unicode character + { + const staticResp = await fetch(`${baseUrl}/static`); + + const staticText = await staticResp.text(); + expect(staticText).toBe("▲"); + expect(staticResp.headers.get("content-type")).toBe("text/plain; charset=utf-8"); + } + + // Test Japanese characters + { + const resp = await fetch(`${baseUrl}/unicode-string`); + const text = await resp.text(); + + expect(text).toBe("こんにちは世界"); + expect(resp.headers.get("content-type")).toBe("text/plain; charset=utf-8"); + } + + // Test emoji + { + const resp = await fetch(`${baseUrl}/emoji`); + const text = await resp.text(); + + expect(text).toBe("🎉🚀✨"); + expect(resp.headers.get("content-type")).toBe("text/plain; charset=utf-8"); + } +}); + +test("static routes with explicit content-type should not override", async () => { + using server = Bun.serve({ + port: 0, + routes: { + "/custom": new Response("▲", { headers: { "content-type": "text/html" } }), + }, + }); + + const baseUrl = server.url.href; + + const resp = await fetch(`${baseUrl}/custom`); + const text = await resp.text(); + + expect(text).toBe("▲"); + // Should respect the explicit content-type + expect(resp.headers.get("content-type")).toBe("text/html"); +}); From 0480d55a676e8f4c960042e5485e92aa079434a9 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Wed, 19 Nov 2025 21:20:55 -0800 Subject: [PATCH 04/20] fix(YAML): handle exponential merge keys (#24729) ### What does this PR do? Fixes ENG-21490 ### How did you verify your code works? Added a test that would previously fail due to timeout. It also confirms the parsed result is correct. --------- Co-authored-by: taylor.fish --- src/collections/baby_list.zig | 21 ++-- src/interchange/yaml.zig | 187 ++++++++++++++++------------------ test/js/bun/yaml/yaml.test.ts | 125 +++++++++++++++++++++++ 3 files changed, 223 insertions(+), 110 deletions(-) diff --git a/src/collections/baby_list.zig b/src/collections/baby_list.zig index 80237c0c3f..bac768a269 100644 --- a/src/collections/baby_list.zig +++ b/src/collections/baby_list.zig @@ -53,13 +53,6 @@ pub fn BabyList(comptime Type: type) type { const unsupported_arg_msg = "unsupported argument to `moveFromList`: *" ++ @typeName(ListType); - const items = if (comptime @hasField(ListType, "items")) - list_ptr.items - else if (comptime std.meta.hasFn(ListType, "slice")) - list_ptr.slice() - else - @compileError(unsupported_arg_msg); - const capacity = if (comptime @hasField(ListType, "capacity")) list_ptr.capacity else if (comptime @hasField(ListType, "cap")) @@ -69,6 +62,15 @@ pub fn BabyList(comptime Type: type) type { else @compileError(unsupported_arg_msg); + const items = if (comptime std.meta.hasFn(ListType, "moveToUnmanaged")) + list_ptr.moveToUnmanaged().items + else if (comptime @hasField(ListType, "items")) + list_ptr.items + else if (comptime std.meta.hasFn(ListType, "slice")) + list_ptr.slice() + else + @compileError(unsupported_arg_msg); + if (comptime Environment.allow_assert) { bun.assert(items.len <= capacity); } @@ -88,7 +90,10 @@ pub fn BabyList(comptime Type: type) type { list_ptr.* = .empty; } else { this.#allocator.set(bun.allocators.asStd(allocator)); - list_ptr.* = .init(allocator); + // `moveToUnmanaged` already cleared the old list. + if (comptime !std.meta.hasFn(ListType, "moveToUnmanaged")) { + list_ptr.* = .init(allocator); + } } return this; } diff --git a/src/interchange/yaml.zig b/src/interchange/yaml.zig index 7aab24b11b..048fc86d53 100644 --- a/src/interchange/yaml.zig +++ b/src/interchange/yaml.zig @@ -780,7 +780,7 @@ pub fn Parser(comptime enc: Encoding) type { const mapping_line = self.token.line; _ = mapping_line; - var props: std.array_list.Managed(G.Property) = .init(self.allocator); + var props: MappingProps = .init(self.allocator); { try self.context.set(.flow_in); @@ -833,39 +833,7 @@ pub fn Parser(comptime enc: Encoding) type { }); } else { const value = try self.parseNode(.{}); - - append: { - switch (key.data) { - .e_string => |key_string| { - if (key_string.eqlComptime("<<")) { - switch (value.data) { - .e_object => |value_obj| { - try props.appendSlice(value_obj.properties.slice()); - break :append; - }, - .e_array => |value_arr| { - for (value_arr.slice()) |item| { - switch (item.data) { - .e_object => |item_obj| { - try props.appendSlice(item_obj.properties.slice()); - }, - else => {}, - } - } - break :append; - }, - else => {}, - } - } - }, - else => {}, - } - - try props.append(.{ - .key = key, - .value = value, - }); - } + try props.appendMaybeMerge(key, value); } if (self.token.data == .collect_entry) { @@ -880,7 +848,7 @@ pub fn Parser(comptime enc: Encoding) type { try self.scan(.{}); - return .init(E.Object, .{ .properties = .moveFromList(&props) }, mapping_start.loc()); + return .init(E.Object, .{ .properties = props.moveList() }, mapping_start.loc()); } fn parseBlockSequence(self: *@This()) ParseError!Expr { @@ -994,6 +962,83 @@ pub fn Parser(comptime enc: Encoding) type { return .init(E.Array, .{ .items = .moveFromList(&seq) }, sequence_start.loc()); } + /// Should only be used with expressions created with the YAML parser. It assumes + /// only null, boolean, number, string, array, object are possible. It also only + /// does pointer comparison with arrays and objects (so exponential merges are avoided) + fn yamlMergeKeyExprEql(l: Expr, r: Expr) bool { + if (std.meta.activeTag(l.data) != std.meta.activeTag(r.data)) { + return false; + } + + return switch (l.data) { + .e_null => true, + .e_boolean => |l_boolean| l_boolean.value == r.data.e_boolean.value, + .e_number => |l_number| l_number.value == r.data.e_number.value, + .e_string => |l_string| l_string.eql(E.String, r.data.e_string), + + .e_array => |l_array| l_array == r.data.e_array, + .e_object => |l_object| l_object == r.data.e_object, + + else => false, + }; + } + + const MappingProps = struct { + #list: bun.collections.ArrayList(G.Property), + + pub fn init(allocator: std.mem.Allocator) MappingProps { + return .{ .#list = .initIn(allocator) }; + } + + pub fn merge(self: *MappingProps, merge_props: []const G.Property) OOM!void { + try self.#list.ensureUnusedCapacity(merge_props.len); + var iter = std.mem.reverseIterator(merge_props); + next_merge_prop: while (iter.next()) |merge_prop| { + const merge_key = merge_prop.key.?; + for (self.#list.items()) |existing_prop| { + const existing_key = existing_prop.key.?; + if (yamlMergeKeyExprEql(existing_key, merge_key)) { + continue :next_merge_prop; + } + } + self.#list.appendAssumeCapacity(merge_prop); + } + } + + pub fn append(self: *MappingProps, prop: G.Property) OOM!void { + try self.#list.append(prop); + } + + pub fn appendMaybeMerge(self: *MappingProps, key: Expr, value: Expr) OOM!void { + if (switch (key.data) { + .e_string => |key_str| !key_str.eqlComptime("<<"), + else => true, + }) { + return self.#list.append(.{ .key = key, .value = value }); + } + + return switch (value.data) { + .e_object => |value_obj| self.merge(value_obj.properties.slice()), + .e_array => |value_arr| { + for (value_arr.items.slice()) |item| { + const item_obj = switch (item.data) { + .e_object => |obj| obj, + else => continue, + }; + + try self.merge(item_obj.properties.slice()); + } + }, + + else => self.#list.append(.{ .key = key, .value = value }), + }; + } + + pub fn moveList(self: *MappingProps) G.Property.List { + return .moveFromList(&self.#list); + } + }; + fn parseBlockMapping( self: *@This(), first_key: Expr, @@ -1011,7 +1056,7 @@ pub fn Parser(comptime enc: Encoding) type { try self.block_indents.push(mapping_indent); defer self.block_indents.pop(); - var props: std.array_list.Managed(G.Property) = .init(self.allocator); + var props: MappingProps = .init(self.allocator); { // try self.context.set(.block_in); @@ -1056,42 +1101,11 @@ pub fn Parser(comptime enc: Encoding) type { }, }; - append: { - switch (first_key.data) { - .e_string => |key_string| { - if (key_string.eqlComptime("<<")) { - switch (value.data) { - .e_object => |value_obj| { - try props.appendSlice(value_obj.properties.slice()); - break :append; - }, - .e_array => |value_arr| { - for (value_arr.slice()) |item| { - switch (item.data) { - .e_object => |item_obj| { - try props.appendSlice(item_obj.properties.slice()); - }, - else => {}, - } - } - break :append; - }, - else => {}, - } - } - }, - else => {}, - } - - try props.append(.{ - .key = first_key, - .value = value, - }); - } + try props.appendMaybeMerge(first_key, value); } if (self.context.get() == .flow_in) { - return .init(E.Object, .{ .properties = .moveFromList(&props) }, mapping_start.loc()); + return .init(E.Object, .{ .properties = props.moveList() }, mapping_start.loc()); } try self.context.set(.block_in); @@ -1173,41 +1187,10 @@ pub fn Parser(comptime enc: Encoding) type { }, }; - append: { - switch (key.data) { - .e_string => |key_string| { - if (key_string.eqlComptime("<<")) { - switch (value.data) { - .e_object => |value_obj| { - try props.appendSlice(value_obj.properties.slice()); - break :append; - }, - .e_array => |value_arr| { - for (value_arr.slice()) |item| { - switch (item.data) { - .e_object => |item_obj| { - try props.appendSlice(item_obj.properties.slice()); - }, - else => {}, - } - } - break :append; - }, - else => {}, - } - } - }, - else => {}, - } - - try props.append(.{ - .key = key, - .value = value, - }); - } + try props.appendMaybeMerge(key, value); } - return .init(E.Object, .{ .properties = .moveFromList(&props) }, mapping_start.loc()); + return .init(E.Object, .{ .properties = props.moveList() }, mapping_start.loc()); } const NodeProperties = struct { diff --git a/test/js/bun/yaml/yaml.test.ts b/test/js/bun/yaml/yaml.test.ts index 59276fc20e..cecb864cd3 100644 --- a/test/js/bun/yaml/yaml.test.ts +++ b/test/js/bun/yaml/yaml.test.ts @@ -813,6 +813,131 @@ my_config: const input2 = await file(join(import.meta.dir, "fixtures", "AHatInTime.yaml")).text(); expect(YAML.parse(input2)).toMatchSnapshot(); }); + + test("handles YAML bombs", () => { + function buildTest(depth) { + const lines: string[] = []; + lines.push(`a0: &a0\n k0: 0`); + for (let i = 1; i <= depth; i++) { + const refs = Array.from({ length: i }, (_, j) => `*a${j}`).join(", "); + lines.push(`a${i}: &a${i}\n <<: [${refs}]\n k${i}: ${i}`); + } + lines.push(`root:\n <<: *a${depth}`); + const input = lines.join("\n"); + + const expected: any = {}; + for (let i = 0; i <= depth; i++) { + const record = {}; + for (let j = 0; j <= i; j++) record[`k${j}`] = j; + expected[`a${i}`] = record; + } + expected.root = { ...expected[`a${depth}`] }; + + return { input, expected }; + } + + const { input, expected } = buildTest(24); + + expect(YAML.parse(input)).toEqual(expected); + }, 100); + + describe("merge keys", () => { + test("merge overrides", () => { + const input = ` +--- +- &CENTER { x: 1, 'y': 2 } +- &LEFT { x: 0, 'y': 2 } +- &BIG { r: 10 } +- &SMALL { r: 1 } + +# All the following maps are equal: + +- # Explicit keys + x: 1 + 'y': 2 + r: 10 + label: center/big + +- # Merge one map + << : *CENTER + r: 10 + label: center/big + +- # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +- # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big + `; + + const expected = [ + { x: 1, y: 2 }, + { x: 0, y: 2 }, + { r: 10 }, + { r: 1 }, + { x: 1, y: 2, r: 10, label: "center/big" }, + { x: 1, y: 2, r: 10, label: "center/big" }, + { x: 1, y: 2, r: 10, label: "center/big" }, + { x: 1, y: 2, r: 10, label: "center/big" }, + ]; + + expect(YAML.parse(input)).toEqual(expected); + }); + + test("duplicate merge key", () => { + const input = ` +--- +<<: {x: 1, y: 2} +foo: bar +<<: {z: 3, t: 4} +`; + + expect(YAML.parse(input)).toEqual({ + x: 1, + y: 2, + z: 3, + t: 4, + foo: "bar", + }); + }); + + test("duplicate keys from the same anchor", () => { + let input = ` +defaults: &d + foo: 1 + foo: 2 +config: + <<: *d`; + expect(YAML.parse(input)).toEqual({ + defaults: { + foo: 2, + }, + config: { + foo: 2, + }, + }); + + // Can still override + input = ` +defaults: &d + foo: 1 + foo: 2 +config: + <<: *d + foo: 3`; + expect(YAML.parse(input)).toEqual({ + defaults: { + foo: 2, + }, + config: { + foo: 3, + }, + }); + }); + }); }); describe("stringify", () => { From d584c86d5bb8b63905d794bd95ff8d498c43a1e8 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 19 Nov 2025 22:16:41 -0800 Subject: [PATCH 05/20] Delete incorrect debug assertion --- src/bun.js/bindings/bindings.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index b0c60b98c6..945c31bda4 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -5359,8 +5359,6 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC::JSGlobalObject* arg0 JSValue::decode(JSValue4) }; - ASSERT(microtaskArgs[0].isCallable()); - if (microtaskArgs[1].isEmpty()) { microtaskArgs[1] = jsUndefined(); } From 0e23375d204c5140de5d648f0450677948747af9 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Wed, 19 Nov 2025 22:44:21 -0800 Subject: [PATCH 06/20] fix ENG-21527 (#24861) ### What does this PR do? fixes ENG-21527 ### How did you verify your code works? Added a test --- src/bun.js/bindings/webcore/JSDOMFormData.cpp | 10 ++++++++-- test/js/web/html/FormData.test.ts | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp index 65d317ecf9..c6b9683540 100644 --- a/src/bun.js/bindings/webcore/JSDOMFormData.cpp +++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp @@ -651,6 +651,7 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSGlobalObject* lexicalGlobalObj if (jsValue.isString() || jsValue.inherits()) { // Make sure this runs before the deferral scope is called. JSValue resultValue = toJSValue(value); + RETURN_IF_EXCEPTION(throwScope, {}); ensureStillAliveHere(resultValue); JSC::JSArray* array = nullptr; @@ -672,7 +673,9 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSGlobalObject* lexicalGlobalObj obj->putDirect(vm, ident, array, 0); } else if (jsValue.isObject() && jsValue.getObject()->inherits()) { JSC::JSArray* array = jsCast(jsValue.getObject()); - array->push(lexicalGlobalObject, toJSValue(value)); + JSValue jsValue = toJSValue(value); + RETURN_IF_EXCEPTION(throwScope, {}); + array->push(lexicalGlobalObject, jsValue); RETURN_IF_EXCEPTION(throwScope, {}); } else { @@ -680,7 +683,10 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSGlobalObject* lexicalGlobalObj } } else { seenKeys.add(key); - obj->putDirect(vm, ident, toJSValue(value), 0); + JSValue jsValue = toJSValue(value); + RETURN_IF_EXCEPTION(throwScope, {}); + obj->putDirectMayBeIndex(lexicalGlobalObject, ident, jsValue); + RETURN_IF_EXCEPTION(throwScope, {}); } } diff --git a/test/js/web/html/FormData.test.ts b/test/js/web/html/FormData.test.ts index 8ad2491624..d81dd74345 100644 --- a/test/js/web/html/FormData.test.ts +++ b/test/js/web/html/FormData.test.ts @@ -269,6 +269,14 @@ describe("FormData", () => { }); }); + test("FormData.toJSON doesn't crash with numbers", () => { + const fd = new FormData(); + // @ts-expect-error + fd.append(1, 1); + // @ts-expect-error + expect(fd.toJSON()).toEqual({ "1": "1" }); + }); + it("should throw on bad boundary", async () => { const response = new Response('foo\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n', { headers: { From 788f03454d4f77f82a2d3cc6223130048fba4a35 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 19 Nov 2025 22:52:01 -0800 Subject: [PATCH 07/20] Show debugger in crash reports (#24871) ### What does this PR do? Show debugger in crash reports ### How did you verify your code works? --- src/analytics.zig | 1 + src/bun.js/Debugger.zig | 1 + 2 files changed, 2 insertions(+) diff --git a/src/analytics.zig b/src/analytics.zig index fd8f46876d..0afa2d7605 100644 --- a/src/analytics.zig +++ b/src/analytics.zig @@ -39,6 +39,7 @@ pub const Features = struct { pub var bunfig: usize = 0; pub var define: usize = 0; pub var dotenv: usize = 0; + pub var debugger: usize = 0; pub var external: usize = 0; pub var extracted_packages: usize = 0; pub var fetch: usize = 0; diff --git a/src/bun.js/Debugger.zig b/src/bun.js/Debugger.zig index 8a1f5e0afe..090becc477 100644 --- a/src/bun.js/Debugger.zig +++ b/src/bun.js/Debugger.zig @@ -30,6 +30,7 @@ var futex_atomic: std.atomic.Value(u32) = .init(0); pub fn waitForDebuggerIfNecessary(this: *VirtualMachine) void { const debugger = &(this.debugger orelse return); + bun.analytics.Features.debugger += 1; if (!debugger.must_block_until_connected) { return; } From b38ba38a187b53de55137d1941c8517f8ab04095 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Thu, 20 Nov 2025 02:08:49 -0500 Subject: [PATCH 08/20] types: correct ReadableStream methods, allow Response instance for serve routes under a method (#24872) --- packages/bun-types/overrides.d.ts | 25 ++++--- packages/bun-types/serve.d.ts | 29 ++++---- test/integration/bun-types/bun-types.test.ts | 2 +- test/integration/bun-types/fixture/globals.ts | 5 ++ .../bun-types/fixture/serve-types.test.ts | 67 ++++++++++++++++--- test/integration/bun-types/fixture/streams.ts | 12 ++++ 6 files changed, 109 insertions(+), 31 deletions(-) diff --git a/packages/bun-types/overrides.d.ts b/packages/bun-types/overrides.d.ts index f3b6f5e2cf..1ebfac963c 100644 --- a/packages/bun-types/overrides.d.ts +++ b/packages/bun-types/overrides.d.ts @@ -23,16 +23,6 @@ interface BunConsumerConvenienceMethods { * Consume as JSON */ json(): Promise; - - /** - * Consume as a FormData instance - */ - formData(): Promise; - - /** - * Consume as an ArrayBuffer - */ - arrayBuffer(): Promise; } declare module "stream/web" { @@ -51,6 +41,21 @@ declare module "buffer" { // slightly different from just "copying in the methods" (the difference is // related to how type parameters are resolved) bytes(): Promise>; + + /** + * Consume the blob as a FormData instance + */ + formData(): Promise; + + /** + * Consume the blob as an ArrayBuffer + */ + arrayBuffer(): Promise; + + /** + * Returns a readable stream of the blob's contents + */ + stream(): ReadableStream>; } } diff --git a/packages/bun-types/serve.d.ts b/packages/bun-types/serve.d.ts index c465af924c..78b0eea3d4 100644 --- a/packages/bun-types/serve.d.ts +++ b/packages/bun-types/serve.d.ts @@ -283,7 +283,8 @@ declare module "bun" { * return new Response(); * }, * websocket: { - * open(ws) { + * data: {} as {accessToken: string | null}, + * message(ws) { * console.log(ws.data.accessToken); * } * } @@ -486,13 +487,15 @@ declare module "bun" { } namespace Serve { - type ExtractRouteParams = T extends `${string}:${infer Param}/${infer Rest}` - ? { [K in Param]: string } & ExtractRouteParams - : T extends `${string}:${infer Param}` - ? { [K in Param]: string } - : T extends `${string}*` - ? {} - : {}; + type ExtractRouteParams = string extends T + ? Record + : T extends `${string}:${infer Param}/${infer Rest}` + ? { [K in Param]: string } & ExtractRouteParams + : T extends `${string}:${infer Param}` + ? { [K in Param]: string } + : T extends `${string}*` + ? {} + : {}; /** * Development configuration for {@link Bun.serve} @@ -549,14 +552,16 @@ declare module "bun" { [Path in R]: | BaseRouteValue | Handler, Server, Response> - | Partial, Server, Response>>>; + | Partial, Server, Response> | Response>>; }; type RoutesWithUpgrade = { [Path in R]: | BaseRouteValue | Handler, Server, Response | undefined | void> - | Partial, Server, Response | undefined | void>>>; + | Partial< + Record, Server, Response | undefined | void> | Response> + >; }; type FetchOrRoutes = @@ -786,7 +791,7 @@ declare module "bun" { * } satisfies Bun.Serve.Options<{ name: string }>; * ``` */ - type Options = Bun.__internal.XOR< + type Options = Bun.__internal.XOR< HostnamePortServeOptions, UnixServeOptions > & @@ -1276,7 +1281,7 @@ declare module "bun" { * }); * ``` */ - function serve( + function serve( options: Serve.Options, ): Server; } diff --git a/test/integration/bun-types/bun-types.test.ts b/test/integration/bun-types/bun-types.test.ts index 9e4a37d094..a48d0c17b8 100644 --- a/test/integration/bun-types/bun-types.test.ts +++ b/test/integration/bun-types/bun-types.test.ts @@ -435,7 +435,7 @@ describe("@types/bun integration test", () => { code: 2322, line: "24154.ts:11:3", message: - "Type 'Blob' is not assignable to type 'import(\"buffer\").Blob'.\nThe types returned by 'stream()' are incompatible between these types.\nType 'ReadableStream>' is missing the following properties from type 'ReadableStream': blob, text, bytes, json, and 2 more.", + "Type 'Blob' is not assignable to type 'import(\"buffer\").Blob'.\nThe types returned by 'stream()' are incompatible between these types.\nType 'ReadableStream>' is missing the following properties from type 'ReadableStream': blob, text, bytes, json", }, { code: 2769, diff --git a/test/integration/bun-types/fixture/globals.ts b/test/integration/bun-types/fixture/globals.ts index 148ef0e534..0ca54f0153 100644 --- a/test/integration/bun-types/fixture/globals.ts +++ b/test/integration/bun-types/fixture/globals.ts @@ -335,3 +335,8 @@ new Error("asdf", { new Error("asdf", { cause: new Error("asdf"), }); + +// @ts-expect-error this interface is defined top level in globals.d.ts so we +// are making sure that .d.ts is a module and that anything top level doesn't +// leak to userland +expectType(); diff --git a/test/integration/bun-types/fixture/serve-types.test.ts b/test/integration/bun-types/fixture/serve-types.test.ts index 1f71f0434d..90af82d392 100644 --- a/test/integration/bun-types/fixture/serve-types.test.ts +++ b/test/integration/bun-types/fixture/serve-types.test.ts @@ -20,13 +20,18 @@ export default { expectType(ws.data).is<{ name: string }>(); }, }, -} satisfies Bun.ServeOptions<{ name: string }>; + routes: { + "/": req => { + expectType(req.params).is>(); + }, + }, +} satisfies Bun.Serve.Options<{ name: string }>; function expectInstanceOf(value: unknown, constructor: new (...args: any[]) => T): asserts value is T { expect(value).toBeInstanceOf(constructor); } -function test( +function test( name: string, options: Bun.Serve.Options, { @@ -71,6 +76,11 @@ function test( } test("basic", { + routes: { + "/123": { + "GET": new Response("Cool/great"), + }, + }, fetch(req) { console.log(req.url); // => http://localhost:3000/ return new Response("Hello World"); @@ -459,15 +469,15 @@ test("very basic fetch with websocket message handler", { fetch: () => new Response("ok"), websocket: { message: ws => { - // + expectType(ws).is>(); }, }, }); test("yet another basic fetch and websocket message handler", { websocket: { - message: () => { - // + message: ws => { + expectType(ws).is>(); }, }, fetch: (req, server) => { @@ -481,8 +491,8 @@ test("yet another basic fetch and websocket message handler", { test("websocket + upgrade on a route path", { websocket: { - message: () => { - // + message: ws => { + expectType(ws).is>(); }, }, routes: { @@ -563,7 +573,7 @@ test( if (Math.random() > 0.5) return undefined; return new Response(); }, - websocket: { message() {} }, + websocket: { message: ws => expectType(ws).is>() }, }, { overrideExpectBehavior: server => { @@ -816,3 +826,44 @@ test("multiple properties combined", { return new Response(`Combined server error: ${error.message}`, { status: 500 }); }, }); + +test("#24819 regression", { + development: !process.env.production, + routes: { + "/health": { + GET: new Response("OK"), + POST: req => { + expectType(req).is>(); + return Response.json("Sup"); + }, + }, + }, +}); + +// @ts-expect-error +test("#24819 regression with no response requires websocket", { + development: !process.env.production, + routes: { + "/health": { + GET: new Response("OK"), + POST: req => { + expectType(req).is>(); + }, + }, + }, +}); + +test("#24819 regression with websocket is happy", { + websocket: { + message: console.log, + }, + development: !process.env.production, + routes: { + "/health": { + GET: new Response("OK"), + POST: req => { + expectType(req).is>(); + }, + }, + }, +}); diff --git a/test/integration/bun-types/fixture/streams.ts b/test/integration/bun-types/fixture/streams.ts index d4989ecc5e..ad64910184 100644 --- a/test/integration/bun-types/fixture/streams.ts +++ b/test/integration/bun-types/fixture/streams.ts @@ -69,3 +69,15 @@ Bun.file("./foo.csv") }, }), ); + +// @ts-expect-error These properties do not exist right now +expectType(new ReadableStream().arrayBuffer()); +// @ts-expect-error These properties do not exist right now +expectType(new ReadableStream().formData()); + +expectType(new Blob([]).text()).is>(); +expectType(new Blob([]).arrayBuffer()).is>(); +expectType(new Blob([]).bytes()).is>>(); +expectType(new Blob([]).json()).is>(); +expectType(new Blob([]).formData()).is>(); +expectType(new Blob([]).stream()).is>>(); From 595ad7de93004f8c8661741ef2a7cdbab1f886c9 Mon Sep 17 00:00:00 2001 From: Conner Phillippi <98125604+connerlphillippi@users.noreply.github.com> Date: Wed, 19 Nov 2025 23:37:55 -0800 Subject: [PATCH 09/20] Add .aikido configuration file to exclude test and scripts directories (#24877) --- .aikido | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .aikido diff --git a/.aikido b/.aikido new file mode 100644 index 0000000000..0af82c86f5 --- /dev/null +++ b/.aikido @@ -0,0 +1,4 @@ +exclude: + paths: + - test + - scripts \ No newline at end of file From 28b950e2b062485e1272f06f90d799662daeb485 Mon Sep 17 00:00:00 2001 From: Conner Phillippi <98125604+connerlphillippi@users.noreply.github.com> Date: Wed, 19 Nov 2025 23:46:30 -0800 Subject: [PATCH 10/20] Add 'bench' to excluded paths in .aikido (#24879) ### What does this PR do? ### How did you verify your code works? --- .aikido | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.aikido b/.aikido index 0af82c86f5..2afa8b44bd 100644 --- a/.aikido +++ b/.aikido @@ -1,4 +1,5 @@ exclude: paths: - test - - scripts \ No newline at end of file + - scripts + - bench From a4af0aa4a84948108b7f26da4ad0496f46d8af9c Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 20 Nov 2025 15:36:02 -0800 Subject: [PATCH 11/20] Rename test-http-should-not-emit-or-throw error-when-writing-after-socket.end.ts to test-http-should-not-emit-or-throw-error-when-writing-after-socket.end.ts --- ...ould-not-emit-or-throw-error-when-writing-after-socket.end.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/js/bun/test/parallel/{test-http-should-not-emit-or-throw error-when-writing-after-socket.end.ts => test-http-should-not-emit-or-throw-error-when-writing-after-socket.end.ts} (100%) diff --git a/test/js/bun/test/parallel/test-http-should-not-emit-or-throw error-when-writing-after-socket.end.ts b/test/js/bun/test/parallel/test-http-should-not-emit-or-throw-error-when-writing-after-socket.end.ts similarity index 100% rename from test/js/bun/test/parallel/test-http-should-not-emit-or-throw error-when-writing-after-socket.end.ts rename to test/js/bun/test/parallel/test-http-should-not-emit-or-throw-error-when-writing-after-socket.end.ts From b92d2edcffd678c91996e1f1833499446ef9a83f Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 20 Nov 2025 15:36:31 -0800 Subject: [PATCH 12/20] Rename test-http-chunked-encoding-must be-valid-after-without-flushHeaders.ts to test-http-chunked-encoding-must-be-valid-after-without-flushHeaders.ts --- ...-chunked-encoding-must-be-valid-after-without-flushHeaders.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/js/bun/test/parallel/{test-http-chunked-encoding-must be-valid-after-without-flushHeaders.ts => test-http-chunked-encoding-must-be-valid-after-without-flushHeaders.ts} (100%) diff --git a/test/js/bun/test/parallel/test-http-chunked-encoding-must be-valid-after-without-flushHeaders.ts b/test/js/bun/test/parallel/test-http-chunked-encoding-must-be-valid-after-without-flushHeaders.ts similarity index 100% rename from test/js/bun/test/parallel/test-http-chunked-encoding-must be-valid-after-without-flushHeaders.ts rename to test/js/bun/test/parallel/test-http-chunked-encoding-must-be-valid-after-without-flushHeaders.ts From b72ba3144140d719a94edf8d874815d68cdf511b Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Thu, 20 Nov 2025 17:01:24 -0800 Subject: [PATCH 13/20] fix(Blob.prototype.stream): handle undefined chunkSize (#24900) ### What does this PR do? `blob.stream(undefined)` ### How did you verify your code works? Added a test --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/bun.js/webcore/Blob.zig | 9 ++++----- test/internal/ban-limits.json | 2 +- test/js/web/streams/streams.test.js | 8 ++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/bun.js/webcore/Blob.zig b/src/bun.js/webcore/Blob.zig index ffaa35af51..90d6b452ea 100644 --- a/src/bun.js/webcore/Blob.zig +++ b/src/bun.js/webcore/Blob.zig @@ -1980,14 +1980,13 @@ pub fn getStream( return cached; } var recommended_chunk_size: SizeType = 0; - var arguments_ = callframe.arguments_old(2); - var arguments = arguments_.ptr[0..arguments_.len]; - if (arguments.len > 0) { - if (!arguments[0].isNumber() and !arguments[0].isUndefinedOrNull()) { + const recommended_chunk_size_value = callframe.argument(0); + if (!recommended_chunk_size_value.isUndefinedOrNull()) { + if (!recommended_chunk_size_value.isNumber()) { return globalThis.throwInvalidArguments("chunkSize must be a number", .{}); } - recommended_chunk_size = @as(SizeType, @intCast(@max(0, @as(i52, @truncate(arguments[0].toInt64()))))); + recommended_chunk_size = @intCast(@max(0, @as(i52, @truncate(recommended_chunk_size_value.toInt64())))); } const stream = try jsc.WebCore.ReadableStream.fromBlobCopyRef( globalThis, diff --git a/test/internal/ban-limits.json b/test/internal/ban-limits.json index 699a509b0d..82ae899b8b 100644 --- a/test/internal/ban-limits.json +++ b/test/internal/ban-limits.json @@ -4,7 +4,7 @@ " catch bun.outOfMemory()": 0, "!= alloc.ptr": 0, "!= allocator.ptr": 0, - ".arguments_old(": 265, + ".arguments_old(": 264, ".jsBoolean(false)": 0, ".jsBoolean(true)": 0, ".stdDir()": 42, diff --git a/test/js/web/streams/streams.test.js b/test/js/web/streams/streams.test.js index 2d03f58f87..d1c5bd8b9c 100644 --- a/test/js/web/streams/streams.test.js +++ b/test/js/web/streams/streams.test.js @@ -1059,6 +1059,14 @@ it("new Response(stream).blob() (direct)", async () => { expect(await blob.text()).toBe('{"hello":true}'); }); +it("Blob.stream(undefined) does not crash", () => { + var blob = new Blob(["abdefgh"]); + var stream = blob.stream(undefined); + expect(stream instanceof ReadableStream).toBeTrue(); + stream = blob.stream(null); + expect(stream instanceof ReadableStream).toBeTrue(); +}); + it("Blob.stream() -> new Response(stream).text()", async () => { var blob = new Blob(["abdefgh"]); var stream = blob.stream(); From 5702b39ef1a90969dcb6aad87d799d67973e4911 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 20 Nov 2025 17:14:37 -0800 Subject: [PATCH 14/20] runtime: implement CompressionStream/DecompressionStream (#24757) Closes https://github.com/oven-sh/bun/issues/1723 Closes https://github.com/oven-sh/bun/pull/22214 Closes https://github.com/oven-sh/bun/pull/24241 also supports the `"brotli"` and `"zstd"` formats image --- bench/runner.mjs | 5 +- bench/snippets/compression-streams.mjs | 156 ++++++++++++++++++ docs/runtime/nodejs-compat.mdx | 4 +- src/bun.js/bindings/JSCompressionStream.cpp | 139 ++++++++++++++++ src/bun.js/bindings/JSCompressionStream.h | 40 +++++ src/bun.js/bindings/JSDecompressionStream.cpp | 139 ++++++++++++++++ src/bun.js/bindings/JSDecompressionStream.h | 40 +++++ src/bun.js/bindings/ZigGlobalObject.cpp | 4 + src/bun.js/bindings/ZigGlobalObject.lut.txt | 6 +- src/bun.js/bindings/js_classes.ts | 2 + .../bindings/webcore/DOMClientIsoSubspaces.h | 2 + src/bun.js/bindings/webcore/DOMConstructors.h | 4 +- src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 2 + src/codegen/bundle-modules.ts | 2 +- src/js/builtins/CompressionStream.ts | 33 ++++ src/js/builtins/DecompressionStream.ts | 33 ++++ src/js/node/stream.web.ts | 4 +- test/integration/bun-types/fixture/streams.ts | 2 + .../test/parallel/test-global-webstreams.js | 24 +++ .../test-whatwg-webstreams-compression.js | 70 ++++++++ 20 files changed, 699 insertions(+), 12 deletions(-) create mode 100644 bench/snippets/compression-streams.mjs create mode 100644 src/bun.js/bindings/JSCompressionStream.cpp create mode 100644 src/bun.js/bindings/JSCompressionStream.h create mode 100644 src/bun.js/bindings/JSDecompressionStream.cpp create mode 100644 src/bun.js/bindings/JSDecompressionStream.h create mode 100644 src/js/builtins/CompressionStream.ts create mode 100644 src/js/builtins/DecompressionStream.ts create mode 100644 test/js/node/test/parallel/test-global-webstreams.js create mode 100644 test/js/node/test/parallel/test-whatwg-webstreams-compression.js diff --git a/bench/runner.mjs b/bench/runner.mjs index 9f6bcee16f..b9715232f0 100644 --- a/bench/runner.mjs +++ b/bench/runner.mjs @@ -13,7 +13,4 @@ export function run(opts = {}) { } export const bench = Mitata.bench; - -export function group(_name, fn) { - return Mitata.group(fn); -} +export const group = Mitata.group; diff --git a/bench/snippets/compression-streams.mjs b/bench/snippets/compression-streams.mjs new file mode 100644 index 0000000000..b8f3d34cd5 --- /dev/null +++ b/bench/snippets/compression-streams.mjs @@ -0,0 +1,156 @@ +import { bench, group, run } from "../runner.mjs"; + +const runAll = !process.argv.includes("--simple"); + +const small = new Uint8Array(1024); +const medium = new Uint8Array(1024 * 100); +const large = new Uint8Array(1024 * 1024); + +for (let i = 0; i < large.length; i++) { + const value = Math.floor(Math.sin(i / 100) * 128 + 128); + if (i < small.length) small[i] = value; + if (i < medium.length) medium[i] = value; + large[i] = value; +} + +const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" }); + +async function compress(data, format) { + const cs = new CompressionStream(format); + const writer = cs.writable.getWriter(); + const reader = cs.readable.getReader(); + + writer.write(data); + writer.close(); + + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + chunks.push(value); + } + + const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let offset = 0; + for (const chunk of chunks) { + result.set(chunk, offset); + offset += chunk.length; + } + return result; +} + +async function decompress(data, format) { + const ds = new DecompressionStream(format); + const writer = ds.writable.getWriter(); + const reader = ds.readable.getReader(); + + writer.write(data); + writer.close(); + + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + chunks.push(value); + } + + const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0)); + let offset = 0; + for (const chunk of chunks) { + result.set(chunk, offset); + offset += chunk.length; + } + return result; +} + +async function roundTrip(data, format) { + const compressed = await compress(data, format); + return await decompress(compressed, format); +} + +const formats = ["deflate", "gzip", "deflate-raw"]; +if (runAll) formats.push("brotli", "zstd"); + +// Small data benchmarks (1KB) +group(`CompressionStream ${format.format(small.length)}`, () => { + for (const fmt of formats) { + try { + new CompressionStream(fmt); + bench(fmt, async () => await compress(small, fmt)); + } catch (e) { + // Skip unsupported formats + } + } +}); + +// Medium data benchmarks (100KB) +group(`CompressionStream ${format.format(medium.length)}`, () => { + for (const fmt of formats) { + try { + new CompressionStream(fmt); + bench(fmt, async () => await compress(medium, fmt)); + } catch (e) {} + } +}); + +// Large data benchmarks (1MB) +group(`CompressionStream ${format.format(large.length)}`, () => { + for (const fmt of formats) { + try { + new CompressionStream(fmt); + bench(fmt, async () => await compress(large, fmt)); + } catch (e) { + // Skip unsupported formats + } + } +}); + +const compressedData = {}; +for (const fmt of formats) { + try { + compressedData[fmt] = { + small: await compress(small, fmt), + medium: await compress(medium, fmt), + large: await compress(large, fmt), + }; + } catch (e) { + // Skip unsupported formats + } +} + +group(`DecompressionStream ${format.format(small.length)}`, () => { + for (const fmt of formats) { + if (compressedData[fmt]) { + bench(fmt, async () => await decompress(compressedData[fmt].small, fmt)); + } + } +}); + +group(`DecompressionStream ${format.format(medium.length)}`, () => { + for (const fmt of formats) { + if (compressedData[fmt]) { + bench(fmt, async () => await decompress(compressedData[fmt].medium, fmt)); + } + } +}); + +group(`DecompressionStream ${format.format(large.length)}`, () => { + for (const fmt of formats) { + if (compressedData[fmt]) { + bench(fmt, async () => await decompress(compressedData[fmt].large, fmt)); + } + } +}); + +group(`roundtrip ${format.format(large.length)}`, () => { + for (const fmt of formats) { + try { + new CompressionStream(fmt); + bench(fmt, async () => await roundTrip(large, fmt)); + } catch (e) { + // Skip unsupported formats + } + } +}); + +await run(); diff --git a/docs/runtime/nodejs-compat.mdx b/docs/runtime/nodejs-compat.mdx index ff5b489d99..0a382f97bd 100644 --- a/docs/runtime/nodejs-compat.mdx +++ b/docs/runtime/nodejs-compat.mdx @@ -245,7 +245,7 @@ The table below lists all globals implemented by Node.js and Bun's current compa ### [`CompressionStream`](https://developer.mozilla.org/en-US/docs/Web/API/CompressionStream) -🔴 Not implemented. +🟢 Fully implemented. ### [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) @@ -273,7 +273,7 @@ The table below lists all globals implemented by Node.js and Bun's current compa ### [`DecompressionStream`](https://developer.mozilla.org/en-US/docs/Web/API/DecompressionStream) -🔴 Not implemented. +🟢 Fully implemented. ### [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) diff --git a/src/bun.js/bindings/JSCompressionStream.cpp b/src/bun.js/bindings/JSCompressionStream.cpp new file mode 100644 index 0000000000..5fa7be0e09 --- /dev/null +++ b/src/bun.js/bindings/JSCompressionStream.cpp @@ -0,0 +1,139 @@ +#include "config.h" +#include "JSCompressionStream.h" + +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMGlobalObjectInlines.h" +#include "WebCoreJSClientData.h" +#include "WebCoreJSBuiltins.h" +#include +#include + +namespace WebCore { + +using namespace JSC; + +static JSC_DECLARE_CUSTOM_GETTER(jsCompressionStreamConstructor); + +class JSCompressionStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSCompressionStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSCompressionStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSCompressionStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSCompressionStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSCompressionStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSCompressionStreamPrototype, JSCompressionStreamPrototype::Base); + +using JSCompressionStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSCompressionStreamDOMConstructor::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStreamDOMConstructor) }; + +template<> JSValue JSCompressionStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSCompressionStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "CompressionStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSCompressionStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSCompressionStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return compressionStreamInitializeCompressionStreamCodeGenerator(vm); +} + +static const HashTableValue JSCompressionStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsCompressionStreamConstructor, 0 } }, + { "readable"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, compressionStreamReadableCodeGenerator, 0 } }, + { "writable"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, compressionStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSCompressionStreamPrototype::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStreamPrototype) }; + +void JSCompressionStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSCompressionStream::info(), JSCompressionStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSCompressionStream::s_info = { "CompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCompressionStream) }; + +JSC::GCClient::IsoSubspace* JSCompressionStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForCompressionStream.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForCompressionStream = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForCompressionStream.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForCompressionStream = std::forward(space); }); +} + +JSCompressionStream::JSCompressionStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +void JSCompressionStream::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSObject* JSCompressionStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSCompressionStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSCompressionStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSCompressionStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSCompressionStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsCompressionStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (!prototype) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSCompressionStream::getConstructor(vm, lexicalGlobalObject)); +} + +} // namespace WebCore diff --git a/src/bun.js/bindings/JSCompressionStream.h b/src/bun.js/bindings/JSCompressionStream.h new file mode 100644 index 0000000000..99833ab99d --- /dev/null +++ b/src/bun.js/bindings/JSCompressionStream.h @@ -0,0 +1,40 @@ +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSCompressionStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSCompressionStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + JSCompressionStream* ptr = new (NotNull, JSC::allocateCell(globalObject->vm())) JSCompressionStream(structure, *globalObject); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + + JSCompressionStream(JSC::Structure*, JSDOMGlobalObject&); + void finishCreation(JSC::VM&); +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/JSDecompressionStream.cpp b/src/bun.js/bindings/JSDecompressionStream.cpp new file mode 100644 index 0000000000..160e01eb47 --- /dev/null +++ b/src/bun.js/bindings/JSDecompressionStream.cpp @@ -0,0 +1,139 @@ +#include "config.h" +#include "JSDecompressionStream.h" + +#include "JSDOMBuiltinConstructor.h" +#include "JSDOMGlobalObjectInlines.h" +#include "WebCoreJSClientData.h" +#include "WebCoreJSBuiltins.h" +#include +#include + +namespace WebCore { + +using namespace JSC; + +static JSC_DECLARE_CUSTOM_GETTER(jsDecompressionStreamConstructor); + +class JSDecompressionStreamPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static JSDecompressionStreamPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure) + { + JSDecompressionStreamPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSDecompressionStreamPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDecompressionStreamPrototype, Base); + return &vm.plainObjectSpace(); + } + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + +private: + JSDecompressionStreamPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure) + : JSC::JSNonFinalObject(vm, structure) + { + } + + void finishCreation(JSC::VM&); +}; +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSDecompressionStreamPrototype, JSDecompressionStreamPrototype::Base); + +using JSDecompressionStreamDOMConstructor = JSDOMBuiltinConstructor; + +template<> const ClassInfo JSDecompressionStreamDOMConstructor::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStreamDOMConstructor) }; + +template<> JSValue JSDecompressionStreamDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject) +{ + UNUSED_PARAM(vm); + return globalObject.functionPrototype(); +} + +template<> void JSDecompressionStreamDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject) +{ + putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + JSString* nameString = jsNontrivialString(vm, "DecompressionStream"_s); + m_originalName.set(vm, this, nameString); + putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + putDirect(vm, vm.propertyNames->prototype, JSDecompressionStream::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +template<> FunctionExecutable* JSDecompressionStreamDOMConstructor::initializeExecutable(VM& vm) +{ + return decompressionStreamInitializeDecompressionStreamCodeGenerator(vm); +} + +static const HashTableValue JSDecompressionStreamPrototypeTableValues[] = { + { "constructor"_s, static_cast(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsDecompressionStreamConstructor, 0 } }, + { "readable"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, decompressionStreamReadableCodeGenerator, 0 } }, + { "writable"_s, static_cast(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinAccessorType, decompressionStreamWritableCodeGenerator, 0 } }, +}; + +const ClassInfo JSDecompressionStreamPrototype::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStreamPrototype) }; + +void JSDecompressionStreamPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSDecompressionStream::info(), JSDecompressionStreamPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +const ClassInfo JSDecompressionStream::s_info = { "DecompressionStream"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDecompressionStream) }; + +JSC::GCClient::IsoSubspace* JSDecompressionStream::subspaceForImpl(JSC::VM& vm) +{ + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForDecompressionStream.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForDecompressionStream = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForDecompressionStream.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForDecompressionStream = std::forward(space); }); +} + +JSDecompressionStream::JSDecompressionStream(Structure* structure, JSDOMGlobalObject& globalObject) + : JSDOMObject(structure, globalObject) +{ +} + +void JSDecompressionStream::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSObject* JSDecompressionStream::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + auto* structure = JSDecompressionStreamPrototype::createStructure(vm, &globalObject, globalObject.objectPrototype()); + structure->setMayBePrototype(true); + return JSDecompressionStreamPrototype::create(vm, &globalObject, structure); +} + +JSObject* JSDecompressionStream::prototype(VM& vm, JSDOMGlobalObject& globalObject) +{ + return getDOMPrototype(vm, globalObject); +} + +JSValue JSDecompressionStream::getConstructor(VM& vm, const JSGlobalObject* globalObject) +{ + return getDOMConstructor(vm, *jsCast(globalObject)); +} + +JSC_DEFINE_CUSTOM_GETTER(jsDecompressionStreamConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); + if (!prototype) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(JSDecompressionStream::getConstructor(vm, lexicalGlobalObject)); +} + +} // namespace WebCore diff --git a/src/bun.js/bindings/JSDecompressionStream.h b/src/bun.js/bindings/JSDecompressionStream.h new file mode 100644 index 0000000000..b35119606a --- /dev/null +++ b/src/bun.js/bindings/JSDecompressionStream.h @@ -0,0 +1,40 @@ +#pragma once + +#include "JSDOMWrapper.h" + +namespace WebCore { + +class JSDecompressionStream : public JSDOMObject { +public: + using Base = JSDOMObject; + static JSDecompressionStream* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject) + { + JSDecompressionStream* ptr = new (NotNull, JSC::allocateCell(globalObject->vm())) JSDecompressionStream(structure, *globalObject); + ptr->finishCreation(globalObject->vm()); + return ptr; + } + + static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&); + static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*); + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return subspaceForImpl(vm); + } + static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); + + JSDecompressionStream(JSC::Structure*, JSDOMGlobalObject&); + void finishCreation(JSC::VM&); +}; + +} // namespace WebCore diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index caf3c26aa2..7e22705369 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -78,6 +78,8 @@ #include "JSAbortAlgorithm.h" #include "JSAbortController.h" #include "JSAbortSignal.h" +#include "JSCompressionStream.h" +#include "JSDecompressionStream.h" #include "JSBroadcastChannel.h" #include "JSBuffer.h" #include "JSBufferList.h" @@ -962,9 +964,11 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(AbortSignal); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(BroadcastChannel); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ByteLengthQueuingStrategy) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CloseEvent); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CompressionStream); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CountQueuingStrategy) WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CryptoKey); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(CustomEvent); +WEBCORE_GENERATED_CONSTRUCTOR_GETTER(DecompressionStream); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(DOMException); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(DOMFormData); WEBCORE_GENERATED_CONSTRUCTOR_GETTER(DOMURL); diff --git a/src/bun.js/bindings/ZigGlobalObject.lut.txt b/src/bun.js/bindings/ZigGlobalObject.lut.txt index 49b22f1412..9fbf2e00f0 100644 --- a/src/bun.js/bindings/ZigGlobalObject.lut.txt +++ b/src/bun.js/bindings/ZigGlobalObject.lut.txt @@ -21,9 +21,9 @@ setInterval functionSetInterval Function 1 setTimeout functionSetTimeout Function 1 structuredClone WebCore::jsFunctionStructuredClone Function 2 - + global GlobalObject_getGlobalThis PropertyCallback - + Bun GlobalObject::m_bunObject CellProperty|DontDelete|ReadOnly File GlobalObject::m_JSDOMFileConstructor CellProperty crypto GlobalObject::m_cryptoObject CellProperty @@ -48,9 +48,11 @@ BroadcastChannel BroadcastChannelConstructorCallback PropertyCallback ByteLengthQueuingStrategy ByteLengthQueuingStrategyConstructorCallback PropertyCallback CloseEvent CloseEventConstructorCallback PropertyCallback + CompressionStream CompressionStreamConstructorCallback PropertyCallback CountQueuingStrategy CountQueuingStrategyConstructorCallback PropertyCallback CryptoKey CryptoKeyConstructorCallback PropertyCallback CustomEvent CustomEventConstructorCallback PropertyCallback + DecompressionStream DecompressionStreamConstructorCallback PropertyCallback DOMException DOMExceptionConstructorCallback PropertyCallback ErrorEvent ErrorEventConstructorCallback PropertyCallback Event EventConstructorCallback PropertyCallback diff --git a/src/bun.js/bindings/js_classes.ts b/src/bun.js/bindings/js_classes.ts index 564fbb39bc..989f69cbb4 100644 --- a/src/bun.js/bindings/js_classes.ts +++ b/src/bun.js/bindings/js_classes.ts @@ -8,4 +8,6 @@ export default [ ["WritableStream", "JSWritableStream.h"], ["TransformStream", "JSTransformStream.h"], ["ArrayBuffer"], + ["CompressionStream", "JSCompressionStream.h"], + ["DecompressionStream", "JSDecompressionStream.h"], ]; diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index ce662148a5..b19ae4faa9 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -283,6 +283,8 @@ public: std::unique_ptr m_clientSubspaceForReadableStreamSource; std::unique_ptr m_clientSubspaceForTransformStream; std::unique_ptr m_clientSubspaceForTransformStreamDefaultController; + std::unique_ptr m_clientSubspaceForCompressionStream; + std::unique_ptr m_clientSubspaceForDecompressionStream; std::unique_ptr m_clientSubspaceForWritableStream; std::unique_ptr m_clientSubspaceForWritableStreamDefaultController; std::unique_ptr m_clientSubspaceForWritableStreamDefaultWriter; diff --git a/src/bun.js/bindings/webcore/DOMConstructors.h b/src/bun.js/bindings/webcore/DOMConstructors.h index d83ef6cfe3..78b38e131a 100644 --- a/src/bun.js/bindings/webcore/DOMConstructors.h +++ b/src/bun.js/bindings/webcore/DOMConstructors.h @@ -198,6 +198,8 @@ enum class DOMConstructorID : uint16_t { ReadableStreamSource, TransformStream, TransformStreamDefaultController, + CompressionStream, + DecompressionStream, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, @@ -860,7 +862,7 @@ enum class DOMConstructorID : uint16_t { EventEmitter, }; -static constexpr unsigned numberOfDOMConstructorsBase = 846; +static constexpr unsigned numberOfDOMConstructorsBase = 848; static constexpr unsigned bunExtraConstructors = 3; diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index cce908c751..049b161b8f 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -266,6 +266,8 @@ public: std::unique_ptr m_subspaceForReadableStreamSource; std::unique_ptr m_subspaceForTransformStream; std::unique_ptr m_subspaceForTransformStreamDefaultController; + std::unique_ptr m_subspaceForCompressionStream; + std::unique_ptr m_subspaceForDecompressionStream; std::unique_ptr m_subspaceForWritableStream; std::unique_ptr m_subspaceForWritableStreamDefaultController; std::unique_ptr m_subspaceForWritableStreamDefaultWriter; diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index f539171ef6..c282da4560 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -532,7 +532,7 @@ declare module "module" { `; for (const [name] of jsclasses) { - dts += `\ndeclare function $inherits${name}(value: any): boolean;`; + dts += `\ndeclare function $inherits${name}(value: any): value is ${name};`; } return dts; diff --git a/src/js/builtins/CompressionStream.ts b/src/js/builtins/CompressionStream.ts new file mode 100644 index 0000000000..a777b35a53 --- /dev/null +++ b/src/js/builtins/CompressionStream.ts @@ -0,0 +1,33 @@ +export function initializeCompressionStream(this, format) { + const zlib = require("node:zlib"); + const stream = require("node:stream"); + + const builders = { + "deflate": zlib.createDeflate, + "deflate-raw": zlib.createDeflateRaw, + "gzip": zlib.createGzip, + "brotli": zlib.createBrotliCompress, + "zstd": zlib.createZstdCompress, + }; + + if (!(format in builders)) + throw $ERR_INVALID_ARG_VALUE("format", format, "must be one of: " + Object.keys(builders).join(", ")); + + const handle = builders[format](); + $putByIdDirectPrivate(this, "readable", stream.Readable.toWeb(handle)); + $putByIdDirectPrivate(this, "writable", stream.Writable.toWeb(handle)); + + return this; +} + +$getter; +export function readable(this) { + if (!$inheritsCompressionStream(this)) throw $makeGetterTypeError("CompressionStream", "readable"); + return $getByIdDirectPrivate(this, "readable"); +} + +$getter; +export function writable(this) { + if (!$inheritsCompressionStream(this)) throw $makeGetterTypeError("CompressionStream", "writable"); + return $getByIdDirectPrivate(this, "writable"); +} diff --git a/src/js/builtins/DecompressionStream.ts b/src/js/builtins/DecompressionStream.ts new file mode 100644 index 0000000000..bf608d03fd --- /dev/null +++ b/src/js/builtins/DecompressionStream.ts @@ -0,0 +1,33 @@ +export function initializeDecompressionStream(this, format) { + const zlib = require("node:zlib"); + const stream = require("node:stream"); + + const builders = { + "deflate": zlib.createInflate, + "deflate-raw": zlib.createInflateRaw, + "gzip": zlib.createGunzip, + "brotli": zlib.createBrotliDecompress, + "zstd": zlib.createZstdDecompress, + }; + + if (!(format in builders)) + throw $ERR_INVALID_ARG_VALUE("format", format, "must be one of: " + Object.keys(builders).join(", ")); + + const handle = builders[format](); + $putByIdDirectPrivate(this, "readable", stream.Readable.toWeb(handle)); + $putByIdDirectPrivate(this, "writable", stream.Writable.toWeb(handle)); + + return this; +} + +$getter; +export function readable(this) { + if (!$inheritsDecompressionStream(this)) throw $makeGetterTypeError("DecompressionStream", "readable"); + return $getByIdDirectPrivate(this, "readable"); +} + +$getter; +export function writable(this) { + if (!$inheritsDecompressionStream(this)) throw $makeGetterTypeError("DecompressionStream", "writable"); + return $getByIdDirectPrivate(this, "writable"); +} diff --git a/src/js/node/stream.web.ts b/src/js/node/stream.web.ts index a0d92215bd..8de228b824 100644 --- a/src/js/node/stream.web.ts +++ b/src/js/node/stream.web.ts @@ -15,6 +15,6 @@ export default { CountQueuingStrategy, TextEncoderStream, TextDecoderStream, - CompressionStream: undefined, - DecompressionStream: undefined, + CompressionStream, + DecompressionStream, }; diff --git a/test/integration/bun-types/fixture/streams.ts b/test/integration/bun-types/fixture/streams.ts index ad64910184..f53338482a 100644 --- a/test/integration/bun-types/fixture/streams.ts +++ b/test/integration/bun-types/fixture/streams.ts @@ -59,6 +59,8 @@ expectType(node_stream.blob()).is>(); Bun.file("./foo.csv").stream().pipeThrough(new TextDecoderStream()).pipeThrough(new TextEncoderStream()); +Bun.file("./foo.csv").stream().pipeThrough(new CompressionStream("gzip")).pipeThrough(new DecompressionStream("gzip")); + Bun.file("./foo.csv") .stream() .pipeThrough(new TextDecoderStream()) diff --git a/test/js/node/test/parallel/test-global-webstreams.js b/test/js/node/test/parallel/test-global-webstreams.js new file mode 100644 index 0000000000..ab20e376b7 --- /dev/null +++ b/test/js/node/test/parallel/test-global-webstreams.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const webstreams = require('stream/web'); + +assert.strictEqual(ReadableStream, webstreams.ReadableStream); +assert.strictEqual(ReadableStreamDefaultReader, webstreams.ReadableStreamDefaultReader); +assert.strictEqual(ReadableStreamBYOBReader, webstreams.ReadableStreamBYOBReader); +assert.strictEqual(ReadableStreamBYOBRequest, webstreams.ReadableStreamBYOBRequest); +assert.strictEqual(ReadableByteStreamController, webstreams.ReadableByteStreamController); +assert.strictEqual(ReadableStreamDefaultController, webstreams.ReadableStreamDefaultController); +assert.strictEqual(TransformStream, webstreams.TransformStream); +assert.strictEqual(TransformStreamDefaultController, webstreams.TransformStreamDefaultController); +assert.strictEqual(WritableStream, webstreams.WritableStream); +assert.strictEqual(WritableStreamDefaultWriter, webstreams.WritableStreamDefaultWriter); +assert.strictEqual(WritableStreamDefaultController, webstreams.WritableStreamDefaultController); +assert.strictEqual(ByteLengthQueuingStrategy, webstreams.ByteLengthQueuingStrategy); +assert.strictEqual(CountQueuingStrategy, webstreams.CountQueuingStrategy); +assert.strictEqual(TextEncoderStream, webstreams.TextEncoderStream); +assert.strictEqual(TextDecoderStream, webstreams.TextDecoderStream); +assert.strictEqual(CompressionStream, webstreams.CompressionStream); +assert.strictEqual(DecompressionStream, webstreams.DecompressionStream); diff --git a/test/js/node/test/parallel/test-whatwg-webstreams-compression.js b/test/js/node/test/parallel/test-whatwg-webstreams-compression.js new file mode 100644 index 0000000000..a6f2e1b425 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-webstreams-compression.js @@ -0,0 +1,70 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); + +const { + CompressionStream, + DecompressionStream, +} = require('stream/web'); + +const assert = require('assert'); +const dec = new TextDecoder(); + +async function test(format) { + const gzip = new CompressionStream(format); + const gunzip = new DecompressionStream(format); + + assert.strictEqual(gzip[Symbol.toStringTag], 'CompressionStream'); + assert.strictEqual(gunzip[Symbol.toStringTag], 'DecompressionStream'); + + gzip.readable.pipeTo(gunzip.writable).then(common.mustCall()); + + const reader = gunzip.readable.getReader(); + const writer = gzip.writable.getWriter(); + + const compressed_data = []; + const reader_function = ({ value, done }) => { + if (value) + compressed_data.push(value); + if (!done) + return reader.read().then(reader_function); + assert.strictEqual(dec.decode(Buffer.concat(compressed_data)), 'hello'); + }; + const reader_promise = reader.read().then(reader_function); + + await Promise.all([ + reader_promise, + reader_promise.then(() => reader.read().then(({ done }) => assert(done))), + writer.write('hello'), + writer.close(), + ]); +} + +Promise.all(['gzip', 'deflate', 'deflate-raw', 'brotli', 'zstd'].map((i) => test(i))).then(common.mustCall()); + +[1, 'hello', false, {}].forEach((i) => { + assert.throws(() => new CompressionStream(i), { + code: 'ERR_INVALID_ARG_VALUE', + }); + assert.throws(() => new DecompressionStream(i), { + code: 'ERR_INVALID_ARG_VALUE', + }); +}); + +assert.throws( + () => Reflect.get(CompressionStream.prototype, 'readable', {}), { + name: 'TypeError', + }); +assert.throws( + () => Reflect.get(CompressionStream.prototype, 'writable', {}), { + name: 'TypeError', + }); +assert.throws( + () => Reflect.get(DecompressionStream.prototype, 'readable', {}), { + name: 'TypeError', + }); +assert.throws( + () => Reflect.get(DecompressionStream.prototype, 'writable', {}), { + name: 'TypeError', + }); From a0c5edb15be6b994c8289c432cbfcc7245659e26 Mon Sep 17 00:00:00 2001 From: Conner Phillippi <98125604+connerlphillippi@users.noreply.github.com> Date: Thu, 20 Nov 2025 17:35:16 -0800 Subject: [PATCH 15/20] Add new package paths to .aikido configuration --- .aikido | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.aikido b/.aikido index 2afa8b44bd..db335af624 100644 --- a/.aikido +++ b/.aikido @@ -3,3 +3,17 @@ exclude: - test - scripts - bench + - packages/bun-lambda + - packages/bun-release + - packages/bun-wasm + - packages/bun-vscode + - packages/bun-plugin-yaml + - packages/bun-plugin-svelte + - packages/bun-native-plugin-rs + - packages/bun-native-bundler-plugin-api + - packages/bun-inspector-protocol + - packages/bun-inspector-frontend + - packages/bun-error + - packages/bun-debug-adapter-protocol + - packages/bun-build-mdx-rs + - packages/@types/bun From 274e01c737e85f8142070a9745b43a2ba09fce4c Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Thu, 20 Nov 2025 20:56:02 -0800 Subject: [PATCH 16/20] remove `jsc.createCallback` (#24910) ### What does this PR do? This was creating `Zig::FFIFunction` when we could instead use a plain `JSC::JSFunction` ### How did you verify your code works? Added a test --- src/bake/FrameworkRouter.zig | 4 +- src/bun.js/api/BunObject.zig | 8 ++-- src/bun.js/api/FFIObject.zig | 2 +- src/bun.js/api/HashObject.zig | 9 +--- src/bun.js/api/TOMLObject.zig | 7 ++-- src/bun.js/api/UnsafeObject.zig | 2 +- src/bun.js/api/YAMLObject.zig | 14 ++++--- src/bun.js/api/crypto/PasswordObject.zig | 8 ++-- src/bun.js/api/ffi.zig | 4 -- src/bun.js/bindings/JSFFIFunction.cpp | 22 +++------- src/bun.js/bindings/JSFFIFunction.h | 2 - src/bun.js/bindings/JSFunction.zig | 8 +++- src/bun.js/bindings/JSGlobalObject.zig | 11 ----- src/bun.js/bindings/ZigGlobalObject.cpp | 12 ------ src/bun.js/bindings/ZigGlobalObject.h | 4 -- src/bun.js/jsc.zig | 1 - src/bun.js/jsc/host_fn.zig | 52 +----------------------- src/bun.js/test/DoneCallback.zig | 2 +- src/bun.js/test/ScopeFunctions.zig | 8 ++-- src/bun.js/test/jest.zig | 32 +++++++-------- src/cli/upgrade_command.zig | 4 +- src/codegen/bindgen.ts | 2 +- src/crash_handler.zig | 2 +- src/csrf.zig | 4 +- src/deps/uws/UpgradedDuplex.zig | 4 -- src/install/install_binding.zig | 2 +- src/install/npm.zig | 2 +- src/semver/SemverObject.zig | 16 ++++---- test/js/bun/util/hash.test.js | 12 ++++++ 29 files changed, 88 insertions(+), 172 deletions(-) diff --git a/src/bake/FrameworkRouter.zig b/src/bake/FrameworkRouter.zig index 8824e851d7..010c60b202 100644 --- a/src/bake/FrameworkRouter.zig +++ b/src/bake/FrameworkRouter.zig @@ -1166,7 +1166,7 @@ pub const JSFrameworkRouter = struct { pub fn getBindings(global: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue { return (try jsc.JSObject.create(.{ - .parseRoutePattern = global.createHostFunction("parseRoutePattern", parseRoutePattern, 1), + .parseRoutePattern = jsc.JSFunction.create(global, "parseRoutePattern", jsc.host_fn.toJSHostFn(parseRoutePattern), 1, .{}), .FrameworkRouter = js.getConstructor(global), }, global)).toJS(); } @@ -1313,7 +1313,7 @@ pub const JSFrameworkRouter = struct { bun.destroy(this); } - pub fn parseRoutePattern(global: *JSGlobalObject, frame: *CallFrame) !JSValue { + pub fn parseRoutePattern(global: *JSGlobalObject, frame: *CallFrame) bun.JSError!JSValue { var arena = std.heap.ArenaAllocator.init(bun.default_allocator); defer arena.deinit(); const alloc = arena.allocator(); diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 8eb64a3126..e45ff6a664 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -508,10 +508,10 @@ export fn Bun__inspect_singleline(globalThis: *JSGlobalObject, value: JSValue) b } pub fn getInspect(globalObject: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue { - const fun = jsc.createCallback(globalObject, ZigString.static("inspect"), 2, inspect); + const fun = jsc.JSFunction.create(globalObject, "inspect", inspect, 2, .{}); var str = ZigString.init("nodejs.util.inspect.custom"); fun.put(globalObject, ZigString.static("custom"), jsc.JSValue.symbolFor(globalObject, &str)); - fun.put(globalObject, ZigString.static("table"), jsc.createCallback(globalObject, ZigString.static("table"), 3, inspectTable)); + fun.put(globalObject, ZigString.static("table"), jsc.JSFunction.create(globalObject, "table", inspectTable, 3, .{})); return fun; } @@ -1375,13 +1375,13 @@ const CSRFObject = struct { object.put( globalThis, ZigString.static("generate"), - jsc.createCallback(globalThis, ZigString.static("generate"), 1, @import("../../csrf.zig").csrf__generate), + jsc.JSFunction.create(globalThis, "generate", @import("../../csrf.zig").csrf__generate, 1, .{}), ); object.put( globalThis, ZigString.static("verify"), - jsc.createCallback(globalThis, ZigString.static("verify"), 1, @import("../../csrf.zig").csrf__verify), + jsc.JSFunction.create(globalThis, "verify", @import("../../csrf.zig").csrf__verify, 1, .{}), ); return object; diff --git a/src/bun.js/api/FFIObject.zig b/src/bun.js/api/FFIObject.zig index d45da78d31..f7e2b0a3b8 100644 --- a/src/bun.js/api/FFIObject.zig +++ b/src/bun.js/api/FFIObject.zig @@ -19,7 +19,7 @@ pub fn toJS(globalObject: *jsc.JSGlobalObject) jsc.JSValue { object.put( globalObject, comptime ZigString.static(field), - jsc.createCallback(globalObject, comptime ZigString.static(field), 1, comptime @field(fields, field)), + jsc.JSFunction.create(globalObject, field, @field(fields, field), 1, .{}), ); } diff --git a/src/bun.js/api/HashObject.zig b/src/bun.js/api/HashObject.zig index 3d547a1a62..05256cf581 100644 --- a/src/bun.js/api/HashObject.zig +++ b/src/bun.js/api/HashObject.zig @@ -32,7 +32,7 @@ pub const murmur64v2 = hashWrap(std.hash.murmur.Murmur2_64); pub const rapidhash = hashWrap(bun.deprecated.RapidHash); pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { - const function = jsc.createCallback(globalThis, ZigString.static("hash"), 1, wyhash); + const function = jsc.JSFunction.create(globalThis, "hash", wyhash, 1, .{}); const fns = comptime .{ "wyhash", "adler32", @@ -48,12 +48,7 @@ pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { "rapidhash", }; inline for (fns) |name| { - const value = jsc.createCallback( - globalThis, - ZigString.static(name), - 1, - @field(HashObject, name), - ); + const value = jsc.JSFunction.create(globalThis, name, @field(HashObject, name), 1, .{}); function.put(globalThis, comptime ZigString.static(name), value); } diff --git a/src/bun.js/api/TOMLObject.zig b/src/bun.js/api/TOMLObject.zig index 8b3d414d9e..419d0579de 100644 --- a/src/bun.js/api/TOMLObject.zig +++ b/src/bun.js/api/TOMLObject.zig @@ -3,11 +3,12 @@ pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { object.put( globalThis, ZigString.static("parse"), - jsc.createCallback( + jsc.JSFunction.create( globalThis, - ZigString.static("parse"), - 1, + "parse", parse, + 1, + .{}, ), ); diff --git a/src/bun.js/api/UnsafeObject.zig b/src/bun.js/api/UnsafeObject.zig index f42fcb29a2..7ab191b9c3 100644 --- a/src/bun.js/api/UnsafeObject.zig +++ b/src/bun.js/api/UnsafeObject.zig @@ -9,7 +9,7 @@ pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { object.put( globalThis, comptime ZigString.static(name), - jsc.createCallback(globalThis, comptime ZigString.static(name), 1, comptime @field(fields, name)), + jsc.JSFunction.create(globalThis, name, @field(fields, name), 1, .{}), ); } return object; diff --git a/src/bun.js/api/YAMLObject.zig b/src/bun.js/api/YAMLObject.zig index 6862e2e6dd..043352e793 100644 --- a/src/bun.js/api/YAMLObject.zig +++ b/src/bun.js/api/YAMLObject.zig @@ -3,21 +3,23 @@ pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { object.put( globalThis, ZigString.static("parse"), - jsc.createCallback( + jsc.JSFunction.create( globalThis, - ZigString.static("parse"), - 1, + "parse", parse, + 1, + .{}, ), ); object.put( globalThis, ZigString.static("stringify"), - jsc.createCallback( + jsc.JSFunction.create( globalThis, - ZigString.static("stringify"), - 3, + "stringify", stringify, + 3, + .{}, ), ); diff --git a/src/bun.js/api/crypto/PasswordObject.zig b/src/bun.js/api/crypto/PasswordObject.zig index 8f8d7a1fde..0a7753b1c9 100644 --- a/src/bun.js/api/crypto/PasswordObject.zig +++ b/src/bun.js/api/crypto/PasswordObject.zig @@ -324,10 +324,10 @@ pub const JSPasswordObject = struct { pub export fn JSPasswordObject__create(globalObject: *jsc.JSGlobalObject) jsc.JSValue { var object = JSValue.createEmptyObject(globalObject, 4); - object.put(globalObject, ZigString.static("hash"), jsc.createCallback(globalObject, ZigString.static("hash"), 2, JSPasswordObject__hash)); - object.put(globalObject, ZigString.static("hashSync"), jsc.createCallback(globalObject, ZigString.static("hashSync"), 2, JSPasswordObject__hashSync)); - object.put(globalObject, ZigString.static("verify"), jsc.createCallback(globalObject, ZigString.static("verify"), 2, JSPasswordObject__verify)); - object.put(globalObject, ZigString.static("verifySync"), jsc.createCallback(globalObject, ZigString.static("verifySync"), 2, JSPasswordObject__verifySync)); + object.put(globalObject, ZigString.static("hash"), jsc.JSFunction.create(globalObject, "hash", JSPasswordObject__hash, 2, .{})); + object.put(globalObject, ZigString.static("hashSync"), jsc.JSFunction.create(globalObject, "hashSync", JSPasswordObject__hashSync, 2, .{})); + object.put(globalObject, ZigString.static("verify"), jsc.JSFunction.create(globalObject, "verify", JSPasswordObject__verify, 2, .{})); + object.put(globalObject, ZigString.static("verifySync"), jsc.JSFunction.create(globalObject, "verifySync", JSPasswordObject__verifySync, 2, .{})); return object; } diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index d1127e4e15..94e736920b 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -780,7 +780,6 @@ pub const FFI = struct { &str, @as(u32, @intCast(function.arg_types.items.len)), bun.cast(*const jsc.JSHostFn, compiled.ptr), - false, true, function.symbol_from_dynamic_library, ); @@ -1134,7 +1133,6 @@ pub const FFI = struct { &str, @as(u32, @intCast(function.arg_types.items.len)), bun.cast(*const jsc.JSHostFn, compiled.ptr), - false, true, function.symbol_from_dynamic_library, ); @@ -1237,7 +1235,6 @@ pub const FFI = struct { name, @as(u32, @intCast(function.arg_types.items.len)), bun.cast(*jsc.JSHostFn, compiled.ptr), - false, true, function.symbol_from_dynamic_library, ); @@ -1449,7 +1446,6 @@ pub const FFI = struct { // val.allocator.free(val.step.compiled.buf); if (val.step.compiled.js_function != .zero) { _ = globalThis; - // _ = jsc.untrackFunction(globalThis, val.step.compiled.js_function); val.step.compiled.js_function = .zero; } diff --git a/src/bun.js/bindings/JSFFIFunction.cpp b/src/bun.js/bindings/JSFFIFunction.cpp index ee99be8c8e..0a66f73665 100644 --- a/src/bun.js/bindings/JSFFIFunction.cpp +++ b/src/bun.js/bindings/JSFFIFunction.cpp @@ -71,24 +71,17 @@ extern "C" FFICallbackFunctionWrapper* Bun__createFFICallbackFunction( return wrapper; } -extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunctionWithData(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong, void* data) +extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunctionWithData(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, void* data) { auto& vm = JSC::getVM(globalObject); Zig::JSFFIFunction* function = Zig::JSFFIFunction::create(vm, globalObject, argCount, symbolName != nullptr ? Zig::toStringCopy(*symbolName) : String(), functionPointer, JSC::NoIntrinsic); - if (strong) - globalObject->trackFFIFunction(function); function->dataPtr = data; return function; } -extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionWithDataValue(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong, void* data) +extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionWithDataValue(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, void* data) { - return JSC::JSValue::encode(Bun__CreateFFIFunctionWithData(globalObject, symbolName, argCount, functionPointer, strong, data)); -} - -extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunction(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong) -{ - return Bun__CreateFFIFunctionWithData(globalObject, symbolName, argCount, functionPointer, strong, nullptr); + return JSC::JSValue::encode(Bun__CreateFFIFunctionWithData(globalObject, symbolName, argCount, functionPointer, data)); } extern "C" void* Bun__FFIFunction_getDataPtr(JSC::EncodedJSValue jsValue) @@ -110,11 +103,8 @@ extern "C" void Bun__FFIFunction_setDataPtr(JSC::EncodedJSValue jsValue, void* p function->dataPtr = ptr; } -extern "C" void Bun__untrackFFIFunction(Zig::GlobalObject* globalObject, JSC::EncodedJSValue function) -{ - globalObject->untrackFFIFunction(JSC::jsCast(JSC::JSValue::decode(function))); -} -extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionValue(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong, bool addPtrField, void* symbolFromDynamicLibrary) + +extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionValue(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool addPtrField, void* symbolFromDynamicLibrary) { if (addPtrField) { auto* function = Zig::JSFFIFunction::createForFFI(globalObject->vm(), globalObject, argCount, symbolName != nullptr ? Zig::toStringCopy(*symbolName) : String(), reinterpret_cast(functionPointer)); @@ -127,7 +117,7 @@ extern "C" JSC::EncodedJSValue Bun__CreateFFIFunctionValue(Zig::GlobalObject* gl return JSC::JSValue::encode(function); } - return Bun__CreateFFIFunctionWithDataValue(globalObject, symbolName, argCount, functionPointer, strong, nullptr); + return Bun__CreateFFIFunctionWithDataValue(globalObject, symbolName, argCount, functionPointer, nullptr); } namespace Zig { diff --git a/src/bun.js/bindings/JSFFIFunction.h b/src/bun.js/bindings/JSFFIFunction.h index 4b7cc250e9..6eccae7b61 100644 --- a/src/bun.js/bindings/JSFFIFunction.h +++ b/src/bun.js/bindings/JSFFIFunction.h @@ -98,5 +98,3 @@ private: }; } // namespace JSC - -extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunction(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong); diff --git a/src/bun.js/bindings/JSFunction.zig b/src/bun.js/bindings/JSFunction.zig index 9e4ede4e1a..4cb565da6a 100644 --- a/src/bun.js/bindings/JSFunction.zig +++ b/src/bun.js/bindings/JSFunction.zig @@ -30,7 +30,7 @@ pub const JSFunction = opaque { pub fn create( global: *JSGlobalObject, fn_name: anytype, - comptime implementation: jsc.JSHostFnZig, + comptime implementation: anytype, function_length: u32, options: CreateJSFunctionOptions, ) JSValue { @@ -40,7 +40,11 @@ pub const JSFunction = opaque { bun.String => fn_name, else => bun.String.init(fn_name), }, - jsc.toJSHostFn(implementation), + switch (@TypeOf(implementation)) { + jsc.JSHostFnZig => jsc.toJSHostFn(implementation), + jsc.JSHostFn => implementation, + else => @compileError("unexpected function type"), + }, function_length, options.implementation_visibility, options.intrinsic, diff --git a/src/bun.js/bindings/JSGlobalObject.zig b/src/bun.js/bindings/JSGlobalObject.zig index e882f8de98..fa2a092018 100644 --- a/src/bun.js/bindings/JSGlobalObject.zig +++ b/src/bun.js/bindings/JSGlobalObject.zig @@ -776,17 +776,6 @@ pub const JSGlobalObject = opaque { return default; } - pub inline fn createHostFunction( - global: *JSGlobalObject, - comptime display_name: [:0]const u8, - // when querying from JavaScript, 'func.name' - comptime function: anytype, - // when querying from JavaScript, 'func.len' - comptime argument_count: u32, - ) JSValue { - return jsc.host_fn.NewRuntimeFunction(global, ZigString.static(display_name), argument_count, jsc.toJSHostFn(function), false, false, null); - } - /// Get a lazily-initialized `JSC::String` from `BunCommonStrings.h`. pub inline fn commonStrings(this: *jsc.JSGlobalObject) CommonStrings { jsc.markBinding(@src()); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 7e22705369..c2d3b64f8a 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2995,7 +2995,6 @@ void GlobalObject::visitAdditionalChildren(Visitor& visitor) thisObject->globalEventScope->visitJSEventListeners(visitor); thisObject->m_aboutToBeNotifiedRejectedPromises.visit(thisObject, visitor); - thisObject->m_ffiFunctions.visit(thisObject, visitor); ScriptExecutionContext* context = thisObject->scriptExecutionContext(); visitor.addOpaqueRoot(context); @@ -3558,17 +3557,6 @@ bool GlobalObject::hasNapiFinalizers() const void GlobalObject::setNodeWorkerEnvironmentData(JSMap* data) { m_nodeWorkerEnvironmentData.set(vm(), this, data); } -void GlobalObject::trackFFIFunction(JSC::JSFunction* function) -{ - this->m_ffiFunctions.append(vm(), this, function); -} -bool GlobalObject::untrackFFIFunction(JSC::JSFunction* function) -{ - return this->m_ffiFunctions.removeFirstMatching(this, [&](JSC::WriteBarrier& untrackedFunction) -> bool { - return untrackedFunction.get() == function; - }); -} - extern "C" void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject) { auto& vm = JSC::getVM(globalObject); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 937da8c906..ffbc145c0e 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -672,9 +672,6 @@ public: String agentClusterID() const; static String defaultAgentClusterID(); - void trackFFIFunction(JSC::JSFunction* function); - bool untrackFFIFunction(JSC::JSFunction* function); - BunPlugin::OnLoad onLoadPlugins {}; BunPlugin::OnResolve onResolvePlugins {}; @@ -734,7 +731,6 @@ private: WebCore::SubtleCrypto* m_subtleCrypto = nullptr; Bun::WriteBarrierList m_aboutToBeNotifiedRejectedPromises; - Bun::WriteBarrierList m_ffiFunctions; }; class EvalGlobalObject : public GlobalObject { diff --git a/src/bun.js/jsc.zig b/src/bun.js/jsc.zig index 61adcd7564..b4f8cc51ab 100644 --- a/src/bun.js/jsc.zig +++ b/src/bun.js/jsc.zig @@ -37,7 +37,6 @@ pub const toJSHostFnWithContext = host_fn.toJSHostFnWithContext; pub const toJSHostCall = host_fn.toJSHostCall; pub const fromJSHostCall = host_fn.fromJSHostCall; pub const fromJSHostCallGeneric = host_fn.fromJSHostCallGeneric; -pub const createCallback = host_fn.createCallback; // JSC Classes Bindings pub const AnyPromise = @import("./bindings/AnyPromise.zig").AnyPromise; diff --git a/src/bun.js/jsc/host_fn.zig b/src/bun.js/jsc/host_fn.zig index 1cc4d1b904..738080a0f5 100644 --- a/src/bun.js/jsc/host_fn.zig +++ b/src/bun.js/jsc/host_fn.zig @@ -271,72 +271,32 @@ const private = struct { ?*const ZigString, argCount: u32, function: *const JSHostFn, - strong: bool, data: *anyopaque, ) JSValue; - pub extern fn Bun__CreateFFIFunction( - globalObject: *JSGlobalObject, - symbolName: ?*const ZigString, - argCount: u32, - function: *const JSHostFn, - strong: bool, - ) *anyopaque; pub extern fn Bun__CreateFFIFunctionValue( globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, function: *const JSHostFn, - strong: bool, add_ptr_field: bool, inputFunctionPtr: ?*anyopaque, ) JSValue; - pub extern fn Bun__untrackFFIFunction( - globalObject: *JSGlobalObject, - function: JSValue, - ) bool; - pub extern fn Bun__FFIFunction_getDataPtr(JSValue) ?*anyopaque; pub extern fn Bun__FFIFunction_setDataPtr(JSValue, ?*anyopaque) void; }; -pub fn NewFunction( - globalObject: *JSGlobalObject, - symbolName: ?*const ZigString, - argCount: u32, - comptime function: anytype, - strong: bool, -) JSValue { - if (@TypeOf(function) == JSHostFn) { - return NewRuntimeFunction(globalObject, symbolName, argCount, function, strong, false, null); - } - return NewRuntimeFunction(globalObject, symbolName, argCount, toJSHostFn(function), strong, false, null); -} - -pub fn createCallback( - globalObject: *JSGlobalObject, - symbolName: ?*const ZigString, - argCount: u32, - comptime function: anytype, -) JSValue { - if (@TypeOf(function) == JSHostFn) { - return NewRuntimeFunction(globalObject, symbolName, argCount, function, false, false, null); - } - return NewRuntimeFunction(globalObject, symbolName, argCount, toJSHostFn(function), false, false, null); -} - pub fn NewRuntimeFunction( globalObject: *JSGlobalObject, symbolName: ?*const ZigString, argCount: u32, functionPointer: *const JSHostFn, - strong: bool, add_ptr_property: bool, inputFunctionPtr: ?*anyopaque, ) JSValue { jsc.markBinding(@src()); - return private.Bun__CreateFFIFunctionValue(globalObject, symbolName, argCount, functionPointer, strong, add_ptr_property, inputFunctionPtr); + return private.Bun__CreateFFIFunctionValue(globalObject, symbolName, argCount, functionPointer, add_ptr_property, inputFunctionPtr); } pub fn getFunctionData(function: JSValue) ?*anyopaque { @@ -354,7 +314,6 @@ pub fn NewFunctionWithData( symbolName: ?*const ZigString, argCount: u32, comptime function: JSHostFnZig, - strong: bool, data: *anyopaque, ) JSValue { jsc.markBinding(@src()); @@ -363,19 +322,10 @@ pub fn NewFunctionWithData( symbolName, argCount, toJSHostFn(function), - strong, data, ); } -pub fn untrackFunction( - globalObject: *JSGlobalObject, - value: JSValue, -) bool { - jsc.markBinding(@src()); - return private.Bun__untrackFFIFunction(globalObject, value); -} - pub const DOMEffect = struct { reads: [4]ID = std.mem.zeroes([4]ID), writes: [4]ID = std.mem.zeroes([4]ID), diff --git a/src/bun.js/test/DoneCallback.zig b/src/bun.js/test/DoneCallback.zig index 7c1c4e49a3..f9bb8e3619 100644 --- a/src/bun.js/test/DoneCallback.zig +++ b/src/bun.js/test/DoneCallback.zig @@ -29,7 +29,7 @@ pub fn createUnbound(globalThis: *JSGlobalObject) JSValue { } pub fn bind(value: JSValue, globalThis: *JSGlobalObject) bun.JSError!JSValue { - const callFn = jsc.host_fn.NewFunction(globalThis, bun.ZigString.static("done"), 1, BunTest.bunTestDoneCallback, false); + const callFn = jsc.JSFunction.create(globalThis, "done", BunTest.bunTestDoneCallback, 1, .{}); return try callFn.bind(globalThis, value, &bun.String.static("done"), 1, &.{}); } diff --git a/src/bun.js/test/ScopeFunctions.zig b/src/bun.js/test/ScopeFunctions.zig index 9c44d3b388..715c334505 100644 --- a/src/bun.js/test/ScopeFunctions.zig +++ b/src/bun.js/test/ScopeFunctions.zig @@ -450,9 +450,9 @@ pub fn createUnbound(globalThis: *JSGlobalObject, mode: Mode, each: jsc.JSValue, return value; } -pub fn bind(value: JSValue, globalThis: *JSGlobalObject, name: *const bun.String) bun.JSError!JSValue { - const callFn = jsc.host_fn.NewFunction(globalThis, &name.toZigString(), 1, callAsFunction, false); - const bound = try callFn.bind(globalThis, value, name, 1, &.{}); +pub fn bind(value: JSValue, globalThis: *JSGlobalObject, name: bun.String) bun.JSError!JSValue { + const callFn = jsc.JSFunction.create(globalThis, name, callAsFunction, 1, .{}); + const bound = try callFn.bind(globalThis, value, &name, 1, &.{}); try bound.setPrototypeDirect(value.getPrototype(globalThis), globalThis); return bound; } @@ -462,7 +462,7 @@ pub fn createBound(globalThis: *JSGlobalObject, mode: Mode, each: jsc.JSValue, c defer groupLog.end(); const value = createUnbound(globalThis, mode, each, cfg); - return bind(value, globalThis, &name); + return bind(value, globalThis, name); } const bun = @import("bun"); diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index a2add25650..3725a5d5cd 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -195,12 +195,12 @@ pub const Jest = struct { const xdescribe_scope_functions = bun_test.ScopeFunctions.createBound(globalObject, .describe, .zero, .{ .self_mode = .skip }, bun_test.ScopeFunctions.strings.xdescribe) catch return .zero; module.put(globalObject, ZigString.static("xdescribe"), xdescribe_scope_functions); - module.put(globalObject, ZigString.static("beforeEach"), jsc.host_fn.NewFunction(globalObject, ZigString.static("beforeEach"), 1, bun_test.js_fns.genericHook(.beforeEach).hookFn, false)); - module.put(globalObject, ZigString.static("beforeAll"), jsc.host_fn.NewFunction(globalObject, ZigString.static("beforeAll"), 1, bun_test.js_fns.genericHook(.beforeAll).hookFn, false)); - module.put(globalObject, ZigString.static("afterAll"), jsc.host_fn.NewFunction(globalObject, ZigString.static("afterAll"), 1, bun_test.js_fns.genericHook(.afterAll).hookFn, false)); - module.put(globalObject, ZigString.static("afterEach"), jsc.host_fn.NewFunction(globalObject, ZigString.static("afterEach"), 1, bun_test.js_fns.genericHook(.afterEach).hookFn, false)); - module.put(globalObject, ZigString.static("onTestFinished"), jsc.host_fn.NewFunction(globalObject, ZigString.static("onTestFinished"), 1, bun_test.js_fns.genericHook(.onTestFinished).hookFn, false)); - module.put(globalObject, ZigString.static("setDefaultTimeout"), jsc.host_fn.NewFunction(globalObject, ZigString.static("setDefaultTimeout"), 1, jsSetDefaultTimeout, false)); + module.put(globalObject, ZigString.static("beforeEach"), jsc.JSFunction.create(globalObject, "beforeEach", bun_test.js_fns.genericHook(.beforeEach).hookFn, 1, .{})); + module.put(globalObject, ZigString.static("beforeAll"), jsc.JSFunction.create(globalObject, "beforeAll", bun_test.js_fns.genericHook(.beforeAll).hookFn, 1, .{})); + module.put(globalObject, ZigString.static("afterAll"), jsc.JSFunction.create(globalObject, "afterAll", bun_test.js_fns.genericHook(.afterAll).hookFn, 1, .{})); + module.put(globalObject, ZigString.static("afterEach"), jsc.JSFunction.create(globalObject, "afterEach", bun_test.js_fns.genericHook(.afterEach).hookFn, 1, .{})); + module.put(globalObject, ZigString.static("onTestFinished"), jsc.JSFunction.create(globalObject, "onTestFinished", bun_test.js_fns.genericHook(.onTestFinished).hookFn, 1, .{})); + module.put(globalObject, ZigString.static("setDefaultTimeout"), jsc.JSFunction.create(globalObject, "setDefaultTimeout", jsSetDefaultTimeout, 1, .{})); module.put(globalObject, ZigString.static("expect"), Expect.js.getConstructor(globalObject)); module.put(globalObject, ZigString.static("expectTypeOf"), ExpectTypeOf.js.getConstructor(globalObject)); @@ -211,20 +211,20 @@ pub const Jest = struct { } fn createMockObjects(globalObject: *JSGlobalObject, module: JSValue) void { - const setSystemTime = jsc.host_fn.NewFunction(globalObject, ZigString.static("setSystemTime"), 0, JSMock__jsSetSystemTime, false); + const setSystemTime = jsc.JSFunction.create(globalObject, "setSystemTime", JSMock__jsSetSystemTime, 0, .{}); module.put( globalObject, ZigString.static("setSystemTime"), setSystemTime, ); - const useFakeTimers = jsc.host_fn.NewFunction(globalObject, ZigString.static("useFakeTimers"), 0, JSMock__jsUseFakeTimers, false); - const useRealTimers = jsc.host_fn.NewFunction(globalObject, ZigString.static("useRealTimers"), 0, JSMock__jsUseRealTimers, false); + const useFakeTimers = jsc.JSFunction.create(globalObject, "useFakeTimers", JSMock__jsUseFakeTimers, 0, .{}); + const useRealTimers = jsc.JSFunction.create(globalObject, "useRealTimers", JSMock__jsUseRealTimers, 0, .{}); - const mockFn = jsc.host_fn.NewFunction(globalObject, ZigString.static("fn"), 1, JSMock__jsMockFn, false); - const spyOn = jsc.host_fn.NewFunction(globalObject, ZigString.static("spyOn"), 2, JSMock__jsSpyOn, false); - const restoreAllMocks = jsc.host_fn.NewFunction(globalObject, ZigString.static("restoreAllMocks"), 2, JSMock__jsRestoreAllMocks, false); - const clearAllMocks = jsc.host_fn.NewFunction(globalObject, ZigString.static("clearAllMocks"), 2, JSMock__jsClearAllMocks, false); - const mockModuleFn = jsc.host_fn.NewFunction(globalObject, ZigString.static("module"), 2, JSMock__jsModuleMock, false); + const mockFn = jsc.JSFunction.create(globalObject, "fn", JSMock__jsMockFn, 1, .{}); + const spyOn = jsc.JSFunction.create(globalObject, "spyOn", JSMock__jsSpyOn, 2, .{}); + const restoreAllMocks = jsc.JSFunction.create(globalObject, "restoreAllMocks", JSMock__jsRestoreAllMocks, 2, .{}); + const clearAllMocks = jsc.JSFunction.create(globalObject, "clearAllMocks", JSMock__jsClearAllMocks, 2, .{}); + const mockModuleFn = jsc.JSFunction.create(globalObject, "module", JSMock__jsModuleMock, 2, .{}); module.put(globalObject, ZigString.static("mock"), mockFn); mockFn.put(globalObject, ZigString.static("module"), mockModuleFn); mockFn.put(globalObject, ZigString.static("restore"), restoreAllMocks); @@ -252,8 +252,8 @@ pub const Jest = struct { ZigString.static("useRealTimers"), useRealTimers, ); - jest.put(globalObject, ZigString.static("now"), jsc.host_fn.NewFunction(globalObject, ZigString.static("now"), 0, JSMock__jsNow, false)); - jest.put(globalObject, ZigString.static("setTimeout"), jsc.host_fn.NewFunction(globalObject, ZigString.static("setTimeout"), 1, jsSetDefaultTimeout, false)); + jest.put(globalObject, ZigString.static("now"), jsc.JSFunction.create(globalObject, "now", JSMock__jsNow, 0, .{})); + jest.put(globalObject, ZigString.static("setTimeout"), jsc.JSFunction.create(globalObject, "setTimeout", jsSetDefaultTimeout, 1, .{})); module.put(globalObject, ZigString.static("jest"), jest); module.put(globalObject, ZigString.static("spyOn"), spyOn); diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index cf9dc91427..9252782e01 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -903,9 +903,9 @@ pub const upgrade_js_bindings = struct { pub fn generate(global: *jsc.JSGlobalObject) jsc.JSValue { const obj = JSValue.createEmptyObject(global, 2); const open = ZigString.static("openTempDirWithoutSharingDelete"); - obj.put(global, open, jsc.createCallback(global, open, 1, jsOpenTempDirWithoutSharingDelete)); + obj.put(global, open, jsc.JSFunction.create(global, "openTempDirWithoutSharingDelete", jsOpenTempDirWithoutSharingDelete, 1, .{})); const close = ZigString.static("closeTempDirHandle"); - obj.put(global, close, jsc.createCallback(global, close, 1, jsCloseTempDirHandle)); + obj.put(global, close, jsc.JSFunction.create(global, "closeTempDirHandle", jsCloseTempDirHandle, 1, .{})); return obj; } diff --git a/src/codegen/bindgen.ts b/src/codegen/bindgen.ts index 6e9fd9d408..0ab0708612 100644 --- a/src/codegen/bindgen.ts +++ b/src/codegen/bindgen.ts @@ -1455,7 +1455,7 @@ for (const [filename, { functions, typedefs }] of files) { const minArgCount = fn.variants.reduce((acc, vari) => Math.min(acc, vari.args.length), Number.MAX_SAFE_INTEGER); zig.line(`pub fn ${wrapperName}(global: *jsc.JSGlobalObject) callconv(jsc.conv) jsc.JSValue {`); zig.line( - ` return jsc.host_fn.NewRuntimeFunction(global, jsc.ZigString.static(${str(fn.name)}), ${minArgCount}, js${cap(fn.name)}, false, false, null);`, + ` return jsc.host_fn.NewRuntimeFunction(global, jsc.ZigString.static(${str(fn.name)}), ${minArgCount}, js${cap(fn.name)}, false, null);`, ); zig.line(`}`); } diff --git a/src/crash_handler.zig b/src/crash_handler.zig index 3bc18b642b..d902410444 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -1858,7 +1858,7 @@ pub const js_bindings = struct { .{ "raiseIgnoringPanicHandler", jsRaiseIgnoringPanicHandler }, }) |tuple| { const name = jsc.ZigString.static(tuple[0]); - obj.put(global, name, jsc.createCallback(global, name, 1, tuple[1])); + obj.put(global, name, jsc.JSFunction.create(global, tuple[0], tuple[1], 1, .{})); } return obj; } diff --git a/src/csrf.zig b/src/csrf.zig index d78756a83f..45a204c92a 100644 --- a/src/csrf.zig +++ b/src/csrf.zig @@ -291,7 +291,7 @@ pub fn csrf__generate_impl(globalObject: *jsc.JSGlobalObject, callframe: *jsc.Ca return encoding.toNodeEncoding().encodeWithMaxSize(globalObject, boring.EVP_MAX_MD_SIZE + 32, token_bytes); } -pub const csrf__generate = jsc.toJSHostFn(csrf__generate_impl); +pub const csrf__generate = csrf__generate_impl; /// JS binding function for verifying CSRF tokens /// First argument is token (required), second is options (optional) @@ -377,7 +377,7 @@ pub fn csrf__verify_impl(globalObject: *jsc.JSGlobalObject, call_frame: *jsc.Cal return jsc.JSValue.jsBoolean(is_valid); } -pub const csrf__verify = jsc.toJSHostFn(csrf__verify_impl); +pub const csrf__verify = csrf__verify_impl; const hmac = @import("./hmac.zig"); const std = @import("std"); diff --git a/src/deps/uws/UpgradedDuplex.zig b/src/deps/uws/UpgradedDuplex.zig index 26204293e5..419c03b690 100644 --- a/src/deps/uws/UpgradedDuplex.zig +++ b/src/deps/uws/UpgradedDuplex.zig @@ -270,7 +270,6 @@ pub fn getJSHandlers(this: *UpgradedDuplex, globalThis: *jsc.JSGlobalObject) bun null, 0, onReceivedData, - false, this, ); dataCallback.ensureStillAlive(); @@ -290,7 +289,6 @@ pub fn getJSHandlers(this: *UpgradedDuplex, globalThis: *jsc.JSGlobalObject) bun null, 0, onReceivedData, - false, this, ); endCallback.ensureStillAlive(); @@ -310,7 +308,6 @@ pub fn getJSHandlers(this: *UpgradedDuplex, globalThis: *jsc.JSGlobalObject) bun null, 0, onWritable, - false, this, ); writableCallback.ensureStillAlive(); @@ -329,7 +326,6 @@ pub fn getJSHandlers(this: *UpgradedDuplex, globalThis: *jsc.JSGlobalObject) bun null, 0, onCloseJS, - false, this, ); closeCallback.ensureStillAlive(); diff --git a/src/install/install_binding.zig b/src/install/install_binding.zig index fc937f4529..504c2391d3 100644 --- a/src/install/install_binding.zig +++ b/src/install/install_binding.zig @@ -6,7 +6,7 @@ pub const bun_install_js_bindings = struct { pub fn generate(global: *JSGlobalObject) JSValue { const obj = JSValue.createEmptyObject(global, 1); const parseLockfile = ZigString.static("parseLockfile"); - obj.put(global, parseLockfile, jsc.createCallback(global, parseLockfile, 1, jsParseLockfile)); + obj.put(global, parseLockfile, jsc.JSFunction.create(global, "parseLockfile", jsParseLockfile, 1, .{})); return obj; } diff --git a/src/install/npm.zig b/src/install/npm.zig index 11ca8fa72d..b79fb89cbb 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -1337,7 +1337,7 @@ pub const PackageManifest = struct { pub fn generate(global: *JSGlobalObject) JSValue { const obj = JSValue.createEmptyObject(global, 1); const parseManifestString = ZigString.static("parseManifest"); - obj.put(global, parseManifestString, jsc.createCallback(global, parseManifestString, 2, jsParseManifest)); + obj.put(global, parseManifestString, jsc.JSFunction.create(global, "parseManifest", jsParseManifest, 2, .{})); return obj; } diff --git a/src/semver/SemverObject.zig b/src/semver/SemverObject.zig index 91c7d90ee2..7fbc12bc5a 100644 --- a/src/semver/SemverObject.zig +++ b/src/semver/SemverObject.zig @@ -6,24 +6,24 @@ pub fn create(globalThis: *jsc.JSGlobalObject) jsc.JSValue { object.put( globalThis, jsc.ZigString.static("satisfies"), - jsc.host_fn.NewFunction( + jsc.JSFunction.create( globalThis, - jsc.ZigString.static("satisfies"), - 2, + "satisfies", SemverObject.satisfies, - false, + 2, + .{}, ), ); object.put( globalThis, jsc.ZigString.static("order"), - jsc.host_fn.NewFunction( + jsc.JSFunction.create( globalThis, - jsc.ZigString.static("order"), - 2, + "order", SemverObject.order, - false, + 2, + .{}, ), ); diff --git a/test/js/bun/util/hash.test.js b/test/js/bun/util/hash.test.js index 9dd290439f..c680649b01 100644 --- a/test/js/bun/util/hash.test.js +++ b/test/js/bun/util/hash.test.js @@ -74,3 +74,15 @@ it(`Bun.hash.rapidhash()`, () => { gcTick(); expect(Bun.hash.rapidhash(new TextEncoder().encode("hello world"))).toBe(0x58a89bdcee89c08cn); }); +it("does not crash when changing Int32Array constructor with Bun.hash.xxHash32 as species", () => { + const arr = new Int32Array(); + function foo(a4) { + return a4; + } + foo[Symbol.species] = Bun.hash.xxHash32; + arr.constructor = foo; + + expect(() => { + arr.map(Bun.hash.xxHash32); + }).toThrow("species is not a constructor"); +}); From abb1b0c4d75acabf1742b951a1f739068c94a7bd Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Thu, 20 Nov 2025 23:37:31 -0800 Subject: [PATCH 17/20] test(ENG-21524): Fuzzilli Stop-Gap (#24826) ### What does this PR do? Adds [@mschwarzl's Fuzzilli Support PR](https://github.com/oven-sh/bun/pull/23862) with the changes necessary to be able to: - Run it in CI - Make no impact on `debug` and `release` mode. ### How did you verify your code works? --------- Co-authored-by: Martin Schwarzl Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Dylan Conway --- build.zig | 11 + cmake/CompilerFlags.cmake | 17 ++ cmake/Options.cmake | 2 + cmake/targets/BuildBun.cmake | 1 + package.json | 1 + src/bun.js/bindings/FuzzilliREPRL.cpp | 286 ++++++++++++++++++++++++ src/bun.js/bindings/ZigGlobalObject.cpp | 9 + src/cli.zig | 18 +- src/cli/fuzzilli_command.zig | 63 ++++++ src/env.zig | 1 + src/js/eval/fuzzilli-reprl.ts | 91 ++++++++ 11 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 src/bun.js/bindings/FuzzilliREPRL.cpp create mode 100644 src/cli/fuzzilli_command.zig create mode 100644 src/js/eval/fuzzilli-reprl.ts diff --git a/build.zig b/build.zig index eb16d47401..3b50fd1fbe 100644 --- a/build.zig +++ b/build.zig @@ -32,6 +32,7 @@ const BunBuildOptions = struct { /// enable debug logs in release builds enable_logs: bool = false, enable_asan: bool, + enable_fuzzilli: bool, enable_valgrind: bool, use_mimalloc: bool, tracy_callstack_depth: u16, @@ -81,6 +82,7 @@ const BunBuildOptions = struct { opts.addOption(bool, "baseline", this.isBaseline()); opts.addOption(bool, "enable_logs", this.enable_logs); opts.addOption(bool, "enable_asan", this.enable_asan); + opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli); opts.addOption(bool, "enable_valgrind", this.enable_valgrind); opts.addOption(bool, "use_mimalloc", this.use_mimalloc); opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version})); @@ -255,6 +257,7 @@ pub fn build(b: *Build) !void { .tracy_callstack_depth = b.option(u16, "tracy_callstack_depth", "") orelse 10, .enable_logs = b.option(bool, "enable_logs", "Enable logs in release") orelse false, .enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false, + .enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false, .enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false, .use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false, .llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1, @@ -490,6 +493,7 @@ fn addMultiCheck( .no_llvm = root_build_options.no_llvm, .enable_asan = root_build_options.enable_asan, .enable_valgrind = root_build_options.enable_valgrind, + .enable_fuzzilli = root_build_options.enable_fuzzilli, .use_mimalloc = root_build_options.use_mimalloc, .override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis, }; @@ -605,13 +609,20 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void { obj.no_link_obj = opts.os != .windows; + if (opts.enable_asan and !enableFastBuild(b)) { if (@hasField(Build.Module, "sanitize_address")) { + if (opts.enable_fuzzilli) { + obj.sanitize_coverage_trace_pc_guard = true; + } obj.root_module.sanitize_address = true; } else { const fail_step = b.addFail("asan is not supported on this platform"); obj.step.dependOn(&fail_step.step); } + } else if (opts.enable_fuzzilli) { + const fail_step = b.addFail("fuzzilli requires asan"); + obj.step.dependOn(&fail_step.step); } obj.bundle_compiler_rt = false; obj.bundle_ubsan_rt = false; diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake index cff32fb166..bff19e1974 100644 --- a/cmake/CompilerFlags.cmake +++ b/cmake/CompilerFlags.cmake @@ -51,6 +51,23 @@ if(ENABLE_ASAN) ) endif() +if(ENABLE_FUZZILLI) + register_compiler_flags( + DESCRIPTION "Enable coverage instrumentation for fuzzing" + -fsanitize-coverage=trace-pc-guard + ) + + register_linker_flags( + DESCRIPTION "Link coverage instrumentation" + -fsanitize-coverage=trace-pc-guard + ) + + register_compiler_flags( + DESCRIPTION "Enable fuzzilli-specific code" + -DFUZZILLI_ENABLED + ) +endif() + # --- Optimization level --- if(DEBUG) register_compiler_flags( diff --git a/cmake/Options.cmake b/cmake/Options.cmake index ac6ce10c74..e54f6db166 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -127,6 +127,8 @@ if (NOT ENABLE_ASAN) set(ENABLE_ZIG_ASAN OFF) endif() +optionx(ENABLE_FUZZILLI BOOL "If fuzzilli support should be enabled" DEFAULT OFF) + if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN) set(DEFAULT_LTO ON) else() diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index 43b061846b..6b23266ade 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -695,6 +695,7 @@ register_command( -Dcpu=${ZIG_CPU} -Denable_logs=$,true,false> -Denable_asan=$,true,false> + -Denable_fuzzilli=$,true,false> -Denable_valgrind=$,true,false> -Duse_mimalloc=$,true,false> -Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS} diff --git a/package.json b/package.json index c8bdbc36fa..ee34f430b8 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "bd:v": "(bun run --silent build:debug &> /tmp/bun.debug.build.log || (cat /tmp/bun.debug.build.log && rm -rf /tmp/bun.debug.build.log && exit 1)) && rm -f /tmp/bun.debug.build.log && ./build/debug/bun-debug", "bd": "BUN_DEBUG_QUIET_LOGS=1 bun --silent bd:v", "build:debug": "export COMSPEC=\"C:\\Windows\\System32\\cmd.exe\" && bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug --log-level=NOTICE", + "build:debug:fuzzilli": "export COMSPEC=\"C:\\Windows\\System32\\cmd.exe\" && bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug-fuzz -DENABLE_FUZZILLI=ON --log-level=NOTICE", "build:debug:noasan": "export COMSPEC=\"C:\\Windows\\System32\\cmd.exe\" && bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_ASAN=OFF -B build/debug --log-level=NOTICE", "build:release": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -B build/release", "build:ci": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DCI=true -B build/release-ci --verbose --fresh", diff --git a/src/bun.js/bindings/FuzzilliREPRL.cpp b/src/bun.js/bindings/FuzzilliREPRL.cpp new file mode 100644 index 0000000000..b3e0815271 --- /dev/null +++ b/src/bun.js/bindings/FuzzilliREPRL.cpp @@ -0,0 +1,286 @@ +#ifdef FUZZILLI_ENABLED +#include "JavaScriptCore/CallFrame.h" +#include "JavaScriptCore/Identifier.h" +#include "JavaScriptCore/JSGlobalObject.h" +#include "ZigGlobalObject.h" +#include "root.h" +#include "wtf/text/WTFString.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REPRL_DWFD 103 + +extern "C" { + +// Signal handler to ensure output is flushed before crash +static void fuzzilliSignalHandler(int sig) +{ + // Flush all output + fflush(stdout); + fflush(stderr); + fsync(STDOUT_FILENO); + fsync(STDERR_FILENO); + + // Re-raise the signal with default handler + signal(sig, SIG_DFL); + raise(sig); +} + +// Implementation of the global fuzzilli() function for Bun +// This function is used by Fuzzilli to: +// 1. Test crash detection with fuzzilli('FUZZILLI_CRASH', type) +// 2. Print output with fuzzilli('FUZZILLI_PRINT', value) +static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES functionFuzzilli(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 1) { + return JSC::JSValue::encode(JSC::jsUndefined()); + } + + JSC::JSValue arg0 = callFrame->argument(0); + WTF::String command = arg0.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); + + if (command == "FUZZILLI_CRASH"_s) { + // Fuzzilli uses this to test crash detection + // The second argument is an integer specifying the crash type + int crashType = 0; + if (callFrame->argumentCount() >= 2) { + JSC::JSValue arg1 = callFrame->argument(1); + crashType = arg1.toInt32(globalObject); + } + + // Print the crash type for debugging + fprintf(stdout, "FUZZILLI_CRASH: %d\n", crashType); + fflush(stdout); + + // Trigger different types of crashes for testing (similar to V8 implementation) + switch (crashType) { + case 0: + // IMMEDIATE_CRASH - Simple abort + std::abort(); + break; + + case 1: + // CHECK failure - assertion in release builds + // Use __builtin_trap() for a direct crash + __builtin_trap(); + break; + + case 2: + // DCHECK failure - always crash (use trap instead of assert which is disabled in release) + __builtin_trap(); + break; + + case 3: + // Wild write - heap buffer overflow (will be caught by ASAN) + { + volatile char* buffer = new char[10]; + buffer[20] = 'x'; // Write past the end - ASAN should catch this + // Don't delete to make it more obvious + } + break; + + case 4: + // Use-after-free (will be caught by ASAN) + { + volatile char* buffer = new char[10]; + delete[] buffer; + buffer[0] = 'x'; // Use after free - ASAN should catch this + } + break; + + case 5: + // Null pointer dereference + { + volatile int* ptr = nullptr; + *ptr = 42; + } + break; + + case 6: + // Stack buffer overflow (will be caught by ASAN) + { + volatile char buffer[10]; + volatile char* p = const_cast(buffer); + p[20] = 'x'; // Write past stack buffer + } + break; + + case 7: + // Double free (will be caught by ASAN) + { + char* buffer = new char[10]; + delete[] buffer; + delete[] buffer; // Double free - ASAN should catch this + } + break; + + case 8: + // Verify DEBUG or ASAN is enabled + // Expected to be compiled with debug or ASAN, don't crash + fprintf(stdout, "DEBUG or ASAN is enabled\n"); + fflush(stdout); + break; + + default: + // Unknown crash type, just abort + std::abort(); + break; + } + } else if (command == "FUZZILLI_PRINT"_s) { + // Optional: Print the second argument + if (callFrame->argumentCount() >= 2) { + JSC::JSValue arg1 = callFrame->argument(1); + WTF::String output = arg1.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); + + FILE* f = fdopen(REPRL_DWFD, "w"); + fprintf(f, "%s\n", output.utf8().data()); + fflush(f); + } + } + + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// ============================================================================ +// Coverage instrumentation for Fuzzilli +// Based on workerd implementation +// Only enabled when ASAN is active +// ============================================================================ + +#define SHM_SIZE 0x200000 +#define MAX_EDGES ((SHM_SIZE - 4) * 8) + +struct shmem_data { + uint32_t num_edges; + unsigned char edges[]; +}; + +// Global coverage data +static struct shmem_data* __shmem = nullptr; +static uint32_t* __edges_start = nullptr; +static uint32_t* __edges_stop = nullptr; + +// Reset edge guards for next iteration +static void __sanitizer_cov_reset_edgeguards() +{ + if (!__edges_start || !__edges_stop) return; + uint64_t N = 0; + for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++) { + *x = ++N; + } +} + +// Called by the compiler to initialize coverage instrumentation +extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) +{ + // Avoid duplicate initialization + if (start == stop || *start) return; + + if (__edges_start != nullptr || __edges_stop != nullptr) { + fprintf(stderr, "[COV] Coverage instrumentation is only supported for a single module\n"); + _exit(-1); + } + + __edges_start = start; + __edges_stop = stop; + + // Map the shared memory region + const char* shm_key = getenv("SHM_ID"); + if (!shm_key) { + fprintf(stderr, "[COV] no shared memory bitmap available, using malloc\n"); + __shmem = (struct shmem_data*)malloc(SHM_SIZE); + if (!__shmem) { + fprintf(stderr, "[COV] Failed to allocate coverage bitmap\n"); + _exit(-1); + } + memset(__shmem, 0, SHM_SIZE); + } else { + int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE); + if (fd <= -1) { + fprintf(stderr, "[COV] Failed to open shared memory region: %s\n", strerror(errno)); + _exit(-1); + } + + __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (__shmem == MAP_FAILED) { + fprintf(stderr, "[COV] Failed to mmap shared memory region\n"); + _exit(-1); + } + } + + __sanitizer_cov_reset_edgeguards(); + __shmem->num_edges = stop - start; + fprintf(stderr, "[COV] Coverage instrumentation initialized with %u edges\n", __shmem->num_edges); +} + +// Called by the compiler for each edge +extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) +{ + // There's a small race condition here: if this function executes in two threads for the same + // edge at the same time, the first thread might disable the edge (by setting the guard to zero) + // before the second thread fetches the guard value (and thus the index). However, our + // instrumentation ignores the first edge (see libcoverage.c) and so the race is unproblematic. + if (!__shmem) return; + uint32_t index = *guard; + // If this function is called before coverage instrumentation is properly initialized we want to return early. + if (!index) return; + __shmem->edges[index / 8] |= 1 << (index % 8); + *guard = 0; +} + +// Function to reset coverage for next REPRL iteration +// This should be called after each script execution +JSC_DEFINE_HOST_FUNCTION(jsResetCoverage, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + __sanitizer_cov_reset_edgeguards(); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// Register the fuzzilli() function on a Bun global object +void Bun__REPRL__registerFuzzilliFunctions(Zig::GlobalObject* globalObject) +{ + JSC::VM& vm = globalObject->vm(); + + // Install signal handlers to ensure output is flushed before crashes + // This is important for ASAN output to be captured + signal(SIGABRT, fuzzilliSignalHandler); + signal(SIGSEGV, fuzzilliSignalHandler); + signal(SIGILL, fuzzilliSignalHandler); + signal(SIGFPE, fuzzilliSignalHandler); + + globalObject->putDirectNativeFunction( + vm, + globalObject, + JSC::Identifier::fromString(vm, "fuzzilli"_s), + 2, // max 2 arguments + functionFuzzilli, + JSC::ImplementationVisibility::Public, + JSC::NoIntrinsic, + JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); + + globalObject->putDirectNativeFunction( + vm, + globalObject, + JSC::Identifier::fromString(vm, "resetCoverage"_s), + 0, + jsResetCoverage, + JSC::ImplementationVisibility::Public, + JSC::NoIntrinsic, + JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); +} + +} // extern "C" + +#endif // FUZZILLI_ENABLED diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index c2d3b64f8a..a47cb93a25 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -260,6 +260,11 @@ extern "C" unsigned getJSCBytecodeCacheVersion() return getWebKitBytecodeCacheVersion(); } +// Declare fuzzilli function registration from FuzzilliREPRL.cpp +#ifdef FUZZILLI_ENABLED +extern "C" void Bun__REPRL__registerFuzzilliFunctions(Zig::GlobalObject*); +#endif + extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(const char* ptr, size_t length), bool evalMode) { static std::once_flag jsc_init_flag; @@ -503,6 +508,10 @@ extern "C" JSC::JSGlobalObject* Zig__GlobalObject__create(void* console_client, Bun__setDefaultGlobalObject(globalObject); JSC::gcProtect(globalObject); +#ifdef FUZZILLI_ENABLED + Bun__REPRL__registerFuzzilliFunctions(static_cast(globalObject)); +#endif + vm.setOnComputeErrorInfo(computeErrorInfoWrapperToString); vm.setOnComputeErrorInfoJSValue(computeErrorInfoWrapperToJSValue); vm.setComputeLineColumnWithSourcemap(computeLineColumnWithSourcemap); diff --git a/src/cli.zig b/src/cli.zig index 63b605339c..204999fbe9 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -91,6 +91,7 @@ pub const PackCommand = @import("./cli/pack_command.zig").PackCommand; pub const AuditCommand = @import("./cli/audit_command.zig").AuditCommand; pub const InitCommand = @import("./cli/init_command.zig").InitCommand; pub const WhyCommand = @import("./cli/why_command.zig").WhyCommand; +pub const FuzzilliCommand = @import("./cli/fuzzilli_command.zig").FuzzilliCommand; pub const Arguments = @import("./cli/Arguments.zig"); @@ -626,6 +627,10 @@ pub const Command = struct { RootCommandMatcher.case("prune") => .ReservedCommand, RootCommandMatcher.case("list") => .PackageManagerCommand, RootCommandMatcher.case("why") => .WhyCommand, + RootCommandMatcher.case("fuzzilli") => if (bun.Environment.enable_fuzzilli) + .FuzzilliCommand + else + .AutoCommand, RootCommandMatcher.case("-e") => .AutoCommand, @@ -935,6 +940,15 @@ pub const Command = struct { try ExecCommand.exec(ctx); } else Tag.printHelp(.ExecCommand, true); }, + .FuzzilliCommand => { + if (bun.Environment.enable_fuzzilli) { + const ctx = try Command.init(allocator, log, .FuzzilliCommand); + try FuzzilliCommand.exec(ctx); + return; + } else { + return error.UnrecognizedCommand; + } + }, } } @@ -970,6 +984,7 @@ pub const Command = struct { PublishCommand, AuditCommand, WhyCommand, + FuzzilliCommand, /// Used by crash reports. /// @@ -1007,6 +1022,7 @@ pub const Command = struct { .PublishCommand => 'k', .AuditCommand => 'A', .WhyCommand => 'W', + .FuzzilliCommand => 'F', }; } @@ -1320,7 +1336,7 @@ pub const Command = struct { Output.flush(); }, else => { - HelpCommand.printWithReason(.explicit); + HelpCommand.printWithReason(.explicit, false); }, } } diff --git a/src/cli/fuzzilli_command.zig b/src/cli/fuzzilli_command.zig new file mode 100644 index 0000000000..f1c24005e0 --- /dev/null +++ b/src/cli/fuzzilli_command.zig @@ -0,0 +1,63 @@ +pub const FuzzilliCommand = if (bun.Environment.enable_fuzzilli) struct { + pub fn exec(ctx: Command.Context) !void { + @branchHint(.cold); + + if (!Environment.isPosix) { + Output.prettyErrorln("error: Fuzzilli mode is only supported on POSIX systems", .{}); + Global.exit(1); + } + + // Set an environment variable so we can detect fuzzilli mode in JavaScript + + // Verify REPRL file descriptors are available + const REPRL_CRFD: c_int = 100; + verifyFd(REPRL_CRFD) catch { + Output.prettyErrorln("error: REPRL_CRFD (fd {d}) is not available. Run Bun under Fuzzilli.", .{REPRL_CRFD}); + Output.prettyErrorln("Example: fuzzilli --profile=bun /path/to/bun fuzzilli", .{}); + Global.exit(1); + }; + + // Always embed the REPRL script (it's small and not worth the runtime overhead) + const reprl_script = @embedFile("../js/eval/fuzzilli-reprl.ts"); + + // Create temp file for the script + var temp_dir = bun.FD.cwd().openDir("/tmp", .{}) catch { + Output.prettyErrorln("error: Could not access /tmp directory", .{}); + Global.exit(1); + }; + defer temp_dir.close(); + + const temp_file_name = "bun-fuzzilli-reprl.js"; + const temp_file = temp_dir.createFile(temp_file_name, .{ .truncate = true }) catch { + Output.prettyErrorln("error: Could not create temp file", .{}); + Global.exit(1); + }; + defer temp_file.close(); + + _ = temp_file.writeAll(reprl_script) catch { + Output.prettyErrorln("error: Could not write temp file", .{}); + Global.exit(1); + }; + + Output.prettyErrorln("[FUZZILLI] Temp file written, booting JS runtime", .{}); + + // Run the temp file + const temp_path = "/tmp/bun-fuzzilli-reprl.js"; + try Run.boot(ctx, temp_path, null); + } + + fn verifyFd(fd: c_int) !void { + const stat = try std_posix.fstat(fd); + _ = stat; + } +} else {}; + +const bun = @import("bun"); +const Environment = bun.Environment; +const Global = bun.Global; +const Output = bun.Output; +const Command = bun.cli.Command; +const Run = bun.bun_js.Run; + +const std = @import("std"); +const std_posix = std.posix; diff --git a/src/env.zig b/src/env.zig index be7e75dafd..528d4ef5a1 100644 --- a/src/env.zig +++ b/src/env.zig @@ -51,6 +51,7 @@ pub const dump_source = isDebug and !isTest; pub const base_path = build_options.base_path; pub const enable_logs = build_options.enable_logs; pub const enable_asan = build_options.enable_asan; +pub const enable_fuzzilli = build_options.enable_fuzzilli; pub const codegen_path = build_options.codegen_path; pub const codegen_embed = build_options.codegen_embed; diff --git a/src/js/eval/fuzzilli-reprl.ts b/src/js/eval/fuzzilli-reprl.ts new file mode 100644 index 0000000000..2cc125036d --- /dev/null +++ b/src/js/eval/fuzzilli-reprl.ts @@ -0,0 +1,91 @@ +// Comprehensive REPRL wrapper for Bun fuzzing with all runtime APIs exposed +// Based on workerd's approach to maximize fuzzing coverage +// https://bun.com/docs/runtime + +const REPRL_CRFD = 100; // Control read FD +const REPRL_CWFD = 101; // Control write FD +const REPRL_DRFD = 102; // Data read FD + +const fs = require("node:fs"); + +// Make common Node modules available +globalThis.require = require; +globalThis.__dirname = "/"; +globalThis.__filename = "/fuzzilli.js"; + +// ============================================================================ +// REPRL Protocol Loop +// ============================================================================ + +// Verify we're running under Fuzzilli before starting REPRL loop +// The Zig code should have already checked, but double-check here +try { + // Try to stat fd 100 to see if it exists + fs.fstatSync(REPRL_CRFD); +} catch { + // FD doesn't exist - not running under Fuzzilli + console.error("ERROR: REPRL file descriptors not available. Must run under Fuzzilli."); + process.exit(1); +} + +// Send HELO handshake +fs.writeSync(REPRL_CWFD, Buffer.from("HELO")); + +// Read HELO response +const response = Buffer.alloc(4); +const responseBytes = fs.readSync(REPRL_CRFD, response, 0, 4, null); +if (responseBytes !== 4) { + throw new Error(`REPRL handshake failed: expected 4 bytes, got ${responseBytes}`); +} + +// Main REPRL loop +while (true) { + // Read command + const cmd = Buffer.alloc(4); + const cmd_n = fs.readSync(REPRL_CRFD, cmd, 0, 4, null); + + if (cmd_n === 0) { + // EOF + break; + } + + if (cmd_n !== 4 || cmd.toString() !== "exec") { + throw new Error(`Invalid REPRL command: expected 'exec', got ${cmd.toString()}`); + } + + // Read script size (8 bytes, little-endian) + const size_bytes = Buffer.alloc(8); + fs.readSync(REPRL_CRFD, size_bytes, 0, 8, null); + const script_size = Number(size_bytes.readBigUInt64LE(0)); + + // Read script data from REPRL_DRFD + const script_data = Buffer.alloc(script_size); + let total_read = 0; + while (total_read < script_size) { + const n = fs.readSync(REPRL_DRFD, script_data, total_read, script_size - total_read, null); + if (n === 0) break; + total_read += n; + } + + const script = script_data.toString("utf8"); + + // Execute script + let exit_code = 0; + try { + // Use indirect eval to execute in global scope + (0, eval)(script); + } catch (_e) { + // Print uncaught exception like workerd does + console.log(`uncaught:${_e}`); + exit_code = 1; + } + + // Send status back (4 bytes: exit code in REPRL format) + // Format: lower 8 bits = signal number, next 8 bits = exit code + const status = exit_code << 8; + const status_bytes = Buffer.alloc(4); + status_bytes.writeUInt32LE(status, 0); + fs.writeSync(REPRL_CWFD, status_bytes); + + resetCoverage(); +} From 7ec1aa8c951b99a2c87a3879d7b4aae64943f943 Mon Sep 17 00:00:00 2001 From: robobun Date: Fri, 21 Nov 2025 12:45:57 -0800 Subject: [PATCH 18/20] docs: document new features from v1.3.2 and v1.3.3 (#24932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What does this PR do? Adds missing documentation for features introduced in Bun v1.3.2 and v1.3.3: - **Standalone executable config flags** (`docs/bundler/executables.mdx`): Document `--no-compile-autoload-dotenv` and `--no-compile-autoload-bunfig` flags that control automatic config file loading in compiled binaries - **Test retry/repeats** (`docs/test/writing-tests.mdx`): Document the `retry` and `repeats` test options for handling flaky tests - **Disable env file loading** (`docs/runtime/environment-variables.mdx`): Document `--no-env-file` flag and `env = false` bunfig option ## How did you verify your code works? - [x] Verified documentation is accurate against source code implementation in `src/cli/Arguments.zig` - [x] Verified features are not already documented elsewhere - [x] Cross-referenced with v1.3.2 and v1.3.3 release notes 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot Co-authored-by: Claude --- docs/bundler/executables.mdx | 29 ++++++++++++++++++++ docs/runtime/environment-variables.mdx | 17 ++++++++++++ docs/test/writing-tests.mdx | 37 ++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/docs/bundler/executables.mdx b/docs/bundler/executables.mdx index 8de435e715..fefa2769e6 100644 --- a/docs/bundler/executables.mdx +++ b/docs/bundler/executables.mdx @@ -183,6 +183,35 @@ console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"] --- +## Disabling automatic config loading + +By default, standalone executables look for `.env` and `bunfig.toml` files in the directory where the executable is run. You can disable this behavior at build time for deterministic execution regardless of the user's working directory. + +```bash icon="terminal" terminal +# Disable .env loading +bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp + +# Disable bunfig.toml loading +bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp + +# Disable both +bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp +``` + +You can also configure this via the JavaScript API: + +```ts +await Bun.build({ + entrypoints: ["./app.ts"], + compile: { + autoloadDotenv: false, // Disable .env loading + autoloadBunfig: false, // Disable bunfig.toml loading + }, +}); +``` + +--- + ## Act as the Bun CLI New in Bun v1.2.16 diff --git a/docs/runtime/environment-variables.mdx b/docs/runtime/environment-variables.mdx index c1b38edc1b..529b5348af 100644 --- a/docs/runtime/environment-variables.mdx +++ b/docs/runtime/environment-variables.mdx @@ -72,6 +72,23 @@ bun --env-file=.env.1 src/index.ts bun --env-file=.env.abc --env-file=.env.def run build ``` +## Disabling automatic `.env` loading + +Use `--no-env-file` to disable Bun's automatic `.env` file loading. This is useful in production environments or CI/CD pipelines where you want to rely solely on system environment variables. + +```sh +bun run --no-env-file index.ts +``` + +This can also be configured in `bunfig.toml`: + +```toml bunfig.toml icon="settings" +# Disable loading .env files +env = false +``` + +Explicitly provided environment files via `--env-file` will still be loaded even when default loading is disabled. + --- ## Quotation marks diff --git a/docs/test/writing-tests.mdx b/docs/test/writing-tests.mdx index a2de3839a5..5fee977356 100644 --- a/docs/test/writing-tests.mdx +++ b/docs/test/writing-tests.mdx @@ -78,6 +78,43 @@ In `bun:test`, test timeouts throw an uncatchable exception to force the test to The default timeout for each test is 5000ms (5 seconds) if not overridden by this timeout option or `jest.setDefaultTimeout()`. +## Retries and Repeats + +### test.retry + +Use the `retry` option to automatically retry a test if it fails. The test passes if it succeeds within the specified number of attempts. This is useful for flaky tests that may fail intermittently. + +```ts title="example.test.ts" icon="/icons/typescript.svg" +import { test } from "bun:test"; + +test( + "flaky network request", + async () => { + const response = await fetch("https://example.com/api"); + expect(response.ok).toBe(true); + }, + { retry: 3 }, // Retry up to 3 times if the test fails +); +``` + +### test.repeats + +Use the `repeats` option to run a test multiple times regardless of pass/fail status. The test fails if any iteration fails. This is useful for detecting flaky tests or stress testing. Note that `repeats: N` runs the test N+1 times total (1 initial run + N repeats). + +```ts title="example.test.ts" icon="/icons/typescript.svg" +import { test } from "bun:test"; + +test( + "ensure test is stable", + () => { + expect(Math.random()).toBeLessThan(1); + }, + { repeats: 20 }, // Runs 21 times total (1 initial + 20 repeats) +); +``` + +You cannot use both `retry` and `repeats` on the same test. + ### 🧟 Zombie Process Killer When a test times out and processes spawned in the test via `Bun.spawn`, `Bun.spawnSync`, or `node:child_process` are not killed, they will be automatically killed and a message will be logged to the console. This prevents zombie processes from lingering in the background after timed-out tests. From 4450d738fa76b1cccdf02b4376270e5da7027aee Mon Sep 17 00:00:00 2001 From: Michael H Date: Sat, 22 Nov 2025 09:06:19 +1100 Subject: [PATCH 19/20] docs: more consistency + minor updates (#24764) Co-authored-by: RiskyMH --- README.md | 8 +- docs/bundler/css.mdx | 2 +- docs/bundler/executables.mdx | 6 +- docs/bundler/fullstack.mdx | 6 +- docs/bundler/hot-reloading.mdx | 4 +- docs/bundler/html-static.mdx | 7 +- docs/bundler/index.mdx | 33 ++++--- docs/bundler/loaders.mdx | 4 +- docs/bundler/macros.mdx | 2 +- docs/docs.json | 2 +- docs/feedback.mdx | 14 +-- docs/guides/binary/arraybuffer-to-array.mdx | 2 +- docs/guides/binary/arraybuffer-to-blob.mdx | 2 +- docs/guides/binary/arraybuffer-to-buffer.mdx | 2 +- docs/guides/binary/arraybuffer-to-string.mdx | 2 +- .../binary/arraybuffer-to-typedarray.mdx | 2 +- docs/guides/binary/blob-to-arraybuffer.mdx | 2 +- docs/guides/binary/blob-to-dataview.mdx | 2 +- docs/guides/binary/blob-to-stream.mdx | 2 +- docs/guides/binary/blob-to-string.mdx | 2 +- docs/guides/binary/blob-to-typedarray.mdx | 2 +- docs/guides/binary/buffer-to-arraybuffer.mdx | 2 +- docs/guides/binary/buffer-to-blob.mdx | 2 +- .../binary/buffer-to-readablestream.mdx | 2 +- docs/guides/binary/buffer-to-string.mdx | 2 +- docs/guides/binary/buffer-to-typedarray.mdx | 2 +- docs/guides/binary/dataview-to-string.mdx | 2 +- .../binary/typedarray-to-arraybuffer.mdx | 2 +- docs/guides/binary/typedarray-to-blob.mdx | 2 +- docs/guides/binary/typedarray-to-buffer.mdx | 2 +- docs/guides/binary/typedarray-to-dataview.mdx | 2 +- .../binary/typedarray-to-readablestream.mdx | 2 +- docs/guides/binary/typedarray-to-string.mdx | 2 +- docs/guides/deployment/vercel.mdx | 2 - docs/guides/ecosystem/discordjs.mdx | 4 +- docs/guides/ecosystem/express.mdx | 4 +- docs/guides/ecosystem/mongoose.mdx | 2 +- docs/guides/ecosystem/neon-drizzle.mdx | 4 +- .../ecosystem/neon-serverless-postgres.mdx | 4 +- docs/guides/ecosystem/prisma-postgres.mdx | 2 +- docs/guides/ecosystem/prisma.mdx | 2 +- docs/guides/ecosystem/qwik.mdx | 8 +- docs/guides/ecosystem/sentry.mdx | 4 +- docs/guides/ecosystem/sveltekit.mdx | 2 +- docs/guides/ecosystem/tanstack-start.mdx | 1 - docs/guides/ecosystem/upstash.mdx | 2 +- docs/guides/ecosystem/vite.mdx | 2 +- docs/guides/html-rewriter/extract-links.mdx | 4 +- .../html-rewriter/extract-social-meta.mdx | 2 +- docs/guides/http/file-uploads.mdx | 2 +- docs/guides/http/hot.mdx | 2 +- docs/guides/http/server.mdx | 2 +- docs/guides/http/simple.mdx | 2 +- docs/guides/http/stream-file.mdx | 6 +- docs/guides/http/tls.mdx | 2 +- docs/guides/install/add-dev.mdx | 2 +- docs/guides/install/add-git.mdx | 2 +- docs/guides/install/add-optional.mdx | 2 +- docs/guides/install/add-peer.mdx | 2 +- docs/guides/install/add-tarball.mdx | 2 +- docs/guides/install/add.mdx | 2 +- docs/guides/install/azure-artifacts.mdx | 6 +- docs/guides/install/custom-registry.mdx | 4 +- docs/guides/install/git-diff-bun-lockfile.mdx | 3 +- docs/guides/install/npm-alias.mdx | 2 +- docs/guides/install/registry-scope.mdx | 6 +- docs/guides/install/trusted.mdx | 2 +- docs/guides/install/workspaces.mdx | 2 +- docs/guides/install/yarnlock.mdx | 7 +- docs/guides/process/ctrl-c.mdx | 2 +- docs/guides/process/ipc.mdx | 4 +- docs/guides/process/nanoseconds.mdx | 2 +- docs/guides/process/os-signals.mdx | 2 +- docs/guides/process/spawn-stderr.mdx | 4 +- docs/guides/process/spawn-stdout.mdx | 4 +- docs/guides/process/spawn.mdx | 4 +- docs/guides/process/stdin.mdx | 2 +- docs/guides/read-file/arraybuffer.mdx | 4 +- docs/guides/read-file/buffer.mdx | 2 +- docs/guides/read-file/exists.mdx | 2 +- docs/guides/read-file/mime.mdx | 2 +- docs/guides/read-file/stream.mdx | 2 +- docs/guides/read-file/uint8array.mdx | 2 +- docs/guides/read-file/watch.mdx | 2 +- docs/guides/runtime/cicd.mdx | 2 +- docs/guides/runtime/delete-directory.mdx | 2 +- docs/guides/runtime/delete-file.mdx | 2 +- docs/guides/runtime/import-json.mdx | 2 +- docs/guides/runtime/import-toml.mdx | 2 +- docs/guides/runtime/import-yaml.mdx | 2 +- docs/guides/runtime/read-env.mdx | 2 +- docs/guides/runtime/set-env.mdx | 4 +- docs/guides/runtime/shell.mdx | 2 +- docs/guides/runtime/tsconfig-paths.mdx | 2 +- docs/guides/runtime/typescript.mdx | 2 +- docs/guides/runtime/vscode-debugger.mdx | 3 +- docs/guides/runtime/web-debugger.mdx | 2 +- docs/guides/streams/to-array.mdx | 2 +- docs/guides/streams/to-arraybuffer.mdx | 2 +- docs/guides/streams/to-blob.mdx | 2 +- docs/guides/streams/to-buffer.mdx | 2 +- docs/guides/streams/to-json.mdx | 2 +- docs/guides/streams/to-string.mdx | 2 +- docs/guides/streams/to-typedarray.mdx | 2 +- docs/guides/test/bail.mdx | 2 +- docs/guides/test/concurrent-test-glob.mdx | 7 +- docs/guides/test/coverage-threshold.mdx | 2 +- docs/guides/test/coverage.mdx | 2 +- docs/guides/test/happy-dom.mdx | 2 +- docs/guides/test/migrate-from-jest.mdx | 10 +- docs/guides/test/mock-clock.mdx | 4 +- docs/guides/test/mock-functions.mdx | 2 +- docs/guides/test/rerun-each.mdx | 2 +- docs/guides/test/run-tests.mdx | 4 +- docs/guides/test/skip-tests.mdx | 2 +- docs/guides/test/snapshot.mdx | 4 +- docs/guides/test/spy-on.mdx | 2 +- docs/guides/test/svelte-test.mdx | 6 +- docs/guides/test/testing-library.mdx | 6 +- docs/guides/test/timeout.mdx | 2 +- docs/guides/test/todo-tests.mdx | 2 +- docs/guides/test/update-snapshots.mdx | 2 +- docs/guides/test/watch-mode.mdx | 2 +- docs/guides/util/base64.mdx | 2 +- docs/guides/util/deep-equals.mdx | 6 +- docs/guides/util/deflate.mdx | 2 +- docs/guides/util/entrypoint.mdx | 4 +- docs/guides/util/escape-html.mdx | 2 +- docs/guides/util/file-url-to-path.mdx | 2 +- docs/guides/util/gzip.mdx | 2 +- docs/guides/util/hash-a-password.mdx | 2 +- docs/guides/util/import-meta-dir.mdx | 4 +- docs/guides/util/import-meta-file.mdx | 4 +- docs/guides/util/import-meta-path.mdx | 4 +- docs/guides/util/javascript-uuid.mdx | 4 +- docs/guides/util/main.mdx | 2 +- docs/guides/util/path-to-file-url.mdx | 2 +- docs/guides/util/sleep.mdx | 2 +- docs/guides/util/version.mdx | 2 +- .../util/which-path-to-executable-bin.mdx | 2 +- docs/guides/websocket/context.mdx | 2 +- docs/guides/websocket/simple.mdx | 2 +- docs/guides/write-file/basic.mdx | 4 +- docs/guides/write-file/blob.mdx | 4 +- docs/guides/write-file/cat.mdx | 4 +- docs/guides/write-file/file-cp.mdx | 4 +- docs/guides/write-file/filesink.mdx | 2 +- docs/guides/write-file/response.mdx | 4 +- docs/guides/write-file/stdout.mdx | 4 +- docs/guides/write-file/stream.mdx | 4 +- docs/guides/write-file/unlink.mdx | 2 +- docs/index.mdx | 2 +- docs/installation.mdx | 2 +- docs/normalize-internal-links.js | 1 - docs/pm/bunx.mdx | 4 +- docs/pm/cli/install.mdx | 2 +- docs/pm/cli/link.mdx | 4 +- docs/pm/cli/outdated.mdx | 6 +- docs/pm/workspaces.mdx | 3 +- docs/project/building-windows.mdx | 2 +- docs/project/license.mdx | 2 +- docs/quickstart.mdx | 4 +- docs/runtime/bunfig.mdx | 28 +++++- docs/runtime/c-compiler.mdx | 4 +- docs/runtime/child-process.mdx | 2 +- docs/runtime/cookies.mdx | 38 ++++---- docs/runtime/debugger.mdx | 2 +- docs/runtime/environment-variables.mdx | 12 +-- docs/runtime/http/routing.mdx | 2 +- docs/runtime/http/server.mdx | 10 +- docs/runtime/http/websockets.mdx | 2 +- docs/runtime/index.mdx | 4 +- docs/runtime/module-resolution.mdx | 4 +- docs/runtime/networking/fetch.mdx | 2 +- docs/runtime/nodejs-compat.mdx | 2 +- docs/runtime/s3.mdx | 6 +- docs/runtime/secrets.mdx | 17 ++-- docs/runtime/sql.mdx | 20 ++-- docs/runtime/sqlite.mdx | 19 +++- docs/runtime/templating/create.mdx | 8 +- docs/runtime/transpiler.mdx | 2 +- docs/runtime/utils.mdx | 4 +- docs/runtime/watch-mode.mdx | 2 +- docs/runtime/workers.mdx | 22 +---- docs/snippets/cli/publish.mdx | 4 +- docs/snippets/guides.jsx | 4 + docs/snippets/product-card.mdx | 32 ------- docs/snippets/product-tiles.mdx | 94 ------------------- docs/test/index.mdx | 2 +- docs/test/snapshots.mdx | 6 +- docs/typescript.mdx | 4 +- packages/bun-release/src/npm/install.ts | 2 +- packages/bun-types/.gitignore | 3 +- packages/bun-types/package.json | 3 +- packages/bun-types/scripts/build.ts | 30 +++++- src/bun.js/bindings/v8/CLAUDE.md | 6 +- src/cli.zig | 2 +- src/cli/install.sh | 2 +- src/cli/upgrade_command.zig | 2 +- src/init/rule.md | 6 +- .../PackageManager/CommandLineArguments.zig | 2 +- src/install/windows-shim/bun_shim_impl.zig | 2 +- .../components/Footer.jsx | 6 +- .../react-spa-no-tailwind/components/Hero.jsx | 2 +- test/cli/create/tailwind.tsx | 2 +- 205 files changed, 429 insertions(+), 504 deletions(-) delete mode 100644 docs/snippets/product-card.mdx delete mode 100644 docs/snippets/product-tiles.mdx diff --git a/README.md b/README.md index 3c845722d1..82c0626611 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Bun supports Linux (x64 & arm64), macOS (x64 & Apple Silicon) and Windows (x64). curl -fsSL https://bun.com/install | bash # on windows -powershell -c "irm bun.com/install.ps1 | iex" +powershell -c "irm bun.sh/install.ps1 | iex" # with npm npm install -g bun @@ -104,13 +104,13 @@ bun upgrade --canary - [File types (Loaders)](https://bun.com/docs/runtime/loaders) - [TypeScript](https://bun.com/docs/runtime/typescript) - [JSX](https://bun.com/docs/runtime/jsx) - - [Environment variables](https://bun.com/docs/runtime/env) + - [Environment variables](https://bun.com/docs/runtime/environment-variables) - [Bun APIs](https://bun.com/docs/runtime/bun-apis) - [Web APIs](https://bun.com/docs/runtime/web-apis) - - [Node.js compatibility](https://bun.com/docs/runtime/nodejs-apis) + - [Node.js compatibility](https://bun.com/docs/runtime/nodejs-compat) - [Single-file executable](https://bun.com/docs/bundler/executables) - [Plugins](https://bun.com/docs/runtime/plugins) - - [Watch mode / Hot Reloading](https://bun.com/docs/runtime/hot) + - [Watch mode / Hot Reloading](https://bun.com/docs/runtime/watch-mode) - [Module resolution](https://bun.com/docs/runtime/modules) - [Auto-install](https://bun.com/docs/runtime/autoimport) - [bunfig.toml](https://bun.com/docs/runtime/bunfig) diff --git a/docs/bundler/css.mdx b/docs/bundler/css.mdx index 730332f173..7a73280b79 100644 --- a/docs/bundler/css.mdx +++ b/docs/bundler/css.mdx @@ -72,7 +72,7 @@ Bun's CSS bundler automatically converts this nested syntax into traditional fla You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns: -```css title="styles.css" icon="file-code" +```scss title="styles.css" icon="file-code" .responsive-element { display: block; diff --git a/docs/bundler/executables.mdx b/docs/bundler/executables.mdx index fefa2769e6..71b3386a4b 100644 --- a/docs/bundler/executables.mdx +++ b/docs/bundler/executables.mdx @@ -118,7 +118,7 @@ These constants are embedded directly into your compiled binary at build time, p For comprehensive examples and advanced patterns, see the [Build-time constants - guide](https://bun.com/guides/runtime/build-time-constants). + guide](/guides/runtime/build-time-constants). --- @@ -288,12 +288,12 @@ console.log(`Server running at http://localhost:${server.port}`);

Hello World

- + ``` -```ts app.js icon="file-code" +```ts app.ts icon="file-code" console.log("Hello from the client!"); ``` diff --git a/docs/bundler/fullstack.mdx b/docs/bundler/fullstack.mdx index 937d41d39f..6b108d9866 100644 --- a/docs/bundler/fullstack.mdx +++ b/docs/bundler/fullstack.mdx @@ -632,7 +632,7 @@ const server = serve({ console.log(`🚀 Server running on ${server.url}`); ``` -```html title="public/index.html" +```html title="public/index.html" icon="file-code" @@ -757,7 +757,7 @@ export function App() { } ``` -```css title="src/styles.css" +```css title="src/styles.css" icon="file-code" * { margin: 0; padding: 0; @@ -999,7 +999,7 @@ CMD ["bun", "index.js"] ### Environment Variables -```bash title=".env.production" icon="file-code" +```ini title=".env.production" icon="file-code" NODE_ENV=production PORT=3000 DATABASE_URL=postgresql://user:pass@localhost:5432/myapp diff --git a/docs/bundler/hot-reloading.mdx b/docs/bundler/hot-reloading.mdx index 3e82484220..0204191d50 100644 --- a/docs/bundler/hot-reloading.mdx +++ b/docs/bundler/hot-reloading.mdx @@ -9,7 +9,7 @@ Hot Module Replacement (HMR) allows you to update modules in a running applicati ## `import.meta.hot` API Reference -Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production. +Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vite.dev/guide/api-hmr). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production. ```ts title="index.ts" icon="/icons/typescript.svg" if (import.meta.hot) { @@ -144,7 +144,7 @@ Indicates that multiple dependencies' modules can be accepted. This variant acce `import.meta.hot.data` maintains state between module instances during hot replacement, enabling data transfer from previous to new versions. When `import.meta.hot.data` is written into, Bun will also mark this module as capable of self-accepting (equivalent of calling `import.meta.hot.accept()`). -```jsx title="index.ts" icon="/icons/typescript.svg" +```tsx title="index.tsx" icon="/icons/typescript.svg" import { createRoot } from "react-dom/client"; import { App } from "./app"; diff --git a/docs/bundler/html-static.mdx b/docs/bundler/html-static.mdx index 135b7f41ac..5ae8d10653 100644 --- a/docs/bundler/html-static.mdx +++ b/docs/bundler/html-static.mdx @@ -164,7 +164,7 @@ For example: } ``` -```css abc.css +```css abc.css icon="file-code" body { background-color: red; } @@ -174,7 +174,7 @@ body { This outputs: -```css +```css styles.css icon="file-code" body { background-color: red; } @@ -385,7 +385,8 @@ All paths are resolved relative to your HTML file, making it easy to organize yo - Need more configuration options for things like asset handling - Need a way to configure CORS, headers, etc. -If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/bun.js/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point. +{/* todo: find the correct link to link to as this 404's and there isn't any similar files */} +{/* If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/bun.js/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point. */} diff --git a/docs/bundler/index.mdx b/docs/bundler/index.mdx index ce0736b746..5a6743318c 100644 --- a/docs/bundler/index.mdx +++ b/docs/bundler/index.mdx @@ -106,7 +106,7 @@ For each file specified in `entrypoints`, Bun will generate a new bundle. This b The contents of `out/index.js` will look something like this: -```ts title="out/index.js" icon="/icons/javascript.svg" +```js title="out/index.js" icon="/icons/javascript.svg" // out/index.js // ... // ~20k lines of code @@ -527,7 +527,7 @@ Injects environment variables into the bundled output by converting `process.env For the input below: -```ts title="input.js" icon="/icons/javascript.svg" +```js title="input.js" icon="/icons/javascript.svg" // input.js console.log(process.env.FOO); console.log(process.env.BAZ); @@ -535,7 +535,7 @@ console.log(process.env.BAZ); The generated bundle will contain the following code: -```ts title="output.js" icon="/icons/javascript.svg" +```js title="output.js" icon="/icons/javascript.svg" // output.js console.log("bar"); console.log("123"); @@ -580,7 +580,7 @@ console.log(process.env.BAZ); The generated bundle will contain the following code: -```ts title="output.js" icon="/icons/javascript.svg" +```js title="output.js" icon="/icons/javascript.svg" console.log(process.env.FOO); console.log("https://acme.com"); console.log(process.env.BAZ); @@ -722,7 +722,7 @@ Normally, bundling `index.tsx` would generate a bundle containing the entire sou The generated bundle will look something like this: -```ts title="out/index.js" icon="/icons/javascript.svg" +```js title="out/index.js" icon="/icons/javascript.svg" import { z } from "zod"; // ... @@ -1026,7 +1026,7 @@ Setting `publicPath` will prefix all file paths with the specified value. The output file would now look something like this. -```ts title="out/index.js" icon="/icons/javascript.svg" +```js title="out/index.js" icon="/icons/javascript.svg" var logo = "https://cdn.example.com/logo-a7305bdef.svg"; ``` @@ -1356,10 +1356,12 @@ interface BuildConfig { * JSX configuration object for controlling JSX transform behavior */ jsx?: { + runtime?: "automatic" | "classic"; + importSource?: string; factory?: string; fragment?: string; - importSource?: string; - runtime?: "automatic" | "classic"; + sideEffects?: boolean; + development?: boolean; }; naming?: | string @@ -1443,13 +1445,20 @@ interface BuildConfig { drop?: string[]; /** - * When set to `true`, the returned promise rejects with an AggregateError when a build failure happens. - * When set to `false`, the `success` property of the returned object will be `false` when a build failure happens. + * - When set to `true`, the returned promise rejects with an AggregateError when a build failure happens. + * - When set to `false`, returns a {@link BuildOutput} with `{success: false}` * - * This defaults to `false` in Bun 1.1 and will change to `true` in Bun 1.2 - * as most usage of `Bun.build` forgets to check for errors. + * @default true */ throw?: boolean; + + /** + * Custom tsconfig.json file path to use for path resolution. + * Equivalent to `--tsconfig-override` in the CLI. + */ + tsconfig?: string; + + outdir?: string; } interface BuildOutput { diff --git a/docs/bundler/loaders.mdx b/docs/bundler/loaders.mdx index bb55a49675..daa612dc7c 100644 --- a/docs/bundler/loaders.mdx +++ b/docs/bundler/loaders.mdx @@ -312,7 +312,7 @@ The `html` loader processes HTML files and bundles any referenced assets. It wil For example, given this HTML file: -```html title="src/index.html" +```html title="src/index.html" icon="file-code" @@ -325,7 +325,7 @@ For example, given this HTML file: It will output a new HTML file with the bundled assets: -```html title="dist/index.html" +```html title="dist/index.html" icon="file-code" diff --git a/docs/bundler/macros.mdx b/docs/bundler/macros.mdx index e4cfffeb37..23dd41c802 100644 --- a/docs/bundler/macros.mdx +++ b/docs/bundler/macros.mdx @@ -87,7 +87,7 @@ macro(); When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment. -```json title="package.json" icon="file-code" +```json title="package.json" icon="file-json" { "name": "my-package", "exports": { diff --git a/docs/docs.json b/docs/docs.json index 2ad4eae8a2..4bec7db641 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -456,7 +456,6 @@ "pages": [ "/guides/test/run-tests", "/guides/test/watch-mode", - "/guides/test/concurrent-test-glob", "/guides/test/migrate-from-jest", "/guides/test/mock-functions", "/guides/test/spy-on", @@ -465,6 +464,7 @@ "/guides/test/update-snapshots", "/guides/test/coverage", "/guides/test/coverage-threshold", + "/guides/test/concurrent-test-glob", "/guides/test/skip-tests", "/guides/test/todo-tests", "/guides/test/timeout", diff --git a/docs/feedback.mdx b/docs/feedback.mdx index 710cb89b7a..fdb2f298b6 100644 --- a/docs/feedback.mdx +++ b/docs/feedback.mdx @@ -4,13 +4,9 @@ description: Share feedback, bug reports, and feature requests mode: center --- -import Feedback from "/snippets/cli/feedback.mdx"; - Whether you've found a bug, have a performance issue, or just want to suggest an improvement, here's how you can open a helpful issue: - - For general questions, please join our [Discord](https://discord.com/invite/CXdq2DP29u). - +For general questions, please join our [Discord](https://bun.com/discord). ## Reporting Issues @@ -56,9 +52,7 @@ Whether you've found a bug, have a performance issue, or just want to suggest an - For MacOS and Linux: copy the output of `uname -mprs` - For Windows: copy the output of this command in the powershell console: - ```powershell - "$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })" - ``` + `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` @@ -79,7 +73,3 @@ echo "please document X" | bun feedback --email you@example.com ``` You can provide feedback as text arguments, file paths, or piped input. - ---- - - diff --git a/docs/guides/binary/arraybuffer-to-array.mdx b/docs/guides/binary/arraybuffer-to-array.mdx index 6141431fae..bce60089f6 100644 --- a/docs/guides/binary/arraybuffer-to-array.mdx +++ b/docs/guides/binary/arraybuffer-to-array.mdx @@ -26,4 +26,4 @@ const regularArr = Array.from(uintArr); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/arraybuffer-to-blob.mdx b/docs/guides/binary/arraybuffer-to-blob.mdx index d3b4bef160..74c59f3dae 100644 --- a/docs/guides/binary/arraybuffer-to-blob.mdx +++ b/docs/guides/binary/arraybuffer-to-blob.mdx @@ -23,4 +23,4 @@ blob.type; // => "application/octet-stream" --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/arraybuffer-to-buffer.mdx b/docs/guides/binary/arraybuffer-to-buffer.mdx index ba448c7456..b2c6f282a4 100644 --- a/docs/guides/binary/arraybuffer-to-buffer.mdx +++ b/docs/guides/binary/arraybuffer-to-buffer.mdx @@ -24,4 +24,4 @@ const nodeBuffer = Buffer.from(arrBuffer, 0, 16); // view first 16 bytes --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/arraybuffer-to-string.mdx b/docs/guides/binary/arraybuffer-to-string.mdx index 67d8559f93..d488baa515 100644 --- a/docs/guides/binary/arraybuffer-to-string.mdx +++ b/docs/guides/binary/arraybuffer-to-string.mdx @@ -14,4 +14,4 @@ const str = decoder.decode(buf); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/arraybuffer-to-typedarray.mdx b/docs/guides/binary/arraybuffer-to-typedarray.mdx index 194c26c05b..9293568b5e 100644 --- a/docs/guides/binary/arraybuffer-to-typedarray.mdx +++ b/docs/guides/binary/arraybuffer-to-typedarray.mdx @@ -38,4 +38,4 @@ const arr = new Uint8Array(buffer, 0, 16); // view first 16 bytes --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/binary/blob-to-arraybuffer.mdx b/docs/guides/binary/blob-to-arraybuffer.mdx index 6edfa274c1..d4e5edfaba 100644 --- a/docs/guides/binary/blob-to-arraybuffer.mdx +++ b/docs/guides/binary/blob-to-arraybuffer.mdx @@ -13,4 +13,4 @@ const buf = await blob.arrayBuffer(); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/blob-to-dataview.mdx b/docs/guides/binary/blob-to-dataview.mdx index 9b9a77d281..0db0ff26c4 100644 --- a/docs/guides/binary/blob-to-dataview.mdx +++ b/docs/guides/binary/blob-to-dataview.mdx @@ -13,4 +13,4 @@ const arr = new DataView(await blob.arrayBuffer()); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/blob-to-stream.mdx b/docs/guides/binary/blob-to-stream.mdx index bd433d7fb2..1a746b83b8 100644 --- a/docs/guides/binary/blob-to-stream.mdx +++ b/docs/guides/binary/blob-to-stream.mdx @@ -13,4 +13,4 @@ const stream = await blob.stream(); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/blob-to-string.mdx b/docs/guides/binary/blob-to-string.mdx index af1bb71bc3..39e1898e41 100644 --- a/docs/guides/binary/blob-to-string.mdx +++ b/docs/guides/binary/blob-to-string.mdx @@ -14,4 +14,4 @@ const str = await blob.text(); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/blob-to-typedarray.mdx b/docs/guides/binary/blob-to-typedarray.mdx index 80bfb2f4c4..8453e7cb9e 100644 --- a/docs/guides/binary/blob-to-typedarray.mdx +++ b/docs/guides/binary/blob-to-typedarray.mdx @@ -13,4 +13,4 @@ const arr = new Uint8Array(await blob.arrayBuffer()); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/buffer-to-arraybuffer.mdx b/docs/guides/binary/buffer-to-arraybuffer.mdx index 57c5c13608..549e88f8e8 100644 --- a/docs/guides/binary/buffer-to-arraybuffer.mdx +++ b/docs/guides/binary/buffer-to-arraybuffer.mdx @@ -13,4 +13,4 @@ const arrBuf = nodeBuf.buffer; --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/buffer-to-blob.mdx b/docs/guides/binary/buffer-to-blob.mdx index 325bb457f7..f108221118 100644 --- a/docs/guides/binary/buffer-to-blob.mdx +++ b/docs/guides/binary/buffer-to-blob.mdx @@ -13,4 +13,4 @@ const blob = new Blob([buf]); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/buffer-to-readablestream.mdx b/docs/guides/binary/buffer-to-readablestream.mdx index 6456dd9792..5765fcf965 100644 --- a/docs/guides/binary/buffer-to-readablestream.mdx +++ b/docs/guides/binary/buffer-to-readablestream.mdx @@ -40,4 +40,4 @@ const stream = blob.stream(1024); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/buffer-to-string.mdx b/docs/guides/binary/buffer-to-string.mdx index cf761d54a4..32ba943f7e 100644 --- a/docs/guides/binary/buffer-to-string.mdx +++ b/docs/guides/binary/buffer-to-string.mdx @@ -24,4 +24,4 @@ const str = buf.toString("utf8", 0, 5); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/buffer-to-typedarray.mdx b/docs/guides/binary/buffer-to-typedarray.mdx index 6a9678c2cb..2d34841092 100644 --- a/docs/guides/binary/buffer-to-typedarray.mdx +++ b/docs/guides/binary/buffer-to-typedarray.mdx @@ -13,4 +13,4 @@ buf instanceof Uint8Array; // => true --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/dataview-to-string.mdx b/docs/guides/binary/dataview-to-string.mdx index a1147cf2d6..db2c60051c 100644 --- a/docs/guides/binary/dataview-to-string.mdx +++ b/docs/guides/binary/dataview-to-string.mdx @@ -14,4 +14,4 @@ const str = decoder.decode(dv); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-arraybuffer.mdx b/docs/guides/binary/typedarray-to-arraybuffer.mdx index a746d2007f..1be382e5b5 100644 --- a/docs/guides/binary/typedarray-to-arraybuffer.mdx +++ b/docs/guides/binary/typedarray-to-arraybuffer.mdx @@ -24,4 +24,4 @@ arr.byteLength; // => 32 --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-blob.mdx b/docs/guides/binary/typedarray-to-blob.mdx index 5ac00a6779..afa9c33b1c 100644 --- a/docs/guides/binary/typedarray-to-blob.mdx +++ b/docs/guides/binary/typedarray-to-blob.mdx @@ -15,4 +15,4 @@ console.log(await blob.text()); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-buffer.mdx b/docs/guides/binary/typedarray-to-buffer.mdx index daa782a159..e9bdd2054e 100644 --- a/docs/guides/binary/typedarray-to-buffer.mdx +++ b/docs/guides/binary/typedarray-to-buffer.mdx @@ -13,4 +13,4 @@ const buf = Buffer.from(arr); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-dataview.mdx b/docs/guides/binary/typedarray-to-dataview.mdx index 3a7603e74b..5822d7ef19 100644 --- a/docs/guides/binary/typedarray-to-dataview.mdx +++ b/docs/guides/binary/typedarray-to-dataview.mdx @@ -13,4 +13,4 @@ const dv = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-readablestream.mdx b/docs/guides/binary/typedarray-to-readablestream.mdx index dec46b02d9..4871ab1b52 100644 --- a/docs/guides/binary/typedarray-to-readablestream.mdx +++ b/docs/guides/binary/typedarray-to-readablestream.mdx @@ -40,4 +40,4 @@ const stream = blob.stream(1024); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/binary/typedarray-to-string.mdx b/docs/guides/binary/typedarray-to-string.mdx index 5e6e5726a0..86ab4c4b50 100644 --- a/docs/guides/binary/typedarray-to-string.mdx +++ b/docs/guides/binary/typedarray-to-string.mdx @@ -15,4 +15,4 @@ const str = decoder.decode(arr); --- -See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun. +See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun. diff --git a/docs/guides/deployment/vercel.mdx b/docs/guides/deployment/vercel.mdx index ff4ea58202..8fd052b6ff 100644 --- a/docs/guides/deployment/vercel.mdx +++ b/docs/guides/deployment/vercel.mdx @@ -4,8 +4,6 @@ sidebarTitle: Deploy on Vercel mode: center --- -import { ProductCard } from "/snippets/product-card.mdx"; - [Vercel](https://vercel.com/) is a cloud platform that lets you build, deploy, and scale your apps. diff --git a/docs/guides/ecosystem/discordjs.mdx b/docs/guides/ecosystem/discordjs.mdx index 7ab36e2727..9e5869fa18 100644 --- a/docs/guides/ecosystem/discordjs.mdx +++ b/docs/guides/ecosystem/discordjs.mdx @@ -22,7 +22,7 @@ bun add discord.js --- -Before we go further, we need to go to the [Discord developer portal](https://discord.com/developers/applications), login/signup, create a new _Application_, then create a new _Bot_ within that application. Follow the [official guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot) for step-by-step instructions. +Before we go further, we need to go to the [Discord developer portal](https://discord.com/developers/applications), login/signup, create a new _Application_, then create a new _Bot_ within that application. Follow the [official guide](https://discordjs.guide/legacy/preparations/app-setup#creating-your-bot) for step-by-step instructions. --- @@ -30,7 +30,7 @@ Once complete, you'll be presented with your bot's _private key_. Let's add this This is an example token that has already been invalidated. -```txt .env.local icon="settings" +```ini .env.local icon="settings" DISCORD_TOKEN=NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I ``` diff --git a/docs/guides/ecosystem/express.mdx b/docs/guides/ecosystem/express.mdx index e61fe1fbf3..0257f030d6 100644 --- a/docs/guides/ecosystem/express.mdx +++ b/docs/guides/ecosystem/express.mdx @@ -7,8 +7,8 @@ mode: center Express and other major Node.js HTTP libraries should work out of the box. Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on. - Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed - compatibility information. + Refer to the [Runtime > Node.js APIs](/runtime/nodejs-compat#node-http) page for more detailed compatibility + information. ```sh terminal icon="terminal" diff --git a/docs/guides/ecosystem/mongoose.mdx b/docs/guides/ecosystem/mongoose.mdx index 3555838789..01d3d2fb83 100644 --- a/docs/guides/ecosystem/mongoose.mdx +++ b/docs/guides/ecosystem/mongoose.mdx @@ -89,4 +89,4 @@ Moo! --- -This is a simple introduction to using Mongoose with TypeScript and Bun. As you build your application, refer to the official [MongoDB](https://docs.mongodb.com/) and [Mongoose](https://mongoosejs.com/docs/) sites for complete documentation. +This is a simple introduction to using Mongoose with TypeScript and Bun. As you build your application, refer to the official [MongoDB](https://www.mongodb.com/docs) and [Mongoose](https://mongoosejs.com/docs/) sites for complete documentation. diff --git a/docs/guides/ecosystem/neon-drizzle.mdx b/docs/guides/ecosystem/neon-drizzle.mdx index 5cef85e7f6..f33f7d3b94 100644 --- a/docs/guides/ecosystem/neon-drizzle.mdx +++ b/docs/guides/ecosystem/neon-drizzle.mdx @@ -20,7 +20,7 @@ bun add -D drizzle-kit Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. -```txt .env.local icon="settings" +```ini .env.local icon="settings" DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` @@ -33,7 +33,7 @@ import { neon } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-http"; // Bun automatically loads the DATABASE_URL from .env.local -// Refer to: https://bun.com/docs/runtime/env for more information +// Refer to: https://bun.com/docs/runtime/environment-variables for more information const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql); diff --git a/docs/guides/ecosystem/neon-serverless-postgres.mdx b/docs/guides/ecosystem/neon-serverless-postgres.mdx index 84cd4d870e..a081f37f6d 100644 --- a/docs/guides/ecosystem/neon-serverless-postgres.mdx +++ b/docs/guides/ecosystem/neon-serverless-postgres.mdx @@ -21,7 +21,7 @@ bun add @neondatabase/serverless Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. -```sh .env.local icon="settings" +```ini .env.local icon="settings" DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` @@ -33,7 +33,7 @@ Paste the following code into your project's `index.ts` file. import { neon } from "@neondatabase/serverless"; // Bun automatically loads the DATABASE_URL from .env.local -// Refer to: https://bun.com/docs/runtime/env for more information +// Refer to: https://bun.com/docs/runtime/environment-variables for more information const sql = neon(process.env.DATABASE_URL); const rows = await sql`SELECT version()`; diff --git a/docs/guides/ecosystem/prisma-postgres.mdx b/docs/guides/ecosystem/prisma-postgres.mdx index d8bc15ca45..fb0133e30f 100644 --- a/docs/guides/ecosystem/prisma-postgres.mdx +++ b/docs/guides/ecosystem/prisma-postgres.mdx @@ -62,7 +62,7 @@ mode: center Set up your Postgres database URL in the `.env` file. - ```env .env icon="settings" + ```ini .env icon="settings" DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public" ``` diff --git a/docs/guides/ecosystem/prisma.mdx b/docs/guides/ecosystem/prisma.mdx index a762159423..f586b654a4 100644 --- a/docs/guides/ecosystem/prisma.mdx +++ b/docs/guides/ecosystem/prisma.mdx @@ -161,4 +161,4 @@ mode: center --- -That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client) as you continue to develop your application. +That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/orm/prisma-client) as you continue to develop your application. diff --git a/docs/guides/ecosystem/qwik.mdx b/docs/guides/ecosystem/qwik.mdx index 3a5f38695c..1c3cdd8192 100644 --- a/docs/guides/ecosystem/qwik.mdx +++ b/docs/guides/ecosystem/qwik.mdx @@ -65,14 +65,14 @@ bun create qwik │ bun qwik add │ │ │ │ Relevant docs: │ -│ https://qwik.builder.io/docs/getting-started/ │ +│ https://qwik.dev/docs/getting-started/ │ │ │ │ Questions? Start the conversation at: │ -│ https://qwik.builder.io/chat │ +│ https://qwik.dev/chat │ │ https://twitter.com/QwikDev │ │ │ │ Presentations, Podcasts and Videos: │ -│ https://qwik.builder.io/media/ │ +│ https://qwik.dev/media/ │ │ │ │ Next steps: │ │ cd my-app │ @@ -111,4 +111,4 @@ Open [http://localhost:5173](http://localhost:5173) with your browser to see the --- -Refer to the [Qwik docs](https://qwik.builder.io/docs/getting-started/) for complete documentation. +Refer to the [Qwik docs](https://qwik.dev/docs/getting-started/) for complete documentation. diff --git a/docs/guides/ecosystem/sentry.mdx b/docs/guides/ecosystem/sentry.mdx index a7bc1353cd..8f515b9d75 100644 --- a/docs/guides/ecosystem/sentry.mdx +++ b/docs/guides/ecosystem/sentry.mdx @@ -20,7 +20,7 @@ bun add @sentry/bun Then, initialize the Sentry SDK with your Sentry DSN in your app's entry file. You can find your DSN in your Sentry project settings. -```js sentry.ts icon="/icons/typescript.svg" +```ts sentry.ts icon="/icons/typescript.svg" import * as Sentry from "@sentry/bun"; // Ensure to call this before importing any other modules! @@ -37,7 +37,7 @@ Sentry.init({ You can verify that Sentry is working by capturing a test error: -```js sentry.ts icon="/icons/typescript.svg" +```ts sentry.ts icon="/icons/typescript.svg" setTimeout(() => { try { foo(); diff --git a/docs/guides/ecosystem/sveltekit.mdx b/docs/guides/ecosystem/sveltekit.mdx index 9b0d892d38..f886e335ab 100644 --- a/docs/guides/ecosystem/sveltekit.mdx +++ b/docs/guides/ecosystem/sveltekit.mdx @@ -88,7 +88,7 @@ To build for production, you'll need to add the right SvelteKit adapter. Current Now, make the following changes to your `svelte.config.js`. -```ts svelte.config.js icon="file-code" +```js svelte.config.js icon="file-code" import adapter from "@sveltejs/adapter-auto"; // [!code --] import adapter from "svelte-adapter-bun"; // [!code ++] import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; diff --git a/docs/guides/ecosystem/tanstack-start.mdx b/docs/guides/ecosystem/tanstack-start.mdx index 2071671a36..a84a385627 100644 --- a/docs/guides/ecosystem/tanstack-start.mdx +++ b/docs/guides/ecosystem/tanstack-start.mdx @@ -98,7 +98,6 @@ To host your TanStack Start app, you can use [Nitro](https://nitro.build/) or a } ``` - You do **not** need the custom `start` script when deploying to Vercel. diff --git a/docs/guides/ecosystem/upstash.mdx b/docs/guides/ecosystem/upstash.mdx index d28680b0a9..d7e8f1301e 100644 --- a/docs/guides/ecosystem/upstash.mdx +++ b/docs/guides/ecosystem/upstash.mdx @@ -34,7 +34,7 @@ mode: center Set the `REDIS_URL` environment variable in your `.env` file using the Redis endpoint (not the REST URL): - ```env .env icon="settings" + ```ini .env icon="settings" REDIS_URL=rediss://********@********.upstash.io:6379 ``` diff --git a/docs/guides/ecosystem/vite.mdx b/docs/guides/ecosystem/vite.mdx index 64f3c89f3d..938432b2de 100644 --- a/docs/guides/ecosystem/vite.mdx +++ b/docs/guides/ecosystem/vite.mdx @@ -74,4 +74,4 @@ bunx --bun vite build --- -This is a stripped down guide to get you started with Vite + Bun. For more information, see the [Vite documentation](https://vitejs.dev/guide/). +This is a stripped down guide to get you started with Vite + Bun. For more information, see the [Vite documentation](https://vite.dev/guide/). diff --git a/docs/guides/html-rewriter/extract-links.mdx b/docs/guides/html-rewriter/extract-links.mdx index d019c3e5a4..eabeff55ed 100644 --- a/docs/guides/html-rewriter/extract-links.mdx +++ b/docs/guides/html-rewriter/extract-links.mdx @@ -6,7 +6,7 @@ mode: center ## Extract links from a webpage -Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`. +Bun's [HTMLRewriter](/runtime/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`. ```ts extract-links.ts icon="/icons/typescript.svg" async function extractLinks(url: string) { @@ -68,4 +68,4 @@ const websiteLinks = await extractLinksFromURL("https://example.com"); --- -See [Docs > API > HTMLRewriter](https://bun.com/docs/api/html-rewriter) for complete documentation on HTML transformation with Bun. +See [Docs > API > HTMLRewriter](/runtime/html-rewriter) for complete documentation on HTML transformation with Bun. diff --git a/docs/guides/html-rewriter/extract-social-meta.mdx b/docs/guides/html-rewriter/extract-social-meta.mdx index 455c75739d..c4d35ed2c6 100644 --- a/docs/guides/html-rewriter/extract-social-meta.mdx +++ b/docs/guides/html-rewriter/extract-social-meta.mdx @@ -6,7 +6,7 @@ mode: center ## Extract social share images and Open Graph tags -Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process. +Bun's [HTMLRewriter](/runtime/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process. ```ts extract-social-meta.ts icon="/icons/typescript.svg" interface SocialMetadata { diff --git a/docs/guides/http/file-uploads.mdx b/docs/guides/http/file-uploads.mdx index 787487039f..1741914a79 100644 --- a/docs/guides/http/file-uploads.mdx +++ b/docs/guides/http/file-uploads.mdx @@ -63,7 +63,7 @@ Our form will send a `POST` request to the `/action` endpoint with the form data First we use the [`.formData()`](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData) method on the incoming `Request` to asynchronously parse its contents to a `FormData` instance. Then we can use the [`.get()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/get) method to extract the value of the `name` and `profilePicture` fields. Here `name` corresponds to a `string` and `profilePicture` is a `Blob`. -Finally, we write the `Blob` to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). +Finally, we write the `Blob` to disk using [`Bun.write()`](/runtime/file-io#writing-files-bun-write). {/* prettier-ignore */} ```ts index.ts icon="/icons/typescript.svg" diff --git a/docs/guides/http/hot.mdx b/docs/guides/http/hot.mdx index ecc6e04627..fac3d5dcdf 100644 --- a/docs/guides/http/hot.mdx +++ b/docs/guides/http/hot.mdx @@ -4,7 +4,7 @@ sidebarTitle: Hot reload an HTTP server mode: center --- -Bun supports the [`--hot`](https://bun.com/docs/runtime/hot#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file. +Bun supports the [`--hot`](/runtime/watch-mode#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file. ```sh terminal icon="terminal" bun --hot run index.ts diff --git a/docs/guides/http/server.mdx b/docs/guides/http/server.mdx index ec75f1635e..efbd1a54fe 100644 --- a/docs/guides/http/server.mdx +++ b/docs/guides/http/server.mdx @@ -6,7 +6,7 @@ mode: center This starts an HTTP server listening on port `3000`. It demonstrates basic routing with a number of common responses and also handles POST data from standard forms or as JSON. -See [`Bun.serve`](https://bun.com/docs/api/http) for details. +See [`Bun.serve`](/runtime/http/server) for details. ```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ diff --git a/docs/guides/http/simple.mdx b/docs/guides/http/simple.mdx index 91e862465c..a8e6479c2c 100644 --- a/docs/guides/http/simple.mdx +++ b/docs/guides/http/simple.mdx @@ -6,7 +6,7 @@ mode: center This starts an HTTP server listening on port `3000`. It responds to all requests with a `Response` with status `200` and body `"Welcome to Bun!"`. -See [`Bun.serve`](https://bun.com/docs/api/http) for details. +See [`Bun.serve`](/runtime/http/server) for details. ```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ diff --git a/docs/guides/http/stream-file.mdx b/docs/guides/http/stream-file.mdx index b5a31639a2..08cee886a6 100644 --- a/docs/guides/http/stream-file.mdx +++ b/docs/guides/http/stream-file.mdx @@ -4,7 +4,7 @@ sidebarTitle: Stream file response mode: center --- -This snippet reads a file from disk using [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor. +This snippet reads a file from disk using [`Bun.file()`](/runtime/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor. ```ts server.ts icon="/icons/typescript.svg" const path = "/path/to/file.txt"; @@ -32,7 +32,7 @@ new Response(Bun.file("./img.png")).headers.get("Content-Type"); --- -Putting it all together with [`Bun.serve()`](https://bun.com/docs/api/http#bun-serve). +Putting it all together with [`Bun.serve()`](/runtime/http/server). ```ts server.ts icon="/icons/typescript.svg" // static file server @@ -47,4 +47,4 @@ Bun.serve({ --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/http/tls.mdx b/docs/guides/http/tls.mdx index 14bb44c447..e0ffbc3b15 100644 --- a/docs/guides/http/tls.mdx +++ b/docs/guides/http/tls.mdx @@ -4,7 +4,7 @@ sidebarTitle: Configure TLS mode: center --- -Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file) to read the contents. +Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](/runtime/file-io#reading-files-bun-file) to read the contents. ```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ diff --git a/docs/guides/install/add-dev.mdx b/docs/guides/install/add-dev.mdx index 7e0a501f91..c9bf2f8e78 100644 --- a/docs/guides/install/add-dev.mdx +++ b/docs/guides/install/add-dev.mdx @@ -25,4 +25,4 @@ This will add the package to `devDependencies` in `package.json`. --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-git.mdx b/docs/guides/install/add-git.mdx index 7f8f3c8d81..882e67aba8 100644 --- a/docs/guides/install/add-git.mdx +++ b/docs/guides/install/add-git.mdx @@ -37,4 +37,4 @@ bun add github:colinhacks/zod --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-optional.mdx b/docs/guides/install/add-optional.mdx index 568ebeba4e..d1a4fd8214 100644 --- a/docs/guides/install/add-optional.mdx +++ b/docs/guides/install/add-optional.mdx @@ -24,4 +24,4 @@ This will add the package to `optionalDependencies` in `package.json`. --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-peer.mdx b/docs/guides/install/add-peer.mdx index e50980a74f..d2e55f517f 100644 --- a/docs/guides/install/add-peer.mdx +++ b/docs/guides/install/add-peer.mdx @@ -42,4 +42,4 @@ Running `bun install` will install peer dependencies by default, unless marked o --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-tarball.mdx b/docs/guides/install/add-tarball.mdx index 36abdd66b0..1165b404e1 100644 --- a/docs/guides/install/add-tarball.mdx +++ b/docs/guides/install/add-tarball.mdx @@ -32,4 +32,4 @@ import { z } from "zod"; --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add.mdx b/docs/guides/install/add.mdx index bb69bd2753..f9d2a81201 100644 --- a/docs/guides/install/add.mdx +++ b/docs/guides/install/add.mdx @@ -41,4 +41,4 @@ bun add zod@next --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/azure-artifacts.mdx b/docs/guides/install/azure-artifacts.mdx index 9ec522f0c1..2e4d703dbb 100644 --- a/docs/guides/install/azure-artifacts.mdx +++ b/docs/guides/install/azure-artifacts.mdx @@ -31,9 +31,9 @@ password = "$NPM_PASSWORD" --- -Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](https://bun.com/docs/runtime/env) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you. +Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](/runtime/environment-variables) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you. -```txt .env icon="settings" +```ini .env icon="settings" NPM_PASSWORD= ``` @@ -43,7 +43,7 @@ NPM_PASSWORD= --- -To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. +To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the appropriate values. ```bash terminal icon="terminal" NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=:_password= diff --git a/docs/guides/install/custom-registry.mdx b/docs/guides/install/custom-registry.mdx index ce5cd1426b..c5d2e56e7f 100644 --- a/docs/guides/install/custom-registry.mdx +++ b/docs/guides/install/custom-registry.mdx @@ -20,7 +20,7 @@ registry = "https://usertitle:password@registry.npmjs.org" --- -Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information. +Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/runtime/environment-variables) for more information. ```toml bunfig.toml icon="settings" [install] @@ -29,4 +29,4 @@ registry = { url = "https://registry.npmjs.org", token = "$npm_token" } --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/git-diff-bun-lockfile.mdx b/docs/guides/install/git-diff-bun-lockfile.mdx index b92fe38035..016aa9705f 100644 --- a/docs/guides/install/git-diff-bun-lockfile.mdx +++ b/docs/guides/install/git-diff-bun-lockfile.mdx @@ -6,7 +6,8 @@ mode: center Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without - configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) + configuration, at no cost to performance. In 1.2.0+ it is the default format used for new projects. [**Learn + more.**](/pm/lockfile#text-based-lockfile) --- diff --git a/docs/guides/install/npm-alias.mdx b/docs/guides/install/npm-alias.mdx index e6201aeb4c..6639db6396 100644 --- a/docs/guides/install/npm-alias.mdx +++ b/docs/guides/install/npm-alias.mdx @@ -22,4 +22,4 @@ z.string(); --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/registry-scope.mdx b/docs/guides/install/registry-scope.mdx index 1c9496a0d6..337ca6fd9c 100644 --- a/docs/guides/install/registry-scope.mdx +++ b/docs/guides/install/registry-scope.mdx @@ -4,7 +4,7 @@ sidebarTitle: Configure a scoped registry mode: center --- -Private registries can be configured using either [`.npmrc`](https://bun.com/docs/install/npmrc) or [`bunfig.toml`](https://bun.com/docs/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options. +Private registries can be configured using either [`.npmrc`](/pm/npmrc) or [`bunfig.toml`](/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options. To configure a registry for a particular npm scope: @@ -28,7 +28,7 @@ To configure a registry for a particular npm scope: --- -Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information. +Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/runtime/environment-variables) for more information. ```toml bunfig.toml icon="settings" [install.scopes] @@ -37,4 +37,4 @@ Your `bunfig.toml` can reference environment variables. Bun automatically loads --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/trusted.mdx b/docs/guides/install/trusted.mdx index 49efb33e5f..7660315003 100644 --- a/docs/guides/install/trusted.mdx +++ b/docs/guides/install/trusted.mdx @@ -47,4 +47,4 @@ bun install --- -See [Docs > Package manager > Trusted dependencies](https://bun.com/docs/install/lifecycle) for complete documentation of trusted dependencies. +See [Docs > Package manager > Trusted dependencies](/pm/lifecycle) for complete documentation of trusted dependencies. diff --git a/docs/guides/install/workspaces.mdx b/docs/guides/install/workspaces.mdx index 6e789d2eb1..7653ee9fa3 100644 --- a/docs/guides/install/workspaces.mdx +++ b/docs/guides/install/workspaces.mdx @@ -67,4 +67,4 @@ bun add zod --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/yarnlock.mdx b/docs/guides/install/yarnlock.mdx index 534033bc1e..c40539ee8d 100644 --- a/docs/guides/install/yarnlock.mdx +++ b/docs/guides/install/yarnlock.mdx @@ -6,12 +6,13 @@ mode: center Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without - configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) + configuration, at no cost to performance. In 1.2.0+ it is the default format used for new projects. [**Learn + more.**](/pm/lockfile#text-based-lockfile) --- -Use the `--yarn` flag to generate a Yarn-compatible `yarn.lock` file (in addition to `bun.lock`). +Use the `--yarn` flag to generate a Yarn-compatible `yarn.lock` file (in addition to `bun.lock{b}`). ```sh terminal icon="terminal" bun install --yarn @@ -47,4 +48,4 @@ abab@^2.0.6: --- -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. +See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/process/ctrl-c.mdx b/docs/guides/process/ctrl-c.mdx index cffee3085f..1d000e7a55 100644 --- a/docs/guides/process/ctrl-c.mdx +++ b/docs/guides/process/ctrl-c.mdx @@ -15,4 +15,4 @@ process.on("SIGINT", () => { --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/process/ipc.mdx b/docs/guides/process/ipc.mdx index 1df59fcb04..6a9b15de41 100644 --- a/docs/guides/process/ipc.mdx +++ b/docs/guides/process/ipc.mdx @@ -4,7 +4,7 @@ sidebarTitle: Spawn a child process and communicate using IPC mode: center --- -Use [`Bun.spawn()`](https://bun.com/docs/api/spawn) to spawn a child process. When spawning a second `bun` process, you can open a direct inter-process communication (IPC) channel between the two processes. +Use [`Bun.spawn()`](/runtime/child-process) to spawn a child process. When spawning a second `bun` process, you can open a direct inter-process communication (IPC) channel between the two processes. This API is only compatible with other `bun` processes. Use `process.execPath` to get a path to the currently running @@ -66,4 +66,4 @@ process.send({ message: "Hello from child as object" }); --- -See [Docs > API > Child processes](https://bun.com/docs/api/spawn) for complete documentation. +See [Docs > API > Child processes](/runtime/child-process) for complete documentation. diff --git a/docs/guides/process/nanoseconds.mdx b/docs/guides/process/nanoseconds.mdx index 8f974a84fb..74a677b487 100644 --- a/docs/guides/process/nanoseconds.mdx +++ b/docs/guides/process/nanoseconds.mdx @@ -12,4 +12,4 @@ Bun.nanoseconds(); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/process/os-signals.mdx b/docs/guides/process/os-signals.mdx index eff7d3f0ab..b124911ac3 100644 --- a/docs/guides/process/os-signals.mdx +++ b/docs/guides/process/os-signals.mdx @@ -38,4 +38,4 @@ process.on("exit", code => { --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/process/spawn-stderr.mdx b/docs/guides/process/spawn-stderr.mdx index cadfdf0f13..73155ecbfc 100644 --- a/docs/guides/process/spawn-stderr.mdx +++ b/docs/guides/process/spawn-stderr.mdx @@ -4,7 +4,7 @@ sidebarTitle: Read stderr mode: center --- -When using [`Bun.spawn()`](https://bun.com/docs/api/spawn), the child process inherits the `stderr` of the spawning process. If instead you'd prefer to read and handle `stderr`, set the `stderr` option to `"pipe"`. +When using [`Bun.spawn()`](/runtime/child-process), the child process inherits the `stderr` of the spawning process. If instead you'd prefer to read and handle `stderr`, set the `stderr` option to `"pipe"`. ```ts const proc = Bun.spawn(["echo", "hello"], { @@ -31,4 +31,4 @@ if (errors) { --- -See [Docs > API > Child processes](https://bun.com/docs/api/spawn) for complete documentation. +See [Docs > API > Child processes](/runtime/child-process) for complete documentation. diff --git a/docs/guides/process/spawn-stdout.mdx b/docs/guides/process/spawn-stdout.mdx index ab64f2a2cf..0b84fe89fc 100644 --- a/docs/guides/process/spawn-stdout.mdx +++ b/docs/guides/process/spawn-stdout.mdx @@ -4,7 +4,7 @@ sidebarTitle: Read stdout mode: center --- -When using [`Bun.spawn()`](https://bun.com/docs/api/spawn), the `stdout` of the child process can be consumed as a `ReadableStream` via `proc.stdout`. +When using [`Bun.spawn()`](/runtime/child-process), the `stdout` of the child process can be consumed as a `ReadableStream` via `proc.stdout`. ```ts const proc = Bun.spawn(["echo", "hello"]); @@ -25,4 +25,4 @@ const proc = Bun.spawn(["echo", "hello"], { --- -See [Docs > API > Child processes](https://bun.com/docs/api/spawn) for complete documentation. +See [Docs > API > Child processes](/runtime/child-process) for complete documentation. diff --git a/docs/guides/process/spawn.mdx b/docs/guides/process/spawn.mdx index a03ac5eaea..9a217d19ed 100644 --- a/docs/guides/process/spawn.mdx +++ b/docs/guides/process/spawn.mdx @@ -4,7 +4,7 @@ sidebarTitle: Spawn child process mode: center --- -Use [`Bun.spawn()`](https://bun.com/docs/api/spawn) to spawn a child process. +Use [`Bun.spawn()`](/runtime/child-process) to spawn a child process. ```ts const proc = Bun.spawn(["echo", "hello"]); @@ -40,4 +40,4 @@ output; // => "hello\n" --- -See [Docs > API > Child processes](https://bun.com/docs/api/spawn) for complete documentation. +See [Docs > API > Child processes](/runtime/child-process) for complete documentation. diff --git a/docs/guides/process/stdin.mdx b/docs/guides/process/stdin.mdx index b9fb18f614..d0e132fb9d 100644 --- a/docs/guides/process/stdin.mdx +++ b/docs/guides/process/stdin.mdx @@ -59,4 +59,4 @@ Chunk: hello --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/read-file/arraybuffer.mdx b/docs/guides/read-file/arraybuffer.mdx index 563ca71d1d..ca37d89524 100644 --- a/docs/guides/read-file/arraybuffer.mdx +++ b/docs/guides/read-file/arraybuffer.mdx @@ -15,7 +15,7 @@ const buffer = await file.arrayBuffer(); --- -The binary content in the `ArrayBuffer` can then be read as a typed array, such as `Int8Array`. For `Uint8Array`, use [`.bytes()`](./uint8array). +The binary content in the `ArrayBuffer` can then be read as a typed array, such as `Int8Array`. For `Uint8Array`, use [`.bytes()`](/guides/read-file/uint8array). ```ts index.ts icon="/icons/typescript.svg" const buffer = await file.arrayBuffer(); @@ -27,4 +27,4 @@ bytes.length; --- -Refer to the [Typed arrays](https://bun.com/docs/api/binary-data#typedarray) docs for more information on working with typed arrays in Bun. +Refer to the [Typed arrays](/runtime/binary-data#typedarray) docs for more information on working with typed arrays in Bun. diff --git a/docs/guides/read-file/buffer.mdx b/docs/guides/read-file/buffer.mdx index 5b24eda0a4..f1f8aad4d4 100644 --- a/docs/guides/read-file/buffer.mdx +++ b/docs/guides/read-file/buffer.mdx @@ -18,4 +18,4 @@ const buffer = Buffer.from(arrbuf); --- -Refer to [Binary data > Buffer](https://bun.com/docs/api/binary-data#buffer) for more information on working with `Buffer` and other binary data formats in Bun. +Refer to [Binary data > Buffer](/runtime/binary-data#buffer) for more information on working with `Buffer` and other binary data formats in Bun. diff --git a/docs/guides/read-file/exists.mdx b/docs/guides/read-file/exists.mdx index 320e659252..787668ff03 100644 --- a/docs/guides/read-file/exists.mdx +++ b/docs/guides/read-file/exists.mdx @@ -15,4 +15,4 @@ await file.exists(); // boolean; --- -Refer to [API > File I/O](https://bun.com/docs/api/file-io) for more information on working with `BunFile`. +Refer to [API > File I/O](/runtime/file-io) for more information on working with `BunFile`. diff --git a/docs/guides/read-file/mime.mdx b/docs/guides/read-file/mime.mdx index c3a369a013..1349d9bbf4 100644 --- a/docs/guides/read-file/mime.mdx +++ b/docs/guides/read-file/mime.mdx @@ -19,4 +19,4 @@ file.type; // image/png --- -Refer to [API > File I/O](https://bun.com/docs/api/file-io) for more information on working with `BunFile`. +Refer to [API > File I/O](/runtime/file-io) for more information on working with `BunFile`. diff --git a/docs/guides/read-file/stream.mdx b/docs/guides/read-file/stream.mdx index 2724ad2f6a..a2542c22a6 100644 --- a/docs/guides/read-file/stream.mdx +++ b/docs/guides/read-file/stream.mdx @@ -25,4 +25,4 @@ for await (const chunk of stream) { --- -Refer to the [Streams](https://bun.com/docs/api/streams) documentation for more information on working with streams in Bun. +Refer to the [Streams](/runtime/streams) documentation for more information on working with streams in Bun. diff --git a/docs/guides/read-file/uint8array.mdx b/docs/guides/read-file/uint8array.mdx index 53c902e682..939aa277f5 100644 --- a/docs/guides/read-file/uint8array.mdx +++ b/docs/guides/read-file/uint8array.mdx @@ -20,4 +20,4 @@ byteArray.length; // length of byteArray --- -Refer to [API > Binary data > Typed arrays](https://bun.com/docs/api/binary-data#typedarray) for more information on working with `Uint8Array` and other binary data formats in Bun. +Refer to [API > Binary data > Typed arrays](/runtime/binary-data#typedarray) for more information on working with `Uint8Array` and other binary data formats in Bun. diff --git a/docs/guides/read-file/watch.mdx b/docs/guides/read-file/watch.mdx index dde7a8c2a1..ef41129bab 100644 --- a/docs/guides/read-file/watch.mdx +++ b/docs/guides/read-file/watch.mdx @@ -63,4 +63,4 @@ process.on("SIGINT", () => { --- -Refer to [API > Binary data > Typed arrays](https://bun.com/docs/api/binary-data#typedarray) for more information on working with `Uint8Array` and other binary data formats in Bun. +Refer to [API > Binary data > Typed arrays](/runtime/binary-data#typedarray) for more information on working with `Uint8Array` and other binary data formats in Bun. diff --git a/docs/guides/runtime/cicd.mdx b/docs/guides/runtime/cicd.mdx index a4aae4f126..1fc0786640 100644 --- a/docs/guides/runtime/cicd.mdx +++ b/docs/guides/runtime/cicd.mdx @@ -37,7 +37,7 @@ jobs: # ... - uses: oven-sh/setup-bun@v2 with: # [!code ++] - bun-version: 1.2.0 # or "latest", "canary", # [!code ++] + bun-version: 1.3.2 # or "latest", "canary", # [!code ++] ``` --- diff --git a/docs/guides/runtime/delete-directory.mdx b/docs/guides/runtime/delete-directory.mdx index bcc8055051..460bf8c3ed 100644 --- a/docs/guides/runtime/delete-directory.mdx +++ b/docs/guides/runtime/delete-directory.mdx @@ -36,4 +36,4 @@ try { --- -See [Docs > API > FileSystem](https://bun.com/docs/api/file-io) for more filesystem operations. +See [Docs > API > FileSystem](/runtime/file-io) for more filesystem operations. diff --git a/docs/guides/runtime/delete-file.mdx b/docs/guides/runtime/delete-file.mdx index e8e24390e5..a3aabea4fc 100644 --- a/docs/guides/runtime/delete-file.mdx +++ b/docs/guides/runtime/delete-file.mdx @@ -18,4 +18,4 @@ const exists = await file.exists(); --- -See [Docs > API > FileSystem](https://bun.com/docs/api/file-io) for more filesystem operations. +See [Docs > API > FileSystem](/runtime/file-io) for more filesystem operations. diff --git a/docs/guides/runtime/import-json.mdx b/docs/guides/runtime/import-json.mdx index 1ed290a601..3ebdb20612 100644 --- a/docs/guides/runtime/import-json.mdx +++ b/docs/guides/runtime/import-json.mdx @@ -43,4 +43,4 @@ data.author.name; // => "John Dough" --- -See [Docs > Runtime > TypeScript](https://bun.com/docs/runtime/typescript) for more information on using TypeScript with Bun. +See [Docs > Runtime > TypeScript](/runtime/typescript) for more information on using TypeScript with Bun. diff --git a/docs/guides/runtime/import-toml.mdx b/docs/guides/runtime/import-toml.mdx index 991e12e049..3976b7e226 100644 --- a/docs/guides/runtime/import-toml.mdx +++ b/docs/guides/runtime/import-toml.mdx @@ -29,4 +29,4 @@ data.author.name; // => "John Dough" --- -See [Docs > Runtime > TypeScript](https://bun.com/docs/runtime/typescript) for more information on using TypeScript with Bun. +See [Docs > Runtime > TypeScript](/runtime/typescript) for more information on using TypeScript with Bun. diff --git a/docs/guides/runtime/import-yaml.mdx b/docs/guides/runtime/import-yaml.mdx index e4ff38155b..c9e7a036ad 100644 --- a/docs/guides/runtime/import-yaml.mdx +++ b/docs/guides/runtime/import-yaml.mdx @@ -101,4 +101,4 @@ export = contents; --- -See [Docs > API > YAML](https://bun.com/docs/api/yaml) for complete documentation on YAML support in Bun. +See [Docs > API > YAML](/runtime/yaml) for complete documentation on YAML support in Bun. diff --git a/docs/guides/runtime/read-env.mdx b/docs/guides/runtime/read-env.mdx index 7e3294e246..0f8c828292 100644 --- a/docs/guides/runtime/read-env.mdx +++ b/docs/guides/runtime/read-env.mdx @@ -34,4 +34,4 @@ FOOBAR=aaaaaa --- -See [Docs > Runtime > Environment variables](https://bun.com/docs/runtime/env) for more information on using environment variables with Bun. +See [Docs > Runtime > Environment variables](/runtime/environment-variables) for more information on using environment variables with Bun. diff --git a/docs/guides/runtime/set-env.mdx b/docs/guides/runtime/set-env.mdx index 27ed10b32c..a374875227 100644 --- a/docs/guides/runtime/set-env.mdx +++ b/docs/guides/runtime/set-env.mdx @@ -21,7 +21,7 @@ Bun reads the following files automatically (listed in order of increasing prece - `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`) - `.env.local` (not loaded when `NODE_ENV=test`) -```txt .env icon="settings" +```ini .env icon="settings" FOO=hello BAR=world ``` @@ -48,4 +48,4 @@ $env:FOO="helloworld"; bun run dev --- -See [Docs > Runtime > Environment variables](https://bun.com/docs/runtime/env) for more information on using environment variables with Bun. +See [Docs > Runtime > Environment variables](/runtime/environment-variables) for more information on using environment variables with Bun. diff --git a/docs/guides/runtime/shell.mdx b/docs/guides/runtime/shell.mdx index b845a625a5..723aa3fa8e 100644 --- a/docs/guides/runtime/shell.mdx +++ b/docs/guides/runtime/shell.mdx @@ -39,4 +39,4 @@ for await (const line of $`ls -l`.lines()) { --- -See [Docs > API > Shell](https://bun.com/docs/runtime/shell) for complete documentation. +See [Docs > API > Shell](/runtime/shell) for complete documentation. diff --git a/docs/guides/runtime/tsconfig-paths.mdx b/docs/guides/runtime/tsconfig-paths.mdx index 6c102d483b..5381084c19 100644 --- a/docs/guides/runtime/tsconfig-paths.mdx +++ b/docs/guides/runtime/tsconfig-paths.mdx @@ -28,4 +28,4 @@ import { Button } from "@components/Button"; // imports from "./src/components/B --- -See [Docs > Runtime > TypeScript](https://bun.com/docs/runtime/typescript) for more information on using TypeScript with Bun. +See [Docs > Runtime > TypeScript](/runtime/typescript) for more information on using TypeScript with Bun. diff --git a/docs/guides/runtime/typescript.mdx b/docs/guides/runtime/typescript.mdx index 25a52df0c6..811e6b1d9a 100644 --- a/docs/guides/runtime/typescript.mdx +++ b/docs/guides/runtime/typescript.mdx @@ -48,4 +48,4 @@ Below is the full set of recommended `compilerOptions` for a Bun project. With t --- -Refer to [Ecosystem > TypeScript](https://bun.com/docs/runtime/typescript) for a complete guide to TypeScript support in Bun. +Refer to [Ecosystem > TypeScript](/runtime/typescript) for a complete guide to TypeScript support in Bun. diff --git a/docs/guides/runtime/vscode-debugger.mdx b/docs/guides/runtime/vscode-debugger.mdx index d254ee4ab9..fae551616b 100644 --- a/docs/guides/runtime/vscode-debugger.mdx +++ b/docs/guides/runtime/vscode-debugger.mdx @@ -5,8 +5,7 @@ mode: center --- - VSCode extension support is currently buggy. We recommend the [Web - Debugger](https://bun.com/guides/runtime/web-debugger) for now. + VSCode extension support is currently buggy. We recommend the [Web Debugger](/guides/runtime/web-debugger) for now. Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts) so you can debug your code with an interactive debugger. diff --git a/docs/guides/runtime/web-debugger.mdx b/docs/guides/runtime/web-debugger.mdx index 46cc4d7e63..36a63301bc 100644 --- a/docs/guides/runtime/web-debugger.mdx +++ b/docs/guides/runtime/web-debugger.mdx @@ -4,7 +4,7 @@ sidebarTitle: Web debugger mode: center --- -Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-vscode/types/jsc.d.ts). To enable debugging when running code with Bun, use the `--inspect` flag. For demonstration purposes, consider the following simple web server. +Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts). To enable debugging when running code with Bun, use the `--inspect` flag. For demonstration purposes, consider the following simple web server. ```ts server.ts icon="/icons/typescript.svg" Bun.serve({ diff --git a/docs/guides/streams/to-array.mdx b/docs/guides/streams/to-array.mdx index 3fc1547231..ff9134696d 100644 --- a/docs/guides/streams/to-array.mdx +++ b/docs/guides/streams/to-array.mdx @@ -13,4 +13,4 @@ const str = await Bun.readableStreamToArray(stream); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-arraybuffer.mdx b/docs/guides/streams/to-arraybuffer.mdx index 4e05cb8cee..9b9e0535ec 100644 --- a/docs/guides/streams/to-arraybuffer.mdx +++ b/docs/guides/streams/to-arraybuffer.mdx @@ -13,4 +13,4 @@ const buf = await Bun.readableStreamToArrayBuffer(stream); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-blob.mdx b/docs/guides/streams/to-blob.mdx index df604513c6..1585e01c44 100644 --- a/docs/guides/streams/to-blob.mdx +++ b/docs/guides/streams/to-blob.mdx @@ -13,4 +13,4 @@ const blob = await Bun.readableStreamToBlob(stream); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-buffer.mdx b/docs/guides/streams/to-buffer.mdx index 2befca63cd..46e98dbaaf 100644 --- a/docs/guides/streams/to-buffer.mdx +++ b/docs/guides/streams/to-buffer.mdx @@ -14,4 +14,4 @@ const nodeBuf = Buffer.from(arrBuf); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-json.mdx b/docs/guides/streams/to-json.mdx index 567decf586..5e93fc37df 100644 --- a/docs/guides/streams/to-json.mdx +++ b/docs/guides/streams/to-json.mdx @@ -13,4 +13,4 @@ const json = await stream.json(); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-string.mdx b/docs/guides/streams/to-string.mdx index c07f8a3991..81f26efe1c 100644 --- a/docs/guides/streams/to-string.mdx +++ b/docs/guides/streams/to-string.mdx @@ -13,4 +13,4 @@ const str = await Bun.readableStreamToText(stream); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/streams/to-typedarray.mdx b/docs/guides/streams/to-typedarray.mdx index 4925910af4..4d6dd6adff 100644 --- a/docs/guides/streams/to-typedarray.mdx +++ b/docs/guides/streams/to-typedarray.mdx @@ -21,4 +21,4 @@ const uint8 = await Bun.readableStreamToBytes(stream); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. +See [Docs > API > Utils](/runtime/utils#bun-readablestreamto) for documentation on Bun's other `ReadableStream` conversion functions. diff --git a/docs/guides/test/bail.mdx b/docs/guides/test/bail.mdx index c69f8f0fbb..9cd56cacf9 100644 --- a/docs/guides/test/bail.mdx +++ b/docs/guides/test/bail.mdx @@ -21,4 +21,4 @@ bun test --bail=10 --- -See [Docs > Test runner](https://bun.sh/docs/cli/test) for complete documentation of `bun test`. +See [Docs > Test runner](/test) for complete documentation of `bun test`. diff --git a/docs/guides/test/concurrent-test-glob.mdx b/docs/guides/test/concurrent-test-glob.mdx index 8a2d5a2c8e..5f68c50351 100644 --- a/docs/guides/test/concurrent-test-glob.mdx +++ b/docs/guides/test/concurrent-test-glob.mdx @@ -2,6 +2,7 @@ title: Selectively run tests concurrently with glob patterns sidebarTitle: Concurrent test glob mode: center +description: Set a glob pattern to decide which tests from which files run in parallel --- This guide demonstrates how to use the `concurrentTestGlob` option to selectively run tests concurrently based on file naming patterns. @@ -69,12 +70,14 @@ test("fetch user data", async () => { expect(response.ok).toBe(true); }); -test("fetch posts", async () => { +// can also use test.concurrent() for explicitly marking it as concurrent +test.concurrent("fetch posts", async () => { const response = await fetch("/api/posts"); expect(response.ok).toBe(true); }); -test("fetch comments", async () => { +// can also use test.serial() for explicitly marking it as sequential +test.serial("fetch comments", async () => { const response = await fetch("/api/comments"); expect(response.ok).toBe(true); }); diff --git a/docs/guides/test/coverage-threshold.mdx b/docs/guides/test/coverage-threshold.mdx index 560e38756b..5efdc197b4 100644 --- a/docs/guides/test/coverage-threshold.mdx +++ b/docs/guides/test/coverage-threshold.mdx @@ -64,4 +64,4 @@ coverageThreshold = { lines = 0.5, functions = 0.7 } --- -See [Docs > Test runner > Coverage](https://bun.sh/docs/test/coverage) for complete documentation on code coverage reporting in Bun. +See [Docs > Test runner > Coverage](/test/code-coverage) for complete documentation on code coverage reporting in Bun. diff --git a/docs/guides/test/coverage.mdx b/docs/guides/test/coverage.mdx index 7c2315c297..0c18dba670 100644 --- a/docs/guides/test/coverage.mdx +++ b/docs/guides/test/coverage.mdx @@ -46,4 +46,4 @@ coverage = true # always enable coverage --- -Refer to [Docs > Test runner > Coverage](https://bun.sh/docs/test/coverage) for complete documentation on code coverage reporting in Bun. +Refer to [Docs > Test runner > Coverage](/test/code-coverage) for complete documentation on code coverage reporting in Bun. diff --git a/docs/guides/test/happy-dom.mdx b/docs/guides/test/happy-dom.mdx index 14a40db2f0..88673378a0 100644 --- a/docs/guides/test/happy-dom.mdx +++ b/docs/guides/test/happy-dom.mdx @@ -70,4 +70,4 @@ Ran 1 tests across 1 files. 1 total [125.00ms] --- -Refer to the [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](https://bun.sh/docs/test/dom) for complete documentation on writing browser tests with Bun. +Refer to the [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](/test/dom) for complete documentation on writing browser tests with Bun. diff --git a/docs/guides/test/migrate-from-jest.mdx b/docs/guides/test/migrate-from-jest.mdx index af0ebc0daf..fbe3e6dca4 100644 --- a/docs/guides/test/migrate-from-jest.mdx +++ b/docs/guides/test/migrate-from-jest.mdx @@ -62,7 +62,7 @@ describe("my test suite", () => { --- -Bun implements the vast majority of Jest's matchers, but compatibility isn't 100% yet. Refer to the full compatibility table at [Docs > Test runner > Writing tests](https://bun.sh/docs/test/writing#matchers). +Bun implements the vast majority of Jest's matchers, but compatibility isn't 100% yet. Refer to the full compatibility table at [Docs > Test runner > Writing tests](/test/writing-tests#matchers). Some notable missing features: @@ -74,7 +74,7 @@ If you're using `testEnvironment: "jsdom"` to run your tests in a browser-like e At the moment jsdom does not work in Bun due to its internal use of V8 APIs. Track support for it [here](https://github.com/oven-sh/bun/issues/3554). -```toml bunfig.toml +```toml bunfig.toml icon="settings" [test] preload = ["./happy-dom.ts"] ``` @@ -107,11 +107,11 @@ bun test --timeout 10000 Many other flags become irrelevant or obsolete when using `bun test`. -- `transform` — Bun supports TypeScript & JSX. Other file types can be configured with [Plugins](https://bun.sh/docs/runtime/plugins). +- `transform` — Bun supports TypeScript & JSX. Other file types can be configured with [Plugins](/runtime/plugins). - `extensionsToTreatAsEsm` - `haste` — Bun uses it's own internal source maps - `watchman`, `watchPlugins`, `watchPathIgnorePatterns` — use `--watch` to run tests in watch mode -- `verbose` — set `logLevel: "debug"` in [`bunfig.toml`](https://bun.sh/docs/runtime/bunfig#loglevel) +- `verbose` — set `logLevel: "debug"` in [`bunfig.toml`](/runtime/bunfig#loglevel) --- @@ -122,4 +122,4 @@ Settings that aren't mentioned here are not supported or have no equivalent. Ple See also: - [Mark a test as a todo](/guides/test/todo-tests) -- [Docs > Test runner > Writing tests](https://bun.sh/docs/test/writing) +- [Docs > Test runner > Writing tests](/test/writing-tests) diff --git a/docs/guides/test/mock-clock.mdx b/docs/guides/test/mock-clock.mdx index fd862254fa..21ec7e2776 100644 --- a/docs/guides/test/mock-clock.mdx +++ b/docs/guides/test/mock-clock.mdx @@ -22,7 +22,7 @@ test("party like it's 1999", () => { --- -The `setSystemTime` function is commonly used on conjunction with [Lifecycle Hooks](https://bun.sh/docs/test/lifecycle) to configure a testing environment with a deterministic "fake clock". +The `setSystemTime` function is commonly used on conjunction with [Lifecycle Hooks](/test/lifecycle) to configure a testing environment with a deterministic "fake clock". ```ts import { test, expect, beforeAll, setSystemTime } from "bun:test"; @@ -47,4 +47,4 @@ setSystemTime(); // reset to actual time --- -See [Docs > Test Runner > Date and time](https://bun.sh/docs/test/time) for complete documentation on mocking with the Bun test runner. +See [Docs > Test Runner > Date and time](/test/dates-times) for complete documentation on mocking with the Bun test runner. diff --git a/docs/guides/test/mock-functions.mdx b/docs/guides/test/mock-functions.mdx index 93b14c953e..8abd9602c2 100644 --- a/docs/guides/test/mock-functions.mdx +++ b/docs/guides/test/mock-functions.mdx @@ -67,4 +67,4 @@ test("random", async () => { --- -See [Docs > Test Runner > Mocks](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner. +See [Docs > Test Runner > Mocks](/test/mocks) for complete documentation on mocking with the Bun test runner. diff --git a/docs/guides/test/rerun-each.mdx b/docs/guides/test/rerun-each.mdx index 12046aebbd..964d976508 100644 --- a/docs/guides/test/rerun-each.mdx +++ b/docs/guides/test/rerun-each.mdx @@ -13,4 +13,4 @@ bun test --rerun-each 10 --- -See [Docs > Test runner](https://bun.sh/docs/cli/test) for complete documentation of `bun test`. +See [Docs > Test runner](/test) for complete documentation of `bun test`. diff --git a/docs/guides/test/run-tests.mdx b/docs/guides/test/run-tests.mdx index 54638b950e..fd0b9759b1 100644 --- a/docs/guides/test/run-tests.mdx +++ b/docs/guides/test/run-tests.mdx @@ -4,7 +4,7 @@ sidebarTitle: Run tests mode: center --- -Bun has a built-in [test runner](https://bun.sh/docs/cli/test) with a Jest-like `expect` API. +Bun has a built-in [test runner](/test) with a Jest-like `expect` API. --- @@ -113,4 +113,4 @@ Ran 6 tests across 3 files. [59.00ms] --- -See [Docs > Test Runner](https://bun.sh/docs/cli/test) for complete documentation on the test runner. +See [Docs > Test Runner](/test) for complete documentation on the test runner. diff --git a/docs/guides/test/skip-tests.mdx b/docs/guides/test/skip-tests.mdx index 135e516efb..11a2565668 100644 --- a/docs/guides/test/skip-tests.mdx +++ b/docs/guides/test/skip-tests.mdx @@ -40,4 +40,4 @@ Ran 3 tests across 1 files. [74.00ms] See also: - [Mark a test as a todo](/guides/test/todo-tests) -- [Docs > Test runner > Writing tests](https://bun.sh/docs/test/writing) +- [Docs > Test runner > Writing tests](/test/writing-tests) diff --git a/docs/guides/test/snapshot.mdx b/docs/guides/test/snapshot.mdx index df16c0f73d..1be74fcbbf 100644 --- a/docs/guides/test/snapshot.mdx +++ b/docs/guides/test/snapshot.mdx @@ -49,7 +49,7 @@ test The `snap.test.ts.snap` file is a JavaScript file that exports a serialized version of the value passed into `expect()`. The `{foo: "bar"}` object has been serialized to JSON. ```js snap.test.ts.snap icon="file-code" -// Bun Snapshot v1, https://bun.sh/docs/test/snapshots +// Bun Snapshot v1, https://bun.com/docs/test/snapshots exports[`snapshot 1`] = ` { @@ -99,4 +99,4 @@ Ran 1 tests across 1 files. [102.00ms] --- -See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner. +See [Docs > Test Runner > Snapshots](/test/snapshots) for complete documentation on snapshots with the Bun test runner. diff --git a/docs/guides/test/spy-on.mdx b/docs/guides/test/spy-on.mdx index 74519c08c5..4685ddf047 100644 --- a/docs/guides/test/spy-on.mdx +++ b/docs/guides/test/spy-on.mdx @@ -46,4 +46,4 @@ test("turtles", () => { // [!code ++] --- -See [Docs > Test Runner > Mocks](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner. +See [Docs > Test Runner > Mocks](/test/mocks) for complete documentation on mocking with the Bun test runner. diff --git a/docs/guides/test/svelte-test.mdx b/docs/guides/test/svelte-test.mdx index 54e555ae76..6f57573f0e 100644 --- a/docs/guides/test/svelte-test.mdx +++ b/docs/guides/test/svelte-test.mdx @@ -14,7 +14,7 @@ bun add @testing-library/svelte svelte@4 @happy-dom/global-registrator Then, save this plugin in your project. -```ts svelte-loader.js icon="/icons/typescript.svg" +```ts svelte-loader.ts icon="/icons/typescript.svg" import { plugin } from "bun"; import { compile } from "svelte/compiler"; import { readFileSync } from "fs"; @@ -61,10 +61,10 @@ Add this to `bunfig.toml` to tell Bun to preload the plugin, so it loads before ```toml bunfig.toml icon="settings" [test] # Tell Bun to load this plugin before your tests run -preload = ["./svelte-loader.js"] +preload = ["./svelte-loader.ts"] # This also works: -# test.preload = ["./svelte-loader.js"] +# test.preload = ["./svelte-loader.ts"] ``` --- diff --git a/docs/guides/test/testing-library.mdx b/docs/guides/test/testing-library.mdx index 36e5b61540..7e4ed0a440 100644 --- a/docs/guides/test/testing-library.mdx +++ b/docs/guides/test/testing-library.mdx @@ -8,7 +8,7 @@ You can use [Testing Library](https://testing-library.com/) with Bun's test runn --- -As a prerequisite to using Testing Library you will need to install [Happy Dom](https://github.com/capricorn86/happy-dom). ([see Bun's Happy DOM guide for more information](https://bun.sh/guides/test/happy-dom)). +As a prerequisite to using Testing Library you will need to install [Happy Dom](https://github.com/capricorn86/happy-dom). ([see Bun's Happy DOM guide for more information](/guides/test/happy-dom)). ```sh terminal icon="terminal" bun add -D @happy-dom/global-registrator @@ -24,7 +24,7 @@ bun add -D @testing-library/react @testing-library/dom @testing-library/jest-dom --- -Next you will need to create a preload script for Happy DOM and for Testing Library. For more details about the Happy DOM setup script see [Bun's Happy DOM guide](https://bun.sh/guides/test/happy-dom). +Next you will need to create a preload script for Happy DOM and for Testing Library. For more details about the Happy DOM setup script see [Bun's Happy DOM guide](/guides/test/happy-dom). ```ts happydom.ts icon="/icons/typescript.svg" import { GlobalRegistrator } from "@happy-dom/global-registrator"; @@ -90,4 +90,4 @@ test("Can use Testing Library", () => { --- -Refer to the [Testing Library docs](https://testing-library.com/), [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](https://bun.sh/docs/test/dom) for complete documentation on writing browser tests with Bun. +Refer to the [Testing Library docs](https://testing-library.com/), [Happy DOM repo](https://github.com/capricorn86/happy-dom) and [Docs > Test runner > DOM](/test/dom) for complete documentation on writing browser tests with Bun. diff --git a/docs/guides/test/timeout.mdx b/docs/guides/test/timeout.mdx index 182fbea339..9d333ac2c4 100644 --- a/docs/guides/test/timeout.mdx +++ b/docs/guides/test/timeout.mdx @@ -14,4 +14,4 @@ bun test --timeout 3000 # 3 seconds --- -See [Docs > Test runner](https://bun.sh/docs/cli/test) for complete documentation of `bun test`. +See [Docs > Test runner](/test) for complete documentation of `bun test`. diff --git a/docs/guides/test/todo-tests.mdx b/docs/guides/test/todo-tests.mdx index a349b088a1..e1ff067044 100644 --- a/docs/guides/test/todo-tests.mdx +++ b/docs/guides/test/todo-tests.mdx @@ -71,4 +71,4 @@ $ echo $? See also: - [Skip a test](/guides/test/skip-tests) -- [Docs > Test runner > Writing tests](https://bun.sh/docs/test/writing) +- [Docs > Test runner > Writing tests](/test/writing-tests) diff --git a/docs/guides/test/update-snapshots.mdx b/docs/guides/test/update-snapshots.mdx index 4cdd5f3c2f..f0069dbe33 100644 --- a/docs/guides/test/update-snapshots.mdx +++ b/docs/guides/test/update-snapshots.mdx @@ -46,4 +46,4 @@ Ran 1 tests across 1 files. [102.00ms] --- -See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner. +See [Docs > Test Runner > Snapshots](/test/snapshots) for complete documentation on snapshots with the Bun test runner. diff --git a/docs/guides/test/watch-mode.mdx b/docs/guides/test/watch-mode.mdx index dd24911c7a..8b3f7b9556 100644 --- a/docs/guides/test/watch-mode.mdx +++ b/docs/guides/test/watch-mode.mdx @@ -21,4 +21,4 @@ This will restart the running Bun process whenever a file change is detected. It --- -See [Docs > Test Runner](https://bun.sh/docs/cli/test) for complete documentation on the test runner. +See [Docs > Test Runner](/test) for complete documentation on the test runner. diff --git a/docs/guides/util/base64.mdx b/docs/guides/util/base64.mdx index 11ae957d4d..350ee72c4c 100644 --- a/docs/guides/util/base64.mdx +++ b/docs/guides/util/base64.mdx @@ -14,4 +14,4 @@ const decoded = atob(encoded); // => "hello world" --- -See [Docs > Web APIs](https://bun.com/docs/runtime/web-apis) for a complete breakdown of the Web APIs implemented in Bun. +See [Docs > Web APIs](/runtime/web-apis) for a complete breakdown of the Web APIs implemented in Bun. diff --git a/docs/guides/util/deep-equals.mdx b/docs/guides/util/deep-equals.mdx index f5f15fae28..c3ef2d626c 100644 --- a/docs/guides/util/deep-equals.mdx +++ b/docs/guides/util/deep-equals.mdx @@ -4,7 +4,7 @@ sidebarTitle: Deep equality mode: center --- -Check if two objects are deeply equal. This is used internally by `expect().toEqual()` in Bun's [test runner](https://bun.com/docs/test/writing). +Check if two objects are deeply equal. This is used internally by `expect().toEqual()` in Bun's [test runner](/test/writing-tests). ```ts index.ts icon="/icons/typescript.svg" const a = { a: 1, b: 2, c: { d: 3 } }; @@ -15,7 +15,7 @@ Bun.deepEquals(a, b); // true --- -Pass `true` as a third argument to enable strict mode. This is used internally by `expect().toStrictEqual()` in Bun's [test runner](https://bun.com/docs/test/writing). +Pass `true` as a third argument to enable strict mode. This is used internally by `expect().toStrictEqual()` in Bun's [test runner](/test/writing-tests). The following examples would return `true` in non-strict mode but `false` in strict mode. @@ -38,4 +38,4 @@ Bun.deepEquals(new Foo(), { a: 1 }, true); // false --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/deflate.mdx b/docs/guides/util/deflate.mdx index 6c525db2ef..90f367bb4e 100644 --- a/docs/guides/util/deflate.mdx +++ b/docs/guides/util/deflate.mdx @@ -17,4 +17,4 @@ const decompressed = Bun.inflateSync(compressed); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/entrypoint.mdx b/docs/guides/util/entrypoint.mdx index a7105a00d0..e495ecc683 100644 --- a/docs/guides/util/entrypoint.mdx +++ b/docs/guides/util/entrypoint.mdx @@ -4,7 +4,7 @@ sidebarTitle: Check entrypoint mode: center --- -Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.main` to check if the current file is the entrypoint of the current process. +Bun provides a handful of module-specific utilities on the [`import.meta`](/runtime/module-resolution#import-meta) object. Use `import.meta.main` to check if the current file is the entrypoint of the current process. ```ts index.ts icon="/icons/typescript.svg" if (import.meta.main) { @@ -16,4 +16,4 @@ if (import.meta.main) { --- -See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. +See [Docs > API > import.meta](/runtime/module-resolution#import-meta) for complete documentation. diff --git a/docs/guides/util/escape-html.mdx b/docs/guides/util/escape-html.mdx index ca2fec3cfa..fc08af9a63 100644 --- a/docs/guides/util/escape-html.mdx +++ b/docs/guides/util/escape-html.mdx @@ -21,4 +21,4 @@ Bun.escapeHTML(""); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/file-url-to-path.mdx b/docs/guides/util/file-url-to-path.mdx index 1ff86fc31b..3990f3befb 100644 --- a/docs/guides/util/file-url-to-path.mdx +++ b/docs/guides/util/file-url-to-path.mdx @@ -13,4 +13,4 @@ Bun.fileURLToPath("file:///path/to/file.txt"); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/gzip.mdx b/docs/guides/util/gzip.mdx index 84b9d97067..0a3763f9ed 100644 --- a/docs/guides/util/gzip.mdx +++ b/docs/guides/util/gzip.mdx @@ -17,4 +17,4 @@ const decompressed = Bun.gunzipSync(compressed); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/hash-a-password.mdx b/docs/guides/util/hash-a-password.mdx index d45d6da6e2..ef01dde286 100644 --- a/docs/guides/util/hash-a-password.mdx +++ b/docs/guides/util/hash-a-password.mdx @@ -53,4 +53,4 @@ const isMatch = await Bun.password.verify(password, hash); --- -See [Docs > API > Hashing](https://bun.com/docs/api/hashing#bun-password) for complete documentation. +See [Docs > API > Hashing](/runtime/hashing#bun-password) for complete documentation. diff --git a/docs/guides/util/import-meta-dir.mdx b/docs/guides/util/import-meta-dir.mdx index 2890273034..8a2355cd89 100644 --- a/docs/guides/util/import-meta-dir.mdx +++ b/docs/guides/util/import-meta-dir.mdx @@ -4,7 +4,7 @@ sidebarTitle: import.meta.dir mode: center --- -Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. +Bun provides a handful of module-specific utilities on the [`import.meta`](/runtime/module-resolution#import-meta) object. ```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.dir; // => "/a/b" @@ -12,4 +12,4 @@ import.meta.dir; // => "/a/b" --- -See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. +See [Docs > API > import.meta](/runtime/module-resolution#import-meta) for complete documentation. diff --git a/docs/guides/util/import-meta-file.mdx b/docs/guides/util/import-meta-file.mdx index cf16226b35..01edbafa66 100644 --- a/docs/guides/util/import-meta-file.mdx +++ b/docs/guides/util/import-meta-file.mdx @@ -4,7 +4,7 @@ sidebarTitle: import.meta.file mode: center --- -Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.file` to retrieve the name of the current file. +Bun provides a handful of module-specific utilities on the [`import.meta`](/runtime/module-resolution#import-meta) object. Use `import.meta.file` to retrieve the name of the current file. ```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.file; // => "c.ts" @@ -12,4 +12,4 @@ import.meta.file; // => "c.ts" --- -See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. +See [Docs > API > import.meta](/runtime/module-resolution#import-meta) for complete documentation. diff --git a/docs/guides/util/import-meta-path.mdx b/docs/guides/util/import-meta-path.mdx index 3e1f20aec8..80e92bfd1f 100644 --- a/docs/guides/util/import-meta-path.mdx +++ b/docs/guides/util/import-meta-path.mdx @@ -4,7 +4,7 @@ sidebarTitle: import.meta.path mode: center --- -Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.path` to retrieve the absolute path of the current file. +Bun provides a handful of module-specific utilities on the [`import.meta`](/runtime/module-resolution#import-meta) object. Use `import.meta.path` to retrieve the absolute path of the current file. ```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.path; // => "/a/b/c.ts" @@ -12,4 +12,4 @@ import.meta.path; // => "/a/b/c.ts" --- -See [Docs > API > import.meta](https://bun.com/docs/api/import-meta) for complete documentation. +See [Docs > API > import.meta](/runtime/module-resolution#import-meta) for complete documentation. diff --git a/docs/guides/util/javascript-uuid.mdx b/docs/guides/util/javascript-uuid.mdx index 1d9a0ea0f6..1c0d4bfa6b 100644 --- a/docs/guides/util/javascript-uuid.mdx +++ b/docs/guides/util/javascript-uuid.mdx @@ -8,7 +8,7 @@ Use `crypto.randomUUID()` to generate a UUID v4. This API works in Bun, Node.js, ```ts crypto.randomUUID(); -// => "123e4567-e89b-12d3-a456-426614174000" +// => "123e4567-e89b-42d3-a456-426614174000" ``` --- @@ -22,4 +22,4 @@ Bun.randomUUIDv7(); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/main.mdx b/docs/guides/util/main.mdx index f308c64a7c..ea41dbf2d1 100644 --- a/docs/guides/util/main.mdx +++ b/docs/guides/util/main.mdx @@ -40,4 +40,4 @@ bun run foo.ts --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/path-to-file-url.mdx b/docs/guides/util/path-to-file-url.mdx index aac29788df..65ffe7b201 100644 --- a/docs/guides/util/path-to-file-url.mdx +++ b/docs/guides/util/path-to-file-url.mdx @@ -13,4 +13,4 @@ Bun.pathToFileURL("/path/to/file.txt"); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/sleep.mdx b/docs/guides/util/sleep.mdx index 30daedc805..738738c213 100644 --- a/docs/guides/util/sleep.mdx +++ b/docs/guides/util/sleep.mdx @@ -21,4 +21,4 @@ await new Promise(resolve => setTimeout(resolve, ms)); --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/version.mdx b/docs/guides/util/version.mdx index 73b3942744..5fa039b08a 100644 --- a/docs/guides/util/version.mdx +++ b/docs/guides/util/version.mdx @@ -20,4 +20,4 @@ Bun.revision; // => "49231b2cb9aa48497ab966fc0bb6b742dacc4994" --- -See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities. +See [Docs > API > Utils](/runtime/utils) for more useful utilities. diff --git a/docs/guides/util/which-path-to-executable-bin.mdx b/docs/guides/util/which-path-to-executable-bin.mdx index fe9cda53a6..e524561089 100644 --- a/docs/guides/util/which-path-to-executable-bin.mdx +++ b/docs/guides/util/which-path-to-executable-bin.mdx @@ -14,4 +14,4 @@ Bun.which("bun"); // => "/home/user/.bun/bin/bun" --- -See [Docs > API > Utils](https://bun.com/docs/api/utils#bun-which) for complete documentation. +See [Docs > API > Utils](/runtime/utils#bun-which) for complete documentation. diff --git a/docs/guides/websocket/context.mdx b/docs/guides/websocket/context.mdx index 85c8ecc05b..3104ec9a12 100644 --- a/docs/guides/websocket/context.mdx +++ b/docs/guides/websocket/context.mdx @@ -6,7 +6,7 @@ mode: center When building a WebSocket server, it's typically necessary to store some identifying information or context associated with each connected client. -With [Bun.serve()](https://bun.com/docs/api/websockets contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call. +With [Bun.serve()](/runtime/http/websockets#contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call. ```ts server.ts icon="/icons/typescript.svg" Bun.serve({ diff --git a/docs/guides/websocket/simple.mdx b/docs/guides/websocket/simple.mdx index 4450b0e418..6239d13365 100644 --- a/docs/guides/websocket/simple.mdx +++ b/docs/guides/websocket/simple.mdx @@ -4,7 +4,7 @@ sidebarTitle: Simple server mode: center --- -Start a simple WebSocket server using [`Bun.serve`](https://bun.com/docs/api/http). +Start a simple WebSocket server using [`Bun.serve`](/runtime/http/server). Inside `fetch`, we attempt to upgrade incoming `ws:` or `wss:` requests to WebSocket connections. diff --git a/docs/guides/write-file/basic.mdx b/docs/guides/write-file/basic.mdx index 4edd4207a2..6f3eec5287 100644 --- a/docs/guides/write-file/basic.mdx +++ b/docs/guides/write-file/basic.mdx @@ -6,7 +6,7 @@ mode: center This code snippet writes a string to disk at a particular _absolute path_. -It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_; the second is the _data_ to write. +It uses the fast [`Bun.write()`](/runtime/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_; the second is the _data_ to write. ```ts const path = "/path/to/file.txt"; @@ -43,4 +43,4 @@ const bytes = await Bun.write(path, "Lorem ipsum"); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/blob.mdx b/docs/guides/write-file/blob.mdx index ccaaaefc78..78810b740d 100644 --- a/docs/guides/write-file/blob.mdx +++ b/docs/guides/write-file/blob.mdx @@ -6,7 +6,7 @@ mode: center This code snippet writes a `Blob` to disk at a particular path. -It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. +It uses the fast [`Bun.write()`](/runtime/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. ```ts const path = "/path/to/file.txt"; @@ -27,4 +27,4 @@ await Bun.write(path, data); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/cat.mdx b/docs/guides/write-file/cat.mdx index 44b5d91cf0..92ca362e04 100644 --- a/docs/guides/write-file/cat.mdx +++ b/docs/guides/write-file/cat.mdx @@ -4,7 +4,7 @@ sidebarTitle: Write file to stdout mode: center --- -Bun exposes `stdout` as a `BunFile` with the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). +Bun exposes `stdout` as a `BunFile` with the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](/runtime/file-io#writing-files-bun-write). This code writes a file to `stdout` similar to the `cat` command in Unix. @@ -16,4 +16,4 @@ await Bun.write(Bun.stdout, file); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/file-cp.mdx b/docs/guides/write-file/file-cp.mdx index d43d5f7225..d8eb08d67c 100644 --- a/docs/guides/write-file/file-cp.mdx +++ b/docs/guides/write-file/file-cp.mdx @@ -6,7 +6,7 @@ mode: center This code snippet copies a file to another location on disk. -It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. +It uses the fast [`Bun.write()`](/runtime/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. ```ts const file = Bun.file("/path/to/original.txt"); @@ -15,4 +15,4 @@ await Bun.write("/path/to/copy.txt", file); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/filesink.mdx b/docs/guides/write-file/filesink.mdx index 5414ed933f..68333e5445 100644 --- a/docs/guides/write-file/filesink.mdx +++ b/docs/guides/write-file/filesink.mdx @@ -51,4 +51,4 @@ writer.end(); --- -Full documentation: [FileSink](https://bun.com/docs/api/file-io#incremental-writing-with-filesink). +Full documentation: [FileSink](/runtime/file-io#incremental-writing-with-filesink). diff --git a/docs/guides/write-file/response.mdx b/docs/guides/write-file/response.mdx index 2f0721623a..20e04ff69c 100644 --- a/docs/guides/write-file/response.mdx +++ b/docs/guides/write-file/response.mdx @@ -6,7 +6,7 @@ mode: center This code snippet writes a `Response` to disk at a particular path. Bun will consume the `Response` body according to its `Content-Type` header. -It uses the fast [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. +It uses the fast [`Bun.write()`](/runtime/file-io#writing-files-bun-write) API to efficiently write data to disk. The first argument is a _destination_, like an absolute path or `BunFile` instance. The second argument is the _data_ to write. ```ts const result = await fetch("https://bun.com"); @@ -16,4 +16,4 @@ await Bun.write(path, result); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/stdout.mdx b/docs/guides/write-file/stdout.mdx index 1e980c0947..3c1d9fd681 100644 --- a/docs/guides/write-file/stdout.mdx +++ b/docs/guides/write-file/stdout.mdx @@ -12,7 +12,7 @@ console.log("Lorem ipsum"); --- -For more advanced use cases, Bun exposes `stdout` as a `BunFile` via the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). +For more advanced use cases, Bun exposes `stdout` as a `BunFile` via the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](/runtime/file-io#writing-files-bun-write). ```ts await Bun.write(Bun.stdout, "Lorem ipsum"); @@ -20,4 +20,4 @@ await Bun.write(Bun.stdout, "Lorem ipsum"); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/stream.mdx b/docs/guides/write-file/stream.mdx index 739c60c320..1c13bd5c48 100644 --- a/docs/guides/write-file/stream.mdx +++ b/docs/guides/write-file/stream.mdx @@ -4,7 +4,7 @@ sidebarTitle: Write stream mode: center --- -To write a `ReadableStream` to disk, first create a `Response` instance from the stream. This `Response` can then be written to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). +To write a `ReadableStream` to disk, first create a `Response` instance from the stream. This `Response` can then be written to disk using [`Bun.write()`](/runtime/file-io#writing-files-bun-write). ```ts const stream: ReadableStream = ...; @@ -16,4 +16,4 @@ await Bun.write(path, response); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. +See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`. diff --git a/docs/guides/write-file/unlink.mdx b/docs/guides/write-file/unlink.mdx index d20cc37287..7835f503d2 100644 --- a/docs/guides/write-file/unlink.mdx +++ b/docs/guides/write-file/unlink.mdx @@ -15,4 +15,4 @@ await file.delete(); --- -See [Docs > API > File I/O](https://bun.com/docs/api/file-io#reading-files-bun-file) for complete documentation of `Bun.file()`. +See [Docs > API > File I/O](/runtime/file-io#reading-files-bun-file) for complete documentation of `Bun.file()`. diff --git a/docs/index.mdx b/docs/index.mdx index f45293b998..471e021af4 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -127,7 +127,7 @@ Bun is designed from the ground-up with today's JavaScript ecosystem in mind. - **Speed**. Bun processes start [4x faster than Node.js](https://twitter.com/jarredsumner/status/1499225725492076544) currently (try it yourself!) - **TypeScript & JSX support**. You can directly execute `.jsx`, `.ts`, and `.tsx` files; Bun's transpiler converts these to vanilla JavaScript before execution. - **ESM & CommonJS compatibility**. The world is moving towards ES modules (ESM), but millions of packages on npm still require CommonJS. Bun recommends ES modules, but supports CommonJS. -- **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp). +- **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp). - **Node.js compatibility**. In addition to supporting Node-style module resolution, Bun aims for full compatibility with built-in Node.js globals (`process`, `Buffer`) and modules (`path`, `fs`, `http`, etc.) _This is an ongoing effort that is not complete._ Refer to the [compatibility page](/runtime/nodejs-compat) for the current status. Bun is more than a runtime. The long-term goal is to be a cohesive, infrastructural toolkit for building apps with JavaScript/TypeScript, including a package manager, transpiler, bundler, script runner, test runner, and more. diff --git a/docs/installation.mdx b/docs/installation.mdx index 4f518c3033..0280436eec 100644 --- a/docs/installation.mdx +++ b/docs/installation.mdx @@ -1,6 +1,6 @@ --- title: Installation -description: Install Bun +description: Install Bun with npm, Homebrew, Docker, or the official script. --- ## Overview diff --git a/docs/normalize-internal-links.js b/docs/normalize-internal-links.js index 3fd5d44504..97a1957b4d 100644 --- a/docs/normalize-internal-links.js +++ b/docs/normalize-internal-links.js @@ -15,7 +15,6 @@ if (element.getAttribute("rel") === "noreferrer") { element.removeAttribute("rel"); } - console.log(`Removed target="_blank" from: ${element.textContent || element.innerHTML.substring(0, 50)}`); } }); }); diff --git a/docs/pm/bunx.mdx b/docs/pm/bunx.mdx index d2299cf9df..48dafc0e73 100644 --- a/docs/pm/bunx.mdx +++ b/docs/pm/bunx.mdx @@ -30,7 +30,7 @@ Packages can declare executables in the `"bin"` field of their `package.json`. T These executables are commonly plain JavaScript files marked with a [shebang line]() to indicate which program should be used to execute them. The following file indicates that it should be executed with `node`. -```ts dist/index.js icon="/icons/javascript.svg" +```js dist/index.js icon="/icons/javascript.svg" #!/usr/bin/env node console.log("Hello world!"); @@ -78,6 +78,6 @@ bunx --package @angular/cli ng To force bun to always be used with a script, use a shebang. -```ts dist/index.js icon="/icons/javascript.svg" +```js dist/index.js icon="/icons/javascript.svg" #!/usr/bin/env bun ``` diff --git a/docs/pm/cli/install.mdx b/docs/pm/cli/install.mdx index 65b0c3ce8a..054e364a91 100644 --- a/docs/pm/cli/install.mdx +++ b/docs/pm/cli/install.mdx @@ -135,7 +135,7 @@ For more information on filtering with `bun install`, refer to [Package Manager Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](/pm/overrides) for complete documentation. {/* prettier-ignore */} -```json package.json file="file-json" +```json package.json icon="file-json" { "name": "my-app", "dependencies": { diff --git a/docs/pm/cli/link.mdx b/docs/pm/cli/link.mdx index 860d6be2b4..2591abbf30 100644 --- a/docs/pm/cli/link.mdx +++ b/docs/pm/cli/link.mdx @@ -14,7 +14,7 @@ bun link ``` ```txt -bun link v1.x (7416672e) +bun link v1.3.2 (7416672e) Success! Registered "cool-pkg" To use cool-pkg in a project, run: @@ -53,7 +53,7 @@ bun unlink ``` ```txt -bun unlink v1.x (7416672e) +bun unlink v1.3.2 (7416672e) ``` --- diff --git a/docs/pm/cli/outdated.mdx b/docs/pm/cli/outdated.mdx index da72e0c490..69a70b3998 100644 --- a/docs/pm/cli/outdated.mdx +++ b/docs/pm/cli/outdated.mdx @@ -15,7 +15,7 @@ bun outdated | Package | Current | Update | Latest | | ------------------------------ | ------- | --------- | ---------- | | @sinclair/typebox | 0.34.15 | 0.34.16 | 0.34.16 | -| @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | +| @types/bun (dev) | 1.3.0 | 1.3.2 | 1.3.2 | | eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | | eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | | eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | @@ -55,7 +55,7 @@ bun outdated eslint-plugin-security eslint-plugin-sonarjs You can also pass glob patterns to check for outdated packages: ```sh terminal icon="terminal" -bun outdated eslint* +bun outdated 'eslint*' ``` ```txt @@ -75,7 +75,7 @@ bun outdated '@types/*' ```txt | Package | Current | Update | Latest | | ------------------ | ------- | ------ | ------ | -| @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | +| @types/bun (dev) | 1.3.0 | 1.3.2 | 1.3.2 | ``` Or to exclude all `@types/*` packages: diff --git a/docs/pm/workspaces.mdx b/docs/pm/workspaces.mdx index e524822b89..961037914a 100644 --- a/docs/pm/workspaces.mdx +++ b/docs/pm/workspaces.mdx @@ -43,8 +43,7 @@ In the root `package.json`, the `"workspaces"` key is used to indicate which sub **Glob support** — Bun supports full glob syntax in `"workspaces"`, including negative patterns (e.g. - `!**/excluded/**`). See [here](https://bun.com/docs/api/glob#supported-glob-patterns) for a comprehensive list of - supported syntax. + `!**/excluded/**`). See [here](/runtime/glob#supported-glob-patterns) for a comprehensive list of supported syntax. ```json package.json icon="file-json" diff --git a/docs/project/building-windows.mdx b/docs/project/building-windows.mdx index 59cb8f788e..9a4a3ab2d7 100644 --- a/docs/project/building-windows.mdx +++ b/docs/project/building-windows.mdx @@ -22,7 +22,7 @@ By default, running unverified scripts are blocked. Bun v1.1 or later. We use Bun to run it's own code generators. ```ps1 -> irm bun.com/install.ps1 | iex +> irm bun.sh/install.ps1 | iex ``` [Visual Studio](https://visualstudio.microsoft.com) with the "Desktop Development with C++" workload. While installing, make sure to install Git as well, if Git for Windows is not already installed. diff --git a/docs/project/license.mdx b/docs/project/license.mdx index 679ff7be84..fa51203c26 100644 --- a/docs/project/license.mdx +++ b/docs/project/license.mdx @@ -11,7 +11,7 @@ Bun statically links JavaScriptCore (and WebKit) which is LGPL-2 licensed. WebCo > (1) If you statically link against an LGPL'd library, you must also provide your application in an object (not necessarily source) format, so that a user has the opportunity to modify the library and relink the application. -You can find the patched version of WebKit used by Bun here: [https://github.com/oven-sh/webkit](https://github.com/oven-sh/webkit). If you would like to relink Bun with changes: +You can find the patched version of WebKit used by Bun here: https://github.com/oven-sh/webkit. If you would like to relink Bun with changes: - `git submodule update --init --recursive` - `make jsc` diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index 59939e6887..6781199996 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -92,7 +92,7 @@ Build a minimal HTTP server with `Bun.serve`, run it locally, then evolve it by Then add the following to your `compilerOptions` in `tsconfig.json`: - ```json tsconfig.json icon="file-code" + ```json tsconfig.json icon="file-json" { "compilerOptions": { "lib": ["ESNext"], @@ -220,7 +220,7 @@ Build a minimal HTTP server with `Bun.serve`, run it locally, then evolve it by Bun can also execute `"scripts"` from your `package.json`. Add the following script: {/* prettier-ignore */} -```json package.json icon="file-code" +```json package.json icon="file-json" { "name": "quickstart", "module": "index.ts", diff --git a/docs/runtime/bunfig.mdx b/docs/runtime/bunfig.mdx index a296acb827..669e9a1330 100644 --- a/docs/runtime/bunfig.mdx +++ b/docs/runtime/bunfig.mdx @@ -452,8 +452,8 @@ To configure the directory where Bun installs globally installed binaries and CL Environment variable: `BUN_INSTALL_BIN` ```toml title="bunfig.toml" icon="settings" -# where globally-installed package bins are linked [install] +# where globally-installed package bins are linked globalBinDir = "~/.bun/bin" ``` @@ -585,6 +585,32 @@ editor = "code" # - "emacs" ``` +### `install.security.scanner` + +Configure a security scanner to scan packages for vulnerabilities before installation. + +First, install a security scanner from npm: + +```bash terminal icon="terminal" +bun add -d @acme/bun-security-scanner +``` + +Then configure it in your `bunfig.toml`: + +```toml bunfig.toml icon="settings" +[install.security] +scanner = "@acme/bun-security-scanner" +``` + +When a security scanner is configured: + +- Auto-install is automatically disabled for security +- Packages are scanned before installation +- Installation is cancelled if fatal issues are found +- Security warnings are displayed during installation + +Learn more about [using and writing security scanners](/pm/security-scanner-api). + ### `install.minimumReleaseAge` Configure a minimum age (in seconds) for npm package versions. Package versions published more recently than this threshold will be filtered out during installation. Default is `null` (disabled). diff --git a/docs/runtime/c-compiler.mdx b/docs/runtime/c-compiler.mdx index 14c0e27216..516fc3efbd 100644 --- a/docs/runtime/c-compiler.mdx +++ b/docs/runtime/c-compiler.mdx @@ -13,7 +13,7 @@ See the [introduction blog post](https://bun.com/blog/compile-and-run-c-in-js) f JavaScript: -```ts hello.js icon="file-code" +```ts hello.ts icon="file-code" import { cc } from "bun:ffi"; import source from "./hello.c" with { type: "file" }; @@ -87,7 +87,7 @@ You can also pass a `napi_env` to receive the N-API environment used to call the For example, if you have a string in C, you can return it to JavaScript like this: -```ts hello.js +```ts hello.ts import { cc } from "bun:ffi"; import source from "./hello.c" with { type: "file" }; diff --git a/docs/runtime/child-process.mdx b/docs/runtime/child-process.mdx index 6be68a6f27..8ebdc0b92a 100644 --- a/docs/runtime/child-process.mdx +++ b/docs/runtime/child-process.mdx @@ -289,7 +289,7 @@ childProc.disconnect(); To use IPC between a `bun` process and a Node.js process, set `serialization: "json"` in `Bun.spawn`. This is because Node.js and Bun use different JavaScript engines with different object serialization formats. -```ts bun-node-ipc.js icon="file-code" +```js bun-node-ipc.js icon="file-code" if (typeof Bun !== "undefined") { const prefix = `[bun ${process.versions.bun} 🐇]`; const node = Bun.spawn({ diff --git a/docs/runtime/cookies.mdx b/docs/runtime/cookies.mdx index 641dfc7989..0d90bd0420 100644 --- a/docs/runtime/cookies.mdx +++ b/docs/runtime/cookies.mdx @@ -9,7 +9,7 @@ Bun provides native APIs for working with HTTP cookies through `Bun.Cookie` and `Bun.CookieMap` provides a Map-like interface for working with collections of cookies. It implements the `Iterable` interface, allowing you to use it with `for...of` loops and other iteration methods. -```ts filename="cookies.ts" icon="/icons/typescript.svg" +```ts title="cookies.ts" icon="/icons/typescript.svg" // Empty cookie map const cookies = new Bun.CookieMap(); @@ -33,7 +33,7 @@ const cookies3 = new Bun.CookieMap([ In Bun's HTTP server, the `cookies` property on the request object (in `routes`) is an instance of `CookieMap`: -```ts filename="server.ts" icon="/icons/typescript.svg" +```ts title="server.ts" icon="/icons/typescript.svg" const server = Bun.serve({ routes: { "/": req => { @@ -68,7 +68,7 @@ console.log("Server listening at: " + server.url); Retrieves a cookie by name. Returns `null` if the cookie doesn't exist. -```ts filename="get-cookie.ts" icon="/icons/typescript.svg" +```ts title="get-cookie.ts" icon="/icons/typescript.svg" // Get by name const cookie = cookies.get("session"); @@ -81,7 +81,7 @@ if (cookie != null) { Checks if a cookie with the given name exists. -```ts filename="has-cookie.ts" icon="/icons/typescript.svg" +```ts title="has-cookie.ts" icon="/icons/typescript.svg" // Check if cookie exists if (cookies.has("session")) { // Cookie exists @@ -96,7 +96,7 @@ if (cookies.has("session")) { Adds or updates a cookie in the map. Cookies default to `{ path: "/", sameSite: "lax" }`. -```ts filename="set-cookie.ts" icon="/icons/typescript.svg" +```ts title="set-cookie.ts" icon="/icons/typescript.svg" // Set by name and value cookies.set("session", "abc123"); @@ -119,7 +119,7 @@ cookies.set(cookie); Removes a cookie from the map. When applied to a Response, this adds a cookie with an empty string value and an expiry date in the past. A cookie will only delete successfully on the browser if the domain and path is the same as it was when the cookie was created. -```ts filename="delete-cookie.ts" icon="/icons/typescript.svg" +```ts title="delete-cookie.ts" icon="/icons/typescript.svg" // Delete by name using default domain and path. cookies.delete("session"); @@ -135,7 +135,7 @@ cookies.delete({ Converts the cookie map to a serializable format. -```ts filename="cookie-to-json.ts" icon="/icons/typescript.svg" +```ts title="cookie-to-json.ts" icon="/icons/typescript.svg" const json = cookies.toJSON(); ``` @@ -145,7 +145,7 @@ Returns an array of values for Set-Cookie headers that can be used to apply all When using `Bun.serve()`, you don't need to call this method explicitly. Any changes made to the `req.cookies` map are automatically applied to the response headers. This method is primarily useful when working with other HTTP server implementations. -```ts filename="node-server.js" icon="file-code" +```js title="node-server.js" icon="file-code" import { createServer } from "node:http"; import { CookieMap } from "bun"; @@ -172,7 +172,7 @@ server.listen(3000, () => { `CookieMap` provides several methods for iteration: -```ts filename="iterate-cookies.ts" icon="/icons/typescript.svg" +```ts title="iterate-cookies.ts" icon="/icons/typescript.svg" // Iterate over [name, cookie] entries for (const [name, value] of cookies) { console.log(`${name}: ${value}`); @@ -205,7 +205,7 @@ cookies.forEach((value, name) => { Returns the number of cookies in the map. -```ts filename="cookie-size.ts" icon="/icons/typescript.svg" +```ts title="cookie-size.ts" icon="/icons/typescript.svg" console.log(cookies.size); // Number of cookies ``` @@ -213,7 +213,7 @@ console.log(cookies.size); // Number of cookies `Bun.Cookie` represents an HTTP cookie with its name, value, and attributes. -```ts filename="cookie-class.ts" icon="/icons/typescript.svg" +```ts title="cookie-class.ts" icon="/icons/typescript.svg" import { Cookie } from "bun"; // Create a basic cookie @@ -243,7 +243,7 @@ const objCookie = new Bun.Cookie({ ### Constructors -```ts filename="constructors.ts" icon="/icons/typescript.svg" +```ts title="constructors.ts" icon="/icons/typescript.svg" // Basic constructor with name/value new Bun.Cookie(name: string, value: string); @@ -259,7 +259,7 @@ new Bun.Cookie(options: CookieInit); ### Properties -```ts filename="cookie-properties.ts" icon="/icons/typescript.svg" +```ts title="cookie-properties.ts" icon="/icons/typescript.svg" cookie.name; // string - Cookie name cookie.value; // string - Cookie value cookie.domain; // string | null - Domain scope (null if not specified) @@ -278,7 +278,7 @@ cookie.httpOnly; // boolean - Accessible only via HTTP (not JavaScript) Checks if the cookie has expired. -```ts filename="is-expired.ts" icon="/icons/typescript.svg" +```ts title="is-expired.ts" icon="/icons/typescript.svg" // Expired cookie (Date in the past) const expiredCookie = new Bun.Cookie("name", "value", { expires: new Date(Date.now() - 1000), @@ -302,7 +302,7 @@ console.log(sessionCookie.isExpired()); // false Returns a string representation of the cookie suitable for a `Set-Cookie` header. -```ts filename="serialize-cookie.ts" icon="/icons/typescript.svg" +```ts title="serialize-cookie.ts" icon="/icons/typescript.svg" const cookie = new Bun.Cookie("session", "abc123", { domain: "example.com", path: "/admin", @@ -322,7 +322,7 @@ console.log(cookie.toString()); Converts the cookie to a plain object suitable for JSON serialization. -```ts filename="cookie-json.ts" icon="/icons/typescript.svg" +```ts title="cookie-json.ts" icon="/icons/typescript.svg" const cookie = new Bun.Cookie("session", "abc123", { secure: true, httpOnly: true, @@ -349,7 +349,7 @@ const jsonString = JSON.stringify(cookie); Parses a cookie string into a `Cookie` instance. -```ts filename="parse-cookie.ts" icon="/icons/typescript.svg" +```ts title="parse-cookie.ts" icon="/icons/typescript.svg" const cookie = Bun.Cookie.parse("name=value; Path=/; Secure; SameSite=Lax"); console.log(cookie.name); // "name" @@ -363,7 +363,7 @@ console.log(cookie.sameSite); // "lax" Factory method to create a cookie. -```ts filename="cookie-from.ts" icon="/icons/typescript.svg" +```ts title="cookie-from.ts" icon="/icons/typescript.svg" const cookie = Bun.Cookie.from("session", "abc123", { httpOnly: true, secure: true, @@ -373,7 +373,7 @@ const cookie = Bun.Cookie.from("session", "abc123", { ## Types -```ts filename="types.ts" icon="/icons/typescript.svg" +```ts title="types.ts" icon="/icons/typescript.svg" interface CookieInit { name?: string; value?: string; diff --git a/docs/runtime/debugger.mdx b/docs/runtime/debugger.mdx index c718f2632f..add9018b37 100644 --- a/docs/runtime/debugger.mdx +++ b/docs/runtime/debugger.mdx @@ -115,7 +115,7 @@ Here's a cheat sheet explaining the functions of the control flow buttons. ### Visual Studio Code Debugger -Experimental support for debugging Bun scripts is available in Visual Studio Code. To use it, you'll need to install the [Bun VSCode extension](https://bun.com/guides/runtime/vscode-debugger). +Experimental support for debugging Bun scripts is available in Visual Studio Code. To use it, you'll need to install the [Bun VSCode extension](/guides/runtime/vscode-debugger). --- diff --git a/docs/runtime/environment-variables.mdx b/docs/runtime/environment-variables.mdx index 529b5348af..087da9ef56 100644 --- a/docs/runtime/environment-variables.mdx +++ b/docs/runtime/environment-variables.mdx @@ -13,7 +13,7 @@ Bun reads the following files automatically (listed in order of increasing prece - `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`) - `.env.local` -```txt .env icon="settings" +```ini .env icon="settings" FOO=hello BAR=world ``` @@ -46,7 +46,7 @@ bun exec 'FOO=helloworld bun run dev' On Windows, `package.json` scripts called with `bun run` will automatically use the **bun shell**, making the following also cross-platform. -```json package.json +```json package.json icon="file-json" "scripts": { "dev": "NODE_ENV=development bun --watch app.ts", }, @@ -95,7 +95,7 @@ Explicitly provided environment files via `--env-file` will still be loaded even Bun supports double quotes, single quotes, and template literal backticks: -```txt .env icon="settings" +```ini .env icon="settings" FOO='hello' FOO="hello" FOO=`hello` @@ -105,7 +105,7 @@ FOO=`hello` Environment variables are automatically _expanded_. This means you can reference previously-defined variables in your environment variables. -```txt .env icon="settings" +```ini .env icon="settings" FOO=world BAR=hello$FOO ``` @@ -116,7 +116,7 @@ process.env.BAR; // => "helloworld" This is useful for constructing connection strings or other compound values. -```txt .env icon="settings" +```ini .env icon="settings" DB_USER=postgres DB_PASSWORD=secret DB_HOST=localhost @@ -126,7 +126,7 @@ DB_URL=postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME This can be disabled by escaping the `$` with a backslash. -```txt .env icon="settings" +```ini .env icon="settings" FOO=world BAR=hello\$FOO ``` diff --git a/docs/runtime/http/routing.mdx b/docs/runtime/http/routing.mdx index 37cb4b3545..418e421866 100644 --- a/docs/runtime/http/routing.mdx +++ b/docs/runtime/http/routing.mdx @@ -102,7 +102,7 @@ Bun.serve({ TypeScript parses route parameters when passed as a string literal, so that your editor will show autocomplete when accessing `request.params`. -```ts title="index.ts" +```ts title="index.ts" icon="/icons/typescript.svg" import type { BunRequest } from "bun"; Bun.serve({ diff --git a/docs/runtime/http/server.mdx b/docs/runtime/http/server.mdx index f230f1e7ea..59083df7f2 100644 --- a/docs/runtime/http/server.mdx +++ b/docs/runtime/http/server.mdx @@ -32,12 +32,8 @@ const server = Bun.serve({ // Redirect from /blog/hello to /blog/hello/world "/blog/hello": Response.redirect("/blog/hello/world"), - // Serve a file by buffering it in memory - "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { - headers: { - "Content-Type": "image/x-icon", - }, - }), + // Serve a file by lazily loading it into memory + "/favicon.ico": Bun.file("./favicon.ico"), }, // (optional) fallback for unmatched routes: @@ -126,7 +122,7 @@ bun --port=4002 server.ts - `BUN_PORT` environment variable ```sh -bun_PORT=4002 bun server.ts +BUN_PORT=4002 bun server.ts ``` - `PORT` environment variable diff --git a/docs/runtime/http/websockets.mdx b/docs/runtime/http/websockets.mdx index 2b005c488a..657da9f40b 100644 --- a/docs/runtime/http/websockets.mdx +++ b/docs/runtime/http/websockets.mdx @@ -174,7 +174,7 @@ Bun.serve({ To connect to this server from the browser, create a new `WebSocket`. -```ts browser.js icon="file-code" +```js browser.js icon="file-code" const socket = new WebSocket("ws://localhost:3000/chat"); socket.addEventListener("message", event => { diff --git a/docs/runtime/index.mdx b/docs/runtime/index.mdx index 7ffa1d0a9b..9a01aa2e73 100644 --- a/docs/runtime/index.mdx +++ b/docs/runtime/index.mdx @@ -94,7 +94,7 @@ Cleaning... Done. ``` -Bun executes the script command in a subshell. On Linux & macOS, it checks for the following shells in order, using the first one it finds: `bash`, `sh`, `zsh`. On windows, it uses [bun shell](https://bun.com/docs/runtime/shell) to support bash-like syntax and many common commands. +Bun executes the script command in a subshell. On Linux & macOS, it checks for the following shells in order, using the first one it finds: `bash`, `sh`, `zsh`. On Windows, it uses [bun shell](/runtime/shell) to support bash-like syntax and many common commands. ⚡️ The startup time for `npm run` on Linux is roughly 170ms; with Bun it is `6ms`. @@ -153,7 +153,7 @@ bun run --filter 'ba*'