Standard input/output manager for Node.js with command-line parsing, async file reading, interactive terminal, and progress bars
—
Read individual lines from standard input with stream management, automatic cleanup, and singleton stream handling for optimal resource usage.
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);
}The readLine function uses efficient singleton stream management for optimal resource usage.
Singleton Pattern:
InputStream instance across multiple callsStream Lifecycle:
close: true)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:
null when stream closesHandles 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]);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);
}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 });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
}
}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