/**
* @module Utils
* @description A set of utilities
*/
/**
* Sum the values of the array (must be numbers).
*
* @static
* @method sum
*
* @param array {Array} array of number values
* @return {Number} the sum of the values in the array
*/
export function sum(array) {
return (array || []).reduce(function (memo, num) {
if (typeof num === "number") {
memo += num;
}
return memo;
}, 0);
}
export function is(obj, type) {
return Object.prototype.toString.call(obj) === "[object " + type + "]";
}
const interestingValues = (entry) =>
typeof entry[1] !== "undefined" && entry[1] !== "undefined" && entry[1] !== Number.MAX_VALUE;
const formatEntry = (entry) => `${entry[0]} = ${entry[1]}`;
const formatObject = (entry) =>
typeof entry[1] === "object"
? [entry[0], `{${entry[1]?.type ? entry[1].type : "object"}}`]
: entry;
// This displays well in Firefox, it doesn’t add much on Chrome
class Logger {
#enabled = true;
#name = [];
constructor() {}
enable() {
this.#enabled = true;
}
disable() {
this.#enabled = false;
}
start(name, params = {}) {
if (!this.#enabled) return;
this.#name.push(name);
const paramsString = Object.entries(params)
.filter(interestingValues)
.map(formatObject)
.map(formatEntry)
.join(", ");
console.groupCollapsed(`${name}: ${paramsString}`);
}
end(obj) {
if (!this.#enabled) return obj;
console.groupEnd();
if (typeof obj === "string") {
console.log("%c" + obj, "color: #2289B6");
} else {
console.log(obj);
}
return obj;
}
}
export const logger = new Logger();
/**
* Yields control back to the event loop as a macrotask (not just a microtask), so that
* anything else already queued to run — e.g. a Comlink call waiting on the same worker's
* message queue — gets a chance to execute before the caller resumes. Works identically in
* a browser Worker and a Node worker_threads Worker, since both implement setTimeout.
*
* @returns {Promise<void>}
*/
export function yieldToEventLoop() {
return new Promise((resolve) => setTimeout(resolve, 0));
}
/**
* The idiomatic way of trying something repeatedly until the outcome is acceptable can be
* awkard to express (do/while loop), so this converts it into something that is easier to
* read and understand. There is a counter to break out of accidental infinite loops.
*
* @param {Function} generationFunc a function that returns a value
* @param {Function} testFunc that returns true if result is acceptable, false otherwise
* @returns {*} the final result
*/
export function tryUntil(generationFunc, testFunc) {
let num = 0;
do {
if (num > 50) {
console.error("tryUntil could not find result in 50 iterations");
return generationFunc();
}
num++;
var result = generationFunc();
} while (!testFunc(result));
return result;
}