@@ -10,6 +10,8 @@ export interface GitHubUserData {
1010 location : string | null
1111 websiteUrl : string | null
1212 twitterUsername : string | null
13+ blueskyHandle : string | null
14+ mastodonUrl : string | null
1315}
1416
1517export interface GitHubContributor extends GitHubUserData {
@@ -30,7 +32,7 @@ type GitHubAPIContributor = Omit<GitHubContributor, 'role' | 'sponsors_url' | ke
3032
3133// Fallback when no GitHub token is available (e.g. preview environments).
3234// Only stewards are shown as maintainers; everyone else is a contributor.
33- const FALLBACK_STEWARDS = new Set ( [ 'danielroe' , 'patak-dev ' ] )
35+ const FALLBACK_STEWARDS = new Set ( [ 'danielroe' , 'patak-cat ' ] )
3436
3537const DEFAULT_USER_INFO : GitHubUserData = {
3638 name : null ,
@@ -40,6 +42,8 @@ const DEFAULT_USER_INFO: GitHubUserData = {
4042 location : null ,
4143 websiteUrl : null ,
4244 twitterUsername : null ,
45+ blueskyHandle : null ,
46+ mastodonUrl : null ,
4347}
4448
4549// Configure sanitize-html for GitHub's companyHTML and company fields
@@ -142,7 +146,7 @@ async function fetchGitHubUserData(
142146 // Build aliased GraphQL query: user0: user(login: "x") { hasSponsorsListing login }
143147 const fragments = logins . map (
144148 ( login , i ) =>
145- `user${ i } : user(login: "${ login } ") { hasSponsorsListing login name bio company companyHTML location websiteUrl twitterUsername }` ,
149+ `user${ i } : user(login: "${ login } ") { hasSponsorsListing login name bio company companyHTML location websiteUrl twitterUsername socialAccounts(first: 10) { nodes { provider url } } }` ,
146150 )
147151 const query = `{ ${ fragments . join ( '\n' ) } }`
148152
@@ -165,7 +169,12 @@ async function fetchGitHubUserData(
165169 const json = ( await response . json ( ) ) as {
166170 data ?: Record <
167171 string ,
168- ( GitHubUserData & { login : string ; hasSponsorsListing : boolean } ) | null
172+ | ( GitHubUserData & {
173+ login : string
174+ hasSponsorsListing : boolean
175+ socialAccounts : { nodes : { provider : string ; url : string } [ ] }
176+ } )
177+ | null
169178 >
170179 }
171180
@@ -176,6 +185,25 @@ async function fetchGitHubUserData(
176185 if ( user . hasSponsorsListing ) {
177186 sponsorable . add ( user . login )
178187 }
188+
189+ // Extract Bluesky and Mastodon from socialAccounts
190+ let blueskyHandle : string | null = null
191+ let mastodonUrl : string | null = null
192+
193+ if ( user . socialAccounts ?. nodes ) {
194+ for ( const account of user . socialAccounts . nodes ) {
195+ if ( account . url . includes ( 'bsky.app' ) ) {
196+ // Extract handle from URL: https://bsky.app/profile/handle.bsky.social
197+ const match = account . url . match ( / b s k y \. a p p \/ p r o f i l e \/ ( [ ^ / ? ] + ) / )
198+ if ( match ) {
199+ blueskyHandle = match [ 1 ] as string
200+ }
201+ } else if ( account . provider === 'MASTODON' ) {
202+ mastodonUrl = account . url
203+ }
204+ }
205+ }
206+
179207 // --- SERVER-SIDE SANITIZATION AND BATCHING ---
180208 usersData . set ( user . login , {
181209 name : cleanString ( user . name ) ,
@@ -186,6 +214,8 @@ async function fetchGitHubUserData(
186214 location : cleanString ( user . location ) ,
187215 websiteUrl : cleanString ( user . websiteUrl , true ) ,
188216 twitterUsername : cleanString ( user . twitterUsername ) ,
217+ blueskyHandle,
218+ mastodonUrl,
189219 } )
190220 }
191221 }
@@ -272,11 +302,30 @@ export default defineCachedEventHandler(
272302 const sponsors_url = sponsorable . has ( c . login )
273303 ? `https://github.com/sponsors/${ c . login } `
274304 : null
275- Object . assign ( c , { role, order, sponsors_url, ...userInfo } )
276- return c as GitHubContributor & { order : number ; sponsors_url : string | null ; role : Role }
305+
306+ // Construct the final object with only necessary fields
307+ return {
308+ id : c . id ,
309+ login : c . login ,
310+ name : userInfo . name ,
311+ avatar_url : c . avatar_url ,
312+ html_url : c . html_url ,
313+ role,
314+ bio : userInfo . bio ,
315+ company : userInfo . company ,
316+ companyHTML : userInfo . companyHTML ,
317+ location : userInfo . location ,
318+ websiteUrl : userInfo . websiteUrl ,
319+ twitterUsername : userInfo . twitterUsername ,
320+ blueskyHandle : userInfo . blueskyHandle ,
321+ mastodonUrl : userInfo . mastodonUrl ,
322+ sponsors_url,
323+ contributions : c . contributions ,
324+ order, // kept for sorting, removed in next step
325+ }
277326 } )
278327 . sort ( ( a , b ) => a . order - b . order || b . contributions - a . contributions )
279- . map ( ( { order : _ , ...rest } ) => rest )
328+ . map ( ( { order : _ , ...rest } ) => rest ) as GitHubContributor [ ]
280329 } ,
281330 {
282331 maxAge : 3600 , // Cache for 1 hour
0 commit comments