@@ -10,25 +10,11 @@ const UpstreamLikesLeaderboardEntrySchema = v.object({
1010} )
1111
1212const UpstreamLikesLeaderboardResponseSchema = v . object ( {
13- totalLikes : v . optional ( v . number ( ) ) ,
14- totalUniqueLikers : v . optional ( v . number ( ) ) ,
1513 leaderBoard : v . array ( UpstreamLikesLeaderboardEntrySchema ) ,
1614} )
1715
18- type UpstreamLikesLeaderboardResponse = v . InferOutput < typeof UpstreamLikesLeaderboardResponseSchema >
19-
20- export type ResolvedLikesLeaderboard = {
21- totalLikes : number | null
22- totalUniqueLikers : number | null
23- entries : LikesLeaderboardEntry [ ]
24- }
25-
2616const LIKES_LEADERBOARD_FETCH_TIMEOUT_MS = 750
2717
28- type LikesLeaderboardOptions = {
29- timeoutMs ?: number
30- }
31-
3218export const LIKES_LEADERBOARD_MAX_ENTRIES = 10
3319
3420export function extractPackageNameFromSubjectRef ( subjectRef : string ) : string | null {
@@ -42,82 +28,69 @@ export function extractPackageNameFromSubjectRef(subjectRef: string): string | n
4228 }
4329}
4430
45- export function normalizeLikesLeaderboardPayload (
46- payload : unknown ,
47- ) : ResolvedLikesLeaderboard | null {
31+ export function normalizeLikesLeaderboardPayload ( payload : unknown ) : LikesLeaderboardEntry [ ] | null {
4832 const parsedPayload = v . safeParse ( UpstreamLikesLeaderboardResponseSchema , payload )
4933 if ( ! parsedPayload . success ) {
5034 return null
5135 }
5236
5337 // PRECONDITION: the response is already sorted by totalLikes in descending order
54- const entries = parsedPayload . output . leaderBoard
55- . map ( ( entry ) : LikesLeaderboardEntry | null => {
56- const packageName = extractPackageNameFromSubjectRef ( entry . subjectRef )
57- if ( ! packageName ) return null
58-
59- return {
60- rank : 0 ,
61- packageName,
62- subjectRef : entry . subjectRef ,
63- totalLikes : entry . totalLikes ,
64- }
65- } )
66- . filter ( ( entry ) : entry is LikesLeaderboardEntry => entry !== null )
67- // oxlint-disable-next-line no-map-spread -- only a few elements
68- . map ( ( entry , index ) => ( {
69- ...entry ,
70- rank : index + 1 ,
71- } ) )
72-
73- return {
74- totalLikes : parsedPayload . output . totalLikes ?? null ,
75- totalUniqueLikers : parsedPayload . output . totalUniqueLikers ?? null ,
76- entries,
77- }
38+ return (
39+ parsedPayload . output . leaderBoard
40+ . map ( ( entry ) : LikesLeaderboardEntry | null => {
41+ const packageName = extractPackageNameFromSubjectRef ( entry . subjectRef )
42+ if ( ! packageName ) return null
43+
44+ return {
45+ rank : 0 ,
46+ packageName,
47+ subjectRef : entry . subjectRef ,
48+ totalLikes : entry . totalLikes ,
49+ }
50+ } )
51+ . filter ( ( entry ) : entry is LikesLeaderboardEntry => entry !== null )
52+ // oxlint-disable-next-line no-map-spread -- only a few elements
53+ . map ( ( entry , index ) => ( {
54+ ...entry ,
55+ rank : index + 1 ,
56+ } ) )
57+ )
7858}
7959
80- export async function getLikesLeaderboard (
81- event : H3Event ,
82- options : LikesLeaderboardOptions = { } ,
83- ) : Promise < ResolvedLikesLeaderboard | null > {
84- const timeoutMs = options . timeoutMs ?? LIKES_LEADERBOARD_FETCH_TIMEOUT_MS
60+ export async function getLikesLeaderboard ( event : H3Event ) : Promise < LikesLeaderboardEntry [ ] | null > {
8561 const cachedFetch = event . context . cachedFetch as CachedFetchFunction | undefined
8662 if ( ! cachedFetch ) {
87- console . error ( 'Something went wrong: event.context.cachedFetch is missing. Aborting fetch.' , {
88- eventContext : event . context ,
89- } )
63+ console . error ( '[likes-leaderboard] Missing cachedFetch in request context' )
9064 return null
9165 }
9266
9367 try {
9468 const url = new URL ( LIKES_LEADERBOARD_API_URL )
9569 url . searchParams . set ( 'limit' , LIKES_LEADERBOARD_MAX_ENTRIES . toString ( ) )
9670
97- const { data } = await cachedFetch < UpstreamLikesLeaderboardResponse > (
71+ const { data } = await cachedFetch (
9872 url . toString ( ) ,
9973 {
10074 headers : {
10175 'User-Agent' : 'npmx' ,
10276 'Accept' : 'application/json' ,
10377 } ,
104- signal : AbortSignal . timeout ( timeoutMs ) ,
78+ signal : AbortSignal . timeout ( LIKES_LEADERBOARD_FETCH_TIMEOUT_MS ) ,
10579 } ,
10680 CACHE_MAX_AGE_ONE_HOUR ,
10781 )
10882
10983 return normalizeLikesLeaderboardPayload ( data )
11084 } catch ( err ) {
111- console . error ( 'Failed to fetch likes leaderboard' , { err } )
85+ console . error (
86+ '[likes-leaderboard] Failed to fetch likes leaderboard:' ,
87+ err instanceof Error ? err . message : 'Unknown error' ,
88+ )
11289 return null
11390 }
11491}
11592
116- export async function getTopLikedRank (
117- event : H3Event ,
118- subjectRef : string ,
119- options : LikesLeaderboardOptions = { } ,
120- ) : Promise < number | null > {
121- const leaderboard = await getLikesLeaderboard ( event , options )
122- return leaderboard ?. entries . find ( entry => entry . subjectRef === subjectRef ) ?. rank ?? null
93+ export async function getTopLikedRank ( event : H3Event , subjectRef : string ) : Promise < number | null > {
94+ const leaderboard = await getLikesLeaderboard ( event )
95+ return leaderboard ?. find ( entry => entry . subjectRef === subjectRef ) ?. rank ?? null
12396}
0 commit comments