diff --git a/CHANGELOG.md b/CHANGELOG.md index 43910832..42db34f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil ### Added - new: './src/flows/balance/balance.ts' - service file separated out of main flow containing the pure functions to perform balance requests for one or multiple addresses - new: './tests/balance.test.ts' - new unit tests file for balance pure functions +- new: './src/common/logger.ts' - utility file consisting of the logger used throughout the entropy cli +- new: './src/common/masking.ts' - utility helper file for EntropyLogger, used to mask private data in the payload (message) of the logging method ### Fixed - keyring retrieval method was incorrectly returning the default keyring when no keyring was found, which is not the intended flow ### Changed diff --git a/package.json b/package.json index 1c65b4dc..b9036603 100644 --- a/package.json +++ b/package.json @@ -49,12 +49,13 @@ "ansi-colors": "^4.1.3", "cli-progress": "^3.12.0", "commander": "^12.0.0", - "debug": "^4.3.4", "env-paths": "^3.0.0", "inquirer": "8.0.0", + "lodash.clonedeep": "^4.5.0", "mkdirp": "^3.0.1", "typescript": "^4.8.4", "viem": "^2.7.8", + "winston": "^3.13.0", "x25519": "^0.1.0" }, "devDependencies": { diff --git a/src/cli.ts b/src/cli.ts index 62998940..ad04efca 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -10,7 +10,6 @@ import { cliGetBalance } from './flows/balance/cli' import { cliListAccounts } from './flows/manage-accounts/cli' import { cliEntropyTransfer } from './flows/entropyTransfer/cli' import { cliSign } from './flows/sign/cli' -// import { debug } from './common/utils' const program = new Command() @@ -68,7 +67,6 @@ program.command('list') .description('List all accounts. Output is JSON of form [{ name, address, data }]') .action(async () => { // TODO: test if it's an encrypted account, if no password provided, throw because later on there's no protection from a prompt coming up - const accounts = await cliListAccounts() writeOut(accounts) process.exit(0) diff --git a/src/common/initializeEntropy.ts b/src/common/initializeEntropy.ts index 7eec720a..13c2ffdf 100644 --- a/src/common/initializeEntropy.ts +++ b/src/common/initializeEntropy.ts @@ -4,9 +4,9 @@ import Entropy, { wasmGlobalsReady } from "@entropyxyz/sdk" import Keyring from "@entropyxyz/sdk/keys" import inquirer from "inquirer" import { decrypt, encrypt } from "../flows/password" -import { debug } from "../common/utils" import * as config from "../config" import { EntropyAccountData } from "../config/types" +import { EntropyLogger } from "./logger" // TODO: unused // let defaultAccount // have a main account to use @@ -36,6 +36,7 @@ type MaybeKeyMaterial = EntropyAccountData | string // WARNING: in programatic cli mode this function should NEVER prompt users, but it will if no password was provided // This is currently caught earlier in the code export const initializeEntropy = async ({ keyMaterial, password, endpoint, configPath }: InitializeEntropyOpts): Promise => { + const logger = new EntropyLogger('initializeEntropy', endpoint) try { await wasmGlobalsReady() @@ -90,7 +91,7 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi }) keyrings.default = keyring - debug(keyring) + logger.debug(keyring) // TO-DO: fix in sdk: admin should be on kering.accounts by default // /*WANT*/ keyrings[keyring.admin.address] = keyring @@ -111,6 +112,7 @@ export const initializeEntropy = async ({ keyMaterial, password, endpoint, confi return entropy } catch (error) { + logger.error('Error while initializing entropy', error) console.error(error.message) if (error.message.includes('TimeError')) { process.exit(1) diff --git a/src/common/logger.ts b/src/common/logger.ts new file mode 100644 index 00000000..436f3b4b --- /dev/null +++ b/src/common/logger.ts @@ -0,0 +1,126 @@ +import envPaths from 'env-paths' +import { join } from 'path' +import * as winston from 'winston' +import { maskPayload } from './masking' +import { EntropyLoggerOptions } from 'src/types' + +/** + * Winston Base Log Levels for NPM + * { + * error: 0, + * warn: 1, + * info: 2, + * http: 3, + * verbose: 4, + * debug: 5, + * silly: 6 + * } + */ + +export class EntropyLogger { + protected context: string + protected endpoint: string + private winstonLogger: winston.Logger + // TO-DO: update commander with debug, testing, and level options for both programmatic and textual cli + constructor (context: string, endpoint: string, { debug, isTesting, level }: EntropyLoggerOptions = {}) { + this.context = context + this.endpoint = endpoint + + let format = winston.format.combine( + // Add timestamp key: { timestamp: 'YYYY-MM-DD HH:mm:ss.SSS' } + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss.SSS', + }), + // If message is instanceof Error, log Error's message property and stack + winston.format.errors({ stack: true }), + // Allows for string interpolation tokens '%s' in message with splat key values + // Ex. { message: 'my message %s', splat: ['test'] } -> { message: 'my message test' } + winston.format.splat(), + // Uses safe-stable-stringify to finalize full object message as string + // (prevents circular references from crashing) + winston.format.json(), + ); + + if (isTesting) { + format = winston.format.combine( + format, + winston.format.colorize({ level: true }), + winston.format.printf(info => { + let message = typeof info.message === 'object' ? JSON.stringify(info.message, null, 2) : info.message; + if (info.stack) { + message = `${message}\n${info.stack}`; + } + return `${info.level}: ${message}`; + }), + ); + } + const paths = envPaths('entropy-cryptography', { suffix: '' }) + const DEBUG_PATH = join(paths.log, 'entropy-cli.debug.log') + const ERROR_PATH = join(paths.log, 'entropy-cli.error.log') + const INFO_PATH = join(paths.log, 'entropy-cli.info.log') + + this.winstonLogger = winston.createLogger({ + level: level || 'info', + format, + defaultMeta: { service: 'Entropy CLI' }, + transports: [ + new winston.transports.File({ + level: 'error', + filename: ERROR_PATH + }), + new winston.transports.File({ + level: 'info', + filename: INFO_PATH + }), + new winston.transports.File({ + level: 'debug', + filename: DEBUG_PATH, + }), + ], + }) + + // If env var is set then stream logs to console as well as a file + if (debug) { + this.winstonLogger.add(new winston.transports.Console({ + format: winston.format.cli() + })) + } + } + + // maps to winston:error + public error (description: string, error: Error): void { + this.writeLogMsg('error', error?.message || error, this.context, description, error.stack); + } + + // maps to winston:info + public log (message: any, context?: string): void { + this.writeLogMsg('info', message, context); + } + + // maps to winston:warn + public warn (message: any, context?: string): void { + this.writeLogMsg('warn', message, context); + } + + // maps to winston:debug + public debug (message: any, context?: string): void { + this.writeLogMsg('debug', message, context); + } + + // maps to winston:verbose + public verbose (message: any, context?: string): void { + this.writeLogMsg('verbose', message, context); + } + + protected writeLogMsg (level: string, message: any, context?: string, description?: string, stack?: string) { + this.winstonLogger.log({ + level, + message: maskPayload(message), + context: context || this.context, + endpoint: this.endpoint, + description, + stack, + }); + } + +} \ No newline at end of file diff --git a/src/common/masking.ts b/src/common/masking.ts new file mode 100644 index 00000000..34ba7ac0 --- /dev/null +++ b/src/common/masking.ts @@ -0,0 +1,37 @@ +import cloneDeep from 'lodash.clonedeep' + +const DEFAULT_MASKED_FIELDS = [ + 'seed', + 'secretKey', + 'addressRaw', +]; + +export function maskPayload (payload: any): any { + const clonedPayload = cloneDeep(payload); + const maskedPayload = {} + + if (!clonedPayload) { + return clonedPayload; + } + + // maskJSONFields doesn't handle nested objects very well so we'll + // need to recursively walk to object and mask them one by one + for (const [property, value] of Object.entries(clonedPayload)) { + console.log(property, typeof value); + + if (value && typeof value === 'object') { + if (Object.keys(clonedPayload[property]).filter(key => isNaN(parseInt(key))).length === 0) { + const reconstructedUintArr: number[] = Object.values(clonedPayload[property]) + maskedPayload[property] = "base64:" + Buffer.from(reconstructedUintArr).toString("base64"); + } else { + maskedPayload[property] = maskPayload(value); + } + } else if (value && typeof value === 'string' && DEFAULT_MASKED_FIELDS.includes(property)) { + maskedPayload[property] = "*".repeat(clonedPayload[property].length) + } else { + maskedPayload[property] = value + } + } + + return maskedPayload; +} diff --git a/src/common/utils.ts b/src/common/utils.ts index ece9bdcb..b7e03451 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -1,24 +1,14 @@ import { decodeAddress, encodeAddress } from "@polkadot/keyring" import { hexToU8a, isHex } from "@polkadot/util" import { Buffer } from 'buffer' -import Debug from 'debug' import { EntropyAccountConfig } from "../config/types" -const _debug = Debug('@entropyxyz/cli') - export function stripHexPrefix (str: string): string { if (str.startsWith('0x')) return str.slice(2) return str } -export function debug (...args: any[]) { - _debug(...args.map(arg => { - return typeof arg === 'object' - ? JSON.stringify(arg, replacer, 2) - : arg - })) -} -function replacer (key, value) { +export function replacer (key, value) { if(value instanceof Uint8Array ){ return Buffer.from(value).toString('base64') } diff --git a/src/config/index.ts b/src/config/index.ts index 3ece9e38..e502b780 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -6,6 +6,7 @@ import envPaths from 'env-paths' import allMigrations from './migrations' +import { replacer } from 'src/common/utils' const paths = envPaths('entropy-cryptography', { suffix: '' }) const CONFIG_PATH = join(paths.config, 'entropy-cli.json') @@ -67,5 +68,5 @@ export function getSync (configPath = CONFIG_PATH) { export async function set (config = {}, configPath = CONFIG_PATH) { await mkdirp(dirname(configPath)) - await writeFile(configPath, JSON.stringify(config)) + await writeFile(configPath, JSON.stringify(config, replacer)) } diff --git a/src/flows/DeployPrograms/index.ts b/src/flows/DeployPrograms/index.ts index ec205398..479add72 100644 --- a/src/flows/DeployPrograms/index.ts +++ b/src/flows/DeployPrograms/index.ts @@ -3,9 +3,10 @@ import * as util from "@polkadot/util" import inquirer from "inquirer" import { readFileSync } from "fs" import { initializeEntropy } from "../../common/initializeEntropy" -import { debug, print, getSelectedAccount } from "../../common/utils" +import { print, getSelectedAccount } from "../../common/utils" +import { EntropyTuiOptions } from "src/types" -export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options) { +export async function devPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options: EntropyTuiOptions) { const { endpoint } = options const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) @@ -84,7 +85,6 @@ async function deployProgram (entropy: Entropy, account: any) { async function getOwnedPrograms (entropy: Entropy, account: any) { const userAddress = account.address - debug('Account address:',userAddress) if (!userAddress) return try { diff --git a/src/flows/UserPrograms/index.ts b/src/flows/UserPrograms/index.ts index 54fae1c9..da4e7c4f 100644 --- a/src/flows/UserPrograms/index.ts +++ b/src/flows/UserPrograms/index.ts @@ -1,11 +1,13 @@ import inquirer from "inquirer" import * as util from "@polkadot/util" import { initializeEntropy } from "../../common/initializeEntropy" -import { debug, getSelectedAccount, print } from "../../common/utils" +import { getSelectedAccount, print } from "../../common/utils" +import { EntropyLogger } from "src/common/logger"; let verifyingKey: string; -export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options) { +export async function userPrograms ({ accounts, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) { + const FLOW_CONTEXT = 'USER_PROGRAMS' const { endpoint } = options const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) @@ -76,7 +78,7 @@ export async function userPrograms ({ accounts, selectedAccount: selectedAccount message: "Enter the program pointer you wish to check:", validate: (input) => (input ? true : "Program pointer is required!"), }]) - debug('program pointer', programPointer); + logger.debug(`program pointer: ${programPointer}`, `${FLOW_CONTEXT}::PROGRAM_PRESENCE_CHECK`); const program = await entropy.programs.dev.get(programPointer); print(program); } catch (error) { diff --git a/src/flows/balance/cli.ts b/src/flows/balance/cli.ts index 2b8c7d83..8681af65 100644 --- a/src/flows/balance/cli.ts +++ b/src/flows/balance/cli.ts @@ -1,14 +1,15 @@ import { initializeEntropy } from '../../common/initializeEntropy' import * as config from '../../config' -import { debug } from '../../common/utils' import { getBalance } from './balance' +import { EntropyLogger } from 'src/common/logger' export async function cliGetBalance ({ address, password, endpoint }) { + const logger = new EntropyLogger('CLI::CHECK_BALANCE', endpoint) const storedConfig = await config.get() const account = storedConfig.accounts.find(account => account.address === address) if (!account) throw Error(`No account with address ${address}`) // QUESTION: is throwing the right response? - debug('account', account) + logger.debug('account', account) // check if data is encrypted + we have a password if (typeof account.data === 'string' && !password) { diff --git a/src/flows/balance/index.ts b/src/flows/balance/index.ts index 13bd3eb0..0481c86e 100644 --- a/src/flows/balance/index.ts +++ b/src/flows/balance/index.ts @@ -1,14 +1,17 @@ +import { EntropyLogger } from "src/common/logger"; import { initializeEntropy } from "../../common/initializeEntropy" -import { print, debug, getSelectedAccount } from "../../common/utils" +import { print, getSelectedAccount } from "../../common/utils" import { getBalance } from "./balance"; // TO-DO setup flow method to provide options to allow users to select account, // use external address, or get balances for all accounts in config -export async function checkBalance ({ accounts, selectedAccount: selectedAccountAddress }, options) { +export async function checkBalance ({ accounts, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) { + const FLOW_CONTEXT = 'CHECK_BALANCE' const { endpoint } = options - debug('endpoint', endpoint); + logger.debug(`endpoint: ${endpoint}`, FLOW_CONTEXT) const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) + logger.log(selectedAccount, FLOW_CONTEXT) const entropy = await initializeEntropy({ keyMaterial: selectedAccount.data, endpoint }); const accountAddress = selectedAccountAddress const freeBalance = await getBalance(entropy, accountAddress) diff --git a/src/flows/entropyFaucet/index.ts b/src/flows/entropyFaucet/index.ts index 52a48942..3a38942c 100644 --- a/src/flows/entropyFaucet/index.ts +++ b/src/flows/entropyFaucet/index.ts @@ -1,5 +1,5 @@ import inquirer from "inquirer" -import { print, debug, accountChoices } from "../../common/utils" +import { print, accountChoices } from "../../common/utils" import { initializeEntropy } from "../../common/initializeEntropy" export async function entropyFaucet ({ accounts }, options) { @@ -14,7 +14,6 @@ export async function entropyFaucet ({ accounts }, options) { const answers = await inquirer.prompt([accountQuestion]) const selectedAccount = answers.selectedAccount - debug('selectedAccount', selectedAccount) const recipientAddress = selectedAccount.address const aliceAccount = { diff --git a/src/flows/entropyTransfer/cli.ts b/src/flows/entropyTransfer/cli.ts index 6ed45b0c..1a0abe51 100644 --- a/src/flows/entropyTransfer/cli.ts +++ b/src/flows/entropyTransfer/cli.ts @@ -1,15 +1,17 @@ import { initializeEntropy } from '../../common/initializeEntropy' import * as config from '../../config' -import { debug, formatAmountAsHex } from '../../common/utils' +import { formatAmountAsHex } from '../../common/utils' +import { EntropyLogger } from 'src/common/logger' export async function cliEntropyTransfer ({ source, password, destination, amount, endpoint }) { + const logger = new EntropyLogger('CLI::TRANSFER', endpoint) // NOTE: password is optional, is only for source account (if that is encrypted) const storedConfig = await config.get() const account = storedConfig.accounts.find(account => account.address === source) if (!account) throw Error(`No account with address ${source}`) // QUESTION: is throwing the right response? - debug('account', account) + logger.debug('account', account) const entropy = await initializeEntropy({ keyMaterial: account.data, password, endpoint }) @@ -24,6 +26,7 @@ export async function cliEntropyTransfer ({ source, password, destination, amoun ) await tx.signAndSend (entropy.registrationManager.signer.pair, ({ status }) => { - debug('signAndSend status', status) + logger.debug('signAndSend status:') + logger.debug(status) }) } diff --git a/src/flows/manage-accounts/cli.ts b/src/flows/manage-accounts/cli.ts index c2712e66..34c6ec57 100644 --- a/src/flows/manage-accounts/cli.ts +++ b/src/flows/manage-accounts/cli.ts @@ -6,6 +6,7 @@ export async function cliListAccounts () { return storedConfig.accounts .map(account => ({ name: account.name, - address: account.address + address: account.address, + verifyingKeys: account?.data?.admin?.verifyingKeys })) } diff --git a/src/flows/manage-accounts/helpers/import-key.ts b/src/flows/manage-accounts/helpers/import-key.ts index 17e9f848..50667d01 100644 --- a/src/flows/manage-accounts/helpers/import-key.ts +++ b/src/flows/manage-accounts/helpers/import-key.ts @@ -1,4 +1,3 @@ -import { debug } from '../../../common/utils' // import { mnemonicValidate, mnemonicToMiniSecret } from '@polkadot/util-crypto' export const importQuestions = [ @@ -16,7 +15,6 @@ export const importQuestions = [ message: 'Enter seed:', validate: (secret) => { // validate: (secret, { secretType }) => { - debug('\nsecret:', secret, typeof secret) // if (secretType === 'mnemonic') return mnemonicValidate(secret) ? true : 'not a valid mnemonic' if (secret.includes('#debug')) return true if (secret.length === 66 && secret.startsWith('0x')) return true diff --git a/src/flows/manage-accounts/index.ts b/src/flows/manage-accounts/index.ts index b911afa2..413f2d16 100644 --- a/src/flows/manage-accounts/index.ts +++ b/src/flows/manage-accounts/index.ts @@ -1,8 +1,10 @@ import inquirer from 'inquirer' -import { debug, print } from '../../common/utils' +import { print } from '../../common/utils' import { newKey } from './new-key' import { selectAccount } from './select-account' import { listAccounts } from './list' +import { EntropyTuiOptions } from 'src/types' +import { EntropyLogger } from 'src/common/logger' const actions = { 'Create/Import Account': newKey, @@ -27,9 +29,11 @@ const questions = [{ choices, }] -export async function manageAccounts (config) { +export async function manageAccounts (config, _options: EntropyTuiOptions, logger: EntropyLogger) { + const FLOW_CONTEXT = 'MANAGE_ACCOUNTS' const { choice } = await inquirer.prompt(questions) - const responses = await actions[choice](config) || {} - debug('returned config update:', { accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount }) + const responses = await actions[choice](config, logger) || {} + logger.debug('returned config update', FLOW_CONTEXT) + logger.debug({ accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount }, FLOW_CONTEXT) return { accounts: responses.accounts ? responses.accounts : config.accounts, selectedAccount: responses.selectedAccount || config.selectedAccount } } diff --git a/src/flows/manage-accounts/new-key.ts b/src/flows/manage-accounts/new-key.ts index 39161c19..f0a781a6 100644 --- a/src/flows/manage-accounts/new-key.ts +++ b/src/flows/manage-accounts/new-key.ts @@ -4,9 +4,11 @@ import { randomAsHex } from '@polkadot/util-crypto' import Keyring from '@entropyxyz/sdk/keys' import { importQuestions } from './helpers/import-key' // import * as passwordFlow from '../password' -import { debug, print } from '../../common/utils' +import { print } from '../../common/utils' +import { EntropyLogger } from 'src/common/logger' -export async function newKey ({ accounts }) { +export async function newKey ({ accounts }, logger: EntropyLogger) { + const FLOW_CONTEXT = 'MANAGE_ACCOUNTS::NEW_KEY' accounts = Array.isArray(accounts) ? accounts : [] const questions = [ @@ -66,7 +68,8 @@ export async function newKey ({ accounts }) { const fullAccount = keyring.getAccount() // TO-DO: sdk should create account on constructor const { admin } = keyring.getAccount() - debug('fullAccount:', fullAccount) + logger.debug('fullAccount:', FLOW_CONTEXT) + logger.debug(fullAccount, FLOW_CONTEXT) const data = fullAccount delete admin.pair diff --git a/src/flows/register/index.ts b/src/flows/register/index.ts index 043cdd4f..d15d6f88 100644 --- a/src/flows/register/index.ts +++ b/src/flows/register/index.ts @@ -1,8 +1,10 @@ // import inquirer from "inquirer" -import { debug, getSelectedAccount, print, /*accountChoices*/ } from "../../common/utils" +import { getSelectedAccount, print, /*accountChoices*/ } from "../../common/utils" import { initializeEntropy } from "../../common/initializeEntropy" +import { EntropyLogger } from "src/common/logger"; -export async function register (storedConfig, options) { +export async function register (storedConfig, options, logger: EntropyLogger) { + const FLOW_CONTEXT = 'REGISTER' const { accounts, selectedAccount: selectedFromConfig } = storedConfig; const { endpoint } = options @@ -21,7 +23,7 @@ export async function register (storedConfig, options) { // default: '0x0000000000000000000000000000000000000000000000000000000000000000' // }]) //@ts-ignore: - debug('about to register selectedAccount.address' + selectedAccount.address + 'keyring:' + entropy.keyring.getLazyLoadAccountProxy('registration').pair.address) + logger.debug('about to register selectedAccount.address' + selectedAccount.address + 'keyring:' + entropy.keyring.getLazyLoadAccountProxy('registration').pair.address, FLOW_CONTEXT) print("Attempting to register the address:", selectedAccount.address, ) let verifyingKey: string try { @@ -44,7 +46,7 @@ export async function register (storedConfig, options) { } catch (error) { console.error('error', error); if (!verifyingKey) { - debug('Pruning Registration') + logger.debug('Pruning Registration', FLOW_CONTEXT) try { const tx = await entropy.substrate.tx.registry.pruneRegistration() await tx.signAndSend(entropy.keyring.accounts.registration.pair, ({ status }) => { diff --git a/src/flows/sign/cli.ts b/src/flows/sign/cli.ts index fa32ac02..5fb4c6aa 100644 --- a/src/flows/sign/cli.ts +++ b/src/flows/sign/cli.ts @@ -1,16 +1,17 @@ +import { EntropyLogger } from "src/common/logger" import { initializeEntropy } from "../../common/initializeEntropy" -import { debug } from "../../common/utils" import * as config from '../../config' import { signWithAdapters } from './sign' // TODO: revisit this file, rename as signEthTransaction? export async function cliSign ({ address, message, endpoint }) { - + const logger = new EntropyLogger('CLI::SIGN', endpoint) const storedConfig = await config.get() const account = storedConfig.accounts.find(account => account.address === address) if (!account) throw Error(`No account with address ${address}`) // QUESTION: is throwing the right response? - debug('account', account) + logger.debug('account:') + logger.debug(account) const entropy = await initializeEntropy({ keyMaterial: account.data, endpoint }) diff --git a/src/flows/sign/index.ts b/src/flows/sign/index.ts index 0982f19d..64ec78dc 100644 --- a/src/flows/sign/index.ts +++ b/src/flows/sign/index.ts @@ -1,8 +1,9 @@ import inquirer from "inquirer" import { u8aToHex } from '@polkadot/util' import { initializeEntropy } from "../../common/initializeEntropy" -import { debug, getSelectedAccount, print } from "../../common/utils" +import { getSelectedAccount, print } from "../../common/utils" import { signWithAdapters } from './sign' +import { EntropyLogger } from "src/common/logger" async function signWithAdaptersInOrder (entropy, msg?: string, signingAttempts = 0) { try { @@ -63,7 +64,8 @@ async function signWithAdaptersInOrder (entropy, msg?: string, signingAttempts = } } -export async function sign ({ accounts, endpoints, selectedAccount: selectedAccountAddress }, options) { +export async function sign ({ accounts, endpoints, selectedAccount: selectedAccountAddress }, options, logger: EntropyLogger) { + const FLOW_CONTEXT = 'SIGN' const endpoint = endpoints[options.ENDPOINT] const actionChoice = await inquirer.prompt([ { @@ -81,12 +83,14 @@ export async function sign ({ accounts, endpoints, selectedAccount: selectedAcco ]) const selectedAccount = getSelectedAccount(accounts, selectedAccountAddress) - debug("selectedAccount:", selectedAccount) + logger.debug("selectedAccount:", FLOW_CONTEXT) + logger.debug(selectedAccount, FLOW_CONTEXT) const keyMaterial = selectedAccount?.data; const entropy = await initializeEntropy({ keyMaterial, endpoint }) const { address } = entropy.keyring.accounts.registration - debug("address:", address) + logger.debug("address:", FLOW_CONTEXT) + logger.debug(address, FLOW_CONTEXT) if (address == undefined) { throw new Error("address issue") } diff --git a/src/tui.ts b/src/tui.ts index 4db94cce..872fd5ee 100644 --- a/src/tui.ts +++ b/src/tui.ts @@ -3,17 +3,18 @@ import * as config from './config' import * as flows from './flows' import { EntropyTuiOptions } from './types' import { logo } from './common/ascii' -import { debug, print } from './common/utils' +import { print } from './common/utils' +import { EntropyLogger } from './common/logger' let shouldInit = true // tui = text user interface export default function tui (options: EntropyTuiOptions) { - + const logger = new EntropyLogger('TUI', options.endpoint) console.clear() console.log(logo) // the Entropy logo - debug(options) + logger.debug(options) const choices = { 'Manage Accounts': flows.manageAccounts, @@ -26,20 +27,19 @@ export default function tui (options: EntropyTuiOptions) { // 'Construct an Ethereum Tx': flows.ethTransaction, } - const devChoices = { - // 'Entropy Faucet': flows.entropyFaucet, - } + // const devChoices = { + // // 'Entropy Faucet': flows.entropyFaucet, + // } - if (options.dev) Object.assign(choices, devChoices) + // if (options.dev) Object.assign(choices, devChoices) // assign exit so its last Object.assign(choices, { 'Exit': async () => {} }) - main(choices, options) + main(choices, options, logger) } -async function main (choices, options) { - +async function main (choices, options, logger: EntropyLogger) { if (shouldInit) { await config.init() shouldInit = false @@ -72,8 +72,8 @@ async function main (choices, options) { if (!storedConfig.selectedAccount && answers.choice !== 'Manage Accounts') { console.error('There are currently no accounts available, please create or import your new account using the Manage Accounts feature') } else { - debug(answers) - const newConfigUpdates = await choices[answers.choice](storedConfig, options) + logger.debug(answers) + const newConfigUpdates = await choices[answers.choice](storedConfig, options, logger) if (typeof newConfigUpdates === 'string' && newConfigUpdates === 'exit') { returnToMain = true } else { @@ -90,7 +90,7 @@ async function main (choices, options) { }])) } - if (returnToMain) main(choices, options) + if (returnToMain) main(choices, options, logger) else { print('Have a nice day') process.exit() diff --git a/src/types/index.ts b/src/types/index.ts index 26295245..af964cd7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,3 +3,10 @@ export interface EntropyTuiOptions { endpoint: string } +type EntropyLoggerLogLevel = 'error' | 'warn' | 'info' | 'debug' + +export interface EntropyLoggerOptions { + debug?: boolean + level?: EntropyLoggerLogLevel + isTesting?: boolean +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e4d8e319..c9406aec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,20 @@ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@entropyxyz/entropy-protocol-nodejs@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@entropyxyz/entropy-protocol-nodejs/-/entropy-protocol-nodejs-0.2.0.tgz#ad8870181e43c9acd71765def06c9c53335118f5" @@ -999,6 +1013,11 @@ dependencies: "@types/node" "*" +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@typescript-eslint/eslint-plugin@^6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" @@ -1220,6 +1239,11 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -1375,6 +1399,13 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1382,16 +1413,45 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + commander@^12.0.0, commander@~12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" @@ -1558,6 +1618,11 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + env-paths@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" @@ -1901,6 +1966,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" @@ -1960,6 +2030,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -2248,7 +2323,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2297,6 +2372,11 @@ is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: call-bind "^1.0.2" get-intrinsic "^1.2.1" +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2545,6 +2625,11 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -2608,6 +2693,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -2634,6 +2724,18 @@ log-update@^6.0.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + lru-cache@^10.2.0: version "10.2.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" @@ -2725,6 +2827,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -2826,6 +2933,13 @@ once@^1.3.0: dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -3016,6 +3130,15 @@ readable-stream@2.2.9: string_decoder "~1.0.0" util-deprecate "~1.0.1" +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" @@ -3166,6 +3289,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" @@ -3175,6 +3303,11 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -3244,6 +3377,13 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3286,6 +3426,11 @@ split@1.0.0: dependencies: through "2" +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -3353,6 +3498,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" @@ -3485,6 +3637,11 @@ tape@^5.7.5: resolve "^2.0.0-next.5" string.prototype.trim "^1.2.9" +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -3548,6 +3705,11 @@ trim@0.0.1: resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + ts-api-utils@^1.0.1: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -3686,7 +3848,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -3768,6 +3930,32 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"