import type { RudderContext } from '../core/context.js'; import { api } from '../generated/api.js'; import type { RankEntry } from '../generated/leaderboards.js'; /** * A cached handle for a specific leaderboard. * * Created by `LeaderboardsService.findBySlug()` — subsequent calls with * the same slug return the same handle, avoiding redundant fetches. */ export class LeaderboardHandle { private entries: RankEntry[] = []; constructor( public readonly slug: string, private readonly ctx: RudderContext, ) {} getEntries(): readonly RankEntry[] { return this.entries; } async submit(score: number): Promise { return api.submitScore(this.ctx, { slug: this.slug }, { slug: this.slug, score }); } async list(limit = 100): Promise { const response = await api.getRanking( this.ctx, { slug: this.slug }, { limit: limit > 0 ? limit : undefined }, ); this.entries = response.entries ?? []; return this.entries; } } export class LeaderboardsService { private readonly cache = new Map(); constructor(private readonly ctx: RudderContext) {} /** Returns a cached handle for the given leaderboard slug. */ findBySlug(slug: string): LeaderboardHandle { let handle = this.cache.get(slug); if (!handle) { handle = new LeaderboardHandle(slug, this.ctx); this.cache.set(slug, handle); } return handle; } }