@gotza02/sequential-thinking 2026.2.40 → 2026.2.41
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/dist/tools/sports/core/base.d.ts +169 -0
- package/dist/tools/sports/core/base.js +289 -0
- package/dist/tools/sports/core/cache.d.ts +106 -0
- package/dist/tools/sports/core/cache.js +305 -0
- package/dist/tools/sports/core/constants.d.ts +179 -0
- package/dist/tools/sports/core/constants.js +149 -0
- package/dist/tools/sports/core/types.d.ts +379 -0
- package/dist/tools/sports/core/types.js +5 -0
- package/dist/tools/sports/index.d.ts +34 -0
- package/dist/tools/sports/index.js +50 -0
- package/dist/tools/sports/providers/api.d.ts +73 -0
- package/dist/tools/sports/providers/api.js +517 -0
- package/dist/tools/sports/providers/scraper.d.ts +66 -0
- package/dist/tools/sports/providers/scraper.js +186 -0
- package/dist/tools/sports/providers/search.d.ts +54 -0
- package/dist/tools/sports/providers/search.js +224 -0
- package/dist/tools/sports/tools/betting.d.ts +6 -0
- package/dist/tools/sports/tools/betting.js +251 -0
- package/dist/tools/sports/tools/league.d.ts +11 -0
- package/dist/tools/sports/tools/league.js +12 -0
- package/dist/tools/sports/tools/live.d.ts +9 -0
- package/dist/tools/sports/tools/live.js +235 -0
- package/dist/tools/sports/tools/match.d.ts +6 -0
- package/dist/tools/sports/tools/match.js +323 -0
- package/dist/tools/sports/tools/player.d.ts +6 -0
- package/dist/tools/sports/tools/player.js +152 -0
- package/dist/tools/sports/tools/team.d.ts +6 -0
- package/dist/tools/sports/tools/team.js +370 -0
- package/dist/tools/sports/utils/calculator.d.ts +69 -0
- package/dist/tools/sports/utils/calculator.js +156 -0
- package/dist/tools/sports/utils/formatter.d.ts +57 -0
- package/dist/tools/sports/utils/formatter.js +206 -0
- package/dist/tools/sports.d.ts +7 -0
- package/dist/tools/sports.js +27 -6
- package/package.json +1 -1
- package/system_instruction.md +155 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPORTS MODULE BASE CLASSES
|
|
3
|
+
* Abstract base classes and interfaces for data providers
|
|
4
|
+
*/
|
|
5
|
+
import { Match, Team, Player, TableEntry, APIResponse, ProviderStatus, ProviderType } from './types.js';
|
|
6
|
+
import { CacheService } from './cache.js';
|
|
7
|
+
/**
|
|
8
|
+
* Base interface for all data providers
|
|
9
|
+
*/
|
|
10
|
+
export interface IDataProvider {
|
|
11
|
+
/**
|
|
12
|
+
* Provider name/type
|
|
13
|
+
*/
|
|
14
|
+
readonly type: ProviderType;
|
|
15
|
+
/**
|
|
16
|
+
* Check if provider is available
|
|
17
|
+
*/
|
|
18
|
+
isAvailable(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get provider status
|
|
21
|
+
*/
|
|
22
|
+
getStatus(): ProviderStatus;
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a match by ID
|
|
25
|
+
*/
|
|
26
|
+
getMatch(matchId: string): Promise<APIResponse<Match>>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch live matches for a league
|
|
29
|
+
*/
|
|
30
|
+
getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
|
|
31
|
+
/**
|
|
32
|
+
* Fetch league standings
|
|
33
|
+
*/
|
|
34
|
+
getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
|
|
35
|
+
/**
|
|
36
|
+
* Fetch team information
|
|
37
|
+
*/
|
|
38
|
+
getTeam(teamId: string): Promise<APIResponse<Team>>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch player information
|
|
41
|
+
*/
|
|
42
|
+
getPlayer(playerId: string): Promise<APIResponse<Player>>;
|
|
43
|
+
/**
|
|
44
|
+
* Search for teams
|
|
45
|
+
*/
|
|
46
|
+
searchTeams(query: string): Promise<APIResponse<Team[]>>;
|
|
47
|
+
/**
|
|
48
|
+
* Search for players
|
|
49
|
+
*/
|
|
50
|
+
searchPlayers(query: string): Promise<APIResponse<Player[]>>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Abstract base class for API-based providers
|
|
54
|
+
*/
|
|
55
|
+
export declare abstract class APIProviderBase implements IDataProvider {
|
|
56
|
+
readonly type: ProviderType;
|
|
57
|
+
protected apiKey: string;
|
|
58
|
+
protected baseUrl: string;
|
|
59
|
+
protected rateLimit: number;
|
|
60
|
+
protected cache: CacheService;
|
|
61
|
+
protected rateLimitUntil: Date | null;
|
|
62
|
+
protected lastCall: Date | null;
|
|
63
|
+
protected callCount: number;
|
|
64
|
+
constructor(type: ProviderType, apiKey: string, baseUrl: string, rateLimit?: number);
|
|
65
|
+
/**
|
|
66
|
+
* Check if provider has valid credentials
|
|
67
|
+
*/
|
|
68
|
+
isAvailable(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Check if currently rate limited
|
|
71
|
+
*/
|
|
72
|
+
isRateLimited(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Get current provider status
|
|
75
|
+
*/
|
|
76
|
+
getStatus(): ProviderStatus;
|
|
77
|
+
/**
|
|
78
|
+
* Abstract methods to be implemented by concrete providers
|
|
79
|
+
*/
|
|
80
|
+
abstract getMatch(matchId: string): Promise<APIResponse<Match>>;
|
|
81
|
+
abstract getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
|
|
82
|
+
abstract getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
|
|
83
|
+
abstract getTeam(teamId: string): Promise<APIResponse<Team>>;
|
|
84
|
+
abstract getPlayer(playerId: string): Promise<APIResponse<Player>>;
|
|
85
|
+
abstract searchTeams(query: string): Promise<APIResponse<Team[]>>;
|
|
86
|
+
abstract searchPlayers(query: string): Promise<APIResponse<Player[]>>;
|
|
87
|
+
/**
|
|
88
|
+
* Make a rate-limited API call
|
|
89
|
+
*/
|
|
90
|
+
protected callAPI<T>(endpoint: string, options?: RequestInit): Promise<APIResponse<T>>;
|
|
91
|
+
/**
|
|
92
|
+
* Get authentication headers for the API
|
|
93
|
+
*/
|
|
94
|
+
protected abstract getAuthHeaders(): Record<string, string>;
|
|
95
|
+
/**
|
|
96
|
+
* Transform raw API response to standard format
|
|
97
|
+
*/
|
|
98
|
+
protected abstract transformResponse<T>(data: any): T;
|
|
99
|
+
/**
|
|
100
|
+
* Reset rate limit tracking (for testing)
|
|
101
|
+
*/
|
|
102
|
+
resetRateLimit(): void;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Abstract base class for scraper-based providers
|
|
106
|
+
*/
|
|
107
|
+
export declare abstract class ScraperProviderBase implements IDataProvider {
|
|
108
|
+
protected cache: CacheService;
|
|
109
|
+
readonly type: ProviderType;
|
|
110
|
+
constructor();
|
|
111
|
+
isAvailable(): boolean;
|
|
112
|
+
getStatus(): ProviderStatus;
|
|
113
|
+
abstract getMatch(matchId: string): Promise<APIResponse<Match>>;
|
|
114
|
+
abstract getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
|
|
115
|
+
abstract getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
|
|
116
|
+
abstract getTeam(teamId: string): Promise<APIResponse<Team>>;
|
|
117
|
+
abstract getPlayer(playerId: string): Promise<APIResponse<Player>>;
|
|
118
|
+
abstract searchTeams(query: string): Promise<APIResponse<Team[]>>;
|
|
119
|
+
abstract searchPlayers(query: string): Promise<APIResponse<Player[]>>;
|
|
120
|
+
/**
|
|
121
|
+
* Scrape a webpage and extract structured data
|
|
122
|
+
*/
|
|
123
|
+
protected scrape<T>(url: string, extractor: (html: string) => T): Promise<APIResponse<T>>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Fallback provider that tries multiple sources in order
|
|
127
|
+
*/
|
|
128
|
+
export declare class FallbackProvider implements IDataProvider {
|
|
129
|
+
private providers;
|
|
130
|
+
private cache;
|
|
131
|
+
readonly type: ProviderType;
|
|
132
|
+
constructor(providers: IDataProvider[], cache?: CacheService);
|
|
133
|
+
isAvailable(): boolean;
|
|
134
|
+
getStatus(): ProviderStatus;
|
|
135
|
+
/**
|
|
136
|
+
* Try each provider in sequence until one succeeds
|
|
137
|
+
*/
|
|
138
|
+
private tryProviders;
|
|
139
|
+
getMatch(matchId: string): Promise<APIResponse<Match>>;
|
|
140
|
+
getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
|
|
141
|
+
getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
|
|
142
|
+
getTeam(teamId: string): Promise<APIResponse<Team>>;
|
|
143
|
+
getPlayer(playerId: string): Promise<APIResponse<Player>>;
|
|
144
|
+
searchTeams(query: string): Promise<APIResponse<Team[]>>;
|
|
145
|
+
searchPlayers(query: string): Promise<APIResponse<Player[]>>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Base class for sports tools
|
|
149
|
+
*/
|
|
150
|
+
export declare abstract class SportsToolBase {
|
|
151
|
+
protected cache: CacheService;
|
|
152
|
+
constructor(cache?: CacheService);
|
|
153
|
+
/**
|
|
154
|
+
* Format error message for user
|
|
155
|
+
*/
|
|
156
|
+
protected formatError(error: unknown, context: string): string;
|
|
157
|
+
/**
|
|
158
|
+
* Sanitize user input
|
|
159
|
+
*/
|
|
160
|
+
protected sanitizeInput(input: string): string;
|
|
161
|
+
/**
|
|
162
|
+
* Format a team name for search
|
|
163
|
+
*/
|
|
164
|
+
protected formatTeamName(name: string): string;
|
|
165
|
+
/**
|
|
166
|
+
* Format date to readable string
|
|
167
|
+
*/
|
|
168
|
+
protected formatDate(date: Date): string;
|
|
169
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPORTS MODULE BASE CLASSES
|
|
3
|
+
* Abstract base classes and interfaces for data providers
|
|
4
|
+
*/
|
|
5
|
+
import { CacheService } from './cache.js';
|
|
6
|
+
import { logger } from '../../../utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Abstract base class for API-based providers
|
|
9
|
+
*/
|
|
10
|
+
export class APIProviderBase {
|
|
11
|
+
type;
|
|
12
|
+
apiKey;
|
|
13
|
+
baseUrl;
|
|
14
|
+
rateLimit;
|
|
15
|
+
cache;
|
|
16
|
+
rateLimitUntil = null;
|
|
17
|
+
lastCall = null;
|
|
18
|
+
callCount = 0;
|
|
19
|
+
constructor(type, apiKey, baseUrl, rateLimit = 10 // calls per minute
|
|
20
|
+
) {
|
|
21
|
+
this.type = type;
|
|
22
|
+
this.apiKey = apiKey;
|
|
23
|
+
this.baseUrl = baseUrl;
|
|
24
|
+
this.rateLimit = rateLimit;
|
|
25
|
+
this.cache = new CacheService();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if provider has valid credentials
|
|
29
|
+
*/
|
|
30
|
+
isAvailable() {
|
|
31
|
+
return !!this.apiKey && !this.isRateLimited();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if currently rate limited
|
|
35
|
+
*/
|
|
36
|
+
isRateLimited() {
|
|
37
|
+
if (!this.rateLimitUntil)
|
|
38
|
+
return false;
|
|
39
|
+
return new Date() < this.rateLimitUntil;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get current provider status
|
|
43
|
+
*/
|
|
44
|
+
getStatus() {
|
|
45
|
+
return {
|
|
46
|
+
name: this.type,
|
|
47
|
+
available: this.isAvailable(),
|
|
48
|
+
quotaRemaining: Math.max(0, this.rateLimit - this.callCount),
|
|
49
|
+
quotaLimit: this.rateLimit,
|
|
50
|
+
rateLimitUntil: this.rateLimitUntil || undefined,
|
|
51
|
+
lastCall: this.lastCall || undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Make a rate-limited API call
|
|
56
|
+
*/
|
|
57
|
+
async callAPI(endpoint, options = {}) {
|
|
58
|
+
// Check rate limit
|
|
59
|
+
if (this.isRateLimited()) {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: `Rate limited until ${this.rateLimitUntil}`,
|
|
63
|
+
rateLimited: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Check availability
|
|
67
|
+
if (!this.isAvailable()) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: 'Provider not available (missing API key)',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
...options,
|
|
77
|
+
headers: {
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
...this.getAuthHeaders(),
|
|
80
|
+
...options.headers,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
this.lastCall = new Date();
|
|
84
|
+
this.callCount++;
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
// Handle rate limiting
|
|
87
|
+
if (response.status === 429) {
|
|
88
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
89
|
+
this.rateLimitUntil = new Date(Date.now() + (retryAfter ? parseInt(retryAfter) * 1000 : 60000));
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: 'Rate limited',
|
|
93
|
+
rateLimited: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: `HTTP ${response.status}: ${response.statusText}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const data = await response.json();
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
data: this.transformResponse(data),
|
|
105
|
+
provider: this.type,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.error(`${this.type} API error: ${error}`);
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: error instanceof Error ? error.message : String(error),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Reset rate limit tracking (for testing)
|
|
118
|
+
*/
|
|
119
|
+
resetRateLimit() {
|
|
120
|
+
this.rateLimitUntil = null;
|
|
121
|
+
this.callCount = 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Abstract base class for scraper-based providers
|
|
126
|
+
*/
|
|
127
|
+
export class ScraperProviderBase {
|
|
128
|
+
cache;
|
|
129
|
+
type = 'scraper';
|
|
130
|
+
constructor() {
|
|
131
|
+
this.cache = new CacheService();
|
|
132
|
+
}
|
|
133
|
+
isAvailable() {
|
|
134
|
+
return true; // Scraping is always "available"
|
|
135
|
+
}
|
|
136
|
+
getStatus() {
|
|
137
|
+
return {
|
|
138
|
+
name: this.type,
|
|
139
|
+
available: true,
|
|
140
|
+
quotaRemaining: Infinity,
|
|
141
|
+
quotaLimit: Infinity,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Scrape a webpage and extract structured data
|
|
146
|
+
*/
|
|
147
|
+
async scrape(url, extractor) {
|
|
148
|
+
try {
|
|
149
|
+
const { fetchWithRetry } = await import('../../../utils.js');
|
|
150
|
+
const response = await fetchWithRetry(url);
|
|
151
|
+
const html = await response.text();
|
|
152
|
+
const data = extractor(html);
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
data,
|
|
156
|
+
provider: 'scraper',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
logger.error(`Scraping error for ${url}: ${error}`);
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
error: error instanceof Error ? error.message : String(error),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Fallback provider that tries multiple sources in order
|
|
170
|
+
*/
|
|
171
|
+
export class FallbackProvider {
|
|
172
|
+
providers;
|
|
173
|
+
cache;
|
|
174
|
+
type = 'scraper';
|
|
175
|
+
constructor(providers, cache = new CacheService()) {
|
|
176
|
+
this.providers = providers;
|
|
177
|
+
this.cache = cache;
|
|
178
|
+
}
|
|
179
|
+
isAvailable() {
|
|
180
|
+
return this.providers.some(p => p.isAvailable());
|
|
181
|
+
}
|
|
182
|
+
getStatus() {
|
|
183
|
+
const available = this.providers.filter(p => p.isAvailable());
|
|
184
|
+
return {
|
|
185
|
+
name: this.type,
|
|
186
|
+
available: available.length > 0,
|
|
187
|
+
quotaRemaining: available.length,
|
|
188
|
+
quotaLimit: this.providers.length,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Try each provider in sequence until one succeeds
|
|
193
|
+
*/
|
|
194
|
+
async tryProviders(fn) {
|
|
195
|
+
const errors = [];
|
|
196
|
+
for (const provider of this.providers) {
|
|
197
|
+
if (!provider.isAvailable()) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const result = await fn(provider);
|
|
201
|
+
if (result.success) {
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
errors.push(`${provider.type}: ${result.error || 'Unknown error'}`);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
error: `All providers failed:\n${errors.join('\n')}`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async getMatch(matchId) {
|
|
212
|
+
const cacheKey = CacheService.generateKey('match', matchId);
|
|
213
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getMatch(matchId)), 5 * 60 * 1000 // 5 minutes
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
async getLiveMatches(leagueId) {
|
|
217
|
+
const cacheKey = CacheService.generateKey('live', leagueId || 'all');
|
|
218
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getLiveMatches(leagueId)), 60 * 1000 // 1 minute
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
async getStandings(leagueId, season) {
|
|
222
|
+
const cacheKey = CacheService.generateKey('standings', leagueId, season || 'current');
|
|
223
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getStandings(leagueId, season)), 30 * 60 * 1000 // 30 minutes
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
async getTeam(teamId) {
|
|
227
|
+
const cacheKey = CacheService.generateKey('team', teamId);
|
|
228
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getTeam(teamId)), 60 * 60 * 1000 // 1 hour
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
async getPlayer(playerId) {
|
|
232
|
+
const cacheKey = CacheService.generateKey('player', playerId);
|
|
233
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getPlayer(playerId)), 60 * 60 * 1000 // 1 hour
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
async searchTeams(query) {
|
|
237
|
+
const cacheKey = CacheService.generateKey('search', 'team', query);
|
|
238
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.searchTeams(query)), 60 * 60 * 1000 // 1 hour
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
async searchPlayers(query) {
|
|
242
|
+
const cacheKey = CacheService.generateKey('search', 'player', query);
|
|
243
|
+
return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.searchPlayers(query)), 60 * 60 * 1000 // 1 hour
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Base class for sports tools
|
|
249
|
+
*/
|
|
250
|
+
export class SportsToolBase {
|
|
251
|
+
cache;
|
|
252
|
+
constructor(cache) {
|
|
253
|
+
this.cache = cache || new CacheService();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Format error message for user
|
|
257
|
+
*/
|
|
258
|
+
formatError(error, context) {
|
|
259
|
+
if (error instanceof Error) {
|
|
260
|
+
return `Error in ${context}: ${error.message}`;
|
|
261
|
+
}
|
|
262
|
+
return `Unknown error in ${context}`;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Sanitize user input
|
|
266
|
+
*/
|
|
267
|
+
sanitizeInput(input) {
|
|
268
|
+
return input.trim().replace(/[<>]/g, '');
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Format a team name for search
|
|
272
|
+
*/
|
|
273
|
+
formatTeamName(name) {
|
|
274
|
+
return this.sanitizeInput(name).toLowerCase();
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Format date to readable string
|
|
278
|
+
*/
|
|
279
|
+
formatDate(date) {
|
|
280
|
+
return date.toLocaleDateString('en-US', {
|
|
281
|
+
weekday: 'short',
|
|
282
|
+
year: 'numeric',
|
|
283
|
+
month: 'short',
|
|
284
|
+
day: 'numeric',
|
|
285
|
+
hour: '2-digit',
|
|
286
|
+
minute: '2-digit',
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPORTS MODULE CACHE SERVICE
|
|
3
|
+
* TTL-based caching with file persistence for the football intelligence system
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* CacheService - Thread-safe TTL-based caching with persistence
|
|
7
|
+
*/
|
|
8
|
+
export declare class CacheService {
|
|
9
|
+
private cache;
|
|
10
|
+
private cachePath;
|
|
11
|
+
private maxAge;
|
|
12
|
+
private maxSize;
|
|
13
|
+
private savePending;
|
|
14
|
+
private saveTimer;
|
|
15
|
+
constructor(cachePath?: string, maxAge?: number, maxSize?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Generate a cache key from components
|
|
18
|
+
*/
|
|
19
|
+
static generateKey(...parts: (string | number)[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get a value from cache
|
|
22
|
+
*/
|
|
23
|
+
get<T>(key: string): T | null;
|
|
24
|
+
/**
|
|
25
|
+
* Set a value in cache
|
|
26
|
+
*/
|
|
27
|
+
set<T>(key: string, data: T, ttl?: number): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a key exists and is not expired
|
|
30
|
+
*/
|
|
31
|
+
has(key: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Delete a specific key
|
|
34
|
+
*/
|
|
35
|
+
delete(key: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all cache entries
|
|
38
|
+
*/
|
|
39
|
+
clear(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get cache statistics
|
|
42
|
+
*/
|
|
43
|
+
getStats(): {
|
|
44
|
+
size: number;
|
|
45
|
+
maxSize: number;
|
|
46
|
+
hits: number;
|
|
47
|
+
entries: Array<{
|
|
48
|
+
key: string;
|
|
49
|
+
hits: number;
|
|
50
|
+
age: number;
|
|
51
|
+
ttl: number;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Clean up expired entries
|
|
56
|
+
*/
|
|
57
|
+
cleanup(): number;
|
|
58
|
+
/**
|
|
59
|
+
* Evict the oldest entry (LRU)
|
|
60
|
+
*/
|
|
61
|
+
private evictOldest;
|
|
62
|
+
/**
|
|
63
|
+
* Schedule a save operation (debounced)
|
|
64
|
+
*/
|
|
65
|
+
private scheduleSave;
|
|
66
|
+
/**
|
|
67
|
+
* Save cache to file
|
|
68
|
+
*/
|
|
69
|
+
private saveToFile;
|
|
70
|
+
/**
|
|
71
|
+
* Load cache from file
|
|
72
|
+
*/
|
|
73
|
+
private loadFromFile;
|
|
74
|
+
/**
|
|
75
|
+
* Force save cache to file immediately
|
|
76
|
+
*/
|
|
77
|
+
flush(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Get or set pattern - returns cached value or computes and caches it
|
|
80
|
+
*/
|
|
81
|
+
getOrSet<T>(key: string, factory: () => T | Promise<T>, ttl?: number): Promise<T>;
|
|
82
|
+
/**
|
|
83
|
+
* Get multiple keys at once
|
|
84
|
+
*/
|
|
85
|
+
getMultiple<T>(keys: string[]): Map<string, T>;
|
|
86
|
+
/**
|
|
87
|
+
* Set multiple keys at once
|
|
88
|
+
*/
|
|
89
|
+
setMultiple<T>(entries: Map<string, T>, ttl?: number): void;
|
|
90
|
+
/**
|
|
91
|
+
* Invalidate cache by pattern
|
|
92
|
+
*/
|
|
93
|
+
invalidatePattern(pattern: string): number;
|
|
94
|
+
/**
|
|
95
|
+
* Get cache size breakdown by key prefix
|
|
96
|
+
*/
|
|
97
|
+
getBreakdown(): Record<string, number>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get or create the global cache instance
|
|
101
|
+
*/
|
|
102
|
+
export declare function getGlobalCache(): CacheService;
|
|
103
|
+
/**
|
|
104
|
+
* Reset the global cache instance
|
|
105
|
+
*/
|
|
106
|
+
export declare function resetGlobalCache(): void;
|