/**
 * ConcurrencyLimiter — bounded-concurrency scheduler for the fact-loader
 * adapters (D0091).
 *
 * The GCS reader fans a `Promise.all` over every artifact entry in a run and,
 * during a backfill, over hundreds of runs. Without a bound that would open
 * hundreds of concurrent GCS downloads and saturate the client connection pool
 * and GCS's per-prefix read quota. This limiter bounds in-flight work to `limit`
 * steady-state (transiently `limit + 1` in the microtask window between a task
 * releasing its slot and a woken waiter re-incrementing — a soft cap, matching
 * the writer's limiter); excess tasks queue FIFO and each completion wakes
 * exactly one waiter.
 *
 * It is the same shape as the private limiter in `GcsArtifactWriter`
 * (`artifact-capture/gcs-artifact-writer.ts`), lifted to a named module so the
 * reader can share the pattern without duplicating an anonymous class.
 *
 * @see docs/decisions/D0091-bigquery-analytics-fact-loader.md
 */
export class ConcurrencyLimiter {
    inflight = 0;
    waiters = [];
    limit;
    constructor(limit) {
        // Clamp at 1 so a misconfigured limit (0 or negative) cannot deadlock —
        // the admission check is `inflight >= limit`, so `limit=0` would wedge
        // every caller.
        this.limit = Math.max(1, Math.floor(limit));
    }
    /** Run `task` once a slot is free; releases the slot (and wakes one waiter) when it settles. */
    async run(task) {
        if (this.inflight >= this.limit) {
            await new Promise((resolve) => this.waiters.push(resolve));
        }
        this.inflight++;
        try {
            return await task();
        }
        finally {
            this.inflight--;
            const next = this.waiters.shift();
            if (next)
                next();
        }
    }
}
