CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stdio

Standard input/output manager for Node.js with command-line parsing, async file reading, interactive terminal, and progress bars

Pending
Overview
Eval results
Files

readLine.mddocs/

Single Line Input

Read individual lines from standard input with stream management, automatic cleanup, and singleton stream handling for optimal resource usage.

Capabilities

Main ReadLine Function

Reads a single line from input stream with automatic stream management and resource cleanup.

/**
 * Read a single line from input stream
 * @param options - Configuration options for stream and cleanup behavior
 * @returns Promise resolving to the line content as string
 */
function readLine(options?: ReadLineOptions): Promise<string>;

interface ReadLineOptions {
  /** Input stream to read from (defaults to process.stdin) */
  stream?: NodeJS.ReadableStream;
  /** Whether to close the stream after reading (defaults to false) */
  close?: boolean;
}

Usage Examples:

import { readLine } from "stdio";

// Read single line from stdin
const userInput = await readLine();
console.log(`You entered: ${userInput}`);

// Read with explicit stream closure
const input = await readLine({ close: true });
console.log(`Final input: ${input}`);

// Read from custom stream
import { createReadStream } from 'fs';

const fileStream = createReadStream('input.txt');
const firstLine = await readLine({ stream: fileStream });
console.log(`First line from file: ${firstLine}`);

// Interactive input loop
console.log('Enter commands (type "quit" to exit):');
while (true) {
  const command = await readLine();
  if (command === 'quit') {
    // Close stream on final read
    await readLine({ close: true });
    break;
  }
  console.log(`Processing command: ${command}`);
}

// Error handling
try {
  const input = await readLine({ close: true });
  const number = parseInt(input);
  if (isNaN(number)) {
    throw new Error('Invalid number format');
  }
  console.log(`Number: ${number}`);
} catch (error) {
  console.error('Input error:', error.message);
}

Stream Management

The readLine function uses efficient singleton stream management for optimal resource usage.

Singleton Pattern:

  • Reuses the same InputStream instance across multiple calls
  • Reduces resource overhead for multiple readline operations
  • Automatically manages stream lifecycle

Stream Lifecycle:

  • Stream is created on first use
  • Remains open for subsequent reads (unless close: true)
  • Properly cleans up resources when closed

InputStream Class

Internal class that manages readline operations with buffering and event handling.

class InputStream {
  constructor(input?: NodeJS.ReadableStream);
  
  /**
   * Get the next line from the input stream
   * @returns Promise resolving to line content or null on stream end
   */
  getLine(): Promise<string>;
  
  /**
   * Close the input stream and clean up resources
   */
  close(): void;
}

Internal Implementation Details:

The InputStream class provides:

  • Event-driven Processing: Uses readline events for efficient line handling
  • Buffer Management: Maintains internal buffer for immediate line availability
  • Promise Queue: Handles multiple concurrent readLine calls correctly
  • Stream End Handling: Properly resolves with null when stream closes

Advanced Features

Buffering and Queue Management

Handles multiple readline requests efficiently:

// Multiple concurrent reads are properly queued
const promise1 = readLine();
const promise2 = readLine();
const promise3 = readLine();

// Lines are returned in order as they arrive
const [line1, line2, line3] = await Promise.all([promise1, promise2, promise3]);

Stream End Detection

Properly handles stream closure and end-of-input:

import { readLine } from "stdio";

try {
  const line = await readLine();
  if (line === null) {
    console.log('Stream ended (Ctrl+D pressed or pipe closed)');
  } else {
    console.log(`Read: ${line}`);
  }
} catch (error) {
  console.error('Read error:', error);
}

Resource Management

Efficient resource usage with proper cleanup:

// Stream stays open for multiple reads (efficient)
const name = await readLine();
const age = await readLine();
const email = await readLine();

// Explicitly close when done
await readLine({ close: true });

// Or close in final operation
const confirmation = await readLine({ close: true });

Integration Patterns

Common integration patterns for different use cases:

Interactive CLI Applications:

import { readLine } from "stdio";

async function runCLI() {
  console.log('Welcome to My CLI');
  
  while (true) {
    process.stdout.write('> ');
    const command = await readLine();
    
    if (command === 'exit') {
      console.log('Goodbye!');
      await readLine({ close: true });
      break;
    }
    
    await processCommand(command);
  }
}

Configuration Input:

async function getConfiguration() {
  const config = {};
  
  console.log('Please enter configuration:');
  config.host = await readLine();
  config.port = parseInt(await readLine());
  config.database = await readLine({ close: true });
  
  return config;
}

File Processing:

import { createReadStream } from 'fs';

async function processFileLines(filename: string) {
  const stream = createReadStream(filename);
  
  try {
    while (true) {
      const line = await readLine({ stream });
      if (line === null) break; // End of file
      
      await processLine(line);
    }
  } finally {
    // Stream is automatically closed when file ends
  }
}

Error Handling

Comprehensive error handling for various failure scenarios:

try {
  const input = await readLine();
  // Process input
} catch (error) {
  if (error.code === 'ENOENT') {
    console.error('Input stream not available');
  } else if (error.code === 'EPERM') {
    console.error('Permission denied reading input');
  } else {
    console.error('Unexpected error:', error.message);
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-stdio

docs

ask.md

getopt.md

index.md

progress-bar.md

read.md

readLine.md

tile.json