/**
 * Minimal async mutex: serializes async sections via a promise chain. Each
 * runExclusive call waits for the previous one to settle before starting, so
 * critical sections never interleave across awaits. Rejections are isolated —
 * a failing section does not break the chain for later callers.
 */
export class AsyncMutex {
  private tail: Promise<unknown> = Promise.resolve();

  runExclusive<T>(fn: () => Promise<T>): Promise<T> {
    const result = this.tail.then(() => fn());
    // Keep the chain alive regardless of this section's outcome.
    this.tail = result.then(() => undefined, () => undefined);
    return result;
  }
}
