@speckle/shared
Version:
Shared code between various Speckle JS packages
139 lines • 5.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.xor = exports.waitForever = exports.isArrayOf = exports.coerceUndefinedValuesToNull = exports.removeNullOrUndefinedKeys = exports.profileSync = exports.profile = exports.retry = exports.timeoutAt = exports.isNonNullable = exports.waitIntervalUntil = exports.wait = exports.isNullOrUndefined = exports.buildManualPromise = exports.WaitIntervalUntilCanceledError = exports.TimeoutError = void 0;
const _lodash_1 = require("#lodash");
const error_js_1 = require("./error.js");
class TimeoutError extends Error {
}
exports.TimeoutError = TimeoutError;
class WaitIntervalUntilCanceledError extends Error {
}
exports.WaitIntervalUntilCanceledError = WaitIntervalUntilCanceledError;
/**
* Build promise that can be resolved/rejected manually outside of the promise's execution scope
*/
const buildManualPromise = () => {
let resolve;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
const resolveWrapper = (...args) => resolve(...args);
const rejectWrapper = (...args) => reject(...args);
return { promise, resolve: resolveWrapper, reject: rejectWrapper };
};
exports.buildManualPromise = buildManualPromise;
const isNullOrUndefined = (val) => (0, _lodash_1.isNull)(val) || (0, _lodash_1.isUndefined)(val);
exports.isNullOrUndefined = isNullOrUndefined;
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
exports.wait = wait;
const waitIntervalUntil = (ms, predicate) => {
const { promise, resolve, reject } = (0, exports.buildManualPromise)();
const interval = setInterval(() => {
if (predicate()) {
clearInterval(interval);
resolve();
}
}, ms);
const ret = promise;
ret.cancel = () => {
clearInterval(interval);
reject(new WaitIntervalUntilCanceledError());
};
return ret;
};
exports.waitIntervalUntil = waitIntervalUntil;
/**
* Not nullable type guard, useful in `.filter()` calls for proper TS typed
* results
*/
const isNonNullable = (v) => !!v;
exports.isNonNullable = isNonNullable;
/**
* Make the promise throw after enough time has passed. Useful for implementing timeout functionality in various flows.
*/
const timeoutAt = (ms, optionalMessage) => {
// create error beforehand, so we have a better stack trace
const err = new TimeoutError(optionalMessage || 'timeoutAt() timed out');
return new Promise((_resolve, reject) => setTimeout(() => {
reject(err);
}, ms));
};
exports.timeoutAt = timeoutAt;
/**
* Invoke and return fn(), but retry it up to n times if it throws
*/
const retry = async (fn, n, delayMs) => {
let lastError;
for (let i = 0; i < n; i++) {
try {
const res = await Promise.resolve(fn());
return res;
}
catch (error) {
lastError = (0, error_js_1.ensureError)(error);
if (delayMs && i + 1 < n) {
if ((0, _lodash_1.isNumber)(delayMs)) {
await (0, exports.wait)(delayMs);
}
else {
await (0, exports.wait)(delayMs(i + 1, lastError));
}
}
}
}
throw lastError || new Error('Unexpected retry() failure');
};
exports.retry = retry;
/**
* For quickly profiling a function
*/
const profile = async (fn, label, extra) => {
const start = performance.now();
const res = await Promise.resolve(fn());
const end = performance.now();
console.log(`[${label || 'profile'}] took ${end - start}ms`, ...(extra ? [extra] : []));
return res;
};
exports.profile = profile;
/**
* For quickly profiling a sync function
*/
const profileSync = (fn, label, extra) => {
const start = performance.now();
const res = fn();
const end = performance.now();
console.log(`[${label || 'profile'}] took ${end - start}ms`, ...(extra ? [extra] : []));
return res;
};
exports.profileSync = profileSync;
const removeNullOrUndefinedKeys = (obj) => {
const ret = {};
for (const key in obj) {
if (!(0, exports.isNullOrUndefined)(obj[key])) {
ret[key] = obj[key];
}
}
return ret;
};
exports.removeNullOrUndefinedKeys = removeNullOrUndefinedKeys;
const coerceUndefinedValuesToNull = (obj) => {
const ret = {};
for (const [key, value] of Object.entries(obj)) {
ret[key] = (0, _lodash_1.isUndefined)(value) ? null : value;
}
return ret;
};
exports.coerceUndefinedValuesToNull = coerceUndefinedValuesToNull;
const isArrayOf = (arr, guard) => Array.isArray(arr) && arr.every(guard);
exports.isArrayOf = isArrayOf;
const waitForever = () => new Promise(_lodash_1.noop);
exports.waitForever = waitForever;
/**
* Returns true if only one of the arguments is truthy
*/
const xor = (a, b) => !!((a || b) && !(a && b));
exports.xor = xor;
//# sourceMappingURL=utility.js.map