@@ -31,12 +31,9 @@ type AOJChallengeContestAPI = {
31
31
} ;
32
32
33
33
/**
34
- * Enum representing the types of challenge contests available.
34
+ * Represents the types of challenge contests available.
35
35
*/
36
- enum ChallengeContestType {
37
- PCK = 'pck' ,
38
- JAG = 'jag' ,
39
- }
36
+ type ChallengeContestType = 'PCK' | 'JAG' ;
40
37
41
38
/**
42
39
* Represents a challenge contest in the AOJ
@@ -81,31 +78,25 @@ type AOJTaskAPI = {
81
78
type AOJTaskAPIs = AOJTaskAPI [ ] ;
82
79
83
80
/**
84
- * Enum representing PCK contest rounds
81
+ * Represents PCK contest rounds
85
82
*/
86
- enum PckRound {
87
- PRELIM = 'prelim' ,
88
- FINAL = 'final' ,
89
- }
83
+ type PckRound = 'PRELIM' | 'FINAL' ;
90
84
91
85
/**
92
- * Enum representing JAG contest rounds
86
+ * Represents JAG contest rounds
93
87
*/
94
- enum JagRound {
95
- PRELIM = 'prelim' ,
96
- REGIONAL = 'regional' ,
97
- }
88
+ type JagRound = 'PRELIM' | 'REGIONAL' ;
98
89
99
90
/**
100
91
* A map that associates each type of challenge contest with its corresponding round type.
101
92
*
102
93
* @typedef {Object } ChallengeRoundMap
103
- * @property {PckRound } ChallengeContestType. PCK - The round type for PCK contests.
104
- * @property {JagRound } ChallengeContestType. JAG - The round type for JAG contests.
94
+ * @property {PckRound } PCK - The round type for PCK contests.
95
+ * @property {JagRound } JAG - The round type for JAG contests.
105
96
*/
106
97
type ChallengeRoundMap = {
107
- [ ChallengeContestType . PCK ] : PckRound ;
108
- [ ChallengeContestType . JAG ] : JagRound ;
98
+ PCK : PckRound ;
99
+ JAG : JagRound ;
109
100
} ;
110
101
111
102
/**
@@ -121,6 +112,17 @@ const PENDING = -1;
121
112
const DEFAULT_CACHE_TTL = 60 * 60 * 1000 ; // 1 hour in milliseconds
122
113
const DEFAULT_MAX_CACHE_SIZE = 50 ;
123
114
115
+ /**
116
+ * Configuration options for caching.
117
+ *
118
+ * @property {number } [timeToLive] - The duration (in milliseconds) for which a cache entry should remain valid.
119
+ * @property {number } [maxSize] - The maximum number of entries that the cache can hold.
120
+ */
121
+ interface CacheConfig {
122
+ timeToLive ?: number ;
123
+ maxSize ?: number ;
124
+ }
125
+
124
126
/**
125
127
* Represents a cache entry with data and a timestamp.
126
128
*
@@ -195,6 +197,10 @@ class Cache<T> {
195
197
* @param data - The data to be cached.
196
198
*/
197
199
set ( key : string , data : T ) : void {
200
+ if ( ! key || typeof key !== 'string' || key . length > 255 ) {
201
+ throw new Error ( 'Invalid cache key' ) ;
202
+ }
203
+
198
204
if ( this . cache . size >= this . maxSize ) {
199
205
const oldestKey = this . findOldestEntry ( ) ;
200
206
@@ -279,6 +285,11 @@ class Cache<T> {
279
285
}
280
286
}
281
287
288
+ interface ApiClientConfig {
289
+ contestCache : CacheConfig ;
290
+ taskCache : CacheConfig ;
291
+ }
292
+
282
293
/**
283
294
* AojApiClient is a client for interacting with the Aizu Online Judge (AOJ) API.
284
295
* It extends the ContestSiteApiClient and provides methods to fetch contests and tasks
@@ -310,6 +321,30 @@ export class AojApiClient extends ContestSiteApiClient {
310
321
*/
311
322
private readonly taskCache = new Cache < TasksForImport > ( ) ;
312
323
324
+ /**
325
+ * Constructs an instance of the Aizu Online Judge client.
326
+ *
327
+ * @param {ApiClientConfig } [config] - Optional configuration object for the API client.
328
+ * @param {Cache<ContestsForImport> } [config.contestCache] - Configuration for the contest cache.
329
+ * @param {number } [config.contestCache.timeToLive] - Time to live for contest cache entries.
330
+ * @param {number } [config.contestCache.maxSize] - Maximum size of the contest cache.
331
+ * @param {Cache<TasksForImport> } [config.taskCache] - Configuration for the task cache.
332
+ * @param {number } [config.taskCache.timeToLive] - Time to live for task cache entries.
333
+ * @param {number } [config.taskCache.maxSize] - Maximum size of the task cache.
334
+ */
335
+ constructor ( config ?: ApiClientConfig ) {
336
+ super ( ) ;
337
+
338
+ this . contestCache = new Cache < ContestsForImport > (
339
+ config ?. contestCache ?. timeToLive ,
340
+ config ?. contestCache ?. maxSize ,
341
+ ) ;
342
+ this . taskCache = new Cache < TasksForImport > (
343
+ config ?. taskCache ?. timeToLive ,
344
+ config ?. taskCache ?. maxSize ,
345
+ ) ;
346
+ }
347
+
313
348
/**
314
349
* Disposes of the resources used by the client.
315
350
* Clears the contest and task caches to free up memory.
@@ -331,10 +366,10 @@ export class AojApiClient extends ContestSiteApiClient {
331
366
try {
332
367
const results = await Promise . allSettled ( [
333
368
this . fetchCourseContests ( ) ,
334
- this . fetchChallengeContests ( ChallengeContestType . PCK , PckRound . PRELIM ) ,
335
- this . fetchChallengeContests ( ChallengeContestType . PCK , PckRound . FINAL ) ,
336
- this . fetchChallengeContests ( ChallengeContestType . JAG , JagRound . PRELIM ) ,
337
- this . fetchChallengeContests ( ChallengeContestType . JAG , JagRound . REGIONAL ) ,
369
+ this . fetchChallengeContests ( ' PCK' , ' PRELIM' ) ,
370
+ this . fetchChallengeContests ( ' PCK' , ' FINAL' ) ,
371
+ this . fetchChallengeContests ( ' JAG' , ' PRELIM' ) ,
372
+ this . fetchChallengeContests ( ' JAG' , ' REGIONAL' ) ,
338
373
] ) ;
339
374
340
375
const [ courses , pckPrelims , pckFinals , jagPrelims , jagRegionals ] = results . map ( ( result ) => {
@@ -492,7 +527,7 @@ export class AojApiClient extends ContestSiteApiClient {
492
527
const validateSegment = ( segment : string ) : boolean => {
493
528
return (
494
529
segment . length <= MAX_SEGMENT_LENGTH &&
495
- / ^ [ a - z A - Z ] [ a - z A - Z 0 - 9 ] { 0 , 98 } [ - _ a - z A - Z 0 - 9 ] * $ / . test ( segment ) &&
530
+ / ^ [ a - z A - Z ] (?: [ a - z A - Z 0 - 9 ] | [ - _ ] (? = [ a - z A - Z 0 - 9 ] ) ) { 0 , 98 } [ a - z A - Z 0 - 9 ] $ / . test ( segment ) &&
496
531
! segment . includes ( '..' )
497
532
) ;
498
533
} ;
@@ -550,10 +585,10 @@ export class AojApiClient extends ContestSiteApiClient {
550
585
try {
551
586
const results = await Promise . allSettled ( [
552
587
this . fetchCourseTasks ( ) ,
553
- this . fetchChallengeTasks ( ChallengeContestType . PCK , PckRound . PRELIM ) ,
554
- this . fetchChallengeTasks ( ChallengeContestType . PCK , PckRound . FINAL ) ,
555
- this . fetchChallengeTasks ( ChallengeContestType . JAG , JagRound . PRELIM ) ,
556
- this . fetchChallengeTasks ( ChallengeContestType . JAG , JagRound . REGIONAL ) ,
588
+ this . fetchChallengeTasks ( ' PCK' , ' PRELIM' ) ,
589
+ this . fetchChallengeTasks ( ' PCK' , ' FINAL' ) ,
590
+ this . fetchChallengeTasks ( ' JAG' , ' PRELIM' ) ,
591
+ this . fetchChallengeTasks ( ' JAG' , ' REGIONAL' ) ,
557
592
] ) ;
558
593
559
594
const [ courses , pckPrelims , pckFinals , jagPrelims , jagRegionals ] = results . map ( ( result ) => {
0 commit comments