import { ApiError } from '$lib/api/client'; import type { EventDTO } from '$lib/api/schema-helpers'; import { getResults } from '$lib/api/tournaments'; import type { EventResult } from './aggregate'; /** * Fetches scored results one event at a time. * * `POST /api/Tournament/GetResults` throws a 500 when any bracket in the request * is missing a rank-1 or rank-2 row, so asking for thirty events in one call means * one broken import hides all thirty. Per-event requests cost more round trips but * degrade to "we skipped event #97" instead of "statistics are unavailable". */ export interface FailedEvent { event: EventDTO; message: string; } export interface LoadOutcome { loaded: EventResult[]; failed: FailedEvent[]; } export interface LoadOptions { /** Called after each event settles, for a progress indicator. */ onProgress?: (done: number, total: number) => void; /** Abort further requests, e.g. when the user changes scope mid-load. */ signal?: AbortSignal; /** Parallel requests. Enough to be quick, few enough to be polite to the API. */ concurrency?: number; } export async function loadEventResults( events: EventDTO[], options: LoadOptions = {} ): Promise { const { onProgress, signal, concurrency = 6 } = options; const targets = events.filter((event) => event.id !== undefined); const loaded: EventResult[] = []; const failed: FailedEvent[] = []; let done = 0; let next = 0; async function worker() { while (next < targets.length) { if (signal?.aborted) return; const event = targets[next++]; try { const result = await getResults([event.id as number], { signal }); loaded.push({ event, result }); } catch (cause) { if (cause instanceof DOMException && cause.name === 'AbortError') throw cause; // A 401 has to stop everything: retrying N times just burns requests. if (cause instanceof ApiError && cause.status === 401) throw cause; failed.push({ event, message: cause instanceof ApiError ? cause.message : 'Request failed' }); } onProgress?.(++done, targets.length); } } const workers = Array.from({ length: Math.min(concurrency, targets.length) }, worker); await Promise.all(workers); return { loaded, failed }; }