Restructure the js app (#243)
* prettier * linter * add prettier, and format the code * remove common, merge it with frontend * refactor the app * better lint and code fix * travis for the frontend app * travis build script Signed-off-by: Daniel Maricic <daniel@woss.io> * lint and build * update the README.md Signed-off-by: Daniel Maricic <daniel@woss.io> * change the commands to reflect refactor Signed-off-by: Daniel Maricic <daniel@woss.io> * prettier and tslint are friends Signed-off-by: Daniel Maricic <daniel@woss.io> * code that wasn't linted properly before Signed-off-by: Daniel Maricic <daniel@woss.io> * prettier rc got deleted * workgin on making the travis pass Signed-off-by: Daniel Maricic <daniel@woss.io> * travis build please? Signed-off-by: Daniel Maricic <daniel@woss.io> * update readme.md Signed-off-by: Daniel Maricic <daniel@woss.io> * dockerfile deleted from fronted - out of scope Signed-off-by: Daniel Maricic <daniel@woss.io> * remove Signed-off-by: Daniel Maricic <daniel@woss.io> * tsconfig Signed-off-by: Daniel Maricic <daniel@woss.io> * found the reason why EOL wasn't happening Signed-off-by: Daniel Maricic <daniel@woss.io> * type for the event in the ConnectionInput as suggested * strictnullCheck to true * noImplicitAny * noUnusedParams * AfgHandling * update * fix Location.tsx * Few minor fixes * remove connection input and revert to original * esnext fixes the imports for icons and non default `* as ` * update to the tsconfig.test.json don't use commonjs please * fixed wrong comment for TIMEOUT_BASE * return totem.svg and type decraration of maybe Signed-off-by: Daniel Maricic <daniel@woss.io> Co-authored-by: Will <w.kopp@kigroup.de>
@@ -1,4 +1,8 @@
|
|||||||
[*.{js,ts,tsx,json,css}]
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ backend/target
|
|||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
packages/*/coverage
|
coverage
|
||||||
|
|
||||||
# production
|
# production
|
||||||
packages/*/build
|
build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- yarn
|
- yarn
|
||||||
- yarn check:all
|
- cd frontend && yarn
|
||||||
- yarn build:all
|
- yarn test
|
||||||
- cd backend && cargo test
|
- yarn check
|
||||||
|
- yarn build
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
# Polkadot Telemetry
|
# Polkadot Telemetry
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
To run the backend, you will need `cargo` to build the binary. We recommend using [`rustup`](https://rustup.rs/).
|
To run the backend, you will need `cargo` to build the binary. We recommend using [`rustup`](https://rustup.rs/).
|
||||||
|
|
||||||
To run the frontend make sure to grab the latest stable version of node and install dependencies before doing anything:
|
To run the frontend make sure to grab the latest stable version of node and install dependencies before doing anything:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
nvm install stable
|
nvm install stable
|
||||||
yarn
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Terminal 1 - Backend
|
### Terminal 1 - Backend
|
||||||
|
|
||||||
```
|
```
|
||||||
cd backend
|
cd backend
|
||||||
cargo build --release
|
cargo build --release
|
||||||
@@ -20,35 +22,45 @@ cargo build --release
|
|||||||
By default, telemetry will listen on the local interface only (127.0.0.1) on port 8000. You may change both those values:
|
By default, telemetry will listen on the local interface only (127.0.0.1) on port 8000. You may change both those values:
|
||||||
|
|
||||||
Use another port:
|
Use another port:
|
||||||
|
|
||||||
```
|
```
|
||||||
PORT=8123 telemetry
|
PORT=8123 telemetry
|
||||||
```
|
```
|
||||||
|
|
||||||
You may also change the the listening interface. This is especially required if you are using docker:
|
You may also change the the listening interface. This is especially required if you are using docker:
|
||||||
|
|
||||||
```
|
```
|
||||||
BIND=0.0.0.0 telemetry
|
BIND=0.0.0.0 telemetry
|
||||||
```
|
```
|
||||||
|
|
||||||
### Terminal 2 - Frontend
|
### Terminal 2 - Frontend
|
||||||
```
|
|
||||||
yarn start:frontend
|
```sh
|
||||||
|
cd frontend
|
||||||
|
yarn
|
||||||
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
### Terminal 3 - Node
|
### Terminal 3 - Node
|
||||||
|
|
||||||
Follow up installation instructions from the [Polkadot repo](https://github.com/paritytech/polkadot)
|
Follow up installation instructions from the [Polkadot repo](https://github.com/paritytech/polkadot)
|
||||||
|
|
||||||
```
|
```sh
|
||||||
./target/release/polkadot --dev --telemetry-url ws://localhost:8000/submit
|
./target/release/polkadot --dev --telemetry-url ws://localhost:8000/submit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run via Docker
|
### Run via Docker
|
||||||
|
|
||||||
To run via docker make sure that you have Docker Desktop
|
To run via docker make sure that you have Docker Desktop
|
||||||
- If you dont you can download for you OS here [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
```
|
- If you dont you can download for you OS here [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
|
||||||
|
```sh
|
||||||
docker-compose up --build -d
|
docker-compose up --build -d
|
||||||
```
|
```
|
||||||
- -d stands for detach, if you would like to see logs i recommend using [Kitmatic](https://kitematic.com/) or dont use the -d
|
|
||||||
- --build will build the images and rebuild, but this is not required everytime
|
- -d stands for detach, if you would like to see logs i recommend using [Kitmatic](https://kitematic.com/) or don't use the -d
|
||||||
- If you want to makes UI changes, there is no need to rebuild the image as the files are being copied in via volumes.
|
- --build will build the images and rebuild, but this is not required every time
|
||||||
|
- If you want to makes UI changes, there is no need to rebuild the image as the files are being copied in via volumes.
|
||||||
|
|
||||||
Now navigate to localhost:3000 in your browser to view the app.
|
Now navigate to localhost:3000 in your browser to view the app.
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
"blakejs": "^1.1.0",
|
"blakejs": "^1.1.0",
|
||||||
"husky": "^4.2.3",
|
"husky": "^4.2.3",
|
||||||
"lint-staged": "^10.1.0",
|
"lint-staged": "^10.1.0",
|
||||||
"react": "16.4.0",
|
"react": "^16.13.1",
|
||||||
"react-dom": "16.4.2",
|
"react-dom": "^16.13.1",
|
||||||
"react-measure": "^2.3.0",
|
"react-measure": "^2.3.0",
|
||||||
"react-scripts-ts": "2.17.0",
|
"react-scripts-ts": "2.17.0",
|
||||||
"react-svg": "^4.1.1",
|
"react-svg": "^4.1.1",
|
||||||
@@ -56,10 +56,10 @@
|
|||||||
"jest": "^23.6.0",
|
"jest": "^23.6.0",
|
||||||
"jest-localstorage-mock": "^2.2.0",
|
"jest-localstorage-mock": "^2.2.0",
|
||||||
"mock-socket": "^8.0.2",
|
"mock-socket": "^8.0.2",
|
||||||
|
"prettier": "^2.0.2",
|
||||||
"react-test-renderer": "^16.5.2",
|
"react-test-renderer": "^16.5.2",
|
||||||
"ts-jest": "^23.10.2",
|
"ts-jest": "^23.10.2",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"prettier": "^2.0.2",
|
|
||||||
"tslint-plugin-prettier": "^2.3.0",
|
"tslint-plugin-prettier": "^2.3.0",
|
||||||
"typescript": "^2.9.2"
|
"typescript": "^2.9.2"
|
||||||
},
|
},
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Types } from '@dotstats/common';
|
import { Types } from './common';
|
||||||
import { State, UpdateBound } from './state';
|
import { State, UpdateBound } from './state';
|
||||||
|
|
||||||
// Number of blocks which are kept in memory
|
// Number of blocks which are kept in memory
|
||||||
@@ -21,7 +21,7 @@ export class AfgHandling {
|
|||||||
this.getState().authoritySetId != null &&
|
this.getState().authoritySetId != null &&
|
||||||
authoritySetId !== this.getState().authoritySetId
|
authoritySetId !== this.getState().authoritySetId
|
||||||
) {
|
) {
|
||||||
// the visualization is restarted when we receive a new auhority set
|
// the visualization is restarted when we receive a new authority set
|
||||||
this.updateState({
|
this.updateState({
|
||||||
authoritySetId,
|
authoritySetId,
|
||||||
authorities,
|
authorities,
|
||||||
@@ -113,7 +113,6 @@ export class AfgHandling {
|
|||||||
public receivedPre(
|
public receivedPre(
|
||||||
addr: Types.Address,
|
addr: Types.Address,
|
||||||
height: Types.BlockNumber,
|
height: Types.BlockNumber,
|
||||||
hash: Types.BlockHash,
|
|
||||||
voter: Types.Address,
|
voter: Types.Address,
|
||||||
what: string
|
what: string
|
||||||
) {
|
) {
|
||||||
@@ -136,7 +135,7 @@ export class AfgHandling {
|
|||||||
// a vote on all preceding blocks. This function marks
|
// a vote on all preceding blocks. This function marks
|
||||||
// the preceding blocks as implicitly voted on and stores
|
// the preceding blocks as implicitly voted on and stores
|
||||||
// a pointer to the block which contains the explicit vote.
|
// a pointer to the block which contains the explicit vote.
|
||||||
const op = (i: Types.BlockNumber, index: number): boolean => {
|
const op = (index: number): boolean => {
|
||||||
const consensusDetail = state.consensusInfo[index][1][addr][voter];
|
const consensusDetail = state.consensusInfo[index][1][addr][voter];
|
||||||
if (
|
if (
|
||||||
what === 'prevote' &&
|
what === 'prevote' &&
|
||||||
@@ -192,7 +191,7 @@ export class AfgHandling {
|
|||||||
|
|
||||||
const item: Types.ConsensusItem = [height, consensusView];
|
const item: Types.ConsensusItem = [height, consensusView];
|
||||||
const insertPos = consensusInfo.findIndex(
|
const insertPos = consensusInfo.findIndex(
|
||||||
([elHeight, elView]) => elHeight < height
|
([elHeight]) => elHeight < height
|
||||||
);
|
);
|
||||||
if (insertPos >= 0) {
|
if (insertPos >= 0) {
|
||||||
consensusInfo.splice(insertPos, 0, item);
|
consensusInfo.splice(insertPos, 0, item);
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
.App-no-telemetry {
|
.App-no-telemetry {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
/* height: 100vh; */
|
||||||
line-height: 80vh;
|
/* line-height: 80vh; */
|
||||||
font-size: 56px;
|
font-size: 56px;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, SortedCollection, Maybe, Compare } from '@dotstats/common';
|
import { Types, SortedCollection, Maybe, Compare } from './common';
|
||||||
import { AllChains, Chains, Chain, Ago, OfflineIndicator } from './components';
|
import { AllChains, Chains, Chain, Ago, OfflineIndicator } from './components';
|
||||||
import { Row, Column } from './components/List';
|
import { Row, Column } from './components/List';
|
||||||
import { Connection } from './Connection';
|
import { Connection } from './Connection';
|
||||||
@@ -189,7 +189,7 @@ export default class App extends React.Component<{}, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHashChange = (event: Event) => {
|
private onHashChange = () => {
|
||||||
const { tab = '' } = getHashData();
|
const { tab = '' } = getHashData();
|
||||||
|
|
||||||
this.setState({ tab });
|
this.setState({ tab });
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { VERSION, timestamp, FeedMessage, Types, Maybe, sleep } from './common';
|
||||||
VERSION,
|
|
||||||
timestamp,
|
|
||||||
FeedMessage,
|
|
||||||
Types,
|
|
||||||
Maybe,
|
|
||||||
sleep,
|
|
||||||
} from '@dotstats/common';
|
|
||||||
import {
|
import {
|
||||||
State,
|
State,
|
||||||
Update,
|
Update,
|
||||||
@@ -17,10 +10,9 @@ import {
|
|||||||
import { PersistentSet } from './persist';
|
import { PersistentSet } from './persist';
|
||||||
import { getHashData, setHashData } from './utils';
|
import { getHashData, setHashData } from './utils';
|
||||||
import { AfgHandling } from './AfgHandling';
|
import { AfgHandling } from './AfgHandling';
|
||||||
import { VIS_AUTHORITIES_LIMIT } from '../../frontend/src/components/Consensus';
|
import { VIS_AUTHORITIES_LIMIT } from './components/Consensus';
|
||||||
import { Column } from './components/List';
|
import { Column } from './components/List';
|
||||||
|
import { ACTIONS } from './common/feed';
|
||||||
const { Actions } = FeedMessage;
|
|
||||||
|
|
||||||
const TIMEOUT_BASE = (1000 * 5) as Types.Milliseconds; // 5 seconds
|
const TIMEOUT_BASE = (1000 * 5) as Types.Milliseconds; // 5 seconds
|
||||||
const TIMEOUT_MAX = (1000 * 60 * 5) as Types.Milliseconds; // 5 minutes
|
const TIMEOUT_MAX = (1000 * 60 * 5) as Types.Milliseconds; // 5 minutes
|
||||||
@@ -40,8 +32,6 @@ export class Connection {
|
|||||||
? `wss://${window.location.hostname}/feed/`
|
? `wss://${window.location.hostname}/feed/`
|
||||||
: `ws://127.0.0.1:8000/feed`;
|
: `ws://127.0.0.1:8000/feed`;
|
||||||
|
|
||||||
// private static readonly address = 'wss://telemetry.polkadot.io/feed/';
|
|
||||||
|
|
||||||
private static async socket(): Promise<WebSocket> {
|
private static async socket(): Promise<WebSocket> {
|
||||||
let socket = await Connection.trySocket();
|
let socket = await Connection.trySocket();
|
||||||
let timeout = TIMEOUT_BASE;
|
let timeout = TIMEOUT_BASE;
|
||||||
@@ -158,7 +148,7 @@ export class Connection {
|
|||||||
|
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
switch (message.action) {
|
switch (message.action) {
|
||||||
case Actions.FeedVersion: {
|
case ACTIONS.FeedVersion: {
|
||||||
if (message.payload !== VERSION) {
|
if (message.payload !== VERSION) {
|
||||||
this.state = this.update({ status: 'upgrade-requested' });
|
this.state = this.update({ status: 'upgrade-requested' });
|
||||||
this.clean();
|
this.clean();
|
||||||
@@ -172,7 +162,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.BestBlock: {
|
case ACTIONS.BestBlock: {
|
||||||
const [best, blockTimestamp, blockAverage] = message.payload;
|
const [best, blockTimestamp, blockAverage] = message.payload;
|
||||||
|
|
||||||
nodes.mutEach((node) => node.newBestBlock());
|
nodes.mutEach((node) => node.newBestBlock());
|
||||||
@@ -182,7 +172,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.BestFinalized: {
|
case ACTIONS.BestFinalized: {
|
||||||
const [finalized /*, hash */] = message.payload;
|
const [finalized /*, hash */] = message.payload;
|
||||||
|
|
||||||
this.state = this.update({ finalized });
|
this.state = this.update({ finalized });
|
||||||
@@ -190,7 +180,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AddedNode: {
|
case ACTIONS.AddedNode: {
|
||||||
const [
|
const [
|
||||||
id,
|
id,
|
||||||
nodeDetails,
|
nodeDetails,
|
||||||
@@ -219,7 +209,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.RemovedNode: {
|
case ACTIONS.RemovedNode: {
|
||||||
const id = message.payload;
|
const id = message.payload;
|
||||||
|
|
||||||
nodes.remove(id);
|
nodes.remove(id);
|
||||||
@@ -227,7 +217,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.StaleNode: {
|
case ACTIONS.StaleNode: {
|
||||||
const id = message.payload;
|
const id = message.payload;
|
||||||
|
|
||||||
nodes.mutAndSort(id, (node) => node.setStale(true));
|
nodes.mutAndSort(id, (node) => node.setStale(true));
|
||||||
@@ -235,7 +225,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.LocatedNode: {
|
case ACTIONS.LocatedNode: {
|
||||||
const [id, lat, lon, city] = message.payload;
|
const [id, lat, lon, city] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndMaybeSort(
|
nodes.mutAndMaybeSort(
|
||||||
@@ -247,7 +237,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.ImportedBlock: {
|
case ACTIONS.ImportedBlock: {
|
||||||
const [id, blockDetails] = message.payload;
|
const [id, blockDetails] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndSort(id, (node) => node.updateBlock(blockDetails));
|
nodes.mutAndSort(id, (node) => node.updateBlock(blockDetails));
|
||||||
@@ -255,7 +245,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.FinalizedBlock: {
|
case ACTIONS.FinalizedBlock: {
|
||||||
const [id, height, hash] = message.payload;
|
const [id, height, hash] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndMaybeSort(
|
nodes.mutAndMaybeSort(
|
||||||
@@ -268,7 +258,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.NodeStats: {
|
case ACTIONS.NodeStats: {
|
||||||
const [id, nodeStats] = message.payload;
|
const [id, nodeStats] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndMaybeSort(
|
nodes.mutAndMaybeSort(
|
||||||
@@ -280,7 +270,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.NodeHardware: {
|
case ACTIONS.NodeHardware: {
|
||||||
const [id, nodeHardware] = message.payload;
|
const [id, nodeHardware] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndMaybeSort(
|
nodes.mutAndMaybeSort(
|
||||||
@@ -295,7 +285,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.NodeIO: {
|
case ACTIONS.NodeIO: {
|
||||||
const [id, nodeIO] = message.payload;
|
const [id, nodeIO] = message.payload;
|
||||||
|
|
||||||
nodes.mutAndMaybeSort(
|
nodes.mutAndMaybeSort(
|
||||||
@@ -310,7 +300,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.TimeSync: {
|
case ACTIONS.TimeSync: {
|
||||||
this.state = this.update({
|
this.state = this.update({
|
||||||
timeDiff: (timestamp() - message.payload) as Types.Milliseconds,
|
timeDiff: (timestamp() - message.payload) as Types.Milliseconds,
|
||||||
});
|
});
|
||||||
@@ -318,7 +308,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AddedChain: {
|
case ACTIONS.AddedChain: {
|
||||||
const [label, nodeCount] = message.payload;
|
const [label, nodeCount] = message.payload;
|
||||||
const chain = chains.get(label);
|
const chain = chains.get(label);
|
||||||
|
|
||||||
@@ -333,7 +323,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.RemovedChain: {
|
case ACTIONS.RemovedChain: {
|
||||||
chains.delete(message.payload);
|
chains.delete(message.payload);
|
||||||
|
|
||||||
if (this.state.subscribed === message.payload) {
|
if (this.state.subscribed === message.payload) {
|
||||||
@@ -345,7 +335,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.SubscribedTo: {
|
case ACTIONS.SubscribedTo: {
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
|
|
||||||
this.state = this.update({ subscribed: message.payload, nodes });
|
this.state = this.update({ subscribed: message.payload, nodes });
|
||||||
@@ -353,7 +343,7 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.UnsubscribedFrom: {
|
case ACTIONS.UnsubscribedFrom: {
|
||||||
if (this.state.subscribed === message.payload) {
|
if (this.state.subscribed === message.payload) {
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
|
|
||||||
@@ -363,13 +353,13 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.Pong: {
|
case ACTIONS.Pong: {
|
||||||
this.pong(Number(message.payload));
|
this.pong(Number(message.payload));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AfgFinalized: {
|
case ACTIONS.AfgFinalized: {
|
||||||
const [nodeAddress, finalizedNumber, finalizedHash] = message.payload;
|
const [nodeAddress, finalizedNumber, finalizedHash] = message.payload;
|
||||||
const no = parseInt(String(finalizedNumber), 10) as Types.BlockNumber;
|
const no = parseInt(String(finalizedNumber), 10) as Types.BlockNumber;
|
||||||
afg.receivedFinalized(nodeAddress, no, finalizedHash);
|
afg.receivedFinalized(nodeAddress, no, finalizedHash);
|
||||||
@@ -377,23 +367,23 @@ export class Connection {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AfgReceivedPrevote: {
|
case ACTIONS.AfgReceivedPrevote: {
|
||||||
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
|
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
|
||||||
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
|
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
|
||||||
afg.receivedPre(nodeAddress, no, blockHash, voter, 'prevote');
|
afg.receivedPre(nodeAddress, no, voter, 'prevote');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AfgReceivedPrecommit: {
|
case ACTIONS.AfgReceivedPrecommit: {
|
||||||
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
|
const [nodeAddress, blockNumber, blockHash, voter] = message.payload;
|
||||||
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
|
const no = parseInt(String(blockNumber), 10) as Types.BlockNumber;
|
||||||
afg.receivedPre(nodeAddress, no, blockHash, voter, 'precommit');
|
afg.receivedPre(nodeAddress, no, voter, 'precommit');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Actions.AfgAuthoritySet: {
|
case ACTIONS.AfgAuthoritySet: {
|
||||||
const [authoritySetId, authorities] = message.payload;
|
const [authoritySetId, authorities] = message.payload;
|
||||||
afg.receivedAuthoritySet(authoritySetId, authorities);
|
afg.receivedAuthoritySet(authoritySetId, authorities);
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 324 KiB |
@@ -12,7 +12,11 @@ export type Compare<T> = (a: T, b: T) => number;
|
|||||||
*
|
*
|
||||||
* @return {number} insertion index
|
* @return {number} insertion index
|
||||||
*/
|
*/
|
||||||
export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): number {
|
export function sortedInsert<T>(
|
||||||
|
item: T,
|
||||||
|
into: Array<T>,
|
||||||
|
compare: Compare<T>
|
||||||
|
): number {
|
||||||
if (into.length === 0) {
|
if (into.length === 0) {
|
||||||
into.push(item);
|
into.push(item);
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): n
|
|||||||
let max = into.length - 1;
|
let max = into.length - 1;
|
||||||
|
|
||||||
while (min !== max) {
|
while (min !== max) {
|
||||||
const guess = (min + max) / 2 | 0;
|
const guess = ((min + max) / 2) | 0;
|
||||||
|
|
||||||
if (compare(item, into[guess]) < 0) {
|
if (compare(item, into[guess]) < 0) {
|
||||||
max = Math.max(min, guess - 1);
|
max = Math.max(min, guess - 1);
|
||||||
@@ -50,7 +54,11 @@ export function sortedInsert<T>(item: T, into: Array<T>, compare: Compare<T>): n
|
|||||||
*
|
*
|
||||||
* @return {number} index of the element, `-1` if not found
|
* @return {number} index of the element, `-1` if not found
|
||||||
*/
|
*/
|
||||||
export function sortedIndexOf<T>(item: T, within: Array<T>, compare: Compare<T>): number {
|
export function sortedIndexOf<T>(
|
||||||
|
item: T,
|
||||||
|
within: Array<T>,
|
||||||
|
compare: Compare<T>
|
||||||
|
): number {
|
||||||
if (within.length === 0) {
|
if (within.length === 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -59,7 +67,7 @@ export function sortedIndexOf<T>(item: T, within: Array<T>, compare: Compare<T>)
|
|||||||
let max = within.length - 1;
|
let max = within.length - 1;
|
||||||
|
|
||||||
while (min !== max) {
|
while (min !== max) {
|
||||||
let guess = (min + max) / 2 | 0;
|
const guess = ((min + max) / 2) | 0;
|
||||||
const other = within[guess];
|
const other = within[guess];
|
||||||
|
|
||||||
if (item === other) {
|
if (item === other) {
|
||||||
@@ -113,7 +121,9 @@ export class SortedCollection<Item extends { id: number }> {
|
|||||||
public add(item: Item) {
|
public add(item: Item) {
|
||||||
if (this.map.length <= item.id) {
|
if (this.map.length <= item.id) {
|
||||||
// Grow map if item.id would be out of scope
|
// Grow map if item.id would be out of scope
|
||||||
this.map = this.map.concat(Array<Maybe<Item>>(Math.max(10, 1 + item.id - this.map.length)));
|
this.map = this.map.concat(
|
||||||
|
Array<Maybe<Item>>(Math.max(10, 1 + item.id - this.map.length))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old item if overriding
|
// Remove old item if overriding
|
||||||
@@ -178,7 +188,11 @@ export class SortedCollection<Item extends { id: number }> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutAndMaybeSort(id: number, mutator: (item: Item) => void, sort: boolean) {
|
public mutAndMaybeSort(
|
||||||
|
id: number,
|
||||||
|
mutator: (item: Item) => void,
|
||||||
|
sort: boolean
|
||||||
|
) {
|
||||||
if (sort) {
|
if (sort) {
|
||||||
this.mutAndSort(id, mutator);
|
this.mutAndSort(id, mutator);
|
||||||
} else {
|
} else {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Opaque, Maybe } from './helpers';
|
import { Maybe } from './helpers';
|
||||||
import { stringify, parse, Stringified } from './stringify';
|
import { stringify, parse, Stringified } from './stringify';
|
||||||
import {
|
import {
|
||||||
FeedVersion,
|
FeedVersion,
|
||||||
@@ -22,32 +22,32 @@ import {
|
|||||||
AuthoritySetInfo,
|
AuthoritySetInfo,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export const Actions = {
|
export const ACTIONS = {
|
||||||
FeedVersion : 0x00 as 0x00,
|
FeedVersion: 0x00 as 0x00,
|
||||||
BestBlock : 0x01 as 0x01,
|
BestBlock: 0x01 as 0x01,
|
||||||
BestFinalized : 0x02 as 0x02,
|
BestFinalized: 0x02 as 0x02,
|
||||||
AddedNode : 0x03 as 0x03,
|
AddedNode: 0x03 as 0x03,
|
||||||
RemovedNode : 0x04 as 0x04,
|
RemovedNode: 0x04 as 0x04,
|
||||||
LocatedNode : 0x05 as 0x05,
|
LocatedNode: 0x05 as 0x05,
|
||||||
ImportedBlock : 0x06 as 0x06,
|
ImportedBlock: 0x06 as 0x06,
|
||||||
FinalizedBlock : 0x07 as 0x07,
|
FinalizedBlock: 0x07 as 0x07,
|
||||||
NodeStats : 0x08 as 0x08,
|
NodeStats: 0x08 as 0x08,
|
||||||
NodeHardware : 0x09 as 0x09,
|
NodeHardware: 0x09 as 0x09,
|
||||||
TimeSync : 0x0A as 0x0A,
|
TimeSync: 0x0a as 0x0a,
|
||||||
AddedChain : 0x0B as 0x0B,
|
AddedChain: 0x0b as 0x0b,
|
||||||
RemovedChain : 0x0C as 0x0C,
|
RemovedChain: 0x0c as 0x0c,
|
||||||
SubscribedTo : 0x0D as 0x0D,
|
SubscribedTo: 0x0d as 0x0d,
|
||||||
UnsubscribedFrom : 0x0E as 0x0E,
|
UnsubscribedFrom: 0x0e as 0x0e,
|
||||||
Pong : 0x0F as 0x0F,
|
Pong: 0x0f as 0x0f,
|
||||||
AfgFinalized : 0x10 as 0x10,
|
AfgFinalized: 0x10 as 0x10,
|
||||||
AfgReceivedPrevote : 0x11 as 0x11,
|
AfgReceivedPrevote: 0x11 as 0x11,
|
||||||
AfgReceivedPrecommit : 0x12 as 0x12,
|
AfgReceivedPrecommit: 0x12 as 0x12,
|
||||||
AfgAuthoritySet : 0x13 as 0x13,
|
AfgAuthoritySet: 0x13 as 0x13,
|
||||||
StaleNode : 0x14 as 0x14,
|
StaleNode: 0x14 as 0x14,
|
||||||
NodeIO : 0x15 as 0x15,
|
NodeIO: 0x15 as 0x15,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action = typeof Actions[keyof typeof Actions];
|
export type Action = typeof ACTIONS[keyof typeof ACTIONS];
|
||||||
export type Payload = Message['payload'];
|
export type Payload = Message['payload'];
|
||||||
|
|
||||||
export namespace Variants {
|
export namespace Variants {
|
||||||
@@ -56,112 +56,121 @@ export namespace Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FeedVersionMessage extends MessageBase {
|
export interface FeedVersionMessage extends MessageBase {
|
||||||
action: typeof Actions.FeedVersion;
|
action: typeof ACTIONS.FeedVersion;
|
||||||
payload: FeedVersion;
|
payload: FeedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BestBlockMessage extends MessageBase {
|
export interface BestBlockMessage extends MessageBase {
|
||||||
action: typeof Actions.BestBlock;
|
action: typeof ACTIONS.BestBlock;
|
||||||
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
|
payload: [BlockNumber, Timestamp, Maybe<Milliseconds>];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BestFinalizedBlockMessage extends MessageBase {
|
export interface BestFinalizedBlockMessage extends MessageBase {
|
||||||
action: typeof Actions.BestFinalized;
|
action: typeof ACTIONS.BestFinalized;
|
||||||
payload: [BlockNumber, BlockHash];
|
payload: [BlockNumber, BlockHash];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddedNodeMessage extends MessageBase {
|
export interface AddedNodeMessage extends MessageBase {
|
||||||
action: typeof Actions.AddedNode;
|
action: typeof ACTIONS.AddedNode;
|
||||||
payload: [NodeId, NodeDetails, NodeStats, NodeIO, NodeHardware, BlockDetails, Maybe<NodeLocation>, Timestamp];
|
payload: [
|
||||||
|
NodeId,
|
||||||
|
NodeDetails,
|
||||||
|
NodeStats,
|
||||||
|
NodeIO,
|
||||||
|
NodeHardware,
|
||||||
|
BlockDetails,
|
||||||
|
Maybe<NodeLocation>,
|
||||||
|
Timestamp
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemovedNodeMessage extends MessageBase {
|
export interface RemovedNodeMessage extends MessageBase {
|
||||||
action: typeof Actions.RemovedNode;
|
action: typeof ACTIONS.RemovedNode;
|
||||||
payload: NodeId;
|
payload: NodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocatedNodeMessage extends MessageBase {
|
export interface LocatedNodeMessage extends MessageBase {
|
||||||
action: typeof Actions.LocatedNode;
|
action: typeof ACTIONS.LocatedNode;
|
||||||
payload: [NodeId, Latitude, Longitude, City];
|
payload: [NodeId, Latitude, Longitude, City];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImportedBlockMessage extends MessageBase {
|
export interface ImportedBlockMessage extends MessageBase {
|
||||||
action: typeof Actions.ImportedBlock;
|
action: typeof ACTIONS.ImportedBlock;
|
||||||
payload: [NodeId, BlockDetails];
|
payload: [NodeId, BlockDetails];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FinalizedBlockMessage extends MessageBase {
|
export interface FinalizedBlockMessage extends MessageBase {
|
||||||
action: typeof Actions.FinalizedBlock;
|
action: typeof ACTIONS.FinalizedBlock;
|
||||||
payload: [NodeId, BlockNumber, BlockHash];
|
payload: [NodeId, BlockNumber, BlockHash];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeStatsMessage extends MessageBase {
|
export interface NodeStatsMessage extends MessageBase {
|
||||||
action: typeof Actions.NodeStats;
|
action: typeof ACTIONS.NodeStats;
|
||||||
payload: [NodeId, NodeStats];
|
payload: [NodeId, NodeStats];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeHardwareMessage extends MessageBase {
|
export interface NodeHardwareMessage extends MessageBase {
|
||||||
action: typeof Actions.NodeHardware;
|
action: typeof ACTIONS.NodeHardware;
|
||||||
payload: [NodeId, NodeHardware];
|
payload: [NodeId, NodeHardware];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeIOMessage extends MessageBase {
|
export interface NodeIOMessage extends MessageBase {
|
||||||
action: typeof Actions.NodeIO;
|
action: typeof ACTIONS.NodeIO;
|
||||||
payload: [NodeId, NodeIO];
|
payload: [NodeId, NodeIO];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimeSyncMessage extends MessageBase {
|
export interface TimeSyncMessage extends MessageBase {
|
||||||
action: typeof Actions.TimeSync;
|
action: typeof ACTIONS.TimeSync;
|
||||||
payload: Timestamp;
|
payload: Timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddedChainMessage extends MessageBase {
|
export interface AddedChainMessage extends MessageBase {
|
||||||
action: typeof Actions.AddedChain;
|
action: typeof ACTIONS.AddedChain;
|
||||||
payload: [ChainLabel, NodeCount];
|
payload: [ChainLabel, NodeCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemovedChainMessage extends MessageBase {
|
export interface RemovedChainMessage extends MessageBase {
|
||||||
action: typeof Actions.RemovedChain;
|
action: typeof ACTIONS.RemovedChain;
|
||||||
payload: ChainLabel;
|
payload: ChainLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SubscribedToMessage extends MessageBase {
|
export interface SubscribedToMessage extends MessageBase {
|
||||||
action: typeof Actions.SubscribedTo;
|
action: typeof ACTIONS.SubscribedTo;
|
||||||
payload: ChainLabel;
|
payload: ChainLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UnsubscribedFromMessage extends MessageBase {
|
export interface UnsubscribedFromMessage extends MessageBase {
|
||||||
action: typeof Actions.UnsubscribedFrom;
|
action: typeof ACTIONS.UnsubscribedFrom;
|
||||||
payload: ChainLabel;
|
payload: ChainLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PongMessage extends MessageBase {
|
export interface PongMessage extends MessageBase {
|
||||||
action: typeof Actions.Pong;
|
action: typeof ACTIONS.Pong;
|
||||||
payload: string; // just echo whatever `ping` sent
|
payload: string; // just echo whatever `ping` sent
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AfgFinalizedMessage extends MessageBase {
|
export interface AfgFinalizedMessage extends MessageBase {
|
||||||
action: typeof Actions.AfgFinalized;
|
action: typeof ACTIONS.AfgFinalized;
|
||||||
payload: [Address, BlockNumber, BlockHash];
|
payload: [Address, BlockNumber, BlockHash];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AfgAuthoritySet extends MessageBase {
|
export interface AfgAuthoritySet extends MessageBase {
|
||||||
action: typeof Actions.AfgAuthoritySet;
|
action: typeof ACTIONS.AfgAuthoritySet;
|
||||||
payload: AuthoritySetInfo;
|
payload: AuthoritySetInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AfgReceivedPrecommit extends MessageBase {
|
export interface AfgReceivedPrecommit extends MessageBase {
|
||||||
action: typeof Actions.AfgReceivedPrecommit;
|
action: typeof ACTIONS.AfgReceivedPrecommit;
|
||||||
payload: [Address, BlockNumber, BlockHash, Address];
|
payload: [Address, BlockNumber, BlockHash, Address];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AfgReceivedPrevote extends MessageBase {
|
export interface AfgReceivedPrevote extends MessageBase {
|
||||||
action: typeof Actions.AfgReceivedPrevote;
|
action: typeof ACTIONS.AfgReceivedPrevote;
|
||||||
payload: [Address, BlockNumber, BlockHash, Address];
|
payload: [Address, BlockNumber, BlockHash, Address];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StaleNodeMessage extends MessageBase {
|
export interface StaleNodeMessage extends MessageBase {
|
||||||
action: typeof Actions.StaleNode;
|
action: typeof ACTIONS.StaleNode;
|
||||||
payload: NodeId;
|
payload: NodeId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +203,7 @@ export type Message =
|
|||||||
* Data type to be sent to the feed. Passing through strings means we can only serialize once,
|
* Data type to be sent to the feed. Passing through strings means we can only serialize once,
|
||||||
* no matter how many feed clients are listening in.
|
* no matter how many feed clients are listening in.
|
||||||
*/
|
*/
|
||||||
export interface SquashedMessages extends Array<Action | Payload> {};
|
export interface SquashedMessages extends Array<Action | Payload> {}
|
||||||
export type Data = Stringified<SquashedMessages>;
|
export type Data = Stringified<SquashedMessages>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +222,7 @@ export function serialize(messages: Array<Message>): Data {
|
|||||||
|
|
||||||
squashed[index++] = action;
|
squashed[index++] = action;
|
||||||
squashed[index++] = payload;
|
squashed[index++] = payload;
|
||||||
})
|
});
|
||||||
|
|
||||||
return stringify(squashed);
|
return stringify(squashed);
|
||||||
}
|
}
|
||||||
@@ -231,7 +240,7 @@ export function deserialize(data: Data): Array<Message> {
|
|||||||
const messages = new Array<Message>(json.length / 2);
|
const messages = new Array<Message>(json.length / 2);
|
||||||
|
|
||||||
for (const index of messages.keys()) {
|
for (const index of messages.keys()) {
|
||||||
const [ action, payload ] = json.slice(index * 2);
|
const [action, payload] = json.slice(index * 2);
|
||||||
|
|
||||||
messages[index] = { action, payload } as Message;
|
messages[index] = { action, payload } as Message;
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import { Milliseconds, Timestamp } from './types';
|
|||||||
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
||||||
* the compiler.
|
* the compiler.
|
||||||
*/
|
*/
|
||||||
export abstract class PhantomData<P> { public __PHANTOM__: P }
|
export abstract class PhantomData<P> {
|
||||||
|
public __PHANTOM__: P;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opaque type, similar to `opaque type` in Flow, or new types in Rust/C.
|
* Opaque type, similar to `opaque type` in Flow, or new types in Rust/C.
|
||||||
@@ -109,6 +111,8 @@ export class NumStats<T extends number> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private nonEmpty(): Readonly<Array<number>> {
|
private nonEmpty(): Readonly<Array<number>> {
|
||||||
return this.index < this.history ? this.stack.slice(0, this.index) : this.stack;
|
return this.index < this.history
|
||||||
|
? this.stack.slice(0, this.index)
|
||||||
|
: this.stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ export type Id<T> = Opaque<number, T>;
|
|||||||
* Higher order function producing new auto-incremented `Id`s.
|
* Higher order function producing new auto-incremented `Id`s.
|
||||||
*/
|
*/
|
||||||
export function idGenerator<I extends Id<any>>(): () => I {
|
export function idGenerator<I extends Id<any>>(): () => I {
|
||||||
let current = 0;
|
let current = 0;
|
||||||
|
|
||||||
return () => current++ as I;
|
return () => current++ as I;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
export function* map<T, U>(
|
||||||
|
iter: IterableIterator<T>,
|
||||||
|
fn: (item: T) => U
|
||||||
|
): IterableIterator<U> {
|
||||||
|
for (const item of iter) {
|
||||||
|
yield fn(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* chain<T>(
|
||||||
|
a: IterableIterator<T>,
|
||||||
|
b: IterableIterator<T>
|
||||||
|
): IterableIterator<T> {
|
||||||
|
yield* a;
|
||||||
|
yield* b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* zip<T, U>(
|
||||||
|
a: IterableIterator<T>,
|
||||||
|
b: IterableIterator<U>
|
||||||
|
): IterableIterator<[T, U]> {
|
||||||
|
let itemA = a.next();
|
||||||
|
let itemB = b.next();
|
||||||
|
|
||||||
|
while (!itemA.done && !itemB.done) {
|
||||||
|
yield [itemA.value, itemB.value];
|
||||||
|
|
||||||
|
itemA = a.next();
|
||||||
|
itemB = b.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* take<T>(
|
||||||
|
iter: IterableIterator<T>,
|
||||||
|
n: number
|
||||||
|
): IterableIterator<T> {
|
||||||
|
for (const item of iter) {
|
||||||
|
if (n-- === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skip<T>(
|
||||||
|
iter: IterableIterator<T>,
|
||||||
|
n: number
|
||||||
|
): IterableIterator<T> {
|
||||||
|
while (n-- !== 0 && !iter.next().done) {}
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reduce<T, R>(
|
||||||
|
iter: IterableIterator<T>,
|
||||||
|
fn: (accu: R, item: T) => R,
|
||||||
|
accumulator: R
|
||||||
|
): R {
|
||||||
|
for (const item of iter) {
|
||||||
|
accumulator = fn(accumulator, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function join(
|
||||||
|
iter: IterableIterator<{ toString: () => string }>,
|
||||||
|
glue: string
|
||||||
|
): string {
|
||||||
|
const first = iter.next();
|
||||||
|
|
||||||
|
if (first.done) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = first.value.toString();
|
||||||
|
|
||||||
|
for (const item of iter) {
|
||||||
|
result += glue + item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export abstract class Stringified<T> {
|
||||||
|
public __PHANTOM__: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parse = (JSON.parse as any) as <T>(val: Stringified<T>) => T;
|
||||||
|
export const stringify = (JSON.stringify as any) as <T>(
|
||||||
|
val: T
|
||||||
|
) => Stringified<T>;
|
||||||
@@ -27,26 +27,55 @@ export type BytesPerSecond = Opaque<number, 'BytesPerSecond'>;
|
|||||||
export type NetworkId = Opaque<string, 'NetworkId'>;
|
export type NetworkId = Opaque<string, 'NetworkId'>;
|
||||||
export type NetworkState = Opaque<string | object, 'NetworkState'>;
|
export type NetworkState = Opaque<string | object, 'NetworkState'>;
|
||||||
|
|
||||||
export type BlockDetails = [BlockNumber, BlockHash, Milliseconds, Timestamp, Maybe<PropagationTime>];
|
export type BlockDetails = [
|
||||||
export type NodeDetails = [NodeName, NodeImplementation, NodeVersion, Maybe<Address>, Maybe<NetworkId>];
|
BlockNumber,
|
||||||
|
BlockHash,
|
||||||
|
Milliseconds,
|
||||||
|
Timestamp,
|
||||||
|
Maybe<PropagationTime>
|
||||||
|
];
|
||||||
|
export type NodeDetails = [
|
||||||
|
NodeName,
|
||||||
|
NodeImplementation,
|
||||||
|
NodeVersion,
|
||||||
|
Maybe<Address>,
|
||||||
|
Maybe<NetworkId>
|
||||||
|
];
|
||||||
export type NodeStats = [PeerCount, TransactionCount];
|
export type NodeStats = [PeerCount, TransactionCount];
|
||||||
export type NodeIO = [Array<Bytes>, Array<Bytes>, Array<BytesPerSecond>, Array<BytesPerSecond>];
|
export type NodeIO = [
|
||||||
export type NodeHardware = [Array<MemoryUse>, Array<CPUUse>, Array<BytesPerSecond>, Array<BytesPerSecond>, Array<Timestamp>];
|
Array<Bytes>,
|
||||||
|
Array<Bytes>,
|
||||||
|
Array<BytesPerSecond>,
|
||||||
|
Array<BytesPerSecond>
|
||||||
|
];
|
||||||
|
export type NodeHardware = [
|
||||||
|
Array<MemoryUse>,
|
||||||
|
Array<CPUUse>,
|
||||||
|
Array<BytesPerSecond>,
|
||||||
|
Array<BytesPerSecond>,
|
||||||
|
Array<Timestamp>
|
||||||
|
];
|
||||||
export type NodeLocation = [Latitude, Longitude, City];
|
export type NodeLocation = [Latitude, Longitude, City];
|
||||||
|
|
||||||
export declare type Authority = {
|
export interface Authority {
|
||||||
Address: Address,
|
Address: Address;
|
||||||
NodeId: Maybe<NodeId>,
|
NodeId: Maybe<NodeId>;
|
||||||
Name: Maybe<NodeName>,
|
Name: Maybe<NodeName>;
|
||||||
};
|
}
|
||||||
export declare type Authorities = Array<Address>;
|
export declare type Authorities = Array<Address>;
|
||||||
export declare type AuthoritySetId = Opaque<number, 'AuthoritySetId'>;
|
export declare type AuthoritySetId = Opaque<number, 'AuthoritySetId'>;
|
||||||
export declare type AuthoritySetInfo = [AuthoritySetId, Authorities, Address, BlockNumber, BlockHash];
|
export declare type AuthoritySetInfo = [
|
||||||
|
AuthoritySetId,
|
||||||
|
Authorities,
|
||||||
|
Address,
|
||||||
|
BlockNumber,
|
||||||
|
BlockHash
|
||||||
|
];
|
||||||
export declare type ConsensusItem = [BlockNumber, ConsensusView];
|
export declare type ConsensusItem = [BlockNumber, ConsensusView];
|
||||||
export declare type ConsensusInfo = Array<ConsensusItem>;
|
export declare type ConsensusInfo = Array<ConsensusItem>;
|
||||||
export declare type ConsensusView = Map<Address, ConsensusState>;
|
export declare type ConsensusView = Map<Address, ConsensusState>;
|
||||||
export declare type ConsensusState = Map<Address, ConsensusDetail>;
|
export declare type ConsensusState = Map<Address, ConsensusDetail>;
|
||||||
export declare type ConsensusDetail = {
|
export interface ConsensusDetail {
|
||||||
Precommit: Precommit;
|
Precommit: Precommit;
|
||||||
ImplicitPrecommit: ImplicitPrecommit;
|
ImplicitPrecommit: ImplicitPrecommit;
|
||||||
Prevote: Prevote;
|
Prevote: Prevote;
|
||||||
@@ -56,7 +85,7 @@ export declare type ConsensusDetail = {
|
|||||||
ImplicitFinalized: Finalized;
|
ImplicitFinalized: Finalized;
|
||||||
FinalizedHash: BlockHash;
|
FinalizedHash: BlockHash;
|
||||||
FinalizedHeight: BlockNumber;
|
FinalizedHeight: BlockNumber;
|
||||||
};
|
}
|
||||||
export declare type Precommit = Opaque<boolean, 'Precommit'>;
|
export declare type Precommit = Opaque<boolean, 'Precommit'>;
|
||||||
export declare type Prevote = Opaque<boolean, 'Prevote'>;
|
export declare type Prevote = Opaque<boolean, 'Prevote'>;
|
||||||
export declare type Finalized = Opaque<boolean, 'Finalized'>;
|
export declare type Finalized = Opaque<boolean, 'Finalized'>;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import './Tile.css';
|
import './Tile.css';
|
||||||
import { timestamp, Types } from '@dotstats/common';
|
import { timestamp, Types } from '../common';
|
||||||
|
|
||||||
export namespace Ago {
|
export namespace Ago {
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Connection } from '../Connection';
|
import { Connection } from '../Connection';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../common';
|
||||||
import { ChainData } from '../state';
|
import { ChainData } from '../state';
|
||||||
|
|
||||||
import './AllChains.css';
|
import './AllChains.css';
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Connection } from '../../Connection';
|
import { Connection } from '../../Connection';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
import { State as AppState } from '../../state';
|
import { State as AppState } from '../../state';
|
||||||
import { formatNumber, secondsWithPrecision, getHashData } from '../../utils';
|
import { formatNumber, secondsWithPrecision, getHashData } from '../../utils';
|
||||||
import { Tab } from './';
|
import { Tab } from './';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Connection } from '../Connection';
|
import { Connection } from '../Connection';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../common';
|
||||||
import { ChainData } from '../state';
|
import { ChainData } from '../state';
|
||||||
|
|
||||||
import githubIcon from '../icons/mark-github.svg';
|
import githubIcon from '../icons/mark-github.svg';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
import { Connection } from '../../Connection';
|
import { Connection } from '../../Connection';
|
||||||
import Measure, { BoundingRect, ContentRect } from 'react-measure';
|
import Measure, { BoundingRect, ContentRect } from 'react-measure';
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ export class Consensus extends React.Component<Consensus.Props, {}> {
|
|||||||
|
|
||||||
private getSmallRow(blocks: Types.ConsensusInfo) {
|
private getSmallRow(blocks: Types.ConsensusInfo) {
|
||||||
const smallBlockSizeChanged = (
|
const smallBlockSizeChanged = (
|
||||||
isFirstBlock: boolean,
|
_isFirstBlock: boolean,
|
||||||
rect: BoundingRect
|
rect: BoundingRect
|
||||||
) => {
|
) => {
|
||||||
if (this.smallBlocksSizeDetected(this.state)) {
|
if (this.smallBlocksSizeDetected(this.state)) {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import Measure, { BoundingRect, ContentRect } from 'react-measure';
|
import Measure, { BoundingRect, ContentRect } from 'react-measure';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
|
|
||||||
import { Icon, Tooltip, PolkadotIcon } from '../';
|
import { Icon, Tooltip, PolkadotIcon } from '../';
|
||||||
import Jdenticon from './Jdenticon';
|
import Jdenticon from './Jdenticon';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Maybe } from '@dotstats/common';
|
import { Maybe } from '../common';
|
||||||
import { Node } from '../state';
|
import { Node } from '../state';
|
||||||
import { Icon } from './';
|
import { Icon } from './';
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe, timestamp } from '@dotstats/common';
|
import { Types, Maybe, timestamp } from '../../common';
|
||||||
import { State, Node } from '../../state';
|
import { State, Node } from '../../state';
|
||||||
import { Truncate } from './';
|
import { Truncate } from './';
|
||||||
import { Ago, Icon, Tooltip, Sparkline, PolkadotIcon } from '../';
|
import { Ago, Icon, Tooltip, Sparkline, PolkadotIcon } from '../';
|
||||||
@@ -59,6 +59,7 @@ import dothereumIcon from '../../icons/dothereum.svg';
|
|||||||
import katalchainIcon from '../../icons/katalchain.svg';
|
import katalchainIcon from '../../icons/katalchain.svg';
|
||||||
import bifrostIcon from '../../icons/bifrost.svg';
|
import bifrostIcon from '../../icons/bifrost.svg';
|
||||||
import totemIcon from '../../icons/totem.svg';
|
import totemIcon from '../../icons/totem.svg';
|
||||||
|
|
||||||
import unknownImplementationIcon from '../../icons/question-solid.svg';
|
import unknownImplementationIcon from '../../icons/question-solid.svg';
|
||||||
|
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Maybe } from '@dotstats/common';
|
import { Maybe } from '../../common';
|
||||||
import { Column } from './';
|
import { Column } from './';
|
||||||
import { Icon, Tooltip } from '../';
|
import { Icon, Tooltip } from '../';
|
||||||
import { Persistent } from '../../persist';
|
import { Persistent } from '../../persist';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
import { Filter } from '../';
|
import { Filter } from '../';
|
||||||
import { State as AppState, Node } from '../../state';
|
import { State as AppState, Node } from '../../state';
|
||||||
import { Row } from './';
|
import { Row } from './';
|
||||||
@@ -86,7 +86,7 @@ export class List extends React.Component<List.Props, {}> {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="List" style={{ height }}>
|
<div className="List" style={{ height }}>
|
||||||
<table>
|
<table>
|
||||||
<Row.Header columns={selectedColumns} sortBy={sortBy} />
|
<Row.HEADER columns={selectedColumns} sortBy={sortBy} />
|
||||||
<tbody style={{ transform }}>
|
<tbody style={{ transform }}>
|
||||||
{nodes.map((node) => (
|
{nodes.map((node) => (
|
||||||
<Row
|
<Row
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
import { Node } from '../../state';
|
import { Node } from '../../state';
|
||||||
import { Persistent, PersistentSet } from '../../persist';
|
import { Persistent, PersistentSet } from '../../persist';
|
||||||
import { HeaderCell, Column } from './';
|
import { HeaderCell, Column } from './';
|
||||||
@@ -51,7 +51,7 @@ export class Row extends React.Component<Row.Props, Row.State> {
|
|||||||
Column.NETWORK_STATE,
|
Column.NETWORK_STATE,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static Header = (props: HeaderProps) => {
|
public static HEADER = (props: HeaderProps) => {
|
||||||
const { columns, sortBy } = props;
|
const { columns, sortBy } = props;
|
||||||
const last = columns.length - 1;
|
const last = columns.length - 1;
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export class Location extends React.Component<Location.Props, Location.State> {
|
|||||||
city,
|
city,
|
||||||
} = this.props.node;
|
} = this.props.node;
|
||||||
|
|
||||||
let validatorRow = null;
|
let validatorRow = <div />;
|
||||||
|
|
||||||
if (validator) {
|
if (validator) {
|
||||||
validatorRow = (
|
validatorRow = (
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../../common';
|
||||||
import { Filter } from '../';
|
import { Filter } from '../';
|
||||||
import { State as AppState, Node } from '../../state';
|
import { State as AppState, Node } from '../../state';
|
||||||
import { Location } from './';
|
import { Location } from './';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Maybe } from '@dotstats/common';
|
import { Maybe } from '../../common';
|
||||||
import { State as AppState } from '../../state';
|
import { State as AppState } from '../../state';
|
||||||
import { Setting } from './';
|
import { Setting } from './';
|
||||||
import { Row } from '../List';
|
import { Row } from '../List';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Types, Maybe } from '@dotstats/common';
|
import { Types, Maybe } from '../common';
|
||||||
import sparkline from '@fnando/sparkline';
|
import sparkline from '@fnando/sparkline';
|
||||||
import { Tooltip } from './';
|
import { Tooltip } from './';
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export class Sparkline extends React.Component<Sparkline.Props, {}> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private onMouseMove = (
|
private onMouseMove = (
|
||||||
event: MouseEvent,
|
_event: MouseEvent,
|
||||||
data: { value: number; index: number }
|
data: { value: number; index: number }
|
||||||
) => {
|
) => {
|
||||||
const { format, stamps } = this.props;
|
const { format, stamps } = this.props;
|
||||||
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 144 B After Width: | Height: | Size: 144 B |
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 144 B After Width: | Height: | Size: 144 B |
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
|
Before Width: | Height: | Size: 395 B After Width: | Height: | Size: 395 B |
|
Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 250 B |
|
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 397 B |
|
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 283 B |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 268 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 588 B After Width: | Height: | Size: 588 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 646 B After Width: | Height: | Size: 646 B |