algokit-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -0
- package/dist/api/boj-scraper.d.ts +68 -0
- package/dist/api/boj-scraper.d.ts.map +1 -0
- package/dist/api/boj-scraper.js +197 -0
- package/dist/api/boj-scraper.js.map +1 -0
- package/dist/api/programmers-scraper.d.ts +70 -0
- package/dist/api/programmers-scraper.d.ts.map +1 -0
- package/dist/api/programmers-scraper.js +303 -0
- package/dist/api/programmers-scraper.js.map +1 -0
- package/dist/api/solvedac-client.d.ts +67 -0
- package/dist/api/solvedac-client.d.ts.map +1 -0
- package/dist/api/solvedac-client.js +220 -0
- package/dist/api/solvedac-client.js.map +1 -0
- package/dist/api/types.d.ts +125 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +110 -0
- package/dist/api/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +594 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/hint-guide.d.ts +73 -0
- package/dist/prompts/hint-guide.d.ts.map +1 -0
- package/dist/prompts/hint-guide.js +174 -0
- package/dist/prompts/hint-guide.js.map +1 -0
- package/dist/prompts/programmers-hint-guide.d.ts +38 -0
- package/dist/prompts/programmers-hint-guide.d.ts.map +1 -0
- package/dist/prompts/programmers-hint-guide.js +134 -0
- package/dist/prompts/programmers-hint-guide.js.map +1 -0
- package/dist/services/code-analyzer.d.ts +46 -0
- package/dist/services/code-analyzer.d.ts.map +1 -0
- package/dist/services/code-analyzer.js +177 -0
- package/dist/services/code-analyzer.js.map +1 -0
- package/dist/services/hint-generator.d.ts +60 -0
- package/dist/services/hint-generator.d.ts.map +1 -0
- package/dist/services/hint-generator.js +222 -0
- package/dist/services/hint-generator.js.map +1 -0
- package/dist/services/problem-analyzer.d.ts +38 -0
- package/dist/services/problem-analyzer.d.ts.map +1 -0
- package/dist/services/problem-analyzer.js +114 -0
- package/dist/services/problem-analyzer.js.map +1 -0
- package/dist/services/programmers-problem-analyzer.d.ts +26 -0
- package/dist/services/programmers-problem-analyzer.d.ts.map +1 -0
- package/dist/services/programmers-problem-analyzer.js +64 -0
- package/dist/services/programmers-problem-analyzer.js.map +1 -0
- package/dist/services/programmers-review-template-generator.d.ts +27 -0
- package/dist/services/programmers-review-template-generator.d.ts.map +1 -0
- package/dist/services/programmers-review-template-generator.js +153 -0
- package/dist/services/programmers-review-template-generator.js.map +1 -0
- package/dist/services/review-generator.d.ts +51 -0
- package/dist/services/review-generator.d.ts.map +1 -0
- package/dist/services/review-generator.js +149 -0
- package/dist/services/review-generator.js.map +1 -0
- package/dist/services/review-template-generator.d.ts +27 -0
- package/dist/services/review-template-generator.d.ts.map +1 -0
- package/dist/services/review-template-generator.js +163 -0
- package/dist/services/review-template-generator.js.map +1 -0
- package/dist/templates/review-guideline.md +89 -0
- package/dist/tools/analyze-code-submission-boj.d.ts +67 -0
- package/dist/tools/analyze-code-submission-boj.d.ts.map +1 -0
- package/dist/tools/analyze-code-submission-boj.js +89 -0
- package/dist/tools/analyze-code-submission-boj.js.map +1 -0
- package/dist/tools/analyze-code-submission-programmers.d.ts +60 -0
- package/dist/tools/analyze-code-submission-programmers.d.ts.map +1 -0
- package/dist/tools/analyze-code-submission-programmers.js +83 -0
- package/dist/tools/analyze-code-submission-programmers.js.map +1 -0
- package/dist/tools/analyze-code-submission.d.ts +67 -0
- package/dist/tools/analyze-code-submission.d.ts.map +1 -0
- package/dist/tools/analyze-code-submission.js +89 -0
- package/dist/tools/analyze-code-submission.js.map +1 -0
- package/dist/tools/analyze-problem-boj.d.ts +48 -0
- package/dist/tools/analyze-problem-boj.d.ts.map +1 -0
- package/dist/tools/analyze-problem-boj.js +52 -0
- package/dist/tools/analyze-problem-boj.js.map +1 -0
- package/dist/tools/analyze-problem-programmers.d.ts +48 -0
- package/dist/tools/analyze-problem-programmers.d.ts.map +1 -0
- package/dist/tools/analyze-problem-programmers.js +53 -0
- package/dist/tools/analyze-problem-programmers.js.map +1 -0
- package/dist/tools/analyze-problem.d.ts +48 -0
- package/dist/tools/analyze-problem.d.ts.map +1 -0
- package/dist/tools/analyze-problem.js +52 -0
- package/dist/tools/analyze-problem.js.map +1 -0
- package/dist/tools/create-review.d.ts +47 -0
- package/dist/tools/create-review.d.ts.map +1 -0
- package/dist/tools/create-review.js +122 -0
- package/dist/tools/create-review.js.map +1 -0
- package/dist/tools/fetch-problem-content-boj.d.ts +49 -0
- package/dist/tools/fetch-problem-content-boj.d.ts.map +1 -0
- package/dist/tools/fetch-problem-content-boj.js +93 -0
- package/dist/tools/fetch-problem-content-boj.js.map +1 -0
- package/dist/tools/fetch-problem-content-programmers.d.ts +46 -0
- package/dist/tools/fetch-problem-content-programmers.d.ts.map +1 -0
- package/dist/tools/fetch-problem-content-programmers.js +74 -0
- package/dist/tools/fetch-problem-content-programmers.js.map +1 -0
- package/dist/tools/fetch-problem-content.d.ts +49 -0
- package/dist/tools/fetch-problem-content.d.ts.map +1 -0
- package/dist/tools/fetch-problem-content.js +93 -0
- package/dist/tools/fetch-problem-content.js.map +1 -0
- package/dist/tools/generate-hint-boj.d.ts +42 -0
- package/dist/tools/generate-hint-boj.d.ts.map +1 -0
- package/dist/tools/generate-hint-boj.js +80 -0
- package/dist/tools/generate-hint-boj.js.map +1 -0
- package/dist/tools/generate-hint-programmers.d.ts +42 -0
- package/dist/tools/generate-hint-programmers.d.ts.map +1 -0
- package/dist/tools/generate-hint-programmers.js +78 -0
- package/dist/tools/generate-hint-programmers.js.map +1 -0
- package/dist/tools/generate-hint.d.ts +42 -0
- package/dist/tools/generate-hint.d.ts.map +1 -0
- package/dist/tools/generate-hint.js +80 -0
- package/dist/tools/generate-hint.js.map +1 -0
- package/dist/tools/generate-review-template-boj.d.ts +48 -0
- package/dist/tools/generate-review-template-boj.d.ts.map +1 -0
- package/dist/tools/generate-review-template-boj.js +52 -0
- package/dist/tools/generate-review-template-boj.js.map +1 -0
- package/dist/tools/generate-review-template-programmers.d.ts +48 -0
- package/dist/tools/generate-review-template-programmers.d.ts.map +1 -0
- package/dist/tools/generate-review-template-programmers.js +53 -0
- package/dist/tools/generate-review-template-programmers.js.map +1 -0
- package/dist/tools/generate-review-template.d.ts +48 -0
- package/dist/tools/generate-review-template.d.ts.map +1 -0
- package/dist/tools/generate-review-template.js +52 -0
- package/dist/tools/generate-review-template.js.map +1 -0
- package/dist/tools/get-hint.d.ts +41 -0
- package/dist/tools/get-hint.d.ts.map +1 -0
- package/dist/tools/get-hint.js +108 -0
- package/dist/tools/get-hint.js.map +1 -0
- package/dist/tools/get-problem.d.ts +22 -0
- package/dist/tools/get-problem.d.ts.map +1 -0
- package/dist/tools/get-problem.js +89 -0
- package/dist/tools/get-problem.js.map +1 -0
- package/dist/tools/get-programmers-problem.d.ts +53 -0
- package/dist/tools/get-programmers-problem.d.ts.map +1 -0
- package/dist/tools/get-programmers-problem.js +161 -0
- package/dist/tools/get-programmers-problem.js.map +1 -0
- package/dist/tools/search-problems.d.ts +42 -0
- package/dist/tools/search-problems.d.ts.map +1 -0
- package/dist/tools/search-problems.js +123 -0
- package/dist/tools/search-problems.js.map +1 -0
- package/dist/tools/search-programmers-problems.d.ts +73 -0
- package/dist/tools/search-programmers-problems.d.ts.map +1 -0
- package/dist/tools/search-programmers-problems.js +171 -0
- package/dist/tools/search-programmers-problems.js.map +1 -0
- package/dist/tools/search-tags.d.ts +22 -0
- package/dist/tools/search-tags.d.ts.map +1 -0
- package/dist/tools/search-tags.js +70 -0
- package/dist/tools/search-tags.js.map +1 -0
- package/dist/types/analysis.d.ts +211 -0
- package/dist/types/analysis.d.ts.map +1 -0
- package/dist/types/analysis.js +11 -0
- package/dist/types/analysis.js.map +1 -0
- package/dist/types/problem-content.d.ts +110 -0
- package/dist/types/problem-content.d.ts.map +1 -0
- package/dist/types/problem-content.js +7 -0
- package/dist/types/problem-content.js.map +1 -0
- package/dist/types/programmers.d.ts +72 -0
- package/dist/types/programmers.d.ts.map +1 -0
- package/dist/types/programmers.js +5 -0
- package/dist/types/programmers.js.map +1 -0
- package/dist/utils/browser-pool.d.ts +93 -0
- package/dist/utils/browser-pool.d.ts.map +1 -0
- package/dist/utils/browser-pool.js +193 -0
- package/dist/utils/browser-pool.js.map +1 -0
- package/dist/utils/cache-stats.d.ts +60 -0
- package/dist/utils/cache-stats.d.ts.map +1 -0
- package/dist/utils/cache-stats.js +78 -0
- package/dist/utils/cache-stats.js.map +1 -0
- package/dist/utils/cache.d.ts +72 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +99 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/html-parser.d.ts +57 -0
- package/dist/utils/html-parser.d.ts.map +1 -0
- package/dist/utils/html-parser.js +383 -0
- package/dist/utils/html-parser.js.map +1 -0
- package/dist/utils/lru-cache.d.ts +124 -0
- package/dist/utils/lru-cache.d.ts.map +1 -0
- package/dist/utils/lru-cache.js +259 -0
- package/dist/utils/lru-cache.js.map +1 -0
- package/dist/utils/programmers-converter.d.ts +15 -0
- package/dist/utils/programmers-converter.d.ts.map +1 -0
- package/dist/utils/programmers-converter.js +41 -0
- package/dist/utils/programmers-converter.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +145 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +244 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/tier-converter.d.ts +108 -0
- package/dist/utils/tier-converter.d.ts.map +1 -0
- package/dist/utils/tier-converter.js +198 -0
- package/dist/utils/tier-converter.js.map +1 -0
- package/dist/utils/url-parser.d.ts +25 -0
- package/dist/utils/url-parser.d.ts.map +1 -0
- package/dist/utils/url-parser.js +45 -0
- package/dist/utils/url-parser.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 간단한 인메모리 캐시 구현
|
|
3
|
+
* TTL (Time To Live) 기반 캐싱
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 인메모리 캐시 클래스
|
|
7
|
+
*/
|
|
8
|
+
export class Cache {
|
|
9
|
+
store = new Map();
|
|
10
|
+
defaultTtl;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.defaultTtl = options.ttl || 60 * 60 * 1000; // 기본 1시간
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 캐시에 값 저장
|
|
16
|
+
* @param key - 캐시 키
|
|
17
|
+
* @param value - 저장할 값
|
|
18
|
+
* @param ttl - TTL (밀리초, 선택사항)
|
|
19
|
+
*/
|
|
20
|
+
set(key, value, ttl) {
|
|
21
|
+
const expiresAt = Date.now() + (ttl || this.defaultTtl);
|
|
22
|
+
this.store.set(key, { value, expiresAt });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 캐시에서 값 조회
|
|
26
|
+
* @param key - 캐시 키
|
|
27
|
+
* @returns 캐시된 값 또는 undefined
|
|
28
|
+
*/
|
|
29
|
+
get(key) {
|
|
30
|
+
const entry = this.store.get(key);
|
|
31
|
+
if (!entry) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
// TTL 만료 확인
|
|
35
|
+
if (Date.now() > entry.expiresAt) {
|
|
36
|
+
this.store.delete(key);
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return entry.value;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 캐시에 키가 존재하는지 확인
|
|
43
|
+
* @param key - 캐시 키
|
|
44
|
+
* @returns 존재 여부
|
|
45
|
+
*/
|
|
46
|
+
has(key) {
|
|
47
|
+
return this.get(key) !== undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 캐시에서 키 삭제
|
|
51
|
+
* @param key - 캐시 키
|
|
52
|
+
* @returns 삭제 성공 여부
|
|
53
|
+
*/
|
|
54
|
+
delete(key) {
|
|
55
|
+
return this.store.delete(key);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 캐시 전체 삭제
|
|
59
|
+
*/
|
|
60
|
+
clear() {
|
|
61
|
+
this.store.clear();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 만료된 항목 정리
|
|
65
|
+
*/
|
|
66
|
+
cleanup() {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
for (const [key, entry] of this.store.entries()) {
|
|
69
|
+
if (now > entry.expiresAt) {
|
|
70
|
+
this.store.delete(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 캐시 크기 반환
|
|
76
|
+
*/
|
|
77
|
+
size() {
|
|
78
|
+
return this.store.size;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 캐시 히트율 측정용 통계 (간단한 구현)
|
|
82
|
+
*/
|
|
83
|
+
getStats() {
|
|
84
|
+
return {
|
|
85
|
+
size: this.store.size,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 문제 상세 정보 캐시 (싱글톤)
|
|
91
|
+
* TTL: 1시간 (문제 메타데이터는 자주 변경되지 않음)
|
|
92
|
+
*/
|
|
93
|
+
export const problemCache = new Cache({ ttl: 60 * 60 * 1000 });
|
|
94
|
+
/**
|
|
95
|
+
* 검색 결과 캐시 (싱글톤)
|
|
96
|
+
* TTL: 10분 (검색 결과는 상대적으로 자주 변경될 수 있음)
|
|
97
|
+
*/
|
|
98
|
+
export const searchCache = new Cache({ ttl: 10 * 60 * 1000 });
|
|
99
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;GAEG;AACH,MAAM,OAAO,KAAK;IACR,KAAK,GAA+B,IAAI,GAAG,EAAE,CAAC;IAC9C,UAAU,CAAS;IAE3B,YAAY,UAAwB,EAAE;QACpC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;IAC5D,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,GAAY;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,YAAY;QACZ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;SACtB,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAE/D;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOJ HTML 파싱 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* Phase 6 - P6-002: 문제 본문 스크래퍼 구현
|
|
5
|
+
*
|
|
6
|
+
* cheerio를 사용하여 BOJ 문제 페이지를 파싱합니다.
|
|
7
|
+
*/
|
|
8
|
+
import type { ProblemContent } from '../types/problem-content.js';
|
|
9
|
+
/**
|
|
10
|
+
* HTML 파싱 에러
|
|
11
|
+
*/
|
|
12
|
+
export declare class HtmlParseError extends Error {
|
|
13
|
+
field: string;
|
|
14
|
+
constructor(message: string, field: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* BOJ 문제 페이지 HTML을 파싱하여 구조화된 데이터로 변환
|
|
18
|
+
*
|
|
19
|
+
* @param html - BOJ 문제 페이지의 HTML 문자열
|
|
20
|
+
* @param problemId - 문제 번호
|
|
21
|
+
* @returns 파싱된 문제 콘텐츠
|
|
22
|
+
* @throws {HtmlParseError} 필수 필드 파싱 실패 시
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const html = await scraper.fetchProblemPage(1000);
|
|
27
|
+
* const content = parseProblemContent(html, 1000);
|
|
28
|
+
* console.log(content.title); // "A+B"
|
|
29
|
+
* console.log(content.examples[0].input); // "1 2"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseProblemContent(html: string, problemId: number): ProblemContent;
|
|
33
|
+
/**
|
|
34
|
+
* HTML 엔티티 디코딩 (cheerio가 대부분 처리하지만 보조 함수로 제공)
|
|
35
|
+
*
|
|
36
|
+
* @param text - HTML 엔티티가 포함된 텍스트
|
|
37
|
+
* @returns 디코딩된 텍스트
|
|
38
|
+
*/
|
|
39
|
+
export declare function decodeHtmlEntities(text: string): string;
|
|
40
|
+
import type { ProgrammersProblemDetail } from '../types/programmers.js';
|
|
41
|
+
/**
|
|
42
|
+
* 프로그래머스 문제 페이지 HTML을 파싱하여 구조화된 데이터로 변환
|
|
43
|
+
*
|
|
44
|
+
* @param html - 프로그래머스 문제 페이지의 HTML 문자열
|
|
45
|
+
* @param problemId - 문제 ID
|
|
46
|
+
* @returns 파싱된 문제 콘텐츠
|
|
47
|
+
* @throws {HtmlParseError} 필수 필드 파싱 실패 시
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const html = await scraper.fetchProblemPage('389632');
|
|
52
|
+
* const content = parseProgrammersProblemContent(html, '389632');
|
|
53
|
+
* console.log(content.title); // "문자열과 알파벳과 쿼리"
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseProgrammersProblemContent(html: string, problemId: string): ProgrammersProblemDetail;
|
|
57
|
+
//# sourceMappingURL=html-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-parser.d.ts","sourceRoot":"","sources":["../../src/utils/html-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAiC,MAAM,6BAA6B,CAAC;AAwBjG;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACH,KAAK,EAAE,MAAM;gBAArC,OAAO,EAAE,MAAM,EAAS,KAAK,EAAE,MAAM;CAIlD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,CAuCnF;AA8GD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWvD;AAMD,OAAO,KAAK,EACV,wBAAwB,EAEzB,MAAM,yBAAyB,CAAC;AAoBjC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAkC1B"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BOJ HTML 파싱 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* Phase 6 - P6-002: 문제 본문 스크래퍼 구현
|
|
5
|
+
*
|
|
6
|
+
* cheerio를 사용하여 BOJ 문제 페이지를 파싱합니다.
|
|
7
|
+
*/
|
|
8
|
+
import * as cheerio from 'cheerio';
|
|
9
|
+
/**
|
|
10
|
+
* BOJ 페이지 CSS Selector 정의
|
|
11
|
+
*/
|
|
12
|
+
const BOJ_SELECTORS = {
|
|
13
|
+
/** 문제 제목 */
|
|
14
|
+
title: '#problem_title',
|
|
15
|
+
/** 문제 설명 */
|
|
16
|
+
description: '#problem_description',
|
|
17
|
+
/** 입력 형식 */
|
|
18
|
+
input: '#problem_input',
|
|
19
|
+
/** 출력 형식 */
|
|
20
|
+
output: '#problem_output',
|
|
21
|
+
/** 예제 입력 (n은 1부터 시작) */
|
|
22
|
+
sampleInput: (n) => `#sample-input-${n}`,
|
|
23
|
+
/** 예제 출력 (n은 1부터 시작) */
|
|
24
|
+
sampleOutput: (n) => `#sample-output-${n}`,
|
|
25
|
+
/** 시간 제한 */
|
|
26
|
+
timeLimit: '#problem-info tbody tr td:nth-child(1)',
|
|
27
|
+
/** 메모리 제한 */
|
|
28
|
+
memoryLimit: '#problem-info tbody tr td:nth-child(2)',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* HTML 파싱 에러
|
|
32
|
+
*/
|
|
33
|
+
export class HtmlParseError extends Error {
|
|
34
|
+
field;
|
|
35
|
+
constructor(message, field) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.field = field;
|
|
38
|
+
this.name = 'HtmlParseError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* BOJ 문제 페이지 HTML을 파싱하여 구조화된 데이터로 변환
|
|
43
|
+
*
|
|
44
|
+
* @param html - BOJ 문제 페이지의 HTML 문자열
|
|
45
|
+
* @param problemId - 문제 번호
|
|
46
|
+
* @returns 파싱된 문제 콘텐츠
|
|
47
|
+
* @throws {HtmlParseError} 필수 필드 파싱 실패 시
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const html = await scraper.fetchProblemPage(1000);
|
|
52
|
+
* const content = parseProblemContent(html, 1000);
|
|
53
|
+
* console.log(content.title); // "A+B"
|
|
54
|
+
* console.log(content.examples[0].input); // "1 2"
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function parseProblemContent(html, problemId) {
|
|
58
|
+
const $ = cheerio.load(html);
|
|
59
|
+
// 1. 제목 추출
|
|
60
|
+
const title = parseTitle($);
|
|
61
|
+
// 2. 설명 추출
|
|
62
|
+
const description = parseDescription($);
|
|
63
|
+
// 3. 입력 형식 추출
|
|
64
|
+
const inputFormat = parseInputFormat($);
|
|
65
|
+
// 4. 출력 형식 추출
|
|
66
|
+
const outputFormat = parseOutputFormat($);
|
|
67
|
+
// 5. 예제 추출
|
|
68
|
+
const examples = parseExamples($);
|
|
69
|
+
// 6. 제한사항 추출
|
|
70
|
+
const limits = parseLimits($);
|
|
71
|
+
// 현재 시각 및 만료 시각 계산
|
|
72
|
+
const now = new Date();
|
|
73
|
+
const expiresAt = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30일 후
|
|
74
|
+
return {
|
|
75
|
+
problemId,
|
|
76
|
+
title,
|
|
77
|
+
description,
|
|
78
|
+
inputFormat,
|
|
79
|
+
outputFormat,
|
|
80
|
+
examples,
|
|
81
|
+
limits,
|
|
82
|
+
metadata: {
|
|
83
|
+
fetchedAt: now.toISOString(),
|
|
84
|
+
source: 'web',
|
|
85
|
+
cacheExpiresAt: expiresAt.toISOString(),
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 제목 파싱
|
|
91
|
+
*/
|
|
92
|
+
function parseTitle($) {
|
|
93
|
+
const title = $(BOJ_SELECTORS.title).text().trim();
|
|
94
|
+
if (!title) {
|
|
95
|
+
throw new HtmlParseError('문제 제목을 찾을 수 없습니다.', 'title');
|
|
96
|
+
}
|
|
97
|
+
return title;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 설명 파싱
|
|
101
|
+
*/
|
|
102
|
+
function parseDescription($) {
|
|
103
|
+
const description = $(BOJ_SELECTORS.description).text().trim();
|
|
104
|
+
if (!description) {
|
|
105
|
+
throw new HtmlParseError('문제 설명을 찾을 수 없습니다.', 'description');
|
|
106
|
+
}
|
|
107
|
+
// 여러 줄 공백을 단일 공백으로 치환
|
|
108
|
+
return description.replace(/\s+/g, ' ').trim();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 입력 형식 파싱
|
|
112
|
+
*/
|
|
113
|
+
function parseInputFormat($) {
|
|
114
|
+
const input = $(BOJ_SELECTORS.input).text().trim();
|
|
115
|
+
if (!input) {
|
|
116
|
+
throw new HtmlParseError('입력 형식을 찾을 수 없습니다.', 'inputFormat');
|
|
117
|
+
}
|
|
118
|
+
return input.replace(/\s+/g, ' ').trim();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 출력 형식 파싱
|
|
122
|
+
*/
|
|
123
|
+
function parseOutputFormat($) {
|
|
124
|
+
const output = $(BOJ_SELECTORS.output).text().trim();
|
|
125
|
+
if (!output) {
|
|
126
|
+
throw new HtmlParseError('출력 형식을 찾을 수 없습니다.', 'outputFormat');
|
|
127
|
+
}
|
|
128
|
+
return output.replace(/\s+/g, ' ').trim();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 예제 입출력 파싱
|
|
132
|
+
*/
|
|
133
|
+
function parseExamples($) {
|
|
134
|
+
const examples = [];
|
|
135
|
+
// 최대 10개의 예제 확인
|
|
136
|
+
for (let i = 1; i <= 10; i++) {
|
|
137
|
+
const inputSelector = BOJ_SELECTORS.sampleInput(i);
|
|
138
|
+
const outputSelector = BOJ_SELECTORS.sampleOutput(i);
|
|
139
|
+
const input = $(inputSelector).text();
|
|
140
|
+
const output = $(outputSelector).text();
|
|
141
|
+
// 더 이상 예제가 없으면 종료
|
|
142
|
+
if (!input && !output) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
// 입력 또는 출력 중 하나만 있으면 경고 (일부 문제는 이럴 수 있음)
|
|
146
|
+
if (!input || !output) {
|
|
147
|
+
console.warn(`예제 ${i}번의 입력 또는 출력이 누락되었습니다.`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
examples.push({
|
|
151
|
+
input: input.trim(),
|
|
152
|
+
output: output.trim(),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (examples.length === 0) {
|
|
156
|
+
throw new HtmlParseError('예제를 찾을 수 없습니다.', 'examples');
|
|
157
|
+
}
|
|
158
|
+
return examples;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 시간/메모리 제한 파싱
|
|
162
|
+
*/
|
|
163
|
+
function parseLimits($) {
|
|
164
|
+
const timeLimit = $(BOJ_SELECTORS.timeLimit).text().trim();
|
|
165
|
+
const memoryLimit = $(BOJ_SELECTORS.memoryLimit).text().trim();
|
|
166
|
+
if (!timeLimit || !memoryLimit) {
|
|
167
|
+
throw new HtmlParseError('시간 또는 메모리 제한을 찾을 수 없습니다.', 'limits');
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
timeLimit,
|
|
171
|
+
memoryLimit,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* HTML 엔티티 디코딩 (cheerio가 대부분 처리하지만 보조 함수로 제공)
|
|
176
|
+
*
|
|
177
|
+
* @param text - HTML 엔티티가 포함된 텍스트
|
|
178
|
+
* @returns 디코딩된 텍스트
|
|
179
|
+
*/
|
|
180
|
+
export function decodeHtmlEntities(text) {
|
|
181
|
+
const entities = {
|
|
182
|
+
'<': '<',
|
|
183
|
+
'>': '>',
|
|
184
|
+
' ': ' ',
|
|
185
|
+
'&': '&',
|
|
186
|
+
'"': '"',
|
|
187
|
+
''': "'",
|
|
188
|
+
};
|
|
189
|
+
return text.replace(/&[^;]+;/g, (match) => entities[match] || match);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 프로그래머스 페이지 CSS Selector 정의
|
|
193
|
+
*/
|
|
194
|
+
const PROGRAMMERS_SELECTORS = {
|
|
195
|
+
/** 제목 */
|
|
196
|
+
title: '.challenge-title',
|
|
197
|
+
/** 카테고리 (breadcrumb - 두 번째 li의 a 태그) */
|
|
198
|
+
categoryBreadcrumb: '.breadcrumb li:nth-child(2) a',
|
|
199
|
+
/** 레벨 (data attribute) */
|
|
200
|
+
levelDataAttr: '.lesson-content',
|
|
201
|
+
/** 문제 설명 */
|
|
202
|
+
description: '.guide-section-description',
|
|
203
|
+
/** 제한사항 섹션 */
|
|
204
|
+
constraintsHeading: 'h5:contains("제한사항")',
|
|
205
|
+
/** 입출력 예제 테이블 */
|
|
206
|
+
examplesTable: 'table',
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* 프로그래머스 문제 페이지 HTML을 파싱하여 구조화된 데이터로 변환
|
|
210
|
+
*
|
|
211
|
+
* @param html - 프로그래머스 문제 페이지의 HTML 문자열
|
|
212
|
+
* @param problemId - 문제 ID
|
|
213
|
+
* @returns 파싱된 문제 콘텐츠
|
|
214
|
+
* @throws {HtmlParseError} 필수 필드 파싱 실패 시
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const html = await scraper.fetchProblemPage('389632');
|
|
219
|
+
* const content = parseProgrammersProblemContent(html, '389632');
|
|
220
|
+
* console.log(content.title); // "문자열과 알파벳과 쿼리"
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export function parseProgrammersProblemContent(html, problemId) {
|
|
224
|
+
const $ = cheerio.load(html);
|
|
225
|
+
// 1. 제목 추출
|
|
226
|
+
const title = parseProgrammersTitle($);
|
|
227
|
+
// 2. 레벨 추출 (data attribute)
|
|
228
|
+
const level = parseProgrammersLevel($);
|
|
229
|
+
// 3. 카테고리 추출
|
|
230
|
+
const category = parseProgrammersCategory($);
|
|
231
|
+
// 4. 문제 설명 추출
|
|
232
|
+
const description = parseProgrammersDescription($);
|
|
233
|
+
// 5. 제한사항 추출
|
|
234
|
+
const constraints = parseProgrammersConstraints($);
|
|
235
|
+
// 6. 입출력 예제 추출
|
|
236
|
+
const examples = parseProgrammersExamples($);
|
|
237
|
+
// 7. 태그 추출 (프로그래머스는 태그 정보 없음, 빈 배열)
|
|
238
|
+
const tags = [];
|
|
239
|
+
return {
|
|
240
|
+
problemId,
|
|
241
|
+
title,
|
|
242
|
+
level,
|
|
243
|
+
category,
|
|
244
|
+
description,
|
|
245
|
+
constraints,
|
|
246
|
+
examples,
|
|
247
|
+
tags,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 프로그래머스 제목 파싱
|
|
252
|
+
*/
|
|
253
|
+
function parseProgrammersTitle($) {
|
|
254
|
+
const title = $(PROGRAMMERS_SELECTORS.title).text().trim();
|
|
255
|
+
if (!title) {
|
|
256
|
+
throw new HtmlParseError('문제 제목을 찾을 수 없습니다.', 'title');
|
|
257
|
+
}
|
|
258
|
+
return title;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 프로그래머스 레벨 파싱 (data attribute)
|
|
262
|
+
*/
|
|
263
|
+
function parseProgrammersLevel($) {
|
|
264
|
+
const levelAttr = $(PROGRAMMERS_SELECTORS.levelDataAttr).attr('data-challenge-level');
|
|
265
|
+
if (!levelAttr) {
|
|
266
|
+
// 레벨 정보 없으면 0 (일부 문제는 레벨 없음)
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
const level = parseInt(levelAttr, 10);
|
|
270
|
+
if (isNaN(level) || level < 0 || level > 5) {
|
|
271
|
+
throw new HtmlParseError(`유효하지 않은 레벨: ${levelAttr}`, 'level');
|
|
272
|
+
}
|
|
273
|
+
return level;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* 프로그래머스 카테고리 파싱
|
|
277
|
+
*/
|
|
278
|
+
function parseProgrammersCategory($) {
|
|
279
|
+
const category = $(PROGRAMMERS_SELECTORS.categoryBreadcrumb).text().trim();
|
|
280
|
+
if (!category) {
|
|
281
|
+
// 카테고리 없는 경우 기본값
|
|
282
|
+
return '기타';
|
|
283
|
+
}
|
|
284
|
+
return category;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* 프로그래머스 설명 파싱
|
|
288
|
+
*/
|
|
289
|
+
function parseProgrammersDescription($) {
|
|
290
|
+
const descEl = $(PROGRAMMERS_SELECTORS.description).first();
|
|
291
|
+
if (!descEl.length) {
|
|
292
|
+
throw new HtmlParseError('문제 설명을 찾을 수 없습니다.', 'description');
|
|
293
|
+
}
|
|
294
|
+
// HTML 그대로 반환 (마크다운 형식 포함)
|
|
295
|
+
const html = descEl.html();
|
|
296
|
+
if (!html) {
|
|
297
|
+
throw new HtmlParseError('문제 설명이 비어있습니다.', 'description');
|
|
298
|
+
}
|
|
299
|
+
return html.trim();
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* 프로그래머스 제한사항 파싱
|
|
303
|
+
*/
|
|
304
|
+
function parseProgrammersConstraints($) {
|
|
305
|
+
const constraints = [];
|
|
306
|
+
// "제한사항" 섹션 찾기
|
|
307
|
+
const heading = $(PROGRAMMERS_SELECTORS.constraintsHeading);
|
|
308
|
+
if (!heading.length) {
|
|
309
|
+
// 제한사항 없는 문제도 있음 (빈 배열 반환)
|
|
310
|
+
return constraints;
|
|
311
|
+
}
|
|
312
|
+
// 다음 요소 (ul 또는 div) 가져오기
|
|
313
|
+
let next = heading.next();
|
|
314
|
+
while (next.length > 0) {
|
|
315
|
+
const tagName = next.prop('tagName');
|
|
316
|
+
if (!tagName)
|
|
317
|
+
break;
|
|
318
|
+
// ul이면 li 항목들 추출
|
|
319
|
+
if (tagName.toLowerCase() === 'ul') {
|
|
320
|
+
next.find('li').each((_, li) => {
|
|
321
|
+
const text = $(li).text().trim();
|
|
322
|
+
if (text) {
|
|
323
|
+
constraints.push(text);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
// div면 텍스트 추출
|
|
329
|
+
if (tagName.toLowerCase() === 'div') {
|
|
330
|
+
const text = next.text().trim();
|
|
331
|
+
if (text) {
|
|
332
|
+
constraints.push(text);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// 다음 h6 (새 섹션) 만나면 종료
|
|
336
|
+
if (tagName.toLowerCase() === 'h6') {
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
next = next.next();
|
|
340
|
+
}
|
|
341
|
+
return constraints;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 프로그래머스 입출력 예제 파싱
|
|
345
|
+
*/
|
|
346
|
+
function parseProgrammersExamples($) {
|
|
347
|
+
const examples = [];
|
|
348
|
+
// 입출력 예제 테이블 찾기
|
|
349
|
+
$(PROGRAMMERS_SELECTORS.examplesTable).each((_, table) => {
|
|
350
|
+
const headers = $(table)
|
|
351
|
+
.find('thead th')
|
|
352
|
+
.map((_, th) => $(th).text().trim().toLowerCase())
|
|
353
|
+
.get();
|
|
354
|
+
// "입출력 예" 테이블인지 확인
|
|
355
|
+
if (headers.length < 2)
|
|
356
|
+
return;
|
|
357
|
+
// tbody tr 순회
|
|
358
|
+
$(table)
|
|
359
|
+
.find('tbody tr')
|
|
360
|
+
.each((_, row) => {
|
|
361
|
+
const cells = $(row).find('td');
|
|
362
|
+
if (cells.length < 2)
|
|
363
|
+
return;
|
|
364
|
+
const input = $(cells[0]).text().trim();
|
|
365
|
+
const output = $(cells[1]).text().trim();
|
|
366
|
+
// 입력 또는 출력이 비어있으면 스킵
|
|
367
|
+
if (!input || !output)
|
|
368
|
+
return;
|
|
369
|
+
examples.push({
|
|
370
|
+
input,
|
|
371
|
+
output,
|
|
372
|
+
// 3번째 열이 있으면 설명
|
|
373
|
+
explanation: cells.length >= 3 ? $(cells[2]).text().trim() : undefined,
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
if (examples.length === 0) {
|
|
378
|
+
// 예제가 없는 문제도 있음 (경고만 출력)
|
|
379
|
+
console.warn('입출력 예제를 찾을 수 없습니다.');
|
|
380
|
+
}
|
|
381
|
+
return examples;
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=html-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-parser.js","sourceRoot":"","sources":["../../src/utils/html-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LRU (Least Recently Used) 캐시 구현
|
|
3
|
+
*
|
|
4
|
+
* 특징:
|
|
5
|
+
* - Doubly Linked List + Map으로 O(1) 조회/삽입/제거
|
|
6
|
+
* - TTL 기반 만료 처리
|
|
7
|
+
* - 캐시 통계 수집 (hits, misses, evictions)
|
|
8
|
+
* - 제네릭 타입 지원
|
|
9
|
+
*
|
|
10
|
+
* 알고리즘:
|
|
11
|
+
* - Head: 가장 최근 사용된 항목
|
|
12
|
+
* - Tail: 가장 오래된 항목 (제거 대상)
|
|
13
|
+
* - get() 또는 set() 시 항목을 Head로 이동
|
|
14
|
+
* - 용량 초과 시 Tail 제거 (LRU eviction)
|
|
15
|
+
*/
|
|
16
|
+
export interface CacheNode<K, V> {
|
|
17
|
+
key: K;
|
|
18
|
+
value: V;
|
|
19
|
+
expiresAt: number;
|
|
20
|
+
prev: CacheNode<K, V> | null;
|
|
21
|
+
next: CacheNode<K, V> | null;
|
|
22
|
+
}
|
|
23
|
+
export interface CacheStats {
|
|
24
|
+
hits: number;
|
|
25
|
+
misses: number;
|
|
26
|
+
evictions: number;
|
|
27
|
+
hitRate: number;
|
|
28
|
+
size: number;
|
|
29
|
+
capacity: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* LRUCache 클래스
|
|
33
|
+
*
|
|
34
|
+
* @template K - 키 타입
|
|
35
|
+
* @template V - 값 타입
|
|
36
|
+
*/
|
|
37
|
+
export declare class LRUCache<K, V> {
|
|
38
|
+
private cache;
|
|
39
|
+
private head;
|
|
40
|
+
private tail;
|
|
41
|
+
private capacity;
|
|
42
|
+
private defaultTtl;
|
|
43
|
+
private stats;
|
|
44
|
+
/**
|
|
45
|
+
* LRUCache 생성자
|
|
46
|
+
*
|
|
47
|
+
* @param capacity - 최대 항목 수 (기본 100)
|
|
48
|
+
* @param defaultTtl - 기본 TTL (밀리초, 기본 1시간)
|
|
49
|
+
* @throws {Error} capacity가 0 이하인 경우
|
|
50
|
+
*/
|
|
51
|
+
constructor(capacity?: number, defaultTtl?: number);
|
|
52
|
+
/**
|
|
53
|
+
* 값 저장 또는 업데이트
|
|
54
|
+
*
|
|
55
|
+
* @param key - 저장할 키
|
|
56
|
+
* @param value - 저장할 값
|
|
57
|
+
* @param ttl - TTL (밀리초, 선택사항)
|
|
58
|
+
*/
|
|
59
|
+
set(key: K, value: V, ttl?: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* 값 조회
|
|
62
|
+
*
|
|
63
|
+
* @param key - 조회할 키
|
|
64
|
+
* @returns 캐시된 값 또는 undefined (미스 또는 만료)
|
|
65
|
+
*/
|
|
66
|
+
get(key: K): V | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* 키 존재 여부 확인
|
|
69
|
+
*
|
|
70
|
+
* @param key - 확인할 키
|
|
71
|
+
* @returns 키가 존재하고 만료되지 않았으면 true
|
|
72
|
+
*/
|
|
73
|
+
has(key: K): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* 키 삭제
|
|
76
|
+
*
|
|
77
|
+
* @param key - 삭제할 키
|
|
78
|
+
* @returns 삭제 성공 시 true, 키가 없으면 false
|
|
79
|
+
*/
|
|
80
|
+
delete(key: K): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 캐시 전체 삭제 및 통계 초기화
|
|
83
|
+
*/
|
|
84
|
+
clear(): void;
|
|
85
|
+
/**
|
|
86
|
+
* 캐시 크기 조회
|
|
87
|
+
*
|
|
88
|
+
* @returns 현재 캐시 항목 수
|
|
89
|
+
*/
|
|
90
|
+
size(): number;
|
|
91
|
+
/**
|
|
92
|
+
* 캐시 통계 조회
|
|
93
|
+
*
|
|
94
|
+
* @returns 캐시 통계 (hits, misses, evictions, hitRate)
|
|
95
|
+
*/
|
|
96
|
+
getStats(): CacheStats;
|
|
97
|
+
/**
|
|
98
|
+
* 만료된 항목 일괄 제거
|
|
99
|
+
*/
|
|
100
|
+
cleanup(): void;
|
|
101
|
+
/**
|
|
102
|
+
* 노드를 Head로 이동 (최근 사용됨)
|
|
103
|
+
*
|
|
104
|
+
* @param node - 이동할 노드
|
|
105
|
+
*/
|
|
106
|
+
private moveToHead;
|
|
107
|
+
/**
|
|
108
|
+
* 노드를 Head에 추가
|
|
109
|
+
*
|
|
110
|
+
* @param node - 추가할 노드
|
|
111
|
+
*/
|
|
112
|
+
private addToHead;
|
|
113
|
+
/**
|
|
114
|
+
* 노드를 Linked List에서 제거
|
|
115
|
+
*
|
|
116
|
+
* @param node - 제거할 노드
|
|
117
|
+
*/
|
|
118
|
+
private removeNode;
|
|
119
|
+
/**
|
|
120
|
+
* LRU 제거 (Tail 제거)
|
|
121
|
+
*/
|
|
122
|
+
private evictLRU;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=lru-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru-cache.d.ts","sourceRoot":"","sources":["../../src/utils/lru-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC;IAC7B,GAAG,EAAE,CAAC,CAAC;IACP,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC;IACxB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;IAG3B,OAAO,CAAC,KAAK,CAIX;IAEF;;;;;;OAMG;gBACS,QAAQ,GAAE,MAAY,EAAE,UAAU,GAAE,MAAgB;IAQhE;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAgCzC;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAsB1B;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAcpB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IASvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAWb;;;;OAIG;IACH,IAAI,IAAI,MAAM;IAId;;;;OAIG;IACH,QAAQ,IAAI,UAAU;IActB;;OAEG;IACH,OAAO,IAAI,IAAI;IAqBf;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAUlB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAejB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAgBlB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAQjB"}
|