@@ -31,12 +31,9 @@ type AOJChallengeContestAPI = {
3131} ;
3232
3333/**
34- * Enum representing the types of challenge contests available.
34+ * Represents the types of challenge contests available.
3535 */
36- enum ChallengeContestType {
37- PCK = 'pck' ,
38- JAG = 'jag' ,
39- }
36+ type ChallengeContestType = 'PCK' | 'JAG' ;
4037
4138/**
4239 * Represents a challenge contest in the AOJ
@@ -81,31 +78,25 @@ type AOJTaskAPI = {
8178type AOJTaskAPIs = AOJTaskAPI [ ] ;
8279
8380/**
84- * Enum representing PCK contest rounds
81+ * Represents PCK contest rounds
8582 */
86- enum PckRound {
87- PRELIM = 'prelim' ,
88- FINAL = 'final' ,
89- }
83+ type PckRound = 'PRELIM' | 'FINAL' ;
9084
9185/**
92- * Enum representing JAG contest rounds
86+ * Represents JAG contest rounds
9387 */
94- enum JagRound {
95- PRELIM = 'prelim' ,
96- REGIONAL = 'regional' ,
97- }
88+ type JagRound = 'PRELIM' | 'REGIONAL' ;
9889
9990/**
10091 * A map that associates each type of challenge contest with its corresponding round type.
10192 *
10293 * @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.
10596 */
10697type ChallengeRoundMap = {
107- [ ChallengeContestType . PCK ] : PckRound ;
108- [ ChallengeContestType . JAG ] : JagRound ;
98+ PCK : PckRound ;
99+ JAG : JagRound ;
109100} ;
110101
111102/**
@@ -121,6 +112,17 @@ const PENDING = -1;
121112const DEFAULT_CACHE_TTL = 60 * 60 * 1000 ; // 1 hour in milliseconds
122113const DEFAULT_MAX_CACHE_SIZE = 50 ;
123114
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+
124126/**
125127 * Represents a cache entry with data and a timestamp.
126128 *
@@ -195,6 +197,10 @@ class Cache<T> {
195197 * @param data - The data to be cached.
196198 */
197199 set ( key : string , data : T ) : void {
200+ if ( ! key || typeof key !== 'string' || key . length > 255 ) {
201+ throw new Error ( 'Invalid cache key' ) ;
202+ }
203+
198204 if ( this . cache . size >= this . maxSize ) {
199205 const oldestKey = this . findOldestEntry ( ) ;
200206
@@ -279,6 +285,11 @@ class Cache<T> {
279285 }
280286}
281287
288+ interface ApiClientConfig {
289+ contestCache : CacheConfig ;
290+ taskCache : CacheConfig ;
291+ }
292+
282293/**
283294 * AojApiClient is a client for interacting with the Aizu Online Judge (AOJ) API.
284295 * It extends the ContestSiteApiClient and provides methods to fetch contests and tasks
@@ -310,6 +321,30 @@ export class AojApiClient extends ContestSiteApiClient {
310321 */
311322 private readonly taskCache = new Cache < TasksForImport > ( ) ;
312323
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+
313348 /**
314349 * Disposes of the resources used by the client.
315350 * Clears the contest and task caches to free up memory.
@@ -331,10 +366,10 @@ export class AojApiClient extends ContestSiteApiClient {
331366 try {
332367 const results = await Promise . allSettled ( [
333368 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' ) ,
338373 ] ) ;
339374
340375 const [ courses , pckPrelims , pckFinals , jagPrelims , jagRegionals ] = results . map ( ( result ) => {
@@ -492,7 +527,7 @@ export class AojApiClient extends ContestSiteApiClient {
492527 const validateSegment = ( segment : string ) : boolean => {
493528 return (
494529 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 ) &&
496531 ! segment . includes ( '..' )
497532 ) ;
498533 } ;
@@ -550,10 +585,10 @@ export class AojApiClient extends ContestSiteApiClient {
550585 try {
551586 const results = await Promise . allSettled ( [
552587 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' ) ,
557592 ] ) ;
558593
559594 const [ courses , pckPrelims , pckFinals , jagPrelims , jagRegionals ] = results . map ( ( result ) => {
0 commit comments