npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

molex-ftp

v2.3.1

Published

Lightweight FTP client using native Node.js TCP sockets (net module) with zero dependencies

Readme

molex-ftp-npm

npm version npm downloads GitHub License: ISC Node.js Dependencies

Lightweight FTP client built with native Node.js TCP sockets. Zero dependencies, optimized for performance.

Features

  • Zero dependencies - Uses only native Node.js modules
  • Promise-based API - Modern async/await support
  • Streaming support - Memory-efficient downloads for large files
  • Full FTP support - Upload, download, list, delete, rename, chmod, stat, and more
  • Debug mode - See all FTP commands and responses in real-time

Installation

npm install molex-ftp

Quick Start

const FTPClient = require('molex-ftp');

const client = new FTPClient();

await client.connect({
  host: 'ftp.example.com',
  user: 'username',
  password: 'password'
});

// Upload content (string or Buffer)
await client.upload('Hello World!', '/path/to/file.txt', true);

// Upload local file to FTP server
await client.uploadFile('./local.txt', '/remote/file.txt');

// Download to memory
const data = await client.download('/path/to/file.txt');
console.log(data.toString());

// Download to local file
await client.downloadFile('/remote/file.txt', './local.txt');

await client.close();

Constructor Options

const client = new FTPClient({
  debug: false,       // Enable debug logging
  timeout: 30000,     // Command timeout in milliseconds (default: 30000)
  logger: console.log // Custom logger function
});

API Reference

Connection Methods

connect(options)

await client.connect({
  host: 'ftp.example.com',    // Required
  port: 21,                    // Default: 21
  user: 'username',            // Default: 'anonymous'
  password: 'password'         // Default: 'anonymous@'
});

close()

await client.close();

File Methods

upload(data, remotePath, ensureDir)

await client.upload('content', '/path/file.txt');           // Basic upload
await client.upload(buffer, '/path/file.bin');              // Upload Buffer
await client.upload('content', '/deep/path/file.txt', true); // Auto-create parent dirs

uploadFile(localPath, remotePath, ensureDir)

Upload local file from disk to FTP server.

await client.uploadFile('./local.txt', '/remote/file.txt');           // Upload local file
await client.uploadFile('./docs/report.pdf', '/reports/2026.pdf');   // Auto-creates parent dirs

download(remotePath)Buffer

Download file into memory as a Buffer.

const data = await client.download('/path/file.txt');
console.log(data.toString());

downloadFile(remotePath, localPath)number

Download file from FTP server to local disk.

const bytes = await client.downloadFile('/backup.zip', './local-backup.zip');
console.log(`Downloaded ${bytes} bytes`);

downloadStream(remotePath, writeStream)number

Stream download directly to a writable stream (for custom processing).

const fs = require('fs');
const fileStream = fs.createWriteStream('./local-file.bin');
const bytes = await client.downloadStream('/remote.bin', fileStream);
console.log(`Saved ${bytes} bytes to disk`);

delete(path)

await client.delete('/path/file.txt');

removeDir(path, recursive)

Remove directory, optionally with all contents.

await client.removeDir('/path/emptydir');           // Remove empty directory
await client.removeDir('/path/dir', true);          // Delete recursively with contents

rename(from, to)

await client.rename('/old.txt', '/new.txt');

exists(path)boolean

const exists = await client.exists('/path/file.txt');

stat(path)Object

Get detailed file/directory information.

const info = await client.stat('/path/file.txt');
// { exists: true, size: 1024, isFile: true, isDirectory: false }

size(path)number

const bytes = await client.size('/path/file.txt');

modifiedTime(path)Date

const date = await client.modifiedTime('/path/file.txt');

chmod(path, mode)

Change file permissions (Unix/Linux servers only).

await client.chmod('/path/file.txt', '755');    // String format
await client.chmod('/path/script.sh', 0755);    // Octal format

Directory Methods

