1 import { getRandomValues
} from
'node:crypto'
2 import * as os from
'node:os'
4 import type { KillBehavior
} from
'./worker/worker-options.js'
9 export const DEFAULT_TASK_NAME
= 'default'
12 * An intentional empty function.
14 export const EMPTY_FUNCTION
: () => void = Object.freeze(() => {
15 /* Intentionally empty */
19 * Returns safe host OS optimized estimate of the default amount of parallelism a pool should use.
20 * Always returns a value greater than zero.
22 * @returns The host OS optimized maximum pool size.
24 export const availableParallelism
= (): number => {
25 let availableParallelism
= 1
27 availableParallelism
= os
.availableParallelism()
29 const cpus
= os
.cpus()
30 if (Array.isArray(cpus
) && cpus
.length
> 0) {
31 availableParallelism
= cpus
.length
34 return availableParallelism
38 * Sleeps for the given amount of milliseconds.
40 * @param ms - The amount of milliseconds to sleep.
41 * @returns A promise that resolves after the given amount of milliseconds.
44 export const sleep
= async (ms
: number): Promise
<void> => {
45 await new Promise(resolve
=> {
46 setTimeout(resolve
, ms
)
51 * Computes the retry delay in milliseconds using an exponential back off algorithm.
53 * @param retryNumber - The number of retries that have already been attempted
54 * @param delayFactor - The base delay factor in milliseconds
55 * @returns Delay in milliseconds
58 export const exponentialDelay
= (
62 const delay
= Math.pow(2, retryNumber
) * delayFactor
63 const randomSum
= delay
* 0.2 * secureRandom() // 0-20% of the delay
64 return delay
+ randomSum
68 * Computes the average of the given data set.
70 * @param dataSet - Data set.
71 * @returns The average of the given data set.
74 export const average
= (dataSet
: number[]): number => {
75 if (Array.isArray(dataSet
) && dataSet
.length
=== 0) {
77 } else if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
81 dataSet
.reduce((accumulator
, number) => accumulator
+ number, 0) /
87 * Computes the median of the given data set.
89 * @param dataSet - Data set.
90 * @returns The median of the given data set.
93 export const median
= (dataSet
: number[]): number => {
94 if (Array.isArray(dataSet
) && dataSet
.length
=== 0) {
96 } else if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
99 const sortedDataSet
= dataSet
.slice().sort((a
, b
) => a
- b
)
101 (sortedDataSet
[(sortedDataSet
.length
- 1) >> 1] +
102 sortedDataSet
[sortedDataSet
.length
>> 1]) /
108 * Rounds the given number to the given scale.
109 * The rounding is done using the "round half away from zero" method.
111 * @param num - The number to round.
112 * @param scale - The scale to round to.
113 * @returns The rounded number.
116 export const round
= (num
: number, scale
= 2): number => {
117 const rounder
= Math.pow(10, scale
)
118 return Math.round(num
* rounder
* (1 + Number.EPSILON
)) / rounder
122 * Is the given value a plain object?
124 * @param value - The value to check.
125 * @returns `true` if the given value is a plain object, `false` otherwise.
128 export const isPlainObject
= (value
: unknown
): value
is object
=>
129 typeof value
=== 'object' &&
131 value
.constructor
=== Object &&
132 Object.prototype
.toString
.call(value
) === '[object Object]'
135 * Detects whether the given value is a kill behavior or not.
137 * @typeParam KB - Which specific KillBehavior type to test against.
138 * @param killBehavior - Which kind of kill behavior to detect.
139 * @param value - Unknown value.
140 * @returns `true` if `value` was strictly equals to `killBehavior`, otherwise `false`.
143 export const isKillBehavior
= <KB
extends KillBehavior
>(
147 return value
=== killBehavior
151 * Detects whether the given value is an asynchronous function or not.
153 * @param fn - Unknown value.
154 * @returns `true` if `fn` was an asynchronous function, otherwise `false`.
157 export const isAsyncFunction
= (
159 ): fn
is (...args
: unknown
[]) => Promise
<unknown
> => {
160 return typeof fn
=== 'function' && fn
.constructor
.name
=== 'AsyncFunction'
164 * Generates a cryptographically secure random number in the [0,1[ range
166 * @returns A number in the [0,1[ range
169 export const secureRandom
= (): number => {
170 return getRandomValues(new Uint32Array(1))[0] / 0x100000000
174 * Returns the minimum of the given numbers.
175 * If no numbers are given, `Infinity` is returned.
177 * @param args - The numbers to get the minimum of.
178 * @returns The minimum of the given numbers.
181 export const min
= (...args
: number[]): number =>
182 args
.reduce((minimum
, num
) => (minimum
< num
? minimum
: num
), Infinity)
185 * Returns the maximum of the given numbers.
186 * If no numbers are given, `-Infinity` is returned.
188 * @param args - The numbers to get the maximum of.
189 * @returns The maximum of the given numbers.
192 export const max
= (...args
: number[]): number =>
193 args
.reduce((maximum
, num
) => (maximum
> num
? maximum
: num
), -Infinity)
196 * Wraps a function so that it can only be called once.
198 * @param fn - The function to wrap.
199 * @param context - The context to bind the function to.
200 * @returns The wrapped function.
203 // eslint-disable-next-line @typescript-eslint/no-explicit-any
204 export const once
= <A
extends any[], R
, T
>(
205 fn
: (...args
: A
) => R
,
207 ): ((...args
: A
) => R
) => {
209 return (...args
: A
) => {
210 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
212 result
= fn
.apply
<T
, A
, R
>(context
, args
)
213 ;(fn
as unknown
as undefined) = (context
as unknown
as undefined) =