Files
pezkuwi-telemetry/frontend/src/common/helpers.ts
T
James Wilson 779c5698a0 Update frontend build system to shed old dependencies, and address fallout from that (#521)
* get tests working and update copyright headers

* lint fix

* eslint ts update

* update CI to use NPM

* fix CI

* prettier

* npm, not yarn for docker

* remove unused package

* bump dockerfile to latest node LTS and no need for cache clean

* make frontend Dockerfile work and remove unused SUBSTRATE_TELEMETRY_SAMPLE

* remove final yarn references

* add another comment

* remove unused import

* remove more unused deps

* comment typo
2023-01-27 14:13:34 +00:00

135 lines
3.4 KiB
TypeScript

// Source code for the Substrate Telemetry Server.
// Copyright (C) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { Milliseconds, Timestamp } from './types';
/**
* PhantomData akin to Rust, because sometimes you need to be smarter than
* the compiler.
*/
export abstract class PhantomData<P> {
public __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;
/**
* Asynchronous sleep
*/
export function sleep(time: Milliseconds): Promise<void> {
return new Promise<void>((resolve) => {
setTimeout(() => resolve(), time);
});
}
export const timestamp = Date.now as () => Timestamp;
/**
* Keep track of last N numbers pushed onto internal stack.
* Provides means to get an average of said numbers.
*/
export class NumStats<T extends number> {
private readonly stack: Array<T>;
private readonly history: number;
private index = 0;
constructor(history: number) {
if (history < 1) {
throw new Error('Must track at least one number');
}
this.history = history;
this.stack = new Array(history);
}
public push(val: T) {
this.stack[this.index++ % this.history] = val;
}
/**
* Get average value of all values on the stack.
*
* @return {T} average value
*/
public average(): T {
if (this.index === 0) {
return 0 as T;
}
const list = this.nonEmpty();
let sum = 0;
for (const n of list as Array<number>) {
sum += n;
}
return (sum / list.length) as T;
}
/**
* Get average value of all values of the stack after filtering
* out a number of highest and lowest values
*
* @param {number} extremes number of high/low values to ignore
* @return {T} average value
*/
public averageWithoutExtremes(extremes: number): T {
if (this.index === 0) {
return 0 as T;
}
const list = this.nonEmpty();
const count = list.length - extremes * 2;
if (count < 1) {
// Not enough entries to remove desired number of extremes,
// fall back to regular average
return this.average();
}
let sum = 0;
for (const n of [...list]
.sort((a, b) => a - b)
.slice(extremes, -extremes)) {
sum += n;
}
return (sum / count) as T;
}
private nonEmpty(): Readonly<Array<number>> {
return this.index < this.history
? this.stack.slice(0, this.index)
: this.stack;
}
}