Fix MLB standings 406 error and refactor ESPN adapter shared code #62

Merged
chrisp merged 3 commits from fix/mlb-standings-espn-refactor into main 2026-06-01 03:31:19 +00:00
Owner

Summary

  • Root cause: statsapi.mlb.com returns 406 (deprecated). Switched MLB to ESPN's free standings API (site.api.espn.com/apis/v2/sports/baseball/mlb/standings), consistent with NBA, WNBA, and MLS.
  • Refactor: Extracted a shared espn.ts utility module, eliminating 4× duplication of statsMap(), flattenEspnStandings(), ESPN interfaces, and the playoffSeed → conference rank logic across adapters.
  • Bug fixes found during review and applied across all affected adapters:
    • parseConferenceRank: || undefined falsy-zero bug replaced with isNaN guard
    • Sort comparators: stable alphabetical tiebreaker added to MLB, NBA, WNBA
    • winPct: falls back to wins/(wins+losses) if ESPN omits the stat (was silently 0)
    • statsMap pre-built once per entry before sorting in all adapters (was rebuilt per comparison)
    • WNBA parseEntry: accepts pre-computed sm instead of rebuilding it internally
    • MLB gamesBack: tests updated to reflect ESPN returns numeric 0 for division leaders (old API used "-"undefined)

Test plan

  • All 60 standings-sync unit tests pass (npm run test:run -- app/services/standings-sync)
  • Trigger MLB standings sync from admin panel and confirm it returns data without a 406
  • Confirm NBA, WNBA, MLS syncs still work (adapters touched but behaviour unchanged)

🤖 Generated with Claude Code

## Summary - **Root cause**: `statsapi.mlb.com` returns 406 (deprecated). Switched MLB to ESPN's free standings API (`site.api.espn.com/apis/v2/sports/baseball/mlb/standings`), consistent with NBA, WNBA, and MLS. - **Refactor**: Extracted a shared `espn.ts` utility module, eliminating 4× duplication of `statsMap()`, `flattenEspnStandings()`, ESPN interfaces, and the `playoffSeed` → conference rank logic across adapters. - **Bug fixes** found during review and applied across all affected adapters: - `parseConferenceRank`: `|| undefined` falsy-zero bug replaced with `isNaN` guard - Sort comparators: stable alphabetical tiebreaker added to MLB, NBA, WNBA - `winPct`: falls back to `wins/(wins+losses)` if ESPN omits the stat (was silently 0) - statsMap pre-built once per entry before sorting in all adapters (was rebuilt per comparison) - WNBA `parseEntry`: accepts pre-computed `sm` instead of rebuilding it internally - MLB `gamesBack`: tests updated to reflect ESPN returns numeric `0` for division leaders (old API used `"-"` → `undefined`) ## Test plan - [ ] All 60 standings-sync unit tests pass (`npm run test:run -- app/services/standings-sync`) - [ ] Trigger MLB standings sync from admin panel and confirm it returns data without a 406 - [ ] Confirm NBA, WNBA, MLS syncs still work (adapters touched but behaviour unchanged) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chrisp added 1 commit 2026-06-01 01:18:35 +00:00
Fix MLB standings 406 error and refactor ESPN adapter shared code
Some checks failed
🚀 Deploy / 🧪 Test (pull_request) Successful in 2m28s
🚀 Deploy / ʦ TypeScript (pull_request) Failing after 1m9s
🚀 Deploy / 🔍 Lint (pull_request) Successful in 46s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
e353c17a0b
- Switch MLB standings from deprecated statsapi.mlb.com to ESPN API
  (same source as NBA, WNBA, MLS — no auth required)
- Extract shared espn.ts utility: EspnStat/Team/Entry/Group/Response
  interfaces, statsMap(), flattenEspnStandings(), parseConferenceRank(),
  sortByWinLoss(); removes 4× duplication across adapters
- Fix parseConferenceRank falsy-zero bug (|| undefined → isNaN guard)
- Add stable alphabetical tiebreaker to MLB, NBA, WNBA sort comparators
- Fix winPct silent-zero: fall back to wins/(wins+losses) if ESPN omits stat
- Pre-build statsMap once per entry before sorting in all adapters
- Fix WNBA parseEntry to accept pre-computed sm instead of rebuilding it
- Restore and update gamesBack tests (ESPN returns numeric 0 for leaders,
  not the old API's "-" string)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chrisp added 1 commit 2026-06-01 02:54:27 +00:00
Fix TypeScript errors: cast response.json() to EspnStandingsResponse
Some checks failed
🚀 Deploy / 🧪 Test (pull_request) Failing after 2m39s
🚀 Deploy / ʦ TypeScript (pull_request) Successful in 1m19s
🚀 Deploy / 🔍 Lint (pull_request) Successful in 51s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
880b7faa6b
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chrisp added 1 commit 2026-06-01 03:20:28 +00:00
Fix intermittent test timeouts in simulator-inputs and llws-simulator
All checks were successful
🚀 Deploy / 🧪 Test (pull_request) Successful in 2m27s
🚀 Deploy / ʦ TypeScript (pull_request) Successful in 1m16s
🚀 Deploy / 🔍 Lint (pull_request) Successful in 48s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
3ebdd16b60
simulator-inputs: move dynamic import to top level so heavy module
initialization happens during file load (no timeout) instead of inside
the test body.

llws-simulator: make iteration count injectable via constructor
(default 50_000 for production, tests pass 1_000). Cuts test suite
from ~4s per test to <150ms for all 21 tests combined.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chrisp merged commit add196902b into main 2026-06-01 03:31:19 +00:00
Sign in to join this conversation.
No description provided.