CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-metro-memory-fs

A memory-based implementation of Node.js fs module for testing purposes

Overall
score

96%

Overview
Eval results
Files

file-descriptors.mddocs/

File Descriptors

Low-level file descriptor operations for fine-grained control over file access, positioning, and data manipulation. File descriptors provide precise control over file operations with manual buffer management.

Capabilities

File Opening

Open files and obtain file descriptors for low-level operations.

/**
 * Synchronously open a file and return a file descriptor
 * @param path - File path to open
 * @param flags - File open flags (string or number)
 * @param mode - File mode for newly created files
 * @returns File descriptor number
 */
openSync(path: string | Buffer, flags: string | number, mode?: number): number;

/**
 * Asynchronously open a file and return a file descriptor
 * @param path - File path to open
 * @param flags - File open flags (string or number)
 * @param mode - File mode for newly created files
 * @param callback - Completion callback with file descriptor
 */
open(path: string | Buffer, flags: string | number, mode?: number, callback?: (err?: Error, fd?: number) => void): void;
open(path: string | Buffer, flags: string | number, callback: (err?: Error, fd?: number) => void): void;

File Flags:

  • 'r' - Open for reading (file must exist)
  • 'r+' - Open for reading and writing (file must exist)
  • 'w' - Open for writing (truncates file or creates new)
  • 'wx' - Open for writing exclusively (fails if file exists)
  • 'w+' - Open for reading and writing (truncates or creates)
  • 'wx+' - Open for reading and writing exclusively

Usage Examples:

// Open file for reading
const fd = fs.openSync('/data.txt', 'r');

// Open for writing with specific mode
const writeFd = fs.openSync('/output.txt', 'w', 0o644);

// Open exclusively (fail if exists)
try {
  const exclusiveFd = fs.openSync('/unique.txt', 'wx');
  console.log('File opened exclusively');
} catch (err) {
  console.log('File already exists');
}

// Async file opening
fs.open('/async.txt', 'r+', (err, fd) => {
  if (!err) {
    console.log('File opened with fd:', fd);
    // Remember to close the file descriptor
    fs.close(fd, () => {});
  }
});

// Promise-based opening
const fd = await fs.promises.open('/promise.txt', 'w');

File Closing

Close file descriptors to free system resources.

/**
 * Synchronously close a file descriptor
 * @param fd - File descriptor to close
 */
closeSync(fd: number): void;

/**
 * Asynchronously close a file descriptor
 * @param fd - File descriptor to close
 * @param callback - Completion callback
 */
close(fd: number, callback: (err?: Error) => void): void;

Usage Examples:

// Open, use, and close file
const fd = fs.openSync('/temp.txt', 'w');
try {
  // ... use file descriptor
  fs.writeSync(fd, 'Hello World');
} finally {
  fs.closeSync(fd); // Always close
}

// Async close
fs.open('/file.txt', 'r', (err, fd) => {
  if (!err) {
    // ... use fd
    fs.close(fd, (closeErr) => {
      if (!closeErr) {
        console.log('File closed successfully');
      }
    });
  }
});

Reading with File Descriptors

Read data from files using file descriptors with precise buffer control.

/**
 * Synchronously read data from a file descriptor into a buffer
 * @param fd - File descriptor to read from
 * @param buffer - Buffer to write data into
 * @param offset - Offset in buffer to start writing
 * @param length - Number of bytes to read
 * @param position - Position in file to read from (null for current position)
 * @returns Number of bytes read
 */
readSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number | null): number;

/**
 * Asynchronously read data from a file descriptor into a buffer
 * @param fd - File descriptor to read from
 * @param buffer - Buffer to write data into
 * @param offset - Offset in buffer to start writing
 * @param length - Number of bytes to read
 * @param position - Position in file to read from (null for current position)
 * @param callback - Completion callback with bytes read
 */
read(fd: number, buffer: Buffer, offset: number, length: number, position?: number | null, callback?: (err?: Error, bytesRead?: number) => void): void;

Usage Examples:

// Read file content into buffer
const fd = fs.openSync('/data.txt', 'r');
const buffer = Buffer.alloc(1024);

try {
  const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
  const content = buffer.subarray(0, bytesRead).toString('utf8');
  console.log('Read content:', content);
} finally {
  fs.closeSync(fd);
}

