mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-06-14 04:51:01 +00:00
Cleanup
This commit is contained in:
+3
-5
@@ -1,15 +1,13 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
# testing
|
||||
/backend/coverage
|
||||
/frontend/coverage
|
||||
packages/*/coverage
|
||||
|
||||
# production
|
||||
/backend/build
|
||||
/frontend/build
|
||||
packages/*/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const feed_1 = require("./feed");
|
||||
const common_1 = require("@dotstats/common");
|
||||
class Aggregator extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.nodes = new common_1.IdSet();
|
||||
this.feeds = new common_1.IdSet();
|
||||
this.height = 0;
|
||||
setInterval(() => this.timeoutCheck(), 10000);
|
||||
}
|
||||
addNode(node) {
|
||||
this.nodes.add(node);
|
||||
this.broadcast(feed_1.default.addedNode(node));
|
||||
node.once('disconnect', () => {
|
||||
node.removeAllListeners('block');
|
||||
this.nodes.remove(node);
|
||||
this.broadcast(feed_1.default.removedNode(node));
|
||||
});
|
||||
node.on('block', () => this.updateBlock(node));
|
||||
}
|
||||
addFeed(feed) {
|
||||
this.feeds.add(feed);
|
||||
feed.send(feed_1.default.bestBlock(this.height));
|
||||
for (const node of this.nodes.entries) {
|
||||
feed.send(feed_1.default.addedNode(node));
|
||||
}
|
||||
feed.once('disconnect', () => {
|
||||
this.feeds.remove(feed);
|
||||
});
|
||||
}
|
||||
nodeList() {
|
||||
return this.nodes.entries;
|
||||
}
|
||||
broadcast(data) {
|
||||
for (const feed of this.feeds.entries) {
|
||||
feed.send(data);
|
||||
}
|
||||
}
|
||||
timeoutCheck() {
|
||||
const now = Date.now();
|
||||
for (const node of this.nodes.entries) {
|
||||
node.timeoutCheck(now);
|
||||
}
|
||||
}
|
||||
updateBlock(node) {
|
||||
if (node.height > this.height) {
|
||||
this.height = node.height;
|
||||
this.broadcast(feed_1.default.bestBlock(this.height));
|
||||
console.log(`New block ${this.height}`);
|
||||
}
|
||||
this.broadcast(feed_1.default.imported(node));
|
||||
console.log(`${node.name} imported ${node.height}, block time: ${node.blockTime / 1000}s, average: ${node.average / 1000}s | latency ${node.latency}`);
|
||||
}
|
||||
}
|
||||
exports.default = Aggregator;
|
||||
//# sourceMappingURL=aggregator.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../src/aggregator.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,59 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const feed_1 = require("./feed");
|
||||
const shared_1 = require("@dotstats/shared");
|
||||
class Aggregator extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.nodes = new shared_1.IdSet();
|
||||
this.feeds = new shared_1.IdSet();
|
||||
this.height = 0;
|
||||
setInterval(() => this.timeoutCheck(), 10000);
|
||||
}
|
||||
addNode(node) {
|
||||
this.nodes.add(node);
|
||||
this.broadcast(feed_1.default.addedNode(node));
|
||||
node.once('disconnect', () => {
|
||||
node.removeAllListeners('block');
|
||||
this.nodes.remove(node);
|
||||
this.broadcast(feed_1.default.removedNode(node));
|
||||
});
|
||||
node.on('block', () => this.updateBlock(node));
|
||||
}
|
||||
addFeed(feed) {
|
||||
this.feeds.add(feed);
|
||||
feed.send(feed_1.default.bestBlock(this.height));
|
||||
for (const node of this.nodes.entries) {
|
||||
feed.send(feed_1.default.addedNode(node));
|
||||
}
|
||||
feed.once('disconnect', () => {
|
||||
this.feeds.remove(feed);
|
||||
});
|
||||
}
|
||||
nodeList() {
|
||||
return this.nodes.entries;
|
||||
}
|
||||
broadcast(data) {
|
||||
for (const feed of this.feeds.entries) {
|
||||
feed.send(data);
|
||||
}
|
||||
}
|
||||
timeoutCheck() {
|
||||
const now = Date.now();
|
||||
for (const node of this.nodes.entries) {
|
||||
node.timeoutCheck(now);
|
||||
}
|
||||
}
|
||||
updateBlock(node) {
|
||||
if (node.height > this.height) {
|
||||
this.height = node.height;
|
||||
this.broadcast(feed_1.default.bestBlock(this.height));
|
||||
console.log(`New block ${this.height}`);
|
||||
}
|
||||
this.broadcast(feed_1.default.imported(node));
|
||||
console.log(`${node.name} imported ${node.height}, block time: ${node.blockTime / 1000}s, average: ${node.average / 1000}s | latency ${node.latency}`);
|
||||
}
|
||||
}
|
||||
exports.default = Aggregator;
|
||||
//# sourceMappingURL=aggregator.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../../../src/aggregator.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,51 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const shared_1 = require("@dotstats/shared");
|
||||
const nextId = shared_1.idGenerator();
|
||||
function serialize(msg) {
|
||||
return JSON.stringify(msg);
|
||||
}
|
||||
class Feed extends EventEmitter {
|
||||
constructor(socket) {
|
||||
super();
|
||||
this.id = nextId();
|
||||
this.socket = socket;
|
||||
socket.on('error', () => this.disconnect());
|
||||
socket.on('close', () => this.disconnect());
|
||||
}
|
||||
static bestBlock(height) {
|
||||
return serialize({
|
||||
action: 'best',
|
||||
payload: height
|
||||
});
|
||||
}
|
||||
static addedNode(node) {
|
||||
return serialize({
|
||||
action: 'added',
|
||||
payload: [node.id, node.nodeInfo(), node.blockInfo()]
|
||||
});
|
||||
}
|
||||
static removedNode(node) {
|
||||
return serialize({
|
||||
action: 'removed',
|
||||
payload: node.id
|
||||
});
|
||||
}
|
||||
static imported(node) {
|
||||
return serialize({
|
||||
action: 'imported',
|
||||
payload: [node.id, node.blockInfo()]
|
||||
});
|
||||
}
|
||||
send(data) {
|
||||
this.socket.send(data);
|
||||
}
|
||||
disconnect() {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close();
|
||||
this.emit('disconnect');
|
||||
}
|
||||
}
|
||||
exports.default = Feed;
|
||||
//# sourceMappingURL=feed.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../../../src/feed.ts"],"names":[],"mappings":";;AACA,uCAAuC;AAEvC,6CAA2D;AAE3D,MAAM,MAAM,GAAG,oBAAW,EAAQ,CAAC;AAoCnC,mBAAmB,GAAY;IAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAa,CAAC;AAC3C,CAAC;AAED,UAA0B,SAAQ,YAAY;IAK1C,YAAY,MAAiB;QACzB,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,MAAc;QAClC,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;SAClB,CAAC,CAAC;IACP,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,IAAU;QAC9B,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;SACxD,CAAC,CAAA;IACN,CAAC;IAEM,MAAM,CAAC,WAAW,CAAC,IAAU;QAChC,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACP,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAC,IAAU;QAC7B,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;SACvC,CAAC,CAAC;IACP,CAAC;IAEM,IAAI,CAAC,IAAc;QACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAEO,UAAU;QACd,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAEpB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;CACJ;AArDD,uBAqDC"}
|
||||
@@ -1,41 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const WebSocket = require("ws");
|
||||
const express = require("express");
|
||||
const http_1 = require("http");
|
||||
const node_1 = require("./node");
|
||||
const feed_1 = require("./feed");
|
||||
const aggregator_1 = require("./aggregator");
|
||||
const shared_1 = require("@dotstats/shared");
|
||||
const aggregator = new aggregator_1.default;
|
||||
const app = express();
|
||||
const server = http_1.createServer(app);
|
||||
// WebSocket for Nodes feeding telemetry data to the server
|
||||
const incomingTelemetry = new WebSocket.Server({ port: 1024 });
|
||||
// WebSocket for web clients listening to the telemetry data aggregate
|
||||
const telemetryFeed = new WebSocket.Server({ server });
|
||||
app.get('/', function (req, res) {
|
||||
function nodeInfo(node) {
|
||||
return `${node.name} | ${node.height} | Block time ${node.blockTime / 1000}s`;
|
||||
}
|
||||
res.send(`<pre>
|
||||
Best block: ${aggregator.height}
|
||||
|
||||
Node list:
|
||||
${shared_1.join(shared_1.map(aggregator.nodeList(), nodeInfo), '\n')}
|
||||
</pre>`);
|
||||
});
|
||||
incomingTelemetry.on('connection', async (socket) => {
|
||||
try {
|
||||
aggregator.addNode(await node_1.default.fromSocket(socket));
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
telemetryFeed.on('connection', (socket) => {
|
||||
aggregator.addFeed(new feed_1.default(socket));
|
||||
});
|
||||
console.log('Starting server on port 8080');
|
||||
server.listen(8080);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";;AAAA,gCAAgC;AAChC,mCAAmC;AACnC,+BAAoC;AACpC,iCAA0B;AAC1B,iCAA0B;AAC1B,6CAAsC;AACtC,6CAA6C;AAE7C,MAAM,UAAU,GAAG,IAAI,oBAAU,CAAC;AAClC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,mBAAY,CAAC,GAAG,CAAC,CAAC;AAEjC,2DAA2D;AAC3D,MAAM,iBAAiB,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAE/D,sEAAsE;AACtE,MAAM,aAAa,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAEvD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,GAAG;IAC3B,kBAAkB,IAAU;QACxB,OAAO,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,MAAM,iBAAiB,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC;IAClF,CAAC;IAED,GAAG,CAAC,IAAI,CAEZ;cACc,UAAU,CAAC,MAAM;;;EAG5B,aAAI,CAAC,YAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAE;OAC7C,CAEF,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,EAAE,MAAiB,EAAE,EAAE;IAC3D,IAAI;QACA,UAAU,CAAC,OAAO,CAAC,MAAM,cAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;KACrD;IAAC,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;KACtB;AACL,CAAC,CAAC,CAAC;AAEH,aAAa,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAiB,EAAE,EAAE;IACjD,UAAU,CAAC,OAAO,CAAC,IAAI,cAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC"}
|
||||
@@ -1,32 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function parseMessage(data) {
|
||||
try {
|
||||
const message = JSON.parse(data.toString());
|
||||
if (message && typeof message.msg === 'string' && typeof message.ts === 'string') {
|
||||
message.ts = new Date(message.ts);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
catch (_) {
|
||||
console.warn('Error parsing message JSON');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.parseMessage = parseMessage;
|
||||
function getBestBlock(message) {
|
||||
switch (message.msg) {
|
||||
case 'node.start':
|
||||
case 'system.interval':
|
||||
case 'block.import':
|
||||
return message;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.getBestBlock = getBestBlock;
|
||||
// received: {"msg":"block.import","level":"INFO","ts":"2018-06-18T17:30:35.285406538+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526}
|
||||
// received: {"msg":"node.start","level":"INFO","ts":"2018-06-18T17:30:40.038731057+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526}
|
||||
// received: {"msg":"system.connected","level":"INFO","ts":"2018-06-18T17:30:40.038975471+02:00","chain":"dev","config":"","version":"0.2.0","implementation":"parity-polkadot","name":"Majestic Widget"}
|
||||
// received: {"msg":"system.interval","level":"INFO","ts":"2018-06-19T14:00:05.091355364+02:00","txcount":0,"best":"360c9563857308703398f637932b7ffe884e5c7b09692600ff09a4d753c9d948","height":7559,"peers":0,"status":"Idle"}
|
||||
//# sourceMappingURL=message.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/message.ts"],"names":[],"mappings":";;AAGA,sBAA6B,IAAU;IACnC,IAAI;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE5C,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ,EAAE;YAC9E,OAAO,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAElC,OAAO,OAAO,CAAC;SAClB;KACJ;IAAC,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;KAC9C;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAdD,oCAcC;AAED,sBAA6B,OAAgB;IACzC,QAAQ,OAAO,CAAC,GAAG,EAAE;QACjB,KAAK,YAAY,CAAC;QAClB,KAAK,iBAAiB,CAAC;QACvB,KAAK,cAAc;YACf,OAAO,OAAO,CAAC;QACnB;YACI,OAAO,IAAI,CAAC;KACnB;AACL,CAAC;AATD,oCASC;AA8CD,oLAAoL;AACpL,kLAAkL;AAClL,yMAAyM;AACzM,8NAA8N"}
|
||||
@@ -1,124 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const shared_1 = require("@dotstats/shared");
|
||||
const message_1 = require("./message");
|
||||
const BLOCK_TIME_HISTORY = 10;
|
||||
const TIMEOUT = 1000 * 60 * 5; // 5 seconds
|
||||
const nextId = shared_1.idGenerator();
|
||||
class Node extends EventEmitter {
|
||||
constructor(socket, name, config, implentation, version) {
|
||||
super();
|
||||
this.height = 0;
|
||||
this.latency = 0;
|
||||
this.blockTime = 0;
|
||||
this.blockTimes = new Array(BLOCK_TIME_HISTORY);
|
||||
this.lastBlockAt = null;
|
||||
this.lastMessage = Date.now();
|
||||
this.id = nextId();
|
||||
this.socket = socket;
|
||||
this.name = name;
|
||||
this.config = config;
|
||||
this.implementation = implentation;
|
||||
this.version = version;
|
||||
console.log(`Listening to a new node: ${name}`);
|
||||
socket.on('message', (data) => {
|
||||
const message = message_1.parseMessage(data);
|
||||
if (!message)
|
||||
return;
|
||||
this.lastMessage = Date.now();
|
||||
this.updateLatency(message.ts);
|
||||
const update = message_1.getBestBlock(message);
|
||||
if (update) {
|
||||
this.updateBestBlock(update);
|
||||
}
|
||||
});
|
||||
socket.on('close', () => {
|
||||
console.log(`${this.name} has disconnected`);
|
||||
this.disconnect();
|
||||
});
|
||||
socket.on('error', (error) => {
|
||||
console.error(`${this.name} has errored`, error);
|
||||
this.disconnect();
|
||||
});
|
||||
}
|
||||
static fromSocket(socket) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function cleanup() {
|
||||
clearTimeout(timeout);
|
||||
socket.removeAllListeners('message');
|
||||
}
|
||||
function handler(data) {
|
||||
const message = message_1.parseMessage(data);
|
||||
if (message && message.msg === "system.connected") {
|
||||
cleanup();
|
||||
const { name, config, implementation, version } = message;
|
||||
resolve(new Node(socket, name, config, implementation, version));
|
||||
}
|
||||
}
|
||||
socket.on('message', handler);
|
||||
const timeout = setTimeout(() => {
|
||||
cleanup();
|
||||
socket.close();
|
||||
return reject(new Error('Timeout on waiting for system.connected message'));
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
timeoutCheck(now) {
|
||||
if (this.lastMessage + TIMEOUT < now) {
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
nodeInfo() {
|
||||
return {
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
blockInfo() {
|
||||
return {
|
||||
height: this.height,
|
||||
blockTime: this.blockTime,
|
||||
};
|
||||
}
|
||||
get average() {
|
||||
let accounted = 0;
|
||||
let sum = 0;
|
||||
for (const time of this.blockTimes) {
|
||||
if (time) {
|
||||
accounted += 1;
|
||||
sum += time;
|
||||
}
|
||||
}
|
||||
if (accounted === 0) {
|
||||
return 0;
|
||||
}
|
||||
return sum / accounted;
|
||||
}
|
||||
disconnect() {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close();
|
||||
this.emit('disconnect');
|
||||
}
|
||||
updateLatency(time) {
|
||||
this.latency = this.lastMessage - +time;
|
||||
}
|
||||
updateBestBlock(update) {
|
||||
const { height, ts: time, best } = update;
|
||||
if (this.height < height) {
|
||||
const blockTime = this.getBlockTime(time);
|
||||
this.height = height;
|
||||
this.lastBlockAt = time;
|
||||
this.blockTimes[height % BLOCK_TIME_HISTORY] = blockTime;
|
||||
this.blockTime = blockTime;
|
||||
this.emit('block');
|
||||
}
|
||||
}
|
||||
getBlockTime(time) {
|
||||
if (!this.lastBlockAt) {
|
||||
return 0;
|
||||
}
|
||||
return +time - +this.lastBlockAt;
|
||||
}
|
||||
}
|
||||
exports.default = Node;
|
||||
//# sourceMappingURL=node.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../../src/node.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,51 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const common_1 = require("@dotstats/common");
|
||||
const nextId = common_1.idGenerator();
|
||||
function serialize(msg) {
|
||||
return JSON.stringify(msg);
|
||||
}
|
||||
class Feed extends EventEmitter {
|
||||
constructor(socket) {
|
||||
super();
|
||||
this.id = nextId();
|
||||
this.socket = socket;
|
||||
socket.on('error', () => this.disconnect());
|
||||
socket.on('close', () => this.disconnect());
|
||||
}
|
||||
static bestBlock(height) {
|
||||
return serialize({
|
||||
action: 'best',
|
||||
payload: height
|
||||
});
|
||||
}
|
||||
static addedNode(node) {
|
||||
return serialize({
|
||||
action: 'added',
|
||||
payload: [node.id, node.nodeInfo(), node.blockInfo()]
|
||||
});
|
||||
}
|
||||
static removedNode(node) {
|
||||
return serialize({
|
||||
action: 'removed',
|
||||
payload: node.id
|
||||
});
|
||||
}
|
||||
static imported(node) {
|
||||
return serialize({
|
||||
action: 'imported',
|
||||
payload: [node.id, node.blockInfo()]
|
||||
});
|
||||
}
|
||||
send(data) {
|
||||
this.socket.send(data);
|
||||
}
|
||||
disconnect() {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close();
|
||||
this.emit('disconnect');
|
||||
}
|
||||
}
|
||||
exports.default = Feed;
|
||||
//# sourceMappingURL=feed.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":";;AACA,uCAAuC;AAEvC,6CAA2D;AAE3D,MAAM,MAAM,GAAG,oBAAW,EAAQ,CAAC;AAoCnC,mBAAmB,GAAY;IAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAa,CAAC;AAC3C,CAAC;AAED,UAA0B,SAAQ,YAAY;IAK1C,YAAY,MAAiB;QACzB,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,MAAc;QAClC,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,MAAM;SAClB,CAAC,CAAC;IACP,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,IAAU;QAC9B,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;SACxD,CAAC,CAAA;IACN,CAAC;IAEM,MAAM,CAAC,WAAW,CAAC,IAAU;QAChC,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACP,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAC,IAAU;QAC7B,OAAO,SAAS,CAAC;YACb,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;SACvC,CAAC,CAAC;IACP,CAAC;IAEM,IAAI,CAAC,IAAc;QACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAEO,UAAU;QACd,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAEpB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;CACJ;AArDD,uBAqDC"}
|
||||
@@ -1,41 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const WebSocket = require("ws");
|
||||
const express = require("express");
|
||||
const http_1 = require("http");
|
||||
const node_1 = require("./node");
|
||||
const feed_1 = require("./feed");
|
||||
const aggregator_1 = require("./aggregator");
|
||||
const common_1 = require("@dotstats/common");
|
||||
const aggregator = new aggregator_1.default;
|
||||
const app = express();
|
||||
const server = http_1.createServer(app);
|
||||
// WebSocket for Nodes feeding telemetry data to the server
|
||||
const incomingTelemetry = new WebSocket.Server({ port: 1024 });
|
||||
// WebSocket for web clients listening to the telemetry data aggregate
|
||||
const telemetryFeed = new WebSocket.Server({ server });
|
||||
app.get('/', function (req, res) {
|
||||
function nodeInfo(node) {
|
||||
return `${node.name} | ${node.height} | Block time ${node.blockTime / 1000}s`;
|
||||
}
|
||||
res.send(`<pre>
|
||||
Best block: ${aggregator.height}
|
||||
|
||||
Node list:
|
||||
${common_1.join(common_1.map(aggregator.nodeList(), nodeInfo), '\n')}
|
||||
</pre>`);
|
||||
});
|
||||
incomingTelemetry.on('connection', async (socket) => {
|
||||
try {
|
||||
aggregator.addNode(await node_1.default.fromSocket(socket));
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
telemetryFeed.on('connection', (socket) => {
|
||||
aggregator.addFeed(new feed_1.default(socket));
|
||||
});
|
||||
console.log('Starting server on port 8080');
|
||||
server.listen(8080);
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,gCAAgC;AAChC,mCAAmC;AACnC,+BAAoC;AACpC,iCAA0B;AAC1B,iCAA0B;AAC1B,6CAAsC;AACtC,6CAA6C;AAE7C,MAAM,UAAU,GAAG,IAAI,oBAAU,CAAC;AAClC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,mBAAY,CAAC,GAAG,CAAC,CAAC;AAEjC,2DAA2D;AAC3D,MAAM,iBAAiB,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAE/D,sEAAsE;AACtE,MAAM,aAAa,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAEvD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,GAAG;IAC3B,kBAAkB,IAAU;QACxB,OAAO,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,MAAM,iBAAiB,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC;IAClF,CAAC;IAED,GAAG,CAAC,IAAI,CAEZ;cACc,UAAU,CAAC,MAAM;;;EAG5B,aAAI,CAAC,YAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAE;OAC7C,CAEF,CAAC;AACN,CAAC,CAAC,CAAC;AAEH,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,EAAE,MAAiB,EAAE,EAAE;IAC3D,IAAI;QACA,UAAU,CAAC,OAAO,CAAC,MAAM,cAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;KACrD;IAAC,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;KACtB;AACL,CAAC,CAAC,CAAC;AAEH,aAAa,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAiB,EAAE,EAAE;IACjD,UAAU,CAAC,OAAO,CAAC,IAAI,cAAI,CAAC,MAAM,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC"}
|
||||
@@ -1,32 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function parseMessage(data) {
|
||||
try {
|
||||
const message = JSON.parse(data.toString());
|
||||
if (message && typeof message.msg === 'string' && typeof message.ts === 'string') {
|
||||
message.ts = new Date(message.ts);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
catch (_) {
|
||||
console.warn('Error parsing message JSON');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.parseMessage = parseMessage;
|
||||
function getBestBlock(message) {
|
||||
switch (message.msg) {
|
||||
case 'node.start':
|
||||
case 'system.interval':
|
||||
case 'block.import':
|
||||
return message;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.getBestBlock = getBestBlock;
|
||||
// received: {"msg":"block.import","level":"INFO","ts":"2018-06-18T17:30:35.285406538+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526}
|
||||
// received: {"msg":"node.start","level":"INFO","ts":"2018-06-18T17:30:40.038731057+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526}
|
||||
// received: {"msg":"system.connected","level":"INFO","ts":"2018-06-18T17:30:40.038975471+02:00","chain":"dev","config":"","version":"0.2.0","implementation":"parity-polkadot","name":"Majestic Widget"}
|
||||
// received: {"msg":"system.interval","level":"INFO","ts":"2018-06-19T14:00:05.091355364+02:00","txcount":0,"best":"360c9563857308703398f637932b7ffe884e5c7b09692600ff09a4d753c9d948","height":7559,"peers":0,"status":"Idle"}
|
||||
//# sourceMappingURL=message.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"message.js","sourceRoot":"","sources":["../src/message.ts"],"names":[],"mappings":";;AAGA,sBAA6B,IAAU;IACnC,IAAI;QACA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE5C,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ,EAAE;YAC9E,OAAO,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAElC,OAAO,OAAO,CAAC;SAClB;KACJ;IAAC,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;KAC9C;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAdD,oCAcC;AAED,sBAA6B,OAAgB;IACzC,QAAQ,OAAO,CAAC,GAAG,EAAE;QACjB,KAAK,YAAY,CAAC;QAClB,KAAK,iBAAiB,CAAC;QACvB,KAAK,cAAc;YACf,OAAO,OAAO,CAAC;QACnB;YACI,OAAO,IAAI,CAAC;KACnB;AACL,CAAC;AATD,oCASC;AA8CD,oLAAoL;AACpL,kLAAkL;AAClL,yMAAyM;AACzM,8NAA8N"}
|
||||
@@ -1,124 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const EventEmitter = require("events");
|
||||
const common_1 = require("@dotstats/common");
|
||||
const message_1 = require("./message");
|
||||
const BLOCK_TIME_HISTORY = 10;
|
||||
const TIMEOUT = 1000 * 60 * 5; // 5 seconds
|
||||
const nextId = common_1.idGenerator();
|
||||
class Node extends EventEmitter {
|
||||
constructor(socket, name, config, implentation, version) {
|
||||
super();
|
||||
this.height = 0;
|
||||
this.latency = 0;
|
||||
this.blockTime = 0;
|
||||
this.blockTimes = new Array(BLOCK_TIME_HISTORY);
|
||||
this.lastBlockAt = null;
|
||||
this.lastMessage = Date.now();
|
||||
this.id = nextId();
|
||||
this.socket = socket;
|
||||
this.name = name;
|
||||
this.config = config;
|
||||
this.implementation = implentation;
|
||||
this.version = version;
|
||||
console.log(`Listening to a new node: ${name}`);
|
||||
socket.on('message', (data) => {
|
||||
const message = message_1.parseMessage(data);
|
||||
if (!message)
|
||||
return;
|
||||
this.lastMessage = Date.now();
|
||||
this.updateLatency(message.ts);
|
||||
const update = message_1.getBestBlock(message);
|
||||
if (update) {
|
||||
this.updateBestBlock(update);
|
||||
}
|
||||
});
|
||||
socket.on('close', () => {
|
||||
console.log(`${this.name} has disconnected`);
|
||||
this.disconnect();
|
||||
});
|
||||
socket.on('error', (error) => {
|
||||
console.error(`${this.name} has errored`, error);
|
||||
this.disconnect();
|
||||
});
|
||||
}
|
||||
static fromSocket(socket) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function cleanup() {
|
||||
clearTimeout(timeout);
|
||||
socket.removeAllListeners('message');
|
||||
}
|
||||
function handler(data) {
|
||||
const message = message_1.parseMessage(data);
|
||||
if (message && message.msg === "system.connected") {
|
||||
cleanup();
|
||||
const { name, config, implementation, version } = message;
|
||||
resolve(new Node(socket, name, config, implementation, version));
|
||||
}
|
||||
}
|
||||
socket.on('message', handler);
|
||||
const timeout = setTimeout(() => {
|
||||
cleanup();
|
||||
socket.close();
|
||||
return reject(new Error('Timeout on waiting for system.connected message'));
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
timeoutCheck(now) {
|
||||
if (this.lastMessage + TIMEOUT < now) {
|
||||
this.disconnect();
|
||||
}
|
||||
}
|
||||
nodeInfo() {
|
||||
return {
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
blockInfo() {
|
||||
return {
|
||||
height: this.height,
|
||||
blockTime: this.blockTime,
|
||||
};
|
||||
}
|
||||
get average() {
|
||||
let accounted = 0;
|
||||
let sum = 0;
|
||||
for (const time of this.blockTimes) {
|
||||
if (time) {
|
||||
accounted += 1;
|
||||
sum += time;
|
||||
}
|
||||
}
|
||||
if (accounted === 0) {
|
||||
return 0;
|
||||
}
|
||||
return sum / accounted;
|
||||
}
|
||||
disconnect() {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close();
|
||||
this.emit('disconnect');
|
||||
}
|
||||
updateLatency(time) {
|
||||
this.latency = this.lastMessage - +time;
|
||||
}
|
||||
updateBestBlock(update) {
|
||||
const { height, ts: time, best } = update;
|
||||
if (this.height < height) {
|
||||
const blockTime = this.getBlockTime(time);
|
||||
this.height = height;
|
||||
this.lastBlockAt = time;
|
||||
this.blockTimes[height % BLOCK_TIME_HISTORY] = blockTime;
|
||||
this.blockTime = blockTime;
|
||||
this.emit('block');
|
||||
}
|
||||
}
|
||||
getBlockTime(time) {
|
||||
if (!this.lastBlockAt) {
|
||||
return 0;
|
||||
}
|
||||
return +time - +this.lastBlockAt;
|
||||
}
|
||||
}
|
||||
exports.default = Node;
|
||||
//# sourceMappingURL=node.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,26 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Higher order function producing new auto-incremented `Id`s.
|
||||
*/
|
||||
function idGenerator() {
|
||||
let current = 0;
|
||||
return () => current++;
|
||||
}
|
||||
exports.idGenerator = idGenerator;
|
||||
class IdSet {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
add(item) {
|
||||
this.map.set(item.id, item);
|
||||
}
|
||||
remove(item) {
|
||||
this.map.delete(item.id);
|
||||
}
|
||||
get entries() {
|
||||
return this.map.values();
|
||||
}
|
||||
}
|
||||
exports.IdSet = IdSet;
|
||||
//# sourceMappingURL=id.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../../../shared/src/id.ts"],"names":[],"mappings":";;AAOA;;GAEG;AACH;IACI,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,OAAO,GAAG,EAAE,CAAC,OAAO,EAAW,CAAC;AACpC,CAAC;AAJD,kCAIC;AAMD;IAAA;QACY,QAAG,GAAkB,IAAI,GAAG,EAAE,CAAC;IAa3C,CAAC;IAXU,GAAG,CAAC,IAAkB;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAEM,MAAM,CAAC,IAAkB;QAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC7B,CAAC;CACJ;AAdD,sBAcC"}
|
||||
@@ -1,9 +0,0 @@
|
||||
"use strict";
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__export(require("./id"));
|
||||
__export(require("./iterators"));
|
||||
__export(require("./types"));
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../shared/src/index.ts"],"names":[],"mappings":";;;;;AAAA,0BAAqB;AACrB,iCAA4B;AAC5B,6BAAwB"}
|
||||
@@ -1,57 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function* map(iter, fn) {
|
||||
for (const item of iter) {
|
||||
yield fn(item);
|
||||
}
|
||||
}
|
||||
exports.map = map;
|
||||
function* chain(a, b) {
|
||||
yield* a;
|
||||
yield* b;
|
||||
}
|
||||
exports.chain = chain;
|
||||
function* zip(a, b) {
|
||||
let itemA = a.next();
|
||||
let itemB = b.next();
|
||||
while (!itemA.done && !itemB.done) {
|
||||
yield [itemA.value, itemB.value];
|
||||
itemA = a.next();
|
||||
itemB = b.next();
|
||||
}
|
||||
}
|
||||
exports.zip = zip;
|
||||
function* take(iter, n) {
|
||||
for (const item of iter) {
|
||||
if (n-- === 0) {
|
||||
return;
|
||||
}
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
exports.take = take;
|
||||
function skip(iter, n) {
|
||||
while (n-- !== 0 && !iter.next().done) { }
|
||||
return iter;
|
||||
}
|
||||
exports.skip = skip;
|
||||
function reduce(iter, fn, accumulator) {
|
||||
for (const item of iter) {
|
||||
accumulator = fn(accumulator, item);
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
exports.reduce = reduce;
|
||||
function join(iter, glue) {
|
||||
const first = iter.next();
|
||||
if (first.done) {
|
||||
return '';
|
||||
}
|
||||
let result = first.value.toString();
|
||||
for (const item of iter) {
|
||||
result += glue + item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.join = join;
|
||||
//# sourceMappingURL=iterators.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"iterators.js","sourceRoot":"","sources":["../../../../shared/src/iterators.ts"],"names":[],"mappings":";;AAAA,QAAe,CAAC,KAAW,IAAyB,EAAE,EAAkB;IACpE,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;KAClB;AACL,CAAC;AAJD,kBAIC;AAED,QAAe,CAAC,OAAU,CAAsB,EAAE,CAAsB;IACpE,KAAK,CAAC,CAAC,CAAC,CAAC;IACT,KAAK,CAAC,CAAC,CAAC,CAAC;AACb,CAAC;AAHD,sBAGC;AAED,QAAe,CAAC,KAAW,CAAsB,EAAE,CAAsB;IACrE,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAErB,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAEjC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB;AACL,CAAC;AAVD,kBAUC;AAED,QAAe,CAAC,MAAS,IAAyB,EAAE,CAAS;IACzD,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE;YACX,OAAO;SACV;QAED,MAAM,IAAI,CAAC;KACd;AACL,CAAC;AARD,oBAQC;AAED,cAAwB,IAAyB,EAAE,CAAS;IACxD,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAE;IAEzC,OAAO,IAAI,CAAC;AAChB,CAAC;AAJD,oBAIC;AAED,gBAA6B,IAAyB,EAAE,EAA2B,EAAE,WAAc;IAC/F,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,WAAW,GAAG,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;KACvC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC;AAND,wBAMC;AAED,cAAqB,IAAkD,EAAE,IAAY;IACjF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAE1B,IAAI,KAAK,CAAC,IAAI,EAAE;QACZ,OAAO,EAAE,CAAC;KACb;IAED,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,MAAM,IAAI,IAAI,GAAG,IAAI,CAAC;KACzB;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAdD,oBAcC"}
|
||||
@@ -1,10 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
||||
* the compiler.
|
||||
*/
|
||||
class PhantomData {
|
||||
}
|
||||
exports.PhantomData = PhantomData;
|
||||
//# sourceMappingURL=types.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../shared/src/types.ts"],"names":[],"mappings":";;AAAA;;;GAGG;AACH;CAAsD;AAAtD,kCAAsD"}
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as EventEmitter from 'events';
|
||||
import Node from './node';
|
||||
import Feed, { FeedData } from './feed';
|
||||
import { Id, IdSet } from '@dotstats/common';
|
||||
import { Types, IdSet } from '@dotstats/common';
|
||||
|
||||
export default class Aggregator extends EventEmitter {
|
||||
private nodes: IdSet<Node> = new IdSet<Node>();
|
||||
private feeds: IdSet<Feed> = new IdSet<Feed>();
|
||||
private nodes = new IdSet<Types.NodeId, Node>();
|
||||
private feeds = new IdSet<Types.FeedId, Feed>();
|
||||
|
||||
public height: number = 0;
|
||||
public height = 0 as Types.BlockNumber;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -34,7 +34,7 @@ export default class Aggregator extends EventEmitter {
|
||||
|
||||
feed.send(Feed.bestBlock(this.height));
|
||||
|
||||
for (const node of this.nodes.entries) {
|
||||
for (const node of this.nodes.values()) {
|
||||
feed.send(Feed.addedNode(node));
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ export default class Aggregator extends EventEmitter {
|
||||
}
|
||||
|
||||
public nodeList(): IterableIterator<Node> {
|
||||
return this.nodes.entries;
|
||||
return this.nodes.values();
|
||||
}
|
||||
|
||||
private broadcast(data: FeedData) {
|
||||
for (const feed of this.feeds.entries) {
|
||||
for (const feed of this.feeds.values()) {
|
||||
feed.send(data);
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export default class Aggregator extends EventEmitter {
|
||||
private timeoutCheck() {
|
||||
const now = Date.now();
|
||||
|
||||
for (const node of this.nodes.entries) {
|
||||
for (const node of this.nodes.values()) {
|
||||
node.timeoutCheck(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,23 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import * as EventEmitter from 'events';
|
||||
import Node, { NodeInfo, BlockInfo } from './node';
|
||||
import { Opaque, Id, idGenerator } from '@dotstats/common';
|
||||
import Node from './node';
|
||||
import { Opaque, Types, idGenerator } from '@dotstats/common';
|
||||
|
||||
const nextId = idGenerator<Feed>();
|
||||
|
||||
export interface BlockInfo {
|
||||
height: number;
|
||||
blockTime: number;
|
||||
}
|
||||
|
||||
interface BestBlock {
|
||||
action: 'best';
|
||||
payload: number;
|
||||
}
|
||||
|
||||
interface AddedNode {
|
||||
action: 'added';
|
||||
payload: [Id<Node>, NodeInfo, BlockInfo];
|
||||
}
|
||||
|
||||
interface RemovedNode {
|
||||
action: 'removed';
|
||||
payload: Id<Node>;
|
||||
}
|
||||
|
||||
interface Imported {
|
||||
action: 'imported';
|
||||
payload: [Id<Node>, BlockInfo];
|
||||
}
|
||||
|
||||
type Message = BestBlock | AddedNode | RemovedNode | Imported;
|
||||
const nextId = idGenerator<Types.FeedId>();
|
||||
|
||||
/**
|
||||
* Opaque 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.
|
||||
*/
|
||||
export type FeedData = Opaque<string, Message>;
|
||||
export type FeedData = Opaque<string, Types.FeedMessage>;
|
||||
|
||||
function serialize(msg: Message): FeedData {
|
||||
function serialize(msg: Types.FeedMessage): FeedData {
|
||||
return JSON.stringify(msg) as FeedData;
|
||||
}
|
||||
|
||||
export default class Feed extends EventEmitter {
|
||||
public id: Id<Feed>;
|
||||
public id: Types.FeedId;
|
||||
|
||||
private socket: WebSocket;
|
||||
|
||||
@@ -58,7 +31,7 @@ export default class Feed extends EventEmitter {
|
||||
socket.on('close', () => this.disconnect());
|
||||
}
|
||||
|
||||
public static bestBlock(height: number): FeedData {
|
||||
public static bestBlock(height: Types.BlockNumber): FeedData {
|
||||
return serialize({
|
||||
action: 'best',
|
||||
payload: height
|
||||
@@ -68,7 +41,7 @@ export default class Feed extends EventEmitter {
|
||||
public static addedNode(node: Node): FeedData {
|
||||
return serialize({
|
||||
action: 'added',
|
||||
payload: [node.id, node.nodeInfo(), node.blockInfo()]
|
||||
payload: [node.id, node.nodeDetails(), node.blockDetails()]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,7 +55,7 @@ export default class Feed extends EventEmitter {
|
||||
public static imported(node: Node): FeedData {
|
||||
return serialize({
|
||||
action: 'imported',
|
||||
payload: [node.id, node.blockInfo()]
|
||||
payload: [node.id, node.blockDetails()]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Data } from 'ws';
|
||||
import { Maybe, Opaque } from '@dotstats/common';
|
||||
import { Maybe, Types } from '@dotstats/common';
|
||||
|
||||
export function parseMessage(data: Data): Maybe<Message> {
|
||||
try {
|
||||
@@ -35,13 +35,13 @@ interface MessageBase {
|
||||
|
||||
export interface BestBlock {
|
||||
best: string,
|
||||
height: number,
|
||||
height: Types.BlockNumber,
|
||||
ts: Date,
|
||||
}
|
||||
|
||||
interface SystemConnected {
|
||||
msg: 'system.connected',
|
||||
name: string,
|
||||
name: Types.NodeName,
|
||||
chain: string,
|
||||
config: string,
|
||||
implementation: string,
|
||||
|
||||
@@ -1,38 +1,29 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import * as EventEmitter from 'events';
|
||||
import { Maybe, Id, idGenerator } from '@dotstats/common';
|
||||
import { Maybe, Types, idGenerator } from '@dotstats/common';
|
||||
import { parseMessage, getBestBlock, Message, BestBlock } from './message';
|
||||
|
||||
const BLOCK_TIME_HISTORY = 10;
|
||||
const TIMEOUT = 1000 * 60 * 5; // 5 seconds
|
||||
|
||||
const nextId = idGenerator<Node>();
|
||||
|
||||
export interface NodeInfo {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface BlockInfo {
|
||||
height: number;
|
||||
blockTime: number;
|
||||
}
|
||||
const nextId = idGenerator<Types.NodeId>();
|
||||
|
||||
export default class Node extends EventEmitter {
|
||||
public lastMessage: number;
|
||||
public id: Id<Node>;
|
||||
public name: string;
|
||||
public id: Types.NodeId;
|
||||
public name: Types.NodeName;
|
||||
public implementation: string;
|
||||
public version: string;
|
||||
public height: number = 0;
|
||||
public config: string;
|
||||
public latency: number = 0;
|
||||
public blockTime: number = 0;
|
||||
public height = 0 as Types.BlockNumber;
|
||||
public latency = 0 as Types.Milliseconds;
|
||||
public blockTime = 0 as Types.Milliseconds;
|
||||
|
||||
private socket: WebSocket;
|
||||
private blockTimes: Array<number> = new Array(BLOCK_TIME_HISTORY);
|
||||
private lastBlockAt: Maybe<Date> = null;
|
||||
|
||||
constructor(socket: WebSocket, name: string, config: string, implentation: string, version: string) {
|
||||
constructor(socket: WebSocket, name: Types.NodeName, config: string, implentation: string, version: string) {
|
||||
super();
|
||||
|
||||
this.lastMessage = Date.now();
|
||||
@@ -46,6 +37,8 @@ export default class Node extends EventEmitter {
|
||||
console.log(`Listening to a new node: ${name}`);
|
||||
|
||||
socket.on('message', (data) => {
|
||||
console.log(data);
|
||||
|
||||
const message = parseMessage(data);
|
||||
|
||||
if (!message) return;
|
||||
@@ -81,6 +74,8 @@ export default class Node extends EventEmitter {
|
||||
}
|
||||
|
||||
function handler(data: WebSocket.Data) {
|
||||
console.log(data);
|
||||
|
||||
const message = parseMessage(data);
|
||||
|
||||
if (message && message.msg === "system.connected") {
|
||||
@@ -110,13 +105,13 @@ export default class Node extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public nodeInfo(): NodeInfo {
|
||||
public nodeDetails(): Types.NodeDetails {
|
||||
return {
|
||||
name: this.name,
|
||||
};
|
||||
}
|
||||
|
||||
public blockInfo(): BlockInfo {
|
||||
public blockDetails(): Types.BlockDetails {
|
||||
return {
|
||||
height: this.height,
|
||||
blockTime: this.blockTime,
|
||||
@@ -141,8 +136,6 @@ export default class Node extends EventEmitter {
|
||||
return sum / accounted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private disconnect() {
|
||||
this.socket.removeAllListeners();
|
||||
this.socket.close();
|
||||
@@ -151,7 +144,7 @@ export default class Node extends EventEmitter {
|
||||
}
|
||||
|
||||
private updateLatency(time: Date) {
|
||||
this.latency = this.lastMessage - +time;
|
||||
this.latency = (this.lastMessage - +time) as Types.Milliseconds;
|
||||
}
|
||||
|
||||
private updateBestBlock(update: BestBlock) {
|
||||
@@ -169,11 +162,11 @@ export default class Node extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private getBlockTime(time: Date): number {
|
||||
private getBlockTime(time: Date): Types.Milliseconds {
|
||||
if (!this.lastBlockAt) {
|
||||
return 0;
|
||||
return 0 as Types.Milliseconds;
|
||||
}
|
||||
|
||||
return +time - +this.lastBlockAt;
|
||||
return (+time - +this.lastBlockAt) as Types.Milliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Higher order function producing new auto-incremented `Id`s.
|
||||
*/
|
||||
function idGenerator() {
|
||||
let current = 0;
|
||||
return () => current++;
|
||||
}
|
||||
exports.idGenerator = idGenerator;
|
||||
class IdSet {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
add(item) {
|
||||
this.map.set(item.id, item);
|
||||
}
|
||||
remove(item) {
|
||||
this.map.delete(item.id);
|
||||
}
|
||||
get entries() {
|
||||
return this.map.values();
|
||||
}
|
||||
}
|
||||
exports.IdSet = IdSet;
|
||||
//# sourceMappingURL=id.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":";;AAOA;;GAEG;AACH;IACI,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,OAAO,GAAG,EAAE,CAAC,OAAO,EAAW,CAAC;AACpC,CAAC;AAJD,kCAIC;AAMD;IAAA;QACY,QAAG,GAAkB,IAAI,GAAG,EAAE,CAAC;IAa3C,CAAC;IAXU,GAAG,CAAC,IAAkB;QACzB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAEM,MAAM,CAAC,IAAkB;QAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC7B,CAAC;CACJ;AAdD,sBAcC"}
|
||||
@@ -1,9 +0,0 @@
|
||||
"use strict";
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__export(require("./id"));
|
||||
__export(require("./iterators"));
|
||||
__export(require("./types"));
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,0BAAqB;AACrB,iCAA4B;AAC5B,6BAAwB"}
|
||||
@@ -1,57 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function* map(iter, fn) {
|
||||
for (const item of iter) {
|
||||
yield fn(item);
|
||||
}
|
||||
}
|
||||
exports.map = map;
|
||||
function* chain(a, b) {
|
||||
yield* a;
|
||||
yield* b;
|
||||
}
|
||||
exports.chain = chain;
|
||||
function* zip(a, b) {
|
||||
let itemA = a.next();
|
||||
let itemB = b.next();
|
||||
while (!itemA.done && !itemB.done) {
|
||||
yield [itemA.value, itemB.value];
|
||||
itemA = a.next();
|
||||
itemB = b.next();
|
||||
}
|
||||
}
|
||||
exports.zip = zip;
|
||||
function* take(iter, n) {
|
||||
for (const item of iter) {
|
||||
if (n-- === 0) {
|
||||
return;
|
||||
}
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
exports.take = take;
|
||||
function skip(iter, n) {
|
||||
while (n-- !== 0 && !iter.next().done) { }
|
||||
return iter;
|
||||
}
|
||||
exports.skip = skip;
|
||||
function reduce(iter, fn, accumulator) {
|
||||
for (const item of iter) {
|
||||
accumulator = fn(accumulator, item);
|
||||
}
|
||||
return accumulator;
|
||||
}
|
||||
exports.reduce = reduce;
|
||||
function join(iter, glue) {
|
||||
const first = iter.next();
|
||||
if (first.done) {
|
||||
return '';
|
||||
}
|
||||
let result = first.value.toString();
|
||||
for (const item of iter) {
|
||||
result += glue + item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.join = join;
|
||||
//# sourceMappingURL=iterators.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"iterators.js","sourceRoot":"","sources":["../src/iterators.ts"],"names":[],"mappings":";;AAAA,QAAe,CAAC,KAAW,IAAyB,EAAE,EAAkB;IACpE,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;KAClB;AACL,CAAC;AAJD,kBAIC;AAED,QAAe,CAAC,OAAU,CAAsB,EAAE,CAAsB;IACpE,KAAK,CAAC,CAAC,CAAC,CAAC;IACT,KAAK,CAAC,CAAC,CAAC,CAAC;AACb,CAAC;AAHD,sBAGC;AAED,QAAe,CAAC,KAAW,CAAsB,EAAE,CAAsB;IACrE,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAErB,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAEjC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB;AACL,CAAC;AAVD,kBAUC;AAED,QAAe,CAAC,MAAS,IAAyB,EAAE,CAAS;IACzD,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE;YACX,OAAO;SACV;QAED,MAAM,IAAI,CAAC;KACd;AACL,CAAC;AARD,oBAQC;AAED,cAAwB,IAAyB,EAAE,CAAS;IACxD,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAE;IAEzC,OAAO,IAAI,CAAC;AAChB,CAAC;AAJD,oBAIC;AAED,gBAA6B,IAAyB,EAAE,EAA2B,EAAE,WAAc;IAC/F,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,WAAW,GAAG,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;KACvC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC;AAND,wBAMC;AAED,cAAqB,IAAkD,EAAE,IAAY;IACjF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAE1B,IAAI,KAAK,CAAC,IAAI,EAAE;QACZ,OAAO,EAAE,CAAC;KACb;IAED,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;QACrB,MAAM,IAAI,IAAI,GAAG,IAAI,CAAC;KACzB;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAdD,oBAcC"}
|
||||
@@ -1,10 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
||||
* the compiler.
|
||||
*/
|
||||
class PhantomData {
|
||||
}
|
||||
exports.PhantomData = PhantomData;
|
||||
//# sourceMappingURL=types.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AAAA;;;GAGG;AACH;CAAsD;AAAtD,kCAAsD"}
|
||||
@@ -5,7 +5,6 @@
|
||||
"license": "GPL-3.0",
|
||||
"description": "Shared utils and types for backend and frontend",
|
||||
"main": "build/index.js",
|
||||
"types": "src/index.ts",
|
||||
"engines": {
|
||||
"node": ">=9.5"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
||||
* the compiler.
|
||||
*/
|
||||
export class PhantomData<P> { private __PHANTOM__: P }
|
||||
|
||||
/**
|
||||
* Opaque type, similar to `opaque type` in Flow, or new types in Rust/C.
|
||||
* These should be produced only by manually casting `t as Opaque<T, P>`.
|
||||
*
|
||||
* `P` can be anything as it's never actually used. Using strings is okay:
|
||||
*
|
||||
* ```
|
||||
* type MyType = Opaque<number, 'MyType'>;
|
||||
* ```
|
||||
*/
|
||||
export type Opaque<T, P> = T & PhantomData<P>;
|
||||
|
||||
/**
|
||||
* Just a readable shorthand for null-ish-able types, akin to `T?` in Flow.
|
||||
*/
|
||||
export type Maybe<T> = T | null | undefined;
|
||||
+14
-10
@@ -1,4 +1,4 @@
|
||||
import { Opaque } from './types';
|
||||
import { Opaque } from './helpers';
|
||||
|
||||
/**
|
||||
* Unique type-constrained Id number.
|
||||
@@ -8,28 +8,32 @@ export type Id<T> = Opaque<number, T>;
|
||||
/**
|
||||
* Higher order function producing new auto-incremented `Id`s.
|
||||
*/
|
||||
export function idGenerator<T>(): () => Id<T> {
|
||||
export function idGenerator<I extends Id<any>>(): () => I {
|
||||
let current = 0;
|
||||
|
||||
return () => current++ as Id<T>;
|
||||
return () => current++ as I;
|
||||
}
|
||||
|
||||
interface HasId<T> {
|
||||
id: Id<T>;
|
||||
interface HasId<I> {
|
||||
id: I;
|
||||
}
|
||||
|
||||
export class IdSet<T> {
|
||||
private map: Map<Id<T>, T> = new Map();
|
||||
export class IdSet<I extends Id<any>, T> {
|
||||
private map: Map<I, T> = new Map();
|
||||
|
||||
public add(item: T & HasId<T>) {
|
||||
public add(item: T & HasId<I>) {
|
||||
this.map.set(item.id, item);
|
||||
}
|
||||
|
||||
public remove(item: T & HasId<T>) {
|
||||
public remove(item: T & HasId<I>) {
|
||||
this.map.delete(item.id);
|
||||
}
|
||||
|
||||
public get entries(): IterableIterator<T> {
|
||||
public entries(): IterableIterator<[I, T]> {
|
||||
return this.map.entries();
|
||||
}
|
||||
|
||||
public values(): IterableIterator<T> {
|
||||
return this.map.values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export * from './id';
|
||||
export * from './iterators';
|
||||
export * from './types';
|
||||
export * from './helpers';
|
||||
export * from './id';
|
||||
|
||||
import * as Types from './types';
|
||||
|
||||
export { Types };
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
/**
|
||||
* PhantomData akin to Rust, because sometimes you need to be smarter than
|
||||
* the compiler.
|
||||
*/
|
||||
export class PhantomData<P> { private __PHANTOM__: P }
|
||||
import { Opaque } from './helpers';
|
||||
import { Id } from './id';
|
||||
|
||||
/**
|
||||
* Opaque type, similar to `opaque type` in Flow, or new types in Rust/C.
|
||||
* These should be produced only by manually casting `t as Opaque<T, P>`.
|
||||
*
|
||||
* `P` can be anything as it's never actually used. Using strings is okay:
|
||||
*
|
||||
* ```
|
||||
* type MyType = Opaque<number, 'MyType'>;
|
||||
* ```
|
||||
*/
|
||||
export type Opaque<T, P> = T & PhantomData<P>;
|
||||
export type FeedId = Id<'Feed'>;
|
||||
export type NodeId = Id<'Node'>;
|
||||
export type NodeName = Opaque<string, 'NodeName'>;
|
||||
export type BlockNumber = Opaque<number, 'BlockNumber'>;
|
||||
export type Milliseconds = Opaque<number, 'Milliseconds'>;
|
||||
|
||||
/**
|
||||
* Just a readable shorthand for null-ish-able types, akin to `T?` in Flow.
|
||||
*/
|
||||
export type Maybe<T> = T | null | undefined;
|
||||
export interface BlockDetails {
|
||||
height: BlockNumber;
|
||||
blockTime: Milliseconds;
|
||||
}
|
||||
|
||||
export interface NodeDetails {
|
||||
name: NodeName;
|
||||
}
|
||||
|
||||
interface BestBlock {
|
||||
action: 'best';
|
||||
payload: BlockNumber;
|
||||
}
|
||||
|
||||
interface AddedNode {
|
||||
action: 'added';
|
||||
payload: [NodeId, NodeDetails, BlockDetails];
|
||||
}
|
||||
|
||||
interface RemovedNode {
|
||||
action: 'removed';
|
||||
payload: NodeId;
|
||||
}
|
||||
|
||||
interface Imported {
|
||||
action: 'imported';
|
||||
payload: [NodeId, BlockDetails];
|
||||
}
|
||||
|
||||
export type FeedMessage = BestBlock | AddedNode | RemovedNode | Imported;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "build",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,51 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import './App.css';
|
||||
import { Id } from '@dotstats/common';
|
||||
|
||||
export interface NodeInfo {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface BlockInfo {
|
||||
height: number;
|
||||
blockTime: number;
|
||||
}
|
||||
|
||||
interface BestBlock {
|
||||
action: 'best';
|
||||
payload: number;
|
||||
}
|
||||
|
||||
interface AddedNode {
|
||||
action: 'added';
|
||||
payload: [Id<Node>, NodeInfo, BlockInfo];
|
||||
}
|
||||
|
||||
interface RemovedNode {
|
||||
action: 'removed';
|
||||
payload: Id<Node>;
|
||||
}
|
||||
|
||||
interface Imported {
|
||||
action: 'imported';
|
||||
payload: [Id<Node>, BlockInfo];
|
||||
}
|
||||
|
||||
type Message = BestBlock | AddedNode | RemovedNode | Imported;
|
||||
import { Types } from '@dotstats/common';
|
||||
|
||||
interface Node {
|
||||
nodeInfo: NodeInfo,
|
||||
blockInfo: BlockInfo,
|
||||
nodeDetails: Types.NodeDetails,
|
||||
blockDetails: Types.BlockDetails,
|
||||
}
|
||||
|
||||
interface State {
|
||||
best: number,
|
||||
nodes: Map<Id<Node>, Node>
|
||||
best: Types.BlockNumber,
|
||||
nodes: Map<Types.NodeId, Node>
|
||||
}
|
||||
|
||||
export default class App extends React.Component<{}, State> {
|
||||
public state: State = {
|
||||
best: 0,
|
||||
best: 0 as Types.BlockNumber,
|
||||
nodes: new Map()
|
||||
};
|
||||
|
||||
@@ -73,9 +42,9 @@ export default class App extends React.Component<{}, State> {
|
||||
{
|
||||
this.nodes().map(([ id, node ]) => (
|
||||
<tr key={id}>
|
||||
<td>{node.nodeInfo.name}</td>
|
||||
<td>{node.blockInfo.height}</td>
|
||||
<td>{node.blockInfo.blockTime / 1000}s</td>
|
||||
<td>{node.nodeDetails.name}</td>
|
||||
<td>{node.blockDetails.height}</td>
|
||||
<td>{node.blockDetails.blockTime / 1000}s</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
@@ -85,11 +54,11 @@ export default class App extends React.Component<{}, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private nodes(): Array<[Id<Node>, Node]> {
|
||||
private nodes(): Array<[Types.NodeId, Node]> {
|
||||
return Array.from(this.state.nodes.entries());
|
||||
}
|
||||
|
||||
private onMessage(message: Message) {
|
||||
private onMessage(message: Types.FeedMessage) {
|
||||
const { nodes } = this.state;
|
||||
|
||||
switch (message.action) {
|
||||
@@ -98,8 +67,8 @@ export default class App extends React.Component<{}, State> {
|
||||
}
|
||||
return;
|
||||
case 'added': {
|
||||
const [id, nodeInfo, blockInfo] = message.payload;
|
||||
const node = { nodeInfo, blockInfo };
|
||||
const [id, nodeDetails, blockDetails] = message.payload;
|
||||
const node = { nodeDetails, blockDetails };
|
||||
|
||||
nodes.set(id, node);
|
||||
}
|
||||
@@ -109,7 +78,7 @@ export default class App extends React.Component<{}, State> {
|
||||
}
|
||||
break;
|
||||
case 'imported': {
|
||||
const [id, blockInfo] = message.payload;
|
||||
const [id, blockDetails] = message.payload;
|
||||
|
||||
const node = nodes.get(id);
|
||||
|
||||
@@ -117,7 +86,7 @@ export default class App extends React.Component<{}, State> {
|
||||
return;
|
||||
}
|
||||
|
||||
node.blockInfo = blockInfo;
|
||||
node.blockDetails = blockDetails;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../shared
|
||||
@@ -8,7 +8,10 @@
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"jsx": "react",
|
||||
"rootDir": "src"
|
||||
"rootDir": "src",
|
||||
"paths": {
|
||||
"@dotstats/common": [ "../common/src" ]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
@@ -20,6 +23,8 @@
|
||||
"src/setupTests.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"../common/src/**/*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"ordered-imports": false,
|
||||
"no-console": false,
|
||||
"interface-name": false,
|
||||
"no-unused-variable": [true, {"ignore-pattern": "^_"}],
|
||||
|
||||
+5
-3
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"outDir": "build",
|
||||
"sourceMap": true,
|
||||
"moduleResolution": "node",
|
||||
"noEmitOnError": false,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
"pretty": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noErrorTruncation": true,
|
||||
@@ -15,7 +14,10 @@
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"paths": {
|
||||
"@dotstats/common": [ "packages/common/src" ]
|
||||
}
|
||||
},
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
|
||||
Reference in New Issue
Block a user