@@ -6,25 +6,36 @@ import { onApiRequest, type ApiRequestInfo } from "./github";
66
77// ── Types ─────────────────────────────────────────────────────────────────────
88
9- export type ApiCallSource =
10- | "lightSearch"
11- | "heavyBackfill"
12- | "forkCheck"
13- | "globalUserSearch"
14- | "unfilteredSearch"
15- | "upstreamDiscovery"
16- | "workflowRuns"
17- | "hotPRStatus"
18- | "hotRunStatus"
19- | "notifications"
20- | "validateUser"
21- | "fetchOrgs"
22- | "fetchRepos"
23- | "rateLimitCheck"
24- | "graphql"
25- | "rest" ;
26-
27- export type ApiPool = "graphql" | "core" ;
9+ const API_CALL_SOURCES = [
10+ "lightSearch" , "heavyBackfill" , "forkCheck" , "globalUserSearch" , "unfilteredSearch" ,
11+ "upstreamDiscovery" , "workflowRuns" , "hotPRStatus" , "hotRunStatus" , "notifications" ,
12+ "validateUser" , "fetchOrgs" , "fetchRepos" , "rateLimitCheck" , "graphql" , "rest" ,
13+ ] as const ;
14+
15+ export type ApiCallSource = ( typeof API_CALL_SOURCES ) [ number ] ;
16+
17+ const API_POOLS = [ "graphql" , "core" ] as const ;
18+
19+ export type ApiPool = ( typeof API_POOLS ) [ number ] ;
20+
21+ export const SOURCE_LABELS : Record < ApiCallSource , string > = {
22+ lightSearch : "Light Search" ,
23+ heavyBackfill : "PR Backfill" ,
24+ forkCheck : "Fork Check" ,
25+ globalUserSearch : "Tracked User Search" ,
26+ unfilteredSearch : "Unfiltered Search" ,
27+ upstreamDiscovery : "Upstream Discovery" ,
28+ workflowRuns : "Workflow Runs" ,
29+ hotPRStatus : "Hot PR Status" ,
30+ hotRunStatus : "Hot Run Status" ,
31+ notifications : "Notifications" ,
32+ validateUser : "Validate User" ,
33+ fetchOrgs : "Fetch Orgs" ,
34+ fetchRepos : "Fetch Repos" ,
35+ rateLimitCheck : "Rate Limit Check" ,
36+ graphql : "GraphQL (other)" ,
37+ rest : "REST (other)" ,
38+ } ;
2839
2940export interface ApiCallRecord {
3041 source : ApiCallSource ;
@@ -45,8 +56,8 @@ const USAGE_STORAGE_KEY = "github-tracker:api-usage";
4556// ── Zod schemas ───────────────────────────────────────────────────────────────
4657
4758const ApiCallRecordSchema = z . object ( {
48- source : z . string ( ) ,
49- pool : z . string ( ) ,
59+ source : z . enum ( API_CALL_SOURCES ) ,
60+ pool : z . enum ( API_POOLS ) ,
5061 count : z . number ( ) ,
5162 lastCalledAt : z . number ( ) ,
5263} ) ;
@@ -64,7 +75,7 @@ export function loadUsageData(): ApiUsageData {
6475 if ( raw === null || raw === undefined ) return { records : { } , resetAt : null } ;
6576 const parsed = JSON . parse ( raw ) as unknown ;
6677 const result = ApiUsageDataSchema . safeParse ( parsed ) ;
67- if ( result . success ) return result . data as ApiUsageData ;
78+ if ( result . success ) return result . data ;
6879 return { records : { } , resetAt : null } ;
6980 } catch {
7081 return { records : { } , resetAt : null } ;
@@ -104,10 +115,10 @@ export function clearUsageData(): void {
104115
105116export function checkAndResetIfExpired ( ) : void {
106117 if ( _usageData . resetAt !== null && Date . now ( ) > _usageData . resetAt ) {
107- resetUsageData ( ) ;
118+ _usageData . records = { } ;
108119 _usageData . resetAt = null ;
109- // Write immediately so the null resetAt persists (prevents redundant re-reset on page reload)
110120 _writeToStorage ( ) ;
121+ _setVersion ( ( v ) => v + 1 ) ;
111122 }
112123}
113124
@@ -195,9 +206,13 @@ const REST_SOURCE_PATTERNS: Array<[RegExp, ApiCallSource]> = [
195206 [ / ^ \/ r a t e _ l i m i t / , "rateLimitCheck" ] ,
196207] ;
197208
209+ const API_CALL_SOURCE_SET = new Set < string > ( API_CALL_SOURCES ) ;
210+
198211export function deriveSource ( info : ApiRequestInfo ) : ApiCallSource {
199212 if ( info . isGraphql ) {
200- return ( info . apiSource as ApiCallSource ) ?? "graphql" ;
213+ return info . apiSource && API_CALL_SOURCE_SET . has ( info . apiSource )
214+ ? ( info . apiSource as ApiCallSource )
215+ : "graphql" ;
201216 }
202217 for ( const [ pattern , source ] of REST_SOURCE_PATTERNS ) {
203218 if ( pattern . test ( info . url ) ) return source ;
@@ -210,5 +225,6 @@ onApiRequest((info) => {
210225 const source = deriveSource ( info ) ;
211226 const pool : ApiPool = info . isGraphql ? "graphql" : "core" ;
212227 trackApiCall ( source , pool ) ;
213- if ( info . resetEpochMs !== null && info . isGraphql ) updateResetAt ( info . resetEpochMs ) ;
228+ // Both pools tracked — Math.max keeps latest; may delay reset of the earlier pool's records
229+ if ( info . resetEpochMs !== null ) updateResetAt ( info . resetEpochMs ) ;
214230} ) ;
0 commit comments