"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Queue = void 0;
class Queue {
    #concurrent;
    #jobs = [];
    #running = 0;
    constructor(concurrent = 1) {
        this.#concurrent = concurrent;
    }
    async execute(func, key) {
        const job = { key, running: false };
        this.#jobs.push(job);
        // Minor optimization/workaround: various tests like the idea that a job that is immediately runnable is run without an event loop spin.
        // This also helps with stack traces in some cases, so avoid an `await` if we can help it.
        if (this.#getNext() !== job) {
            await new Promise((resolve) => {
                job.start = () => {
                    job.running = true;
                    this.#running += 1;
                    resolve();
                };
                this.#executeNext();
            });
        }
        else {
            job.running = true;
            this.#running += 1;
        }
        try {
            return await func();
        }
        finally {
            this.#jobs.splice(this.#jobs.indexOf(job), 1);
            this.#running = Math.max(this.#running - 1, 0);
            this.#executeNext();
        }
    }
    #executeNext() {
        const job = this.#getNext();
        if (job) {
            // biome-ignore lint/style/noNonNullAssertion: if we get here, start is always defined for job
            job.start();
        }
    }
    #getNext() {
        if (this.#running > this.#concurrent - 1) {
            return undefined;
        }
        for (let i = 0; i < this.#jobs.length; i++) {
            const job = this.#jobs[i];
            if (!job.running && (!job.key || !this.#jobs.find((j) => j.key === job.key && j.running))) {
                return job;
            }
        }
        return undefined;
    }
    clear() {
        this.#running = 0;
        this.#jobs.length = 0;
    }
    count() {
        return this.#jobs.length;
    }
}
exports.Queue = Queue;
//# sourceMappingURL=queue.js.map