@@ -35,6 +35,7 @@ export interface ReleaseInfo {
35
35
tagName : string ;
36
36
checksum : string | undefined ;
37
37
kind : "wasm" | "process" ;
38
+ downloadCount : number ;
38
39
}
39
40
40
41
const latestReleaseTagNameCache = new LruCacheWithExpiry < string , ReleaseInfo | undefined > ( {
@@ -43,32 +44,20 @@ const latestReleaseTagNameCache = new LruCacheWithExpiry<string, ReleaseInfo | u
43
44
} ) ;
44
45
45
46
export async function getLatestReleaseInfo ( username : string , repoName : string ) {
46
- if ( ! validateName ( username ) || ! validateName ( repoName ) ) {
47
- return undefined ;
48
- }
49
-
50
- const url = `https://api.github.com/repos/${ username } /${ repoName } /releases/latest` ;
51
- return await latestReleaseTagNameCache . getOrSet ( url , async ( ) => {
52
- const response = await makeGitHubGetRequest ( url , "GET" ) ;
53
- if ( response . status === 404 ) {
54
- await response . text ( ) ; // todo: no way to mark this as used for the sanitizers?
55
- return undefined ;
56
- } else if ( ! response . ok ) {
57
- const text = await response . text ( ) ;
58
- throw new Error ( `Invalid response status: ${ response . status } \n\n${ text } ` ) ;
59
- }
60
- return getReleaseInfo ( await response . json ( ) ) ;
61
- } ) ;
47
+ const releases = await getReleasesData ( username , repoName ) ;
48
+ const latest = releases ?. find ( ( release ) => ! release . draft && ! release . prerelease ) ;
49
+ return latest ? getReleaseInfo ( latest ) : undefined ;
62
50
}
63
51
64
- function getReleaseInfo ( data : { tag_name : string ; body : string ; assets : { name : string } [ ] } ) : ReleaseInfo {
52
+ function getReleaseInfo ( data : GitHubRelease ) : ReleaseInfo {
65
53
if ( typeof data . tag_name !== "string" ) {
66
54
throw new Error ( "The tag name was not a string." ) ;
67
55
}
68
56
return {
69
57
tagName : data . tag_name ,
70
58
checksum : getChecksum ( ) ,
71
59
kind : getPluginKind ( ) ,
60
+ downloadCount : getDownloadCount ( data . assets ) ,
72
61
} ;
73
62
74
63
function getChecksum ( ) {
@@ -97,6 +86,52 @@ function getReleaseInfo(data: { tag_name: string; body: string; assets: { name:
97
86
}
98
87
}
99
88
89
+ function getDownloadCount ( assets : ReleaseAsset [ ] ) {
90
+ return assets . find ( ( { name } ) => name === "plugin.wasm" || name === "plugin.json" ) ?. download_count ?? 0 ;
91
+ }
92
+
93
+ interface ReleaseAsset {
94
+ name : string ;
95
+ download_count : number ;
96
+ }
97
+
98
+ export async function getAllDownloadCount ( username : string , repoName : string ) {
99
+ const releases = await getReleasesData ( username , repoName ) ;
100
+ return releases ?. reduce ( ( total , current ) => total + getDownloadCount ( current . assets ) , 0 ) ?? 0 ;
101
+ }
102
+
103
+ interface GitHubRelease {
104
+ tag_name : string ;
105
+ body : string ;
106
+ draft : boolean ;
107
+ prerelease : boolean ;
108
+ assets : ReleaseAsset [ ] ;
109
+ }
110
+
111
+ const releasesCache = new LruCacheWithExpiry < string , GitHubRelease [ ] > ( {
112
+ size : 1000 ,
113
+ expiryMs : 5 * 60 * 1_000 , // keep entries for 5 minutes
114
+ } ) ;
115
+
116
+ async function getReleasesData ( username : string , repoName : string ) {
117
+ if ( ! validateName ( username ) || ! validateName ( repoName ) ) {
118
+ return ;
119
+ }
120
+
121
+ const url = `https://api.github.com/repos/${ username } /${ repoName } /releases` ;
122
+ return await releasesCache . getOrSet ( url , async ( ) => {
123
+ const response = await makeGitHubGetRequest ( url , "GET" ) ;
124
+ if ( response . status === 404 ) {
125
+ await response . text ( ) ; // todo: no way to mark this as used for the sanitizers?
126
+ return ;
127
+ } else if ( ! response . ok ) {
128
+ const text = await response . text ( ) ;
129
+ throw new Error ( `Invalid response status: ${ response . status } \n\n${ text } ` ) ;
130
+ }
131
+ return response . json ( ) ;
132
+ } ) ;
133
+ }
134
+
100
135
const latestCliReleaseInfo = new LazyExpirableValue < any > ( {
101
136
expiryMs : 10 * 60 * 1_000 , // keep for 10 minutes
102
137
createValue : async ( ) => {
0 commit comments