/** * Pure helpers for the bracket-scoring route action, extracted so the * knockout-detection logic can be unit-tested without the full action harness. */ /** * From a set of matches being scored this action, return the season_participant * ids knocked out for the FIRST time. A loser counts only when its match was NOT * already complete — a re-score/correction of a finished match must not * re-announce the exit (mirrors populateBracketFromDraw's first-completion rule in * the live-sync path). Entries with an unknown loser (`null`) are skipped. * * The ids are season_participant ids of the primary window (playoff_matches are * keyed by season_participant); the fan-out translates them to each mirror * window's own season_participant id before announcing the "Knocked Out" section. */ export function newlyDecidedLosers( entries: Array<{ wasComplete: boolean; loserId: string | null }> ): string[] { return entries .filter((e): e is { wasComplete: boolean; loserId: string } => !e.wasComplete && e.loserId !== null ) .map((e) => e.loserId); }