"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.groupArrayByKey = exports.splitArrayIntoChunks = exports.hasRepeatedValues = exports.deferredPromise = exports.timeoutPromise = exports.DoubleLinkedList = exports.LinkedListNode = exports.sleep = void 0;
__exportStar(require("./logger"), exports);
__exportStar(require("./subscription-set/subscription-set"), exports);
__exportStar(require("./types"), exports);
/**
 * Sleeps for the provided number of milliseconds
 * @param ms - The number of milliseconds to sleep for
 * @returns a Promise that resolves once the specified time passes
 */
const sleep = (ms) => {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
};
exports.sleep = sleep;
class LinkedListNode {
    constructor(key, data) {
        this.data = data;
        this.next = null;
        this.prev = null;
        this.key = key;
    }
}
exports.LinkedListNode = LinkedListNode;
class DoubleLinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }
    insertAtTail(node) {
        if (!this.tail) {
            this.tail = node;
            this.head = node;
            node.next = null;
        }
        else {
            this.tail.next = node;
            node.prev = this.tail;
            this.tail = node;
            node.next = null;
        }
        this.size++;
        return node;
    }
    remove(node) {
        if (!node) {
            return;
        }
        if (node.prev !== null) {
            node.prev.next = node.next;
        }
        if (node.next !== null) {
            node.next.prev = node.prev;
        }
        if (node === this.head) {
            this.head = this.head.next;
        }
        if (node === this.tail) {
            this.tail = this.tail.prev;
        }
        this.size--;
        return node;
    }
    removeHead() {
        return this.remove(this.head);
    }
}
exports.DoubleLinkedList = DoubleLinkedList;
class PromiseTimeoutError extends Error {
}
/**
 * Waits for the provided promise to finish up to a specified amount of time, at which point
 * an error is thrown if the promise hasn't finished yet. Note that this cannot abort the execution
 * of the provided promise, as the underlying node structure cannot cancel the thread.
 *
 * @param promise - the promise to wait for
 * @param timeout - the maximum amount of time to wait for the promise to finish
 * @returns the result of the promise
 */
const timeoutPromise = (label, promise, timeout) => {
    let timer;
    return Promise.race([
        promise,
        new Promise((_, reject) => {
            timer = setTimeout(() => reject(new PromiseTimeoutError(`The promise "${label}" took longer than ${timeout} ms to execute, unblocking execution.
      (NOTE: the original promise is not cancelled from this error, it will continue to execute in the background)`)), timeout);
        }),
    ]).finally(() => clearTimeout(timer));
};
exports.timeoutPromise = timeoutPromise;
/**
 * This function will create a promise, and synchronously return both the promise itself and
 * the resolve and reject callbacks so that they can be used and passed around individually.
 *
 * @returns all the deconstructed elements of a promise, to handle in synchronous-ish logic
 */
const deferredPromise = () => {
    let resolve;
    let reject;
    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return [promise, resolve, reject];
};
exports.deferredPromise = deferredPromise;
/**
 * Checks if the provided array includes any duplicates
 *
 * @param array - any array to check
 * @returns whether the array has duplicate items
 */
const hasRepeatedValues = (array) => array.length !== new Set(array).size;
exports.hasRepeatedValues = hasRepeatedValues;
/**
 * Splits an array into smaller arrays of a specified size.
 *
 * @param array - any array to be chunked
 * @param size - the maximum size of each chunk
 * @returns an array of arrays, each of which contains up to `size` elements
 */
const splitArrayIntoChunks = (array, size) => {
    return array.length > size
        ? [array.slice(0, size), ...(0, exports.splitArrayIntoChunks)(array.slice(size), size)]
        : [array];
};
exports.splitArrayIntoChunks = splitArrayIntoChunks;
/**
 * Groups an array of objects by a specified key
 * @param  array - The array of objects to group
 * @param  key - The name of the key to group by
 * @returns An object where the keys are the unique values of the specified key
 * and the values are array of items with that key
 */
const groupArrayByKey = (array, key) => {
    return array.reduce((groupedItems, item) => {
        const keyValue = item[key];
        groupedItems[keyValue] ?? (groupedItems[keyValue] = []);
        groupedItems[keyValue].push(item);
        return groupedItems;
    }, {});
};
exports.groupArrayByKey = groupArrayByKey;
//# sourceMappingURL=index.js.map