diff options
Diffstat (limited to 'src/javascript/jsc/node/node_fs.zig')
-rw-r--r-- | src/javascript/jsc/node/node_fs.zig | 3657 |
1 files changed, 0 insertions, 3657 deletions
diff --git a/src/javascript/jsc/node/node_fs.zig b/src/javascript/jsc/node/node_fs.zig deleted file mode 100644 index 4a556f15c..000000000 --- a/src/javascript/jsc/node/node_fs.zig +++ /dev/null @@ -1,3657 +0,0 @@ -// This file contains the underlying implementation for sync & async functions -// for interacting with the filesystem from JavaScript. -// The top-level functions assume the arguments are already validated -const std = @import("std"); -const bun = @import("../../../global.zig"); -const strings = bun.strings; -const string = bun.string; -const AsyncIO = @import("io"); -const JSC = @import("../../../jsc.zig"); -const PathString = JSC.PathString; -const Environment = bun.Environment; -const C = bun.C; -const Flavor = JSC.Node.Flavor; -const system = std.os.system; -const Maybe = JSC.Maybe; -const Encoding = JSC.Node.Encoding; -const Syscall = @import("./syscall.zig"); -const Constants = @import("./node_fs_constant.zig").Constants; -const builtin = @import("builtin"); -const os = @import("std").os; -const darwin = os.darwin; -const linux = os.linux; -const PathOrBuffer = JSC.Node.PathOrBuffer; -const PathLike = JSC.Node.PathLike; -const PathOrFileDescriptor = JSC.Node.PathOrFileDescriptor; -const FileDescriptor = JSC.Node.FileDescriptor; -const DirIterator = @import("./dir_iterator.zig"); -const Path = @import("../../../resolver/resolve_path.zig"); -const FileSystem = @import("../../../fs.zig").FileSystem; -const StringOrBuffer = JSC.Node.StringOrBuffer; -const ArgumentsSlice = JSC.Node.ArgumentsSlice; -const TimeLike = JSC.Node.TimeLike; -const Mode = JSC.Node.Mode; - -const uid_t = std.os.uid_t; -const gid_t = std.os.gid_t; - -/// u63 to allow one null bit -const ReadPosition = u63; - -const Stats = JSC.Node.Stats; -const BigIntStats = JSC.Node.BigIntStats; -const DirEnt = JSC.Node.DirEnt; - -pub const FlavoredIO = struct { - io: *AsyncIO, -}; - -pub const default_permission = Syscall.S.IRUSR | - Syscall.S.IWUSR | - Syscall.S.IRGRP | - Syscall.S.IWGRP | - Syscall.S.IROTH | - Syscall.S.IWOTH; - -const ArrayBuffer = JSC.MarkedArrayBuffer; -const Buffer = JSC.Buffer; -const FileSystemFlags = JSC.Node.FileSystemFlags; - -// 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 -// This would reduce stack size, at the cost of instruction cache misses -const Arguments = struct { - pub const Rename = struct { - old_path: PathLike, - new_path: PathLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Rename { - const old_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "oldPath must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - const new_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "newPath must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - return Rename{ .old_path = old_path, .new_path = new_path }; - } - }; - - pub const Truncate = struct { - /// Passing a file descriptor is deprecated and may result in an error being thrown in the future. - path: PathOrFileDescriptor, - len: JSC.WebCore.Blob.SizeType = 0, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Truncate { - const path = PathOrFileDescriptor.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - const len: JSC.WebCore.Blob.SizeType = brk: { - const len_value = arguments.next() orelse break :brk 0; - - if (len_value.isNumber()) { - arguments.eat(); - break :brk len_value.to(JSC.WebCore.Blob.SizeType); - } - - break :brk 0; - }; - - return Truncate{ .path = path, .len = len }; - } - }; - - pub const FTruncate = struct { - fd: FileDescriptor, - len: ?JSC.WebCore.Blob.SizeType = null, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FTruncate { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - if (exception.* != null) return null; - - const len: JSC.WebCore.Blob.SizeType = brk: { - const len_value = arguments.next() orelse break :brk 0; - if (len_value.isNumber()) { - arguments.eat(); - break :brk len_value.to(JSC.WebCore.Blob.SizeType); - } - - break :brk 0; - }; - - return FTruncate{ .fd = fd, .len = len }; - } - }; - - pub const Chown = struct { - path: PathLike, - uid: uid_t = 0, - gid: gid_t = 0, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Chown { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - const uid: uid_t = brk: { - const uid_value = arguments.next() orelse break :brk { - if (exception.* == null) { - JSC.throwInvalidArguments( - "uid is required", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - break :brk @intCast(uid_t, uid_value.toInt32()); - }; - - const gid: gid_t = brk: { - const gid_value = arguments.next() orelse break :brk { - if (exception.* == null) { - JSC.throwInvalidArguments( - "gid is required", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - break :brk @intCast(gid_t, gid_value.toInt32()); - }; - - return Chown{ .path = path, .uid = uid, .gid = gid }; - } - }; - - pub const Fchown = struct { - fd: FileDescriptor, - uid: uid_t, - gid: gid_t, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fchown { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const uid: uid_t = brk: { - const uid_value = arguments.next() orelse break :brk { - if (exception.* == null) { - JSC.throwInvalidArguments( - "uid is required", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - break :brk @intCast(uid_t, uid_value.toInt32()); - }; - - const gid: gid_t = brk: { - const gid_value = arguments.next() orelse break :brk { - if (exception.* == null) { - JSC.throwInvalidArguments( - "gid is required", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - break :brk @intCast(gid_t, gid_value.toInt32()); - }; - - return Fchown{ .fd = fd, .uid = uid, .gid = gid }; - } - }; - - pub const LChown = Chown; - - pub const Lutimes = struct { - path: PathLike, - atime: TimeLike, - mtime: TimeLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Lutimes { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - const atime = JSC.Node.timeLikeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "atime is required", - .{}, - ctx, - exception, - ); - } - - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "atime must be a number or a Date", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - const mtime = JSC.Node.timeLikeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mtime is required", - .{}, - ctx, - exception, - ); - } - - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mtime must be a number or a Date", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - return Lutimes{ .path = path, .atime = atime, .mtime = mtime }; - } - }; - - pub const Chmod = struct { - path: PathLike, - mode: Mode = 0x777, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Chmod { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - const mode: Mode = JSC.Node.modeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mode is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mode must be a string or integer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - return Chmod{ .path = path, .mode = mode }; - } - }; - - pub const FChmod = struct { - fd: FileDescriptor, - mode: Mode = 0x777, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FChmod { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - arguments.eat(); - - const mode: Mode = JSC.Node.modeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mode is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mode must be a string or integer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - return FChmod{ .fd = fd, .mode = mode }; - } - }; - - pub const LCHmod = Chmod; - - pub const Stat = struct { - path: PathLike, - big_int: bool = false, - - 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) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const big_int = brk: { - if (arguments.next()) |next_val| { - if (next_val.isObject()) { - if (next_val.isCallable(ctx.ptr().vm())) break :brk false; - arguments.eat(); - - if (next_val.getIfPropertyExists(ctx.ptr(), "bigint")) |big_int| { - break :brk big_int.toBoolean(); - } - } - } - break :brk false; - }; - - if (exception.* != null) return null; - - return Stat{ .path = path, .big_int = big_int }; - } - }; - - pub const Fstat = struct { - fd: FileDescriptor, - big_int: bool = false, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fstat { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "file descriptor must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const big_int = brk: { - if (arguments.next()) |next_val| { - if (next_val.isObject()) { - if (next_val.isCallable(ctx.ptr().vm())) break :brk false; - arguments.eat(); - - if (next_val.getIfPropertyExists(ctx.ptr(), "bigint")) |big_int| { - break :brk big_int.toBoolean(); - } - } - } - break :brk false; - }; - - if (exception.* != null) return null; - - return Fstat{ .fd = fd, .big_int = big_int }; - } - }; - - pub const Lstat = Stat; - - pub const Link = struct { - old_path: PathLike, - new_path: PathLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Link { - const old_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "oldPath must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const new_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "newPath must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Link{ .old_path = old_path, .new_path = new_path }; - } - }; - - pub const Symlink = struct { - old_path: PathLike, - new_path: PathLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Symlink { - const old_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "target must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const new_path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - if (arguments.next()) |next_val| { - // The type argument is only available on Windows and - // ignored on other platforms. It can be set to 'dir', - // 'file', or 'junction'. If the type argument is not set, - // Node.js will autodetect target type and use 'file' or - // 'dir'. If the target does not exist, 'file' will be used. - // Windows junction points require the destination path to - // be absolute. When using 'junction', the target argument - // will automatically be normalized to absolute path. - if (next_val.isString()) { - comptime if (Environment.isWindows) @compileError("Add support for type argument on Windows"); - arguments.eat(); - } - } - - return Symlink{ .old_path = old_path, .new_path = new_path }; - } - }; - - pub const Readlink = struct { - path: PathLike, - encoding: Encoding = Encoding.utf8, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readlink { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - var encoding = Encoding.utf8; - if (arguments.next()) |val| { - arguments.eat(); - - switch (val.jsType()) { - JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject => { - encoding = Encoding.fromStringValue(val, ctx.ptr()) orelse Encoding.utf8; - }, - else => { - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse Encoding.utf8; - } - } - }, - } - } - - return Readlink{ .path = path, .encoding = encoding }; - } - }; - - pub const Realpath = struct { - path: PathLike, - encoding: Encoding = Encoding.utf8, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Realpath { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - var encoding = Encoding.utf8; - if (arguments.next()) |val| { - arguments.eat(); - - switch (val.jsType()) { - JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject => { - encoding = Encoding.fromStringValue(val, ctx.ptr()) orelse Encoding.utf8; - }, - else => { - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse Encoding.utf8; - } - } - }, - } - } - - return Realpath{ .path = path, .encoding = encoding }; - } - }; - - pub const Unlink = struct { - path: PathLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Unlink { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Unlink{ - .path = path, - }; - } - }; - - pub const Rm = struct { - path: PathLike, - force: bool = false, - max_retries: u32 = 0, - recursive: bool = false, - retry_delay: c_uint = 100, - }; - - pub const RmDir = struct { - path: PathLike, - - max_retries: u32 = 0, - recursive: bool = false, - retry_delay: c_uint = 100, - }; - - /// https://github.com/nodejs/node/blob/master/lib/fs.js#L1285 - pub const Mkdir = struct { - path: PathLike, - /// Indicates whether parent folders should be created. - /// If a folder was created, the path to the first created folder will be returned. - /// @default false - recursive: bool = false, - /// A file mode. If a string is passed, it is parsed as an octal integer. If not specified - /// @default - mode: Mode = 0o777, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Mkdir { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var recursive = false; - var mode: Mode = 0o777; - - if (arguments.next()) |val| { - arguments.eat(); - - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "recursive")) |recursive_| { - recursive = recursive_.toBoolean(); - } - - if (val.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { - mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse mode; - } - } - } - - return Mkdir{ - .path = path, - .recursive = recursive, - .mode = mode, - }; - } - }; - - const MkdirTemp = struct { - prefix: string = "", - encoding: Encoding = Encoding.utf8, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?MkdirTemp { - const prefix_value = arguments.next() orelse return MkdirTemp{}; - - var prefix = JSC.ZigString.Empty; - prefix_value.toZigString(&prefix, ctx.ptr()); - - if (exception.* != null) return null; - - arguments.eat(); - - var encoding = Encoding.utf8; - - if (arguments.next()) |val| { - arguments.eat(); - - switch (val.jsType()) { - JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject => { - encoding = Encoding.fromStringValue(val, ctx.ptr()) orelse Encoding.utf8; - }, - else => { - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse Encoding.utf8; - } - } - }, - } - } - - return MkdirTemp{ - .prefix = prefix.slice(), - .encoding = encoding, - }; - } - }; - - pub const Readdir = struct { - path: PathLike, - encoding: Encoding = Encoding.utf8, - with_file_types: bool = false, - - 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) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var encoding = Encoding.utf8; - var with_file_types = false; - - if (arguments.next()) |val| { - arguments.eat(); - - switch (val.jsType()) { - JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject => { - encoding = Encoding.fromStringValue(val, ctx.ptr()) orelse Encoding.utf8; - }, - else => { - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse Encoding.utf8; - } - - if (val.getIfPropertyExists(ctx.ptr(), "withFileTypes")) |with_file_types_| { - with_file_types = with_file_types_.toBoolean(); - } - } - }, - } - } - - return Readdir{ - .path = path, - .encoding = encoding, - .with_file_types = with_file_types, - }; - } - }; - - pub const Close = struct { - fd: FileDescriptor, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Close { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Close{ - .fd = fd, - }; - } - }; - - pub const Open = struct { - path: PathLike, - flags: FileSystemFlags = FileSystemFlags.@"r", - mode: Mode = default_permission, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Open { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var flags = FileSystemFlags.@"r"; - var mode: Mode = default_permission; - - if (arguments.next()) |val| { - arguments.eat(); - - if (val.isObject()) { - if (val.getIfPropertyExists(ctx.ptr(), "flags")) |flags_| { - flags = FileSystemFlags.fromJS(ctx, flags_, exception) orelse flags; - } - - if (val.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { - mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse mode; - } - } else if (!val.isEmpty()) { - flags = FileSystemFlags.fromJS(ctx, val, exception) orelse flags; - - if (arguments.nextEat()) |next| { - mode = JSC.Node.modeFromJS(ctx, next, exception) orelse mode; - } - } - } - - if (exception.* != null) return null; - - return Open{ - .path = path, - .flags = flags, - .mode = mode, - }; - } - }; - - /// Change the file system timestamps of the object referenced by `path`. - /// - /// The `atime` and `mtime` arguments follow these rules: - /// - /// * Values can be either numbers representing Unix epoch time in seconds,`Date`s, or a numeric string like `'123456789.0'`. - /// * If the value can not be converted to a number, or is `NaN`, `Infinity` or`-Infinity`, an `Error` will be thrown. - /// @since v0.4.2 - pub const Utimes = Lutimes; - - pub const Futimes = struct { - fd: FileDescriptor, - atime: TimeLike, - mtime: TimeLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Futimes { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - arguments.eat(); - if (exception.* != null) return null; - - const atime = JSC.Node.timeLikeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "atime is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "atime must be a number, Date or string", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const mtime = JSC.Node.timeLikeFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mtime is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "mtime must be a number, Date or string", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Futimes{ - .fd = fd, - .atime = atime, - .mtime = mtime, - }; - } - }; - - pub const FSync = struct { - fd: FileDescriptor, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FSync { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return FSync{ - .fd = fd, - }; - } - }; - - /// Write `buffer` to the file specified by `fd`. If `buffer` is a normal object, it - /// must have an own `toString` function property. - /// - /// `offset` determines the part of the buffer to be written, and `length` is - /// an integer specifying the number of bytes to write. - /// - /// `position` refers to the offset from the beginning of the file where this data - /// should be written. If `typeof position !== 'number'`, the data will be written - /// at the current position. See [`pwrite(2)`](http://man7.org/linux/man-pages/man2/pwrite.2.html). - /// - /// The callback will be given three arguments `(err, bytesWritten, buffer)` where`bytesWritten` specifies how many _bytes_ were written from `buffer`. - /// - /// If this method is invoked as its `util.promisify()` ed version, it returns - /// a promise for an `Object` with `bytesWritten` and `buffer` properties. - /// - /// It is unsafe to use `fs.write()` multiple times on the same file without waiting - /// for the callback. For this scenario, {@link createWriteStream} is - /// recommended. - /// - /// On Linux, positional writes don't work when the file is opened in append mode. - /// The kernel ignores the position argument and always appends the data to - /// the end of the file. - /// @since v0.0.2 - /// - pub const Write = struct { - fd: FileDescriptor, - buffer: StringOrBuffer, - // buffer_val: JSC.JSValue = JSC.JSValue.zero, - offset: u64 = 0, - length: u64 = std.math.maxInt(u64), - position: ?ReadPosition = null, - encoding: Encoding = Encoding.buffer, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Write { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - if (exception.* != null) return null; - - const buffer = StringOrBuffer.fromJS(ctx.ptr(), arguments.arena.allocator(), arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "data is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "data must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - if (exception.* != null) return null; - - var args = Write{ - .fd = fd, - .buffer = buffer, - .encoding = switch (buffer) { - .string => Encoding.utf8, - .buffer => Encoding.buffer, - }, - }; - - arguments.eat(); - - // TODO: make this faster by passing argument count at comptime - if (arguments.next()) |current_| { - parse: { - var current = current_; - switch (buffer) { - // fs.write(fd, string[, position[, encoding]], callback) - .string => { - if (current.isNumber()) { - args.position = current.toU32(); - arguments.eat(); - current = arguments.next() orelse break :parse; - } - - if (current.isString()) { - args.encoding = Encoding.fromStringValue(current, ctx.ptr()) orelse Encoding.utf8; - arguments.eat(); - } - }, - // fs.write(fd, buffer[, offset[, length[, position]]], callback) - .buffer => { - if (!current.isNumber()) { - break :parse; - } - - if (!current.isNumber()) break :parse; - args.offset = current.toU32(); - arguments.eat(); - current = arguments.next() orelse break :parse; - - if (!current.isNumber()) break :parse; - args.length = current.toU32(); - arguments.eat(); - current = arguments.next() orelse break :parse; - - if (!current.isNumber()) break :parse; - args.position = current.toU32(); - arguments.eat(); - }, - } - } - } - - return args; - } - }; - - pub const Read = struct { - fd: FileDescriptor, - buffer: Buffer, - offset: u64 = 0, - length: u64 = std.math.maxInt(u64), - position: ?ReadPosition = null, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Read { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - arguments.eat(); - - if (exception.* != null) return null; - - const buffer = Buffer.fromJS(ctx.ptr(), arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "buffer is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "buffer must be a TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - arguments.eat(); - - var args = Read{ - .fd = fd, - .buffer = buffer, - }; - - if (arguments.next()) |current| { - arguments.eat(); - if (current.isNumber()) { - args.offset = current.toU32(); - - if (arguments.remaining.len < 2) { - JSC.throwInvalidArguments( - "length and position are required", - .{}, - ctx, - exception, - ); - - return null; - } - - args.length = arguments.remaining[0].toU32(); - - if (args.length == 0) { - JSC.throwInvalidArguments( - "length must be greater than 0", - .{}, - ctx, - exception, - ); - - return null; - } - - const position: i32 = if (arguments.remaining[1].isNumber()) - arguments.remaining[1].toInt32() - else - -1; - - args.position = if (position > -1) @intCast(ReadPosition, position) else null; - arguments.remaining = arguments.remaining[2..]; - } else if (current.isObject()) { - if (current.getIfPropertyExists(ctx.ptr(), "offset")) |num| { - args.offset = num.toU32(); - } - - if (current.getIfPropertyExists(ctx.ptr(), "length")) |num| { - args.length = num.toU32(); - } - - if (current.getIfPropertyExists(ctx.ptr(), "position")) |num| { - const position: i32 = if (num.isEmptyOrUndefinedOrNull()) -1 else num.toInt32(); - if (position > -1) { - args.position = @intCast(ReadPosition, position); - } - } - } - } - - return args; - } - }; - - /// Asynchronously reads the entire contents of a file. - /// @param path A path to a file. If a URL is provided, it must use the `file:` protocol. - /// If a file descriptor is provided, the underlying file will _not_ be closed automatically. - /// @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. - /// If a flag is not provided, it defaults to `'r'`. - pub const ReadFile = struct { - path: PathOrFileDescriptor, - encoding: Encoding = Encoding.utf8, - - flag: FileSystemFlags = FileSystemFlags.@"r", - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?ReadFile { - const path = PathOrFileDescriptor.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or a file descriptor", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var encoding = Encoding.buffer; - var flag = FileSystemFlags.@"r"; - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - encoding = Encoding.fromStringValue(arg, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } else if (arg.isObject()) { - if (arg.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - if (!encoding_.isUndefinedOrNull()) { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - - if (arg.getIfPropertyExists(ctx.ptr(), "flag")) |flag_| { - flag = FileSystemFlags.fromJS(ctx, flag_, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid flag", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - } - - // Note: Signal is not implemented - return ReadFile{ - .path = path, - .encoding = encoding, - .flag = flag, - }; - } - }; - - pub const WriteFile = struct { - encoding: Encoding = Encoding.utf8, - flag: FileSystemFlags = FileSystemFlags.@"w", - mode: Mode = 0666, - file: PathOrFileDescriptor, - data: StringOrBuffer, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?WriteFile { - const file = PathOrFileDescriptor.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or a file descriptor", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const data = StringOrBuffer.fromJS(ctx.ptr(), arguments.arena.allocator(), arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "data is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "data must be a string or TypedArray", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - arguments.eat(); - - var encoding = Encoding.buffer; - var flag = FileSystemFlags.@"w"; - var mode: Mode = default_permission; - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - encoding = Encoding.fromStringValue(arg, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } else if (arg.isObject()) { - if (arg.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - if (!encoding_.isUndefinedOrNull()) { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - - if (arg.getIfPropertyExists(ctx.ptr(), "flag")) |flag_| { - flag = FileSystemFlags.fromJS(ctx, flag_, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid flag", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { - mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid flag", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - } - - // Note: Signal is not implemented - return WriteFile{ - .file = file, - .encoding = encoding, - .flag = flag, - .mode = mode, - .data = data, - }; - } - }; - - pub const AppendFile = WriteFile; - - pub const OpenDir = struct { - path: PathLike, - encoding: Encoding = Encoding.utf8, - - /// Number of directory entries that are buffered internally when reading from the directory. Higher values lead to better performance but higher memory usage. Default: 32 - buffer_size: c_int = 32, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?OpenDir { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or a file descriptor", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var encoding = Encoding.buffer; - var buffer_size: c_int = 32; - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - encoding = Encoding.fromStringValue(arg, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } else if (arg.isObject()) { - if (arg.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding_| { - if (!encoding_.isUndefinedOrNull()) { - encoding = Encoding.fromStringValue(encoding_, ctx.ptr()) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - - if (arg.getIfPropertyExists(ctx.ptr(), "bufferSize")) |buffer_size_| { - buffer_size = buffer_size_.toInt32(); - if (buffer_size < 0) { - if (exception.* == null) { - JSC.throwInvalidArguments( - "bufferSize must be > 0", - .{}, - ctx, - exception, - ); - } - return null; - } - } - } - } - - return OpenDir{ - .path = path, - .encoding = encoding, - .buffer_size = buffer_size, - }; - } - }; - pub const Exists = struct { - path: PathLike, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Exists { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or buffer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Exists{ - .path = path, - }; - } - }; - - pub const Access = struct { - path: PathLike, - mode: FileSystemFlags = FileSystemFlags.@"r", - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Access { - const path = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "path must be a string or buffer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var mode = FileSystemFlags.@"r"; - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - mode = FileSystemFlags.fromJS(ctx, arg, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "Invalid mode", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - } - - return Access{ - .path = path, - .mode = mode, - }; - } - }; - - pub const CreateReadStream = struct { - file: PathOrFileDescriptor, - flags: FileSystemFlags = FileSystemFlags.@"r", - encoding: Encoding = Encoding.utf8, - mode: Mode = default_permission, - autoClose: bool = true, - emitClose: bool = true, - start: i32 = 0, - end: i32 = std.math.maxInt(i32), - highwater_mark: u32 = 64 * 1024, - global_object: *JSC.JSGlobalObject, - - pub fn copyToState(this: CreateReadStream, state: *JSC.Node.Readable.State) void { - state.encoding = this.encoding; - state.highwater_mark = this.highwater_mark; - state.start = this.start; - state.end = this.end; - } - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?CreateReadStream { - var path = PathLike.fromJS(ctx, arguments, exception); - if (exception.* != null) return null; - if (path == null) arguments.eat(); - - var stream = CreateReadStream{ - .file = undefined, - .global_object = ctx.ptr(), - }; - var fd: FileDescriptor = std.math.maxInt(FileDescriptor); - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - stream.encoding = Encoding.fromStringValue(arg, ctx.ptr()) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } else if (arg.isObject()) { - if (arg.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { - stream.mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid mode", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding| { - stream.encoding = Encoding.fromStringValue(encoding, ctx.ptr()) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "flags")) |flags| { - stream.flags = FileSystemFlags.fromJS(ctx, flags, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid flags", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "fd")) |flags| { - fd = JSC.Node.fileDescriptorFromJS(ctx, flags, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid file descriptor", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "autoClose")) |autoClose| { - stream.autoClose = autoClose.toBoolean(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "emitClose")) |emitClose| { - stream.emitClose = emitClose.toBoolean(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "start")) |start| { - stream.start = start.toInt32(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "end")) |end| { - stream.end = end.toInt32(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "highwaterMark")) |highwaterMark| { - stream.highwater_mark = highwaterMark.toU32(); - } - } - } - - if (fd != std.math.maxInt(FileDescriptor)) { - stream.file = .{ .fd = fd }; - } else if (path) |path_| { - stream.file = .{ .path = path_ }; - } else { - JSC.throwInvalidArguments("Missing path or file descriptor", .{}, ctx, exception); - return null; - } - return stream; - } - }; - - pub const CreateWriteStream = struct { - file: PathOrFileDescriptor, - flags: FileSystemFlags = FileSystemFlags.@"w", - encoding: Encoding = Encoding.utf8, - mode: Mode = default_permission, - autoClose: bool = true, - emitClose: bool = true, - start: i32 = 0, - highwater_mark: u32 = 256 * 1024, - global_object: *JSC.JSGlobalObject, - - pub fn copyToState(this: CreateWriteStream, state: *JSC.Node.Writable.State) void { - state.encoding = this.encoding; - state.highwater_mark = this.highwater_mark; - state.start = this.start; - state.emit_close = this.emitClose; - } - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?CreateWriteStream { - var path = PathLike.fromJS(ctx, arguments, exception); - if (exception.* != null) return null; - if (path == null) arguments.eat(); - - var stream = CreateWriteStream{ - .file = undefined, - .global_object = ctx.ptr(), - }; - var fd: FileDescriptor = std.math.maxInt(FileDescriptor); - - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isString()) { - stream.encoding = Encoding.fromStringValue(arg, ctx.ptr()) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } else if (arg.isObject()) { - if (arg.getIfPropertyExists(ctx.ptr(), "mode")) |mode_| { - stream.mode = JSC.Node.modeFromJS(ctx, mode_, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid mode", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "encoding")) |encoding| { - stream.encoding = Encoding.fromStringValue(encoding, ctx.ptr()) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid encoding", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "flags")) |flags| { - stream.flags = FileSystemFlags.fromJS(ctx, flags, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid flags", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "fd")) |flags| { - fd = JSC.Node.fileDescriptorFromJS(ctx, flags, exception) orelse { - if (exception.* != null) { - JSC.throwInvalidArguments( - "Invalid file descriptor", - .{}, - ctx, - exception, - ); - } - return null; - }; - } - - if (arg.getIfPropertyExists(ctx.ptr(), "autoClose")) |autoClose| { - stream.autoClose = autoClose.toBoolean(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "emitClose")) |emitClose| { - stream.emitClose = emitClose.toBoolean(); - } - - if (arg.getIfPropertyExists(ctx.ptr(), "start")) |start| { - stream.start = start.toInt32(); - } - } - } - - if (fd != std.math.maxInt(FileDescriptor)) { - stream.file = .{ .fd = fd }; - } else if (path) |path_| { - stream.file = .{ .path = path_ }; - } else { - JSC.throwInvalidArguments("Missing path or file descriptor", .{}, ctx, exception); - return null; - } - return stream; - } - }; - - pub const FdataSync = struct { - fd: FileDescriptor, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FdataSync { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return FdataSync{ - .fd = fd, - }; - } - }; - - pub const CopyFile = struct { - src: PathLike, - dest: PathLike, - mode: Constants.Copyfile, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?CopyFile { - const src = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "src must be a string or buffer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - const dest = PathLike.fromJS(ctx, arguments, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "dest must be a string or buffer", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - var mode: i32 = 0; - if (arguments.next()) |arg| { - arguments.eat(); - if (arg.isNumber()) { - mode = arg.toInt32(); - } - } - - return CopyFile{ - .src = src, - .dest = dest, - .mode = @intToEnum(Constants.Copyfile, mode), - }; - } - }; - - pub const WriteEv = struct { - fd: FileDescriptor, - buffers: []const ArrayBuffer, - position: ReadPosition, - }; - - pub const ReadEv = struct { - fd: FileDescriptor, - buffers: []ArrayBuffer, - position: ReadPosition, - }; - - pub const Copy = struct { - pub const FilterCallback = fn (source: string, destination: string) bool; - /// Dereference symlinks - /// @default false - dereference: bool = false, - - /// When `force` is `false`, and the destination - /// exists, throw an error. - /// @default false - errorOnExist: bool = false, - - /// Function to filter copied files/directories. Return - /// `true` to copy the item, `false` to ignore it. - filter: ?FilterCallback = null, - - /// Overwrite existing file or directory. _The copy - /// operation will ignore errors if you set this to false and the destination - /// exists. Use the `errorOnExist` option to change this behavior. - /// @default true - force: bool = true, - - /// When `true` timestamps from `src` will - /// be preserved. - /// @default false - preserve_timestamps: bool = false, - - /// Copy directories recursively. - /// @default false - recursive: bool = false, - }; - - pub const UnwatchFile = void; - pub const Watch = void; - pub const WatchFile = void; - pub const Fsync = struct { - fd: FileDescriptor, - - pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fsync { - const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "File descriptor is required", - .{}, - ctx, - exception, - ); - } - return null; - }, exception) orelse { - if (exception.* == null) { - JSC.throwInvalidArguments( - "fd must be a number", - .{}, - ctx, - exception, - ); - } - return null; - }; - - if (exception.* != null) return null; - - return Fsync{ - .fd = fd, - }; - } - }; -}; - -const Return = struct { - pub const Access = void; - pub const AppendFile = void; - pub const Close = void; - pub const CopyFile = void; - pub const Exists = bool; - pub const Fchmod = void; - pub const Chmod = void; - pub const Fchown = void; - pub const Fdatasync = void; - pub const Fstat = Stats; - pub const Rm = void; - pub const Fsync = void; - pub const Ftruncate = void; - pub const Futimes = void; - pub const Lchmod = void; - pub const Lchown = void; - pub const Link = void; - pub const Lstat = Stats; - pub const Mkdir = string; - pub const Mkdtemp = PathString; - pub const Open = FileDescriptor; - pub const WriteFile = void; - pub const Read = struct { - bytes_read: u52, - - pub fn toJS(this: Read, _: JSC.C.JSContextRef, _: JSC.C.ExceptionRef) JSC.C.JSValueRef { - return JSC.JSValue.jsNumberFromUint64(this.bytes_read).asObjectRef(); - } - }; - pub const ReadPromise = struct { - bytes_read: u52, - buffer_val: JSC.JSValue = JSC.JSValue.zero, - const fields = .{ - .@"bytesRead" = JSC.ZigString.init("bytesRead"), - .@"buffer" = JSC.ZigString.init("buffer"), - }; - pub fn toJS(this: Read, ctx: JSC.C.JSContextRef, _: JSC.C.ExceptionRef) JSC.C.JSValueRef { - defer if (!this.buffer_val.isEmptyOrUndefinedOrNull()) - JSC.C.JSValueUnprotect(ctx, this.buffer_val.asObjectRef()); - - return JSC.JSValue.createObject2( - ctx.ptr(), - &fields.bytesRead, - &fields.buffer, - JSC.JSValue.jsNumberFromUint64(@intCast(u52, @minimum(std.math.maxInt(u52), this.bytes_read))), - this.buffer_val, - ).asObjectRef(); - } - }; - - pub const WritePromise = struct { - bytes_written: u52, - buffer: StringOrBuffer, - buffer_val: JSC.JSValue = JSC.JSValue.zero, - const fields = .{ - .@"bytesWritten" = JSC.ZigString.init("bytesWritten"), - .@"buffer" = JSC.ZigString.init("buffer"), - }; - - // Excited for the issue that's like "cannot read file bigger than 2 GB" - pub fn toJS(this: Write, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { - defer if (!this.buffer_val.isEmptyOrUndefinedOrNull() and this.buffer == .buffer) - JSC.C.JSValueUnprotect(ctx, this.buffer_val.asObjectRef()); - - return JSC.JSValue.createObject2( - ctx.ptr(), - &fields.bytesWritten, - &fields.buffer, - JSC.JSValue.jsNumberFromUint64(@intCast(u52, @minimum(std.math.maxInt(u52), this.bytes_written))), - if (this.buffer == .buffer) - this.buffer_val - else - JSC.JSValue.fromRef(this.buffer.toJS(ctx, exception)), - ).asObjectRef(); - } - }; - pub const Write = struct { - bytes_written: u52, - const fields = .{ - .@"bytesWritten" = JSC.ZigString.init("bytesWritten"), - }; - - // Excited for the issue that's like "cannot read file bigger than 2 GB" - pub fn toJS(this: Write, _: JSC.C.JSContextRef, _: JSC.C.ExceptionRef) JSC.C.JSValueRef { - return JSC.JSValue.jsNumberFromUint64(this.bytes_written).asObjectRef(); - } - }; - - pub const Readdir = union(Tag) { - with_file_types: []const DirEnt, - buffers: []const Buffer, - files: []const PathString, - - pub const Tag = enum { - with_file_types, - buffers, - files, - }; - - pub fn toJS(this: Readdir, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { - return switch (this) { - .with_file_types => JSC.To.JS.withType([]const DirEnt, this.with_file_types, ctx, exception), - .buffers => JSC.To.JS.withType([]const Buffer, this.buffers, ctx, exception), - .files => JSC.To.JS.withTypeClone([]const PathString, this.files, ctx, exception, true), - }; - } - }; - pub const ReadFile = StringOrBuffer; - pub const Readlink = StringOrBuffer; - pub const Realpath = StringOrBuffer; - pub const RealpathNative = Realpath; - pub const Rename = void; - pub const Rmdir = void; - pub const Stat = Stats; - - pub const Symlink = void; - pub const Truncate = void; - pub const Unlink = void; - pub const UnwatchFile = void; - pub const Watch = void; - pub const WatchFile = void; - pub const Utimes = void; - - pub const CreateReadStream = *JSC.Node.Stream; - pub const CreateWriteStream = *JSC.Node.Stream; - pub const Chown = void; - pub const Lutimes = void; -}; - -/// Bun's implementation of the Node.js "fs" module -/// https://nodejs.org/api/fs.html -/// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/fs.d.ts -pub const NodeFS = struct { - async_io: *AsyncIO, - - /// Buffer to store a temporary file path that might appear in a returned error message. - /// - /// We want to avoid allocating a new path buffer for every error message so that JSC can clone + GC it. - /// That means a stack-allocated buffer won't suffice. Instead, we re-use - /// the heap allocated buffer on the NodefS struct - sync_error_buf: [bun.MAX_PATH_BYTES]u8 = undefined, - - pub const ReturnType = Return; - - pub fn access(this: *NodeFS, args: Arguments.Access, comptime _: Flavor) Maybe(Return.Access) { - var path = args.path.sliceZ(&this.sync_error_buf); - const rc = Syscall.system.access(path, @enumToInt(args.mode)); - return Maybe(Return.Access).errnoSysP(rc, .access, path) orelse Maybe(Return.Access).success; - } - - pub fn appendFile(this: *NodeFS, args: Arguments.AppendFile, comptime flavor: Flavor) Maybe(Return.AppendFile) { - var data = args.data.slice(); - - switch (args.file) { - .fd => |fd| { - switch (comptime flavor) { - .sync => { - while (data.len > 0) { - const written = switch (Syscall.write(fd, data)) { - .result => |result| result, - .err => |err| return .{ .err = err }, - }; - data = data[written..]; - } - - return Maybe(Return.AppendFile).success; - }, - else => { - _ = this; - @compileError("Not implemented yet"); - }, - } - }, - .path => |path_| { - const path = path_.sliceZ(&this.sync_error_buf); - switch (comptime flavor) { - .sync => { - const fd = switch (Syscall.open(path, @enumToInt(FileSystemFlags.@"a"), 000666)) { - .result => |result| result, - .err => |err| return .{ .err = err }, - }; - - defer { - _ = Syscall.close(fd); - } - - while (data.len > 0) { - const written = switch (Syscall.write(fd, data)) { - .result => |result| result, - .err => |err| return .{ .err = err }, - }; - data = data[written..]; - } - - return Maybe(Return.AppendFile).success; - }, - else => { - _ = this; - @compileError("Not implemented yet"); - }, - } - }, - } - - return Maybe(Return.AppendFile).todo; - } - - pub fn close(this: *NodeFS, args: Arguments.Close, comptime flavor: Flavor) Maybe(Return.Close) { - switch (comptime flavor) { - .sync => { - return if (Syscall.close(args.fd)) |err| .{ .err = err } else Maybe(Return.Close).success; - }, - else => { - _ = this; - }, - } - - return .{ .err = Syscall.Error.todo }; - } - - /// https://github.com/libuv/libuv/pull/2233 - /// https://github.com/pnpm/pnpm/issues/2761 - /// https://github.com/libuv/libuv/pull/2578 - /// https://github.com/nodejs/node/issues/34624 - pub fn copyFile(this: *NodeFS, args: Arguments.CopyFile, comptime flavor: Flavor) Maybe(Return.CopyFile) { - const ret = Maybe(Return.CopyFile); - - switch (comptime flavor) { - .sync => { - var src_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var dest_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var src = args.src.sliceZ(&src_buf); - var dest = args.dest.sliceZ(&dest_buf); - - if (comptime Environment.isMac) { - if (args.mode.isForceClone()) { - // https://www.manpagez.com/man/2/clonefile/ - return ret.errnoSysP(C.clonefile(src, dest, 0), .clonefile, src) orelse ret.success; - } - - var mode: Mode = C.darwin.COPYFILE_ACL | C.darwin.COPYFILE_DATA; - if (args.mode.shouldntOverwrite()) { - mode |= C.darwin.COPYFILE_EXCL; - } - - return ret.errnoSysP(C.copyfile(src, dest, null, mode), .copyfile, src) orelse ret.success; - } - - if (comptime Environment.isLinux) { - const src_fd = switch (Syscall.open(src, std.os.O.RDONLY, 0644)) { - .result => |result| result, - .err => |err| return .{ .err = err }, - }; - defer { - _ = Syscall.close(src_fd); - } - - const stat_: linux.Stat = switch (Syscall.fstat(src_fd)) { - .result => |result| result, - .err => |err| return Maybe(Return.CopyFile){ .err = err }, - }; - - if (!os.S.ISREG(stat_.mode)) { - return Maybe(Return.CopyFile){ .err = .{ .errno = @enumToInt(C.SystemErrno.ENOTSUP) } }; - } - - var flags: Mode = std.os.O.CREAT | std.os.O.WRONLY | std.os.O.TRUNC; - if (args.mode.shouldntOverwrite()) { - flags |= std.os.O.EXCL; - } - - const dest_fd = switch (Syscall.open(dest, flags, flags)) { - .result => |result| result, - .err => |err| return Maybe(Return.CopyFile){ .err = err }, - }; - defer { - _ = Syscall.close(dest_fd); - } - - var off_in_copy = @bitCast(i64, @as(u64, 0)); - var off_out_copy = @bitCast(i64, @as(u64, 0)); - - // https://manpages.debian.org/testing/manpages-dev/ioctl_ficlone.2.en.html - if (args.mode.isForceClone()) { - return Maybe(Return.CopyFile).todo; - } - - var size = @intCast(usize, @maximum(stat_.size, 0)); - - if (size == 0) { - // copy until EOF - size = std.mem.page_size; - while (true) { - // Linux Kernel 5.3 or later - const written = linux.copy_file_range(src_fd, &off_in_copy, dest_fd, &off_out_copy, size, 0); - if (ret.errnoSysP(written, .copy_file_range, dest)) |err| return err; - // wrote zero bytes means EOF - if (written == 0) break; - size -|= written; - } - } else { - while (size > 0) { - // Linux Kernel 5.3 or later - const written = linux.copy_file_range(src_fd, &off_in_copy, dest_fd, &off_out_copy, size, 0); - if (ret.errnoSysP(written, .copy_file_range, dest)) |err| return err; - // wrote zero bytes means EOF - if (written == 0) break; - size -|= written; - } - } - - return ret.success; - } - }, - else => { - _ = args; - _ = this; - _ = flavor; - }, - } - - return Maybe(Return.CopyFile).todo; - } - pub fn exists(this: *NodeFS, args: Arguments.Exists, comptime flavor: Flavor) Maybe(Return.Exists) { - const Ret = Maybe(Return.Exists); - const path = args.path.sliceZ(&this.sync_error_buf); - switch (comptime flavor) { - .sync => { - // access() may not work correctly on NFS file systems with UID - // mapping enabled, because UID mapping is done on the server and - // hidden from the client, which checks permissions. Similar - // problems can occur to FUSE mounts. - const rc = (system.access(path, std.os.F_OK)); - return Ret{ .result = rc == 0 }; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Ret.todo; - } - - pub fn chown(this: *NodeFS, args: Arguments.Chown, comptime flavor: Flavor) Maybe(Return.Chown) { - const path = args.path.sliceZ(&this.sync_error_buf); - - switch (comptime flavor) { - .sync => return Syscall.chown(path, args.uid, args.gid), - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Chown).todo; - } - - /// This should almost never be async - pub fn chmod(this: *NodeFS, args: Arguments.Chmod, comptime flavor: Flavor) Maybe(Return.Chmod) { - const path = args.path.sliceZ(&this.sync_error_buf); - - switch (comptime flavor) { - .sync => { - return Maybe(Return.Chmod).errnoSysP(C.chmod(path, args.mode), .chmod, path) orelse - Maybe(Return.Chmod).success; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Chmod).todo; - } - - /// This should almost never be async - pub fn fchmod(this: *NodeFS, args: Arguments.FChmod, comptime flavor: Flavor) Maybe(Return.Fchmod) { - switch (comptime flavor) { - .sync => { - return Syscall.fchmod(args.fd, args.mode); - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Fchmod).todo; - } - pub fn fchown(this: *NodeFS, args: Arguments.Fchown, comptime flavor: Flavor) Maybe(Return.Fchown) { - switch (comptime flavor) { - .sync => { - return Maybe(Return.Fchown).errnoSys(C.fchown(args.fd, args.uid, args.gid), .fchown) orelse - Maybe(Return.Fchown).success; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Fchown).todo; - } - pub fn fdatasync(this: *NodeFS, args: Arguments.FdataSync, comptime flavor: Flavor) Maybe(Return.Fdatasync) { - switch (comptime flavor) { - .sync => return Maybe(Return.Fdatasync).errnoSys(system.fdatasync(args.fd), .fdatasync) orelse - Maybe(Return.Fdatasync).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Fdatasync).todo; - } - pub fn fstat(this: *NodeFS, args: Arguments.Fstat, comptime flavor: Flavor) Maybe(Return.Fstat) { - if (args.big_int) return Maybe(Return.Fstat).todo; - - switch (comptime flavor) { - .sync => { - return switch (Syscall.fstat(args.fd)) { - .result => |result| Maybe(Return.Fstat){ .result = Stats.init(result) }, - .err => |err| Maybe(Return.Fstat){ .err = err }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Fstat).todo; - } - - pub fn fsync(this: *NodeFS, args: Arguments.Fsync, comptime flavor: Flavor) Maybe(Return.Fsync) { - switch (comptime flavor) { - .sync => return Maybe(Return.Fsync).errnoSys(system.fsync(args.fd), .fsync) orelse - Maybe(Return.Fsync).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Fsync).todo; - } - - pub fn ftruncate(this: *NodeFS, args: Arguments.FTruncate, comptime flavor: Flavor) Maybe(Return.Ftruncate) { - switch (comptime flavor) { - .sync => return Maybe(Return.Ftruncate).errnoSys(system.ftruncate(args.fd, args.len orelse 0), .ftruncate) orelse - Maybe(Return.Ftruncate).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Ftruncate).todo; - } - pub fn futimes(this: *NodeFS, args: Arguments.Futimes, comptime flavor: Flavor) Maybe(Return.Futimes) { - var times = [2]std.os.timespec{ - .{ - .tv_sec = args.mtime, - .tv_nsec = 0, - }, - .{ - .tv_sec = args.atime, - .tv_nsec = 0, - }, - }; - - switch (comptime flavor) { - .sync => return if (Maybe(Return.Futimes).errnoSys(system.futimens(args.fd, ×), .futimens)) |err| - err - else - Maybe(Return.Futimes).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Futimes).todo; - } - - pub fn lchmod(this: *NodeFS, args: Arguments.LCHmod, comptime flavor: Flavor) Maybe(Return.Lchmod) { - const path = args.path.sliceZ(&this.sync_error_buf); - - switch (comptime flavor) { - .sync => { - return Maybe(Return.Lchmod).errnoSysP(C.lchmod(path, args.mode), .lchmod, path) orelse - Maybe(Return.Lchmod).success; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Lchmod).todo; - } - - pub fn lchown(this: *NodeFS, args: Arguments.LChown, comptime flavor: Flavor) Maybe(Return.Lchown) { - const path = args.path.sliceZ(&this.sync_error_buf); - - switch (comptime flavor) { - .sync => { - return Maybe(Return.Lchown).errnoSysP(C.lchown(path, args.uid, args.gid), .lchown, path) orelse - Maybe(Return.Lchown).success; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Lchown).todo; - } - pub fn link(this: *NodeFS, args: Arguments.Link, comptime flavor: Flavor) Maybe(Return.Link) { - var new_path_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - const from = args.old_path.sliceZ(&this.sync_error_buf); - const to = args.new_path.sliceZ(&new_path_buf); - - switch (comptime flavor) { - .sync => { - return Maybe(Return.Link).errnoSysP(system.link(from, to, 0), .link, from) orelse - Maybe(Return.Link).success; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Link).todo; - } - pub fn lstat(this: *NodeFS, args: Arguments.Lstat, comptime flavor: Flavor) Maybe(Return.Lstat) { - if (args.big_int) return Maybe(Return.Lstat).todo; - - switch (comptime flavor) { - .sync => { - return switch (Syscall.lstat( - args.path.sliceZ( - &this.sync_error_buf, - ), - )) { - .result => |result| Maybe(Return.Lstat){ .result = Return.Lstat.init(result) }, - .err => |err| Maybe(Return.Lstat){ .err = err }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Lstat).todo; - } - - pub fn mkdir(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { - return if (args.recursive) mkdirRecursive(this, args, flavor) else mkdirNonRecursive(this, args, flavor); - } - // Node doesn't absolute the path so we don't have to either - fn mkdirNonRecursive(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { - switch (comptime flavor) { - .sync => { - const path = args.path.sliceZ(&this.sync_error_buf); - return switch (Syscall.mkdir(path, args.mode)) { - .result => Maybe(Return.Mkdir){ .result = "" }, - .err => |err| Maybe(Return.Mkdir){ .err = err }, - }; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Mkdir).todo; - } - - // TODO: windows - // TODO: verify this works correctly with unicode codepoints - fn mkdirRecursive(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { - const Option = Maybe(Return.Mkdir); - if (comptime Environment.isWindows) @compileError("This needs to be implemented on Windows."); - - switch (comptime flavor) { - // The sync version does no allocation except when returning the path - .sync => { - var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - const path = args.path.sliceZWithForceCopy(&buf, true); - const len = @truncate(u16, path.len); - - // First, attempt to create the desired directory - // If that fails, then walk back up the path until we have a match - switch (Syscall.mkdir(path, args.mode)) { - .err => |err| { - switch (err.getErrno()) { - else => { - @memcpy(&this.sync_error_buf, path.ptr, len); - return .{ .err = err.withPath(this.sync_error_buf[0..len]) }; - }, - - .EXIST => { - return Option{ .result = "" }; - }, - // continue - .NOENT => {}, - } - }, - .result => { - return Option{ .result = args.path.slice() }; - }, - } - - var working_mem = &this.sync_error_buf; - @memcpy(working_mem, path.ptr, len); - - var i: u16 = len - 1; - - // iterate backwards until creating the directory works successfully - while (i > 0) : (i -= 1) { - if (path[i] == std.fs.path.sep) { - working_mem[i] = 0; - var parent: [:0]u8 = working_mem[0..i :0]; - - switch (Syscall.mkdir(parent, args.mode)) { - .err => |err| { - working_mem[i] = std.fs.path.sep; - switch (err.getErrno()) { - .EXIST => { - // Handle race condition - break; - }, - .NOENT => { - continue; - }, - else => return .{ .err = err.withPath(parent) }, - } - }, - .result => { - // We found a parent that worked - working_mem[i] = std.fs.path.sep; - break; - }, - } - } - } - var first_match: u16 = i; - i += 1; - // after we find one that works, we go forward _after_ the first working directory - while (i < len) : (i += 1) { - if (path[i] == std.fs.path.sep) { - working_mem[i] = 0; - var parent: [:0]u8 = working_mem[0..i :0]; - - switch (Syscall.mkdir(parent, args.mode)) { - .err => |err| { - working_mem[i] = std.fs.path.sep; - switch (err.getErrno()) { - .EXIST => { - if (Environment.allow_assert) std.debug.assert(false); - continue; - }, - else => return .{ .err = err }, - } - }, - - .result => { - working_mem[i] = std.fs.path.sep; - }, - } - } - } - - working_mem[len] = 0; - - // Our final directory will not have a trailing separator - // so we have to create it once again - switch (Syscall.mkdir(working_mem[0..len :0], args.mode)) { - .err => |err| { - switch (err.getErrno()) { - // handle the race condition - .EXIST => { - var display_path: []const u8 = ""; - if (first_match != std.math.maxInt(u16)) { - // TODO: this leaks memory - display_path = bun.default_allocator.dupe(u8, display_path[0..first_match]) catch unreachable; - } - return Option{ .result = display_path }; - }, - - // NOENT shouldn't happen here - else => return .{ - .err = err.withPath(path), - }, - } - }, - .result => { - var display_path = args.path.slice(); - if (first_match != std.math.maxInt(u16)) { - // TODO: this leaks memory - display_path = bun.default_allocator.dupe(u8, display_path[0..first_match]) catch unreachable; - } - return Option{ .result = display_path }; - }, - } - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Mkdir).todo; - } - - pub fn mkdtemp(this: *NodeFS, args: Arguments.MkdirTemp, comptime flavor: Flavor) Maybe(Return.Mkdtemp) { - var prefix_buf = &this.sync_error_buf; - prefix_buf[0] = 0; - const len = args.prefix.len; - if (len > 0) { - @memcpy(prefix_buf, args.prefix.ptr, len); - prefix_buf[len] = 0; - } - - const rc = C.mkdtemp(prefix_buf); - switch (std.c.getErrno(@ptrToInt(rc))) { - .SUCCESS => {}, - else => |errno| return .{ .err = Syscall.Error{ .errno = @truncate(Syscall.Error.Int, @enumToInt(errno)), .syscall = .mkdtemp } }, - } - - _ = this; - _ = flavor; - return .{ - .result = PathString.init(bun.default_allocator.dupe(u8, std.mem.span(rc.?)) catch unreachable), - }; - } - pub fn open(this: *NodeFS, args: Arguments.Open, comptime flavor: Flavor) Maybe(Return.Open) { - switch (comptime flavor) { - // The sync version does no allocation except when returning the path - .sync => { - const path = args.path.sliceZ(&this.sync_error_buf); - return switch (Syscall.open(path, @enumToInt(args.flags), args.mode)) { - .err => |err| .{ - .err = err.withPath(args.path.slice()), - }, - .result => |fd| .{ .result = fd }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Open).todo; - } - pub fn openDir(this: *NodeFS, args: Arguments.OpenDir, comptime flavor: Flavor) Maybe(Return.OpenDir) { - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.OpenDir).todo; - } - - fn _read(this: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) { - _ = args; - _ = this; - _ = flavor; - if (Environment.allow_assert) std.debug.assert(args.position == null); - - switch (comptime flavor) { - // The sync version does no allocation except when returning the path - .sync => { - var buf = args.buffer.slice(); - buf = buf[@minimum(args.offset, buf.len)..]; - buf = buf[0..@minimum(buf.len, args.length)]; - - return switch (Syscall.read(args.fd, buf)) { - .err => |err| .{ - .err = err, - }, - .result => |amt| .{ - .result = .{ - .bytes_read = @truncate(u52, amt), - }, - }, - }; - }, - else => {}, - } - - return Maybe(Return.Read).todo; - } - - fn _pread(this: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) { - _ = this; - - switch (comptime flavor) { - .sync => { - var buf = args.buffer.slice(); - buf = buf[@minimum(args.offset, buf.len)..]; - buf = buf[0..@minimum(buf.len, args.length)]; - - return switch (Syscall.pread(args.fd, buf, args.position.?)) { - .err => |err| .{ - .err = err, - }, - .result => |amt| .{ - .result = .{ - .bytes_read = @truncate(u52, amt), - }, - }, - }; - }, - else => {}, - } - - return Maybe(Return.Read).todo; - } - - pub fn read(this: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) { - return if (args.position != null) - this._pread( - args, - comptime flavor, - ) - else - this._read( - args, - comptime flavor, - ); - } - - pub fn write(this: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) { - return if (args.position != null) _pwrite(this, args, flavor) else _write(this, args, flavor); - } - fn _write(this: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) { - _ = args; - _ = this; - _ = flavor; - - switch (comptime flavor) { - .sync => { - var buf = args.buffer.slice(); - buf = buf[@minimum(args.offset, buf.len)..]; - buf = buf[0..@minimum(buf.len, args.length)]; - - return switch (Syscall.write(args.fd, buf)) { - .err => |err| .{ - .err = err, - }, - .result => |amt| .{ - .result = .{ - .bytes_written = @truncate(u52, amt), - }, - }, - }; - }, - else => {}, - } - - return Maybe(Return.Write).todo; - } - - fn _pwrite(this: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) { - _ = args; - _ = this; - _ = flavor; - - const position = args.position.?; - - switch (comptime flavor) { - .sync => { - var buf = args.buffer.slice(); - buf = buf[@minimum(args.offset, buf.len)..]; - buf = buf[0..@minimum(args.length, buf.len)]; - - return switch (Syscall.pwrite(args.fd, buf, position)) { - .err => |err| .{ - .err = err, - }, - .result => |amt| .{ .result = .{ - .bytes_written = @truncate(u52, amt), - } }, - }; - }, - else => {}, - } - - return Maybe(Return.Write).todo; - } - - pub fn readdir(this: *NodeFS, args: Arguments.Readdir, comptime flavor: Flavor) Maybe(Return.Readdir) { - return switch (args.encoding) { - .buffer => _readdir( - this, - args, - Buffer, - flavor, - ), - else => { - if (!args.with_file_types) { - return _readdir( - this, - args, - PathString, - flavor, - ); - } - - return _readdir( - this, - args, - DirEnt, - flavor, - ); - }, - }; - } - - pub fn _readdir( - this: *NodeFS, - args: Arguments.Readdir, - comptime ExpectedType: type, - comptime flavor: Flavor, - ) Maybe(Return.Readdir) { - const file_type = comptime switch (ExpectedType) { - DirEnt => "with_file_types", - PathString => "files", - Buffer => "buffers", - else => unreachable, - }; - - switch (comptime flavor) { - .sync => { - var path = args.path.sliceZ(&this.sync_error_buf); - const flags = os.O.DIRECTORY | os.O.RDONLY; - const fd = switch (Syscall.open(path, flags, 0)) { - .err => |err| return .{ - .err = err.withPath(args.path.slice()), - }, - .result => |fd_| fd_, - }; - defer { - _ = Syscall.close(fd); - } - - var entries = std.ArrayList(ExpectedType).init(bun.default_allocator); - var dir = std.fs.Dir{ .fd = fd }; - var iterator = DirIterator.iterate(dir); - var entry = iterator.next(); - while (switch (entry) { - .err => |err| { - for (entries.items) |*item| { - switch (comptime ExpectedType) { - DirEnt => { - bun.default_allocator.free(item.name.slice()); - }, - Buffer => { - item.destroy(); - }, - PathString => { - bun.default_allocator.free(item.slice()); - }, - else => unreachable, - } - } - - entries.deinit(); - - return .{ - .err = err.withPath(args.path.slice()), - }; - }, - .result => |ent| ent, - }) |current| : (entry = iterator.next()) { - switch (comptime ExpectedType) { - DirEnt => { - entries.append(.{ - .name = PathString.init(bun.default_allocator.dupe(u8, current.name.slice()) catch unreachable), - .kind = current.kind, - }) catch unreachable; - }, - Buffer => { - const slice = current.name.slice(); - entries.append(Buffer.fromString(slice, bun.default_allocator) catch unreachable) catch unreachable; - }, - PathString => { - entries.append( - PathString.init(bun.default_allocator.dupe(u8, current.name.slice()) catch unreachable), - ) catch unreachable; - }, - else => unreachable, - } - } - - return .{ .result = @unionInit(Return.Readdir, file_type, entries.items) }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Readdir).todo; - } - pub fn readFile(this: *NodeFS, args: Arguments.ReadFile, comptime flavor: Flavor) Maybe(Return.ReadFile) { - 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); - 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)) { - .err => |err| return .{ - .err = err, - }, - .result => |stat_| stat_, - }; - - const size = @intCast(u64, @maximum(stat_.size, 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; - } - - if (amt == 0) { - break; - } - }, - } - } - buf.items.len = total; - return switch (args.encoding) { - .buffer => .{ - .result = .{ - .buffer = Buffer.fromBytes(buf.items, bun.default_allocator, .Uint8Array), - }, - }, - else => .{ - .result = .{ - .string = buf.items, - }, - }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.ReadFile).todo; - } - - pub fn writeFile(this: *NodeFS, args: Arguments.WriteFile, comptime flavor: Flavor) Maybe(Return.WriteFile) { - var path: [:0]const u8 = undefined; - - switch (comptime flavor) { - .sync => { - const fd = switch (args.file) { - .path => brk: { - path = args.file.path.sliceZ(&this.sync_error_buf); - break :brk switch (Syscall.open( - path, - @enumToInt(args.flag) | os.O.NOCTTY, - args.mode, - )) { - .err => |err| return .{ - .err = err.withPath(path), - }, - .result => |fd_| fd_, - }; - }, - .fd => |_fd| _fd, - }; - - defer { - if (args.file == .path) - _ = Syscall.close(fd); - } - - var buf = args.data.slice(); - var written: usize = 0; - - while (buf.len > 0) { - switch (Syscall.write(fd, buf)) { - .err => |err| return .{ - .err = err, - }, - .result => |amt| { - buf = buf[amt..]; - written += amt; - if (amt == 0) { - break; - } - }, - } - } - - _ = this.ftruncate(.{ .fd = fd, .len = @truncate(JSC.WebCore.Blob.SizeType, written) }, .sync); - - return Maybe(Return.WriteFile).success; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.WriteFile).todo; - } - - pub fn readlink(this: *NodeFS, args: Arguments.Readlink, comptime flavor: Flavor) Maybe(Return.Readlink) { - var outbuf: [bun.MAX_PATH_BYTES]u8 = undefined; - var inbuf = &this.sync_error_buf; - switch (comptime flavor) { - .sync => { - const path = args.path.sliceZ(inbuf); - - const len = switch (Syscall.readlink(path, &outbuf)) { - .err => |err| return .{ - .err = err.withPath(args.path.slice()), - }, - .result => |buf_| buf_, - }; - - return .{ - .result = switch (args.encoding) { - .buffer => .{ - .buffer = Buffer.fromString(outbuf[0..len], bun.default_allocator) catch unreachable, - }, - else => .{ - .string = bun.default_allocator.dupe(u8, outbuf[0..len]) catch unreachable, - }, - }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Readlink).todo; - } - pub fn realpath(this: *NodeFS, args: Arguments.Realpath, comptime flavor: Flavor) Maybe(Return.Realpath) { - var outbuf: [bun.MAX_PATH_BYTES]u8 = undefined; - var inbuf = &this.sync_error_buf; - if (comptime Environment.allow_assert) std.debug.assert(FileSystem.instance_loaded); - - switch (comptime flavor) { - .sync => { - var path_slice = args.path.slice(); - - var parts = [_]string{ FileSystem.instance.top_level_dir, path_slice }; - var path_ = FileSystem.instance.absBuf(&parts, inbuf); - inbuf[path_.len] = 0; - var path: [:0]u8 = inbuf[0..path_.len :0]; - - const flags = if (comptime Environment.isLinux) - // O_PATH is faster - std.os.O.PATH - else - std.os.O.RDONLY; - - const fd = switch (Syscall.open(path, flags, 0)) { - .err => |err| return .{ - .err = err.withPath(path), - }, - .result => |fd_| fd_, - }; - - defer { - _ = Syscall.close(fd); - } - - const buf = switch (Syscall.getFdPath(fd, &outbuf)) { - .err => |err| return .{ - .err = err.withPath(path), - }, - .result => |buf_| buf_, - }; - - return .{ - .result = switch (args.encoding) { - .buffer => .{ - .buffer = Buffer.fromString(buf, bun.default_allocator) catch unreachable, - }, - else => .{ - .string = bun.default_allocator.dupe(u8, buf) catch unreachable, - }, - }, - }; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Realpath).todo; - } - pub const realpathNative = realpath; - // pub fn realpathNative(this: *NodeFS, args: Arguments.Realpath, comptime flavor: Flavor) Maybe(Return.Realpath) { - // _ = args; - // _ = this; - // _ = flavor; - // return error.NotImplementedYet; - // } - pub fn rename(this: *NodeFS, args: Arguments.Rename, comptime flavor: Flavor) Maybe(Return.Rename) { - var from_buf = &this.sync_error_buf; - var to_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - - switch (comptime flavor) { - .sync => { - var from = args.old_path.sliceZ(from_buf); - var to = args.new_path.sliceZ(&to_buf); - return Syscall.rename(from, to); - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Rename).todo; - } - pub fn rmdir(this: *NodeFS, args: Arguments.RmDir, comptime flavor: Flavor) Maybe(Return.Rmdir) { - switch (comptime flavor) { - .sync => { - var dir = args.old_path.sliceZ(&this.sync_error_buf); - _ = dir; - }, - else => {}, - } - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Rmdir).todo; - } - pub fn rm(this: *NodeFS, args: Arguments.RmDir, comptime flavor: Flavor) Maybe(Return.Rm) { - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Rm).todo; - } - pub fn stat(this: *NodeFS, args: Arguments.Stat, comptime flavor: Flavor) Maybe(Return.Stat) { - if (args.big_int) return Maybe(Return.Stat).todo; - - switch (comptime flavor) { - .sync => { - return @as(Maybe(Return.Stat), switch (Syscall.stat( - args.path.sliceZ( - &this.sync_error_buf, - ), - )) { - .result => |result| Maybe(Return.Stat){ .result = Return.Stat.init(result) }, - .err => |err| Maybe(Return.Stat){ .err = err }, - }); - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Stat).todo; - } - - pub fn symlink(this: *NodeFS, args: Arguments.Symlink, comptime flavor: Flavor) Maybe(Return.Symlink) { - var to_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - - switch (comptime flavor) { - .sync => { - return Syscall.symlink( - args.old_path.sliceZ(&this.sync_error_buf), - args.new_path.sliceZ(&to_buf), - ); - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Symlink).todo; - } - fn _truncate(this: *NodeFS, path: PathLike, len: JSC.WebCore.Blob.SizeType, comptime flavor: Flavor) Maybe(Return.Truncate) { - switch (comptime flavor) { - .sync => { - return Maybe(Return.Truncate).errno(C.truncate(path.sliceZ(&this.sync_error_buf), len)) orelse - Maybe(Return.Truncate).success; - }, - else => {}, - } - - _ = this; - _ = flavor; - return Maybe(Return.Truncate).todo; - } - pub fn truncate(this: *NodeFS, args: Arguments.Truncate, comptime flavor: Flavor) Maybe(Return.Truncate) { - return switch (args.path) { - .fd => |fd| this.ftruncate( - Arguments.FTruncate{ .fd = fd, .len = args.len }, - flavor, - ), - .path => this._truncate( - args.path.path, - args.len, - flavor, - ), - }; - } - pub fn unlink(this: *NodeFS, args: Arguments.Unlink, comptime flavor: Flavor) Maybe(Return.Unlink) { - switch (comptime flavor) { - .sync => { - return Maybe(Return.Unlink).errnoSysP(system.unlink(args.path.sliceZ(&this.sync_error_buf)), .unlink, args.path.slice()) orelse - Maybe(Return.Unlink).success; - }, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Unlink).todo; - } - pub fn unwatchFile(this: *NodeFS, args: Arguments.UnwatchFile, comptime flavor: Flavor) Maybe(Return.UnwatchFile) { - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.UnwatchFile).todo; - } - pub fn utimes(this: *NodeFS, args: Arguments.Utimes, comptime flavor: Flavor) Maybe(Return.Utimes) { - var times = [2]std.c.timeval{ - .{ - .tv_sec = args.mtime, - // TODO: is this correct? - .tv_usec = 0, - }, - .{ - .tv_sec = args.atime, - // TODO: is this correct? - .tv_usec = 0, - }, - }; - - switch (comptime flavor) { - // futimes uses the syscall version - // we use libc because here, not for a good reason - // just missing from the linux syscall interface in zig and I don't want to modify that right now - .sync => return if (Maybe(Return.Utimes).errnoSysP(std.c.utimes(args.path.sliceZ(&this.sync_error_buf), ×), .utimes, args.path.slice())) |err| - err - else - Maybe(Return.Utimes).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Utimes).todo; - } - - pub fn lutimes(this: *NodeFS, args: Arguments.Lutimes, comptime flavor: Flavor) Maybe(Return.Lutimes) { - var times = [2]std.c.timeval{ - .{ - .tv_sec = args.mtime, - // TODO: is this correct? - .tv_usec = 0, - }, - .{ - .tv_sec = args.atime, - // TODO: is this correct? - .tv_usec = 0, - }, - }; - - switch (comptime flavor) { - // futimes uses the syscall version - // we use libc because here, not for a good reason - // just missing from the linux syscall interface in zig and I don't want to modify that right now - .sync => return if (Maybe(Return.Lutimes).errnoSysP(C.lutimes(args.path.sliceZ(&this.sync_error_buf), ×), .lutimes, args.path.slice())) |err| - err - else - Maybe(Return.Lutimes).success, - else => {}, - } - - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Lutimes).todo; - } - pub fn watch(this: *NodeFS, args: Arguments.Watch, comptime flavor: Flavor) Maybe(Return.Watch) { - _ = args; - _ = this; - _ = flavor; - return Maybe(Return.Watch).todo; - } - pub fn createReadStream(this: *NodeFS, args: Arguments.CreateReadStream, comptime flavor: Flavor) Maybe(Return.CreateReadStream) { - _ = args; - _ = this; - _ = flavor; - var stream = bun.default_allocator.create(JSC.Node.Stream) catch unreachable; - stream.* = JSC.Node.Stream{ - .sink = .{ - .readable = JSC.Node.Readable{ - .stream = stream, - .globalObject = args.global_object, - }, - }, - .sink_type = .readable, - .content = undefined, - .content_type = undefined, - .allocator = bun.default_allocator, - }; - - args.file.copyToStream(args.flags, args.autoClose, args.mode, bun.default_allocator, stream) catch unreachable; - args.copyToState(&stream.sink.readable.state); - return Maybe(Return.CreateReadStream){ .result = stream }; - } - pub fn createWriteStream(this: *NodeFS, args: Arguments.CreateWriteStream, comptime flavor: Flavor) Maybe(Return.CreateWriteStream) { - _ = args; - _ = this; - _ = flavor; - var stream = bun.default_allocator.create(JSC.Node.Stream) catch unreachable; - stream.* = JSC.Node.Stream{ - .sink = .{ - .writable = JSC.Node.Writable{ - .stream = stream, - .globalObject = args.global_object, - }, - }, - .sink_type = .writable, - .content = undefined, - .content_type = undefined, - .allocator = bun.default_allocator, - }; - args.file.copyToStream(args.flags, args.autoClose, args.mode, bun.default_allocator, stream) catch unreachable; - args.copyToState(&stream.sink.writable.state); - return Maybe(Return.CreateWriteStream){ .result = stream }; - } -}; |