diff --git a/.gitignore b/.gitignore index 242dea65d..d1fa2159a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ npm-debug.log build dist docs +test/tmp .build .coverage .dist diff --git a/Cargo.precompiled.toml b/Cargo.precompiled.toml deleted file mode 100644 index d627a2c38..000000000 --- a/Cargo.precompiled.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -description = "Parity built-in dapps." -name = "parity-ui-precompiled" -version = "1.9.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[features] -default = ["with-syntex", "use-precompiled-js"] -use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"] -with-syntex = ["parity-dapps-glue/with-syntex"] - -[build-dependencies] -parity-dapps-glue = "1.9" - -[dependencies] -parity-dapps-glue = "1.9" - diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 440f2f2fb..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -description = "Parity built-in dapps." -name = "parity-ui-dev" -version = "1.9.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] -build = "build.rs" - -[features] -default = ["with-syntex"] -with-syntex = ["parity-dapps-glue/with-syntex"] - -[build-dependencies] -parity-dapps-glue = "1.9" - -[dependencies] -parity-dapps-glue = "1.9" - diff --git a/build.rs b/build.rs deleted file mode 100644 index a9a5ba8a6..000000000 --- a/build.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -extern crate parity_dapps_glue; - -fn main() { - parity_dapps_glue::js::build(env!("CARGO_MANIFEST_DIR"), "build"); - parity_dapps_glue::generate(); -} diff --git a/electron/index.js b/electron/index.js index 7e15c099b..14f61cb37 100644 --- a/electron/index.js +++ b/electron/index.js @@ -15,10 +15,9 @@ // along with Parity. If not, see . const electron = require('electron'); -const fs = require('fs'); const path = require('path'); const url = require('url'); -const util = require('util'); +const { ensureDir: fsEnsureDir } = require('fs-extra'); const addMenu = require('./menu'); const { cli } = require('./cli'); @@ -33,11 +32,16 @@ const { name: appName } = require('../package.json'); const { app, BrowserWindow, ipcMain, session } = electron; const { URL } = url; -const fsExists = util.promisify(fs.stat); // eslint-disable-line -const fsMkdir = util.promisify(fs.mkdir); - let mainWindow; +function runApp () { + doesParityExist() + .catch(() => fetchParity(mainWindow)) // Install parity if not present + .catch(handleError); // Errors should be handled before, this is really just in case + + return fsEnsureDir(getLocalDappsPath()).then(createWindow); +} + function createWindow () { // Will send these variables to renderers via IPC global.dirName = __dirname; @@ -46,18 +50,10 @@ function createWindow () { mainWindow = new BrowserWindow({ height: 800, - width: 1200 + width: 1200, + webPreferences: { nodeIntegrationInWorker: true } }); - const localDappsPath = getLocalDappsPath(); - - fsExists(localDappsPath) - .catch(() => fsMkdir(localDappsPath)); - - doesParityExist() - .catch(() => fetchParity(mainWindow)) // Install parity if not present - .catch(handleError); // Errors should be handled before, this is really just in case - if (cli.uiDev === true) { // Opens http://127.0.0.1:3000 in --ui-dev mode mainWindow.loadURL('http://127.0.0.1:3000'); @@ -88,22 +84,6 @@ function createWindow () { callback({ requestHeaders: details.requestHeaders }); }); - // Do not accept all kind of web permissions (camera, location...) - // https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content - session.defaultSession - .setPermissionRequestHandler((webContents, permission, callback) => { - if (!webContents.getURL().startsWith('file:')) { - // Denies the permissions request for all non-file://. Currently all - // network dapps are loaded on http://127.0.0.1:8545, so they won't - // have any permissions. - return callback(false); - } - - // All others loaded on file:// (shell, builtin, local) can have those - // permissions. - return callback(true); - }); - // Verify WebView Options Before Creation // https://electronjs.org/docs/tutorial/security#12-verify-webview-options-before-creation mainWindow.webContents.on('will-attach-webview', (event, webPreferences, params) => { @@ -120,6 +100,14 @@ function createWindow () { // Listen to the creation of (dapp) webviews to attach event listeners to them mainWindow.webContents.on('did-attach-webview', (event, webContents) => { + // Do not accept all kinds of web permissions (camera, location...) + // https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content + webContents.session + .setPermissionRequestHandler((webContents, permission, callback) => { + // Deny all permissions for dapps + return callback(false); + }); + let baseUrl; let appId; @@ -166,7 +154,7 @@ function createWindow () { }); } -app.on('ready', createWindow); +app.on('ready', runApp); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { diff --git a/electron/operations/runParity.js b/electron/operations/runParity.js index 53aa36aa9..9fec25b44 100644 --- a/electron/operations/runParity.js +++ b/electron/operations/runParity.js @@ -54,7 +54,7 @@ module.exports = { .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 + let logLastLine = ''; // Always contains last line of the logFile // Run an instance of parity with the correct args parity = spawn(parityPath(), parityArgv); diff --git a/package-lock.json b/package-lock.json index d5be6adc2..18f9a3a5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -211,6 +211,13 @@ "bignumber.js": "3.0.1", "js-sha3": "0.5.5", "utf8": "^2.1.2" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" + } } }, "@parity/api": { @@ -233,6 +240,11 @@ "websocket": "^1.0.25" }, "dependencies": { + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" + }, "store": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", @@ -447,6 +459,14 @@ "bignumber.js": "4.1.0", "js-sha3": "0.5.5", "utf8": "^2.1.2" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=", + "dev": true + } } }, "@parity/api": { @@ -468,6 +488,14 @@ "lodash": "^4.17.4", "store": "^2.0.12", "websocket": "^1.0.25" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=", + "dev": true + } } }, "@parity/ledger": { @@ -505,6 +533,50 @@ "yargs": "6.6.0" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, "mobx": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/mobx/-/mobx-2.6.4.tgz", @@ -520,6 +592,26 @@ "hoist-non-react-statics": "^1.2.0" } }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "react-redux": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.6.tgz", @@ -531,6 +623,106 @@ "lodash": "^4.2.0", "loose-envify": "^1.1.0" } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "solc": { + "version": "github:ngotchac/solc-js#04eb38cc3003fba8cb3656653a7941ed36408818", + "from": "github:ngotchac/solc-js", + "dev": true, + "requires": { + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "yargs": "^4.7.1" + }, + "dependencies": { + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "dev": true, + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } } } }, @@ -815,6 +1007,54 @@ "has-flag": "^2.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, "webpack": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.4.1.tgz", @@ -845,6 +1085,17 @@ "yargs": "^8.0.2" }, "dependencies": { + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -886,6 +1137,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, "yargs-parser": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", @@ -1238,6 +1495,12 @@ "intl-messageformat": "1.3.0" } }, + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=", + "dev": true + }, "keythereum": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/keythereum/-/keythereum-0.4.6.tgz", @@ -1557,6 +1820,16 @@ "integrity": "sha1-jgOPbdsUvXZa4fS1IW4SCUUR4NA=", "dev": true }, + "solc": { + "version": "github:ngotchac/solc-js#04eb38cc3003fba8cb3656653a7941ed36408818", + "from": "github:ngotchac/solc-js#04eb38cc3003fba8cb3656653a7941ed36408818", + "dev": true, + "requires": { + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "yargs": "^4.7.1" + } + }, "webrtc-adapter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-2.1.0.tgz", @@ -1594,6 +1867,28 @@ "schema-utils": "^0.3.0" } }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "dev": true, + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, "zxcvbn": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.1.tgz", @@ -1663,9 +1958,9 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } }, "semantic-ui-react": { @@ -1673,11 +1968,11 @@ "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.78.3.tgz", "integrity": "sha512-JRmuqjyigCehHfzrS2ir5nGoytZWCifU8G2T++G/CMdahUJBME7S6E9rU7WW9Qg2Fqn2aJIxfn6Ry/rlOTJDOw==", "requires": { - "babel-runtime": "6.26.0", - "classnames": "2.2.5", - "fbjs": "0.8.16", - "lodash": "4.17.10", - "prop-types": "15.6.1" + "babel-runtime": "^6.25.0", + "classnames": "^2.2.5", + "fbjs": "^0.8.16", + "lodash": "^4.17.4", + "prop-types": "^15.5.10" } } } @@ -1791,7 +2086,7 @@ "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-4.0.3.tgz", "integrity": "sha1-QOsx0l4LjWMEjtojdsxWv6vRT3U=", "requires": { - "hoist-non-react-statics": "1.2.0" + "hoist-non-react-statics": "^1.2.0" } }, "react-redux": { @@ -1799,10 +2094,10 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.6.tgz", "integrity": "sha1-S50ymFMHoRCWot1hVhmABE/MYgk=", "requires": { - "hoist-non-react-statics": "1.2.0", - "invariant": "2.2.2", - "lodash": "4.17.4", - "loose-envify": "1.3.1" + "hoist-non-react-statics": "^1.0.3", + "invariant": "^2.0.0", + "lodash": "^4.2.0", + "loose-envify": "^1.1.0" } }, "redux": { @@ -1810,18 +2105,18 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-3.6.0.tgz", "integrity": "sha1-iHwrPQub2G7KK+cFccJ2VMGeGI0=", "requires": { - "lodash": "4.17.4", - "lodash-es": "4.17.4", - "loose-envify": "1.3.1", - "symbol-observable": "1.1.0" + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.2" } } } }, "@parity/ui": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.1.7.tgz", - "integrity": "sha512-UqhsKVeaWBkufSwJH8S6HnOaLmQUoDqlu13o9MKfeFtNexcuPk457tRvpMn64Mn6GOLFPJC8Cf9HLD0Bpkpjqg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@parity/ui/-/ui-3.1.8.tgz", + "integrity": "sha512-izyK7nlzBVBi7sKJiDFJtNOcNzgepEX5x+V9+392+xodPw+lbN4n9zO9Lve8ba2eoywmWbyk3q0ZLsWgsLqSIg==", "requires": { "@parity/api": "~2.1.22", "@parity/etherscan": "2.1.x", @@ -1878,9 +2173,9 @@ } }, "@parity/api": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.22.tgz", - "integrity": "sha512-I7HfYQakHM1NrZ6LmKQN1BJe81YsnfkjUUv1ZDAbDFVwtgADlmsfchPeX5Cm8flGY2eHd2wlNmBu5+RZtftKxQ==", + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@parity/api/-/api-2.1.23.tgz", + "integrity": "sha512-Oeo0qvTWczpsMy8IeaN54Jz/JrKLNkIWB69eDE6KsouSnXEOGGh8pWup2LCZoaKhRX3HdXQFYCnWekvsDhst6Q==", "requires": { "@parity/abi": "~2.1.4", "@parity/jsonrpc": "2.1.x", @@ -1919,10 +2214,10 @@ "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.76.0.tgz", "integrity": "sha512-CdiIT8n7ZwUlytZkYsQMnaVGmoIZI/mVs4lijvLcR568kcnlRkYYaFKhMLq5tFDQU6+QhdTD+8WebF7ov0Ql6Q==", "requires": { - "babel-runtime": "6.26.0", - "classnames": "2.2.5", - "lodash": "4.17.4", - "prop-types": "15.5.10" + "babel-runtime": "^6.25.0", + "classnames": "^2.2.5", + "lodash": "^4.17.4", + "prop-types": "^15.5.10" } }, "store": { @@ -2856,7 +3151,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } } } @@ -3814,8 +4109,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base32.js": { "version": "0.1.0", @@ -3857,6 +4151,11 @@ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", "dev": true }, + "big-integer": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.31.tgz", + "integrity": "sha512-lDbZNHHwxDKnjP7LWg2leO+tjs4SyVs2Z83dsR1Idbe2urRnxZAUdeQ8YBhHaGaWK/4WM3mz+RlbZsgqck17CA==" + }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", @@ -3868,6 +4167,15 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-3.0.1.tgz", "integrity": "sha1-gHZS0Q453jfp40lyR+3HmLt0b3Y=" }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", @@ -4067,7 +4375,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4257,6 +4564,16 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, + "buffer-indexof-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", + "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, "buffer-to-vinyl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.1.0.tgz", @@ -4282,6 +4599,11 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" + }, "builder-util": { "version": "5.7.9", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-5.7.9.tgz", @@ -4427,6 +4749,35 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + } + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -4603,6 +4954,21 @@ "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz", "integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w=" }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "requires": { + "traverse": ">=0.3.0 <0.4" + }, + "dependencies": { + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" + } + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -5080,8 +5446,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.0", @@ -5176,6 +5541,20 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, "copy-to-clipboard": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", @@ -5951,6 +6330,12 @@ "array-find-index": "^1.0.1" } }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -7096,7 +7481,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, "requires": { "readable-stream": "^2.0.2" } @@ -7294,15 +7678,15 @@ "integrity": "sha512-SQYDGMLpTgty1bx3NycuDb7dNPzktVSdK2sqPZjyRocauq/uN/V4S2lcpFVLupaHhKlD8zozm9fTpm5UdohvTg==", "dev": true, "requires": { - "debug": "3.1.0", - "env-paths": "1.0.0", - "fs-extra": "4.0.3", - "minimist": "1.2.0", - "nugget": "2.0.1", - "path-exists": "3.0.0", - "rc": "1.2.2", - "semver": "5.4.1", - "sumchecker": "2.0.2" + "debug": "^3.0.0", + "env-paths": "^1.0.0", + "fs-extra": "^4.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.1", + "path-exists": "^3.0.0", + "rc": "^1.2.1", + "semver": "^5.4.1", + "sumchecker": "^2.0.2" } }, "find-up": { @@ -7311,7 +7695,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "fs-extra": { @@ -7320,9 +7704,9 @@ "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "has-flag": { @@ -7343,7 +7727,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "minimist": { @@ -7358,9 +7742,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "path-exists": { @@ -7375,8 +7759,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -7385,7 +7769,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "sumchecker": { @@ -7394,7 +7778,7 @@ "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", "dev": true, "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" }, "dependencies": { "debug": { @@ -7447,18 +7831,18 @@ "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" } }, "yargs-parser": { @@ -7467,7 +7851,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -8556,7 +8940,7 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "1.1.2" + "find-up": "^1.0.0" } } } @@ -9941,10 +10325,20 @@ "integrity": "sha1-Bq1/4Z3dsQQiZEOAZKKjL+4SuHI=", "dev": true }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, "follow-redirects": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", - "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz", + "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", "requires": { "debug": "^3.1.0" }, @@ -10038,14 +10432,23 @@ "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=", "dev": true }, - "fs-extra": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", - "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", "requires": { "graceful-fs": "^4.1.2", - "jsonfile": "^3.0.0", + "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, @@ -10087,11 +10490,22 @@ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", "dev": true }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.1.2", @@ -10996,7 +11410,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -11129,7 +11542,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -12121,6 +12533,12 @@ "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", "dev": true }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, "ignore": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", @@ -12191,7 +12609,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -13424,9 +13841,9 @@ "dev": true }, "js-sha3": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", - "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.7.0.tgz", + "integrity": "sha512-Wpks3yBDm0UcL5qlVhwW9Jr9n9i4FfeWBFOOXP5puDS/SiudJGhw7DPyBqn3487qD4F0lsC0q3zxink37f7zeA==" }, "js-tokens": { "version": "3.0.2", @@ -13549,10 +13966,9 @@ "dev": true }, "jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "requires": { "graceful-fs": "^4.1.6" } @@ -13864,6 +14280,11 @@ "immediate": "~3.0.5" } }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" + }, "load-bmfont": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.3.0.tgz", @@ -14638,7 +15059,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -14658,6 +15078,46 @@ "is-plain-obj": "^1.1.0" } }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + } + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -14801,9 +15261,23 @@ "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=" }, "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } }, "ms": { "version": "2.0.0", @@ -15596,6 +16070,17 @@ "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", "dev": true }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -16514,6 +16999,48 @@ "has-flag": "^2.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, "url-loader": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", @@ -16553,6 +17080,19 @@ "watchpack": "^1.4.0", "webpack-sources": "^1.0.1", "yargs": "^8.0.2" + }, + "dependencies": { + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + } } }, "webpack-dev-server": { @@ -16748,6 +17288,18 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -16931,8 +17483,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -18689,6 +19240,12 @@ "asap": "~2.0.3" } }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, "promise-worker": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/promise-worker/-/promise-worker-1.1.1.tgz", @@ -18758,6 +19315,29 @@ "once": "^1.3.1" } }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -19267,9 +19847,9 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", "requires": { - "fbjs": "0.8.16", - "loose-envify": "1.3.1", - "object-assign": "4.1.1" + "fbjs": "^0.8.16", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" } } } @@ -19712,6 +20292,17 @@ "locate-path": "^2.0.0" } }, + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -19802,6 +20393,15 @@ "esprima": "^4.0.0" } }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsx-ast-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", @@ -19992,6 +20592,48 @@ "has-flag": "^1.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, "url-loader": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.6.2.tgz", @@ -20047,6 +20689,17 @@ "requires": { "has-flag": "^2.0.0" } + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } } } }, @@ -20258,6 +20911,18 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -21116,7 +21781,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, "requires": { "glob": "^7.0.5" } @@ -21162,6 +21826,15 @@ "is-promise": "^2.1.0" } }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, "rx-lite": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", @@ -21428,6 +22101,12 @@ "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=", "dev": true }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -21938,6 +22617,15 @@ "tweetnacl": "~0.14.0" } }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, "stackframe": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", @@ -21996,6 +22684,16 @@ "readable-stream": "^2.0.2" } }, + "stream-each": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, "stream-http": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", @@ -23759,66 +24457,96 @@ "dev": true }, "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz", + "integrity": "sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA==", "dev": true, "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" }, "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" } }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "schema-utils": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", + "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", "dev": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + } }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" + "punycode": "^2.1.0" } } } @@ -23892,6 +24620,24 @@ "integrity": "sha1-khD5vcqsxeHjkpSQ18AZ35bxhxI=", "dev": true }, + "unique-filename": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", + "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", + "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, "unique-stream": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", @@ -23958,8 +24704,7 @@ "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" }, "unpipe": { "version": "1.0.0", @@ -23988,6 +24733,48 @@ "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=" }, + "unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "update-notifier": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.3.0.tgz", @@ -24590,12 +25377,77 @@ "has-flag": "^2.0.0" } }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -25040,7 +25892,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "1.0.1" + "prr": "~1.0.1" } }, "prr": { diff --git a/package.json b/package.json index 2f88e4290..325e64db4 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "stylelint-config-standard": "16.0.0", "to-source": "2.0.3", "uglify-js": "2.8.22", + "uglifyjs-webpack-plugin": "1.2.7", "url-loader": "0.5.7", "webpack": "3.5.5", "webpack-bundle-size-analyzer": "2.5.0", @@ -164,12 +165,15 @@ "@parity/plugin-signer-hardware": "github:parity-js/plugin-signer-hardware#e8b8a4e67adc37870e370d22805632d08012b9ee", "@parity/plugin-signer-qr": "github:parity-js/plugin-signer-qr#c275ba13524e9f6759079fabd10faf49cc00cfc0", "@parity/shared": "2.2.28", - "@parity/ui": "3.1.7", + "@parity/ui": "3.1.8", "axios": "0.18.0", "command-exists": "1.2.2", "commander": "2.15.1", "electron-dl": "1.11.0", + "follow-redirects": "1.5.0", + "fs-extra": "6.0.1", "is-electron": "2.1.0", + "js-sha3": "0.7.0", "keythereum": "1.0.2", "lodash.flatten": "4.4.0", "lodash.omitby": "4.6.0", @@ -194,6 +198,7 @@ "semantic-ui-react": "0.77.0", "solc": "ngotchac/solc-js", "store": "1.3.20", + "unzipper": "0.8.14", "util.promisify": "1.0.0" } } diff --git a/scripts/build.sh b/scripts/build.sh index a6f4a913c..0b6adba78 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -11,11 +11,6 @@ rm -rf $BUILDDIR mkdir -p $BUILDDIR/src BUILD_DEST=$BUILDDIR/build npm run ci:build || EXITCODE=1 -# Copy rust files -cp Cargo.precompiled.toml $BUILDDIR/Cargo.toml -cp build.rs $BUILDDIR -cp src/lib.rs* $BUILDDIR/src - # back to root popd diff --git a/src/Connection/connection.js b/src/Connection/connection.js index 563065c40..05b81b314 100644 --- a/src/Connection/connection.js +++ b/src/Connection/connection.js @@ -33,7 +33,7 @@ import styles from './connection.css'; let electron; if (isElectron()) { - electron = window.require('electron'); + electron = require('electron'); } @observer diff --git a/src/Dapp/dapp.css b/src/Dapp/dapp.css index 3cd0684d4..a9cb7dda7 100644 --- a/src/Dapp/dapp.css +++ b/src/Dapp/dapp.css @@ -29,6 +29,29 @@ .full { font-family: 'Roboto', sans-serif; - font-size: 16px; + font-size: 2em; font-weight: 300; + display: flex; + align-items: center; + justify-content: center; + color: #999; } + +.loading { + opacity: 0; + animation-duration: 0.5s; + animation-name: fadeIn; + animation-delay: 1.5s; + animation-fill-mode: forwards; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + diff --git a/src/Dapp/dapp.js b/src/Dapp/dapp.js index 9b796b803..084952c2b 100644 --- a/src/Dapp/dapp.js +++ b/src/Dapp/dapp.js @@ -149,39 +149,36 @@ export default class Dapp extends Component { } render () { - const { dappsUrl } = this.context.api; const { params } = this.props; const { app, loading } = this.state; if (loading) { - return null; + return ( +
+

+ +

+
+ ); } if (!app) { return (
-
+

-

+

); } - let src = null; - - switch (app.type) { - case 'local': - case 'builtin': - src = `${app.localUrl}?appId=${app.id}`; - break; - - case 'network': - src = `${dappsUrl}/${app.contentHash}/`; - break; - } + let src = `${app.localUrl}?appId=${app.id}`; let hash = ''; diff --git a/src/DappRequests/methodGroups.js b/src/DappRequests/methodGroups.js index efcf6a227..68c1a01bb 100644 --- a/src/DappRequests/methodGroups.js +++ b/src/DappRequests/methodGroups.js @@ -22,8 +22,6 @@ const methodGroups = { }, dapps: { methods: [ - 'parity_dappsRefresh', - 'parity_dappsUrl', 'shell_getApps', 'shell_getMethodPermissions' ] diff --git a/src/Dapps/store.js b/src/Dapps/store.js index c309e0e51..f65e3157e 100644 --- a/src/Dapps/store.js +++ b/src/Dapps/store.js @@ -20,6 +20,7 @@ import store from 'store'; import Contracts from '@parity/shared/lib/contracts'; import { fetchBuiltinApps, fetchLocalApps, fetchRegistryAppIds, fetchRegistryApp, subscribeToChanges } from '../util/dapps'; +import HashFetch from '../util/hashFetch'; const LS_KEY_DISPLAY = 'displayApps'; const LS_KEY_EXTERNAL_ACCEPT = 'acceptExternal'; @@ -123,6 +124,17 @@ export default class DappsStore extends EventEmitter { return this.fetchRegistryApp(dappReg, id, true); }) + .then((app) => { + if (app.type === 'network') { + return HashFetch.get().fetch(this._api, app.contentHash, 'dapp') + .then(appPath => { + app.localUrl = `file://${appPath}/index.html`; + return app; + }) + .catch(e => { console.error(`Error loading dapp ${id}`, e); }); + } + return app; + }) .then((app) => { this.emit('loaded', app); return app; @@ -240,7 +252,9 @@ export default class DappsStore extends EventEmitter { }); return Promise.all(promises); - }); + }) + .then(apps => + apps.filter(app => app)); } @action refreshDapps = () => { diff --git a/src/index.parity.js b/src/index.parity.js index c3f96a4c9..73bd0b5e5 100644 --- a/src/index.parity.js +++ b/src/index.parity.js @@ -60,7 +60,7 @@ function renderUI (token) { if (isElectron()) { // Take --flag options from electron if available - const { remote } = window.require('electron'); + const { remote } = require('electron'); wsUrl = `${remote.getGlobal('wsInterface') || '127.0.0.1'}:${remote.getGlobal('wsPort') || '8546'}`; } diff --git a/src/inject.js b/src/inject.js index e12d8ac78..843d0a0c2 100644 --- a/src/inject.js +++ b/src/inject.js @@ -18,19 +18,15 @@ import Api from '@parity/api'; import qs from 'query-string'; function getAppId () { - // Local dapps: file:///home/username/.config/Parity-UI/dapps/mydapp/index.html?appId=LOCAL-dapp-name + // Local dapps: file:///home/username/.config/parity-ui/dapps/mydapp/index.html?appId=LOCAL-dapp-name // Local dapps served in development mode on a dedicated port: http://localhost:3001/?appId=LOCAL-dapp-name // Built-in dapps: file://path-to-shell/.build/dapps/0x0587.../index.html?appId=dapp-name // Built-in dapps when running Electron in dev mode: http://127.0.0.1:3000/dapps/v1/index.html?appId=dapp-name + // Network dapps: file:///home/username/.config/parity-ui/hashfetch/files/0x8075.../index.html?appId=dapp-name const fromQuery = qs.parse(window.location.search).appId; if (fromQuery) { return fromQuery; } - // Dapps installed from the registry and served by Parity: http://127.0.0.1:8545/ff19... - const [hash] = window.location.pathname.match(/(0x)?[a-f0-9]{64}/i) || []; - - if (hash) { return hash; } - console.error('Could not find appId'); } @@ -84,13 +80,10 @@ if (typeof window !== 'undefined' && !window.isParity) { // Disable eval() for dapps // https://electronjs.org/docs/tutorial/security#7-override-and-disable-eval // - // TODO Currently Web3 Console dapp needs eval(), and is the only builtin - // that needs it, so we cannot blindly disable it as per the recommendation. + // TODO Currently Web3 Console dapp needs eval(), so we cannot blindly disable + // it as per the recommendation. For now we simply allow eval() for all dapps. // One idea is to check here in inject.js if allowJsEval is set to true, but // this requires more work (future PR). - // For now we simply allow eval(). All builtin dapps are trusted and can use - // eval(), and all network dapps are served on 127.0.0.1:8545, which have CSP - // that disallow eval(). So security-wise it should be enough. // // window.eval = global.eval = function () { // eslint-disable-line // throw new Error(`Sorry, this app does not support window.eval().`); diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 04efa7cc5..000000000 --- a/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -#[cfg(feature = "with-syntex")] -include!(concat!(env!("OUT_DIR"), "/lib.rs")); - -#[cfg(not(feature = "with-syntex"))] -include!("lib.rs.in"); - diff --git a/src/lib.rs.in b/src/lib.rs.in deleted file mode 100644 index b811c1066..000000000 --- a/src/lib.rs.in +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -extern crate parity_dapps_glue; - -use std::collections::HashMap; -use parity_dapps_glue::{WebApp, File, Info}; - -#[derive(WebAppFiles)] -#[webapp(path = "../build")] -pub struct App { - pub files: HashMap<&'static str, File>, -} - -impl Default for App { - fn default() -> App { - App { - files: Self::files(), - } - } -} - -impl WebApp for App { - fn file(&self, path: &str) -> Option<&File> { - self.files.get(path) - } - - fn info(&self) -> Info { - Info { - name: "Parity UI", - version: env!("CARGO_PKG_VERSION"), - author: "Parity ", - description: "New UI for Parity.", - icon_url: "icon.png", - } - } -} - -#[test] -fn test_js() { - parity_dapps_glue::js::build(env!("CARGO_MANIFEST_DIR"), "build"); -} diff --git a/src/secureApi.js b/src/secureApi.js index d9b20883b..2b20ddee8 100644 --- a/src/secureApi.js +++ b/src/secureApi.js @@ -29,7 +29,6 @@ export default class SecureApi extends Api { _needsToken = false; _tokens = []; - _dappsUrl = null; _wsUrl = null; static getWsProvider (url, protocol, sysuiToken) { @@ -73,44 +72,7 @@ export default class SecureApi extends Api { wsProvider.on('close', this.connect, this); this - .connect() // Connect with token - .then(() => this._fetchSettings()); // Update this._dappsUrl - } - - get _dappsAddress () { - if (!this._dappsUrl) { - return { - host: null, - port: 8545 - }; - } - - const [host, port] = this._dappsUrl.split(':'); - - return { - host, - port: port ? parseInt(port, 10) : null - }; - } - - get dappsPort () { - return this._dappsAddress.port; - } - - get dappsUrl () { - const { port } = this._dappsAddress; - - return port - ? `${this.protocol()}//${this.hostname}:${port}` - : `${this.protocol()}//${this.hostname}`; - } - - get hostname () { - if (window.location.hostname === 'home.parity') { - return 'dapps.parity'; - } - - return this._dappsAddress.host || '127.0.0.1'; + .connect(); // Connect with token } get isConnecting () { @@ -322,18 +284,6 @@ export default class SecureApi extends Api { }); } - /** - * Retrieve the correct ports from the Node - */ - _fetchSettings () { - return this.parity - .dappsUrl() - .catch(() => null) - .then((dappsUrl) => { - this._dappsUrl = this._resolveHost(dappsUrl); - }); - } - /** * Try to generate an Authorization Token. * Then try to connect with the new token. diff --git a/src/util/dapps.js b/src/util/dapps.js index bcebbbacb..9b7d34d5e 100644 --- a/src/util/dapps.js +++ b/src/util/dapps.js @@ -15,23 +15,22 @@ // along with Parity. If not, see . import BigNumber from 'bignumber.js'; -import { pick, range, uniq } from 'lodash'; +import { range, uniq } from 'lodash'; import { bytesToHex } from '@parity/api/lib/util/format'; -import { IconCache } from '@parity/ui'; import { getBuildPath, getLocalDappsPath } from './host'; -import isElectron from 'is-electron'; +import HashFetch from './hashFetch'; import builtinApps from '../Dapps/dappsBuiltin.json'; import Contracts from '@parity/shared/lib/contracts'; import path from 'path'; -const util = isElectron() ? window.require('util') : require('util'); +const util = require('util'); require('util.promisify').shim(); -const fs = isElectron() ? window.require('fs') : require('fs'); +const fs = require('fs'); const fsReadFile = util.promisify(fs.readFile); const fsReaddir = util.promisify(fs.readdir); const fsStat = util.promisify(fs.stat); @@ -200,6 +199,7 @@ export function fetchRegistryAppIds (api) { }); } +// Returns undefined if dapp is invalid export function fetchRegistryApp (api, dappReg, appId) { return Promise .all([ @@ -208,63 +208,50 @@ export function fetchRegistryApp (api, dappReg, appId) { dappReg.getManifest(appId) ]) .then(([imageId, contentId, manifestId]) => { - const app = { - id: appId, - image: IconCache.hashToImage(imageId), - contentHash: bytesToHex(contentId).substr(2), - manifestHash: bytesToHex(manifestId).substr(2), - type: 'network', - visible: true - }; - - return fetchManifest(api, app.manifestHash) - .then((manifest) => { - if (manifest) { - app.manifestHash = null; - - // Add usefull manifest fields to app - Object.assign(app, pick(manifest, ['author', 'description', 'name', 'version'])); - } - - return app; - }); - }) - .then((app) => { - // Keep dapps that has a Manifest File and an Id - const dapp = (app.manifestHash || !app.id) ? null : app; - - return dapp; - }) - .catch((error) => { - console.warn('DappsStore:fetchRegistryApp', error); - }); -} - -export function fetchManifest (api, manifestHash) { - if (/^(0x)?0+/.test(manifestHash)) { - return Promise.resolve(null); - } - - return api.parity - .dappsUrl() - .then(dappsUrl => { - const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:'; + const imageHash = bytesToHex(imageId).substr(2); + const contentHash = bytesToHex(contentId).substr(2); + const manifestHash = bytesToHex(manifestId).substr(2); + + return Promise.all([ + HashFetch.get().fetch(api, imageHash, 'file').catch((e) => { console.warn(`Couldn't download icon for dapp ${appId}`, e); }), + HashFetch.get().fetch(api, manifestHash, 'file').catch((e) => { throw new Error(`Couldn't download manifest ${e.toString()}`); }) + ]).then(([imagePath, manifestPath]) => + fsReadFile(manifestPath) + .then(manifestJson => { + try { + const manifest = JSON.parse(manifestJson); + + if (!manifest.id) { + throw new Error(`Missing app id in manifest.json ${manifest}`); + } - return fetch( - `${protocol}//${dappsUrl}/api/content/${manifestHash}/`, - { redirect: 'follow', mode: 'cors' } + return manifest; + } catch (e) { + throw new Error(`Couldn't parse manifest.json ${e}`); + } + }) + .catch(e => { + throw new Error(`Couldn't read manifest.json file locally (${manifestPath}) ${e}`); + }) + .then(manifest => { + const { author, description, name, version } = manifest; + const app = { + id: appId, + type: 'network', + author, + description, + name: name || '', + version, + visible: true, // Display by default + image: `file://${imagePath}`, + contentHash + }; + + return app; + }) ); }) - .then((response) => { - return response.ok - ? response.json() - : null; - }) - .then((manifest) => { - return manifest; - }) - .catch((error) => { - console.warn('DappsStore:fetchManifest', error); - return null; - }); + .catch((error) => { + console.warn('DappsStore:fetchRegistryApp', appId, error); + }); } diff --git a/src/util/hashFetch/expoRetry.js b/src/util/hashFetch/expoRetry.js new file mode 100644 index 000000000..7fa453aaa --- /dev/null +++ b/src/util/hashFetch/expoRetry.js @@ -0,0 +1,111 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +const path = require('path'); + +import { readJson as fsReadJson, writeJson as fsWriteJson } from 'fs-extra'; + +import { getHashFetchPath } from '../host'; + +// Handle retries with exponential delay for failed download attempts +export default class ExpoRetry { + static instance = null; + + // We store the URL to allow GH Hint URL updates for a given hash + // to take effect immediately, and to disregard past failed attempts at + // getting this file from other URLs. + // { + // [`${hash}:${url}`]: { + // attempts: [ + // {timestamp: 1529598899017}, + // ... + // ] + // }, + // ... + // } + failHistory = {}; + + // If true, the failHistory var was updated and needs to be written to disk + needWrite = false; + writeQueue = Promise.resolve(); + + static get () { + if (!ExpoRetry.instance) { + ExpoRetry.instance = new ExpoRetry(); + } + + return ExpoRetry.instance; + } + + _getFilePath () { + return path.join(getHashFetchPath(), 'fail_history.json'); + } + + _getKey (hash, url) { + return `${hash}:${url}`; + } + + load () { + const filePath = this._getFilePath(); + + return fsReadJson(filePath) + .then(failHistory => { + this.failHistory = failHistory; + }) + .catch(() => fsWriteJson(filePath, this.failHistory)); + } + + canAttemptDownload (hash, url) { + const key = this._getKey(hash, url); + + // Never tried downloading the file + if (!(key in this.failHistory) || !this.failHistory[key].attempts.length) { + return true; + } + + // Already failed at downloading the file: check if we can retry now + // Delay starts at 30 seconds, max delay is 23 days + const retriesCount = this.failHistory[key].attempts.length - 1; + const latestAttemptTimestamp = this.failHistory[key].attempts.slice(-1)[0].timestamp; + const earliestNextAttemptTimestamp = latestAttemptTimestamp + Math.pow(2, Math.min(16, retriesCount)) * 30000; + + if (Date.now() > earliestNextAttemptTimestamp) { + return true; + } + + return false; + } + + registerFailedAttempt (hash, url) { + const key = this._getKey(hash, url); + + this.failHistory[key] = this.failHistory[key] || { attempts: [] }; + this.failHistory[key].attempts.push({ timestamp: Date.now() }); + + // Once the ongoing write is finished, write anew with the updated contents + this.needWrite = true; + this.writeQueue = this.writeQueue.then(() => { + if (this.needWrite) { + // Skip subsequent promises, considering we are writing the latest value + this.needWrite = false; + + return fsWriteJson(this._getFilePath(), this.failHistory); + } + }).catch(() => { + console.error(`Couldn't write to ${this._getFilePath()}`); + }); + } +} diff --git a/src/util/hashFetch/index.js b/src/util/hashFetch/index.js new file mode 100644 index 000000000..3d88b1846 --- /dev/null +++ b/src/util/hashFetch/index.js @@ -0,0 +1,281 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +const fs = require('fs'); +const util = require('util'); +const path = require('path'); + +import unzip from 'unzipper'; + +import { getHashFetchPath } from '../host'; +import ExpoRetry from './expoRetry'; +import Contracts from '@parity/shared/lib/contracts'; +import { bytesToHex } from '@parity/api/lib/util/format'; +import { ensureDir as fsEnsureDir, emptyDir as fsEmptyDir, move, remove } from 'fs-extra'; +import { http, https } from 'follow-redirects'; +import { keccak256 } from 'js-sha3'; + +const fsExists = util.promisify(fs.stat); +const fsStat = util.promisify(fs.stat); +const fsReaddir = util.promisify(fs.readdir); + +const MAX_DOWNLOADED_FILE_SIZE_BYTES = 10485760; // 20MB + +function registerFailedAttemptAndThrow (hash, url, e) { + ExpoRetry.get().registerFailedAttempt(hash, url); + throw e; +} + +function checkHashMatch (expectedHash, path) { + return new Promise((resolve, reject) => { + const hasher = keccak256.create(); + const fileReadStream = fs.createReadStream(path); + + fileReadStream.on('end', () => { + const actualHash = hasher.hex(); + + if (actualHash !== expectedHash) { + reject(`Hashes don't match: expected ${expectedHash}, got ${actualHash}`); + } else { + resolve(); + } + }); + + fileReadStream.on('data', (chunk) => { + hasher.update(chunk); + }); + }); +} + +// List directory contents and tell if each item is a file or a directory +function ls (folderPath) { + return fsReaddir(folderPath) + .then(filenames => + Promise.all(filenames.map(filename => { + const filePath = path.join(folderPath, filename); + + return fsStat(filePath).then(stat => ({ isDirectory: stat.isDirectory(), filePath, filename })); + })) + ); +} + +function rawUnzipTo (zipPath, extractPath) { + return new Promise((resolve, reject) => { + const unzipParser = unzip.Extract({ path: extractPath }); + + fs.createReadStream(zipPath).pipe(unzipParser); + + unzipParser.on('error', reject); + + unzipParser.on('close', resolve); + }); +} + +function unzipThroughTo (tempPath, extractPath, finalPath) { + return rawUnzipTo(tempPath, extractPath) + .then(() => remove(tempPath)) + .then(() => ls(extractPath)) + .then(files => { + // Check if the zip file contained a root folder + if (files.length === 1 && files[0].isDirectory) { + // Rename the root folder (contaning the dapp) to finalPath + const rootFolderPath = files[0].filePath; + + return move(rootFolderPath, finalPath) + .then(() => remove(extractPath)); + } else { + // No root folder: extractPath contains the dapp + return move(extractPath, finalPath); + } + }); +} + +function download (url, destinationPath) { + return new Promise((resolve, reject) => { + url = url.toLowerCase(); + + let httpx; + + if (url.startsWith('https:')) { + httpx = https; + } else if (url.startsWith('http:')) { + httpx = http; + } else { + reject(`Aborted attempt to download non-HTTP/HTTPS URL ${url}`); + return; + } + + const file = fs.createWriteStream(destinationPath); // Will replace any existing file + + httpx.get(url, response => { + var size = 0; + + response.on('data', (data) => { + size += data.length; + + if (size > MAX_DOWNLOADED_FILE_SIZE_BYTES) { + response.destroy(); + response.unpipe(file); + remove(destinationPath); + reject(`File download aborted: exceeded maximum size of ${MAX_DOWNLOADED_FILE_SIZE_BYTES} bytes`); + } + }); + + response.on('error', (e) => { + reject(`File download failed: ${e}`); + }); + + response.pipe(file); + + file.on('finish', () => { + file.close(resolve); + }); + }); + }); +} + +function hashDownload (hash, url, zip = false) { + const tempFilename = `${hash}.part`; + const tempPath = path.join(getHashFetchPath(), 'partial', tempFilename); + + const finalPath = path.join(getHashFetchPath(), 'files', hash); + + return download(url, tempPath) + .then(() => checkHashMatch(hash, tempPath)) + // @TODO Don't register a failed attempt if the download failed becuse the user was offline. + .catch(e => registerFailedAttemptAndThrow(hash, url, e)) + .then(() => { // Hashes match + if (!zip) { + return move(tempPath, finalPath); + } else { + const extractPath = path.join(getHashFetchPath(), 'partial-extract', tempFilename); + + return unzipThroughTo(tempPath, extractPath, finalPath); + } + }); +} + +function queryRegistryAndDownload (api, hash, expected) { + const { githubHint } = Contracts.get(api); + + return githubHint.getEntry(`0x${hash}`).then(([slug, commitBytes, author]) => { + const commit = bytesToHex(commitBytes); + + if (!slug) { + if (commit === '0x0000000000000000000000000000000000000000' && author === '0x0000000000000000000000000000000000000000') { + throw new Error(`No GitHub Hint entry found.`); + } else { + throw new Error(`GitHub Hint entry has empty slug.`); + } + } + + let url; + let zip; + + if (commit === '0x0000000000000000000000000000000000000000') { // The slug is the URL to a file + if (!slug.toLowerCase().startsWith('http')) { throw new Error(`GitHub Hint URL ${slug} isn't HTTP/HTTPS.`); } + url = slug; + zip = false; + } else if (commit === '0x0000000000000000000000000000000000000001') { // The slug is the URL to a dapp zip file + if (!slug.toLowerCase().startsWith('http')) { throw new Error(`GitHub Hint URL ${slug} isn't HTTP/HTTPS.`); } + url = slug; + zip = true; + } else { // The slug is the `owner/repo` of a dapp stored in GitHub + url = `https://codeload.github.com/${slug}/zip/${commit.substr(2)}`; + zip = true; + } + + if (ExpoRetry.get().canAttemptDownload(hash, url) === false) { + throw new Error(`Previous attempt at downloading ${hash} from ${url} failed; retry delay time not yet elapsed.`); + } + + return hashDownload(hash, url, zip); + }); +} + +function checkExpectedMatch (hash, filePath, expected) { + return fsStat(filePath).then(stat => { + if (stat.isDirectory() && expected === 'file') { + throw new Error(`Expected ${hash} to be a file; got a folder (dapp).`); + } else if (!stat.isDirectory() && expected === 'dapp') { + throw new Error(`Expected ${hash} to be a dapp; got a file.`); + } + }); +} + +// Download a file or download and extract a dapp zip archive, using GitHub Hint +// and the content hash of the file +export default class HashFetch { + static instance = null; + initialize = null; // Initialization promise + promises = {}; // Unsettled or resolved HashFetch#fetch promises only + + static get () { + if (!HashFetch.instance) { + HashFetch.instance = new HashFetch(); + } + + return HashFetch.instance; + } + + constructor () { + this.initialize = this._initialize(); + } + + _initialize () { + const hashFetchPath = getHashFetchPath(); + + return fsEnsureDir(hashFetchPath) + .then(() => + Promise.all([ + fsEnsureDir(path.join(hashFetchPath, 'files')), + fsEmptyDir(path.join(hashFetchPath, 'partial')), + fsEmptyDir(path.join(hashFetchPath, 'partial-extract')), + ExpoRetry.get().load() + ])) + .catch(e => { + throw new Error(`HashFetch initialization failed ${e}`); + }); + } + + /** + * Download a file or dapp based on its contents hash; + * returns a promise of the path to the downloaded file or directory. + * + * @param {String} api + * @param {String} hash - Keccak256 hexadecimal hash of the file without 0x + * @param {String} expected - Either 'file' or 'dapp' + * @return {Promise} A Promise that resolves with the path to the file or directory + */ + fetch (api, hash, expected) { + hash = hash.toLowerCase(); + + if (!/^[0-9a-z]{64}$/.test(hash)) { return Promise.reject(`${hash} isn't a valid hash.`); } + + return this.initialize.then(() => { + const filePath = path.join(getHashFetchPath(), 'files', hash); + + if (!(hash in this.promises)) { // There is no ongoing or resolved fetch for this hash + this.promises[hash] = fsExists(filePath) + .catch(() => queryRegistryAndDownload(api, hash, expected)) + .then(() => checkExpectedMatch(hash, filePath, expected)) + .then(() => filePath) + .catch(e => { delete this.promises[hash]; throw e; }); // Don't prevent retries if the fetch failed + } + return this.promises[hash]; + }); + } +} diff --git a/src/util/host.js b/src/util/host.js index b6accd5d2..bab0e1e5e 100644 --- a/src/util/host.js +++ b/src/util/host.js @@ -45,7 +45,7 @@ export function redirectLocalhost (token) { export function getBuildPath () { // Condition necessary for store.spec.js const basePath = isElectron() - ? window.require('electron').remote.getGlobal('dirName') + ? require('electron').remote.getGlobal('dirName') : path.join(__dirname, '..'); // Replace all backslashes by front-slashes (happens in Windows) @@ -62,11 +62,20 @@ export function getBuildPath () { return buildPath; } +export function getHashFetchPath () { + // Condition necessary for store.spec.js + const userData = isElectron() + ? require('electron').remote.app.getPath('userData') + : path.join(__dirname, '../../test/tmp'); + + return path.join(userData, 'hashfetch'); +} + export function getLocalDappsPath () { // Condition necessary for store.spec.js const userData = isElectron() - ? window.require('electron').remote.app.getPath('userData') - : path.join(__dirname, 'dapps'); + ? require('electron').remote.app.getPath('userData') + : path.join(__dirname, '../../test/tmp'); return path.join(userData, 'dapps'); } diff --git a/wasm/README.md b/wasm/README.md deleted file mode 100644 index 46a01b180..000000000 --- a/wasm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# WASM modules - -- `ethkey` -> `/js/packages/api/local/ethkey/ethkey.wasm` diff --git a/wasm/ethkey/.gitignore b/wasm/ethkey/.gitignore deleted file mode 100644 index a9d37c560..000000000 --- a/wasm/ethkey/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/wasm/ethkey/Cargo.toml b/wasm/ethkey/Cargo.toml deleted file mode 100644 index 22e5bf553..000000000 --- a/wasm/ethkey/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -description = "Parity ethkey WASM module." -name = "parity-ethkey-wasm" -version = "1.9.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[workspace] -members = [] - -[dependencies] -tiny-keccak = "1.3" -tiny-secp256k1 = "0.1" -libc = { version = "0.2.14", default-features = false } - -[profile.release] -panic = "abort" diff --git a/wasm/ethkey/base64ify.js b/wasm/ethkey/base64ify.js deleted file mode 100644 index 853d05257..000000000 --- a/wasm/ethkey/base64ify.js +++ /dev/null @@ -1,5 +0,0 @@ -const fs = require('fs'); - -const file = fs.readFileSync('./ethkey.opt.wasm', { encoding: 'base64' }); - -fs.writeFileSync('../../packages/api/local/ethkey/ethkey.wasm.js', `module.exports = new Buffer('${file}', 'base64');\n`); diff --git a/wasm/ethkey/build.sh b/wasm/ethkey/build.sh deleted file mode 100755 index 5a3035eba..000000000 --- a/wasm/ethkey/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -# Remove previous build to avoid name conflicts -rm -rf target/wasm32-unknown-emscripten/* - -# Build using nightly rustc + emscripten -rustup run nightly cargo build --release --target=wasm32-unknown-emscripten - -# Copy final WASM file over -cp ./target/wasm32-unknown-emscripten/release/deps/parity_ethkey_wasm-*.wasm ./ethkey.wasm - -# Create a Base64-encoded JS version of the wasm file for easy inclusion in Webpack -node base64ify - -# Copy Base64-encoded JS version to src -cp ./ethkey.wasm.js ../../packages/api/local/ethkey/ethkey.wasm.js - -# rm -f ./ethkey.wasm ./ethkey.opt.wasm ./ethkey.wasm.js diff --git a/wasm/ethkey/ethkey.opt.wasm b/wasm/ethkey/ethkey.opt.wasm deleted file mode 100644 index 1cbbd4a65..000000000 Binary files a/wasm/ethkey/ethkey.opt.wasm and /dev/null differ diff --git a/wasm/ethkey/ethkey.wasm b/wasm/ethkey/ethkey.wasm deleted file mode 100644 index 80f8758d3..000000000 Binary files a/wasm/ethkey/ethkey.wasm and /dev/null differ diff --git a/wasm/ethkey/src/main.rs b/wasm/ethkey/src/main.rs deleted file mode 100644 index 01ecd0b40..000000000 --- a/wasm/ethkey/src/main.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -#![feature(lang_items, core_intrinsics)] -#![feature(start)] -#![feature(link_args)] -#![no_std] -use core::intrinsics; - -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; -extern crate tiny_keccak; -extern crate tiny_secp256k1; - -use tiny_secp256k1::{is_valid_secret, create_public_key, ECPointG}; - -// #[link_args = "-s EXPORTED_FUNCTIONS=['_input_ptr','_secret_ptr','_public_ptr','_address_ptr','_ecpointg','_verify_secret','_brain']"] -// extern {} - -use tiny_keccak::Keccak; - -pub trait Keccak256 { - fn keccak256(&self) -> T; -} - -impl Keccak256<[u8; 32]> for [u8] { - #[inline] - fn keccak256(&self) -> [u8; 32] { - let mut keccak = Keccak::new_keccak256(); - let mut result = [0u8; 32]; - keccak.update(self); - keccak.finalize(&mut result); - result - } -} - -static mut INPUT: [u8; 1024] = [0; 1024]; -static mut SECRET: [u8; 32] = [0; 32]; -static mut PUBLIC: [u8; 64] = [0; 64]; -static mut ADDRESS: [u8; 20] = [0; 20]; -static mut G: Option = None; - -#[no_mangle] -pub extern "C" fn ecpointg() -> &'static ECPointG { - let g = unsafe { &G }; - - if let Some(ref g) = *g { - return g; - } - - unsafe { G = Some(ECPointG::new()) }; - g.as_ref().expect("value set above; qed") -} - -#[no_mangle] -pub extern "C" fn input_ptr() -> *const u8 { - unsafe { INPUT.as_ptr() } -} - -#[no_mangle] -pub extern "C" fn secret_ptr() -> *const u8 { - unsafe { SECRET.as_ptr() } -} - -#[no_mangle] -pub extern "C" fn public_ptr() -> *const u8 { - unsafe { PUBLIC.as_ptr() } -} - -#[no_mangle] -pub extern "C" fn address_ptr() -> *const u8 { - unsafe { ADDRESS.as_ptr() } -} - -#[no_mangle] -pub extern "C" fn verify_secret() -> bool { - is_valid_secret(unsafe { &SECRET }) -} - -#[no_mangle] -pub extern "C" fn brain(input_len: usize) { - let data = unsafe { &INPUT[..input_len] }; - let mut secret_out = unsafe { &mut SECRET }; - let mut public_out = unsafe { &mut PUBLIC }; - let mut address_out = unsafe { &mut ADDRESS }; - - let g = ecpointg(); - let mut secret = data.keccak256(); - - let mut i = 0; - loop { - secret = secret.keccak256(); - - match i > 16384 { - false => i += 1, - true => { - if let Some(public) = create_public_key(g, &secret) { - let public = &public[1..]; - let hash = public.keccak256(); - - address_out.copy_from_slice(&hash[12..]); - - if address_out[0] == 0 { - public_out.copy_from_slice(&public); - secret_out.copy_from_slice(&secret); - return; - } - } - } - } - } -} - -// Entry point for this program. -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} - -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} - -// This function may be needed based on the compilation target. -#[lang = "eh_unwind_resume"] -#[no_mangle] -pub extern fn rust_eh_unwind_resume() { -} - -#[lang = "panic_fmt"] -#[no_mangle] -pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, - _file: &'static str, - _line: u32) -> ! { - unsafe { intrinsics::abort() } -} diff --git a/webpack/app.js b/webpack/app.js index 3b51e6f4c..1d055194d 100644 --- a/webpack/app.js +++ b/webpack/app.js @@ -157,9 +157,7 @@ module.exports = { unsafeCache: true }, - node: { - fs: 'empty' - }, + target: 'electron-renderer', plugins: (function () { let plugins = Shared.getPlugins().concat( diff --git a/webpack/shared.js b/webpack/shared.js index d7857db54..ee093ff57 100644 --- a/webpack/shared.js +++ b/webpack/shared.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . const webpack = require('webpack'); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const HappyPack = require('happypack'); const PackageJson = require('../package.json'); @@ -57,14 +58,13 @@ function getPlugins (_isProd = isProd) { if (_isProd) { plugins.push( new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.optimize.UglifyJsPlugin({ + new UglifyJsPlugin({ sourceMap: true, - screwIe8: true, - compress: { - warnings: false - }, - output: { - comments: false + uglifyOptions: { + ecma: 8, + compress: { + warnings: false + } } }) );