0
# Interactive Readline
1
2
Create interactive command-line interfaces with promise-based question handling and completer support. Provides modern async/await compatibility for Node.js readline operations.
3
4
## Capabilities
5
6
### Interface Creation
7
8
Create readline interfaces for interactive command-line input.
9
10
```javascript { .api }
11
/**
12
* Create a readline interface
13
* @param input - Readable stream for input
14
* @param output - Writable stream for output
15
* @param completer - Auto-completion function
16
* @param terminal - Whether to treat streams as TTY
17
* @returns Enhanced Interface instance
18
*/
19
function createInterface(input, output, completer, terminal): Interface;
20
21
/**
22
* Create a readline interface with options object
23
* @param options - Interface configuration options
24
* @returns Enhanced Interface instance
25
*/
26
function createInterface(options): Interface;
27
```
28
29
### Enhanced Interface Class
30
31
Promise-enabled Interface class with auto-completion support.
32
33
```javascript { .api }
34
/**
35
* Enhanced readline Interface with promise support
36
*/
37
class Interface {
38
/**
39
* Ask a question and wait for response
40
* @param query - Question string to display
41
* @param callback - Optional callback for traditional usage
42
* @returns Promise resolving to user's answer
43
*/
44
question(query, callback): Promise<string>;
45
46
// All other Interface methods from native readline are available
47
}
48
```
49
50
### Auto-completion Support
51
52
Enhanced completer function handling for sync, async, and promise-based completers.
53
54
```javascript { .api }
55
/**
56
* Completer function types supported:
57
* - Synchronous: (line) => [completions, line]
58
* - Asynchronous: (line, callback) => void
59
* - Promise-based: (line) => Promise<[completions, line]>
60
*/
61
type CompleterFunction =
62
| ((line: string) => [string[], string])
63
| ((line: string, callback: (err: Error, result: [string[], string]) => void) => void)
64
| ((line: string) => Promise<[string[], string]>);
65
```
66
67
**Usage Examples:**
68
69
```javascript
70
const readline = require('mz/readline');
71
const { stdin: input, stdout: output } = process;
72
73
// Basic question and answer
74
async function askQuestions() {
75
const rl = readline.createInterface({ input, output });
76
77
try {
78
const name = await rl.question('What is your name? ');
79
console.log(`Hello, ${name}!`);
80
81
const age = await rl.question('How old are you? ');
82
console.log(`You are ${age} years old.`);
83
84
} finally {
85
rl.close();
86
}
87
}
88
89
// Interactive command-line application
90
async function commandLineApp() {
91
const rl = readline.createInterface({ input, output });
92
93
console.log('Enter commands (type "quit" to exit):');
94
95
try {
96
while (true) {
97
const command = await rl.question('> ');
98
99
if (command.toLowerCase() === 'quit') {
100
break;
101
}
102
103
// Process command
104
console.log(`You entered: ${command}`);
105
}
106
} finally {
107
rl.close();
108
console.log('Goodbye!');
109
}
110
}
111
112
// With auto-completion (synchronous)
113
async function withSyncCompleter() {
114
function completer(line) {
115
const completions = ['help', 'quit', 'save', 'load', 'status'];
116
const hits = completions.filter(c => c.startsWith(line));
117
return [hits.length ? hits : completions, line];
118
}
119
120
const rl = readline.createInterface({
121
input,
122
output,
123
completer,
124
terminal: true
125
});
126
127
try {
128
const command = await rl.question('Enter command (tab for completion): ');
129
console.log(`Command: ${command}`);
130
} finally {
131
rl.close();
132
}
133
}
134
135
// With auto-completion (promise-based)
136
async function withPromiseCompleter() {
137
async function completer(line) {
138
// Simulate async completion (e.g., from database or API)
139
await new Promise(resolve => setTimeout(resolve, 10));
140
141
const completions = ['apple', 'banana', 'cherry', 'date'];
142
const hits = completions.filter(c => c.startsWith(line));
143
return [hits.length ? hits : completions, line];
144
}
145
146
const rl = readline.createInterface({
147
input,
148
output,
149
completer,
150
terminal: true
151
});
152
153
try {
154
const fruit = await rl.question('Choose a fruit (tab for completion): ');
155
console.log(`You chose: ${fruit}`);
156
} finally {
157
rl.close();
158
}
159
}
160
161
// Callback support is still available
162
const rl = readline.createInterface({ input, output });
163
rl.question('What is your favorite color? ', (answer) => {
164
console.log(`Your favorite color is ${answer}`);
165
rl.close();
166
});
167
168
// Password input (hide characters)
169
async function passwordInput() {
170
const rl = readline.createInterface({
171
input,
172
output: process.stderr, // Use stderr to avoid logging
173
terminal: true
174
});
175
176
try {
177
// Note: This doesn't actually hide input in basic implementation
178
// For true password input, you'd need additional libraries
179
const password = await rl.question('Password: ');
180
console.log('Password entered (hidden from logs)');
181
} finally {
182
rl.close();
183
}
184
}
185
186
// Multi-step form
187
async function multiStepForm() {
188
const rl = readline.createInterface({ input, output });
189
190
const user = {};
191
192
try {
193
user.name = await rl.question('Name: ');
194
user.email = await rl.question('Email: ');
195
user.age = parseInt(await rl.question('Age: '));
196
197
console.log('\nUser information:');
198
console.log(JSON.stringify(user, null, 2));
199
200
} finally {
201
rl.close();
202
}
203
}
204
```
205
206
## Interface Options
207
208
The `createInterface()` function accepts an options object:
209
210
```javascript { .api }
211
interface InterfaceOptions {
212
/** Readable stream for input */
213
input: NodeJS.ReadableStream;
214
/** Writable stream for output */
215
output?: NodeJS.WritableStream;
216
/** Auto-completion function */
217
completer?: CompleterFunction;
218
/** Whether to treat as TTY terminal */
219
terminal?: boolean;
220
/** History size limit */
221
historySize?: number;
222
/** String to use for prompt */
223
prompt?: string;
224
/** String to use for continuation prompt */
225
crlfDelay?: number;
226
/** Whether to remove ANSI escape codes */
227
removeHistoryDuplicates?: boolean;
228
/** Escape code timeout */
229
escapeCodeTimeout?: number;
230
/** Tab size for completion */
231
tabSize?: number;
232
}
233
```
234
235
## Auto-completion Function Types
236
237
The library automatically wraps completer functions to handle different patterns:
238
239
```javascript
240
// Synchronous completer
241
function syncCompleter(line) {
242
const completions = ['help', 'quit'];
243
const hits = completions.filter(c => c.startsWith(line));
244
return [hits, line];
245
}
246
247
// Asynchronous completer with callback
248
function asyncCompleter(line, callback) {
249
setTimeout(() => {
250
const completions = ['help', 'quit'];
251
const hits = completions.filter(c => c.startsWith(line));
252
callback(null, [hits, line]);
253
}, 10);
254
}
255
256
// Promise-based completer
257
async function promiseCompleter(line) {
258
const completions = ['help', 'quit'];
259
const hits = completions.filter(c => c.startsWith(line));
260
return [hits, line];
261
}
262
```
263
264
## Error Handling
265
266
Readline operations can fail for various reasons:
267
268
```javascript
269
const readline = require('mz/readline');
270
271
async function handleReadlineErrors() {
272
const rl = readline.createInterface({
273
input: process.stdin,
274
output: process.stdout
275
});
276
277
try {
278
const answer = await rl.question('Enter something: ');
279
console.log('You entered:', answer);
280
} catch (error) {
281
console.error('Readline error:', error);
282
} finally {
283
rl.close();
284
}
285
}
286
```
287
288
## Practical Patterns
289
290
### Question Validation
291
292
```javascript
293
async function askWithValidation(rl, question, validator) {
294
while (true) {
295
const answer = await rl.question(question);
296
if (validator(answer)) {
297
return answer;
298
}
299
console.log('Invalid input, please try again.');
300
}
301
}
302
303
// Usage
304
const rl = readline.createInterface({ input, output });
305
const email = await askWithValidation(
306
rl,
307
'Email: ',
308
(input) => input.includes('@')
309
);
310
```
311
312
### Menu Selection
313
314
```javascript
315
async function showMenu(rl, options) {
316
console.log('\nSelect an option:');
317
options.forEach((option, index) => {
318
console.log(`${index + 1}. ${option}`);
319
});
320
321
while (true) {
322
const choice = await rl.question('Choice (1-' + options.length + '): ');
323
const index = parseInt(choice) - 1;
324
if (index >= 0 && index < options.length) {
325
return index;
326
}
327
console.log('Invalid choice, please try again.');
328
}
329
}
330
```
331
332
## Implementation Notes
333
334
- Creates custom `InterfaceAsPromised` class that extends native `Interface`
335
- Automatically wraps completer functions to handle sync/async/promise patterns
336
- Uses `object-assign` to merge with native readline exports
337
- Maintains complete compatibility with native readline behavior
338
- Supports both promise and callback interfaces
339
- All native readline methods and properties are available on the Interface