/** * Async concurrency semaphore — limits the number of concurrent async operations. */ export class ConcurrencySemaphore { private running = 0; private waitQueue: Array<() => void> = []; constructor(private readonly maxConcurrency: number) {} async acquire(): Promise { if (this.running < this.maxConcurrency) { this.running++; return; } return new Promise((resolve) => { this.waitQueue.push(() => { this.running++; resolve(); }); }); } release(): void { this.running--; const next = this.waitQueue.shift(); if (next) next(); } }