UNPKG

@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
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; }