aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-07-27 16:55:49 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-27 16:55:49 -0700
commit007f357495d02ccccc4a75f6f04a7e127d022a72 (patch)
tree3103a41b1fa8fb350cbeabb34e31536705e744e4 /src
parent16a7224ce5de519afa4765594d45350c95a87adc (diff)
downloadbun-007f357495d02ccccc4a75f6f04a7e127d022a72.tar.gz
bun-007f357495d02ccccc4a75f6f04a7e127d022a72.tar.zst
bun-007f357495d02ccccc4a75f6f04a7e127d022a72.zip
Make readFile() async (#3850)
* less leaky * async readfile * Update types.zig --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/node/node_fs.zig426
-rw-r--r--src/bun.js/node/node_fs_binding.zig13
-rw-r--r--src/bun.js/node/types.zig28
-rw-r--r--src/js/node/fs.js8
-rw-r--r--src/js/node/fs.promises.ts2
-rw-r--r--src/js/out/modules/node/fs.js5
-rw-r--r--src/js/out/modules/node/fs.promises.js2
7 files changed, 310 insertions, 174 deletions
diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig
index 6d2ec4120..2068cca3f 100644
--- a/src/bun.js/node/node_fs.zig
+++ b/src/bun.js/node/node_fs.zig
@@ -62,14 +62,16 @@ pub const AsyncReaddirTask = struct {
task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
result: JSC.Maybe(Return.Readdir),
ref: JSC.PollRef = .{},
+ arena: bun.ArenaAllocator,
- pub fn create(globalObject: *JSC.JSGlobalObject, readdir_args: Arguments.Readdir, vm: *JSC.VirtualMachine) JSC.JSValue {
+ pub fn create(globalObject: *JSC.JSGlobalObject, readdir_args: Arguments.Readdir, vm: *JSC.VirtualMachine, arena: bun.ArenaAllocator) JSC.JSValue {
var task = bun.default_allocator.create(AsyncReaddirTask) catch @panic("out of memory");
task.* = AsyncReaddirTask{
.promise = JSC.JSPromise.Strong.init(globalObject),
.args = readdir_args,
.result = undefined,
.globalObject = globalObject,
+ .arena = arena,
};
task.ref.ref(vm);
@@ -120,8 +122,9 @@ pub const AsyncReaddirTask = struct {
}
pub fn deinit(this: *AsyncReaddirTask) void {
+ this.arena.deinit();
this.ref.unref(this.globalObject.bunVM());
- this.args.deinit();
+ this.args.deinitAndUnprotect();
this.promise.strong.deinit();
bun.default_allocator.destroy(this);
}
@@ -135,12 +138,14 @@ pub const AsyncStatTask = struct {
result: JSC.Maybe(Return.Stat),
ref: JSC.PollRef = .{},
is_lstat: bool = false,
+ arena: bun.ArenaAllocator,
pub fn create(
globalObject: *JSC.JSGlobalObject,
readdir_args: Arguments.Stat,
vm: *JSC.VirtualMachine,
is_lstat: bool,
+ arena: bun.ArenaAllocator,
) JSC.JSValue {
var task = bun.default_allocator.create(AsyncStatTask) catch @panic("out of memory");
task.* = AsyncStatTask{
@@ -149,6 +154,7 @@ pub const AsyncStatTask = struct {
.result = undefined,
.globalObject = globalObject,
.is_lstat = is_lstat,
+ .arena = arena,
};
task.ref.ref(vm);
@@ -202,13 +208,94 @@ pub const AsyncStatTask = struct {
}
pub fn deinit(this: *AsyncStatTask) void {
+ this.arena.deinit();
this.ref.unref(this.globalObject.bunVM());
- this.args.deinit();
+ this.args.deinitAndUnprotect();
this.promise.strong.deinit();
bun.default_allocator.destroy(this);
}
};
+pub const AsyncReadFileTask = struct {
+ promise: JSC.JSPromise.Strong,
+ args: Arguments.ReadFile,
+ globalObject: *JSC.JSGlobalObject,
+ task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
+ result: JSC.Maybe(Return.ReadFile),
+ ref: JSC.PollRef = .{},
+ arena: bun.ArenaAllocator,
+
+ pub fn create(
+ globalObject: *JSC.JSGlobalObject,
+ args: Arguments.ReadFile,
+ vm: *JSC.VirtualMachine,
+ arena: bun.ArenaAllocator,
+ ) JSC.JSValue {
+ var task = bun.default_allocator.create(AsyncReadFileTask) catch @panic("out of memory");
+ task.* = AsyncReadFileTask{
+ .promise = JSC.JSPromise.Strong.init(globalObject),
+ .args = args,
+ .result = undefined,
+ .globalObject = globalObject,
+ .arena = arena,
+ };
+ task.ref.ref(vm);
+
+ JSC.WorkPool.schedule(&task.task);
+
+ return task.promise.value();
+ }
+
+ fn workPoolCallback(task: *JSC.WorkPoolTask) void {
+ var this: *AsyncReadFileTask = @fieldParentPtr(AsyncReadFileTask, "task", task);
+
+ var node_fs = NodeFS{};
+ this.result = node_fs.readFile(this.args, .promise);
+
+ this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
+ }
+
+ fn runFromJSThread(this: *AsyncReadFileTask) void {
+ var globalObject = this.globalObject;
+ var success = @as(JSC.Maybe(Return.ReadFile).Tag, this.result) == .result;
+ const result = switch (this.result) {
+ .err => |err| err.toJSC(globalObject),
+ .result => |res| brk: {
+ var exceptionref: JSC.C.JSValueRef = null;
+ const out = JSC.JSValue.c(JSC.To.JS.withType(Return.ReadFile, res, globalObject, &exceptionref));
+ const exception = JSC.JSValue.c(exceptionref);
+ if (exception != .zero) {
+ success = false;
+ break :brk exception;
+ }
+
+ break :brk out;
+ },
+ };
+ var promise_value = this.promise.value();
+ var promise = this.promise.get();
+ promise_value.ensureStillAlive();
+
+ this.deinit();
+ switch (success) {
+ false => {
+ promise.reject(globalObject, result);
+ },
+ true => {
+ promise.resolve(globalObject, result);
+ },
+ }
+ }
+
+ pub fn deinit(this: *AsyncReadFileTask) void {
+ this.ref.unref(this.globalObject.bunVM());
+ this.args.deinitAndUnprotect();
+ this.promise.strong.deinit();
+ this.arena.deinit();
+ bun.default_allocator.destroy(this);
+ }
+};
+
// TODO: to improve performance for all of these
// The tagged unions for each type should become regular unions
// and the tags should be passed in as comptime arguments to the functions performing the syscalls
@@ -812,6 +899,10 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: Stat) void {
+ this.path.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Stat {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1309,6 +1400,10 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: Readdir) void {
+ this.path.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readdir {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1872,6 +1967,10 @@ pub const Arguments = struct {
self.path.deinit();
}
+ pub fn deinitAndUnprotect(self: ReadFile) void {
+ self.path.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?ReadFile {
const path = PathOrFileDescriptor.fromJS(ctx, arguments, arguments.arena.allocator(), exception) orelse {
if (exception.* == null) {
@@ -3857,193 +3956,186 @@ pub const NodeFS = struct {
};
}
- pub fn readFileWithOptions(this: *NodeFS, args: Arguments.ReadFile, comptime flavor: Flavor, comptime string_type: StringType) Maybe(Return.ReadFileWithOptions) {
+ pub fn readFileWithOptions(this: *NodeFS, args: Arguments.ReadFile, comptime _: Flavor, comptime string_type: StringType) Maybe(Return.ReadFileWithOptions) {
var path: [:0]const u8 = undefined;
- switch (comptime flavor) {
- .sync => {
- const fd = switch (args.path) {
- .path => brk: {
- path = args.path.path.sliceZ(&this.sync_error_buf);
- if (this.vm) |vm| {
- if (vm.standalone_module_graph) |graph| {
- if (graph.find(path)) |file| {
- if (args.encoding == .buffer) {
- return .{
- .result = .{
- .buffer = Buffer.fromBytes(
- bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"),
- bun.default_allocator,
- .Uint8Array,
- ),
- },
- };
- } else if (comptime string_type == .default)
- return .{
- .result = .{
- .string = bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"),
- },
- }
- else
- return .{
- .result = .{
- .null_terminated = bun.default_allocator.dupeZ(u8, file.contents) catch @panic("out of memory"),
- },
- };
+ const fd = switch (args.path) {
+ .path => brk: {
+ path = args.path.path.sliceZ(&this.sync_error_buf);
+ if (this.vm) |vm| {
+ if (vm.standalone_module_graph) |graph| {
+ if (graph.find(path)) |file| {
+ if (args.encoding == .buffer) {
+ return .{
+ .result = .{
+ .buffer = Buffer.fromBytes(
+ bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"),
+ bun.default_allocator,
+ .Uint8Array,
+ ),
+ },
+ };
+ } else if (comptime string_type == .default)
+ return .{
+ .result = .{
+ .string = bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"),
+ },
}
- }
+ else
+ return .{
+ .result = .{
+ .null_terminated = bun.default_allocator.dupeZ(u8, file.contents) catch @panic("out of memory"),
+ },
+ };
}
-
- break :brk switch (Syscall.open(
- path,
- os.O.RDONLY | os.O.NOCTTY,
- 0,
- )) {
- .err => |err| return .{
- .err = err.withPath(if (args.path == .path) args.path.path.slice() else ""),
- },
- .result => |fd_| fd_,
- };
- },
- .fd => |_fd| _fd,
- };
-
- defer {
- if (args.path == .path)
- _ = Syscall.close(fd);
+ }
}
- const stat_ = switch (Syscall.fstat(fd)) {
+ break :brk switch (Syscall.open(
+ path,
+ os.O.RDONLY | os.O.NOCTTY,
+ 0,
+ )) {
.err => |err| return .{
- .err = err,
+ .err = err.withPath(if (args.path == .path) args.path.path.slice() else ""),
},
- .result => |stat_| stat_,
+ .result => |fd_| fd_,
};
+ },
+ .fd => |_fd| _fd,
+ };
- // Only used in DOMFormData
- if (args.offset > 0) {
- std.os.lseek_SET(fd, args.offset) catch {};
- }
+ defer {
+ if (args.path == .path)
+ _ = Syscall.close(fd);
+ }
- // For certain files, the size might be 0 but the file might still have contents.
- const size = @as(
- u64,
- @intCast(@max(
- @min(
- stat_.size,
- @as(
- @TypeOf(stat_.size),
- // Only used in DOMFormData
- @intCast(args.max_size orelse std.math.maxInt(
- JSC.WebCore.Blob.SizeType,
- )),
- ),
- ),
- 0,
- )),
- ) + if (comptime string_type == .null_terminated) 1 else 0;
-
- var buf = std.ArrayList(u8).init(bun.default_allocator);
- buf.ensureTotalCapacityPrecise(size + 16) catch unreachable;
- buf.expandToCapacity();
- var total: usize = 0;
-
- while (total < size) {
- switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) {
- .err => |err| return .{
- .err = err,
- },
- .result => |amt| {
- total += amt;
- // There are cases where stat()'s size is wrong or out of date
- if (total > size and amt != 0) {
- buf.ensureUnusedCapacity(8096) catch unreachable;
- buf.expandToCapacity();
- continue;
- }
+ const stat_ = switch (Syscall.fstat(fd)) {
+ .err => |err| return .{
+ .err = err,
+ },
+ .result => |stat_| stat_,
+ };
- if (amt == 0) {
- break;
- }
- },
+ // Only used in DOMFormData
+ if (args.offset > 0) {
+ std.os.lseek_SET(fd, args.offset) catch {};
+ }
+
+ // For certain files, the size might be 0 but the file might still have contents.
+ const size = @as(
+ u64,
+ @intCast(@max(
+ @min(
+ stat_.size,
+ @as(
+ @TypeOf(stat_.size),
+ // Only used in DOMFormData
+ @intCast(args.max_size orelse std.math.maxInt(
+ JSC.WebCore.Blob.SizeType,
+ )),
+ ),
+ ),
+ 0,
+ )),
+ ) + if (comptime string_type == .null_terminated) 1 else 0;
+
+ var buf = std.ArrayList(u8).init(bun.default_allocator);
+ buf.ensureTotalCapacityPrecise(size + 16) catch unreachable;
+ buf.expandToCapacity();
+ var total: usize = 0;
+
+ while (total < size) {
+ switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) {
+ .err => |err| return .{
+ .err = err,
+ },
+ .result => |amt| {
+ total += amt;
+ // There are cases where stat()'s size is wrong or out of date
+ if (total > size and amt != 0) {
+ buf.ensureUnusedCapacity(8096) catch unreachable;
+ buf.expandToCapacity();
+ continue;
}
- } else {
- // https://github.com/oven-sh/bun/issues/1220
- while (true) {
- switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) {
- .err => |err| return .{
- .err = err,
- },
- .result => |amt| {
- total += amt;
- // There are cases where stat()'s size is wrong or out of date
- if (total > size and amt != 0) {
- buf.ensureUnusedCapacity(8096) catch unreachable;
- buf.expandToCapacity();
- continue;
- }
- if (amt == 0) {
- break;
- }
- },
- }
+ if (amt == 0) {
+ break;
}
+ },
+ }
+ } else {
+ // https://github.com/oven-sh/bun/issues/1220
+ while (true) {
+ switch (Syscall.read(fd, buf.items.ptr[total..buf.capacity])) {
+ .err => |err| return .{
+ .err = err,
+ },
+ .result => |amt| {
+ total += amt;
+ // There are cases where stat()'s size is wrong or out of date
+ if (total > size and amt != 0) {
+ buf.ensureUnusedCapacity(8096) catch unreachable;
+ buf.expandToCapacity();
+ continue;
+ }
+
+ if (amt == 0) {
+ break;
+ }
+ },
}
+ }
+ }
- buf.items.len = if (comptime string_type == .null_terminated) total + 1 else total;
- if (total == 0) {
- buf.deinit();
- return switch (args.encoding) {
- .buffer => .{
+ buf.items.len = if (comptime string_type == .null_terminated) total + 1 else total;
+ if (total == 0) {
+ buf.deinit();
+ return switch (args.encoding) {
+ .buffer => .{
+ .result = .{
+ .buffer = Buffer.empty,
+ },
+ },
+ else => brk: {
+ if (comptime string_type == .default) {
+ break :brk .{
.result = .{
- .buffer = Buffer.empty,
+ .string = "",
},
- },
- else => brk: {
- if (comptime string_type == .default) {
- break :brk .{
- .result = .{
- .string = "",
- },
- };
- } else {
- break :brk .{
- .result = .{
- .null_terminated = "",
- },
- };
- }
+ };
+ } else {
+ break :brk .{
+ .result = .{
+ .null_terminated = "",
+ },
+ };
+ }
+ },
+ };
+ }
+
+ return switch (args.encoding) {
+ .buffer => .{
+ .result = .{
+ .buffer = Buffer.fromBytes(buf.items, bun.default_allocator, .Uint8Array),
+ },
+ },
+ else => brk: {
+ if (comptime string_type == .default) {
+ break :brk .{
+ .result = .{
+ .string = buf.items,
},
};
- }
-
- return switch (args.encoding) {
- .buffer => .{
+ } else {
+ break :brk .{
.result = .{
- .buffer = Buffer.fromBytes(buf.items, bun.default_allocator, .Uint8Array),
+ .null_terminated = buf.toOwnedSliceSentinel(0) catch unreachable,
},
- },
- else => brk: {
- if (comptime string_type == .default) {
- break :brk .{
- .result = .{
- .string = buf.items,
- },
- };
- } else {
- break :brk .{
- .result = .{
- .null_terminated = buf.toOwnedSliceSentinel(0) catch unreachable,
- },
- };
- }
- },
- };
+ };
+ }
},
- else => {},
- }
-
- return Maybe(Return.ReadFile).todo;
+ };
}
pub fn writeFileWithPathBuffer(pathbuf: *[bun.MAX_PATH_BYTES]u8, args: Arguments.WriteFile) Maybe(Return.WriteFile) {
diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig
index 88e0b30e2..b7ce9996c 100644
--- a/src/bun.js/node/node_fs_binding.zig
+++ b/src/bun.js/node/node_fs_binding.zig
@@ -108,7 +108,7 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
globalObject: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
- if (comptime FunctionEnum != .readdir and FunctionEnum != .lstat and FunctionEnum != .stat) {
+ if (comptime FunctionEnum != .readdir and FunctionEnum != .lstat and FunctionEnum != .stat and FunctionEnum != .readFile) {
globalObject.throw("Not implemented yet", .{});
return .zero;
}
@@ -122,6 +122,7 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
// we might've already thrown
if (exceptionref != null)
globalObject.throwValue(JSC.JSValue.c(exceptionref));
+ slice.deinit();
return .zero;
})
else
@@ -131,17 +132,23 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
if (exception1 != .zero) {
globalObject.throwValue(exception1);
+
+ slice.deinit();
return .zero;
}
// TODO: handle globalObject.throwValue
if (comptime FunctionEnum == .readdir) {
- return JSC.Node.AsyncReaddirTask.create(globalObject, args, slice.vm);
+ return JSC.Node.AsyncReaddirTask.create(globalObject, args, slice.vm, slice.arena);
+ }
+
+ if (comptime FunctionEnum == .readFile) {
+ return JSC.Node.AsyncReadFileTask.create(globalObject, args, slice.vm, slice.arena);
}
if (comptime FunctionEnum == .stat or FunctionEnum == .lstat) {
- return JSC.Node.AsyncStatTask.create(globalObject, args, slice.vm, FunctionEnum == .lstat);
+ return JSC.Node.AsyncStatTask.create(globalObject, args, slice.vm, FunctionEnum == .lstat, slice.arena);
}
// defer {
diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig
index dadf28629..e472f370f 100644
--- a/src/bun.js/node/types.zig
+++ b/src/bun.js/node/types.zig
@@ -629,6 +629,18 @@ pub const PathLike = union(Tag) {
}
}
+ pub fn deinitAndUnprotect(this: *const PathLike) void {
+ switch (this.*) {
+ .slice_with_underlying_string => |val| {
+ val.deinit();
+ },
+ .buffer => |val| {
+ val.buffer.value.unprotect();
+ },
+ else => {},
+ }
+ }
+
pub inline fn slice(this: PathLike) string {
return switch (this) {
.string => this.string.slice(),
@@ -917,6 +929,15 @@ pub const ArgumentsSlice = struct {
};
}
+ pub fn initAsync(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValue) ArgumentsSlice {
+ return ArgumentsSlice{
+ .remaining = bun.default_allocator.dupe(JSC.JSValue, arguments),
+ .vm = vm,
+ .all = arguments,
+ .arena = bun.ArenaAllocator.init(bun.default_allocator),
+ };
+ }
+
pub inline fn len(this: *const ArgumentsSlice) u16 {
return @as(u16, @truncate(this.remaining.len));
}
@@ -1029,6 +1050,12 @@ pub const PathOrFileDescriptor = union(Tag) {
}
}
+ pub fn deinitAndUnprotect(this: PathOrFileDescriptor) void {
+ if (this == .path) {
+ this.path.deinitAndUnprotect();
+ }
+ }
+
pub fn hash(this: JSC.Node.PathOrFileDescriptor) u64 {
return switch (this) {
.path => bun.hash(this.path.slice()),
@@ -1962,6 +1989,7 @@ pub const Path = struct {
defer str.deref();
return str.toJS(globalThis);
}
+
pub fn normalize(globalThis: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue {
if (comptime is_bindgen) return JSC.JSValue.jsUndefined();
if (args_len == 0) return JSC.ZigString.init("").toValue(globalThis);
diff --git a/src/js/node/fs.js b/src/js/node/fs.js
index f847cf179..aa03ffc5c 100644
--- a/src/js/node/fs.js
+++ b/src/js/node/fs.js
@@ -147,7 +147,13 @@ export var access = function access(...args) {
fs.readdir(...args).then(result => callback(null, result), callback);
},
readFile = function readFile(...args) {
- callbackify(fs.readFileSync, args);
+ const callback = args[args.length - 1];
+ if (typeof callback !== "function") {
+ // TODO: set code
+ throw new TypeError("Callback must be a function");
+ }
+
+ fs.readFile(...args).then(result => callback(null, result), callback);
},
writeFile = function writeFile(...args) {
callbackify(fs.writeFileSync, args);
diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts
index 1826586ae..0bf6eb9b2 100644
--- a/src/js/node/fs.promises.ts
+++ b/src/js/node/fs.promises.ts
@@ -90,7 +90,7 @@ export var access = promisify(fs.accessSync),
read = promisify(fs.readSync),
write = promisify(fs.writeSync),
readdir = fs.readdir.bind(fs),
- readFile = promisify(fs.readFileSync),
+ readFile = fs.readFile.bind(fs),
writeFile = promisify(fs.writeFileSync),
readlink = promisify(fs.readlinkSync),
realpath = promisify(fs.realpathSync),
diff --git a/src/js/out/modules/node/fs.js b/src/js/out/modules/node/fs.js
index 947eaf826..720d03134 100644
--- a/src/js/out/modules/node/fs.js
+++ b/src/js/out/modules/node/fs.js
@@ -113,7 +113,10 @@ var access = function access2(...args) {
throw new TypeError("Callback must be a function");
fs.readdir(...args).then((result) => callback(null, result), callback);
}, readFile = function readFile2(...args) {
- callbackify(fs.readFileSync, args);
+ const callback = args[args.length - 1];
+ if (typeof callback !== "function")
+ throw new TypeError("Callback must be a function");
+ fs.readFile(...args).then((result) => callback(null, result), callback);
}, writeFile = function writeFile2(...args) {
callbackify(fs.writeFileSync, args);
}, readlink = function readlink2(...args) {
diff --git a/src/js/out/modules/node/fs.promises.js b/src/js/out/modules/node/fs.promises.js
index 185955dd5..852836098 100644
--- a/src/js/out/modules/node/fs.promises.js
+++ b/src/js/out/modules/node/fs.promises.js
@@ -1 +1 @@
-var o=(g)=>{return import.meta.require(g)};function H(g,q={}){const C=[];if(g instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(g))g=g.toString();else if(typeof g!=="string")throw new TypeError("Expected path to be a string or Buffer");let B=null;if(typeof q==="string")q={encoding:q};return P.watch(g,q||{},(A,z)=>{if(C.push({eventType:A,filename:z}),B){const D=B;B=null,D()}}),{async*[Symbol.asyncIterator](){let A=!1;while(!A){while(C.length){let z=C.shift();if(z.eventType==="close"){A=!0;break}if(z.eventType==="error")throw A=!0,z.filename;yield z}await new Promise((z)=>B=z)}}}}var P=Bun.fs(),G="::bunternal::",U={[G]:(g)=>{return async function(...q){return await 1,g.apply(P,q)}}}[G],I=U(P.accessSync),J=U(P.appendFileSync),K=U(P.closeSync),L=U(P.copyFileSync),M=U(P.existsSync),N=U(P.chownSync),O=U(P.chmodSync),Q=U(P.fchmodSync),S=U(P.fchownSync),V=U(P.fstatSync),X=U(P.fsyncSync),Y=U(P.ftruncateSync),Z=U(P.futimesSync),_=U(P.lchmodSync),$=U(P.lchownSync),T=U(P.linkSync),W=P.lstat.bind(P),j=U(P.mkdirSync),x=U(P.mkdtempSync),E=U(P.openSync),F=U(P.readSync),w=U(P.writeSync),k=P.readdir.bind(P),R=U(P.readFileSync),h=U(P.writeFileSync),b=U(P.readlinkSync),u=U(P.realpathSync),c=U(P.renameSync),d=P.stat.bind(P),v=U(P.symlinkSync),a=U(P.truncateSync),l=U(P.unlinkSync),y=U(P.utimesSync),p=U(P.lutimesSync),m=U(P.rmSync),n=U(P.rmdirSync),t=(g,q,C)=>{return new Promise((B,A)=>{try{var z=P.writevSync(g,q,C)}catch(D){A(D);return}B({bytesWritten:z,buffers:q})})},r=(g,q,C)=>{return new Promise((B,A)=>{try{var z=P.readvSync(g,q,C)}catch(D){A(D);return}B({bytesRead:z,buffers:q})})},f={access:I,appendFile:J,close:K,copyFile:L,exists:M,chown:N,chmod:O,fchmod:Q,fchown:S,fstat:V,fsync:X,ftruncate:Y,futimes:Z,lchmod:_,lchown:$,link:T,lstat:W,mkdir:j,mkdtemp:x,open:E,read:F,write:w,readdir:k,readFile:R,writeFile:h,readlink:b,realpath:u,rename:c,stat:d,symlink:v,truncate:a,unlink:l,utimes:y,lutimes:p,rm:m,rmdir:n,watch:H,writev:t,readv:r,constants,[Symbol.for("CommonJS")]:0};export{t as writev,h as writeFile,w as write,H as watch,y as utimes,l as unlink,a as truncate,v as symlink,d as stat,n as rmdir,m as rm,c as rename,u as realpath,r as readv,b as readlink,k as readdir,R as readFile,F as read,E as open,x as mkdtemp,j as mkdir,p as lutimes,W as lstat,T as link,$ as lchown,_ as lchmod,Z as futimes,Y as ftruncate,X as fsync,V as fstat,S as fchown,Q as fchmod,M as exists,f as default,L as copyFile,K as close,N as chown,O as chmod,J as appendFile,I as access};
+var o=(S)=>{return import.meta.require(S)};function G(S,U={}){const A=[];if(S instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(S))S=S.toString();else if(typeof S!=="string")throw new TypeError("Expected path to be a string or Buffer");let z=null;if(typeof U==="string")U={encoding:U};return C.watch(S,U||{},(q,g)=>{if(A.push({eventType:q,filename:g}),z){const B=z;z=null,B()}}),{async*[Symbol.asyncIterator](){let q=!1;while(!q){while(A.length){let g=A.shift();if(g.eventType==="close"){q=!0;break}if(g.eventType==="error")throw q=!0,g.filename;yield g}await new Promise((g)=>z=g)}}}}var C=Bun.fs(),D="::bunternal::",J={[D]:(S)=>{return async function(...U){return await 1,S.apply(C,U)}}}[D],H=J(C.accessSync),I=J(C.appendFileSync),K=J(C.closeSync),L=J(C.copyFileSync),M=J(C.existsSync),N=J(C.chownSync),O=J(C.chmodSync),P=J(C.fchmodSync),Q=J(C.fchownSync),V=J(C.fstatSync),X=J(C.fsyncSync),Y=J(C.ftruncateSync),Z=J(C.futimesSync),_=J(C.lchmodSync),$=J(C.lchownSync),T=J(C.linkSync),W=C.lstat.bind(C),j=J(C.mkdirSync),x=J(C.mkdtempSync),E=J(C.openSync),F=J(C.readSync),k=J(C.writeSync),R=C.readdir.bind(C),w=C.readFile.bind(C),h=J(C.writeFileSync),b=J(C.readlinkSync),u=J(C.realpathSync),d=J(C.renameSync),c=C.stat.bind(C),v=J(C.symlinkSync),a=J(C.truncateSync),l=J(C.unlinkSync),y=J(C.utimesSync),p=J(C.lutimesSync),m=J(C.rmSync),n=J(C.rmdirSync),t=(S,U,A)=>{return new Promise((z,q)=>{try{var g=C.writevSync(S,U,A)}catch(B){q(B);return}z({bytesWritten:g,buffers:U})})},r=(S,U,A)=>{return new Promise((z,q)=>{try{var g=C.readvSync(S,U,A)}catch(B){q(B);return}z({bytesRead:g,buffers:U})})},i={access:H,appendFile:I,close:K,copyFile:L,exists:M,chown:N,chmod:O,fchmod:P,fchown:Q,fstat:V,fsync:X,ftruncate:Y,futimes:Z,lchmod:_,lchown:$,link:T,lstat:W,mkdir:j,mkdtemp:x,open:E,read:F,write:k,readdir:R,readFile:w,writeFile:h,readlink:b,realpath:u,rename:d,stat:c,symlink:v,truncate:a,unlink:l,utimes:y,lutimes:p,rm:m,rmdir:n,watch:G,writev:t,readv:r,constants,[Symbol.for("CommonJS")]:0};export{t as writev,h as writeFile,k as write,G as watch,y as utimes,l as unlink,a as truncate,v as symlink,c as stat,n as rmdir,m as rm,d as rename,u as realpath,r as readv,b as readlink,R as readdir,w as readFile,F as read,E as open,x as mkdtemp,j as mkdir,p as lutimes,W as lstat,T as link,$ as lchown,_ as lchmod,Z as futimes,Y as ftruncate,X as fsync,V as fstat,Q as fchown,P as fchmod,M as exists,i as default,L as copyFile,K as close,N as chown,O as chmod,I as appendFile,H as access};