CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mz

Promise-based wrappers for Node.js core APIs that modernize callback-based methods to work with async/await patterns

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

readline.mddocs/

Interactive Readline

Create interactive command-line interfaces with promise-based question handling and completer support. Provides modern async/await compatibility for Node.js readline operations.

Capabilities

Interface Creation

Create readline interfaces for interactive command-line input.

/**
 * Create a readline interface
 * @param input - Readable stream for input
 * @param output - Writable stream for output
 * @param completer - Auto-completion function
 * @param terminal - Whether to treat streams as TTY
 * @returns Enhanced Interface instance
 */
function createInterface(input, output, completer, terminal): Interface;

/**
 * Create a readline interface with options object
 * @param options - Interface configuration options
 * @returns Enhanced Interface instance
 */
function createInterface(options): Interface;

Enhanced Interface Class

Promise-enabled Interface class with auto-completion support.

/**
 * Enhanced readline Interface with promise support
 */
class Interface {
  /**
   * Ask a question and wait for response
   * @param query - Question string to display
   * @param callback - Optional callback for traditional usage
   * @returns Promise resolving to user's answer
   */
  question(query, callback): Promise<string>;
  
  // All other Interface methods from native readline are available
}

Auto-completion Support

Enhanced completer function handling for sync, async, and promise-based completers.

/**
 * Completer function types supported:
 * - Synchronous: (line) => [completions, line]
 * - Asynchronous: (line, callback) => void
 * - Promise-based: (line) => Promise<[completions, line]>
 */
type CompleterFunction = 
  | ((line: string) => [string[], string])
  | ((line: string, callback: (err: Error, result: [string[], string]) => void) => void)
  | ((line: string) => Promise<[string[], string]>);

Usage Examples:

const readline = require('mz/readline');
const { stdin: input, stdout: output } = process;

// Basic question and answer
async function askQuestions() {
  const rl = readline.createInterface({ input, output });
  
  try {
    const name = await rl.question('What is your name? ');
    console.log(`Hello, ${name}!`);
    
    const age = await rl.question('How old are you? ');
    console.log(`You are ${age} years old.`);
    
  } finally {
    rl.close();
  }
}

// Interactive command-line application
async function commandLineApp() {
  const rl = readline.createInterface({ input, output });
  
  console.log('Enter commands (type "quit" to exit):');
  
  try {
    while (true) {
      const command = await rl.question('> ');
      
      if (command.toLowerCase() === 'quit') {
        break;
      }
      
      // Process command
      console.log(`You entered: ${command}`);
    }
  } finally {
    rl.close();
    console.log('Goodbye!');
  }
}

// With auto-completion (synchronous)
async function withSyncCompleter() {
  function completer(line) {
    const completions = ['help', 'quit', 'save', 'load', 'status'];
    const hits = completions.filter(c => c.startsWith(line));
    return [hits.length ? hits : completions, line];
  }
  
  const rl = readline.createInterface({ 
    input, 
    output, 
    completer,
    terminal: true 
  });
  
  try {
    const command = await rl.question('Enter command (tab for completion): ');
    console.log(`Command: ${command}`);
  } finally {
    rl.close();
  }
}

// With auto-completion (promise-based)
async function withPromiseCompleter() {
  async function completer(line) {
    // Simulate async completion (e.g., from database or API)
    await new Promise(resolve => setTimeout(resolve, 10));
    
    const completions = ['apple', 'banana', 'cherry', 'date'];
    const hits = completions.filter(c => c.startsWith(line));
    return [hits.length ? hits : completions, line];
  }
  
  const rl = readline.createInterface({ 
    input, 
    output, 
    completer,
    terminal: true 
  });
  
  try {
    const fruit = await rl.question('Choose a fruit (tab for completion): ');
    console.log(`You chose: ${fruit}`);
  } finally {
    rl.close();
  }
}

// Callback support is still available
const rl = readline.createInterface({ input, output });
rl.question('What is your favorite color? ', (answer) => {
  console.log(`Your favorite color is ${answer}`);
  rl.close();
});

