import "dotenv/config"; const AUTH_KEY = Bun.env.TBA_AUTH_KEY as string; if (!AUTH_KEY) { process.exit(1); } const authHeaders = new Headers({ "X-TBA-Auth-Key": AUTH_KEY, }); const BASE_URL = "https://www.thebluealliance.com/api/v3"; interface CacheEntry { etag: string; data: string; staleTime: number; } const CACHE_PATH = Bun.env.CACHE_PATH as string; if (!CACHE_PATH) { process.exit(1); } async function initializeCache() { let data = {}; const cacheFile = Bun.file(".cache"); if (cacheFile.size > 0) { data = JSON.parse(await cacheFile.text()); } return data; } const tbaCache: { [name: string]: CacheEntry } = await initializeCache(); async function writeCache() { Bun.write(CACHE_PATH, JSON.stringify(tbaCache)); } interface TBAResponse { data: string; status: number; } function updateCache( endpoint: string, etag: string | null, data: string, maxAge: number, ) { if (etag) { tbaCache[endpoint] = { etag, data, staleTime: Date.now() + maxAge * 1000 }; } } function fetchCache(endpoint: string) { return tbaCache[endpoint]; } async function getTBAEndpoint(endpoint: string): Promise { const time = Date.now(); if (tbaCache[endpoint] && tbaCache[endpoint].staleTime > time) { console.log("Cache hit... " + endpoint); return { data: fetchCache(endpoint).data, status: 200 }; } console.log("Fetching... " + endpoint); const headers = new Headers(authHeaders); if (tbaCache[endpoint]) { headers.append("If-None-Match", tbaCache[endpoint].etag); } const response = await fetch(BASE_URL + endpoint, { headers }); let data; if (response.status === 200) { data = await response.text(); const maxAge = parseInt( response.headers .get("cache-control") ?.split(",") .filter((d) => d.includes("max-age"))[0] .trim() .split("=")[1] as string, ); updateCache(endpoint, response.headers.get("ETag"), data, maxAge); } else if (response.status === 304) { // Safety: a 304 response indicates out cache is valid based on ETag data = fetchCache(endpoint).data as string; console.log("Unchanged... " + endpoint); } else { data = await response.text(); } return { data, status: response.status, }; } const response = await getTBAEndpoint( "/team/frc4043/event/2024pncmp/matches/simple", ); console.log(response.data); writeCache();