From 94997905cd941a0c13701c3668ded078ffe86754 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sun, 19 Nov 2023 14:25:12 +0200 Subject: [PATCH 01/16] Bug fixes. --- CHANGELOG.md | 10 ++++++++-- src/lib/lightnet.js | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7194123b..d9bb9c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.1] - 2023-XX-XX + +### Added + +- Lightnet sub-commands implementation (`logs`). [#5XX](https://github.com/o1-labs/zkapp-cli/pull/5XX) + ## [0.15.0] - 2023-11-07 -### Changed +### Added -- Lightnet sub-commands implementation (start/stop/status). [#510](https://github.com/o1-labs/zkapp-cli/pull/510) +- Lightnet sub-commands implementation (`start`/`stop`/`status`). [#510](https://github.com/o1-labs/zkapp-cli/pull/510) ## [0.14.1] - 2023-11-03 diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 87af796f..06b14433 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -168,7 +168,12 @@ export async function stopLightnet({ saveLogs, cleanUp, debug }) { await stopDockerContainer(lightnetDockerContainerName); } ); - if (saveLogs && fs.existsSync(lightnetConfigFile)) { + if ( + saveLogs && + fs.existsSync(lightnetConfigFile) && + DockerContainerState.NOT_FOUND !== + getDockerContainerState(lightnetDockerContainerName) + ) { await step('Preserving Docker container processes logs', async () => { logsDir = await saveDockerContainerProcessesLogs(); }); @@ -745,7 +750,8 @@ function secondsToHms(seconds) { if (s > 0) { sDisplay = s + (s == 1 ? ' second' : ' seconds'); } - return hDisplay + mDisplay + sDisplay; + const result = hDisplay + mDisplay + sDisplay; + return result.endsWith(', ') ? result.slice(0, -2) : result; } function printErrorIfDebug(error) { From eda92dbd215a3274366631171d23f3f3efd529fa Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sun, 19 Nov 2023 21:22:31 +0200 Subject: [PATCH 02/16] Lightnet logs sub-command implementation. --- CHANGELOG.md | 4 +- package-lock.json | 4 +- package.json | 2 +- src/bin/index.js | 43 +++++++- src/lib/constants.js | 15 ++- src/lib/lightnet.js | 237 ++++++++++++++++++++++++++++++++++++++----- 6 files changed, 270 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9bb9c9c..6ba467e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.15.1] - 2023-XX-XX +## [0.15.1] - 2023-11-XX ### Added -- Lightnet sub-commands implementation (`logs`). [#5XX](https://github.com/o1-labs/zkapp-cli/pull/5XX) +- Lightnet sub-commands implementation (`logs`). [#520](https://github.com/o1-labs/zkapp-cli/pull/520) ## [0.15.0] - 2023-11-07 diff --git a/package-lock.json b/package-lock.json index 3e301205..d9e90ea0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zkapp-cli", - "version": "0.15.0", + "version": "0.15.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "zkapp-cli", - "version": "0.15.0", + "version": "0.15.1", "license": "Apache-2.0", "dependencies": { "chalk": "^5.3.0", diff --git a/package.json b/package.json index 4f66db21..1ba5219b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zkapp-cli", - "version": "0.15.0", + "version": "0.15.1", "description": "CLI to create zkApps (zero-knowledge apps) for Mina Protocol", "homepage": "https://github.com/o1-labs/zkapp-cli/", "keywords": [ diff --git a/src/bin/index.js b/src/bin/index.js index ce471c9b..c93702ed 100755 --- a/src/bin/index.js +++ b/src/bin/index.js @@ -12,9 +12,11 @@ import { deploy } from '../lib/deploy.js'; import { example } from '../lib/example.js'; import { file } from '../lib/file.js'; import { + lightnetFollowLogs, + lightnetSaveLogs, + lightnetStart, lightnetStatus, - startLightnet, - stopLightnet, + lightnetStop, } from '../lib/lightnet.js'; import { project } from '../lib/project.js'; import system from '../lib/system.js'; @@ -193,7 +195,7 @@ yargs(hideBin(process.argv)) }, ...commonOptions, }, - async (argv) => await startLightnet(argv) + async (argv) => await lightnetStart(argv) ) .command( ['stop [save-logs] [clean-up] [debug]'], @@ -219,7 +221,7 @@ yargs(hideBin(process.argv)) }, ...commonOptions, }, - async (argv) => await stopLightnet(argv) + async (argv) => await lightnetStop(argv) ) .command( ['status [debug]'], @@ -233,6 +235,39 @@ yargs(hideBin(process.argv)) debug: argv.debug, }) ) + .command( + ['logs [options]'], + 'Handle the lightweight Mina blockchain network Docker container processes logs.', + (yargs) => { + yargs + .command( + ['save [debug]'], + 'Save the lightweight Mina blockchain network Docker container processes logs to the host file system.', + { + ...commonOptions, + }, + async (argv) => await lightnetSaveLogs(argv) + ) + .command( + ['follow [process] [debug]'], + 'Follow one of the lightweight Mina blockchain network Docker container processes logs.', + { + process: { + alias: 'p', + demand: false, + string: true, + hidden: false, + choices: Object.values(Constants.lightnetProcessName), + description: + 'The name of the Docker container process to follow the logs of.', + }, + ...commonOptions, + }, + async (argv) => await lightnetFollowLogs(argv) + ) + .demandCommand(); + } + ) .demandCommand(); } ) diff --git a/src/lib/constants.js b/src/lib/constants.js index b92821b2..673d9781 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -8,8 +8,9 @@ import path from 'path'; * @typedef {'fast' | 'real'} LightnetType * @typedef {'none' | 'full'} LightnetProofLevel * @typedef {'rampup' | 'berkeley' | 'develop'} LightnetMinaBranch + * @typedef {{ archiveNodeApi: string, minaArchive: string, minaSingleNodeDaemon: string, minaFish1: string, minaFollowing1: string, minaSeed1: string, minaSnarkCoordinator1: string, minaSnarkWorker1: string, minaWhale1: string, minaWhale2: string }} LightnetProcessName * - * @type {{ uiTypes: UiType[], exampleTypes: ExampleType[], feePayerCacheDir: string, lightnetWorkDir: string, lightnetModes: LightnetMode[], lightnetTypes: LightnetType[], lightnetProofLevels: LightnetProofLevel[], lightnetMinaBranches: LightnetMinaBranch[] }} + * @type {{ uiTypes: UiType[], exampleTypes: ExampleType[], feePayerCacheDir: string, lightnetWorkDir: string, lightnetModes: LightnetMode[], lightnetTypes: LightnetType[], lightnetProofLevels: LightnetProofLevel[], lightnetMinaBranches: LightnetMinaBranch[], lightnetProcessName: LightnetProcessName }} */ const Constants = Object.freeze({ uiTypes: ['next', 'svelte', 'nuxt', 'empty', 'none'], @@ -20,6 +21,18 @@ const Constants = Object.freeze({ lightnetTypes: ['fast', 'real'], lightnetProofLevels: ['none', 'full'], lightnetMinaBranches: ['rampup', 'berkeley', 'develop'], + lightnetProcessName: Object.freeze({ + archiveNodeApi: 'Archive-Node-API application', + minaArchive: 'Mina Archive process', + minaSingleNodeDaemon: 'Mina multi-purpose Daemon', + minaFish1: 'Fish BP #1', + minaFollowing1: 'Non-consensus node #1', + minaSeed1: 'Seed node #1', + minaSnarkCoordinator1: 'SNARK coordinator #1', + minaSnarkWorker1: 'SNARK worker #1', + minaWhale1: 'Whale BP #1', + minaWhale2: 'Whale BP #2', + }), }); export default Constants; diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 06b14433..4cae8a3e 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -17,10 +17,38 @@ const lightnetDockerContainerName = 'mina-local-lightnet'; const lightnetMinaDaemonGraphQlEndpoint = 'http://localhost:8080/graphql'; const lightnetAccountsManagerEndpoint = 'http://localhost:8181'; const lightnetArchiveNodeApiEndpoint = 'http://localhost:8282'; +const lightnetDockerProcessToLogFileMapping = new Map([ + [Constants.lightnetProcessName.archiveNodeApi, 'logs/archive-node-api.log'], + [ + Constants.lightnetProcessName.minaArchive, + 'logs/archive-node.log,archive/log.txt', + ], + [ + Constants.lightnetProcessName.minaSingleNodeDaemon, + 'logs/single-node-network.log', + ], + [Constants.lightnetProcessName.minaFish1, 'fish_0/log.txt'], + [Constants.lightnetProcessName.minaFollowing1, 'node_0/log.txt'], + [Constants.lightnetProcessName.minaSeed1, 'seed/log.txt'], + [ + Constants.lightnetProcessName.minaSnarkCoordinator1, + 'snark_coordinator/log.txt', + ], + [ + Constants.lightnetProcessName.minaSnarkWorker1, + 'snark_workers/worker_0/log.txt', + ], + [Constants.lightnetProcessName.minaWhale1, 'whale_0/log.txt'], + [Constants.lightnetProcessName.minaWhale2, 'whale_1/log.txt'], +]); const DockerContainerState = { RUNNING: 'running', NOT_FOUND: 'not-found', }; +const ContainerLogFilesPrefix = { + SINGLE_NODE: '/root', + MULTI_NODE: '/root/.mina-network/mina-local-network-2-1-1/nodes', +}; let isDebug = false; let quotes = "'"; let escapeQuotes = ''; @@ -47,7 +75,7 @@ if (process.platform === 'win32') { * @param {boolean} argv.debug - Whether to print the debug information. * @returns {Promise} */ -export async function startLightnet({ +export async function lightnetStart({ mode, type, proofLevel, @@ -155,7 +183,7 @@ export async function startLightnet({ * @param {boolean} argv.debug - Whether to print the debug information. * @returns {Promise} */ -export async function stopLightnet({ saveLogs, cleanUp, debug }) { +export async function lightnetStop({ saveLogs, cleanUp, debug }) { isDebug = debug; let logsDir = null; await checkDockerEngineAvailability(); @@ -174,7 +202,7 @@ export async function stopLightnet({ saveLogs, cleanUp, debug }) { DockerContainerState.NOT_FOUND !== getDockerContainerState(lightnetDockerContainerName) ) { - await step('Preserving Docker container processes logs', async () => { + await step('Preserving the Docker container processes logs', async () => { logsDir = await saveDockerContainerProcessesLogs(); }); } @@ -200,7 +228,7 @@ export async function stopLightnet({ saveLogs, cleanUp, debug }) { if (logsDir) { const boldLogs = chalk.reset.bold('logs'); console.log( - `\nThe Docker container processes ${boldLogs} can be found at:\n\n` + + `\nThe Docker container processes ${boldLogs} can be found at the following path:\n\n` + chalk.green.bold(logsDir) + '\n' ); @@ -261,8 +289,146 @@ export async function lightnetStatus({ } } +/** + * Saves the lightweight Mina blockchain network Docker container processes logs to the host file system. + * @param {object} argv - The arguments object provided by yargs. + * @param {boolean} argv.debug - Whether to print the debug information. + * @returns {Promise} + */ +export async function lightnetSaveLogs({ debug }) { + isDebug = debug; + let logsDir = null; + await checkDockerEngineAvailability(); + if ( + fs.existsSync(lightnetConfigFile) && + DockerContainerState.NOT_FOUND !== + getDockerContainerState(lightnetDockerContainerName) + ) { + await step('Preserving the Docker container processes logs', async () => { + logsDir = await saveDockerContainerProcessesLogs(); + }); + if (logsDir) { + const boldLogs = chalk.reset.bold('logs'); + console.log( + `\nThe Docker container processes ${boldLogs} were preserved at the following path:\n\n` + + chalk.green.bold(logsDir) + + '\n' + ); + } else { + console.log( + chalk.red( + '\nIssue happened during the Docker container processes logs preservation!' + ) + ); + shell.exit(1); + } + } else { + console.log( + chalk.red( + '\nIt is impossible to preserve the logs at the moment!' + + '\nPlease ensure that the lightweight Mina blockchain network Docker container exists, then try again.' + ) + ); + shell.exit(1); + } +} + +/** + * Follows one of the lightweight Mina blockchain network Docker container processes logs. + * @param {object} argv - The arguments object provided by yargs. + * @param {string} argv.process - The name of the Docker container process to follow the logs of. + * @param {boolean} argv.debug - Whether to print the debug information. + * @returns {Promise} + */ +export async function lightnetFollowLogs({ process, debug }) { + isDebug = debug; + await checkDockerEngineAvailability(); + if ( + fs.existsSync(lightnetConfigFile) && + DockerContainerState.RUNNING === + getDockerContainerState(lightnetDockerContainerName) + ) { + const lightnetConfig = fs.readJSONSync(lightnetConfigFile); + const mode = lightnetConfig.mode; + const archive = lightnetConfig.archive; + let logFilePath = null; + let processToLogFileMapping = new Map( + lightnetDockerProcessToLogFileMapping + ); + if (mode === 'single-node') { + processToLogFileMapping = new Map( + Array.from(processToLogFileMapping).slice(0, 3) + ); + processToLogFileMapping.forEach((value, key) => { + processToLogFileMapping.set( + key, + `${ContainerLogFilesPrefix.SINGLE_NODE}/${value.split(',')[0]}` + ); + }); + } else { + processToLogFileMapping.delete( + Constants.lightnetProcessName.minaSingleNodeDaemon + ); + processToLogFileMapping.forEach((value, key) => { + const logFilePaths = value.split(','); + processToLogFileMapping.set( + key, + `${ + Constants.lightnetProcessName.archiveNodeApi === key + ? ContainerLogFilesPrefix.SINGLE_NODE + : ContainerLogFilesPrefix.MULTI_NODE + }/${logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]}` + ); + }); + } + if (!archive) { + processToLogFileMapping.delete( + Constants.lightnetProcessName.archiveNodeApi + ); + processToLogFileMapping.delete(Constants.lightnetProcessName.minaArchive); + } + if (!process || !processToLogFileMapping.has(process)) { + const res = await enquirer.prompt({ + type: 'select', + name: 'selectedProcess', + choices: Array.from(processToLogFileMapping.keys()), + message: () => { + return chalk.reset( + 'Please select the Docker container process to follow the logs of' + ); + }, + prefix: (state) => { + // Shows a cyan question mark when not submitted. + // Shows a green check mark if submitted. + // Shows a red "x" if ctrl+C is pressed (default is a magenta). + if (!state.submitted) return `\n${state.symbols.question}`; + return !state.cancelled + ? state.symbols.check + : chalk.red(state.symbols.cross); + }, + result: (val) => val.trim(), + }); + logFilePath = processToLogFileMapping.get(res.selectedProcess); + } else { + logFilePath = processToLogFileMapping.get(process); + } + await streamDockerContainerFileContent( + lightnetDockerContainerName, + logFilePath + ); + } else { + console.log( + chalk.red( + '\nIt is impossible to follow the logs at the moment!' + + '\nPlease ensure that the lightweight Mina blockchain network Docker container is up and running, then try again.' + ) + ); + shell.exit(1); + } +} + async function checkDockerEngineAvailability() { - await step('Checking Docker Engine availability', async () => { + await step('Checking the Docker Engine availability', async () => { if (!shell.which('docker')) { console.log( '\n\nPlease ensure that Docker Engine is installed, then try again.' + @@ -398,6 +564,31 @@ async function removeDanglingDockerImages() { }); } +async function streamDockerContainerFileContent(containerName, filePath) { + try { + const border = getBorderCharacters('norc'); + console.log( + '\n' + chalk.reset.bold('Docker container file content streaming') + '\n' + ); + console.log( + table([[chalk.reset('Use Ctrl+C to stop the file content streaming.')]], { + border, + }) + ); + await shellExec(`docker exec ${containerName} tail -n 50 -f ${filePath}`, { + silent: false, + }); + } catch (error) { + printErrorIfDebug(error); + console.log( + chalk.red( + '\nIssue happened while streaming the Docker container file content!' + ) + ); + shell.exit(1); + } +} + async function saveDockerContainerProcessesLogs() { const timeZoneOffset = new Date().getTimezoneOffset() * 60000; const localMoment = new Date(Date.now() - timeZoneOffset); @@ -413,18 +604,16 @@ async function saveDockerContainerProcessesLogs() { const archive = lightnetConfig.archive; await fs.ensureDir(logsDir); if (mode === 'single-node') { - const logFilePaths = [ - 'logs/archive-node-api.log', - 'logs/archive-node.log', - 'logs/single-node-network.log', - ]; + const logFilePaths = Array.from( + lightnetDockerProcessToLogFileMapping.values() + ).map((value) => value.split(',')[0]); for (const logFilePath of logFilePaths) { try { const destinationFilePath = path.resolve( `${logsDir}/${logFilePath.replace(/\//g, '_')}` ); await shellExec( - `docker cp ${lightnetDockerContainerName}:/root/${logFilePath} ${destinationFilePath}`, + `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.SINGLE_NODE}/${logFilePath} ${destinationFilePath}`, { silent: !isDebug } ); } catch (error) { @@ -432,19 +621,15 @@ async function saveDockerContainerProcessesLogs() { } } } else { - const logFilePaths = [ - 'archive/log.txt', - 'fish_0/log.txt', - 'node_0/log.txt', - 'seed/log.txt', - 'snark_coordinator/log.txt', - 'snark_workers/worker_0/log.txt', - 'whale_0/log.txt', - 'whale_1/log.txt', - ]; + const logFilePaths = Array.from( + lightnetDockerProcessToLogFileMapping.values() + ).map((value) => { + const logFilePaths = value.split(','); + return logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]; + }); if (archive) { await shellExec( - `docker cp ${lightnetDockerContainerName}:/root/logs/archive-node-api.log ${logsDir}`, + `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.SINGLE_NODE}/logs/archive-node-api.log ${logsDir}`, { silent: !isDebug } ); } @@ -454,7 +639,7 @@ async function saveDockerContainerProcessesLogs() { `${logsDir}/${logFilePath.replace(/\//g, '_')}` ); await shellExec( - `docker cp ${lightnetDockerContainerName}:/root/.mina-network/mina-local-network-2-1-1/nodes/${logFilePath} ${destinationFilePath}`, + `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.MULTI_NODE}/${logFilePath} ${destinationFilePath}`, { silent: !isDebug } ); } catch (error) { @@ -573,10 +758,12 @@ function printDockerContainerProcessesLogPaths() { 'Logs produced by different processes are redirected into the files' + '\nlocated by the following path patterns inside the container:' ); - const logs = [[chalk.reset('/root/logs/*.log')]]; + const logs = [ + [chalk.reset(`${ContainerLogFilesPrefix.SINGLE_NODE}/logs/*.log`)], + ]; if (mode === 'multi-node') { logs.push([ - chalk.reset('/root/mina-local-network-2-1-1/nodes/**/logs/*.log'), + chalk.reset(`${ContainerLogFilesPrefix.MULTI_NODE}/**/logs/*.log`), ]); } console.log(boldTitle); From ebdcefb3b216c7fce7d585befc716c20677322ac Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 20 Nov 2023 10:33:28 +0200 Subject: [PATCH 03/16] Refactoring to simplify and improve readability. --- src/lib/lightnet.js | 302 +++++++++++++++++++++++--------------------- 1 file changed, 158 insertions(+), 144 deletions(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 4cae8a3e..8eea0b18 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -17,29 +17,18 @@ const lightnetDockerContainerName = 'mina-local-lightnet'; const lightnetMinaDaemonGraphQlEndpoint = 'http://localhost:8080/graphql'; const lightnetAccountsManagerEndpoint = 'http://localhost:8181'; const lightnetArchiveNodeApiEndpoint = 'http://localhost:8282'; +const lightnetProcessName = Constants.lightnetProcessName; const lightnetDockerProcessToLogFileMapping = new Map([ - [Constants.lightnetProcessName.archiveNodeApi, 'logs/archive-node-api.log'], - [ - Constants.lightnetProcessName.minaArchive, - 'logs/archive-node.log,archive/log.txt', - ], - [ - Constants.lightnetProcessName.minaSingleNodeDaemon, - 'logs/single-node-network.log', - ], - [Constants.lightnetProcessName.minaFish1, 'fish_0/log.txt'], - [Constants.lightnetProcessName.minaFollowing1, 'node_0/log.txt'], - [Constants.lightnetProcessName.minaSeed1, 'seed/log.txt'], - [ - Constants.lightnetProcessName.minaSnarkCoordinator1, - 'snark_coordinator/log.txt', - ], - [ - Constants.lightnetProcessName.minaSnarkWorker1, - 'snark_workers/worker_0/log.txt', - ], - [Constants.lightnetProcessName.minaWhale1, 'whale_0/log.txt'], - [Constants.lightnetProcessName.minaWhale2, 'whale_1/log.txt'], + [lightnetProcessName.archiveNodeApi, 'logs/archive-node-api.log'], + [lightnetProcessName.minaArchive, 'logs/archive-node.log,archive/log.txt'], + [lightnetProcessName.minaSingleNodeDaemon, 'logs/single-node-network.log'], + [lightnetProcessName.minaFish1, 'fish_0/log.txt'], + [lightnetProcessName.minaFollowing1, 'node_0/log.txt'], + [lightnetProcessName.minaSeed1, 'seed/log.txt'], + [lightnetProcessName.minaSnarkCoordinator1, 'snark_coordinator/log.txt'], + [lightnetProcessName.minaSnarkWorker1, 'snark_workers/worker_0/log.txt'], + [lightnetProcessName.minaWhale1, 'whale_0/log.txt'], + [lightnetProcessName.minaWhale2, 'whale_1/log.txt'], ]); const DockerContainerState = { RUNNING: 'running', @@ -343,80 +332,11 @@ export async function lightnetSaveLogs({ debug }) { export async function lightnetFollowLogs({ process, debug }) { isDebug = debug; await checkDockerEngineAvailability(); - if ( + const isDockerContainerRunning = fs.existsSync(lightnetConfigFile) && DockerContainerState.RUNNING === - getDockerContainerState(lightnetDockerContainerName) - ) { - const lightnetConfig = fs.readJSONSync(lightnetConfigFile); - const mode = lightnetConfig.mode; - const archive = lightnetConfig.archive; - let logFilePath = null; - let processToLogFileMapping = new Map( - lightnetDockerProcessToLogFileMapping - ); - if (mode === 'single-node') { - processToLogFileMapping = new Map( - Array.from(processToLogFileMapping).slice(0, 3) - ); - processToLogFileMapping.forEach((value, key) => { - processToLogFileMapping.set( - key, - `${ContainerLogFilesPrefix.SINGLE_NODE}/${value.split(',')[0]}` - ); - }); - } else { - processToLogFileMapping.delete( - Constants.lightnetProcessName.minaSingleNodeDaemon - ); - processToLogFileMapping.forEach((value, key) => { - const logFilePaths = value.split(','); - processToLogFileMapping.set( - key, - `${ - Constants.lightnetProcessName.archiveNodeApi === key - ? ContainerLogFilesPrefix.SINGLE_NODE - : ContainerLogFilesPrefix.MULTI_NODE - }/${logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]}` - ); - }); - } - if (!archive) { - processToLogFileMapping.delete( - Constants.lightnetProcessName.archiveNodeApi - ); - processToLogFileMapping.delete(Constants.lightnetProcessName.minaArchive); - } - if (!process || !processToLogFileMapping.has(process)) { - const res = await enquirer.prompt({ - type: 'select', - name: 'selectedProcess', - choices: Array.from(processToLogFileMapping.keys()), - message: () => { - return chalk.reset( - 'Please select the Docker container process to follow the logs of' - ); - }, - prefix: (state) => { - // Shows a cyan question mark when not submitted. - // Shows a green check mark if submitted. - // Shows a red "x" if ctrl+C is pressed (default is a magenta). - if (!state.submitted) return `\n${state.symbols.question}`; - return !state.cancelled - ? state.symbols.check - : chalk.red(state.symbols.cross); - }, - result: (val) => val.trim(), - }); - logFilePath = processToLogFileMapping.get(res.selectedProcess); - } else { - logFilePath = processToLogFileMapping.get(process); - } - await streamDockerContainerFileContent( - lightnetDockerContainerName, - logFilePath - ); - } else { + getDockerContainerState(lightnetDockerContainerName); + if (!isDockerContainerRunning) { console.log( chalk.red( '\nIt is impossible to follow the logs at the moment!' + @@ -425,6 +345,70 @@ export async function lightnetFollowLogs({ process, debug }) { ); shell.exit(1); } + const lightnetConfig = fs.readJSONSync(lightnetConfigFile); + const processToLogFileMapping = getProcessToLogFileMapping(lightnetConfig); + const selectedProcess = + process || (await promptForDockerContainerProcess(processToLogFileMapping)); + const logFilePath = processToLogFileMapping.get(selectedProcess); + await streamDockerContainerFileContent( + lightnetDockerContainerName, + logFilePath + ); +} + +function getProcessToLogFileMapping({ mode, archive }) { + let mapping = new Map(lightnetDockerProcessToLogFileMapping); + if (mode === 'single-node') { + mapping = new Map(Array.from(mapping).slice(0, 3)); + mapping.forEach((value, key) => { + mapping.set( + key, + `${ContainerLogFilesPrefix.SINGLE_NODE}/${value.split(',')[0]}` + ); + }); + } else { + mapping.delete(lightnetProcessName.minaSingleNodeDaemon); + mapping.forEach((value, key) => { + const logFilePaths = value.split(','); + mapping.set( + key, + `${ + lightnetProcessName.archiveNodeApi === key + ? ContainerLogFilesPrefix.SINGLE_NODE + : ContainerLogFilesPrefix.MULTI_NODE + }/${logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]}` + ); + }); + } + if (!archive) { + mapping.delete(lightnetProcessName.archiveNodeApi); + mapping.delete(lightnetProcessName.minaArchive); + } + return mapping; +} + +async function promptForDockerContainerProcess(processToLogFileMapping) { + const response = await enquirer.prompt({ + type: 'select', + name: 'selectedProcess', + choices: Array.from(processToLogFileMapping.keys()), + message: () => { + return chalk.reset( + 'Please select the Docker container process to follow the logs of' + ); + }, + prefix: (state) => { + // Shows a cyan question mark when not submitted. + // Shows a green check mark if submitted. + // Shows a red "x" if ctrl+C is pressed (default is a magenta). + if (!state.submitted) return `\n${state.symbols.question}`; + return !state.cancelled + ? state.symbols.check + : chalk.red(state.symbols.cross); + }, + result: (val) => val.trim(), + }); + return response.selectedProcess; } async function checkDockerEngineAvailability() { @@ -590,62 +574,18 @@ async function streamDockerContainerFileContent(containerName, filePath) { } async function saveDockerContainerProcessesLogs() { - const timeZoneOffset = new Date().getTimezoneOffset() * 60000; - const localMoment = new Date(Date.now() - timeZoneOffset); - const logsDir = path.resolve( - `${lightnetLogsDir}/${localMoment - .toISOString() - .split('.')[0] - .replace(/:/g, '-')}` - ); + const logsDir = createLogsDirectory(); try { - const lightnetConfig = fs.readJSONSync(lightnetConfigFile); - const mode = lightnetConfig.mode; - const archive = lightnetConfig.archive; await fs.ensureDir(logsDir); + const { mode, archive } = fs.readJSONSync(lightnetConfigFile); + const logFilePaths = getLogFilePaths(mode); if (mode === 'single-node') { - const logFilePaths = Array.from( - lightnetDockerProcessToLogFileMapping.values() - ).map((value) => value.split(',')[0]); - for (const logFilePath of logFilePaths) { - try { - const destinationFilePath = path.resolve( - `${logsDir}/${logFilePath.replace(/\//g, '_')}` - ); - await shellExec( - `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.SINGLE_NODE}/${logFilePath} ${destinationFilePath}`, - { silent: !isDebug } - ); - } catch (error) { - printErrorIfDebug(error); - } - } + await processSingleNodeLogs(logFilePaths, logsDir); } else { - const logFilePaths = Array.from( - lightnetDockerProcessToLogFileMapping.values() - ).map((value) => { - const logFilePaths = value.split(','); - return logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]; - }); if (archive) { - await shellExec( - `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.SINGLE_NODE}/logs/archive-node-api.log ${logsDir}`, - { silent: !isDebug } - ); - } - for (const logFilePath of logFilePaths) { - try { - const destinationFilePath = path.resolve( - `${logsDir}/${logFilePath.replace(/\//g, '_')}` - ); - await shellExec( - `docker cp ${lightnetDockerContainerName}:${ContainerLogFilesPrefix.MULTI_NODE}/${logFilePath} ${destinationFilePath}`, - { silent: !isDebug } - ); - } catch (error) { - printErrorIfDebug(error); - } + await processArchiveNodeApiLogs(logsDir); } + await processMultiNodeLogs(logFilePaths, logsDir); } return logsDir; } catch (error) { @@ -655,6 +595,80 @@ async function saveDockerContainerProcessesLogs() { } } +function createLogsDirectory() { + const timeZoneOffset = new Date().getTimezoneOffset() * 60000; + const localMoment = new Date(Date.now() - timeZoneOffset); + return path.resolve( + `${lightnetLogsDir}/${localMoment + .toISOString() + .split('.')[0] + .replace(/:/g, '-')}` + ); +} + +function getLogFilePaths(mode) { + return Array.from(lightnetDockerProcessToLogFileMapping.values()).map( + (value) => { + const logFilePaths = value.split(','); + return mode === 'single-node' || logFilePaths.length === 1 + ? logFilePaths[0] + : logFilePaths[1]; + } + ); +} + +async function processSingleNodeLogs(logFilePaths, logsDir) { + for (const logFilePath of logFilePaths) { + try { + await copyContainerLogToHost( + logFilePath, + logsDir, + ContainerLogFilesPrefix.SINGLE_NODE + ); + } catch (error) { + printErrorIfDebug(error); + } + } +} + +async function processMultiNodeLogs(logFilePaths, logsDir) { + for (const logFilePath of logFilePaths) { + try { + await copyContainerLogToHost( + logFilePath, + logsDir, + ContainerLogFilesPrefix.MULTI_NODE + ); + } catch (error) { + printErrorIfDebug(error); + } + } +} + +async function processArchiveNodeApiLogs(logsDir) { + try { + await copyContainerLogToHost( + lightnetDockerProcessToLogFileMapping.get( + lightnetProcessName.archiveNodeApi + ), + logsDir, + ContainerLogFilesPrefix.SINGLE_NODE + ); + } catch (error) { + printErrorIfDebug(error); + } +} + +async function copyContainerLogToHost(logFilePath, logsDir, prefix) { + const destinationFilePath = path.resolve( + `${logsDir}/${logFilePath.replace(/\//g, '_')}` + ); + await shellExec( + `docker cp ${lightnetDockerContainerName}:${prefix}/${logFilePath} ${destinationFilePath}`, + { silent: !isDebug } + ); +} + async function waitForBlockchainNetworkReadiness(mode) { let blockchainSyncAttempt = 1; let blockchainIsReady = false; From 7fdc226a450498d475ce8938f923efd277563d75 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 21 Nov 2023 14:53:18 +0200 Subject: [PATCH 04/16] zk lightnet explorer command implementation. --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba467e7..32e50001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.2] - 2023-11-XX + +### Added + +- Lightnet sub-commands implementation (`explorer`). [#521](https://github.com/o1-labs/zkapp-cli/pull/521) + ## [0.15.1] - 2023-11-XX ### Added diff --git a/package.json b/package.json index 1ba5219b..c3c4b134 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zkapp-cli", - "version": "0.15.1", + "version": "0.15.2", "description": "CLI to create zkApps (zero-knowledge apps) for Mina Protocol", "homepage": "https://github.com/o1-labs/zkapp-cli/", "keywords": [ From 92c3cd4cbe62973bf68512107d16ac975a2a6be3 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 21 Nov 2023 19:41:11 +0200 Subject: [PATCH 05/16] Package-lock. --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9e90ea0..28af7d16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zkapp-cli", - "version": "0.15.1", + "version": "0.15.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "zkapp-cli", - "version": "0.15.1", + "version": "0.15.2", "license": "Apache-2.0", "dependencies": { "chalk": "^5.3.0", From c84651839cda4c93011e69473eed6668d4807306 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Tue, 21 Nov 2023 21:58:07 +0200 Subject: [PATCH 06/16] Putting logs streaming within the step to prevent issues with some edge cases. --- src/lib/lightnet.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 8eea0b18..57f14858 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -350,10 +350,13 @@ export async function lightnetFollowLogs({ process, debug }) { const selectedProcess = process || (await promptForDockerContainerProcess(processToLogFileMapping)); const logFilePath = processToLogFileMapping.get(selectedProcess); - await streamDockerContainerFileContent( - lightnetDockerContainerName, - logFilePath - ); + + await step('Docker container file content streaming', async () => { + await streamDockerContainerFileContent( + lightnetDockerContainerName, + logFilePath + ); + }); } function getProcessToLogFileMapping({ mode, archive }) { @@ -552,12 +555,13 @@ async function streamDockerContainerFileContent(containerName, filePath) { try { const border = getBorderCharacters('norc'); console.log( - '\n' + chalk.reset.bold('Docker container file content streaming') + '\n' - ); - console.log( - table([[chalk.reset('Use Ctrl+C to stop the file content streaming.')]], { - border, - }) + '\n' + + table( + [[chalk.reset('Use Ctrl+C to stop the file content streaming.')]], + { + border, + } + ) ); await shellExec(`docker exec ${containerName} tail -n 50 -f ${filePath}`, { silent: false, From 594bb3d133c0f431e8397357d26c4acea5d626c0 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 23 Nov 2023 13:41:42 +0200 Subject: [PATCH 07/16] zk lightnet explorer sub-command implementation. --- package-lock.json | 942 +++++++++++++++++++++++++++++++++++++++----- package.json | 2 + src/bin/index.js | 27 ++ src/lib/lightnet.js | 250 +++++++++++- 4 files changed, 1121 insertions(+), 100 deletions(-) diff --git a/package-lock.json b/package-lock.json index 28af7d16..daa00f68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "Apache-2.0", "dependencies": { "chalk": "^5.3.0", + "decompress": "^4.2.1", "enquirer": "^2.4.1", "envinfo": "^7.10.0", "fast-glob": "^3.3.1", @@ -18,6 +19,7 @@ "gittar": "^0.1.1", "mina-signer": "^2.1.0", "o1js": "^0.14.*", + "opener": "^1.5.2", "ora": "^7.0.1", "shelljs": "^0.8.5", "table": "^6.8.1", @@ -73,12 +75,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", + "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -211,12 +213,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz", + "integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -251,22 +253,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -349,18 +351,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -390,12 +392,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -475,9 +477,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", + "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -664,33 +666,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz", + "integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.4", + "@babel/generator": "^7.23.4", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.23.4", + "@babel/types": "^7.23.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -708,13 +710,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", + "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2288,6 +2290,33 @@ "ieee754": "^1.2.1" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2548,6 +2577,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2585,6 +2619,150 @@ } } }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress/node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -2701,6 +2879,14 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -3070,6 +3256,14 @@ "bser": "2.1.1" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3082,6 +3276,14 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3165,6 +3367,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -3545,6 +3752,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3585,6 +3797,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5308,6 +5525,14 @@ "node": ">=16.4.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5330,6 +5555,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -5558,6 +5791,11 @@ "node": ">=8" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -5587,6 +5825,33 @@ "node": ">=0.10" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -5722,6 +5987,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6033,6 +6303,23 @@ } ] }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -6321,6 +6608,14 @@ "node": ">=8" } }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -6436,6 +6731,59 @@ "node": ">=4.5" } }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tar-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6456,12 +6804,22 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6597,6 +6955,38 @@ "node": ">=14.17" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -6766,6 +7156,14 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6813,6 +7211,15 @@ "node": ">=12" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -6853,12 +7260,12 @@ } }, "@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", + "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", "dev": true, "requires": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "dependencies": { @@ -6964,12 +7371,12 @@ } }, "@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz", + "integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==", "dev": true, "requires": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -6997,19 +7404,19 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -7068,15 +7475,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -7097,12 +7504,12 @@ } }, "@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -7166,9 +7573,9 @@ } }, "@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", + "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -7298,30 +7705,30 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz", + "integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.4", + "@babel/generator": "^7.23.4", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.23.4", + "@babel/types": "^7.23.4", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -7335,13 +7742,13 @@ } }, "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", + "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -8508,6 +8915,30 @@ "ieee754": "^1.2.1" } }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -8680,6 +9111,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -8706,6 +9142,123 @@ "ms": "2.1.2" } }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + } + } + } + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + } + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + } + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, "dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -8791,6 +9344,14 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -9066,6 +9627,14 @@ "bser": "2.1.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -9075,6 +9644,11 @@ "flat-cache": "^3.0.4" } }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -9136,6 +9710,11 @@ "signal-exit": "^4.0.1" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -9390,6 +9969,11 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9412,6 +9996,11 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -10691,6 +11280,11 @@ "tslib": "^2.3.0" } }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -10707,6 +11301,11 @@ "mimic-fn": "^2.1.0" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -10864,6 +11463,11 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10881,6 +11485,24 @@ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "requires": { + "pinkie": "^2.0.0" + } + }, "pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -10972,6 +11594,11 @@ } } }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11164,6 +11791,21 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "requires": { + "commander": "^2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -11372,6 +12014,14 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, "strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -11452,6 +12102,58 @@ "yallist": "^3.1.1" } }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -11469,12 +12171,22 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -11557,6 +12269,26 @@ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -11682,6 +12414,11 @@ } } }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -11717,6 +12454,15 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index c3c4b134..94510982 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ }, "dependencies": { "chalk": "^5.3.0", + "decompress": "^4.2.1", "enquirer": "^2.4.1", "envinfo": "^7.10.0", "fast-glob": "^3.3.1", @@ -62,6 +63,7 @@ "gittar": "^0.1.1", "mina-signer": "^2.1.0", "o1js": "^0.14.*", + "opener": "^1.5.2", "ora": "^7.0.1", "shelljs": "^0.8.5", "table": "^6.8.1", diff --git a/src/bin/index.js b/src/bin/index.js index c93702ed..e37a84da 100755 --- a/src/bin/index.js +++ b/src/bin/index.js @@ -12,6 +12,7 @@ import { deploy } from '../lib/deploy.js'; import { example } from '../lib/example.js'; import { file } from '../lib/file.js'; import { + lightnetExplorer, lightnetFollowLogs, lightnetSaveLogs, lightnetStart, @@ -268,6 +269,32 @@ yargs(hideBin(process.argv)) .demandCommand(); } ) + .command( + ['explorer [use] [list] [debug]'], + 'Launch the lightweight Mina Explorer.', + { + use: { + alias: 'u', + demand: false, + string: true, + hidden: false, + default: 'latest', + description: + 'The version of the lightweight Mina Explorer to use.\nThe "latest" value will use the latest available version.', + }, + list: { + alias: 'l', + demand: false, + boolean: true, + hidden: false, + default: false, + description: + 'Whether to list the available versions of the lightweight Mina Explorer.', + }, + ...commonOptions, + }, + async (argv) => await lightnetExplorer(argv) + ) .demandCommand(); } ) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 57f14858..76a97c0e 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -1,6 +1,8 @@ import chalk from 'chalk'; +import decompress from 'decompress'; import enquirer from 'enquirer'; import fs from 'fs-extra'; +import opener from 'opener'; import path from 'path'; import shell from 'shelljs'; import { getBorderCharacters, table } from 'table'; @@ -13,6 +15,12 @@ const lightnetConfigFile = path.resolve( `${Constants.lightnetWorkDir}/config.json` ); const lightnetLogsDir = path.resolve(`${Constants.lightnetWorkDir}/logs`); +const lightnetExplorerDir = path.resolve( + `${Constants.lightnetWorkDir}/explorer` +); +const lightnetExplorerConfigFile = path.resolve( + `${lightnetExplorerDir}/current.json` +); const lightnetDockerContainerName = 'mina-local-lightnet'; const lightnetMinaDaemonGraphQlEndpoint = 'http://localhost:8080/graphql'; const lightnetAccountsManagerEndpoint = 'http://localhost:8181'; @@ -167,8 +175,8 @@ export async function lightnetStart({ /** * Stops the lightweight Mina blockchain network Docker container. * @param {object} argv - The arguments object provided by yargs. - * @param {string} argv.saveLogs - Whether to save the Docker container processes logs to the host file system. - * @param {string} argv.cleanUp - Whether to perform the clean up. + * @param {boolean} argv.saveLogs - Whether to save the Docker container processes logs to the host file system. + * @param {boolean} argv.cleanUp - Whether to perform the clean up. * @param {boolean} argv.debug - Whether to print the debug information. * @returns {Promise} */ @@ -359,6 +367,244 @@ export async function lightnetFollowLogs({ process, debug }) { }); } +/** + * Launches the lightweight Mina Explorer. + * @param {object} argv - The arguments object provided by yargs. + * @param {string} argv.use - The version of the lightweight Mina Explorer to use. + * @param {boolean} argv.list - Whether to list the available versions of the lightweight Mina Explorer. + * @param {boolean} argv.debug - Whether to print the debug information. + * @returns {Promise} + */ +export async function lightnetExplorer({ use, list, debug }) { + isDebug = debug; + if (list) { + await printExplorerVersions(); + } else { + await launchExplorer(use); + } +} + +async function printExplorerVersions() { + try { + const releasesPrintLimit = 5; + const border = getBorderCharacters('norc'); + const boldTitle = chalk.reset.bold('Lightweight Mina Explorer versions'); + const versions = [ + [boldTitle, '', ''], + ['Version', 'Published on', 'Is in use?'], + ]; + const releases = await fetchExplorerReleases(); + const currentVersion = getCurrentExplorerVersion(); + if (releases.length === 0) { + versions.push([ + chalk.yellow('No data available yet.\nPlease try again later.'), + '', + '', + ]); + console.log( + '\n' + + table(versions, { + border, + spanningCells: [ + { col: 0, row: 0, colSpan: 3, alignment: 'center' }, + { col: 0, row: 2, colSpan: 3, alignment: 'center' }, + ], + }) + ); + return; + } + for (const release of releases.slice(0, releasesPrintLimit)) { + versions.push([ + chalk.reset.bold(release.name), + new Date(release.published_at).toLocaleString(), + currentVersion === release.name ? chalk.reset.green.bold('✓ Yes') : '', + ]); + } + console.log( + '\n' + + table(versions, { + border, + spanningCells: [{ col: 0, row: 0, colSpan: 3, alignment: 'center' }], + }) + ); + if (releases.length > releasesPrintLimit) { + console.log( + `Only ${chalk.green.bold( + releasesPrintLimit + )} most recent versions are shown.` + + '\nPlease refer to the GitHub repository for the full list of available versions:' + + chalk.green( + '\nhttps://github.com/o1-labs/mina-lightweight-explorer/releases' + ) + ); + } + } catch (error) { + printErrorIfDebug(error); + console.log( + chalk.red( + '\nIssue happened while fetching the lightweight Mina Explorer available versions!' + ) + ); + shell.exit(1); + } +} + +async function launchExplorer(use) { + try { + const releases = await fetchExplorerReleases(); + if (releases.length === 0) { + console.log( + chalk.red( + '\nNo lightweight Mina Explorer versions are available yet.\nPlease try again later.' + ) + ); + shell.exit(1); + } + const useVersion = use === 'latest' ? releases[0].name : use; + const release = releases.find((release) => release.name === useVersion); + const explorerReleasePath = path.resolve( + `${lightnetExplorerDir}/${useVersion}` + ); + const explorerReleaseIndexFilePath = path.resolve( + `${explorerReleasePath}/index.html` + ); + if (!release) { + console.log( + chalk.red( + `\nThe specified version ("${useVersion}") of the lightweight Mina Explorer does not exist!` + ) + ); + shell.exit(1); + } + await handleExplorerReleasePresence(explorerReleasePath, release); + await updateCurrentExplorerVersion(useVersion); + await step('Launching the lightweight Mina Explorer', async () => { + opener(explorerReleaseIndexFilePath); + }); + console.log( + chalk.reset( + '\nThe lightweight Mina Explorer is available at the following path:' + + '\n\n' + + chalk.green.bold(explorerReleaseIndexFilePath) + + '\n' + ) + ); + } catch (error) { + printErrorIfDebug(error); + console.log( + chalk.red( + '\nIssue happened while launching the lightweight Mina Explorer!' + ) + ); + shell.exit(1); + } +} + +function getCurrentExplorerVersion() { + if (fs.existsSync(lightnetExplorerConfigFile)) { + return fs.readJSONSync(lightnetExplorerConfigFile).version; + } + return null; +} + +async function updateCurrentExplorerVersion(version) { + const currentVersion = getCurrentExplorerVersion(); + if (currentVersion !== version) { + await step( + 'Updating the current lightweight Mina Explorer version in use', + async () => { + let explorerConfig = { version }; + if (fs.existsSync(lightnetExplorerConfigFile)) { + explorerConfig = fs.readJSONSync(lightnetExplorerConfigFile); + explorerConfig.version = version; + } + fs.ensureDirSync(lightnetExplorerDir); + fs.outputJsonSync(lightnetExplorerConfigFile, explorerConfig, { + spaces: 2, + flag: 'w', + }); + } + ); + } +} + +async function fetchExplorerReleases() { + let releases = []; + await step( + 'Fetching the lightweight Mina Explorer releases information', + async () => { + const response = await fetch( + 'https://api.github.com/repos/o1-labs/mina-lightweight-explorer/releases', + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + } + ); + if (!response.ok) { + throw new Error( + `Received ${response.status} status code from the GitHub API.` + ); + } + releases = await response.json(); + } + ); + return releases; +} + +async function downloadExplorerRelease(release) { + await step( + 'Downloading the lightweight Mina Explorer release bundle', + async () => { + const response = await fetch(release.zipball_url); + if (!response.ok) { + throw new Error( + `Received ${response.status} status code from the GitHub API.` + ); + } + const arrayBuffer = await response.arrayBuffer(); + fs.ensureDirSync(lightnetExplorerDir); + fs.writeFileSync( + path.resolve(`${lightnetExplorerDir}/${release.name}.zip`), + Buffer.from(arrayBuffer), + 'binary' + ); + } + ); +} + +async function handleExplorerReleasePresence(explorerReleasePath, release) { + if ( + !fs.existsSync(explorerReleasePath) || + fs.readdirSync(explorerReleasePath).length === 0 + ) { + const tmpDir = path.resolve(`${explorerReleasePath}-tmp`); + await step('Preparing the file-system', async () => { + fs.removeSync(explorerReleasePath); + fs.ensureDirSync(explorerReleasePath); + fs.ensureDirSync(tmpDir); + }); + await downloadExplorerRelease(release); + await step('Unpacking the release bundle', async () => { + await decompress(`${explorerReleasePath}.zip`, tmpDir); + }); + await step('Restructuring the file-system', async () => { + const extractedDir = fs.readdirSync(tmpDir)[0]; + fs.moveSync( + path.resolve(`${tmpDir}/${extractedDir}`), + path.resolve(explorerReleasePath), + { overwrite: true } + ); + }); + await step('Cleaning up', async () => { + fs.removeSync(`${explorerReleasePath}.zip`); + fs.removeSync(tmpDir); + }); + } +} + function getProcessToLogFileMapping({ mode, archive }) { let mapping = new Map(lightnetDockerProcessToLogFileMapping); if (mode === 'single-node') { From 8c6616bd0a3a16eafa41a5e5113f1cf804d73dd7 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 24 Nov 2023 10:05:06 +0200 Subject: [PATCH 08/16] Add the file:// schema to explorer path. --- src/lib/lightnet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 76a97c0e..f342f2a3 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -479,7 +479,7 @@ async function launchExplorer(use) { await handleExplorerReleasePresence(explorerReleasePath, release); await updateCurrentExplorerVersion(useVersion); await step('Launching the lightweight Mina Explorer', async () => { - opener(explorerReleaseIndexFilePath); + opener(`file://${explorerReleaseIndexFilePath}`); }); console.log( chalk.reset( From fb9aaa3ea8b2f1bdf456942d806e529d27ead114 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 29 Nov 2023 09:21:58 +0200 Subject: [PATCH 09/16] Update src/lib/lightnet.js Co-authored-by: Yoni Mekuria --- src/lib/lightnet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 57f14858..48e7a5b1 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -362,7 +362,7 @@ export async function lightnetFollowLogs({ process, debug }) { function getProcessToLogFileMapping({ mode, archive }) { let mapping = new Map(lightnetDockerProcessToLogFileMapping); if (mode === 'single-node') { - mapping = new Map(Array.from(mapping).slice(0, 3)); + mapping = new Map([...mapping].slice(0, 3)); mapping.forEach((value, key) => { mapping.set( key, From 801593b49fa996c2ffcbfc8afe59522c93184adb Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 29 Nov 2023 09:22:04 +0200 Subject: [PATCH 10/16] Update src/lib/lightnet.js Co-authored-by: Yoni Mekuria --- src/lib/lightnet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 48e7a5b1..d3ce889d 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -394,7 +394,7 @@ async function promptForDockerContainerProcess(processToLogFileMapping) { const response = await enquirer.prompt({ type: 'select', name: 'selectedProcess', - choices: Array.from(processToLogFileMapping.keys()), + [...processToLogFileMapping.keys()], message: () => { return chalk.reset( 'Please select the Docker container process to follow the logs of' From 78449debbed69ee7f06efe12854cc838b8dd310d Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 29 Nov 2023 09:22:13 +0200 Subject: [PATCH 11/16] Update src/lib/lightnet.js Co-authored-by: Yoni Mekuria --- src/lib/lightnet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index d3ce889d..29646f4d 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -611,7 +611,7 @@ function createLogsDirectory() { } function getLogFilePaths(mode) { - return Array.from(lightnetDockerProcessToLogFileMapping.values()).map( + return [...lightnetDockerProcessToLogFileMapping.values()].map( (value) => { const logFilePaths = value.split(','); return mode === 'single-node' || logFilePaths.length === 1 From ab662d131ae7bb8377a0198d1f567016213cd213 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 29 Nov 2023 09:24:39 +0200 Subject: [PATCH 12/16] Fix for review. --- src/lib/lightnet.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 29646f4d..c7d3f6b7 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -394,7 +394,7 @@ async function promptForDockerContainerProcess(processToLogFileMapping) { const response = await enquirer.prompt({ type: 'select', name: 'selectedProcess', - [...processToLogFileMapping.keys()], + choices: [...processToLogFileMapping.keys()], message: () => { return chalk.reset( 'Please select the Docker container process to follow the logs of' @@ -611,14 +611,12 @@ function createLogsDirectory() { } function getLogFilePaths(mode) { - return [...lightnetDockerProcessToLogFileMapping.values()].map( - (value) => { - const logFilePaths = value.split(','); - return mode === 'single-node' || logFilePaths.length === 1 - ? logFilePaths[0] - : logFilePaths[1]; - } - ); + return [...lightnetDockerProcessToLogFileMapping.values()].map((value) => { + const logFilePaths = value.split(','); + return mode === 'single-node' || logFilePaths.length === 1 + ? logFilePaths[0] + : logFilePaths[1]; + }); } async function processSingleNodeLogs(logFilePaths, logsDir) { From abc2d7d933029ba9d90f08db7ae39a150a1ed60b Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 29 Nov 2023 17:03:45 +0200 Subject: [PATCH 13/16] Addressing review comments. --- CHANGELOG.md | 2 +- src/bin/index.js | 4 +++- src/lib/constants.js | 27 +++++++++++++-------------- src/lib/lightnet.js | 44 +++++++++++++++++--------------------------- 4 files changed, 34 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ba467e7..1b229ce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.15.1] - 2023-11-XX +## [0.15.1] - 2023-11-29 ### Added diff --git a/src/bin/index.js b/src/bin/index.js index bb0baccd..62daeb7a 100755 --- a/src/bin/index.js +++ b/src/bin/index.js @@ -257,7 +257,9 @@ yargs(hideBin(process.argv)) demand: false, string: true, hidden: false, - choices: Object.values(Constants.lightnetProcessName), + choices: [ + ...Constants.lightnetProcessToLogFileMapping.keys(), + ], description: 'The name of the Docker container process to follow the logs of.', }, diff --git a/src/lib/constants.js b/src/lib/constants.js index f23ca343..0a14d7dc 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -8,9 +8,8 @@ import path from 'path'; * @typedef {'fast' | 'real'} LightnetType * @typedef {'none' | 'full'} LightnetProofLevel * @typedef {'o1js-main' | 'berkeley' | 'develop'} LightnetMinaBranch - * @typedef {{ archiveNodeApi: string, minaArchive: string, minaSingleNodeDaemon: string, minaFish1: string, minaFollowing1: string, minaSeed1: string, minaSnarkCoordinator1: string, minaSnarkWorker1: string, minaWhale1: string, minaWhale2: string }} LightnetProcessName * - * @type {{ uiTypes: UiType[], exampleTypes: ExampleType[], feePayerCacheDir: string, lightnetWorkDir: string, lightnetModes: LightnetMode[], lightnetTypes: LightnetType[], lightnetProofLevels: LightnetProofLevel[], lightnetMinaBranches: LightnetMinaBranch[], lightnetProcessName: LightnetProcessName }} + * @type {{ uiTypes: UiType[], exampleTypes: ExampleType[], feePayerCacheDir: string, lightnetWorkDir: string, lightnetModes: LightnetMode[], lightnetTypes: LightnetType[], lightnetProofLevels: LightnetProofLevel[], lightnetMinaBranches: LightnetMinaBranch[], lightnetProcessToLogFileMapping: Map }} */ const Constants = Object.freeze({ uiTypes: ['next', 'svelte', 'nuxt', 'empty', 'none'], @@ -21,18 +20,18 @@ const Constants = Object.freeze({ lightnetTypes: ['fast', 'real'], lightnetProofLevels: ['none', 'full'], lightnetMinaBranches: ['o1js-main', 'berkeley', 'develop'], - lightnetProcessName: Object.freeze({ - archiveNodeApi: 'Archive-Node-API application', - minaArchive: 'Mina Archive process', - minaSingleNodeDaemon: 'Mina multi-purpose Daemon', - minaFish1: 'Fish BP #1', - minaFollowing1: 'Non-consensus node #1', - minaSeed1: 'Seed node #1', - minaSnarkCoordinator1: 'SNARK coordinator #1', - minaSnarkWorker1: 'SNARK worker #1', - minaWhale1: 'Whale BP #1', - minaWhale2: 'Whale BP #2', - }), + lightnetProcessToLogFileMapping: new Map([ + ['Archive-Node-API application', 'logs/archive-node-api.log'], + ['Mina Archive process', 'logs/archive-node.log,archive/log.txt'], + ['Mina multi-purpose Daemon', 'logs/single-node-network.log'], + ['Fish BP #1', 'fish_0/log.txt'], + ['Non-consensus node #1', 'node_0/log.txt'], + ['Seed node #1', 'seed/log.txt'], + ['SNARK coordinator #1', 'snark_coordinator/log.txt'], + ['SNARK worker #1', 'snark_workers/worker_0/log.txt'], + ['Whale BP #1', 'whale_0/log.txt'], + ['Whale BP #2', 'whale_1/log.txt'], + ]), }); export default Constants; diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index c7d3f6b7..ad565d4a 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -17,19 +17,9 @@ const lightnetDockerContainerName = 'mina-local-lightnet'; const lightnetMinaDaemonGraphQlEndpoint = 'http://localhost:8080/graphql'; const lightnetAccountsManagerEndpoint = 'http://localhost:8181'; const lightnetArchiveNodeApiEndpoint = 'http://localhost:8282'; -const lightnetProcessName = Constants.lightnetProcessName; -const lightnetDockerProcessToLogFileMapping = new Map([ - [lightnetProcessName.archiveNodeApi, 'logs/archive-node-api.log'], - [lightnetProcessName.minaArchive, 'logs/archive-node.log,archive/log.txt'], - [lightnetProcessName.minaSingleNodeDaemon, 'logs/single-node-network.log'], - [lightnetProcessName.minaFish1, 'fish_0/log.txt'], - [lightnetProcessName.minaFollowing1, 'node_0/log.txt'], - [lightnetProcessName.minaSeed1, 'seed/log.txt'], - [lightnetProcessName.minaSnarkCoordinator1, 'snark_coordinator/log.txt'], - [lightnetProcessName.minaSnarkWorker1, 'snark_workers/worker_0/log.txt'], - [lightnetProcessName.minaWhale1, 'whale_0/log.txt'], - [lightnetProcessName.minaWhale2, 'whale_1/log.txt'], -]); +const archiveNodeApiProcessName = 'Archive-Node-API application'; +const minaArchiveProcessName = 'Mina Archive process'; +const multiPurposeMinaDaemonProcessName = 'Mina multi-purpose Daemon'; const DockerContainerState = { RUNNING: 'running', NOT_FOUND: 'not-found', @@ -360,7 +350,7 @@ export async function lightnetFollowLogs({ process, debug }) { } function getProcessToLogFileMapping({ mode, archive }) { - let mapping = new Map(lightnetDockerProcessToLogFileMapping); + let mapping = new Map(Constants.lightnetProcessToLogFileMapping); if (mode === 'single-node') { mapping = new Map([...mapping].slice(0, 3)); mapping.forEach((value, key) => { @@ -370,13 +360,13 @@ function getProcessToLogFileMapping({ mode, archive }) { ); }); } else { - mapping.delete(lightnetProcessName.minaSingleNodeDaemon); + mapping.delete(multiPurposeMinaDaemonProcessName); mapping.forEach((value, key) => { const logFilePaths = value.split(','); mapping.set( key, `${ - lightnetProcessName.archiveNodeApi === key + archiveNodeApiProcessName === key ? ContainerLogFilesPrefix.SINGLE_NODE : ContainerLogFilesPrefix.MULTI_NODE }/${logFilePaths.length === 1 ? logFilePaths[0] : logFilePaths[1]}` @@ -384,8 +374,8 @@ function getProcessToLogFileMapping({ mode, archive }) { }); } if (!archive) { - mapping.delete(lightnetProcessName.archiveNodeApi); - mapping.delete(lightnetProcessName.minaArchive); + mapping.delete(archiveNodeApiProcessName); + mapping.delete(minaArchiveProcessName); } return mapping; } @@ -611,12 +601,14 @@ function createLogsDirectory() { } function getLogFilePaths(mode) { - return [...lightnetDockerProcessToLogFileMapping.values()].map((value) => { - const logFilePaths = value.split(','); - return mode === 'single-node' || logFilePaths.length === 1 - ? logFilePaths[0] - : logFilePaths[1]; - }); + return [...Constants.lightnetProcessToLogFileMapping.values()].map( + (value) => { + const logFilePaths = value.split(','); + return mode === 'single-node' || logFilePaths.length === 1 + ? logFilePaths[0] + : logFilePaths[1]; + } + ); } async function processSingleNodeLogs(logFilePaths, logsDir) { @@ -650,9 +642,7 @@ async function processMultiNodeLogs(logFilePaths, logsDir) { async function processArchiveNodeApiLogs(logsDir) { try { await copyContainerLogToHost( - lightnetDockerProcessToLogFileMapping.get( - lightnetProcessName.archiveNodeApi - ), + Constants.lightnetProcessToLogFileMapping.get(archiveNodeApiProcessName), logsDir, ContainerLogFilesPrefix.SINGLE_NODE ); From 67166aaa6f4c82891988e97e9aece6bb3d607eb0 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 2 Dec 2023 10:33:03 +0200 Subject: [PATCH 14/16] Update src/lib/lightnet.js Co-authored-by: Yoni Mekuria --- src/lib/lightnet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lightnet.js b/src/lib/lightnet.js index 9ba53adf..7ace5839 100644 --- a/src/lib/lightnet.js +++ b/src/lib/lightnet.js @@ -19,7 +19,7 @@ const lightnetExplorerDir = path.resolve( `${Constants.lightnetWorkDir}/explorer` ); const lightnetExplorerConfigFile = path.resolve( - `${lightnetExplorerDir}/current.json` + `${lightnetExplorerDir}/config.json` ); const lightnetDockerContainerName = 'mina-local-lightnet'; const lightnetMinaDaemonGraphQlEndpoint = 'http://localhost:8080/graphql'; From bd8cc7138cf17758772b7d25d78b424b59a044df Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 2 Dec 2023 10:34:17 +0200 Subject: [PATCH 15/16] CHANGELOG updates. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fc5acc..1d6cd46f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## Unreleased -## [0.15.2] - 2023-11-XX +## [0.15.2] - 2023-12-02 ### Added From 49401f5b03947fa7c61d446cf6519809cb2f61cd Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sun, 3 Dec 2023 16:06:43 +0200 Subject: [PATCH 16/16] CHANGELOG updates. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6cd46f..05be9e04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## Unreleased -## [0.15.2] - 2023-12-02 +## [0.15.2] - 2023-12-04 ### Added