// Copyright 2018-2025 the Deno authors. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { EventEmitter } from "node:events"; import { Buffer } from "node:buffer"; import { Mode, promises, read, write } from "node:fs"; import { core } from "ext:core/mod.js"; import { BinaryOptionsArgument, FileOptionsArgument, ReadOptions, TextOptionsArgument, } from "ext:deno_node/_fs/_fs_common.ts"; import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts"; interface WriteResult { bytesWritten: number; buffer: Buffer | string; } interface ReadResult { bytesRead: number; buffer: Buffer; } type Path = string | Buffer | URL; export class FileHandle extends EventEmitter { #rid: number; #path: Path; constructor(rid: number, path: Path) { super(); this.#rid = rid; this.#path = path; } get fd() { return this.#rid; } read( buffer: Uint8Array, offset?: number, length?: number, position?: number | null, ): Promise; read(options?: ReadOptions): Promise; read( bufferOrOpt: Uint8Array | ReadOptions, offset?: number, length?: number, position?: number | null, ): Promise { if (bufferOrOpt instanceof Uint8Array) { return new Promise((resolve, reject) => { read( this.fd, bufferOrOpt, offset, length, position, (err, bytesRead, buffer) => { if (err) reject(err); else resolve({ buffer, bytesRead }); }, ); }); } else { return new Promise((resolve, reject) => { read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => { if (err) reject(err); else resolve({ buffer, bytesRead }); }); }); } } truncate(len?: number): Promise { return fsCall(ftruncatePromise, this, len); } readFile( opt?: TextOptionsArgument | BinaryOptionsArgument | FileOptionsArgument, ): Promise { return promises.readFile(this, opt); } write( buffer: Buffer, offset: number, length: number, position: number, ): Promise; write(str: string, position: number, encoding: string): Promise; write( bufferOrStr: Uint8Array | string, offsetOrPosition: number, lengthOrEncoding: number | string, position?: number, ): Promise { if (bufferOrStr instanceof Uint8Array) { const buffer = bufferOrStr; const offset = offsetOrPosition; const length = lengthOrEncoding; return new Promise((resolve, reject) => { write( this.fd, buffer, offset, length, position, (err, bytesWritten, buffer) => { if (err) reject(err); else resolve({ buffer, bytesWritten }); }, ); }); } else { const str = bufferOrStr; const position = offsetOrPosition; const encoding = lengthOrEncoding; return new Promise((resolve, reject) => { write(this.fd, str, position, encoding, (err, bytesWritten, buffer) => { if (err) reject(err); else resolve({ buffer, bytesWritten }); }); }); } } writeFile(data, options): Promise { return fsCall(promises.writeFile, this, data, options); } writev(buffers: ArrayBufferView[], position?: number): Promise { return fsCall(writevPromise, this, buffers, position); } close(): Promise { // Note that Deno.close is not async return Promise.resolve(core.close(this.fd)); } stat(): Promise; stat(options: { bigint: false }): Promise; stat(options: { bigint: true }): Promise; stat(options?: { bigint: boolean }): Promise { return fsCall(promises.fstat, this, options); } chmod(mode: Mode): Promise { assertNotClosed(this, promises.chmod.name); return promises.chmod(this.#path, mode); } utimes( atime: number | string | Date, mtime: number | string | Date, ): Promise { assertNotClosed(this, promises.utimes.name); return promises.utimes(this.#path, atime, mtime); } chown(uid: number, gid: number): Promise { assertNotClosed(this, promises.chown.name); return promises.chown(this.#path, uid, gid); } } function assertNotClosed(handle: FileHandle, syscall: string) { if (handle.fd === -1) { const err = new Error("file closed"); throw Object.assign(err, { code: "EBADF", syscall, }); } } function fsCall(fn, handle, ...args) { assertNotClosed(handle, fn.name); return fn(handle.fd, ...args); } export default { FileHandle, };