aiwf 0.3.21 → 0.3.23
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/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
package/src/lib/cache-system.js
DELETED
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { createHash } from 'crypto';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* GitHub API 캐싱 시스템
|
|
9
|
-
* - API 호출 결과를 메모리와 디스크에 캐싱
|
|
10
|
-
* - TTL 기반 캐시 만료 처리
|
|
11
|
-
* - 중복 요청 방지
|
|
12
|
-
*/
|
|
13
|
-
class GitHubAPICache {
|
|
14
|
-
constructor(cacheDir = '.aiwf/cache', ttl = 300000) { // 5분 TTL
|
|
15
|
-
this.cacheDir = cacheDir;
|
|
16
|
-
this.ttl = ttl;
|
|
17
|
-
this.memoryCache = new Map();
|
|
18
|
-
this.pendingRequests = new Map();
|
|
19
|
-
this.stats = {
|
|
20
|
-
hits: 0,
|
|
21
|
-
misses: 0,
|
|
22
|
-
requests: 0
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async init() {
|
|
27
|
-
try {
|
|
28
|
-
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.warn('Failed to create cache directory:', error.message);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 캐시 키 생성
|
|
35
|
-
generateCacheKey(url, headers = {}) {
|
|
36
|
-
const keyData = JSON.stringify({ url, headers });
|
|
37
|
-
return createHash('sha256').update(keyData).digest('hex');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 메모리 캐시에서 가져오기
|
|
41
|
-
getFromMemory(key) {
|
|
42
|
-
const cached = this.memoryCache.get(key);
|
|
43
|
-
if (cached && Date.now() - cached.timestamp < this.ttl) {
|
|
44
|
-
this.stats.hits++;
|
|
45
|
-
return cached.data;
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 디스크 캐시에서 가져오기
|
|
51
|
-
async getFromDisk(key) {
|
|
52
|
-
try {
|
|
53
|
-
const cachePath = path.join(this.cacheDir, `${key}.json`);
|
|
54
|
-
const cacheData = await fs.readFile(cachePath, 'utf8');
|
|
55
|
-
const cached = JSON.parse(cacheData);
|
|
56
|
-
|
|
57
|
-
if (Date.now() - cached.timestamp < this.ttl) {
|
|
58
|
-
// 메모리 캐시에도 저장
|
|
59
|
-
this.memoryCache.set(key, cached);
|
|
60
|
-
this.stats.hits++;
|
|
61
|
-
return cached.data;
|
|
62
|
-
} else {
|
|
63
|
-
// 만료된 캐시 삭제
|
|
64
|
-
await fs.unlink(cachePath).catch(() => {});
|
|
65
|
-
}
|
|
66
|
-
} catch (error) {
|
|
67
|
-
// 캐시 파일이 없거나 읽기 실패
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// 캐시에 저장
|
|
73
|
-
async saveToCache(key, data) {
|
|
74
|
-
const cacheEntry = {
|
|
75
|
-
data,
|
|
76
|
-
timestamp: Date.now()
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// 메모리 캐시에 저장
|
|
80
|
-
this.memoryCache.set(key, cacheEntry);
|
|
81
|
-
|
|
82
|
-
// 디스크 캐시에 저장
|
|
83
|
-
try {
|
|
84
|
-
const cachePath = path.join(this.cacheDir, `${key}.json`);
|
|
85
|
-
await fs.writeFile(cachePath, JSON.stringify(cacheEntry, null, 2));
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.warn('Failed to save to disk cache:', error.message);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 캐시된 데이터 가져오기
|
|
92
|
-
async get(url, headers = {}) {
|
|
93
|
-
const key = this.generateCacheKey(url, headers);
|
|
94
|
-
|
|
95
|
-
// 메모리 캐시 확인
|
|
96
|
-
let cachedData = this.getFromMemory(key);
|
|
97
|
-
if (cachedData) {
|
|
98
|
-
return cachedData;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// 디스크 캐시 확인
|
|
102
|
-
cachedData = await this.getFromDisk(key);
|
|
103
|
-
if (cachedData) {
|
|
104
|
-
return cachedData;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.stats.misses++;
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 캐시에 저장
|
|
112
|
-
async set(url, data, headers = {}) {
|
|
113
|
-
const key = this.generateCacheKey(url, headers);
|
|
114
|
-
await this.saveToCache(key, data);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 중복 요청 방지
|
|
118
|
-
async getOrFetch(url, fetchFunction, headers = {}) {
|
|
119
|
-
const key = this.generateCacheKey(url, headers);
|
|
120
|
-
this.stats.requests++;
|
|
121
|
-
|
|
122
|
-
// 캐시에서 확인
|
|
123
|
-
const cachedData = await this.get(url, headers);
|
|
124
|
-
if (cachedData) {
|
|
125
|
-
return cachedData;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 이미 진행 중인 요청이 있는지 확인
|
|
129
|
-
if (this.pendingRequests.has(key)) {
|
|
130
|
-
return await this.pendingRequests.get(key);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 새로운 요청 시작
|
|
134
|
-
const fetchPromise = fetchFunction().then(async (data) => {
|
|
135
|
-
await this.set(url, data, headers);
|
|
136
|
-
this.pendingRequests.delete(key);
|
|
137
|
-
return data;
|
|
138
|
-
}).catch((error) => {
|
|
139
|
-
this.pendingRequests.delete(key);
|
|
140
|
-
throw error;
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
this.pendingRequests.set(key, fetchPromise);
|
|
144
|
-
return await fetchPromise;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 캐시 통계
|
|
148
|
-
getStats() {
|
|
149
|
-
return {
|
|
150
|
-
...this.stats,
|
|
151
|
-
hitRate: this.stats.requests > 0 ? (this.stats.hits / this.stats.requests * 100).toFixed(2) + '%' : '0%',
|
|
152
|
-
memorySize: this.memoryCache.size,
|
|
153
|
-
cacheDir: this.cacheDir
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// 캐시 정리
|
|
158
|
-
async cleanup() {
|
|
159
|
-
const now = Date.now();
|
|
160
|
-
const expiredKeys = [];
|
|
161
|
-
|
|
162
|
-
// 메모리 캐시 정리
|
|
163
|
-
for (const [key, cached] of this.memoryCache.entries()) {
|
|
164
|
-
if (now - cached.timestamp >= this.ttl) {
|
|
165
|
-
expiredKeys.push(key);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
expiredKeys.forEach(key => this.memoryCache.delete(key));
|
|
170
|
-
|
|
171
|
-
// 디스크 캐시 정리
|
|
172
|
-
try {
|
|
173
|
-
const files = await fs.readdir(this.cacheDir);
|
|
174
|
-
for (const file of files) {
|
|
175
|
-
if (file.endsWith('.json')) {
|
|
176
|
-
const filePath = path.join(this.cacheDir, file);
|
|
177
|
-
try {
|
|
178
|
-
const stat = await fs.stat(filePath);
|
|
179
|
-
if (now - stat.mtime.getTime() >= this.ttl) {
|
|
180
|
-
await fs.unlink(filePath);
|
|
181
|
-
}
|
|
182
|
-
} catch (error) {
|
|
183
|
-
// 파일 삭제 실패는 무시
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
} catch (error) {
|
|
188
|
-
// 디렉토리 읽기 실패는 무시
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
expiredMemoryKeys: expiredKeys.length,
|
|
193
|
-
message: `Cleaned up ${expiredKeys.length} expired cache entries`
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 전체 캐시 초기화
|
|
198
|
-
async clear() {
|
|
199
|
-
this.memoryCache.clear();
|
|
200
|
-
this.stats = { hits: 0, misses: 0, requests: 0 };
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
const files = await fs.readdir(this.cacheDir);
|
|
204
|
-
for (const file of files) {
|
|
205
|
-
if (file.endsWith('.json')) {
|
|
206
|
-
await fs.unlink(path.join(this.cacheDir, file));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
} catch (error) {
|
|
210
|
-
// 디렉토리 읽기 실패는 무시
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 배치 처리를 위한 GitHub API 클라이언트
|
|
216
|
-
class OptimizedGitHubClient {
|
|
217
|
-
constructor(baseUrl = 'https://api.github.com', cacheOptions = {}) {
|
|
218
|
-
this.baseUrl = baseUrl;
|
|
219
|
-
this.cache = new GitHubAPICache(cacheOptions.cacheDir, cacheOptions.ttl);
|
|
220
|
-
this.rateLimiter = new RateLimiter(60, 60000); // 60 requests per minute
|
|
221
|
-
this.batchQueue = [];
|
|
222
|
-
this.batchTimeout = null;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async init() {
|
|
226
|
-
await this.cache.init();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 캐시된 HTTP 요청
|
|
230
|
-
async cachedFetch(url, options = {}) {
|
|
231
|
-
const headers = { 'User-Agent': 'aiwf', ...options.headers };
|
|
232
|
-
|
|
233
|
-
return await this.cache.getOrFetch(url, async () => {
|
|
234
|
-
// Rate limiting
|
|
235
|
-
await this.rateLimiter.waitForNext();
|
|
236
|
-
|
|
237
|
-
const response = await fetch(url, { ...options, headers });
|
|
238
|
-
if (!response.ok) {
|
|
239
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return await response.json();
|
|
243
|
-
}, headers);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// 배치 요청 처리
|
|
247
|
-
async batchRequest(urls, options = {}) {
|
|
248
|
-
const results = [];
|
|
249
|
-
const batchSize = options.batchSize || 5;
|
|
250
|
-
|
|
251
|
-
for (let i = 0; i < urls.length; i += batchSize) {
|
|
252
|
-
const batch = urls.slice(i, i + batchSize);
|
|
253
|
-
const batchPromises = batch.map(url => this.cachedFetch(url, options));
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
const batchResults = await Promise.allSettled(batchPromises);
|
|
257
|
-
results.push(...batchResults);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
console.error('Batch request failed:', error);
|
|
260
|
-
results.push(...batch.map(() => ({ status: 'rejected', reason: error })));
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return results;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// 디렉토리 구조 가져오기 (캐시 적용)
|
|
268
|
-
async getDirectoryStructure(repoPath, dirPath = '') {
|
|
269
|
-
const url = `${this.baseUrl}/repos/${repoPath}/contents/${dirPath}`;
|
|
270
|
-
return await this.cachedFetch(url);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 파일 내용 가져오기 (캐시 적용)
|
|
274
|
-
async getFileContent(repoPath, filePath) {
|
|
275
|
-
const url = `${this.baseUrl}/repos/${repoPath}/contents/${filePath}`;
|
|
276
|
-
return await this.cachedFetch(url);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 통계 정보
|
|
280
|
-
getStats() {
|
|
281
|
-
return {
|
|
282
|
-
cache: this.cache.getStats(),
|
|
283
|
-
rateLimit: this.rateLimiter.getStats()
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 캐시 정리
|
|
288
|
-
async cleanup() {
|
|
289
|
-
return await this.cache.cleanup();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Rate Limiter 클래스
|
|
294
|
-
class RateLimiter {
|
|
295
|
-
constructor(maxRequests, timeWindow) {
|
|
296
|
-
this.maxRequests = maxRequests;
|
|
297
|
-
this.timeWindow = timeWindow;
|
|
298
|
-
this.requests = [];
|
|
299
|
-
this.stats = { totalRequests: 0, throttledRequests: 0 };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
async waitForNext() {
|
|
303
|
-
const now = Date.now();
|
|
304
|
-
|
|
305
|
-
// 시간 윈도우를 벗어난 요청 제거
|
|
306
|
-
this.requests = this.requests.filter(time => now - time < this.timeWindow);
|
|
307
|
-
|
|
308
|
-
if (this.requests.length >= this.maxRequests) {
|
|
309
|
-
const oldestRequest = Math.min(...this.requests);
|
|
310
|
-
const waitTime = this.timeWindow - (now - oldestRequest);
|
|
311
|
-
|
|
312
|
-
if (waitTime > 0) {
|
|
313
|
-
this.stats.throttledRequests++;
|
|
314
|
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
this.requests.push(now);
|
|
319
|
-
this.stats.totalRequests++;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
getStats() {
|
|
323
|
-
return {
|
|
324
|
-
...this.stats,
|
|
325
|
-
currentRequests: this.requests.length,
|
|
326
|
-
maxRequests: this.maxRequests,
|
|
327
|
-
timeWindow: this.timeWindow
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
export { GitHubAPICache, OptimizedGitHubClient, RateLimiter };
|