// Read from specific position
const fd2 = fs.openSync('/large-file.txt', 'r');
const chunk = Buffer.alloc(100);
const bytesRead = fs.readSync(fd2, chunk, 0, 100, 500); // Read 100 bytes from position 500
fs.closeSync(fd2);

// Async reading
fs.open('/async-read.txt', 'r', (err, fd) => {
  if (err) return;
  
  const buffer = Buffer.alloc(256);
  fs.read(fd, buffer, 0, buffer.length, null, (readErr, bytesRead) => {
    if (!readErr) {
      const data = buffer.subarray(0, bytesRead);
      console.log('Read data:', data.toString());
    }
    fs.close(fd, () => {});
  });
});

Writing with File Descriptors

Write data to files using file descriptors with precise positioning control.

/**
 * Synchronously write data from a buffer or string to a file descriptor
 * @param fd - File descriptor to write to
 * @param buffer - Buffer or string containing data to write
 * @param offset - Offset in buffer to start reading from (for Buffer)
 * @param length - Number of bytes to write (for Buffer)
 * @param position - Position in file to write to (null for current position)
 * @returns Number of bytes written
 */
writeSync(fd: number, buffer: Buffer, offset?: number, length?: number, position?: number | null): number;
writeSync(fd: number, string: string, position?: number | null, encoding?: string): number;

/**
 * Asynchronously write data from a buffer or string to a file descriptor
 * @param fd - File descriptor to write to
 * @param buffer - Buffer or string containing data to write
 * @param offset - Offset in buffer to start reading from
 * @param length - Number of bytes to write
 * @param position - Position in file to write to (null for current position)
 * @param callback - Completion callback with bytes written
 */
write(fd: number, buffer: Buffer, offset?: number, length?: number, position?: number | null, callback?: (err?: Error, bytesWritten?: number) => void): void;
write(fd: number, string: string, position?: number | null, encoding?: string, callback?: (err?: Error, bytesWritten?: number) => void): void;

Usage Examples:

// Write string to file
const fd = fs.openSync('/output.txt', 'w');
try {
  const bytesWritten = fs.writeSync(fd, 'Hello, World!');
  console.log(`Wrote ${bytesWritten} bytes`);
} finally {
  fs.closeSync(fd);
}

// Write buffer to specific position
const fd2 = fs.openSync('/patchable.txt', 'r+');
const data = Buffer.from('patched');
fs.writeSync(fd2, data, 0, data.length, 10); // Write at position 10
fs.closeSync(fd2);

// Write with encoding
const fd3 = fs.openSync('/unicode.txt', 'w');
fs.writeSync(fd3, 'Hello 世界', 0, 'utf8');
fs.closeSync(fd3);

// Async writing
fs.open('/async-write.txt', 'w', (err, fd) => {
  if (err) return;
  
  const buffer = Buffer.from('Async data');
  fs.write(fd, buffer, 0, buffer.length, null, (writeErr, bytesWritten) => {
    if (!writeErr) {
      console.log(`Wrote ${bytesWritten} bytes`);
    }
    fs.close(fd, () => {});
  });
});

File Descriptor Statistics

Get file information using file descriptors.

/**
 * Synchronously get file statistics using a file descriptor
 * @param fd - File descriptor
 * @returns Stats object with file information
 */
fstatSync(fd: number): Stats;

/**
 * Asynchronously get file statistics using a file descriptor
 * @param fd - File descriptor
 * @param callback - Completion callback with stats
 */
fstat(fd: number, callback: (err?: Error, stats?: Stats) => void): void;

interface Stats {
  isFile(): boolean;
  isDirectory(): boolean;
  isSymbolicLink(): boolean;
  size: number;
  mode: number;
  uid: number;
  gid: number;
  atime: Date;
  mtime: Date;
  ctime: Date;
  birthtime: Date;
  // ... other properties
}

Usage Examples:

// Get file stats using file descriptor
const fd = fs.openSync('/file.txt', 'r');
try {
  const stats = fs.fstatSync(fd);
  console.log(`File size: ${stats.size} bytes`);
  console.log(`Is file: ${stats.isFile()}`);
  console.log(`Modified: ${stats.mtime}`);
} finally {
  fs.closeSync(fd);
}

// Async stats
fs.open('/data.txt', 'r', (err, fd) => {
  if (err) return;
  
  fs.fstat(fd, (statErr, stats) => {
    if (!statErr) {
      console.log('File info:', {
        size: stats.size,
        isFile: stats.isFile(),
        modified: stats.mtime
      });
    }
    fs.close(fd, () => {});
  });
});

