1 import * as os from
'node:os'
2 import { getRandomValues
} from
'node:crypto'
3 import type { KillBehavior
} from
'./worker/worker-options.js'
8 export const DEFAULT_TASK_NAME
= 'default'
11 * An intentional empty function.
13 export const EMPTY_FUNCTION
: () => void = Object.freeze(() => {
14 /* Intentionally empty */
18 * Returns safe host OS optimized estimate of the default amount of parallelism a pool should use.
19 * Always returns a value greater than zero.
21 * @returns The host OS optimized maximum pool size.
23 export const availableParallelism
= (): number => {
24 let availableParallelism
= 1
26 availableParallelism
= os
.availableParallelism()
28 const cpus
= os
.cpus()
29 if (Array.isArray(cpus
) && cpus
.length
> 0) {
30 availableParallelism
= cpus
.length
33 return availableParallelism
37 * Sleeps for the given amount of milliseconds.
39 * @param ms - The amount of milliseconds to sleep.
40 * @returns A promise that resolves after the given amount of milliseconds.
43 export const sleep
= async (ms
: number): Promise
<void> => {
44 await new Promise(resolve
=> {
45 setTimeout(resolve
, ms
)
50 * Computes the retry delay in milliseconds using an exponential back off algorithm.
52 * @param retryNumber - The number of retries that have already been attempted
53 * @param delayFactor - The base delay factor in milliseconds
54 * @returns Delay in milliseconds
57 export const exponentialDelay
= (
61 const delay
= Math.pow(2, retryNumber
) * delayFactor
62 const randomSum
= delay
* 0.2 * secureRandom() // 0-20% of the delay
63 return delay
+ randomSum
67 * Computes the average of the given data set.
69 * @param dataSet - Data set.
70 * @returns The average of the given data set.
73 export const average
= (dataSet
: number[]): number => {
74 if (Array.isArray(dataSet
) && dataSet
.length
=== 0) {
76 } else if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
80 dataSet
.reduce((accumulator
, number) => accumulator
+ number, 0) /
86 * Computes the median of the given data set.
88 * @param dataSet - Data set.
89 * @returns The median of the given data set.
92 export const median
= (dataSet
: number[]): number => {
93 if (Array.isArray(dataSet
) && dataSet
.length
=== 0) {
95 } else if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
98 const sortedDataSet
= dataSet
.slice().sort((a
, b
) => a
- b
)
100 (sortedDataSet
[(sortedDataSet
.length
- 1) >> 1] +
101 sortedDataSet
[sortedDataSet
.length
>> 1]) /
107 * Rounds the given number to the given scale.
108 * The rounding is done using the "round half away from zero" method.
110 * @param num - The number to round.
111 * @param scale - The scale to round to.
112 * @returns The rounded number.
115 export const round
= (num
: number, scale
= 2): number => {
116 const rounder
= Math.pow(10, scale
)
117 return Math.round(num
* rounder
* (1 + Number.EPSILON
)) / rounder
121 * Is the given value a plain object?
123 * @param value - The value to check.
124 * @returns `true` if the given value is a plain object, `false` otherwise.
127 export const isPlainObject
= (value
: unknown
): value
is object
=>
128 typeof value
=== 'object' &&
130 value
.constructor
=== Object &&
131 Object.prototype
.toString
.call(value
) === '[object Object]'
134 * Detects whether the given value is a kill behavior or not.
136 * @typeParam KB - Which specific KillBehavior type to test against.
137 * @param killBehavior - Which kind of kill behavior to detect.
138 * @param value - Unknown value.
139 * @returns `true` if `value` was strictly equals to `killBehavior`, otherwise `false`.
142 export const isKillBehavior
= <KB
extends KillBehavior
>(
146 return value
=== killBehavior
150 * Detects whether the given value is an asynchronous function or not.
152 * @param fn - Unknown value.
153 * @returns `true` if `fn` was an asynchronous function, otherwise `false`.
156 export const isAsyncFunction
= (
158 ): fn
is (...args
: unknown
[]) => Promise
<unknown
> => {
159 return typeof fn
=== 'function' && fn
.constructor
.name
=== 'AsyncFunction'
163 * Generates a cryptographically secure random number in the [0,1[ range
165 * @returns A number in the [0,1[ range
168 export const secureRandom
= (): number => {
169 return getRandomValues(new Uint32Array(1))[0] / 0x100000000
173 * Returns the minimum of the given numbers.
174 * If no numbers are given, `Infinity` is returned.
176 * @param args - The numbers to get the minimum of.
177 * @returns The minimum of the given numbers.
180 export const min
= (...args
: number[]): number =>
181 args
.reduce((minimum
, num
) => (minimum
< num
? minimum
: num
), Infinity)
184 * Returns the maximum of the given numbers.
185 * If no numbers are given, `-Infinity` is returned.
187 * @param args - The numbers to get the maximum of.
188 * @returns The maximum of the given numbers.
191 export const max
= (...args
: number[]): number =>
192 args
.reduce((maximum
, num
) => (maximum
> num
? maximum
: num
), -Infinity)
195 * Wraps a function so that it can only be called once.
197 * @param fn - The function to wrap.
198 * @param context - The context to bind the function to.
199 * @returns The wrapped function.
202 // eslint-disable-next-line @typescript-eslint/no-explicit-any
203 export const once
= <T
, A
extends any[], R
>(
204 fn
: (...args
: A
) => R
,
206 ): ((...args
: A
) => R
) => {
208 return (...args
: A
) => {
209 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
211 result
= fn
.apply
<T
, A
, R
>(context
, args
)
212 ;(fn
as unknown
as undefined) = (context
as unknown
as undefined) =