UNPKG

@ibgib/helper-gib

Version:

common helper/utils/etc used in ibgib libs. Node v19+ needed for heavily-used isomorphic webcrypto hashing consumed in both node and browsers.

279 lines 9.66 kB
import { statSync } from 'node:fs'; import { readFile, } from 'node:fs/promises'; import * as readline from 'node:readline/promises'; import * as readlineNonPromise from 'node:readline'; import { stdin, stdout } from 'node:process'; // decide if use this or not import { HELPER_LOG_A_LOT } from '../constants.mjs'; import { extractErrorMsg } from './utils-helper.mjs'; import { Writable } from 'node:stream'; const logalot = HELPER_LOG_A_LOT || false; /** * @deprecated * * use {@link tryRead} * tries to read a file with the given path. * * @returns data of given file */ export async function tryRead_node({ relOrAbsPath, }) { return tryRead({ relOrAbsPath }); } export async function tryRead({ relOrAbsPath, }) { const lc = `[${tryRead.name}]`; try { const stat = statSync(relOrAbsPath); if (!stat.isFile()) { throw new Error(`path provided is not a file. (${relOrAbsPath}) (E: f295b7e925534546819edfef9a750164)`); } const resRead = await readFile(relOrAbsPath, { encoding: 'utf8' }); if (logalot) { console.log(`${lc} record found. data length: ${resRead?.length ?? 0}. fullPath: ${relOrAbsPath} (I: aa81b3d01e9542788b07302dd174c03d)`); } return resRead; } catch (error) { if (logalot) { console.log(`${lc} path not found (${relOrAbsPath})\nerror:\n${extractErrorMsg(error)} (I: 6658a0b81d3249d2aefc8e3d28efa87b)`); } return undefined; } finally { if (logalot) { console.log(`${lc} complete. (I: 747a187ca6234dd4b2bf9a11a87a0d91)`); } } } /** * Very simple helper function. * * If wanting to do more analysis, just use `statSync` directly. * * @returns true if the `relOrAbsPath` is a file, else false. */ export function isFile({ relOrAbsPath, }) { const lc = `[${isFile.name}]`; try { const stat = statSync(relOrAbsPath); return stat.isFile(); } catch (error) { console.error(`${lc} ${extractErrorMsg(error)}`); throw error; } finally { if (logalot) { console.log(`${lc} complete. (I: 747a187ca6234dd4b2bf9a11a87a0d91)`); } } } export async function promptForConfirm({ msg, yesLabel, noLabel, }) { const lc = `[${promptForConfirm.name}]`; try { msg ||= 'confirm?'; yesLabel ||= '(y)es'; noLabel ||= '(n)o'; const rl = readline.createInterface({ input: stdin, output: stdout }); try { const fnAnswerMatchesLabel = (answer, label) => { if (answer === label.replace(/[\(\)]/g, '').toLowerCase()) { // matched full label return true; /* <<<< returns early */ } else { // check for short answers const regexCaptureCharsInsideParens = /\((\w+)\)/; const shortLabel = label.match(regexCaptureCharsInsideParens); if ((shortLabel ?? []).length >= 2) { if (answer === shortLabel[1].toLowerCase()) { // matched short label return true; /* <<<< returns early */ } } } // if got this far, nothing positive matched return false; }; // try up to three times. if match found, either way, return early. const maxTries = 3; for (let i = 0; i < maxTries; i++) { let answer = await rl.question(`${msg} [${yesLabel}, ${noLabel}]\n`); if (answer) { answer = answer.toLowerCase(); const matchesYesLabel = fnAnswerMatchesLabel(answer, yesLabel); if (matchesYesLabel) { return true; /* <<<< returns early */ } const matchesNoLabel = fnAnswerMatchesLabel(answer, noLabel); if (matchesNoLabel) { return false; /* <<<< returns early */ } } else { `no answer provided. defaulting to no...`; console.log('huh?'); } } // max tries reached throw new Error(`could not confirm (E: 239797ed8da600a2531e6be4841d8623)`); } catch (error) { throw error; } finally { rl.close(); } } catch (error) { console.error(`${lc} ${error.message}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } export async function promptForBoolean({ msg, trueLabel, trueResponses, falseLabel, falseResponses, defaultNoMatchResult, warnOnUseDefault, }) { const lc = `[${promptForBoolean.name}]`; try { if (!msg) { throw new Error(`msg required (E: ecdd43e761239da9040192f56e109e23)`); } trueLabel ||= '(y)es'; falseLabel ||= '(n)o'; const rl = readline.createInterface({ input: stdin, output: stdout }); try { let answer = undefined; do { answer = await rl.question(`${msg} [${trueLabel}, ${falseLabel}]\n`); if (!answer && defaultNoMatchResult !== undefined) { if (warnOnUseDefault) { console.warn(`${lc} no answer provided. defaulting to ${defaultNoMatchResult}...`); } answer = defaultNoMatchResult ? trueLabel : falseLabel; return defaultNoMatchResult; /* <<<< returns early */ } // true/false responses default to the labels without parens and // the first letter of each (really this should be first unique // discriminating letter between true/false labels) const lowerAndSansParens = (s) => { return s.toLowerCase().replace(/[\(\)]/g, ''); }; trueResponses ??= [ lowerAndSansParens(trueLabel), lowerAndSansParens(trueLabel)[0], ]; falseResponses ??= [ lowerAndSansParens(falseLabel), lowerAndSansParens(falseLabel)[0], ]; if (trueResponses.includes(answer.toLowerCase())) { return true; } else if (falseResponses.includes(answer.toLowerCase())) { return false; } else if (defaultNoMatchResult !== undefined) { return defaultNoMatchResult; } else { console.log(`huh?`); answer = ''; } } while (!answer); throw new Error(`(UNEXPECTED) shouldn't get here. should return early with result (E: dec95d4057b8dbf4e37866374971ea23)`); } catch (error) { throw error; } finally { rl.close(); } } catch (error) { console.error(`${lc} ${error.message}`); throw error; } finally { if (logalot) { console.log(`${lc} complete.`); } } } export async function promptForSecret({ confirm, }) { const lc = `[${promptForSecret.name}]`; try { const mutableStdout = new Writable({ write: function (chunk, encoding, callback) { if (!this.muted) process.stdout.write(chunk, encoding); callback(); } }); mutableStdout.muted = false; let rl = readlineNonPromise.createInterface({ input: process.stdin, output: mutableStdout, terminal: true, }); const fnPrompt = (msg) => { return new Promise((resolve) => { rl.question(msg, function (password) { mutableStdout.muted = false; rl.write('\n'); resolve(password); }); mutableStdout.muted = true; }); }; let secret = undefined; try { do { const secret1 = await fnPrompt(`enter your secret:\n`); if (!secret1) { `no secret provided. please try again.`; continue; } if (confirm) { const secret2 = await fnPrompt(`confirm:\n`); if (secret2 === secret1) { secret = secret1; } else { console.log(`secrets do not match. please try again.`); } } else { secret = secret1; continue; } } while (!secret); return secret; } catch (error) { throw error; } finally { rl.close(); } } catch (error) { console.error(`${lc} ${error.message}`); throw error; } } /** * @deprecated * * use {@link promptForSecret} */ export async function promptForSecret_node({ confirm, }) { return promptForSecret({ confirm }); } //# sourceMappingURL=node-helper.mjs.map