-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclientsView.js
More file actions
116 lines (106 loc) · 3.53 KB
/
Copy pathclientsView.js
File metadata and controls
116 lines (106 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { getRpcMonitoringResults } from './dataService.js';
import { parseClientVersion } from './clientParser.js';
/**
* Aggregate parsed client software across working RPC endpoints.
*
* When `chainId` is provided, returns a single summary for that chain
* (or null if no monitoring data exists for it). The single-chain form
* returns the summary even if every working endpoint reported an
* unparseable client (so the caller can still see `totalNodes` /
* `unknownNodes`).
*
* When `chainId` is omitted, returns an array of summaries — one per
* chain with at least one parseable client. Chains where every working
* endpoint failed to parse are excluded from the list so `/clients`
* stays a directory of *known* client software.
*
* Each summary:
* {
* chainId, chainName,
* totalNodes, // working endpoints considered
* unknownNodes, // working endpoints whose client didn't parse
* clients: [
* { name, repo, language, website, layer, known,
* nodeCount, versions: [{ version, nodeCount }, ...] }
* ]
* }
*
* @param {number} [chainId]
* @returns {object | object[] | null}
*/
export function getClientsByChain(chainId) {
const { results } = getRpcMonitoringResults();
const working = results.filter(r => r.status === 'working');
if (chainId !== undefined) {
const chainResults = working.filter(r => r.chainId === chainId);
if (chainResults.length === 0) return null;
return summarizeChainClients(chainResults);
}
const byChain = new Map();
for (const r of working) {
if (!byChain.has(r.chainId)) byChain.set(r.chainId, []);
byChain.get(r.chainId).push(r);
}
return Array.from(byChain.values())
.map(summarizeChainClients)
.filter(summary => summary && summary.clients.length > 0);
}
/**
* Build a per-chain client summary from a list of endpoint results.
* Parses `clientVersion` lazily via parseClientVersion. Non-working entries
* are ignored. Assumes all entries share the same chainId. Returns null if
* no working endpoints were supplied.
*/
export function summarizeChainClients(chainResults) {
chainResults = chainResults.filter(r => r.status === 'working');
if (chainResults.length === 0) return null;
const { chainId, chainName } = chainResults[0];
const byClient = new Map();
let unknownNodes = 0;
for (const r of chainResults) {
const client = parseClientVersion(r.clientVersion);
if (!client) {
unknownNodes++;
continue;
}
const key = client.name;
let bucket = byClient.get(key);
if (!bucket) {
bucket = {
name: client.name,
repo: client.repo,
language: client.language,
website: client.website,
layer: client.layer,
known: client.known,
nodeCount: 0,
_versions: new Map()
};
byClient.set(key, bucket);
}
bucket.nodeCount++;
const v = client.version ?? 'unknown';
bucket._versions.set(v, (bucket._versions.get(v) ?? 0) + 1);
}
const clients = Array.from(byClient.values())
.map(c => ({
name: c.name,
repo: c.repo,
language: c.language,
website: c.website,
layer: c.layer,
known: c.known,
nodeCount: c.nodeCount,
versions: Array.from(c._versions.entries())
.map(([version, nodeCount]) => ({ version, nodeCount }))
.sort((a, b) => b.nodeCount - a.nodeCount)
}))
.sort((a, b) => b.nodeCount - a.nodeCount);
return {
chainId,
chainName,
totalNodes: chainResults.length,
unknownNodes,
clients
};
}