random bun scripts that dont fit anywhere else
at main 1.7 kB view raw
1import type { TBAApiOptions, TBAEvent, TBATeam } from "./tba.types"; 2 3export const DEFAULT_TBA_API_URL = "https://www.thebluealliance.com/api/v3"; 4 5export class TBAApi { 6 private apiKey: string; 7 private baseUrl: string; 8 private cache: Map<string, { etag: string; data: unknown }>; 9 10 constructor(options: TBAApiOptions) { 11 this.apiKey = options.apiKey; 12 this.baseUrl = options.baseUrl || DEFAULT_TBA_API_URL; 13 this.cache = new Map(); 14 } 15 16 private async fetch<T>(path: string): Promise<T> { 17 const url = `${this.baseUrl}${path}`; 18 const cachedResponse = this.cache.get(url); 19 20 const headers: Record<string, string> = { 21 "X-TBA-Auth-Key": this.apiKey, 22 }; 23 24 if (cachedResponse?.etag) { 25 headers["If-None-Match"] = cachedResponse.etag; 26 } 27 28 const response = await fetch(url, { headers }); 29 30 if (response.status === 304 && cachedResponse) { 31 return cachedResponse.data as T; 32 } 33 34 if (!response.ok) { 35 throw new Error( 36 `TBA API error: ${response.status} ${response.statusText}`, 37 ); 38 } 39 40 const data = await response.json(); 41 const etag = response.headers.get("ETag"); 42 43 if (etag) { 44 this.cache.set(url, { etag, data }); 45 } 46 47 return data as T; 48 } 49 50 async getEvent(eventKey: string): Promise<TBAEvent> { 51 return this.fetch(`/event/${eventKey}`); 52 } 53 54 async getTeamsAtEvent(eventKey: string): Promise<TBATeam[]> { 55 return this.fetch<TBATeam[]>(`/event/${eventKey}/teams`); 56 } 57 58 async getTeam(teamKey: string): Promise<TBATeam> { 59 return this.fetch<TBATeam>(`/team/${teamKey}`); 60 } 61 62 async getTeamEvents(teamKey: string, year: number): Promise<TBAEvent[]> { 63 return this.fetch<TBAEvent[]>(`/team/${teamKey}/events/${year}`); 64 } 65}