/**
 * Promise-based async mutex for serializing concurrent async operations.
 * No external dependencies — uses a simple FIFO queue of pending resolvers.
 */
export class AsyncMutex {
    /** Default timeout for lock-held operations: 30 seconds */
    static DEFAULT_TIMEOUT_MS = 30_000;
    _locked = false;
    _queue = [];
    isLocked() {
        return this._locked;
    }
    async runExclusive(fn, timeoutMs = AsyncMutex.DEFAULT_TIMEOUT_MS) {
        await this._acquire();
        let timer;
        // Store fn()'s promise so we can ensure it settles before releasing the lock,
        // even when the timeout fires first.
        const fnPromise = fn();
        try {
            return await Promise.race([
                fnPromise,
                new Promise((_resolve, reject) => {
                    timer = setTimeout(() => reject(new Error(`AsyncMutex: lock-held operation timed out after ${timeoutMs}ms`)), timeoutMs);
                }),
            ]);
        }
        finally {
            if (timer !== undefined) {
                clearTimeout(timer);
            }
            // Wait for fn() to settle before releasing the lock.
            // If the timeout fired, fn() may still be running — we must not
            // let another caller acquire the lock until fn() completes.
            await fnPromise.catch((_settleErr) => {
                // Intentionally swallowed — we only await settlement to keep
                // the lock held until fn() finishes. The caller already received
                // the timeout rejection from Promise.race above.
            });
            this._release();
        }
    }
    _acquire() {
        if (!this._locked) {
            this._locked = true;
            return Promise.resolve();
        }
        return new Promise((resolve) => {
            this._queue.push(resolve);
        });
    }
    _release() {
        const next = this._queue.shift();
        if (next) {
            next();
        }
        else {
            this._locked = false;
        }
    }
}
//# sourceMappingURL=asyncMutex.js.map