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 = (
+
+ );
+
+ const renderMobileMenu = (
+
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
{
+ 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 (
+
+ );
+ }
+}
+
+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 (
+
+ );
+ }
+}
+
+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 ? (
+
+ ) : (
+
+ )}
+
+
+
+ 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 (
+
+ );
+ }
+}
+
+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
+
+ {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"