16
16
17
17
import { OVSXClient , VSXQueryOptions , VSXQueryResult , VSXSearchOptions , VSXSearchResult } from './ovsx-types' ;
18
18
import { RequestContext , RequestService } from '@theia/request' ;
19
+ import { RateLimiter } from 'limiter' ;
20
+
21
+ export const OVSX_RATE_LIMIT = 15 ;
19
22
20
23
export class OVSXHttpClient implements OVSXClient {
21
24
22
25
/**
23
26
* @param requestService
24
27
* @returns factory that will cache clients based on the requested input URL.
25
28
*/
26
- static createClientFactory ( requestService : RequestService ) : ( url : string ) => OVSXClient {
29
+ static createClientFactory ( requestService : RequestService , rateLimiter ?: RateLimiter ) : ( url : string ) => OVSXClient {
27
30
// eslint-disable-next-line no-null/no-null
28
31
const cachedClients : Record < string , OVSXClient > = Object . create ( null ) ;
29
- return url => cachedClients [ url ] ??= new this ( url , requestService ) ;
32
+ return url => cachedClients [ url ] ??= new this ( url , requestService , rateLimiter ) ;
30
33
}
31
34
32
35
constructor (
33
36
protected vsxRegistryUrl : string ,
34
- protected requestService : RequestService
37
+ protected requestService : RequestService ,
38
+ protected rateLimiter = new RateLimiter ( { tokensPerInterval : OVSX_RATE_LIMIT , interval : 'second' } )
35
39
) { }
36
40
37
41
search ( searchOptions ?: VSXSearchOptions ) : Promise < VSXSearchResult > {
@@ -43,10 +47,25 @@ export class OVSXHttpClient implements OVSXClient {
43
47
}
44
48
45
49
protected async requestJson < R > ( url : string ) : Promise < R > {
46
- return RequestContext . asJson < R > ( await this . requestService . request ( {
47
- url,
48
- headers : { 'Accept' : 'application/json' }
49
- } ) ) ;
50
+ const attempts = 5 ;
51
+ for ( let i = 0 ; i < attempts ; i ++ ) {
52
+ // Use 1, 2, 4, 8, 16 tokens for each attempt
53
+ const tokenCount = Math . pow ( 2 , i ) ;
54
+ await this . rateLimiter . removeTokens ( tokenCount ) ;
55
+ const context = await this . requestService . request ( {
56
+ url,
57
+ headers : { 'Accept' : 'application/json' }
58
+ } ) ;
59
+ if ( context . res . statusCode === 429 ) {
60
+ console . warn ( 'OVSX rate limit exceeded. Consider reducing the rate limit.' ) ;
61
+ // If there are still more attempts left, retry the request with a higher token count
62
+ if ( i < attempts - 1 ) {
63
+ continue ;
64
+ }
65
+ }
66
+ return RequestContext . asJson < R > ( context ) ;
67
+ }
68
+ throw new Error ( 'Failed to fetch data from OVSX.' ) ;
50
69
}
51
70
52
71
protected buildUrl ( url : string , query ?: object ) : string {
0 commit comments