@augment-vir/node
Version:
A collection of augments, helpers types, functions, and classes only for Node.js (backend) JavaScript environments.
85 lines (84 loc) • 3.38 kB
JavaScript
import { log } from '@augment-vir/common';
import { convertDuration } from '@date-vir/duration';
import { createInterface } from 'node:readline';
const defaultAskQuestionOptions = {
timeout: { seconds: 60 },
hideUserInput: false,
};
/**
* Asks the user a question in their terminal and then waits for them to type something in response.
* The response is accepted once the user inputs a new line.
*
* @category Node : Terminal
* @category Package : @augment-vir/node
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
* @see
* - {@link askQuestionUntilConditionMet}: ask a question on loop until the user provides a valid response.
*/
export async function askQuestion(questionToAsk, { hideUserInput = defaultAskQuestionOptions.hideUserInput, timeout = defaultAskQuestionOptions.timeout, } = defaultAskQuestionOptions) {
const cliInterface = createInterface({
input: process.stdin,
output: process.stdout,
});
if (hideUserInput) {
let promptWritten = false;
/** _writeToOutput is not in the types OR in the Node.js documentation but is a thing. */
cliInterface._writeToOutput = (prompt) => {
if (!promptWritten) {
cliInterface.output.write(prompt);
promptWritten = true;
}
};
}
// handle killing the process
cliInterface.on('SIGINT', () => {
cliInterface.close();
process.stdout.write('\n');
process.kill(process.pid, 'SIGINT');
});
return new Promise((resolve, reject) => {
const timeoutMs = convertDuration(timeout, { milliseconds: true }).milliseconds;
const timeoutId = timeoutMs
? setTimeout(() => {
cliInterface.close();
reject(new Error(`Took too long to respond (over "${Math.floor(timeoutMs / 1000)}" seconds)`));
}, timeoutMs)
: undefined;
process.stdout.write(questionToAsk + '\n');
cliInterface.question('', (response) => {
if (timeoutId != undefined) {
clearTimeout(timeoutId);
}
cliInterface.close();
resolve(response);
});
});
}
/**
* Asks the user a question in their terminal and then waits for them to type something in response.
* The response is submitted once the user inputs a new line. If the response fails validation, the
* question is presented again.
*
* @category Node : Terminal
* @category Package : @augment-vir/node
* @package [`@augment-vir/node`](https://www.npmjs.com/package/@augment-vir/node)
* @see
* - {@link askQuestion}: ask a question and accept any response.
*/
export async function askQuestionUntilConditionMet({ questionToAsk, verifyResponseCallback, invalidInputMessage, tryCountMax = 5, ...options }) {
let wasConditionMet = false;
let retryCount = 0;
let response = '';
while (!wasConditionMet && retryCount <= tryCountMax) {
response = (await askQuestion(questionToAsk, options)).trim();
wasConditionMet = await verifyResponseCallback(response);
if (!wasConditionMet) {
log.error(invalidInputMessage);
}
retryCount++;
}
if (retryCount > tryCountMax) {
throw new Error(`Max input attempts (${tryCountMax}) exceeded.`);
}
return response;
}