diff --git a/electron/index.js b/electron/index.js index 8342dde7facd29dd9785e004fba5fa45024c3e61..d61a7aa1c42c1dfd7653453736c3d7206346c7a2 100644 --- a/electron/index.js +++ b/electron/index.js @@ -9,16 +9,19 @@ const url = require('url'); const addMenu = require('./menu'); const { cli } = require('./cli'); -const doesParityExist = require('./operations/doesParityExist'); +const { doesParityExist } = require('./operations/doesParityExist'); const fetchParity = require('./operations/fetchParity'); const handleError = require('./operations/handleError'); const messages = require('./messages'); +const { productName } = require('./config.json'); +const pino = require('./utils/pino')({ name: 'electron' }); const { runParity, killParity } = require('./operations/runParity'); const { app, BrowserWindow, ipcMain, session } = electron; let mainWindow; function createWindow () { + pino.info(`Starting ${productName}...`); mainWindow = new BrowserWindow({ height: 800, width: 1200 diff --git a/electron/operations/doesParityExist.js b/electron/operations/doesParityExist.js index a8f4e06446b316386d60257bf8ab52f91fd0f6f9..c67cebe0cc3391de429d91024f24b2ae4825ef5d 100644 --- a/electron/operations/doesParityExist.js +++ b/electron/operations/doesParityExist.js @@ -3,11 +3,68 @@ // // SPDX-License-Identifier: MIT +const { app } = require('electron'); +const commandExists = require('command-exists'); const fs = require('fs'); -const util = require('util'); +const promiseAny = require('promise-any'); +const { promisify } = require('util'); -const parityPath = require('../utils/parityPath'); +const pino = require('../utils/pino')(); -const fsExists = util.promisify(fs.stat); +const fsExists = promisify(fs.stat); -module.exports = () => fsExists(parityPath()); +// The default path to install parity, in case there's no other instance found +// on the machine. +const defaultParityPath = `${app.getPath( + 'userData' +)}/parity${process.platform === 'win32' ? '.exe' : ''}`; + +let parityPath; // The real parity path, will be populated after doesParityExist Promise resolves + +// OS locations to test if parity binary exists +const locations = { + linux: ['/bin/parity', '/usr/bin/parity', '/usr/local/bin/parity'], + darwin: ['/Applications/Parity Ethereum.app/Contents/MacOS/parity'], + win32: ['C:\\Program Files\\Parity Technologies\\Parity\\parity.exe'] +}; + +module.exports = { + defaultParityPath, + /** + * This function checks if parity has been installed on the local machine: + * - first check if the program is in $PATH, using `command-exists` + * - then check the OS default installation dir if a parity folder exists + * - finally check parity-ui's own userData folder + * This function should run in node env. + * + * @return Promise - Resolves to a string which is the command to run parity. + */ + doesParityExist () { + return commandExists('parity') // First test if `parity` command exists + .then(() => 'parity') // If yes, return `parity` as command to launch parity + .catch(() => + // Then test if OS-specific locations contain parity + promiseAny( + locations[process.platform].map(location => + fsExists(location).then(() => location) + ) + ) + ) + .catch(() => + // Finally test userData folder + fsExists(defaultParityPath).then(() => defaultParityPath) + ) + .then(path => { + parityPath = path; // Save the final result in module variable + pino.info(`Parity found on machine, can be run with "${path}".`); + return path; + }) + .catch(err => { + pino.info(`Parity not found on machine.`); + throw err; + }); + }, + parityPath () { + return parityPath; + } +}; diff --git a/electron/operations/fetchParity.js b/electron/operations/fetchParity.js index 9e726c704343c067322e60be8432d16ba1cc82fe..139a7f7a08f034f347969d1b3b120a39ad67116f 100644 --- a/electron/operations/fetchParity.js +++ b/electron/operations/fetchParity.js @@ -5,17 +5,21 @@ const { app } = require('electron'); const axios = require('axios'); +const cs = require('checksum'); const { download } = require('electron-dl'); const fs = require('fs'); -const util = require('util'); +const { promisify } = require('util'); +const retry = require('async-retry'); +const { defaultParityPath, doesParityExist } = require('./doesParityExist'); const handleError = require('./handleError'); -const { - parity: { channel } -} = require('../../package.json'); -const parityPath = require('../utils/parityPath'); +const { parity: { channel } } = require('../../package.json'); +const pino = require('../utils/pino')(); -const fsChmod = util.promisify(fs.chmod); +const checksum = promisify(cs.file); +const fsChmod = promisify(fs.chmod); + +const VANITY_URL = 'https://vanity-service.parity.io/parity-binaries'; const getArch = () => { switch (process.platform) { @@ -48,25 +52,83 @@ const getOs = () => { } }; +/** + * Remove parity binary in the userData folder + */ +const deleteParity = () => { + if (fs.statSync(defaultParityPath)) { + fs.unlinkSync(defaultParityPath); + } +}; + // Fetch parity from https://vanity-service.parity.io/parity-binaries -module.exports = mainWindow => - axios - .get( - `https://vanity-service.parity.io/parity-binaries?version=${channel}&os=${getOs()}&architecture=${getArch()}` - ) - .then(response => - response.data[0].files.find( - ({ name }) => name === 'parity' || name === 'parity.exe' - ) - ) - .then(({ downloadUrl }) => - download(mainWindow, downloadUrl, { - directory: app.getPath('userData'), - onProgress: progress => - mainWindow.webContents.send('parity-download-progress', progress) // Notify the renderers - }) - ) - .then(() => fsChmod(parityPath(), '755')) - .catch(err => { - handleError(err, 'An error occured while fetching parity.'); - }); +module.exports = mainWindow => { + try { + return retry( + async (_, attempt) => { + if (attempt > 1) { + pino.warn(`Retrying.`); + } + + // Fetch the metadata of the correct version of parity + pino.info( + `Downloading from ${VANITY_URL}?version=${channel}&os=${getOs()}&architecture=${getArch()}.` + ); + const { data } = await axios.get( + `${VANITY_URL}?version=${channel}&os=${getOs()}&architecture=${getArch()}` + ); + + // Get the binary's url + const { downloadUrl } = data[0].files.find( + ({ name }) => name === 'parity' || name === 'parity.exe' + ); + + // Start downloading. This will install parity into defaultParityPath. + const downloadItem = await download(mainWindow, downloadUrl, { + directory: app.getPath('userData'), + onProgress: progress => + // Notify the renderers + mainWindow.webContents.send('parity-download-progress', progress) + }); + const downloadPath = downloadItem.getSavePath(); // Equal to defaultParityPath + + // Once downloaded, we fetch the sha256 checksum + const { downloadUrl: checksumDownloadUrl } = data[0].files.find( + ({ name }) => name === 'parity.sha256' || name === 'parity.exe.sha256' + ); + const { data: checksumData } = await axios.get(checksumDownloadUrl); + // Downloaded checksumData is in the format: "{checksum} {filename}" + const [expectedChecksum] = checksumData.split(' '); + // Calculate the actual checksum + const actualChecksum = await checksum(downloadPath, { + algorithm: 'sha256' + }); + // The 2 checksums should of course match + if (expectedChecksum !== actualChecksum) { + throw new Error( + `Checksum mismatch, expecting ${expectedChecksum}, got ${actualChecksum}.` + ); + } + + // Set a+x permissions on the downloaded binary + await fsChmod(downloadPath, '755'); + + // Double-check that Parity exists now. + return doesParityExist(); + }, + { + onRetry: err => { + pino.warn(err); + + // Everytime we retry, we remove the parity file we just downloaded. + // This needs to be done syncly, since onRetry is sync + deleteParity(); + }, + retries: 3 + } + ); + } catch (err) { + deleteParity(); + handleError(err, 'An error occured while fetching parity.'); + } +}; diff --git a/electron/operations/handleError.js b/electron/operations/handleError.js index e5bcc553963faa9b77b0996a2175de48d6f60bec..cb39b0e6484a990a58e9231d04666cdba7da5dfe 100644 --- a/electron/operations/handleError.js +++ b/electron/operations/handleError.js @@ -5,13 +5,11 @@ const { app, dialog } = require('electron'); -const { - parity: { channel } -} = require('../../package.json'); -const parityPath = require('../utils/parityPath'); +const { bugs: { url }, parity: { channel } } = require('../../package.json'); +const pino = require('../utils/pino')(); module.exports = (err, message = 'An error occurred.') => { - console.error(err); + pino.error(err); dialog.showMessageBox( { buttons: ['OK'], @@ -22,8 +20,8 @@ Channel: ${channel} Error: ${err.message} Please also attach the contents of the following file: -${parityPath()}.log`, - message: `${message} Please file an issue at https://github.com/parity-js/shell/issues.`, +${app.getPath('userData')}/parity.log`, + message: `${message} Please file an issue at ${url}.`, title: 'Parity Error', type: 'error' }, diff --git a/electron/operations/isParityRunning.js b/electron/operations/isParityRunning.js new file mode 100644 index 0000000000000000000000000000000000000000..b032d3e8c7b5f7c436e3f2c8e3797f6bdb5905d0 --- /dev/null +++ b/electron/operations/isParityRunning.js @@ -0,0 +1,44 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +const { promisify } = require('util'); +const pino = require('../utils/pino')(); +const ps = require('ps-node'); + +const lookup = promisify(ps.lookup); + +/** + * Detect if another instance of parity is already running or not. + * + * @return [Object | Boolean] - If there is another instance, return the + * instance object. If not return false. + * @example Here is what's returned when there is an instance running + * { + * pid: '14885', + * command: '/Users/amaurymartiny/Workspace/parity/target/release/parity', + * arguments: [ + * '--testnet', + * '--no-periodic-snapshot', + * '--ws-origins', + * 'all', + * '--light' + * ], + * ppid: '14879' + * } + */ + +const isParityRunning = async () => { + const results = await lookup({ command: 'parity' }); + if (results && results.length) { + pino.info( + `Another instance of parity is already running with pid ${results[0] + .pid}, skip running local instance.` + ); + return results[0]; + } + return false; +}; + +module.exports = isParityRunning; diff --git a/electron/operations/runParity.js b/electron/operations/runParity.js index d0f709b87569a7a849ee7dad4cc0122571d4e16d..aef9b9070172065fd954ed5da0d7e3e82a6a8359 100644 --- a/electron/operations/runParity.js +++ b/electron/operations/runParity.js @@ -5,17 +5,17 @@ const { app } = require('electron'); const fs = require('fs'); -const noop = require('lodash/noop'); const { spawn } = require('child_process'); -const util = require('util'); +const { promisify } = require('util'); const { cli, parityArgv } = require('../cli'); +const isParityRunning = require('./isParityRunning'); const handleError = require('./handleError'); -const parityPath = require('../utils/parityPath'); +const { parityPath } = require('./doesParityExist'); +const pino = require('../utils/pino')(); +const pinoParity = require('../utils/pino')({ name: 'parity' }); -const fsChmod = util.promisify(fs.chmod); -const fsExists = util.promisify(fs.stat); -const fsUnlink = util.promisify(fs.unlink); +const fsChmod = promisify(fs.chmod); let parity = null; // Will hold the running parity instance @@ -28,85 +28,97 @@ const catchableErrors = [ ]; module.exports = { - runParity (mainWindow) { - // Do not run parity with --no-run-parity - if (cli.runParity === false) { - return; - } + async runParity (mainWindow) { + try { + // Do not run parity with --no-run-parity + if (cli.runParity === false) { + return; + } + + // Do not run parity if there is already another instance running + const isRunning = await isParityRunning(); + if (isRunning) { + return; + } + + // Do not run parity if parityPath has not been calculated. Shouldn't + // happen as we always run runParity after doesParityExist resolves. + if (!parityPath) { + throw new Error('Attempting to run Parity before parityPath is set.'); + } + + // Some users somehow had no +x on the parity binary after downloading + // it. We try to set it here (no guarantee it will work, we might not + // have rights to do it). + try { + await fsChmod(parityPath(), '755'); + } catch (e) {} + + let logLastLine; // Always contains last line of the Parity logs + + // Run an instance of parity with the correct args + parity = spawn(parityPath(), parityArgv); + pino.info( + `Running command "${parityPath().replace(' ', '\\ ')} ${parityArgv.join( + ' ' + )}".` + ); + + // Save in memory the last line of the log file, for handling error + const callback = data => { + if (data && data.length) { + logLastLine = data.toString(); + } + pinoParity.info(data.toString()); + }; + parity.stdout.on('data', callback); + parity.stderr.on('data', callback); - // Create a logStream to save logs - const logFile = `${parityPath()}.log`; - - fsExists(logFile) - .then(() => fsUnlink(logFile)) // Delete logFile and create a fresh one on each launch - .catch(noop) - .then(() => fsChmod(parityPath(), '755')) // Should already be 755 after download, just to be sure - .then(() => { - const logStream = fs.createWriteStream(logFile, { flags: 'a' }); - let logLastLine; // Always contains last line of the logFile - - // Run an instance of parity with the correct args - parity = spawn(parityPath(), parityArgv); - - // Pipe all parity command output into the logFile - parity.stdout.pipe(logStream); - parity.stderr.pipe(logStream); - - // Save in memory the last line of the log file, for handling error - const callback = data => { - if (data && data.length) { - logLastLine = data.toString(); - } - }; - parity.stdout.on('data', callback); - parity.stderr.on('data', callback); - - parity.on('error', err => { - handleError(err, 'An error occured while running parity.'); - }); - parity.on('close', (exitCode, signal) => { - if (exitCode === 0) { - return; - } - - // When there's already an instance of parity running, then the log - // is logging a particular line, see below. In this case, we just - // silently ignore our local instance, and let the 1st parity - // instance be the main one. - if (catchableErrors.some(error => logLastLine.includes(error))) { - console.log( - 'Another instance of parity is running, closing local instance.' - ); - return; - } - - // If the exit code is not 0, then we show some error message - if (Object.keys(parityArgv).length > 0) { - // If parity has been launched with some args, then most likely the - // args are wrong, so we show the output of parity. - const log = fs.readFileSync(logFile); - console.log(log.toString()); - app.exit(1); - } else { - handleError( - new Error(`Exit code ${exitCode}, with signal ${signal}.`), - 'An error occured while running parity.' - ); - } - }); - }) - .then(() => { - // Notify the renderers - mainWindow.webContents.send('parity-running', true); - global.isParityRunning = true; // Send this variable to renderes via IPC - }) - .catch(err => { + parity.on('error', err => { handleError(err, 'An error occured while running parity.'); }); + parity.on('close', (exitCode, signal) => { + if (exitCode === 0) { + return; + } + + // When there's already an instance of parity running, then the log + // is logging a particular line, see below. In this case, we just + // silently ignore our local instance, and let the 1st parity + // instance be the main one. + if ( + logLastLine && + catchableErrors.some(error => logLastLine.includes(error)) + ) { + pino.warn( + 'Another instance of parity is running, closing local instance.' + ); + return; + } + + // If the exit code is not 0, then we show some error message + if (Object.keys(parityArgv).length > 0) { + app.exit(1); + } else { + handleError( + new Error(`Exit code ${exitCode}, with signal ${signal}.`), + 'An error occured while running parity.' + ); + } + }); + + // Notify the renderers + mainWindow.webContents.send('parity-running', true); + global.isParityRunning = true; // Send this variable to renderes via IPC + + return Promise.resolve(); + } catch (err) { + handleError(err, 'An error occured while running parity.'); + } }, killParity () { if (parity) { - console.log('Stopping parity.'); + pino.info('Stopping parity.'); parity.kill(); parity = null; } diff --git a/electron/operations/signerNewToken.js b/electron/operations/signerNewToken.js index 5471fdcee39b11e8a759fb134b82d5a84713a3c0..594e501c751b4fa65f24dc129bce0abbc01d9ff8 100644 --- a/electron/operations/signerNewToken.js +++ b/electron/operations/signerNewToken.js @@ -5,7 +5,7 @@ const { spawn } = require('child_process'); -const parityPath = require('../utils/parityPath'); +const { parityPath } = require('./doesParityExist'); module.exports = event => { // Generate a new token diff --git a/electron/utils/parityPath.js b/electron/utils/parityPath.js deleted file mode 100644 index 89f4b99ccf6f352f4ab57971c4e0b92843895454..0000000000000000000000000000000000000000 --- a/electron/utils/parityPath.js +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: MIT - -const { app } = require('electron'); - -const parityPath = `${app.getPath('userData')}/parity${ - process.platform === 'win32' ? '.exe' : '' -}`; - -module.exports = () => parityPath; diff --git a/electron/utils/pino.js b/electron/utils/pino.js new file mode 100644 index 0000000000000000000000000000000000000000..24c97e6d2a1a89d6df7e1c9f6912d3ff337bdda2 --- /dev/null +++ b/electron/utils/pino.js @@ -0,0 +1,32 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: MIT + +const { app } = require('electron'); +const fs = require('fs'); +const { multistream } = require('pino-multi-stream'); +const pino = require('pino'); + +// Pino by default outputs JSON. We prettify that. +const pretty = pino.pretty(); +pretty.pipe(process.stdout); + +// Create 2 output streams: +// - parity.log file (raw JSON) +// - stdout (prettified output) +const streams = [ + { + level: 'info', + stream: fs.createWriteStream(`${app.getPath('userData')}/parity.log`) + }, + { level: 'info', stream: pretty } +]; + +/** + * Usage: const pino = require('../path/to/pino')({ name: 'something' }); + * + * @param {Object} opts - Options to pass to pino. Defaults to { name: 'electron' }. + */ +module.exports = opts => + pino({ name: 'electron', ...opts }, multistream(streams)); diff --git a/package.json b/package.json index 4493707d16906c788c5a1d7cada2d338ea84009e..c5a609a84bc2d774f9387d68c28424e11ccc47f7 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,9 @@ "type": "git", "url": "git+https://github.com/parity-js/light.git" }, + "bugs": { + "url": "https://github.com/parity-js/light/issues" + }, "keywords": [ "Ethereum", "API", @@ -26,7 +29,7 @@ "channel": "beta" }, "scripts": { - "build": "cross-env NODE_ENV=production && npm run build-css && npm run build-js && npm run build-electron", + "build": "cross-env NODE_ENV=production npm run build-css && npm run build-js && npm run build-electron", "build-css": "node-sass-chokidar src/ -o src/", "build-electron": "webpack --config electron/webpack.config.js", "build-js": "react-app-rewired build", @@ -42,7 +45,10 @@ "dependencies": { "@parity/api": "^2.1.22", "@parity/light.js": "https://github.com/parity-js/light.js#0c57c29de42122b0f07ff82dfaf38b48863d8d2e", + "async-retry": "^1.2.1", "axios": "^0.18.0", + "checksum": "^0.1.1", + "command-exists": "^1.2.6", "commander": "^2.15.1", "electron": "^2.0.0", "electron-dl": "^1.11.0", @@ -50,6 +56,10 @@ "lodash": "^4.17.10", "mobx": "^4.2.0", "mobx-react": "^5.1.2", + "pino": "^4.16.1", + "pino-multi-stream": "^3.1.2", + "promise-any": "^0.2.0", + "ps-node": "^0.1.6", "react": "^16.3.2", "react-blockies": "^1.3.0", "react-dom": "^16.3.2", diff --git a/yarn.lock b/yarn.lock index d7a121da098ff14fa5ba2657b665dc5855d87931..22be0b7f4304314e1dc3139fede758b59f86e90f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -490,6 +490,12 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" +async-retry@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.1.tgz#308c6c4e1d91e63397a4676290334ae9bda7bcb1" + dependencies: + retry "0.10.1" + async@^1.4.0, async@^1.5.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1814,6 +1820,12 @@ chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" +checksum@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/checksum/-/checksum-0.1.1.tgz#dc6527d4c90be8560dbd1ed4cecf3297d528e9e9" + dependencies: + optimist "~0.3.5" + chokidar@^1.6.0, chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -2057,6 +2069,10 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +command-exists@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.6.tgz#577f8e5feb0cb0f159cd557a51a9be1bdd76e09e" + commander@2.15.x, commander@^2.11.0, commander@^2.15.1, commander@^2.9.0, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -2131,6 +2147,10 @@ connect-history-api-fallback@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" +connected-domain@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/connected-domain/-/connected-domain-1.0.0.tgz#bfe77238c74be453a79f0cb6058deeb4f2358e93" + console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -3507,6 +3527,10 @@ fast-glob@^2.0.2: merge2 "^1.2.1" micromatch "^3.1.10" +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3515,6 +3539,10 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +fast-safe-stringify@^1.0.8, fast-safe-stringify@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz#9fe22c37fb2f7f86f06b8f004377dbf8f1ee7bc1" + fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" @@ -3682,6 +3710,10 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" +flatstr@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.5.tgz#5b451b08cbd48e2eac54a2bbe0bf46165aa14be3" + flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" @@ -6481,6 +6513,12 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +optimist@~0.3.5: + version "0.3.7" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + dependencies: + wordwrap "~0.0.2" + optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -6783,6 +6821,29 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pino-multi-stream@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pino-multi-stream/-/pino-multi-stream-3.1.2.tgz#261af1b66caf208abe9c97f1bad1990d5c25d303" + dependencies: + pino "^4.7.1" + +pino-std-serializers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.0.0.tgz#63cbacf34bed8c95dd2c67eec824e48b8dd3445f" + +pino@^4.16.1, pino@^4.7.1: + version "4.16.1" + resolved "https://registry.yarnpkg.com/pino/-/pino-4.16.1.tgz#8bcb6b685ee4d4e26adfe79a05f12f6b46962d40" + dependencies: + chalk "^2.3.2" + fast-json-parse "^1.0.3" + fast-safe-stringify "^1.2.3" + flatstr "^1.0.5" + pino-std-serializers "^2.0.0" + pump "^3.0.0" + quick-format-unescaped "^1.1.2" + split2 "^2.2.0" + pkg-conf@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-2.1.0.tgz#2126514ca6f2abfebd168596df18ba57867f0058" @@ -7178,6 +7239,10 @@ progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" +promise-any@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/promise-any/-/promise-any-0.2.0.tgz#7aeaafd6297698d8874cb7d3bc7c0faf89fffe8a" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -7213,6 +7278,12 @@ prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" +ps-node@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ps-node/-/ps-node-0.1.6.tgz#9af67a99d7b1d0132e51a503099d38a8d2ace2c3" + dependencies: + table-parser "^0.1.3" + ps-tree@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" @@ -7240,6 +7311,13 @@ pump@^2.0.0, pump@^2.0.1: end-of-stream "^1.1.0" once "^1.3.1" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pumpify@^1.3.3: version "1.5.0" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.0.tgz#30c905a26c88fa0074927af07256672b474b1c15" @@ -7311,6 +7389,12 @@ querystringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.0.0.tgz#fa3ed6e68eb15159457c89b37bc6472833195755" +quick-format-unescaped@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz#0ca581de3174becef25ac3c2e8956342381db698" + dependencies: + fast-safe-stringify "^1.0.8" + raf@3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" @@ -7924,6 +8008,10 @@ ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" +retry@0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -8320,7 +8408,7 @@ sockjs@0.3.18: faye-websocket "^0.10.0" uuid "^2.0.2" -"solc@github:ngotchac/solc-js": +solc@ngotchac/solc-js: version "0.4.4" resolved "https://codeload.github.com/ngotchac/solc-js/tar.gz/04eb38cc3003fba8cb3656653a7941ed36408818" dependencies: @@ -8439,6 +8527,12 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" + dependencies: + through2 "^2.0.2" + split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" @@ -8741,6 +8835,12 @@ symbol-tree@^3.2.1: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +table-parser@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/table-parser/-/table-parser-0.1.3.tgz#0441cfce16a59481684c27d1b5a67ff15a43c7b0" + dependencies: + connected-domain "^1.0.0" + table@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" @@ -8830,7 +8930,7 @@ throttleit@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" -through2@^2.0.0: +through2@^2.0.0, through2@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" dependencies: