diff --git a/api/api.js b/api/api.js index ae4be06..a9521ec 100644 --- a/api/api.js +++ b/api/api.js @@ -1,6 +1,6 @@ /* This component is a Node.JS server that implements - API handler methods to support the Block Explorer + API handler methods to support the Bus Explorer Web UI. */ import express from 'express'; @@ -16,8 +16,7 @@ import geoip from 'geoip-lite'; import YAML from 'yaml'; import fs from 'fs'; import assert from 'assert'; -//import * as solanaWeb3 from '@solana/web3.js'; -import * as bitconchWeb3 from '@bitconch/bitconch-web3j'; +import * as web3 from '@bitconch/bitconch-web3j'; import config from './config'; @@ -28,7 +27,7 @@ let FULLNODE_URL = 'http://localhost:10099'; const app = express(); -const port = 8961; +const port = 8960; const MINUTE_MS = 60 * 1000; function getClient() { @@ -405,11 +404,9 @@ function sendAccountResult(req, res) { let thePromises = _.map(ids, id => { return new Promise(resolve => { - //const connection = new solanaWeb3.Connection(FULLNODE_URL); - const connection = new bitconchWeb3.Connection(FULLNODE_URL); + const connection = new web3.Connection(FULLNODE_URL); return connection - // .getBalance(new solanaWeb3.PublicKey(id)) - .getBalance(new bitconchWeb3.PublicKey(id)) + .getBalance(new web3.PublicKey(id)) .then(balance => { return resolve({id: id, balance: balance}); }); diff --git a/api/config.js b/api/config.js index c1a81e2..22035e7 100644 --- a/api/config.js +++ b/api/config.js @@ -5,7 +5,7 @@ export default { unixds: true, host: '127.0.0.1', port: 7654, - socket: '/tmp/bitconch-blockstream.sock', + socket: '/bitconch/tmp/bitconch-blockstream.sock', }, redis: { host: '127.0.0.1', diff --git a/api/inbound-stream.js b/api/inbound-stream.js index 9124c6f..e4f5986 100644 --- a/api/inbound-stream.js +++ b/api/inbound-stream.js @@ -1,6 +1,6 @@ /* This component is a Node.JS service that listens for events from - the Solana EntryStream class. It runs a main event loop listening to + the Bitconch EntryStream class. It runs a main event loop listening to a TCP, UDP, and/or Unix Domain Socket and dispatches events to one or more handlers (typically Redis for event aggregation and realtime streaming). @@ -9,7 +9,6 @@ import Base58 from 'base-58'; import dgram from 'dgram'; import net from 'net'; import redis from 'redis'; -//import {Transaction} from '@solana/web3.js'; import {Transaction} from '@bitconch/bitconch-web3j'; import _ from 'lodash'; import fs from 'fs'; diff --git a/bin/blockexplorer.sh b/bin/blockexplorer.sh new file mode 100644 index 0000000..30c47d6 --- /dev/null +++ b/bin/blockexplorer.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -e + +cwd=$PWD + +rootDir=$( + cd "$(dirname "$0")"; + node -p ' + try { + let package_json = [ + "../lib/node_modules/@bitconch/bus-explorer/package.json", + "../@bitconch/bus-explorer/package.json", + "../package.json" + ].find(require("fs").existsSync); + + assert( + require(package_json)["name"] === "@bitconch/bus-explorer", + "Invalid package name in " + package_json + ); + + const path = require("path"); + path.resolve(path.dirname(package_json)) + } catch (err) { + throw new Error("Unable to locate bus-explorer directory: " + String(err)); + } + ' +) +cd "$rootDir" + +if [[ ! -d build || ! -f build/api/api.js ]]; then + echo "Error: build/ artifacts missing. Run |yarn run build| to create them" + exit 1 +fi + +cleanup() { + set +e + for pid in "$api" "$ui"; do + [[ -z $pid ]] || kill "$pid" + done + exit 1 +} +trap cleanup SIGINT SIGTERM ERR + +( + set -x + redis-cli ping +) + +rm -f "$cwd"/bitconch-bus-explorer-{api,ui}.log + +api= +ui= +while true; do + if [[ -z $api ]] || ! kill -0 "$api"; then + logfile="$cwd"/bitconch-bus-explorer-api.log + echo "Starting api process (logfile: $logfile)" + date | tee -a "$logfile" + npm run start-prod:api >> "$logfile" 2>&1 & + api=$! + echo " pid: $api" + fi + + if [[ -z $ui ]] || ! kill -0 "$ui"; then + logfile="$cwd"/bitconch-bus-explorer-ui.log + echo "Starting ui process (logfile: $logfile)" + date | tee -a "$logfile" + npm run start-prod:ui >> "$logfile" 2>&1 & + ui=$! + echo " pid: $ui" + fi + + sleep 1 +done diff --git a/package.json b/package.json index c7ebcee..72dad2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bitconch/bus-explorer", - "version": "0.0.0-development", + "version": "1.1.2-development", "license": "MIT", "author": "Bitconch Labs, Inc", "homepage": "http://bitconch.io/", @@ -12,12 +12,12 @@ "url": "https://github.com/bitconch/bus-explorer/issues " }, "dependencies": { - "@bitconch/bitconch-web3j": "1.1.2", + "@bitconch/bitconch-web3j":"bitconch/bitconch-web3j#master", "babel-plugin-transform-runtime": "^6.23.0", "base-58": "^0.0.1", "copy-to-clipboard": "^3.2.0", "cors": "^2.8.5", - "express": "^4.16.4", + "express": "^4.17.0", "express-ws": "^4.0.0", "geoip-lite": "^1.3.7", "google-map-react": "^1.1.4", @@ -27,7 +27,7 @@ "react-debounce-render": "^5.0.0", "redis": "^2.8.0", "serve": "^11.0.0", - "yaml": "^1.5.1" + "yaml": "^1.6.0" }, "devDependencies": { "@material-ui/core": "^3.9.3", @@ -45,7 +45,7 @@ "eslint-plugin-react": "^7.13", "history": "^4.9.0", "moment": "^2.24.0", - "prettier": "^1.17.0", + "prettier": "^1.17.1", "qrcode.react": "^0.9.3", "react": "^16.8.6", "react-chartjs-2": "^2.7.6", @@ -69,7 +69,7 @@ "prepack": "set -ex; npm run lint; npm run build", "pretty": "prettier --write '{,{api,src}/**/}*.js{,x}'", "start:api": "PORT=8960 set -ex; redis-cli ping; babel-node --presets env api/api.js", - "start:ui": "PORT=8961 react-scripts start", + "start:ui": "HTTPS=true&&PORT=8961 react-scripts start", "start-prod:api": " node build/api/api.js", "start-prod:ui": "serve -s build", "test:ui": "react-scripts test" diff --git a/public/bitconch-logo.svg b/public/bitconch-logo.svg new file mode 100644 index 0000000..4e81360 --- /dev/null +++ b/public/bitconch-logo.svg @@ -0,0 +1,55 @@ + + + + diff --git a/public/bitconch-logo1.svg b/public/bitconch-logo1.svg new file mode 100644 index 0000000..715c5a3 --- /dev/null +++ b/public/bitconch-logo1.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/bitconch_logo.png b/public/bitconch_logo.png new file mode 100644 index 0000000..e4229d9 Binary files /dev/null and b/public/bitconch_logo.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..092efca Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/images/guide.jpg b/public/images/guide.jpg new file mode 100644 index 0000000..0bc4171 Binary files /dev/null and b/public/images/guide.jpg differ diff --git a/public/images/icon/block-height.png b/public/images/icon/block-height.png new file mode 100644 index 0000000..5833e6f Binary files /dev/null and b/public/images/icon/block-height.png differ diff --git a/public/images/icon/block-price.png b/public/images/icon/block-price.png new file mode 100644 index 0000000..9302e5f Binary files /dev/null and b/public/images/icon/block-price.png differ diff --git a/public/images/icon/block-tps.png b/public/images/icon/block-tps.png new file mode 100644 index 0000000..bb82640 Binary files /dev/null and b/public/images/icon/block-tps.png differ diff --git a/public/images/icon/block.png b/public/images/icon/block.png new file mode 100644 index 0000000..9c7e02b Binary files /dev/null and b/public/images/icon/block.png differ diff --git a/public/images/icon/ent-leader.png b/public/images/icon/ent-leader.png new file mode 100644 index 0000000..492812f Binary files /dev/null and b/public/images/icon/ent-leader.png differ diff --git a/public/images/icon/entry.png b/public/images/icon/entry.png new file mode 100644 index 0000000..271a8d5 Binary files /dev/null and b/public/images/icon/entry.png differ diff --git a/public/images/icon/last-dt.png b/public/images/icon/last-dt.png new file mode 100644 index 0000000..c7a869d Binary files /dev/null and b/public/images/icon/last-dt.png differ diff --git a/public/images/icon/tick-height.png b/public/images/icon/tick-height.png new file mode 100644 index 0000000..e4798d6 Binary files /dev/null and b/public/images/icon/tick-height.png differ diff --git a/public/images/icon/transaction.png b/public/images/icon/transaction.png new file mode 100644 index 0000000..6af9287 Binary files /dev/null and b/public/images/icon/transaction.png differ diff --git a/public/images/icon/txn-count.png b/public/images/icon/txn-count.png new file mode 100644 index 0000000..852becb Binary files /dev/null and b/public/images/icon/txn-count.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..a0585df --- /dev/null +++ b/public/index.html @@ -0,0 +1,15 @@ + + + + + + + + + Bitconch Block Explorer + + + +
+ + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..947e27b --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "Bitconch Block Explorer", + "name": "Bitconch Block Explorer", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + /*"theme_color": "#000000",*/ + "background_color": "#f4f5f9" +} diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..21758d2 --- /dev/null +++ b/src/App.css @@ -0,0 +1,27 @@ +body { + background-color: #f4f5f9; +} + +.App { + text-align: center; +} + +.App-logo { + animation: App-logo-spin infinite 20s linear; + height: 40vmin; +} + +.App-header { + /*background-color: #282c34;*/ + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..16a686e --- /dev/null +++ b/src/App.js @@ -0,0 +1,639 @@ +import React, {Component} from 'react'; +import axios from 'axios'; +import {Router} from 'react-router-dom'; +import { + MuiThemeProvider, + withStyles, +} from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import RobustWebSocket from 'robust-websocket'; +import _ from 'lodash'; +import {matchPath, Route} from 'react-router'; +import './App.css'; +import {createBrowserHistory} from 'history'; +import {Connection} from '@bitconch/bitconch-web3j'; + +import EndpointConfig from './EndpointConfig'; +import BxDataItem from './BxDataItem'; +import BxTransactionChart from './BxTransactionChart'; +import BxStatsGridTable from './BxStatsGridTable'; +import BxDialog from './BxDialog'; +import BxDialogTransactions from './BxDialogTransactions'; +import BxDialogWorldMap from './BxDialogWorldMap'; +import BxAppBar from './BxAppBar'; +import {sleep} from './sleep'; +import {styles,theme} from './themeStyle'; + +const history = createBrowserHistory(); + +async function geoip(ip: string) { + let lat = 11.6065; + let lng = 165.3768; + try { + const result = await window.fetch( + `https:${BLOCK_EXPLORER_API_BASE}/geoip/${ip}`, + ); + if (result.status === 200) { + const json = await result.json(); + lat = json[0]; + lng = json[1]; + console.log(ip, 'at', lat, lng); + } + } catch (err) { + console.log('geoip of', ip, 'failed with:', err); + } + //新增Math.random()随机数,防止数据重复 + return [lat + Math.random() / 10 - 0.05, lng + Math.random() / 10 - 0.05]; +} + +const BLOCK_EXPLORER_API_BASE = EndpointConfig.BLOCK_EXPLORER_API_BASE; + +const BxAppBarThemed = withStyles(styles)(BxAppBar); +const BxDialogThemed = withStyles(styles)(BxDialog); +const BxDialogTransactionsThemed = withStyles(styles)(BxDialogTransactions); +const BxDialogWorldMapThemed = withStyles(styles)(BxDialogWorldMap); +const BxStatsGridTableThemed = withStyles(styles)(BxStatsGridTable); +const BxTransactionChartThemed = withStyles(styles)(BxTransactionChart); +// const BxDataTableThemed = withStyles(styles)(BxDataTable); +const BxDataItemThemed = withStyles(styles)(BxDataItem); + +class App extends Component { + constructor(props) { + super(props); + + this.ws = null; + this.connection = new Connection(EndpointConfig.BLOCK_EXPLORER_RPC_URL); + + this.state = { + enabled: true, + dialogOpen: false, + selectedValue: null, + currentMatch: null, + stateLoading: false, + nodes: [], + globalStats: { + '!ent-last-leader': null, + '!blk-last-slot': 0, + '!txn-count': 0, + '!txn-per-sec-max': 0, + }, + txnStats: {}, + users: [], + userState: {}, + transactions: [], + blocks: [], + }; + + const self = this; + + self.updateGlobalStats(); + self.updateTxnStats(); + self.updateBlocks(); + + self.updateTransactions(); + setInterval(() => { + self.updateTransactions(); + }, 10000); + } + + getRemoteState(attr, url, mapFun, limit) { + axios.get(url).then(response => { + let newState = {}; + + if (limit) { + response.data = response.data.slice(0, limit); + } + + if (mapFun) { + newState[attr] = _.map(response.data, mapFun); + } else { + newState[attr] = response.data; + } + + this.updateStateAttributes(newState); + }); + } + + updateSpecificGlobalStateAttribute(attr, value) { + let globalStats = {...this.state.globalStats}; + globalStats[attr] = value; + + this.updateStateAttributes({globalStats: globalStats}); + } + + updateStateAttributes(attrMap) { + let newState = {...this.state}; + + _.forEach(attrMap, (v, k) => { + newState[k] = v; + }); + + this.setState(() => { + return newState; + }); + } + + async updateGlobalStats() { + this.getRemoteState( + 'globalStats', + `https:${BLOCK_EXPLORER_API_BASE}/global-stats`, + ); + + try { + const oldNodes = this.state.nodes; + const newNodes = await this.connection.getClusterNodes(); + const nodes = []; + + let modified = oldNodes.length !== newNodes.length; + + const maybeSetState = () => { + if (modified) { + this.setState({nodes}); + modified = false; + } + }; + for (const newNode of newNodes) { + const oldNode = oldNodes.find(node => node.id === newNode.id); + if (oldNode) { + nodes.push(oldNode); + } else { + const ip = newNode.gossip.split(':')[0]; + const [lat, lng] = await geoip(ip); + newNode.lat = lat; + newNode.lng = lng; + nodes.push(newNode); + modified = true; + } + maybeSetState(); + } + maybeSetState(); + } catch (err) { + console.log('getClusterNodes failed:', err.message); + } + + setTimeout(() => this.updateGlobalStats(), 1200); + } + + updateTxnStats() { + this.getRemoteState( + 'txnStats', + `https:${BLOCK_EXPLORER_API_BASE}/txn-stats`, + ); + setTimeout(() => this.updateTxnStats(), 22000); + } + + updateBlocks() { + if (!this.state.enabled) { + return; + } + + let blkFun = v => { + let newObj = {}; + let fields = v.split('#'); + + newObj.t = 'blk'; + newObj.h = fields[0]; + newObj.l = fields[1]; + newObj.s = fields[2]; + newObj.dt = fields[3]; + newObj.id = fields[4]; + + return newObj; + }; + + this.getRemoteState( + 'blocks', + `https:${BLOCK_EXPLORER_API_BASE}/blk-timeline`, + blkFun, + 10, + ); + } + + updateTransactions() { + if (!this.state.enabled) { + return; + } + + let self = this; + + let txnFun = v => { + return self.parseTransactionMessage(v); + }; + + this.getRemoteState( + 'transactions', + `https:${BLOCK_EXPLORER_API_BASE}/txn-timeline`, + txnFun, + 10, + ); + } + + handleLocationChange = () => location => { + if (location.pathname === '/' && this.selectedValue !== null) { + this.updateStateAttributes({ + selectedValue: null, + dialogOpen: false, + currentMatch: null, + stateLoading: false, + }); + } + + if (location.pathname !== '/') { + let pathMatch = matchPath(window.location.pathname, { + path: '/:type/:id', + exact: false, + strict: false, + }); + + if (pathMatch) { + if (pathMatch.params.type !== 'txns-by-prgid') { + this.unsubscribeWebSocketTransactionsByProgramId(); + } + + this.updateStateAttributes({ + selectedValue: null, + dialogOpen: false, + currentMatch: pathMatch, + stateLoading: true, + }); + + this.handleClickOpen(pathMatch.params.id, pathMatch.params.type)(); + this.updateStateAttributes({ + currentMatch: pathMatch, + stateLoading: true, + }); + } + } + }; + + componentDidMount() { + const self = this; + + if (!self.ws) { + let ws = new RobustWebSocket(`wss:${BLOCK_EXPLORER_API_BASE}/`); + + ws.addEventListener('open', function() { + ws.send(JSON.stringify({hello: 'world'})); + }); + + ws.addEventListener('message', function(event) { + if (!self.state.enabled) { + return; + } + + self.onMessage(JSON.parse(event.data)); + }); + + self.ws = ws; + } + + if (!self.locationListener) { + let locationListener = this.handleLocationChange(); + + history.listen(locationListener); + locationListener(window.location); + + self.locationListener = locationListener; + } + } + + componentWillUnmount() { + if (this.ws) { + this.ws.close(); + } + } + + onMessage = data => { + if (!this.state.enabled) { + return; + } + + let type = data.t; + + if (type === 'blk') { + this.addBlock(this.parseBlockMessage(data.m)); + } + + if (type === 'txns-by-prgid') { + this.addTransactionByProgramId(this.parseTransactionMessage(data.m)); + } + }; + + parseBlockMessage(message) { + let fields = message.split('#'); + + return { + t: 'blk', + h: parseInt(fields[0]), + l: fields[1], + s: parseInt(fields[2]), + dt: fields[3], + id: fields[4], + }; + } + + parseTransactionMessage(message) { + let fields = message.split('#'); + + let instructions = _.map(fields[6].split('|'), i => { + let instParts = i.split('@'); + + return { + program_id: instParts[0], + keys: instParts[1].split(','), + data: instParts[2], + }; + }); + + return { + t: 'txn', + h: parseInt(fields[0]), + l: fields[1], + s: parseInt(fields[2]), + dt: fields[3], + entry_id: fields[4], + id: fields[5], + instructions, + }; + } + + addBlock(block) { + let blocks = [...this.state.blocks]; + + if (blocks.length >= 10) { + blocks.pop(); + } + + blocks.unshift(block); + + this.updateStateAttributes({blocks: blocks}); + } + + addTransactionByProgramId(txn) { + let newValue = {...this.state.selectedValue}; + let newTxns = [...newValue.transactions]; + + if (newTxns.length >= 100) { + newTxns.pop(); + } + + newTxns.unshift(txn); + newValue.transactions = newTxns; + + this.updateStateAttributes({selectedValue: newValue}); + } + + unsubscribeWebSocketTransactionsByProgramId() { + if ( + !this.state.selectedValue || + this.state.selectedValue.t !== 'txns-by-prgid' + ) { + return; + } + + let msg = JSON.stringify({ + action: 'unsubscribe', + type: this.state.selectedValue.t, + id: this.state.selectedValue.id, + }); + + console.log('unsubscribe ' + msg); + this.ws.send(msg); + } + + handleDialogClose = () => { + console.log('dialog close'); + + if ( + this.state.selectedValue && + this.state.selectedValue.t === 'txns-by-prgid' + ) { + this.unsubscribeWebSocketTransactionsByProgramId(); + } + + this.updateStateAttributes({ + selectedValue: null, + dialogOpen: false, + currentMatch: null, + stateLoading: false, + }); + + history.push('/'); + }; + + showMap = () => () => { + history.push(`/map`); + }; + + toggleEnabled = self => event => { + if (event.target.checked === self.state.enabled) { + return; + } + + this.updateStateAttributes({ + enabled: event.target.checked, + }); + }; + + handleSearch = () => event => { + let value = event.target.value; + event.target.value = ''; + + if (value === null || value.length === 0) { + return; + } + + let url = `${BLOCK_EXPLORER_API_BASE}/search/${value}`; + + axios.get(url).then(response => { + let result = response.data; + history.push(`/${result.t}/${result.id}`); + }); + }; + + handleClickOpen = (value, type) => () => { + const self = this; + + let mkUrl = (id, type) => { + let url = null; + + if (type === 'txns-by-prgid') { + url = `${BLOCK_EXPLORER_API_BASE}/txns-by-prgid/${id}`; + } + + if (type === 'txn') { + url = `${BLOCK_EXPLORER_API_BASE}/txn/${id}`; + } + + if (type === 'ent') { + url = `${BLOCK_EXPLORER_API_BASE}/ent/${id}`; + } + + if (type === 'blk') { + url = `${BLOCK_EXPLORER_API_BASE}/blk/${id}`; + } + + return url; + }; + + let url = mkUrl(value, type); + + let updateState = async newVal => { + if (type === 'txns-by-prgid') { + let msg = JSON.stringify({ + action: 'subscribe', + type: type, + id: value, + }); + + console.log('subscribe', msg); + while (self.ws.readyState !== WebSocket.OPEN) { + console.log( + 'Waiting for ws.readyState to be OPEN (1): ', + self.ws.readyState, + ); + await sleep(250); + } + self.ws.send(msg); + + let txns = _(newVal) + .map(v => this.parseTransactionMessage(v)) + .value(); + + let newSelectedValue = { + t: type, + id: value, + transactions: txns, + }; + + self.updateStateAttributes({ + selectedValue: newSelectedValue, + dialogOpen: true, + stateLoading: false, + }); + } else { + self.updateStateAttributes({ + selectedValue: newVal, + dialogOpen: true, + stateLoading: false, + }); + } + }; + + axios + .get(url) + .then(response => updateState(response.data)) + .catch((resp, err) => { + console.error('oops', resp, err); + }); + }; + + render() { + let self = this; + const leaderId = this.state.globalStats['!ent-last-leader']; + + return ( + + +
+ +
+ ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> +
+

+

+ +

+

+
+ + + + + + +
+ + + + + +
+

+

+
+
+
+ ); + } +} + +export default App; diff --git a/src/BxAppBar.jsx b/src/BxAppBar.jsx new file mode 100644 index 0000000..7e47256 --- /dev/null +++ b/src/BxAppBar.jsx @@ -0,0 +1,119 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Grid from '@material-ui/core/Grid'; +import InputBase from '@material-ui/core/InputBase'; +import MenuItem from '@material-ui/core/MenuItem'; +import Menu from '@material-ui/core/Menu'; +import SearchIcon from '@material-ui/icons/Search'; + +class BxAppBar extends React.Component { + state = { + anchorEl: null, + mobileMoreAnchorEl: null, + }; + + handleSearch = (event) => { + if (this.props.handleSearch) { + this.props.handleSearch(event); + } + }; + + handleSwitch = event => { + if (this.props.handleSwitch) { + this.props.handleSwitch(event); + } + }; + + handleProfileMenuOpen = event => { + this.setState({anchorEl: event.currentTarget}); + }; + + handleMenuClose = () => { + this.setState({anchorEl: null}); + this.handleMobileMenuClose(); + }; + + handleMobileMenuOpen = event => { + this.setState({mobileMoreAnchorEl: event.currentTarget}); + }; + + handleMobileMenuClose = () => { + this.setState({mobileMoreAnchorEl: null}); + }; + + render() { + const {anchorEl, mobileMoreAnchorEl} = this.state; + const {classes} = this.props; + const isMenuOpen = Boolean(anchorEl); + const isMobileMenuOpen = Boolean(mobileMoreAnchorEl); + + const renderMenu = ( + + Profile + My account + + ); + + const renderMobileMenu = ( + + + ); + + return ( +
+ + + + +
+ + Bitconch Logo + +
+
+
+ +
+ { + if (ev.key === 'Enter') { + this.handleSearch(ev); + ev.preventDefault(); + } + }} + /> +
+
+ + + + + {renderMenu} + {renderMobileMenu} +
+ ); + } +} +BxAppBar.propTypes = { + classes: PropTypes.object.isRequired, +}; +export default BxAppBar; diff --git a/src/BxDataItem.jsx b/src/BxDataItem.jsx new file mode 100644 index 0000000..47b0e76 --- /dev/null +++ b/src/BxDataItem.jsx @@ -0,0 +1,161 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import BxDateTime from './BxDateTime'; +import BxEntityLink from './BxEntityLink'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import Grid from '@material-ui/core/Grid'; +import _ from 'lodash'; + +class BxDataItem extends React.Component { + renderBlocks() { + const {classes, dataItems} = this.props; + return ( + + + 最新区块 + 最新区块 + + + {_.map(dataItems, row => ( + + + + 区块ID: + + + 区块高度:{row.s} + + + 生成时间:{BxDateTime.formatDateTime(row.dt, {style:BxDateTime.ISO8601_FMT, local:true})} + + + + )) + } + + + ); + } + + renderEntries() { + const {classes, dataItems} = this.props; + return ( + + + 最新记录 + 最新记录 + + + {_.map(dataItems, row => ( + + + + 记录ID: + + + + + 区块高度:{row.s} + + + 时间戳记录数:{row.h} + + + + + 交易数量:{row.txn_count} + + + 发起时间:{BxDateTime.formatDateTime(row.dt, {style:BxDateTime.ISO8601_FMT, local:true})} + + + + + + )) + } + + + ); + } + + renderTransactions() { + const {classes, dataItems, noTitle} = this.props; + + let collectProgramIds = tx => { + return _.chain(tx.instructions) + .map(x => x.program_id) + .uniq() + .value(); + }; + + let collectKeys = tx => { + return _.chain(tx.instructions) + .map(x => x.keys) + .flatten() + .uniq() + .value(); + }; + + return ( + + + 最新交易 + 最新交易(每10s更新) + + + {_.map(dataItems, row => ( + + + + 交易ID: + + + 区块高度:{row.s} + + + 发起时间:{BxDateTime.formatDateTime(row.dt, {style:BxDateTime.ISO8601_FMT, local:true})} + + + + )) + } + + + ); + } + + render() { + const {dataType} = this.props; + + if (dataType === 'blk') { + return this.renderBlocks(); + } + + if (dataType === 'ent') { + return this.renderEntries(); + } + + if (dataType === 'txn') { + return this.renderTransactions(); + } + + return ( + + + (ERROR - No data.) + + + ); + } +} + +BxDataItem.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default BxDataItem; diff --git a/src/BxDataTable.jsx b/src/BxDataTable.jsx new file mode 100644 index 0000000..a84bf7c --- /dev/null +++ b/src/BxDataTable.jsx @@ -0,0 +1,165 @@ +import React from 'react'; +import BxDateTime from './BxDateTime'; +import BxEntityLink from './BxEntityLink'; +import Paper from '@material-ui/core/Paper'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import _ from 'lodash'; + +class BxDataTable extends React.Component { + renderBlocks() { + const {dataItems} = this.props; + + return ( + + + Latest Blocks + + + + + + Block ID + + + Block Height + + Timestamp (approx) + + + + {_.map(dataItems, row => ( + + + + + {row.s} + + + + + ))} + +
+
+ ); + } + + renderTransactions() { + const {dataItems, noTitle} = this.props; + + let collectProgramIds = tx => { + return _.chain(tx.instructions) + .map(x => x.program_id) + .uniq() + .value(); + }; + + let collectKeys = tx => { + return _.chain(tx.instructions) + .map(x => x.keys) + .flatten() + .uniq() + .value(); + }; + + return ( + + {!noTitle && ( + + Transactions + + )} + + + + +
+ Transaction ID +
+
+ Account ID(s) +
+
+ + Program ID(s) + + + Block Height + + Timestamp (approx) +
+
+ + {_.map(dataItems, row => ( + + + +
+ {_.map(collectKeys(row), key => ( + + + + + ))} +
+ + {_.map(collectProgramIds(row), program_id => ( + + +
+
+ ))} +
+ + {row.s} +
+   +
+ + +
+   +
+
+ ))} +
+
+
+ ); + } + + render() { + const {dataType} = this.props; + + if (dataType === 'blk') { + return this.renderBlocks(); + } + + if (dataType === 'txn') { + return this.renderTransactions(); + } + + return ( + + + (ERROR - No data.) + + + ); + } +} + +export default BxDataTable; diff --git a/src/BxDateTime.jsx b/src/BxDateTime.jsx new file mode 100644 index 0000000..e9238dc --- /dev/null +++ b/src/BxDateTime.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import moment from 'moment'; + +class BxDateTime extends React.Component { + static DEFAULT_FMT = 'lll Z'; + static COMPACT_FMT = 'lll Z'; + static ISO8601_FMT_MM = 'HH:mm'; + static ISO8601_FMT = 'YYYY-MM-DD HH:mm'; + static ISO8601_FMT_SS = 'YYYY-MM-DD HH:mm:ss'; + + static formatDateTime(dateTime, options) { + let {fromNow, style, local} = options || {}; + let theDateTime = moment.utc(dateTime); + + if (fromNow) { + return moment(dateTime).fromNow(); + } + + if (!style) { + style = BxDateTime.DEFAULT_FMT; + } + + if (local) { + theDateTime = theDateTime.local(); + } + + return theDateTime.format(style); + } + + render() { + return ( + + {BxDateTime.formatDateTime(this.props.dateTime, this.props)} + + ); + } +} + +export default BxDateTime; diff --git a/src/BxDetail.jsx b/src/BxDetail.jsx new file mode 100644 index 0000000..a64f2b4 --- /dev/null +++ b/src/BxDetail.jsx @@ -0,0 +1,74 @@ +import React from "react"; +import BxDateTime from "./BxDateTime"; +import Paper from '@material-ui/core/Paper'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; + +const location = window.location.href; + +class BxDetail extends React.Component { + + render() { + const {classes, onClose, selectedValue, ...other} = this.props; + + let value = selectedValue || {}; + let title = null; + let url = location; + let rows = []; + if (value.t !== "blk") { + rows.push(["区块ID", value.block_id || (value.block && value.block.id) || ""]); + } + + rows.push(["运算周期", value.s]); + rows.push(["时间戳记录数", value.h]); + let dates = BxDateTime.formatDateTime(value.dt, {style:BxDateTime.ISO8601_FMT, local:true}); + rows.push(["记录时间", dates, value.dt]); + + if (value.t === "txn") { + title = "交易"; + url = url + "txn/" + value.id; + // rows.push(["Signature(s)", value.instructions[0].signatures.join(", ")]); + // rows.push(["Program ID(s)", value.instructions[0].program_id]); + // rows.push(["Account(s)", value.instructions[0].keys.join(", ")]); + } + + if (value.t === "blk") { + title = "区块"; + url = url + "blk/" + value.id; + rows.push(["区块交易", value.entries.join(", ")]); + } + + if (value.t === "ent") { + title = "记录"; + url = url + "ent/" + value.id; + rows.push(["交易信息", value.transactions.join(", ")]); + } + + rows.unshift([title + " ID", value.id]); + + return ( + + + + {rows.map(row => ( + + {row[0]}: + {row[1]} + + ))} + + 当前浏览代码: + +
{JSON.stringify(value, '\\', 4)}
+
+
+
+
+
+ ); + } +} + +export default BxDetail; \ No newline at end of file diff --git a/src/BxDialog.jsx b/src/BxDialog.jsx new file mode 100644 index 0000000..6912f31 --- /dev/null +++ b/src/BxDialog.jsx @@ -0,0 +1,97 @@ +import React from "react"; +import BxDateTime from "./BxDateTime"; +import CloseIcon from '@material-ui/icons/Close'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import IconButton from '@material-ui/core/IconButton'; +import Paper from '@material-ui/core/Paper'; +import PropTypes from "prop-types"; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; + +const location = window.location.href; + +class BxDialog extends React.Component { + render() { + const {classes, onClose, selectedValue, ...other} = this.props; + + let value = selectedValue || {}; + let title = null; + let url = location; + let rows = []; + if (value.t !== "blk") { + rows.push(["区块ID", value.block_id || (value.block && value.block.id) || ""]); + } + rows.push(["运算周期", value.s]); + rows.push(["时间戳记录数", value.h]); + let dates = BxDateTime.formatDateTime(value.dt, {style:BxDateTime.ISO8601_FMT, local:true}); + rows.push(["记录时间", dates, value.dt]); + if (value.t === "txn") { + title = "交易"; + url = url + "txn/" + value.id; + } + + if (value.t === "blk") { + title = "区块"; + url = url + "blk/" + value.id; + } + + if (value.t === "ent") { + title = "记录"; + url = url + "ent/" + value.id; + rows.push(["交易信息", value.transactions.join(", ")]); + } + rows.unshift([title + " ID", value.id]); + + return ( + + + {title} 明细 + + + + + + + + {rows.map(row => ( + + {row[0]}: + {row[1]} + + ))} + + 当前浏览代码: + + + + + +
+
+
+ ); + } +} + +BxDialog.propTypes = { + classes: PropTypes.object.isRequired, + onClose: PropTypes.func, + selectedValue: PropTypes.object, +}; + +export default BxDialog; diff --git a/src/BxDialogTransactions.jsx b/src/BxDialogTransactions.jsx new file mode 100644 index 0000000..9ce5c08 --- /dev/null +++ b/src/BxDialogTransactions.jsx @@ -0,0 +1,97 @@ +import React from 'react'; +import BxDataItem from './BxDataItem'; +import CloseIcon from '@material-ui/icons/Close'; +import debounceRender from 'react-debounce-render'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import IconButton from '@material-ui/core/IconButton'; +import Paper from '@material-ui/core/Paper'; +import PropTypes from 'prop-types'; +import QRCode from 'qrcode.react'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; + +const location = window.location.href; + +class BxDialogTransactions extends React.Component { + renderTable() { + const {selectedValue} = this.props; + + if ( + selectedValue && + selectedValue.transactions && + selectedValue.transactions.length + ) { + return ( + + ); + } else { + return No Transactions. Yet.; + } + } + + render() { + const {classes, selectedValue, onClose, ...other} = this.props; + let value = selectedValue || {}; + let url = location; + + if (value.t === 'txns-by-prgid') { + url = url + 'txns-by-prgid/' + value.id; + } + + return ( + + + Transactions for Program ID: {value.id} + + + + + + + + + + QR Code: + + + + + + +
+ {this.renderTable()} +
+
+ ); + } +} + +BxDialogTransactions.propTypes = { + classes: PropTypes.object.isRequired, + onClose: PropTypes.func, + selectedValue: PropTypes.object, +}; + +export default debounceRender(BxDialogTransactions, 100, {leading: true}); diff --git a/src/BxDialogWorldMap.jsx b/src/BxDialogWorldMap.jsx new file mode 100644 index 0000000..ef8e39c --- /dev/null +++ b/src/BxDialogWorldMap.jsx @@ -0,0 +1,206 @@ +import React from 'react'; +import CloseIcon from '@material-ui/icons/Close'; +import PowerOffIcon from '@material-ui/icons/PowerOff'; +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; +import IconButton from '@material-ui/core/IconButton'; +import PropTypes from 'prop-types'; +import GoogleMapReact from 'google-map-react'; +import Fab from '@material-ui/core/Fab'; +import Popover from '@material-ui/core/Popover'; +import {Connection} from '@bitconch/bitconch-web3j'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import Slide from '@material-ui/core/Slide'; +import Toolbar from '@material-ui/core/Toolbar'; + +const mapApiKey = + process.env.REACT_APP_MAP_API_KEY || + 'AIzaSyArM4e0n53tWyK5drjXP03OmovvVJHk8OU'; + +class Node extends React.Component { + state = { + anchorEl: null, + }; + + handleClick = event => { + this.setState({ + anchorEl: event.currentTarget, + }); + }; + + handleClose = () => { + this.setState({ + anchorEl: null, + }); + }; + + handleTerminate = async () => { + const {node} = this.props; + try { + const rpcUrl = `http://${node.rpc}`; + console.log(rpcUrl); + if (window.confirm('Are you sure you want to terminate this node?')) { + const connection = new Connection(rpcUrl); + const result = await connection.fullnodeExit(); + if (!result) { + window.alert('Node declined to exit'); + } else { + node.terminated = true; + } + } + } catch (err) { + window.alert(`Failed to terminate node: ${err}`); + } + this.handleClose(); + }; + + render() { + const {classes, isLeader, node, ...other} = this.props; + const {anchorEl} = this.state; + const open = Boolean(anchorEl); + + return ( +
+ + {node.terminated ? ( + + ) : ( + Bitconch Logo + )} + + + + Node: {node.id} +
+ Gossip: {node.gossip} + {node.rpc && !node.terminated && ( +
+

+ +

+ )} +
+
+
+ ); + } +} + +Node.propTypes = { + classes: PropTypes.object.isRequired, +}; + +function Transition(props) { + return ; +} + +export default class BxDialogWorldMap extends React.Component { + static defaultProps = { + center: { + lat: 17, + lng: -120.6743, + }, + zoom: 0, + }; + + render() { + const {classes, onClose, leaderId, nodes, ...other} = this.props; + + const sortedNodes = nodes.slice(0); + sortedNodes.sort((a, b) => { + if (a.id === leaderId) { + return 1; + } else if (b.id === leaderId) { + return -1; + } else { + return 0; + } + }); + + return ( + + + + + + + Node Cluster Map + + + +
+ {nodes.length} nodes + + {sortedNodes.map(node => { + return ( + + ); + })} + +
+
+
+ ); + } +} + +BxDialogWorldMap.propTypes = { + classes: PropTypes.object.isRequired, + onClose: PropTypes.func, +}; diff --git a/src/BxEntityLink.jsx b/src/BxEntityLink.jsx new file mode 100644 index 0000000..5b9e342 --- /dev/null +++ b/src/BxEntityLink.jsx @@ -0,0 +1,92 @@ +import React from 'react'; +import {Link as RouterLink} from 'react-router-dom'; +import Link from '@material-ui/core/Link'; + +class BxEntityLink extends React.Component { + renderNode() { + return ( + + {this.props.node.substring(0, 22) + '\u2026'} + + ); + } + + renderBlock() { + return ( + + {this.props.blk} + + ); + } + + renderEntry() { + return ( + + {this.props.ent} + + ); + } + + renderTransaction() { + return ( + + {this.props.txn} + + ); + } + + renderAccountId() { + return ( + + {this.props.acct_id.substring(0, 22) + (this.props.acct_id.length > 22 ? '\u2026' : '')} + + ); + } + + renderProgramId() { + return ( + + + {this.props.prg_id.substring(0, 22) + (this.props.prg_id.length > 22 ? '\u2026' : '')} + + + ); + } + + render() { + const {node, ent, blk, txn, acct_id, prg_id} = this.props; + if (node) { + return this.renderNode(); + } + if (ent) { + return this.renderEntry(); + } + if (blk) { + return this.renderBlock(); + } + if (txn) { + return this.renderTransaction(); + } + if (acct_id) { + return this.renderAccountId(); + } + if (prg_id) { + return this.renderProgramId(); + } + return unknown entity; + } +} + +export default BxEntityLink; diff --git a/src/BxStatsEntityLink.jsx b/src/BxStatsEntityLink.jsx new file mode 100644 index 0000000..d86d3aa --- /dev/null +++ b/src/BxStatsEntityLink.jsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Link as RouterLink } from 'react-router-dom' +import Link from '@material-ui/core/Link'; + +class BxStatsEntityLink extends React.Component { + renderNode() { + return ( + {this.props.node.substring(0, 20) + "\u2026"} + ); + } + + renderBlock() { + return ( + + {this.props.blk} + + ); + } + + renderEntry() { + return ( + + {this.props.ent} + + ); + } + + renderTransaction() { + return ( + + {this.props.txn} + + ); + } + + render() { + const {node, ent, blk, txn} = this.props; + + if (node) { + return this.renderNode(); + } + + if (ent) { + return this.renderEntry(); + } + + if (blk) { + return this.renderBlock(); + } + + if (txn) { + return this.renderTransaction(); + } + + return ( + unknown entity + ); + } +} + +export default BxStatsEntityLink; diff --git a/src/BxStatsGridTable.jsx b/src/BxStatsGridTable.jsx new file mode 100644 index 0000000..6550444 --- /dev/null +++ b/src/BxStatsGridTable.jsx @@ -0,0 +1,81 @@ +import React from "react"; +import BxDateTime from "./BxDateTime"; +import BxStatsEntityLink from "./BxStatsEntityLink"; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +/*import BxStatsHelpLink from "./BxStatsHelpLink";*/ +import _ from 'lodash'; + +class BxStatsTable extends React.Component { + render() { + const {classes, globalStats} = this.props; + + let currentTpsKey = null; + _.forEach(globalStats, (v, k) => { + if (k && (k.indexOf("!txn-per-sec:") === 0)) { + currentTpsKey = k; + } + }); + + return ( + + + 头部节点 + + 头部节点 + + + + + + + 区块高度 + + 区块高度 + + {globalStats['!blk-last-slot'] || "0"} + + + + + 峰值TPS + + 当前/峰值TPS + + {globalStats[currentTpsKey] || "0"}/{globalStats['!txn-per-sec-max'] || "0"} + + + + + 总交易 + + 总交易 + + {globalStats['!txn-count'] || "0"} + + + + + 时间戳记录 + + 时间戳记录 + + {globalStats['!ent-height'] || "-"} + + + + + 最近更新 + + 最近更新 + + + + + + + ); + } +} + +export default BxStatsTable; diff --git a/src/BxStatsHelpLink.jsx b/src/BxStatsHelpLink.jsx new file mode 100644 index 0000000..95f7b68 --- /dev/null +++ b/src/BxStatsHelpLink.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import Link from '@material-ui/core/Link'; +import {testnetDefaultChannel} from '@bitconch/bitconch-web3j/package.json'; + +const BOOK_VERSION = (testnetDefaultChannel === 'edge') ? 'book-edge' : 'book'; + +class BxStatsHelpLink extends React.Component { + render() { + const {text, term} = this.props; + return ( + + + + ); + } +} + +export default BxStatsHelpLink; diff --git a/src/BxTransactionChart.jsx b/src/BxTransactionChart.jsx new file mode 100644 index 0000000..b4343fd --- /dev/null +++ b/src/BxTransactionChart.jsx @@ -0,0 +1,87 @@ +import React from "react"; +import BxDateTime from "./BxDateTime"; +import {Line} from 'react-chartjs-2'; +import Grid from '@material-ui/core/Grid'; +import Paper from '@material-ui/core/Paper'; +import Typography from '@material-ui/core/Typography'; +import _ from 'lodash'; + +const chartOptions = { + ///Boolean - Whether grid lines are shown across the chart + scaleShowGridLines: true, + scaleGridLineColor: "rgba(47,79,79,.05)", + scaleGridLineWidth: 1, + scaleShowHorizontalLines: true, + scaleShowVerticalLines: true, + bezierCurve: false, + bezierCurveTension: 0.4, + pointDot: true, + pointDotRadius: 4, + pointDotStrokeWidth: 1, + pointHitDetectionRadius: 20, + datasetStroke: true, + datasetStrokeWidth: 2, + datasetFill: true, + offsetGridLines: false, +}; + +class BxTransactionChart extends React.Component { + render() { + const {classes, txnStats} = this.props; + if (_.size(txnStats) === 0) { + return ( + + No Data Present - Loading... + + ); + } + let theLabels = _.keys(txnStats).map(x => + BxDateTime.formatDateTime(x, { + style: BxDateTime.ISO8601_FMT, + local: true, + }), + ); + let theData = _(txnStats).values().map(x => parseInt(x || "0")).value(); + const data = (canvas) => { + const ctx = canvas.getContext("2d") + const gradient = ctx.createLinearGradient(0,0,0,350); + gradient.addColorStop(0,'rgba(255,135,67,0.5)'); + gradient.addColorStop(0.5,'rgba(255,135,67,0.25)'); + gradient.addColorStop(0.9,'rgba(255,135,67,0.05)'); + gradient.addColorStop(1,'rgba(255,135,67,0)'); + return { + labels: theLabels, + datasets: [{ + label: "每分钟交易数", + data: theData, + backgroundColor: gradient, + pointBackgroundColor: '#FF5E0C', + borderColor: '#FF5E0C', + }] + } + } + + chartOptions.scales = { + xAxes: [{ + scaleLabel: { + display: true, + labelString: `时间段: ${theLabels[0]} 至 ${theLabels[theLabels.length - 1]}`, + }, + ticks: { + callback: x => { + return x.split(' ').pop(); + }, + }, + }] + }; + + return ( + + + + + + ); + } +} +export default BxTransactionChart; diff --git a/src/EndpointConfig.js b/src/EndpointConfig.js new file mode 100644 index 0000000..7a376c2 --- /dev/null +++ b/src/EndpointConfig.js @@ -0,0 +1,7 @@ +let EndpointConfig = { + // BLOCK_EXPLORER_API_BASE: `//${window.location.hostname}:8960`, + BLOCK_EXPLORER_RPC_URL: `//${window.location.hostname}:10099`, + BLOCK_EXPLORER_API_BASE: `//api.bitconch.io`, +}; + +export default EndpointConfig; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..cd4fd39 --- /dev/null +++ b/src/index.css @@ -0,0 +1,37 @@ +@import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css'); +@import url('https://fonts.googleapis.com/css?family=Roboto'); + +body { + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} + +li { + list-style: none; +} + +.sideBySide {display:flex; flex-direction:row; vertical-align:top;} +.sideBySideHeader {display:flex; flex-direction:row; vertical-align:top;background-color: #0091C8;width: 100%;} + +.headerTypography { + word-break: break-word; + width: 100%; + color:#fff; + /*padding: 20px 0 30px 0;*/ + text-align: -webkit-center; +} +.headerTypographyLast { min-width: 140px; + color: #fff;} + +.cardStats {background: #2976c0;border-radius:4px;} +.cardStatsP {color: #647273;} \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..0c5e75d --- /dev/null +++ b/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './index.css'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +ReactDOM.render(, document.getElementById('root')); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/src/logo.svg b/src/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/serviceWorker.js b/src/serviceWorker.js new file mode 100644 index 0000000..f94eecc --- /dev/null +++ b/src/serviceWorker.js @@ -0,0 +1,135 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read http://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, + ), +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA', + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.', + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.', + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/src/sleep.js b/src/sleep.js new file mode 100644 index 0000000..961d48c --- /dev/null +++ b/src/sleep.js @@ -0,0 +1,6 @@ +// @flow + +// zzz +export function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/src/themeStyle.js b/src/themeStyle.js new file mode 100644 index 0000000..0307f04 --- /dev/null +++ b/src/themeStyle.js @@ -0,0 +1,233 @@ +import { + createMuiTheme, +} from '@material-ui/core/styles'; + +export const styles = theme => ({ + root: { + width: '100%', + }, + headBackGround: { + backgroundImage: "url('/images/guide.jpg')" + }, + grow: { + flexGrow: 1, + }, + menuButton: { + marginLeft: -12, + marginRight: 20, + }, + title: { + display: 'block', + [theme.breakpoints.up('sm')]: { + display: 'block', + }, + }, + closeButton: { + position: 'absolute', + right: theme.spacing.unit, + top: theme.spacing.unit, + color: theme.palette.grey[500], + }, + link: { + color: theme.palette.primary.light, + }, + search: { + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: '#fff', + marginRight: theme.spacing.unit * 2, + marginLeft: 10, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing.unit * 3, + width: 'auto', + }, + }, + searchIcon: { + width: '35px', + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + width: '100%', + }, + inputInput: { + paddingTop: theme.spacing.unit, + paddingRight: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + paddingLeft: '32px', + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('md')]: { + width: 300, + }, + }, + sectionDesktop: { + display: 'none', + [theme.breakpoints.up('md')]: { + display: 'flex', + }, + }, + sectionMobile: { + display: 'flex', + [theme.breakpoints.up('md')]: { + display: 'none', + }, + }, + dataStyle: { + position: "relative", + margin: "auto", + width: "100%", + height: "100%", + backgroundColor: '#f4f5f9', + }, + dataTitle: { + color: '#647273', + paddingLeft: '10px', + }, + dataTableCellTitle: { + color: '#647273', + fontSize: '0.95rem', + padding: '4px 24px 4px 24px', + }, + dataTableCell: { + paddingTop: '5px', + color: '#647273', + }, + gridBackColor: { + backgroundColor: '#2976c0', + }, + dataContent: { + float: "left", + width: "49%", + }, + dataContentMarginLeft: { + marginLeft:"22px", + }, + sideBySideHeader: { + width: "100%", + flex: "0 0 50%", + padding: '20px 0 20px 0', + [theme.breakpoints.up('md')]: { + color: '#fff', + flex: 'auto', + margin: '0 5px 0 5px', + } + }, + headerTypography: { + color:"#fff", + }, + statsGridIcon: { + width: "40px", + height: "40px", + }, + dataListItemTitles: { + color: '#647273', + paddingLeft: '10px', + display: "flex", + textAlign: "left", + paddingTop: "10px", + }, + dataListItemTitle: { + marginLeft: "10px", + lineHeight: "30px", + }, + dataListItemIcon: { + width: "30px", + height: "30px", + }, + sideBySideHeaderSecond: { + display: "flex", + flexWrap: "wrap", + width: "calc(100% + 16px)", + margin: "-8px", + [theme.breakpoints.up('md')]: { + flexWrap: "unset", + flexDirection: "row", + justifyContent: "center", + borderRadius: "4px", + boxShadow: "0px 1px 3px 0px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 2px 1px -1px rgba(0,0,0,0.12)", + }, + backgroundColor: '#0091C8', + }, + dataListContainer: { + backgroundColor: '#f4f5f9', + width: "100%", + paddingLeft: "15px", + paddingRight: "15px", + boxShadow: "none", + }, + dataListGrid: { + width: "100%", + backgroundColor: '#fff', + borderRadius: '4px', + color: '#647273', + [theme.breakpoints.up('md')]: { + width: "100%", + backgroundColor: '#fff', + borderRadius: '4px', + color: '#647273', + } + }, + dataListItemGrid: { + width: "100%", + backgroundColor: '#fff', + borderRadius: '4px', + color: '#647273', + marginBottom: "10px", + boxShadow: "0px 1px 3px 0px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 2px 1px -1px rgba(0,0,0,0.12)", + [theme.breakpoints.up('md')]: { + width: "100%", + backgroundColor: '#fff', + borderRadius: '4px', + color: '#647273', + marginBottom: "0px", + flex: '0 0 50%', + } + }, + dataListItem: { + wordBreak: 'break-word', + border: '1px solid #DFD7CA', + borderTop: '0', + borderLeft: '0', + borderRight: '0', + borderColor: '##EEEEEE', + padding: '10px 10px 0 10px', + }, + dataListItemText: { + padding: '0', + }, + transChart: { + width: "calc(100% + 16px)", + margin: "-8px", + }, + dialog: { + margin: "20px", + }, + dialogTh: { + width: "20%", + padding: "4px 5px 4px 20px", + }, + dialogTd: { + width: "80%", + padding: "4px 5px 4px 20px", + }, +}); + +export const theme = createMuiTheme({ + palette: { + type: 'light', + primary: { + main: '#f4f5f9', + }, + secondary: { + main: '#2BFEBC', + }, + }, + typography: {useNextVariants: true}, +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0fefc3d..3db6a6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1019,10 +1019,10 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" - integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== dependencies: regenerator-runtime "^0.13.2" @@ -1092,6 +1092,23 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@bitconch/bitconch-web3j@bitconch/bitconch-web3j#master": + version "1.1.4" + resolved "https://codeload.github.com/bitconch/bitconch-web3j/tar.gz/e2efbbf659cc5e63cf7873f04dfcdb472e8bd479" + dependencies: + "@babel/runtime" "^7.3.1" + bn.js "^4.11.8" + bs58 "^4.0.1" + buffer-layout "^1.2.0" + esdoc-inject-style-plugin "^1.0.0" + jayson "^3.0.1" + mz "^2.7.0" + node-fetch "^2.2.0" + rpc-websockets "^4.3.3" + superstruct "^0.6.0" + tweetnacl "^1.0.0" + ws "^6.1.0" + "@cnakazawa/watch@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" @@ -1936,13 +1953,13 @@ abbrev@1, abbrev@~1.1.1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.4, accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" + mime-types "~2.1.24" + negotiator "0.6.2" acorn-dynamic-import@^4.0.0: version "4.0.0" @@ -3209,21 +3226,21 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.18.3: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" - integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: - bytes "3.0.0" + bytes "3.1.0" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" + http-errors "1.7.2" + iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" bonjour@^3.5.0: version "3.5.0" @@ -3492,6 +3509,11 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + cacache@^10.0.4: version "10.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" @@ -4190,6 +4212,13 @@ content-disposition@0.5.2: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -4252,10 +4281,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== copy-concurrently@^1.0.0: version "1.0.5" @@ -5716,39 +5745,39 @@ express-ws@^4.0.0: dependencies: ws "^5.2.0" -express@^4.16.2, express@^4.16.4: - version "4.16.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" - integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== +express@^4.16.2, express@^4.17.0: + version "4.17.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.0.tgz#288af62228a73f4c8ea2990ba3b791bb87cd4438" + integrity sha512-1Z7/t3Z5ZnBG252gKUPyItc4xdeaA0X934ca2ewckAsVsw9EG71i++ZHZPYnus8g/s5Bty8IMpSVEuRkmwwPRQ== dependencies: - accepts "~1.3.5" + accepts "~1.3.7" array-flatten "1.1.1" - body-parser "1.18.3" - content-disposition "0.5.2" + body-parser "1.19.0" + content-disposition "0.5.3" content-type "~1.0.4" - cookie "0.3.1" + cookie "0.4.0" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.1.1" + finalhandler "~1.1.2" fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.2" + parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.4" - qs "6.5.2" - range-parser "~1.2.0" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" safe-buffer "5.1.2" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -5958,17 +5987,17 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" + parseurl "~1.3.3" + statuses "~1.5.0" unpipe "~1.0.0" find-cache-dir@^0.1.1: @@ -6219,10 +6248,10 @@ fsevents@^1.0.0, fsevents@^1.2.7: nan "^2.9.2" node-pre-gyp "^0.10.0" -fstream@^1.0.0, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= +fstream@^1.0.0, fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -6376,7 +6405,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -6388,6 +6417,18 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -6808,7 +6849,18 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: +http-errors@1.7.2, http-errors@~1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: version "1.6.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= @@ -6884,13 +6936,6 @@ hyphenate-style-name@^1.0.2: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - iconv-lite@0.4.24, iconv-lite@^0.4.13, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -7136,12 +7181,7 @@ ip@^1.1.0, ip@^1.1.4, ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" - integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= - -ipaddr.js@^1.9.0: +ipaddr.js@1.9.0, ipaddr.js@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== @@ -9133,6 +9173,11 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + "mime-db@>= 1.36.0 < 2", mime-db@~1.38.0: version "1.38.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" @@ -9150,17 +9195,24 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19: version "2.1.22" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: mime-db "~1.38.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== +mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.0.3, mime@^2.3.1: version "2.4.0" @@ -9318,7 +9370,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.0.0, ms@^2.1.1: +ms@2.1.1, ms@^2.0.0, ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== @@ -9391,10 +9443,10 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== neo-async@^2.5.0: version "2.6.0" @@ -10362,10 +10414,10 @@ parse5@5.1.0, parse5@^5.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" @@ -11228,10 +11280,10 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008" - integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw== +prettier@^1.17.1: + version "1.17.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db" + integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg== pretty-bytes@^5.1.0: version "5.1.0" @@ -11346,13 +11398,13 @@ protoduck@^5.0.0: dependencies: genfun "^5.0.0" -proxy-addr@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" - integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== dependencies: forwarded "~0.1.2" - ipaddr.js "1.8.0" + ipaddr.js "1.9.0" prr@~1.0.1: version "1.0.1" @@ -11444,7 +11496,12 @@ qrcode.react@^0.9.3: prop-types "^15.6.0" qr.js "0.0.0" -qs@6.5.2, qs@~6.5.2: +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== @@ -11513,19 +11570,24 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@1.2.0, range-parser@^1.0.3, range-parser@~1.2.0: +range-parser@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= -raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" - integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== +range-parser@^1.0.3, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" unpipe "1.0.0" rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: @@ -12568,10 +12630,10 @@ semver@~5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" depd "~1.1.2" @@ -12580,12 +12642,12 @@ send@0.16.2: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" + range-parser "~1.2.1" + statuses "~1.5.0" serialize-javascript@^1.4.0: version "1.6.1" @@ -12619,15 +12681,15 @@ serve-index@^1.7.2: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" + parseurl "~1.3.3" + send "0.17.1" serve@^11.0.0: version "11.0.0" @@ -12679,6 +12741,11 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -13088,16 +13155,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -13377,12 +13439,12 @@ tapable@^1.0.0, tapable@^1.1.0: integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA== tar@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= + version "2.2.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== dependencies: block-stream "*" - fstream "^1.0.2" + fstream "^1.0.12" inherits "2" tar@^4, tar@^4.4.3, tar@^4.4.8: @@ -13574,6 +13636,11 @@ toggle-selection@^1.0.6: resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + topo@3.x.x: version "3.0.3" resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c" @@ -13690,13 +13757,13 @@ type-fest@^0.4.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== -type-is@~1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" - mime-types "~2.1.18" + mime-types "~2.1.24" typedarray@^0.0.6: version "0.0.6" @@ -14585,12 +14652,12 @@ yallist@^3.0.0, yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== -yaml@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.5.1.tgz#e8201678064fbcfef6afe4122ef802573b6cade8" - integrity sha512-btfJvMOgVthGZSgHBMrDkLuQu4YxOycw6kwuC67cUEOKJmmNozjIa02eKvuSq7usqqqpwwCvflGTF6JcDvSudw== +yaml@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.6.0.tgz#d8a985cfb26086dd73f91c637f6e6bc909fddd3c" + integrity sha512-iZfse3lwrJRoSlfs/9KQ9iIXxs9++RvBFVzAqbbBiFT+giYtyanevreF9r61ZTbGMgWQBxAua3FzJiniiJXWWw== dependencies: - "@babel/runtime" "^7.4.4" + "@babel/runtime" "^7.4.5" yargs-parser@^10.1.0: version "10.1.0"