unified client

Three layers. Comment in cards.ts: compose sub-endpoints so there is one loop, no all-in-one card route. Comment in useCards.ts: components never fetch; hooks own keys.

Layer A: envelope client#

src/api/unifiedClient.ts

  • PLATFORM_BASE: VITE_*_API or /api/{platform}
  • platformUrl encodes the handle, optional path, optional query (view, year)
  • getJson: 429 reads Retry-After (header or body). Other errors prefer message then detail
  • Envelope failure if status === 'error' or error === true or success === false
  • windowUnifiedHeatmap slices year / last 365 / all, then recomputes streaks. Detail pages use this. Aggregated profile mostly windows again on the full view=all payload.

Layer B: composed card#

src/api/cards.ts fetchCard:

  • Six endpoints in parallel: profile, stats, contests, rating, heatmap, badges. Not summary.
  • One failure becomes an empty stub. All six failing throws the first error.
  • Empty stubs keep keys: null / [] / {}.
  • Category ternary: GitHub → development, Codeforces/CodeChef → competitive, everything else → dsa. Schema still lists GFG and HackerRank as fundamentals. The client does not.

Layer C: platform adapters#

Deep-dive pages want native shapes (pinned repos, GFG School/Basic, CodeChef stars).

File Card? Extra
github.ts yes /pinned, /stars, /prs, /org-contributions, /profile-views. Failures fall back via ghJSON
leetcode.ts mixed leftover envelope fields (easySolved, submissionCalendar)
codeforces.ts yes GET /contests/upcoming
gfg.ts yes maps byDifficulty.school/basic/...
codechef.ts yes rating color bands in the client
hackerrank.ts like LeetCode legacy envelope fields
tuf.ts card only comment: TUF has no native extras

useProfileCards flattens comma-separated handles (splitAccounts) into one query per account. Stacked accounts are first-class.