1 import crypto from
'crypto';
3 import { WebSocketCloseEventStatusString
} from
'../types/WebSocket';
5 export default class Utils
{
6 private constructor() {
10 public static logPrefix(prefixString
= ''): string {
11 return new Date().toLocaleString() + prefixString
;
14 public static generateUUID(): string {
15 return crypto
.randomUUID();
18 public static validateUUID(uuid
: string): boolean {
19 return /\
/^[0-9A
-F
]{8}-[0-9A
-F
]{4}-4[0-9A
-F
]{3}-[89AB
][0-9A
-F
]{3}-[0-9A
-F
]{12}$\
/i
/.test(uuid
);
22 public static async sleep(milliSeconds
: number): Promise
<NodeJS
.Timeout
> {
23 return new Promise((resolve
) => setTimeout(resolve
as () => void, milliSeconds
));
26 public static formatDurationMilliSeconds(duration
: number): string {
27 duration
= Utils
.convertToInt(duration
);
28 const hours
= Math.floor(duration
/ (3600 * 1000));
29 const minutes
= Math.floor((duration
/ 1000 - hours
* 3600) / 60);
30 const seconds
= duration
/ 1000 - hours
* 3600 - minutes
* 60;
31 let hoursStr
= hours
.toString();
32 let minutesStr
= minutes
.toString();
33 let secondsStr
= seconds
.toString();
36 hoursStr
= '0' + hours
.toString();
39 minutesStr
= '0' + minutes
.toString();
42 secondsStr
= '0' + seconds
.toString();
44 return hoursStr
+ ':' + minutesStr
+ ':' + secondsStr
.substring(0, 6);
47 public static formatDurationSeconds(duration
: number): string {
48 return Utils
.formatDurationMilliSeconds(duration
* 1000);
51 public static convertToDate(value
: unknown
): Date {
57 if (!(value
instanceof Date)) {
58 return new Date(value
as string);
63 public static convertToInt(value
: unknown
): number {
64 let changedValue
: number = value
as number;
68 if (Number.isSafeInteger(value
)) {
69 return value
as number;
72 if (Utils
.isString(value
)) {
74 changedValue
= parseInt(value
as string);
79 public static convertToFloat(value
: unknown
): number {
80 let changedValue
: number = value
as number;
85 if (Utils
.isString(value
)) {
87 changedValue
= parseFloat(value
as string);
92 public static convertToBoolean(value
: unknown
): boolean {
97 if (typeof value
=== 'boolean') {
102 result
= value
=== 'true';
108 public static getRandomFloat(max
= Number.MAX_VALUE
, min
= 0, negative
= false): number {
109 if (max
< min
|| max
< 0 || min
< 0) {
110 throw new RangeError('Invalid interval');
112 const randomPositiveFloat
= crypto
.randomBytes(4).readUInt32LE() / 0xffffffff;
113 const sign
= negative
&& randomPositiveFloat
< 0.5 ? -1 : 1;
114 return sign
* (randomPositiveFloat
* (max
- min
) + min
);
117 public static getRandomInteger(max
= Number.MAX_SAFE_INTEGER
, min
= 0): number {
118 if (max
< min
|| max
< 0 || min
< 0) {
119 throw new RangeError('Invalid interval');
121 max
= Math.floor(max
);
122 if (!Utils
.isNullOrUndefined(min
) && min
!== 0) {
123 min
= Math.ceil(min
);
124 return Math.floor(Utils
.secureRandom() * (max
- min
+ 1)) + min
;
126 return Math.floor(Utils
.secureRandom() * (max
+ 1));
129 public static roundTo(numberValue
: number, scale
: number): number {
130 const roundPower
= Math.pow(10, scale
);
131 return Math.round(numberValue
* roundPower
) / roundPower
;
134 public static truncTo(numberValue
: number, scale
: number): number {
135 const truncPower
= Math.pow(10, scale
);
136 return Math.trunc(numberValue
* truncPower
) / truncPower
;
139 public static getRandomFloatRounded(max
= Number.MAX_VALUE
, min
= 0, scale
= 2): number {
141 return Utils
.roundTo(Utils
.getRandomFloat(max
, min
), scale
);
143 return Utils
.roundTo(Utils
.getRandomFloat(max
), scale
);
146 public static getRandomFloatFluctuatedRounded(
148 fluctuationPercent
: number,
151 if (fluctuationPercent
=== 0) {
152 return Utils
.roundTo(staticValue
, scale
);
154 const fluctuationRatio
= fluctuationPercent
/ 100;
155 return Utils
.getRandomFloatRounded(
156 staticValue
* (1 + fluctuationRatio
),
157 staticValue
* (1 - fluctuationRatio
),
162 public static cloneObject
<T
>(object
: T
): T
{
163 return JSON
.parse(JSON
.stringify(object
)) as T
;
166 public static isIterable
<T
>(obj
: T
): boolean {
167 return obj
? typeof obj
[Symbol
.iterator
] === 'function' : false;
170 public static isString(value
: unknown
): boolean {
171 return typeof value
=== 'string';
174 public static isEmptyString(value
: unknown
): boolean {
175 return Utils
.isString(value
) && (value
as string).trim().length
=== 0;
178 public static isUndefined(value
: unknown
): boolean {
179 return typeof value
=== 'undefined';
182 public static isNullOrUndefined(value
: unknown
): boolean {
183 // eslint-disable-next-line eqeqeq, no-eq-null
184 return value
== null ? true : false;
187 public static isEmptyArray(object
: unknown
): boolean {
191 if (Array.isArray(object
) === true && (object
as unknown
[]).length
> 0) {
197 public static isEmptyObject(obj
: object
): boolean {
198 if (obj
.constructor
!== Object) {
201 // Iterates over the keys of an object, if
202 // any exist, return false.
203 for (const _
in obj
) {
209 public static insertAt
= (str
: string, subStr
: string, pos
: number): string =>
210 `${str.slice(0, pos)}${subStr}${str.slice(pos)}`;
213 * @param [retryNumber=0]
214 * @returns delay in milliseconds
216 public static exponentialDelay(retryNumber
= 0): number {
217 const delay
= Math.pow(2, retryNumber
) * 100;
218 const randomSum
= delay
* 0.2 * Utils
.secureRandom(); // 0-20% of the delay
219 return delay
+ randomSum
;
222 public static async promiseWithTimeout
<T
>(
226 timeoutCallback
: () => void = () => {
227 /* This is intentional */
230 // Create a timeout promise that rejects in timeout milliseconds
231 const timeoutPromise
= new Promise
<never>((_
, reject
) => {
234 reject(timeoutError
);
238 // Returns a race between timeout promise and the passed promise
239 return Promise
.race
<T
>([promise
, timeoutPromise
]);
243 * Generate a cryptographically secure random number in the [0,1[ range
247 public static secureRandom(): number {
248 return crypto
.randomBytes(4).readUInt32LE() / 0x100000000;
251 public static JSONStringifyWithMapSupport(
252 obj
: Record
<string, unknown
> | Record
<string, unknown
>[],
255 return JSON
.stringify(
257 (key
, value
: Record
<string, unknown
>) => {
258 if (value
instanceof Map
) {
271 * Convert websocket error code to human readable string message
273 * @param code websocket error code
274 * @returns human readable string message
276 public static getWebSocketCloseEventStatusString(code
: number): string {
277 if (code
>= 0 && code
<= 999) {
279 } else if (code
>= 1016) {
281 return '(For WebSocket standard)';
282 } else if (code
<= 2999) {
283 return '(For WebSocket extensions)';
284 } else if (code
<= 3999) {
285 return '(For libraries and frameworks)';
286 } else if (code
<= 4999) {
287 return '(For applications)';
290 if (!Utils
.isUndefined(WebSocketCloseEventStatusString
[code
])) {
291 return WebSocketCloseEventStatusString
[code
] as string;