// Password input (hide characters)
async function passwordInput() {
  const rl = readline.createInterface({ 
    input, 
    output: process.stderr, // Use stderr to avoid logging
    terminal: true 
  });
  
  try {
    // Note: This doesn't actually hide input in basic implementation
    // For true password input, you'd need additional libraries
    const password = await rl.question('Password: ');
    console.log('Password entered (hidden from logs)');
  } finally {
    rl.close();
  }
}

// Multi-step form
async function multiStepForm() {
  const rl = readline.createInterface({ input, output });
  
  const user = {};
  
  try {
    user.name = await rl.question('Name: ');
    user.email = await rl.question('Email: ');
    user.age = parseInt(await rl.question('Age: '));
    
    console.log('\nUser information:');
    console.log(JSON.stringify(user, null, 2));
    
  } finally {
    rl.close();
  }
}

Interface Options

The createInterface() function accepts an options object:

interface InterfaceOptions {
  /** Readable stream for input */
  input: NodeJS.ReadableStream;
  /** Writable stream for output */
  output?: NodeJS.WritableStream;
  /** Auto-completion function */
  completer?: CompleterFunction;
  /** Whether to treat as TTY terminal */
  terminal?: boolean;
  /** History size limit */
  historySize?: number;
  /** String to use for prompt */
  prompt?: string;
  /** String to use for continuation prompt */
  crlfDelay?: number;
  /** Whether to remove ANSI escape codes */
  removeHistoryDuplicates?: boolean;
  /** Escape code timeout */
  escapeCodeTimeout?: number;
  /** Tab size for completion */
  tabSize?: number;
}

Auto-completion Function Types

The library automatically wraps completer functions to handle different patterns:

// Synchronous completer
function syncCompleter(line) {
  const completions = ['help', 'quit'];
  const hits = completions.filter(c => c.startsWith(line));
  return [hits, line];
}

// Asynchronous completer with callback
function asyncCompleter(line, callback) {
  setTimeout(() => {
    const completions = ['help', 'quit'];
    const hits = completions.filter(c => c.startsWith(line));
    callback(null, [hits, line]);
  }, 10);
}

// Promise-based completer
async function promiseCompleter(line) {
  const completions = ['help', 'quit'];
  const hits = completions.filter(c => c.startsWith(line));
  return [hits, line];
}

Error Handling

Readline operations can fail for various reasons:

const readline = require('mz/readline');

async function handleReadlineErrors() {
  const rl = readline.createInterface({ 
    input: process.stdin, 
    output: process.stdout 
  });
  
  try {
    const answer = await rl.question('Enter something: ');
    console.log('You entered:', answer);
  } catch (error) {
    console.error('Readline error:', error);
  } finally {
    rl.close();
  }
}

Practical Patterns

Question Validation

async function askWithValidation(rl, question, validator) {
  while (true) {
    const answer = await rl.question(question);
    if (validator(answer)) {
      return answer;
    }
    console.log('Invalid input, please try again.');
  }
}

// Usage
const rl = readline.createInterface({ input, output });
const email = await askWithValidation(
  rl, 
  'Email: ', 
  (input) => input.includes('@')
);

Menu Selection

async function showMenu(rl, options) {
  console.log('\nSelect an option:');
  options.forEach((option, index) => {
    console.log(`${index + 1}. ${option}`);
  });
  
  while (true) {
    const choice = await rl.question('Choice (1-' + options.length + '): ');
    const index = parseInt(choice) - 1;
    if (index >= 0 && index < options.length) {
      return index;
    }
    console.log('Invalid choice, please try again.');
  }
}

Implementation Notes

  • Creates custom InterfaceAsPromised class that extends native Interface
  • Automatically wraps completer functions to handle sync/async/promise patterns
  • Uses object-assign to merge with native readline exports
  • Maintains complete compatibility with native readline behavior
  • Supports both promise and callback interfaces
  • All native readline methods and properties are available on the Interface

docs

child-process.md

crypto.md

dns.md

fs.md

index.md

readline.md

zlib.md

tile.json