list(path)string

Raw directory listing.

const listing = await client.list('/path');

listDetailed(path)Array

Parsed directory listing with permissions, owner, size, etc.

const files = await client.listDetailed('/path');
// [
//   { name: 'file.txt', type: 'file', permissions: '-rw-r--r--', 
//     owner: 'user', group: 'group', size: 1024, date: 'Jan 15 10:30' },
//   { name: 'subdir', type: 'directory', permissions: 'drwxr-xr-x', ... }
// ]

mkdir(path)

await client.mkdir('/path/newdir');

cd(path)

await client.cd('/path/to/directory');

pwd()string

const currentDir = await client.pwd();

ensureDir(path, recursive)

Create directory if it doesn't exist. Auto-detects file paths (with extensions) and creates parent directory.

await client.ensureDir('/deep/nested/path');       // Create directory path
await client.ensureDir('/path/to/file.txt');       // Auto-detects file, creates /path/to/
await client.ensureDir('/single', false);          // Non-recursive, parent must exist

Utility Methods

getState()Object

Get current client state for debugging.

const state = client.getState();
// {
//   connected: true,
//   authenticated: true,
//   host: 'ftp.example.com',
//   ...
// }

setDebug(enabled)

Toggle debug mode at runtime.

client.setDebug(true);

site(command)Object

Execute server-specific SITE commands.

await client.site('CHMOD 755 /path/file.txt');  // Alternative chmod
const response = await client.site('HELP');      // Get server help

Events

client.on('connected', () => console.log('TCP connection established'));
client.on('response', (line) => console.log('FTP:', line));
client.on('error', (err) => console.error('Error:', err));
client.on('close', () => console.log('Connection closed'));

Debugging

Enable debug mode to see all FTP commands and responses:

const client = new FTPClient({ debug: true });

await client.connect({ host: 'ftp.example.com', user: 'user', password: 'pass' });
// [FTP Debug] Connecting to ftp.example.com:21 as user
// [FTP Debug] TCP connection established
// [FTP Debug] <<< 220 Welcome to FTP server
// [FTP Debug] >>> USER user
// [FTP Debug] <<< 331 Password required
// [FTP Debug] >>> PASS ********
// [FTP Debug] <<< 230 Login successful

Performance

TCP optimizations are automatically applied:

  • TCP_NODELAY - Disables Nagle's algorithm for lower latency
  • Keep-alive - Detects dead connections (10s interval)

Typical transfer speeds: ~2.5 MB/s for 1MB files over standard internet connections.

For large files, use downloadFile() or downloadStream() to save directly to disk without buffering in memory:

// Download directly to disk (recommended for large files)
const bytes = await client.downloadFile('/backup.zip', './local-backup.zip');
console.log(`Saved ${bytes} bytes`);

// Or use downloadStream for custom processing
const fs = require('fs');
const fileStream = fs.createWriteStream('./large-backup.zip');
const bytes = await client.downloadStream('/backup.zip', fileStream);
console.log(`Saved ${bytes} bytes to disk`);

Error Handling

try {
  await client.upload('data', '/readonly/file.txt');
} catch (err) {
  if (err.message.includes('FTP Error 550')) {
    console.error('Permission denied');
  }
}

Example

const FTPClient = require('molex-ftp');

async function main() {
  const client = new FTPClient({ debug: true });

  try {
    await client.connect({
      host: 'ftp.example.com',
      user: 'admin',
      password: 'secret'
    });

    // Check file info
    const info = await client.stat('/backup/data.json');
    if (info.exists) {
      console.log(`File size: ${info.size} bytes`);
      const data = await client.download('/backup/data.json');
      console.log('Downloaded:', data.toString());
    }

    // Upload new file
    await client.upload('new data', '/backup/updated.json', true);
    
    await client.close();
  } catch (err) {
    console.error('FTP Error:', err.message);
  }
}

main();

License

ISC License