random bun scripts that dont fit anywhere else
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}