1 import crypto from
'crypto';
2 import { v4
as uuid
} from
'uuid';
4 export default class Utils
{
5 public static logPrefix(prefixString
= ''): string {
6 return new Date().toLocaleString() + prefixString
;
9 public static generateUUID(): string {
13 public static equals(obj1
: unknown
, obj2
: unknown
): boolean {
14 return JSON
.stringify(obj1
) === JSON
.stringify(obj2
);
17 public static async sleep(milliSeconds
: number): Promise
<NodeJS
.Timeout
> {
18 return new Promise((resolve
) => setTimeout(resolve
as () => void, milliSeconds
));
21 public static formatDurationMilliSeconds(duration
: number): string {
22 duration
= Utils
.convertToInt(duration
);
23 const hours
= Math.floor(duration
/ (3600 * 1000));
24 const minutes
= Math.floor((duration
/ 1000 - hours
* 3600) / 60);
25 const seconds
= duration
/ 1000 - hours
* 3600 - minutes
* 60;
26 let hoursStr
= hours
.toString();
27 let minutesStr
= minutes
.toString();
28 let secondsStr
= seconds
.toString();
31 hoursStr
= '0' + hours
.toString();
34 minutesStr
= '0' + minutes
.toString();
37 secondsStr
= '0' + seconds
.toString();
39 return hoursStr
+ ':' + minutesStr
+ ':' + secondsStr
.substring(0, 6);
42 public static formatDurationSeconds(duration
: number): string {
43 return Utils
.formatDurationMilliSeconds(duration
* 1000);
46 public static convertToDate(value
: unknown
): Date {
52 if (!(value
instanceof Date)) {
53 return new Date(value
as string);
58 public static convertToInt(value
: unknown
): number {
59 let changedValue
: number = value
as number;
63 if (Number.isSafeInteger(value
)) {
64 return value
as number;
67 if (Utils
.isString(value
)) {
69 changedValue
= parseInt(value
as string);
74 public static convertToFloat(value
: unknown
): number {
75 let changedValue
: number = value
as number;
80 if (Utils
.isString(value
)) {
82 changedValue
= parseFloat(value
as string);
87 public static convertToBoolean(value
: unknown
): boolean {
92 if (typeof value
=== 'boolean') {
97 result
= value
=== 'true';
103 public static getRandomFloat(max
: number, min
= 0, negative
= false): number {
104 if (max
< min
|| min
< 0 || max
< 0) {
105 throw new RangeError('Invalid interval');
107 const randomPositiveFloat
= crypto
.randomBytes(4).readUInt32LE() / 0xffffffff;
108 const sign
= negative
&& randomPositiveFloat
< 0.5 ? -1 : 1;
109 return sign
* (randomPositiveFloat
* (max
- min
) + min
);
112 public static getRandomInteger(max
: number, min
= 0): number {
114 throw new RangeError('Invalid interval');
116 max
= Math.floor(max
);
118 if (max
< min
|| min
< 0) {
119 throw new RangeError('Invalid interval');
121 min
= Math.ceil(min
);
122 return Math.floor(Utils
.secureRandom() * (max
- min
+ 1)) + min
;
124 return Math.floor(Utils
.secureRandom() * (max
+ 1));
127 public static roundTo(numberValue
: number, scale
: number): number {
128 const roundPower
= Math.pow(10, scale
);
129 return Math.round(numberValue
* roundPower
) / roundPower
;
132 public static truncTo(numberValue
: number, scale
: number): number {
133 const truncPower
= Math.pow(10, scale
);
134 return Math.trunc(numberValue
* truncPower
) / truncPower
;
137 public static getRandomFloatRounded(max
: number, min
= 0, scale
= 2): number {
139 return Utils
.roundTo(Utils
.getRandomFloat(max
, min
), scale
);
141 return Utils
.roundTo(Utils
.getRandomFloat(max
), scale
);
144 public static getRandomFloatFluctuatedRounded(
146 fluctuationPercent
: number,
149 if (fluctuationPercent
=== 0) {
150 return Utils
.roundTo(staticValue
, scale
);
152 const fluctuationRatio
= fluctuationPercent
/ 100;
153 return Utils
.getRandomFloatRounded(
154 staticValue
* (1 + fluctuationRatio
),
155 staticValue
* (1 - fluctuationRatio
),
160 public static cloneObject
<T
>(object
: T
): T
{
161 return JSON
.parse(JSON
.stringify(object
)) as T
;
164 public static isIterable
<T
>(obj
: T
): boolean {
165 return obj
? typeof obj
[Symbol
.iterator
] === 'function' : false;
168 public static isString(value
: unknown
): boolean {
169 return typeof value
=== 'string';
172 public static isEmptyString(value
: unknown
): boolean {
173 return Utils
.isString(value
) && (value
as string).length
=== 0;
176 public static isUndefined(value
: unknown
): boolean {
177 return typeof value
=== 'undefined';
180 public static isNullOrUndefined(value
: unknown
): boolean {
181 // eslint-disable-next-line eqeqeq, no-eq-null
182 return value
== null ? true : false;
185 public static isEmptyArray(object
: unknown
): boolean {
189 if (Array.isArray(object
) && object
.length
> 0) {
195 public static isEmptyObject(obj
: object
): boolean {
196 return !Object.keys(obj
).length
;
199 public static insertAt
= (str
: string, subStr
: string, pos
: number): string =>
200 `${str.slice(0, pos)}${subStr}${str.slice(pos)}`;
203 * @param [retryNumber=0]
204 * @returns delay in milliseconds
206 public static exponentialDelay(retryNumber
= 0): number {
207 const delay
= Math.pow(2, retryNumber
) * 100;
208 const randomSum
= delay
* 0.2 * Utils
.secureRandom(); // 0-20% of the delay
209 return delay
+ randomSum
;
212 public static async promiseWithTimeout
<T
>(
216 timeoutCallback
: () => void = () => {
217 /* This is intentional */
220 // Create a timeout promise that rejects in timeout milliseconds
221 const timeoutPromise
= new Promise
<never>((_
, reject
) => {
224 reject(timeoutError
);
228 // Returns a race between timeout promise and the passed promise
229 return Promise
.race
<T
>([promise
, timeoutPromise
]);
233 * Generate a cryptographically secure random number in the [0,1[ range
237 public static secureRandom(): number {
238 return crypto
.randomBytes(4).readUInt32LE() / 0x100000000;