// Serialisiert asynchrone Aufgaben je `key` und koalesziert Wartende:
// - Es läuft nie mehr als eine Aufgabe pro Key gleichzeitig.
// - Kommen während eines Laufs weitere Aufrufe rein, wird GENAU EIN weiterer
//   Durchlauf nachgelagert (egal wie viele warten) — sie teilen sich dessen
//   Ergebnis. So sehen alle den jüngsten Stand, ohne einen Lauf-Sturm.
//
// Einsatz: teure, idempotente Vorgänge wie der Hugo-Build (siehe hugo.js).
const state = new Map();

export function coalesce(key, fn) {
  let s = state.get(key);
  if (!s) { s = { running: false, rerun: false, fn, waiters: [] }; state.set(key, s); }
  s.fn = fn;   // jüngste Variante gewinnt für den nächsten Lauf
  return new Promise((resolve, reject) => {
    s.waiters.push({ resolve, reject });
    if (!s.running) drain(key);
    else s.rerun = true;
  });
}

async function drain(key) {
  const s = state.get(key);
  s.running = true;
  try {
    do {
      s.rerun = false;
      const waiters = s.waiters;
      s.waiters = [];
      try {
        const r = await s.fn();
        waiters.forEach((w) => w.resolve(r));
      } catch (e) {
        waiters.forEach((w) => w.reject(e));
      }
    } while (s.rerun);
  } finally {
    s.running = false;
  }
}