File Descriptor Permissions

Change file permissions using file descriptors.

/**
 * Synchronously change file permissions using a file descriptor
 * @param fd - File descriptor
 * @param mode - New file mode (permissions)
 */
fchmodSync(fd: number, mode: number | string): void;

/**
 * Asynchronously change file permissions using a file descriptor
 * @param fd - File descriptor
 * @param mode - New file mode (permissions)
 * @param callback - Completion callback
 */
fchmod(fd: number, mode: number | string, callback?: (err?: Error) => void): void;

Usage Examples:

// Change file permissions via file descriptor
const fd = fs.openSync('/secure.txt', 'r+');
try {
  fs.fchmodSync(fd, 0o600); // Owner read/write only
  console.log('Permissions changed');
} finally {
  fs.closeSync(fd);
}

// Async permission change
fs.open('/data.txt', 'r+', (err, fd) => {
  if (err) return;
  
  fs.fchmod(fd, '644', (chmodErr) => {
    if (!chmodErr) {
      console.log('Permissions updated');
    }
    fs.close(fd, () => {});
  });
});

File Synchronization

Force file data to be written to storage.

/**
 * Synchronously force file data and metadata to be written to storage
 * @param fd - File descriptor
 */
fsyncSync(fd: number): void;

/**
 * Synchronously force file data (not metadata) to be written to storage
 * @param fd - File descriptor
 */
fdatasyncSync(fd: number): void;

/**
 * Asynchronously force file data and metadata to be written to storage
 * @param fd - File descriptor
 * @param callback - Completion callback
 */
fsync(fd: number, callback?: (err?: Error) => void): void;

/**
 * Asynchronously force file data (not metadata) to be written to storage
 * @param fd - File descriptor
 * @param callback - Completion callback
 */
fdatasync(fd: number, callback?: (err?: Error) => void): void;

Usage Examples:

// Ensure data is written to disk
const fd = fs.openSync('/critical.txt', 'w');
try {
  fs.writeSync(fd, 'Important data');
  fs.fsyncSync(fd); // Force write to disk
  console.log('Data synchronized to storage');
} finally {
  fs.closeSync(fd);
}

// Data-only sync (faster than fsync)
fs.fdatasyncSync(fd); // Sync data but not metadata

// Async sync
fs.open('/log.txt', 'w', (err, fd) => {
  if (err) return;
  
  fs.write(fd, 'Log entry', (writeErr) => {
    if (writeErr) return;
    
    fs.fsync(fd, (syncErr) => {
      if (!syncErr) {
        console.log('Log entry synchronized');
      }
      fs.close(fd, () => {});
    });
  });
});

Best Practices

// Always close file descriptors
function safeFileOperation(path) {
  const fd = fs.openSync(path, 'r');
  try {
    // Perform operations
    const buffer = Buffer.alloc(1024);
    const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
    return buffer.subarray(0, bytesRead);
  } finally {
    fs.closeSync(fd); // Always close
  }
}

// Use try-finally for exception safety
function writeToFile(path, data) {
  const fd = fs.openSync(path, 'w');
  try {
    fs.writeSync(fd, data);
    fs.fsyncSync(fd); // Ensure data is written
  } finally {
    fs.closeSync(fd);
  }
}

// Handle async operations properly
function asyncFileRead(path, callback) {
  fs.open(path, 'r', (err, fd) => {
    if (err) return callback(err);
    
    const buffer = Buffer.alloc(1024);
    fs.read(fd, buffer, 0, buffer.length, 0, (readErr, bytesRead) => {
      // Always close fd, even on error
      fs.close(fd, (closeErr) => {
        if (readErr) return callback(readErr);
        if (closeErr) return callback(closeErr);
        
        callback(null, buffer.subarray(0, bytesRead));
      });
    });
  });
}

Error Handling

Common file descriptor errors:

  • EBADF - Bad file descriptor (already closed or invalid)
  • EMFILE - Too many open files
  • ENOENT - File doesn't exist (openSync with 'r' flag)
  • EEXIST - File exists (openSync with 'wx' flag)
  • EISDIR - Is a directory (cannot open directory as file)
  • EPERM - Permission denied

Install with Tessl CLI

npx tessl i tessl/npm-metro-memory-fs

docs

directory-operations.md

file-descriptors.md

file-operations.md

file-watching.md

index.md

stats-permissions.md

streams.md

symbolic-links.md

tile.json