935 lines
25 KiB
JavaScript
935 lines
25 KiB
JavaScript
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message || "Assertion failed");
|
|
}
|
|
}
|
|
function toString(s) {
|
|
return Object.prototype.toString.call(s);
|
|
}
|
|
function getTypeName(s) {
|
|
return s === null ? "null" : typeof s === "object" || typeof s === "function" ? toString(s).slice(8, -1).toLowerCase() : typeof s;
|
|
}
|
|
|
|
function isDef(v) {
|
|
return typeof v !== "undefined";
|
|
}
|
|
function isPrimitive(v) {
|
|
return v === null || typeof v !== "object" && typeof v !== "function";
|
|
}
|
|
function isBoolean(v) {
|
|
return typeof v === "boolean";
|
|
}
|
|
function isFunction(v) {
|
|
return typeof v === "function";
|
|
}
|
|
function isNumber(v) {
|
|
return typeof v === "number";
|
|
}
|
|
function isString(v) {
|
|
return typeof v === "string";
|
|
}
|
|
function isSymbol(v) {
|
|
return typeof v === "symbol";
|
|
}
|
|
function isPlainObject(v) {
|
|
return getTypeName(v) === "object";
|
|
}
|
|
function isArray(v) {
|
|
return Array.isArray(v);
|
|
}
|
|
function isUndefined(v) {
|
|
return typeof v === "undefined";
|
|
}
|
|
function isNull(v) {
|
|
return getTypeName(v) === "null";
|
|
}
|
|
function isRegexp(v) {
|
|
return getTypeName(v) === "regexp";
|
|
}
|
|
function isDate(v) {
|
|
return getTypeName(v) === "date";
|
|
}
|
|
function isEmptyObject(v) {
|
|
if (!isPlainObject(v))
|
|
return false;
|
|
for (const _ in v)
|
|
return false;
|
|
return true;
|
|
}
|
|
function isBlob(v) {
|
|
/* istanbul ignore if -- @preserve */
|
|
if (typeof Blob === "undefined")
|
|
return false;
|
|
return v instanceof Blob;
|
|
}
|
|
function isTypedArray(v) {
|
|
return ArrayBuffer.isView(v) && !(v instanceof DataView);
|
|
}
|
|
function isWindow(v) {
|
|
/* istanbul ignore next -- @preserve */
|
|
return typeof v !== "undefined" && getTypeName(v) === "window";
|
|
}
|
|
function isBrowser() {
|
|
/* istanbul ignore next -- @preserve */
|
|
return typeof window !== "undefined";
|
|
}
|
|
function isJSONObject(obj) {
|
|
if (!isPlainObject(obj)) {
|
|
return false;
|
|
}
|
|
const keys = Reflect.ownKeys(obj);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
const value = obj[key];
|
|
/* istanbul ignore if -- @preserve */
|
|
if (typeof key !== "string") {
|
|
return false;
|
|
}
|
|
if (!isJSONValue(value)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function isJSONArray(value) {
|
|
if (!Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
return value.every((item) => isJSONValue(item));
|
|
}
|
|
function isJSONValue(value) {
|
|
switch (typeof value) {
|
|
case "object": {
|
|
return value === null || isJSONArray(value) || isJSONObject(value);
|
|
}
|
|
case "string":
|
|
case "number":
|
|
case "boolean":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function toArray(v) {
|
|
if (v === null || v === void 0)
|
|
return [];
|
|
if (isArray(v))
|
|
return v;
|
|
return [v];
|
|
}
|
|
function uniq(v) {
|
|
return Array.from(new Set(v));
|
|
}
|
|
function uniqueBy(array, equalFn) {
|
|
return array.reduce((acc, cur) => {
|
|
const index = acc.findIndex((item) => equalFn(cur, item));
|
|
if (index === -1)
|
|
acc.push(cur);
|
|
return acc;
|
|
}, []);
|
|
}
|
|
function remove(array, value) {
|
|
if (!isArray(array))
|
|
return false;
|
|
const index = array.indexOf(value);
|
|
if (index !== -1) {
|
|
array.splice(index, 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function range(...args) {
|
|
let start, stop, step;
|
|
if (args.length === 1) {
|
|
start = 0;
|
|
stop = args[0];
|
|
step = 1;
|
|
} else {
|
|
[start, stop, step = 1] = args;
|
|
}
|
|
const arr = [];
|
|
let current = start;
|
|
while (current < stop) {
|
|
arr.push(current);
|
|
current += step || 1;
|
|
}
|
|
return arr;
|
|
}
|
|
function move(arr, from, to) {
|
|
if (!isArray(arr) || arr.length === 0) {
|
|
return arr;
|
|
}
|
|
arr.splice(to, 0, arr.splice(from, 1)[0]);
|
|
return arr;
|
|
}
|
|
function shuffle(array) {
|
|
for (let i = array.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[array[i], array[j]] = [array[j], array[i]];
|
|
}
|
|
return array;
|
|
}
|
|
function sortBy(array, cb) {
|
|
if (array.length === 0)
|
|
return [];
|
|
return array.sort((a, b) => {
|
|
const s1 = cb(a);
|
|
const s2 = cb(b);
|
|
return s1 > s2 ? 1 : s2 > s1 ? -1 : 0;
|
|
});
|
|
}
|
|
function chunk(input, size = 1) {
|
|
const chunks = [];
|
|
for (let i = 0; i < input.length; i += size)
|
|
chunks.push(input.slice(i, i + size));
|
|
return chunks;
|
|
}
|
|
function union(a, b) {
|
|
return [.../* @__PURE__ */ new Set([...a, ...b])];
|
|
}
|
|
function intersection(firstArr, secondArr) {
|
|
const secondSet = new Set(secondArr);
|
|
return firstArr.filter((item) => {
|
|
return secondSet.has(item);
|
|
});
|
|
}
|
|
|
|
function isTruthy(val) {
|
|
return Boolean(val);
|
|
}
|
|
function notUndefined(val) {
|
|
return typeof val !== "undefined";
|
|
}
|
|
function notNullish(val) {
|
|
return val != null;
|
|
}
|
|
|
|
function hasOwn(obj, key) {
|
|
return obj === null ? false : Object.prototype.hasOwnProperty.call(obj, key);
|
|
}
|
|
function deepFreeze(obj) {
|
|
if (isArray(obj)) {
|
|
for (let i = 0; i < obj.length; i++) {
|
|
deepFreeze(obj[i]);
|
|
}
|
|
} else if (isPlainObject(obj)) {
|
|
Object.freeze(obj);
|
|
const keys = Object.keys(obj);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
deepFreeze(obj[keys[i]]);
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
function isKeyof(obj, key) {
|
|
return key in obj;
|
|
}
|
|
function objectGet(source, path) {
|
|
const keys = path.replace(/\[['"]?(.+?)['"]?\]/g, ".$1").split(".");
|
|
let res = source;
|
|
for (const k of keys)
|
|
res = res?.[k];
|
|
return res;
|
|
}
|
|
function objectMap(obj, fn) {
|
|
return Object.fromEntries(
|
|
Object.entries(obj).map(([k, v]) => fn(k, v)).filter(notNullish)
|
|
);
|
|
}
|
|
function objectKeys(obj) {
|
|
return Object.keys(obj);
|
|
}
|
|
function objectEntries(obj) {
|
|
return Object.entries(obj);
|
|
}
|
|
function omit(obj, keys) {
|
|
const res = { ...obj };
|
|
for (const key of keys) {
|
|
if (isKeyof(obj, key))
|
|
delete res[key];
|
|
}
|
|
return res;
|
|
}
|
|
function pick(obj, keys) {
|
|
const res = {};
|
|
for (const key of keys) {
|
|
if (isKeyof(obj, key))
|
|
res[key] = obj[key];
|
|
}
|
|
return res;
|
|
}
|
|
function deepMerge(target, ...sources) {
|
|
if (!sources.length)
|
|
return target;
|
|
const source = sources.shift();
|
|
if (source === void 0)
|
|
return target;
|
|
if (isMergableObject(target) && isMergableObject(source)) {
|
|
const keys = Object.keys(source);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
if (key === "__proto__" || key === "constructor" || key === "prototype")
|
|
continue;
|
|
if (isMergableObject(source[key])) {
|
|
if (!target[key])
|
|
target[key] = {};
|
|
deepMerge(target[key], source[key]);
|
|
} else {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
}
|
|
return deepMerge(target, ...sources);
|
|
}
|
|
function deepMergeWithArray(target, ...sources) {
|
|
if (!sources.length)
|
|
return target;
|
|
const source = sources.shift();
|
|
if (source === void 0)
|
|
return target;
|
|
if (Array.isArray(target) && Array.isArray(source))
|
|
target.push(...source);
|
|
if (isMergableObject(target) && isMergableObject(source)) {
|
|
const keys = Object.keys(source);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
if (key === "__proto__" || key === "constructor" || key === "prototype")
|
|
continue;
|
|
if (Array.isArray(source[key])) {
|
|
if (!target[key])
|
|
target[key] = [];
|
|
deepMergeWithArray(target[key], source[key]);
|
|
} else if (isMergableObject(source[key])) {
|
|
if (!target[key])
|
|
target[key] = {};
|
|
deepMergeWithArray(target[key], source[key]);
|
|
} else {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
}
|
|
return deepMergeWithArray(target, ...sources);
|
|
}
|
|
function isMergableObject(item) {
|
|
return isPlainObject(item) && !Array.isArray(item);
|
|
}
|
|
|
|
function deepCloneImpl(valueToClone, objectToClone, stack = /* @__PURE__ */ new Map()) {
|
|
if (isPrimitive(valueToClone)) {
|
|
return valueToClone;
|
|
}
|
|
/* istanbul ignore if -- @preserve */
|
|
if (stack.has(valueToClone)) {
|
|
return stack.get(valueToClone);
|
|
}
|
|
if (Array.isArray(valueToClone)) {
|
|
const result = Array.from({ length: valueToClone.length });
|
|
stack.set(valueToClone, result);
|
|
for (let i = 0; i < valueToClone.length; i++) {
|
|
result[i] = deepCloneImpl(valueToClone[i], objectToClone, stack);
|
|
}
|
|
if (Object.hasOwn(valueToClone, "index")) {
|
|
result.index = valueToClone.index;
|
|
}
|
|
if (Object.hasOwn(valueToClone, "input")) {
|
|
result.input = valueToClone.input;
|
|
}
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof Date) {
|
|
return new Date(valueToClone.getTime());
|
|
}
|
|
if (valueToClone instanceof RegExp) {
|
|
const result = new RegExp(valueToClone.source, valueToClone.flags);
|
|
result.lastIndex = valueToClone.lastIndex;
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof Map) {
|
|
const result = /* @__PURE__ */ new Map();
|
|
stack.set(valueToClone, result);
|
|
for (const [key, value] of valueToClone) {
|
|
result.set(key, deepCloneImpl(value, objectToClone, stack));
|
|
}
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof Set) {
|
|
const result = /* @__PURE__ */ new Set();
|
|
stack.set(valueToClone, result);
|
|
for (const value of valueToClone) {
|
|
result.add(deepCloneImpl(value, objectToClone, stack));
|
|
}
|
|
return result;
|
|
}
|
|
if (typeof Buffer !== "undefined" && Buffer.isBuffer(valueToClone)) {
|
|
return valueToClone.subarray();
|
|
}
|
|
if (isTypedArray(valueToClone)) {
|
|
const result = new (Object.getPrototypeOf(valueToClone)).constructor(valueToClone.length);
|
|
stack.set(valueToClone, result);
|
|
for (let i = 0; i < valueToClone.length; i++) {
|
|
result[i] = deepCloneImpl(valueToClone[i], objectToClone, stack);
|
|
}
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && valueToClone instanceof SharedArrayBuffer) {
|
|
return valueToClone.slice(0);
|
|
}
|
|
if (valueToClone instanceof DataView) {
|
|
const result = new DataView(valueToClone.buffer.slice(0), valueToClone.byteOffset, valueToClone.byteLength);
|
|
stack.set(valueToClone, result);
|
|
copyProperties(result, valueToClone, objectToClone, stack);
|
|
return result;
|
|
}
|
|
if (typeof File !== "undefined" && valueToClone instanceof File) {
|
|
const result = new File([valueToClone], valueToClone.name, {
|
|
type: valueToClone.type
|
|
});
|
|
stack.set(valueToClone, result);
|
|
copyProperties(result, valueToClone, objectToClone, stack);
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof Blob) {
|
|
const result = new Blob([valueToClone], { type: valueToClone.type });
|
|
stack.set(valueToClone, result);
|
|
copyProperties(result, valueToClone, objectToClone, stack);
|
|
return result;
|
|
}
|
|
if (valueToClone instanceof Error) {
|
|
const result = new valueToClone.constructor(valueToClone.message, { cause: valueToClone.cause });
|
|
stack.set(valueToClone, result);
|
|
if (hasOwn(valueToClone, "name"))
|
|
result.name = valueToClone.name;
|
|
result.stack = valueToClone.stack;
|
|
copyProperties(result, valueToClone, objectToClone, stack);
|
|
return result;
|
|
}
|
|
/* istanbul ignore if -- @preserve */
|
|
if (typeof valueToClone === "object" && valueToClone !== null) {
|
|
const result = Object.create(Object.getPrototypeOf(valueToClone));
|
|
stack.set(valueToClone, result);
|
|
copyProperties(result, valueToClone, objectToClone, stack);
|
|
return result;
|
|
}
|
|
/* istanbul ignore next -- @preserve */
|
|
return valueToClone;
|
|
}
|
|
function copyProperties(target, source, objectToClone = target, stack) {
|
|
const keys = [...Object.keys(source), ...getSymbols(source)];
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
if (descriptor == null || descriptor.writable) {
|
|
target[key] = deepCloneImpl(source[key], objectToClone, stack);
|
|
}
|
|
}
|
|
}
|
|
function getSymbols(object) {
|
|
return Object.getOwnPropertySymbols(object).filter(
|
|
(symbol) => Object.prototype.propertyIsEnumerable.call(object, symbol)
|
|
);
|
|
}
|
|
|
|
function simpleClone(source) {
|
|
return JSON.parse(JSON.stringify(source));
|
|
}
|
|
function shallowClone(source) {
|
|
if (isPrimitive(source))
|
|
return source;
|
|
if (isArray(source) || isTypedArray(source) || source instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && source instanceof SharedArrayBuffer) {
|
|
return source.slice(0);
|
|
}
|
|
const prototype = Object.getPrototypeOf(source);
|
|
const Constructor = prototype.constructor;
|
|
if (source instanceof Date || source instanceof Map || source instanceof Set) {
|
|
return new Constructor(source);
|
|
}
|
|
if (source instanceof RegExp) {
|
|
const newRegExp = new Constructor(source);
|
|
newRegExp.lastIndex = source.lastIndex;
|
|
return newRegExp;
|
|
}
|
|
if (source instanceof DataView) {
|
|
return new Constructor(source.buffer.slice(0));
|
|
}
|
|
if (source instanceof Error) {
|
|
const newError = new Constructor(source.message, {
|
|
cause: source.cause
|
|
});
|
|
if (hasOwn(source, "name"))
|
|
newError.name = source.name;
|
|
newError.stack = source.stack;
|
|
return newError;
|
|
}
|
|
if (typeof File !== "undefined" && source instanceof File) {
|
|
const newFile = new Constructor([source], source.name, { type: source.type, lastModified: source.lastModified });
|
|
return newFile;
|
|
}
|
|
/* istanbul ignore if -- @preserve */
|
|
if (isPlainObject(source)) {
|
|
return Object.assign(Object.create(prototype), source);
|
|
}
|
|
/* istanbul ignore next -- @preserve */
|
|
return source;
|
|
}
|
|
function deepClone(source) {
|
|
return deepCloneImpl(source, source, /* @__PURE__ */ new Map());
|
|
}
|
|
|
|
function deepEqual(v1, v2) {
|
|
const type1 = getTypeName(v1);
|
|
const type2 = getTypeName(v2);
|
|
if (type1 !== type2)
|
|
return false;
|
|
if (type1 === "array") {
|
|
if (v1.length !== v2.length)
|
|
return false;
|
|
return v1.every((item, index) => deepEqual(item, v2[index]));
|
|
}
|
|
if (type1 === "object") {
|
|
const keys1 = Object.keys(v1);
|
|
if (keys1.length !== Object.keys(v2).length)
|
|
return false;
|
|
return keys1.every((key) => deepEqual(v1[key], v2[key]));
|
|
}
|
|
return Object.is(v1, v2);
|
|
}
|
|
|
|
class BaseEvent {
|
|
_listeners;
|
|
constructor() {
|
|
this._listeners = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* Adds a listener to the specified event.
|
|
*
|
|
* @param event - the event to listen for
|
|
* @param listener - the listener function to be called when the event is triggered
|
|
* @return
|
|
*/
|
|
on(event, listener) {
|
|
if (!this._listeners.has(event))
|
|
this._listeners.set(event, []);
|
|
this._listeners.get(event).push(listener);
|
|
}
|
|
/**
|
|
* Emits the specified event with the given arguments to all registered listeners.
|
|
*
|
|
* @param event - the name of the event to emit
|
|
* @param args - the arguments to pass to the event listeners
|
|
* @return
|
|
*/
|
|
emit(event, ...args) {
|
|
if (this._listeners.has(event))
|
|
this._listeners.get(event).forEach((listener) => listener(...args));
|
|
}
|
|
/**
|
|
* Turn off the specified event listener.
|
|
*
|
|
* @param event - the name of the event to turn off
|
|
* @param listener - (optional) the listener function to turn off
|
|
*/
|
|
off(event, listener) {
|
|
if (this._listeners.has(event)) {
|
|
listener ? this._listeners.get(event).splice(this._listeners.get(event).indexOf(listener), 1) : this._listeners.delete(event);
|
|
}
|
|
}
|
|
/**
|
|
* Execute the listener at most once for a particular event.
|
|
*
|
|
* @param event - the event to listen for
|
|
* @param listener - the function to be executed once for the event
|
|
*/
|
|
once(event, listener) {
|
|
this.on(event, (...args) => {
|
|
this.off(event, listener);
|
|
listener(...args);
|
|
});
|
|
}
|
|
}
|
|
|
|
function noop() {
|
|
}
|
|
function once(func) {
|
|
let called = false;
|
|
let res;
|
|
return (...args) => {
|
|
if (!called) {
|
|
called = true;
|
|
res = func(...args);
|
|
return res;
|
|
}
|
|
return res;
|
|
};
|
|
}
|
|
function invoke(fns) {
|
|
if (Array.isArray(fns))
|
|
fns.forEach((fn) => fn && fn());
|
|
else
|
|
return fns();
|
|
}
|
|
function compose(...fns) {
|
|
return function(...args) {
|
|
const len = fns.length;
|
|
if (len === 0)
|
|
return args;
|
|
if (len === 1)
|
|
return fns[0](...args);
|
|
return fns.slice(0, -1).reduceRight((acc, fn) => fn(acc), fns[len - 1](...args));
|
|
};
|
|
}
|
|
|
|
function clamp(n, min, max) {
|
|
return Math.min(max, Math.max(n, min));
|
|
}
|
|
function inRange(n, min, max) {
|
|
if (max === void 0) {
|
|
max = min;
|
|
min = 0;
|
|
}
|
|
return n >= Math.min(min, max) && n <= Math.max(min, max);
|
|
}
|
|
function random(...args) {
|
|
let min, max, float;
|
|
if (args.length === 1) {
|
|
min = 0;
|
|
max = args[0];
|
|
float = false;
|
|
} else {
|
|
if (typeof args[1] === "number") {
|
|
min = args[0];
|
|
max = args[1];
|
|
float = !!args[2];
|
|
} else {
|
|
min = 0;
|
|
max = args[0];
|
|
float = !!args[1];
|
|
}
|
|
}
|
|
const num = Math.random() * (max - min) + min;
|
|
return float ? num : Math.floor(num);
|
|
}
|
|
|
|
async function sleep(ms, callback) {
|
|
return new Promise(
|
|
(resolve) => setTimeout(async () => {
|
|
await callback?.();
|
|
resolve();
|
|
}, ms)
|
|
);
|
|
}
|
|
function promiseParallel(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
promises = Array.from(promises);
|
|
let current = 0;
|
|
const result = [];
|
|
let resolvedCount = 0;
|
|
const len = promises.length;
|
|
return new Promise((resolve, reject) => {
|
|
function next() {
|
|
const index = current++;
|
|
const promise = promises[index];
|
|
Promise.resolve(isFunction(promise) ? promise() : promise).then((res) => {
|
|
result[index] = res;
|
|
if (++resolvedCount === len)
|
|
resolve(result);
|
|
if (current < len)
|
|
next();
|
|
}).catch((reason) => reject(reason));
|
|
}
|
|
for (let i = 0; i < concurrency && i < len; i++) next();
|
|
});
|
|
}
|
|
function promiseParallelSettled(promises, concurrency = Number.POSITIVE_INFINITY) {
|
|
promises = Array.from(promises);
|
|
let current = 0;
|
|
const result = [];
|
|
let resolvedCount = 0;
|
|
const len = promises.length;
|
|
return new Promise((resolve) => {
|
|
function resolved() {
|
|
if (++resolvedCount === len)
|
|
resolve(result);
|
|
if (current < len)
|
|
next();
|
|
}
|
|
function next() {
|
|
const index = current++;
|
|
const promise = promises[index];
|
|
Promise.resolve(isFunction(promise) ? promise() : promise).then((value) => {
|
|
result[index] = { status: "fulfilled", value };
|
|
resolved();
|
|
}).catch((reason) => {
|
|
result[index] = { status: "rejected", reason };
|
|
resolved();
|
|
});
|
|
}
|
|
for (let i = 0; i < concurrency && i < len; i++) next();
|
|
});
|
|
}
|
|
class TimeoutError extends Error {
|
|
constructor(message = "The operation was timed out") {
|
|
super(message);
|
|
this.name = "TimeoutError";
|
|
}
|
|
}
|
|
async function timeout(ms) {
|
|
await sleep(ms);
|
|
throw new TimeoutError();
|
|
}
|
|
async function withTimeout(run, ms) {
|
|
return Promise.race([run(), timeout(ms)]);
|
|
}
|
|
function createSingletonPromise(fn) {
|
|
let _promise;
|
|
function wrapper() {
|
|
if (!_promise)
|
|
_promise = fn();
|
|
return _promise;
|
|
}
|
|
wrapper.reset = async () => {
|
|
const _prev = _promise;
|
|
_promise = void 0;
|
|
if (_prev)
|
|
await _prev;
|
|
};
|
|
return wrapper;
|
|
}
|
|
function createPromiseLock() {
|
|
const locks = [];
|
|
return {
|
|
async run(fn) {
|
|
const p = fn();
|
|
locks.push(p);
|
|
try {
|
|
return await p;
|
|
} finally {
|
|
remove(locks, p);
|
|
}
|
|
},
|
|
async wait() {
|
|
await Promise.allSettled(locks);
|
|
},
|
|
isWaiting() {
|
|
return Boolean(locks.length);
|
|
},
|
|
clear() {
|
|
locks.length = 0;
|
|
}
|
|
};
|
|
}
|
|
function createControlledPromise() {
|
|
let resolve, reject;
|
|
const promise = new Promise((_resolve, _reject) => {
|
|
resolve = _resolve;
|
|
reject = _reject;
|
|
});
|
|
promise.resolve = resolve;
|
|
promise.reject = reject;
|
|
return promise;
|
|
}
|
|
|
|
function ensurePrefix(prefix, str) {
|
|
if (str.startsWith(prefix))
|
|
return str;
|
|
return prefix + str;
|
|
}
|
|
function ensureSuffix(suffix, str) {
|
|
if (str.endsWith(suffix))
|
|
return str;
|
|
return str + suffix;
|
|
}
|
|
const CASE_SPLIT_PATTERN = /\p{Lu}?\p{Ll}+|\d+|\p{Lu}+(?!\p{Ll})|[\p{Emoji_Presentation}\p{Extended_Pictographic}]|\p{L}+/gu;
|
|
function words(str) {
|
|
return Array.from(str.match(CASE_SPLIT_PATTERN) ?? []);
|
|
}
|
|
function capitalize(s) {
|
|
if (!s)
|
|
return s;
|
|
return s[0].toUpperCase() + s.slice(1).toLowerCase();
|
|
}
|
|
function kebabCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
return parts.map((word) => word.toLowerCase()).join("-");
|
|
}
|
|
function snakeCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
return parts.map((word) => word.toLowerCase()).join("_");
|
|
}
|
|
function camelCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
const [first, ...rest] = parts;
|
|
return `${first.toLowerCase()}${rest.map((word) => capitalize(word)).join("")}`;
|
|
}
|
|
function lowerCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
return parts.map((word) => word.toLowerCase()).join(" ");
|
|
}
|
|
function upperCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
return parts.map((word) => word.toUpperCase()).join(" ");
|
|
}
|
|
function pascalCase(str) {
|
|
const parts = words(str);
|
|
if (parts.length === 0)
|
|
return "";
|
|
return parts.map((word) => capitalize(word)).join("");
|
|
}
|
|
const htmlEscapes = {
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
'"': """,
|
|
"'": "'"
|
|
};
|
|
const RE_ESCAPE = /[&<>"']/g;
|
|
function escape(str) {
|
|
return str.replace(RE_ESCAPE, (match) => htmlEscapes[match]);
|
|
}
|
|
const RE_ESCAPE_REGEXP = /[\\^$.*+?()[\]{}|]/g;
|
|
function escapeRegExp(str) {
|
|
return str.replace(RE_ESCAPE_REGEXP, "\\$&");
|
|
}
|
|
const htmlUnescapes = {
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
""": '"',
|
|
"'": "'"
|
|
};
|
|
const RE_UNESCAPE = /&(?:amp|lt|gt|quot|#(0+)?39);/g;
|
|
function unescape(str) {
|
|
return str.replace(RE_UNESCAPE, (match) => htmlUnescapes[match] || "'");
|
|
}
|
|
|
|
function throttle(delay, callback, options) {
|
|
const {
|
|
noTrailing = false,
|
|
noLeading = false,
|
|
debounceMode = void 0
|
|
} = options || {};
|
|
let timeoutID;
|
|
let cancelled = false;
|
|
let lastExec = 0;
|
|
function clearExistingTimeout() {
|
|
if (timeoutID)
|
|
clearTimeout(timeoutID);
|
|
}
|
|
function cancel(options2 = {}) {
|
|
const { upcomingOnly = false } = options2 || {};
|
|
clearExistingTimeout();
|
|
cancelled = !upcomingOnly;
|
|
}
|
|
function wrapper(...args) {
|
|
const self = this;
|
|
const elapsed = Date.now() - lastExec;
|
|
if (cancelled)
|
|
return;
|
|
function exec() {
|
|
lastExec = Date.now();
|
|
callback.apply(self, args);
|
|
}
|
|
function clear() {
|
|
timeoutID = void 0;
|
|
}
|
|
if (!noLeading && debounceMode && !timeoutID) {
|
|
exec();
|
|
}
|
|
clearExistingTimeout();
|
|
if (debounceMode === void 0 && elapsed > delay) {
|
|
if (noLeading) {
|
|
lastExec = Date.now();
|
|
if (!noTrailing)
|
|
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
|
|
} else {
|
|
exec();
|
|
}
|
|
} else if (noTrailing !== true) {
|
|
timeoutID = setTimeout(
|
|
debounceMode ? clear : exec,
|
|
debounceMode === void 0 ? delay - elapsed : delay
|
|
);
|
|
}
|
|
}
|
|
wrapper.cancel = cancel;
|
|
return wrapper;
|
|
}
|
|
function debounce(delay, callback, options) {
|
|
const { atBegin = false } = options || {};
|
|
return throttle(delay, callback, { debounceMode: atBegin !== false });
|
|
}
|
|
|
|
function timestamp() {
|
|
return +Date.now();
|
|
}
|
|
function isSameDay(date1, date2) {
|
|
if (!date2)
|
|
return false;
|
|
const v1 = new Date(date1);
|
|
const v2 = new Date(date2);
|
|
const y1 = v1.getFullYear();
|
|
const m1 = v1.getMonth();
|
|
const d1 = v1.getDate();
|
|
const y2 = v2.getFullYear();
|
|
const m2 = v2.getMonth();
|
|
const d2 = v2.getDate();
|
|
return y1 === y2 && m1 === m2 && d1 === d2;
|
|
}
|
|
|
|
function slash(s) {
|
|
return s.replace(/\\/g, "/");
|
|
}
|
|
function ensureLeadingSlash(str) {
|
|
return ensurePrefix("/", slash(str));
|
|
}
|
|
function ensureTrailingSlash(str) {
|
|
return ensureSuffix("/", slash(str));
|
|
}
|
|
function removeLeadingSlash(str) {
|
|
if (!str)
|
|
return str;
|
|
str = slash(str);
|
|
return str[0] === "/" ? str.slice(1) : str;
|
|
}
|
|
function removeTrailingSlash(str) {
|
|
if (!str)
|
|
return str;
|
|
str = slash(str);
|
|
return str[str.length - 1] === "/" ? str.slice(0, -1) : str;
|
|
}
|
|
const RE_HTTP = /^(?:https?:)?\/\//i;
|
|
function isHttp(url) {
|
|
return RE_HTTP.test(url);
|
|
}
|
|
function isUrl(url) {
|
|
try {
|
|
new URL(url);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
function combineURLs(baseUrl, ...urls) {
|
|
if (urls.length === 0)
|
|
return baseUrl;
|
|
const url = removeLeadingSlash(urls.join("/").replace(/\/+/g, "/"));
|
|
baseUrl = removeTrailingSlash(baseUrl);
|
|
return `${baseUrl}/${url}`;
|
|
}
|
|
const RE_PROTOCOL_MATCH = /^([-+\w]{1,25})(?::?\/\/|:)/;
|
|
function parseProtocol(url) {
|
|
const match = RE_PROTOCOL_MATCH.exec(url);
|
|
return match?.[1] || "";
|
|
}
|
|
|
|
export { BaseEvent, CASE_SPLIT_PATTERN, TimeoutError, assert, camelCase, capitalize, chunk, clamp, combineURLs, compose, createControlledPromise, createPromiseLock, createSingletonPromise, debounce, deepClone, deepEqual, deepFreeze, deepMerge, deepMergeWithArray, ensureLeadingSlash, ensurePrefix, ensureSuffix, ensureTrailingSlash, escape, escapeRegExp, getTypeName, hasOwn, inRange, intersection, invoke, isArray, isBlob, isBoolean, isBrowser, isDate, isDef, isEmptyObject, isFunction, isHttp, isJSONArray, isJSONObject, isJSONValue, isKeyof, isNull, isNumber, isPlainObject, isPrimitive, isRegexp, isSameDay, isString, isSymbol, isTruthy, isTypedArray, isUndefined, isUrl, isWindow, kebabCase, lowerCase, move, noop, notNullish, notUndefined, objectEntries, objectGet, objectKeys, objectMap, omit, once, parseProtocol, pascalCase, pick, promiseParallel, promiseParallelSettled, random, range, remove, removeLeadingSlash, removeTrailingSlash, shallowClone, shuffle, simpleClone, slash, sleep, snakeCase, sortBy, throttle, timeout, timestamp, toArray, toString, unescape, union, uniq, uniqueBy, upperCase, withTimeout, words };
|