random bun scripts that dont fit anywhere else

feat: add the tba api

Changed files
+126
+65
tba.ts
···
···
+
import type { TBAApiOptions, TBAEvent, TBATeam } from "./tba.types";
+
+
export const DEFAULT_TBA_API_URL = "https://www.thebluealliance.com/api/v3";
+
+
export class TBAApi {
+
private apiKey: string;
+
private baseUrl: string;
+
private cache: Map<string, { etag: string; data: unknown }>;
+
+
constructor(options: TBAApiOptions) {
+
this.apiKey = options.apiKey;
+
this.baseUrl = options.baseUrl || DEFAULT_TBA_API_URL;
+
this.cache = new Map();
+
}
+
+
private async fetch<T>(path: string): Promise<T> {
+
const url = `${this.baseUrl}${path}`;
+
const cachedResponse = this.cache.get(url);
+
+
const headers: Record<string, string> = {
+
"X-TBA-Auth-Key": this.apiKey,
+
};
+
+
if (cachedResponse?.etag) {
+
headers["If-None-Match"] = cachedResponse.etag;
+
}
+
+
const response = await fetch(url, { headers });
+
+
if (response.status === 304 && cachedResponse) {
+
return cachedResponse.data as T;
+
}
+
+
if (!response.ok) {
+
throw new Error(
+
`TBA API error: ${response.status} ${response.statusText}`,
+
);
+
}
+
+
const data = await response.json();
+
const etag = response.headers.get("ETag");
+
+
if (etag) {
+
this.cache.set(url, { etag, data });
+
}
+
+
return data as T;
+
}
+
+
async getEvent(eventKey: string): Promise<TBAEvent> {
+
return this.fetch(`/event/${eventKey}`);
+
}
+
+
async getTeamsAtEvent(eventKey: string): Promise<TBATeam[]> {
+
return this.fetch<TBATeam[]>(`/event/${eventKey}/teams`);
+
}
+
+
async getTeam(teamKey: string): Promise<TBATeam> {
+
return this.fetch<TBATeam>(`/team/${teamKey}`);
+
}
+
+
async getTeamEvents(teamKey: string, year: number): Promise<TBAEvent[]> {
+
return this.fetch<TBAEvent[]>(`/team/${teamKey}/events/${year}`);
+
}
+
}
+61
tba.types.ts
···
···
+
export interface TBAApiOptions {
+
apiKey: string;
+
baseUrl?: string;
+
}
+
+
export interface TBAEvent {
+
address: string;
+
city: string;
+
country: string;
+
district: null | string;
+
division_keys: string[];
+
end_date: string;
+
event_code: string;
+
event_type: number;
+
event_type_string: string;
+
first_event_code: string;
+
first_event_id: null | string;
+
gmaps_place_id: string;
+
gmaps_url: string;
+
key: string;
+
lat: number;
+
lng: number;
+
location_name: string;
+
name: string;
+
parent_event_key: null | string;
+
playoff_type: number;
+
playoff_type_string: string;
+
postal_code: string;
+
short_name: string;
+
start_date: string;
+
state_prov: string;
+
timezone: string;
+
webcasts: Array<{
+
channel: string;
+
type: string;
+
}>;
+
website: null | string;
+
week: number;
+
year: number;
+
}
+
+
export interface TBATeam {
+
address: string | null;
+
city: string;
+
country: string;
+
gmaps_place_id: string | null;
+
gmaps_url: string | null;
+
key: string;
+
lat: number | null;
+
lng: number | null;
+
location_name: string | null;
+
motto: string | null;
+
name: string;
+
nickname: string;
+
postal_code: string | null;
+
rookie_year: number;
+
school_name: string;
+
state_prov: string;
+
team_number: number;
+
website: string | null;
+
}