"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimeoutError = exports.timeout = exports.promiseHash = void 0;
/**
 * Get a hash of promises and await them all.
 * Then return the same hash with the resolved values.
 * @example
 * export async function loader({ request }: LoaderArgs) {
 *   return json(
 *     promiseHash({
 *       user: getUser(request),
 *       posts: getPosts(request),
 *     })
 *   );
 * }
 * @example
 * export async function loader({ request }: LoaderArgs) {
 *   return json(
 *     promiseHash({
 *       user: getUser(request),
 *       posts: promiseHash({
 *         list: getPosts(request),
 *         comments: promiseHash({
 *           list: getComments(request),
 *           likes: getLikes(request),
 *         }),
 *       }),
 *     })
 *   );
 * }
 */
async function promiseHash(hash) {
    return Object.fromEntries(await Promise.all(Object.entries(hash).map(async ([key, promise]) => [key, await promise])));
}
exports.promiseHash = promiseHash;
/**
 * Used to uniquely identify a timeout
 * @private
 */
let TIMEOUT = Symbol("TIMEOUT");
/**
 * Attach a timeout to any promise, if the timeout resolves first ignore the
 * original promise and throw an error
 * @param promise The promise to attach a timeout to
 * @param options The options to use
 * @param options.ms The number of milliseconds to wait before timing out
 * @param options.controller An AbortController to abort the original promise
 * @returns The result of the promise
 * @throws TimeoutError If the timeout resolves first
 * @example
 * try {
 *   let result = await timeout(
 *     fetch("https://example.com"),
 *     { ms: 100 }
 *   );
 * } catch (error) {
 *   if (error instanceof TimeoutError) {
 *     // Handle timeout
 *   }
 * }
 * @example
 * try {
 *   let controller = new AbortController();
 *   let result = await timeout(
 *     fetch("https://example.com", { signal: controller.signal }),
 *     { ms: 100, controller }
 *   );
 * } catch (error) {
 *   if (error instanceof TimeoutError) {
 *     // Handle timeout
 *   }
 * }
 */
function timeout(promise, options) {
    return new Promise(async (resolve, reject) => {
        let timer = null;
        try {
            let result = await Promise.race([
                promise,
                new Promise((resolve) => {
                    timer = setTimeout(() => resolve(TIMEOUT), options.ms);
                }),
            ]);
            if (timer)
                clearTimeout(timer);
            if (result === TIMEOUT) {
                if (options.controller)
                    options.controller.abort();
                return reject(new TimeoutError(`Timed out after ${options.ms}ms`));
            }
            return resolve(result);
        }
        catch (error) {
            if (timer)
                clearTimeout(timer);
            reject(error);
        }
    });
}
exports.timeout = timeout;
/**
 * An error thrown when a timeout occurs
 * @example
 * try {
 *   let result = await timeout(fetch("https://example.com"), { ms: 100 });
 * } catch (error) {
 *   if (error instanceof TimeoutError) {
 *    // Handle timeout
 *   }
 * }
 */
class TimeoutError extends Error {
    constructor(message) {
        super(message);
        this.name = "TimeoutError";
    }
}
exports.TimeoutError = TimeoutError;
