@inso_web/els-mcp 0.1.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 +482 -0
- package/dist/audit/prisma.d.ts +67 -0
- package/dist/audit/prisma.d.ts.map +1 -0
- package/dist/audit/prisma.js +65 -0
- package/dist/audit/prisma.js.map +1 -0
- package/dist/audit/service.d.ts +72 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +137 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/billing/limits.d.ts +34 -0
- package/dist/billing/limits.d.ts.map +1 -0
- package/dist/billing/limits.js +51 -0
- package/dist/billing/limits.js.map +1 -0
- package/dist/billing/tracker.d.ts +39 -0
- package/dist/billing/tracker.d.ts.map +1 -0
- package/dist/billing/tracker.js +92 -0
- package/dist/billing/tracker.js.map +1 -0
- package/dist/cache/cachedElsClient.d.ts +71 -0
- package/dist/cache/cachedElsClient.d.ts.map +1 -0
- package/dist/cache/cachedElsClient.js +167 -0
- package/dist/cache/cachedElsClient.js.map +1 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/policies.d.ts +60 -0
- package/dist/cache/policies.d.ts.map +1 -0
- package/dist/cache/policies.js +90 -0
- package/dist/cache/policies.js.map +1 -0
- package/dist/cache/redis.d.ts +52 -0
- package/dist/cache/redis.d.ts.map +1 -0
- package/dist/cache/redis.js +134 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/cache/types.d.ts +32 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +32 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/cache/wrapper.d.ts +38 -0
- package/dist/cache/wrapper.d.ts.map +1 -0
- package/dist/cache/wrapper.js +109 -0
- package/dist/cache/wrapper.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +86 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +105 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +211 -0
- package/dist/config.js.map +1 -0
- package/dist/elsClient.d.ts +137 -0
- package/dist/elsClient.d.ts.map +1 -0
- package/dist/elsClient.js +285 -0
- package/dist/elsClient.js.map +1 -0
- package/dist/http/app.d.ts +40 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +135 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/jwks.d.ts +8 -0
- package/dist/http/jwks.d.ts.map +1 -0
- package/dist/http/jwks.js +34 -0
- package/dist/http/jwks.js.map +1 -0
- package/dist/http/middleware/auth.d.ts +11 -0
- package/dist/http/middleware/auth.d.ts.map +1 -0
- package/dist/http/middleware/auth.js +225 -0
- package/dist/http/middleware/auth.js.map +1 -0
- package/dist/http/middleware/dcrRateLimit.d.ts +29 -0
- package/dist/http/middleware/dcrRateLimit.d.ts.map +1 -0
- package/dist/http/middleware/dcrRateLimit.js +59 -0
- package/dist/http/middleware/dcrRateLimit.js.map +1 -0
- package/dist/http/middleware/errorHandler.d.ts +12 -0
- package/dist/http/middleware/errorHandler.d.ts.map +1 -0
- package/dist/http/middleware/errorHandler.js +26 -0
- package/dist/http/middleware/errorHandler.js.map +1 -0
- package/dist/http/middleware/originGuard.d.ts +28 -0
- package/dist/http/middleware/originGuard.d.ts.map +1 -0
- package/dist/http/middleware/originGuard.js +55 -0
- package/dist/http/middleware/originGuard.js.map +1 -0
- package/dist/http/middleware/requestId.d.ts +19 -0
- package/dist/http/middleware/requestId.d.ts.map +1 -0
- package/dist/http/middleware/requestId.js +23 -0
- package/dist/http/middleware/requestId.js.map +1 -0
- package/dist/http/routes/health.d.ts +24 -0
- package/dist/http/routes/health.d.ts.map +1 -0
- package/dist/http/routes/health.js +73 -0
- package/dist/http/routes/health.js.map +1 -0
- package/dist/http/routes/metrics.d.ts +18 -0
- package/dist/http/routes/metrics.d.ts.map +1 -0
- package/dist/http/routes/metrics.js +42 -0
- package/dist/http/routes/metrics.js.map +1 -0
- package/dist/http/routes/wellKnown.d.ts +15 -0
- package/dist/http/routes/wellKnown.d.ts.map +1 -0
- package/dist/http/routes/wellKnown.js +43 -0
- package/dist/http/routes/wellKnown.js.map +1 -0
- package/dist/http/types.d.ts +40 -0
- package/dist/http/types.d.ts.map +1 -0
- package/dist/http/types.js +9 -0
- package/dist/http/types.js.map +1 -0
- package/dist/instrumentation.d.ts +22 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +38 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/lib/cursor.d.ts +22 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +95 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/errors.d.ts +49 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +83 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/responseFormat.d.ts +14 -0
- package/dist/lib/responseFormat.d.ts.map +1 -0
- package/dist/lib/responseFormat.js +74 -0
- package/dist/lib/responseFormat.js.map +1 -0
- package/dist/middleware/withMiddleware.d.ts +53 -0
- package/dist/middleware/withMiddleware.d.ts.map +1 -0
- package/dist/middleware/withMiddleware.js +190 -0
- package/dist/middleware/withMiddleware.js.map +1 -0
- package/dist/observability/health.d.ts +51 -0
- package/dist/observability/health.d.ts.map +1 -0
- package/dist/observability/health.js +77 -0
- package/dist/observability/health.js.map +1 -0
- package/dist/observability/index.d.ts +8 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +5 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/logger.d.ts +45 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +75 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +49 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +184 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/tracing.d.ts +28 -0
- package/dist/observability/tracing.d.ts.map +1 -0
- package/dist/observability/tracing.js +56 -0
- package/dist/observability/tracing.js.map +1 -0
- package/dist/prompts/index.d.ts +20 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +202 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/redaction/argsRedactor.d.ts +22 -0
- package/dist/redaction/argsRedactor.d.ts.map +1 -0
- package/dist/redaction/argsRedactor.js +97 -0
- package/dist/redaction/argsRedactor.js.map +1 -0
- package/dist/redaction/fields.d.ts +64 -0
- package/dist/redaction/fields.d.ts.map +1 -0
- package/dist/redaction/fields.js +155 -0
- package/dist/redaction/fields.js.map +1 -0
- package/dist/redaction/index.d.ts +52 -0
- package/dist/redaction/index.d.ts.map +1 -0
- package/dist/redaction/index.js +160 -0
- package/dist/redaction/index.js.map +1 -0
- package/dist/redaction/promptInjection.d.ts +32 -0
- package/dist/redaction/promptInjection.d.ts.map +1 -0
- package/dist/redaction/promptInjection.js +68 -0
- package/dist/redaction/promptInjection.js.map +1 -0
- package/dist/redaction/url.d.ts +8 -0
- package/dist/redaction/url.d.ts.map +1 -0
- package/dist/redaction/url.js +26 -0
- package/dist/redaction/url.js.map +1 -0
- package/dist/redaction/userAgent.d.ts +9 -0
- package/dist/redaction/userAgent.d.ts.map +1 -0
- package/dist/redaction/userAgent.js +39 -0
- package/dist/redaction/userAgent.js.map +1 -0
- package/dist/resources/index.d.ts +24 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +150 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server.d.ts +37 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +35 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/baselineCompare.d.ts +36 -0
- package/dist/tools/baselineCompare.d.ts.map +1 -0
- package/dist/tools/baselineCompare.js +69 -0
- package/dist/tools/baselineCompare.js.map +1 -0
- package/dist/tools/errorHeatmap.d.ts +40 -0
- package/dist/tools/errorHeatmap.d.ts.map +1 -0
- package/dist/tools/errorHeatmap.js +69 -0
- package/dist/tools/errorHeatmap.js.map +1 -0
- package/dist/tools/errorHistogram.d.ts +39 -0
- package/dist/tools/errorHistogram.d.ts.map +1 -0
- package/dist/tools/errorHistogram.js +61 -0
- package/dist/tools/errorHistogram.js.map +1 -0
- package/dist/tools/errorStatsBreakdown.d.ts +43 -0
- package/dist/tools/errorStatsBreakdown.d.ts.map +1 -0
- package/dist/tools/errorStatsBreakdown.js +77 -0
- package/dist/tools/errorStatsBreakdown.js.map +1 -0
- package/dist/tools/errorsInSession.d.ts +44 -0
- package/dist/tools/errorsInSession.d.ts.map +1 -0
- package/dist/tools/errorsInSession.js +91 -0
- package/dist/tools/errorsInSession.js.map +1 -0
- package/dist/tools/explainError.d.ts +35 -0
- package/dist/tools/explainError.d.ts.map +1 -0
- package/dist/tools/explainError.js +98 -0
- package/dist/tools/explainError.js.map +1 -0
- package/dist/tools/findCorrelatedErrors.d.ts +43 -0
- package/dist/tools/findCorrelatedErrors.d.ts.map +1 -0
- package/dist/tools/findCorrelatedErrors.js +59 -0
- package/dist/tools/findCorrelatedErrors.js.map +1 -0
- package/dist/tools/findSimilarErrors.d.ts +44 -0
- package/dist/tools/findSimilarErrors.d.ts.map +1 -0
- package/dist/tools/findSimilarErrors.js +59 -0
- package/dist/tools/findSimilarErrors.js.map +1 -0
- package/dist/tools/getLogDetails.d.ts +30 -0
- package/dist/tools/getLogDetails.d.ts.map +1 -0
- package/dist/tools/getLogDetails.js +49 -0
- package/dist/tools/getLogDetails.js.map +1 -0
- package/dist/tools/groupedErrors.d.ts +46 -0
- package/dist/tools/groupedErrors.d.ts.map +1 -0
- package/dist/tools/groupedErrors.js +71 -0
- package/dist/tools/groupedErrors.js.map +1 -0
- package/dist/tools/impactAnalysis.d.ts +42 -0
- package/dist/tools/impactAnalysis.d.ts.map +1 -0
- package/dist/tools/impactAnalysis.js +61 -0
- package/dist/tools/impactAnalysis.js.map +1 -0
- package/dist/tools/index.d.ts +41 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +83 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listApps.d.ts +27 -0
- package/dist/tools/listApps.d.ts.map +1 -0
- package/dist/tools/listApps.js +78 -0
- package/dist/tools/listApps.js.map +1 -0
- package/dist/tools/queryLogsJql.d.ts +44 -0
- package/dist/tools/queryLogsJql.d.ts.map +1 -0
- package/dist/tools/queryLogsJql.js +107 -0
- package/dist/tools/queryLogsJql.js.map +1 -0
- package/dist/tools/searchLogs.d.ts +60 -0
- package/dist/tools/searchLogs.d.ts.map +1 -0
- package/dist/tools/searchLogs.js +127 -0
- package/dist/tools/searchLogs.js.map +1 -0
- package/dist/tools/topErrorMessages.d.ts +42 -0
- package/dist/tools/topErrorMessages.d.ts.map +1 -0
- package/dist/tools/topErrorMessages.js +63 -0
- package/dist/tools/topErrorMessages.js.map +1 -0
- package/dist/tools/trafficStats.d.ts +39 -0
- package/dist/tools/trafficStats.d.ts.map +1 -0
- package/dist/tools/trafficStats.js +57 -0
- package/dist/tools/trafficStats.js.map +1 -0
- package/dist/tools/triageRecentCritical.d.ts +26 -0
- package/dist/tools/triageRecentCritical.d.ts.map +1 -0
- package/dist/tools/triageRecentCritical.js +81 -0
- package/dist/tools/triageRecentCritical.js.map +1 -0
- package/dist/tools/versionRegression.d.ts +29 -0
- package/dist/tools/versionRegression.d.ts.map +1 -0
- package/dist/tools/versionRegression.js +80 -0
- package/dist/tools/versionRegression.js.map +1 -0
- package/dist/transports/http-server.d.ts +25 -0
- package/dist/transports/http-server.d.ts.map +1 -0
- package/dist/transports/http-server.js +84 -0
- package/dist/transports/http-server.js.map +1 -0
- package/dist/transports/http.d.ts +71 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +315 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +13 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +16 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { CACHE_POLICIES, paramsHash, tenantKey } from './policies.js';
|
|
2
|
+
/**
|
|
3
|
+
* Кэширующая обёртка над `ElsClient`.
|
|
4
|
+
*
|
|
5
|
+
* Дизайн — **composition over inheritance**:
|
|
6
|
+
* - сохраняет 100% совместимую сигнатуру с ElsClient;
|
|
7
|
+
* - не модифицирует ElsClient (additive only);
|
|
8
|
+
* - `ctx` принимается опционально (по умолчанию — fallback context от config).
|
|
9
|
+
*
|
|
10
|
+
* Если cache недоступен (Redis down или disabled) — все методы прозрачно
|
|
11
|
+
* прокидываются в underlying `ElsClient`.
|
|
12
|
+
*
|
|
13
|
+
* Все cache keys построены через `tenantKey()` (см. `policies.ts`), что
|
|
14
|
+
* гарантирует tenant-isolation (cross-tenant leak guard).
|
|
15
|
+
*
|
|
16
|
+
* Какие методы кэшируем (см. `05-high-load.md` § 2.1):
|
|
17
|
+
*
|
|
18
|
+
* | Method | Class | TTL |
|
|
19
|
+
* |----------------------|------------------|--------|
|
|
20
|
+
* | getLogDetails | log_details | 1h |
|
|
21
|
+
* | searchLogs | search_recent | 15s |
|
|
22
|
+
* | topErrorMessages | top_messages | 2m |
|
|
23
|
+
* | errorHistogram | histogram | 1m |
|
|
24
|
+
* | trafficStats | traffic_long | 5m |
|
|
25
|
+
* | listApps | list_apps | 30s |
|
|
26
|
+
* | heatmap | heatmap | 5m |
|
|
27
|
+
* | errorStats | stats_breakdown | 2m |
|
|
28
|
+
* | groupedErrors | grouped_errors | 2m |
|
|
29
|
+
* | baseline | baseline | 5m |
|
|
30
|
+
* | versionRegression | version_timeline | 5m |
|
|
31
|
+
*
|
|
32
|
+
* Что НЕ кэшируем (см. § 2.4):
|
|
33
|
+
* - `queryLogsJql` — высокая кардинальность ключей, hit ratio < 5%.
|
|
34
|
+
* - `findCorrelatedErrors` — корреляция меняется при ingest.
|
|
35
|
+
* - `findSimilarErrors` — агрегаты "за последний час" — слишком волатильны.
|
|
36
|
+
*/
|
|
37
|
+
export class CachedElsClient {
|
|
38
|
+
inner;
|
|
39
|
+
cache;
|
|
40
|
+
policies;
|
|
41
|
+
_log;
|
|
42
|
+
constructor(inner, cache, policies = { ...CACHE_POLICIES }, _log) {
|
|
43
|
+
this.inner = inner;
|
|
44
|
+
this.cache = cache;
|
|
45
|
+
this.policies = policies;
|
|
46
|
+
this._log = _log;
|
|
47
|
+
}
|
|
48
|
+
/** Доступ к raw client (нужен для методов без кэша). */
|
|
49
|
+
get raw() {
|
|
50
|
+
return this.inner;
|
|
51
|
+
}
|
|
52
|
+
async close() {
|
|
53
|
+
await this.inner.close();
|
|
54
|
+
}
|
|
55
|
+
// ─── Cached methods ─────────────────────────────────────────────────────
|
|
56
|
+
async getLogDetails(traceId, ctx) {
|
|
57
|
+
const key = this.buildKey('log_details', ctx, [traceId]);
|
|
58
|
+
const ttl = this.policies.log_details;
|
|
59
|
+
if (!key)
|
|
60
|
+
return this.inner.getLogDetails(traceId);
|
|
61
|
+
const { value } = await this.cache.cached(key, ttl, () => this.inner.getLogDetails(traceId), { toolClass: 'log_details' });
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
async searchLogs(params, ctx) {
|
|
65
|
+
const key = this.buildKey('search_recent', ctx, [paramsHash(params)]);
|
|
66
|
+
const ttl = this.policies.search_recent;
|
|
67
|
+
if (!key)
|
|
68
|
+
return this.inner.searchLogs(params);
|
|
69
|
+
const { value } = await this.cache.cached(key, ttl, () => this.inner.searchLogs(params), { toolClass: 'search_recent' });
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
async topErrorMessages(params, ctx) {
|
|
73
|
+
const key = this.buildKey('top_messages', ctx, [paramsHash(params)]);
|
|
74
|
+
if (!key)
|
|
75
|
+
return this.inner.topErrorMessages(params);
|
|
76
|
+
const { value } = await this.cache.cached(key, this.policies.top_messages, () => this.inner.topErrorMessages(params), { toolClass: 'top_messages' });
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
async errorHistogram(params, ctx) {
|
|
80
|
+
const key = this.buildKey('histogram', ctx, [paramsHash(params)]);
|
|
81
|
+
if (!key)
|
|
82
|
+
return this.inner.errorHistogram(params);
|
|
83
|
+
const { value } = await this.cache.cached(key, this.policies.histogram, () => this.inner.errorHistogram(params), { toolClass: 'histogram' });
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
async trafficStats(params, ctx) {
|
|
87
|
+
const key = this.buildKey('traffic_long', ctx, [paramsHash(params)]);
|
|
88
|
+
if (!key)
|
|
89
|
+
return this.inner.trafficStats(params);
|
|
90
|
+
const { value } = await this.cache.cached(key, this.policies.traffic_long, () => this.inner.trafficStats(params), { toolClass: 'traffic_long' });
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
async listApps(ctx) {
|
|
94
|
+
// listApps — единственный case, где key может быть только по keyPrefix
|
|
95
|
+
// (master-key вообще без appSlug). Передаём пустой scope-tenant fallback.
|
|
96
|
+
const key = this.buildKey('list_apps', ctx, ['v1']);
|
|
97
|
+
if (!key)
|
|
98
|
+
return this.inner.listApps();
|
|
99
|
+
const { value } = await this.cache.cached(key, this.policies.list_apps, () => this.inner.listApps(), { toolClass: 'list_apps' });
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
async heatmap(params, ctx) {
|
|
103
|
+
const key = this.buildKey('heatmap', ctx, [paramsHash(params)]);
|
|
104
|
+
if (!key)
|
|
105
|
+
return this.inner.heatmap(params);
|
|
106
|
+
const { value } = await this.cache.cached(key, this.policies.heatmap, () => this.inner.heatmap(params), { toolClass: 'heatmap' });
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
async errorStats(params, ctx) {
|
|
110
|
+
const key = this.buildKey('stats_breakdown', ctx, [paramsHash(params)]);
|
|
111
|
+
if (!key)
|
|
112
|
+
return this.inner.errorStats(params);
|
|
113
|
+
const { value } = await this.cache.cached(key, this.policies.stats_breakdown, () => this.inner.errorStats(params), { toolClass: 'stats_breakdown' });
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
async groupedErrors(params, ctx) {
|
|
117
|
+
const key = this.buildKey('grouped_errors', ctx, [paramsHash(params)]);
|
|
118
|
+
if (!key)
|
|
119
|
+
return this.inner.groupedErrors(params);
|
|
120
|
+
const { value } = await this.cache.cached(key, this.policies.grouped_errors, () => this.inner.groupedErrors(params), { toolClass: 'grouped_errors' });
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
async baseline(params, ctx) {
|
|
124
|
+
const key = this.buildKey('baseline', ctx, [paramsHash(params)]);
|
|
125
|
+
if (!key)
|
|
126
|
+
return this.inner.baseline(params);
|
|
127
|
+
const { value } = await this.cache.cached(key, this.policies.baseline, () => this.inner.baseline(params), { toolClass: 'baseline' });
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
async versionRegression(params, ctx) {
|
|
131
|
+
const key = this.buildKey('version_timeline', ctx, [paramsHash(params)]);
|
|
132
|
+
if (!key)
|
|
133
|
+
return this.inner.versionRegression(params);
|
|
134
|
+
const { value } = await this.cache.cached(key, this.policies.version_timeline, () => this.inner.versionRegression(params), { toolClass: 'version_timeline' });
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
// ─── Pass-through (не кэшируем) ────────────────────────────────────────
|
|
138
|
+
async findSimilarErrors(...args) {
|
|
139
|
+
return this.inner.findSimilarErrors(...args);
|
|
140
|
+
}
|
|
141
|
+
async findCorrelatedErrors(...args) {
|
|
142
|
+
return this.inner.findCorrelatedErrors(...args);
|
|
143
|
+
}
|
|
144
|
+
async queryLogsJql(...args) {
|
|
145
|
+
return this.inner.queryLogsJql(...args);
|
|
146
|
+
}
|
|
147
|
+
async impact(...args) {
|
|
148
|
+
return this.inner.impact(...args);
|
|
149
|
+
}
|
|
150
|
+
// ─── helpers ────────────────────────────────────────────────────────────
|
|
151
|
+
/**
|
|
152
|
+
* Строит cache key с tenant-isolation. Возвращает null если контекст не даёт
|
|
153
|
+
* tenant идентификатор — caller обязан в этом случае идти в bypass-path.
|
|
154
|
+
*/
|
|
155
|
+
buildKey(scope, ctx, parts) {
|
|
156
|
+
const appSlug = ctx?.appSlug ?? null;
|
|
157
|
+
const keyPrefix = ctx?.keyPrefix;
|
|
158
|
+
try {
|
|
159
|
+
return tenantKey(scope, appSlug, parts, keyPrefix);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Без tenant guard'а — отключаем кэш для этого вызова.
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=cachedElsClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cachedElsClient.js","sourceRoot":"","sources":["../../src/cache/cachedElsClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CACHE_POLICIES, paramsHash, tenantKey, resolvePolicies } from './policies.js';
|
|
2
|
+
export type { CacheClass, CachePolicyOverrides } from './policies.js';
|
|
3
|
+
export { CacheService, cached } from './wrapper.js';
|
|
4
|
+
export type { CacheServiceDeps, CacheOptions } from './wrapper.js';
|
|
5
|
+
export { CachedElsClient } from './cachedElsClient.js';
|
|
6
|
+
export { RedisService, getRedisService, setRedisServiceForTests, resetRedisSingleton, } from './redis.js';
|
|
7
|
+
export type { RedisServiceOptions } from './redis.js';
|
|
8
|
+
export type { RequestContext } from './types.js';
|
|
9
|
+
export { deriveContextFromKey } from './types.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvF,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { CACHE_POLICIES, paramsHash, tenantKey, resolvePolicies } from './policies.js';
|
|
2
|
+
export { CacheService, cached } from './wrapper.js';
|
|
3
|
+
export { CachedElsClient } from './cachedElsClient.js';
|
|
4
|
+
export { RedisService, getRedisService, setRedisServiceForTests, resetRedisSingleton, } from './redis.js';
|
|
5
|
+
export { deriveContextFromKey } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEvF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache TTL policies per "tool class".
|
|
3
|
+
*
|
|
4
|
+
* Источник: `todo/error-logs-service/mcp/05-high-load.md` § 2.1.
|
|
5
|
+
*
|
|
6
|
+
* Class имена используются:
|
|
7
|
+
* - как `tool_class` label в Prometheus метриках (`mcp_cache_hit_ratio`);
|
|
8
|
+
* - как первая часть Redis key namespace (`mcp:cache:{class}:...`).
|
|
9
|
+
*
|
|
10
|
+
* Изменения дефолтов делайте здесь, не в `cachedElsClient.ts` — chair pattern.
|
|
11
|
+
*
|
|
12
|
+
* Значения можно override через ENV `MCP_CACHE_TTL_OVERRIDE_{CLASS}=secs`
|
|
13
|
+
* (см. `src/config.ts`).
|
|
14
|
+
*/
|
|
15
|
+
export declare const CACHE_POLICIES: {
|
|
16
|
+
readonly log_details: 3600;
|
|
17
|
+
readonly traffic_long: 300;
|
|
18
|
+
readonly traffic_short: 60;
|
|
19
|
+
readonly top_messages: 120;
|
|
20
|
+
readonly stats_breakdown: 120;
|
|
21
|
+
readonly search_recent: 15;
|
|
22
|
+
readonly list_apps: 30;
|
|
23
|
+
readonly heatmap: 300;
|
|
24
|
+
readonly histogram: 60;
|
|
25
|
+
readonly baseline: 300;
|
|
26
|
+
readonly version_timeline: 300;
|
|
27
|
+
readonly grouped_errors: 120;
|
|
28
|
+
};
|
|
29
|
+
export type CacheClass = keyof typeof CACHE_POLICIES;
|
|
30
|
+
export interface CachePolicyOverrides {
|
|
31
|
+
[k: string]: number | undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Объединяет дефолтные политики с user overrides из конфига.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolvePolicies(overrides?: CachePolicyOverrides): Record<CacheClass, number>;
|
|
37
|
+
/**
|
|
38
|
+
* Стабильный sha256 hash от объекта параметров (отсортированные ключи).
|
|
39
|
+
*
|
|
40
|
+
* НЕ криптографически стойкий — это просто детерминированный fingerprint.
|
|
41
|
+
* Используется для построения cache key (paramsHash).
|
|
42
|
+
*
|
|
43
|
+
* Возвращает base64url-encoded 16 chars.
|
|
44
|
+
*/
|
|
45
|
+
export declare function paramsHash(params: Record<string, unknown>): string;
|
|
46
|
+
/**
|
|
47
|
+
* Tenant-isolated cache key builder.
|
|
48
|
+
*
|
|
49
|
+
* **Обязательная** проверка: в key всегда присутствует tenant-идентификатор
|
|
50
|
+
* (`appSlug` или `keyPrefix`). Иначе — `Error`.
|
|
51
|
+
*
|
|
52
|
+
* Формат:
|
|
53
|
+
* `mcp:cache:{scope}:{tenant}:{...parts}`
|
|
54
|
+
*
|
|
55
|
+
* Это **единственный** разрешённый путь создания cache key в проекте.
|
|
56
|
+
*
|
|
57
|
+
* @throws Error если tenant=null AND keyPrefix=empty (cross-tenant leak guard)
|
|
58
|
+
*/
|
|
59
|
+
export declare function tenantKey(scope: string, appSlug: string | null, parts: Array<string | number>, fallbackKeyPrefix?: string): string;
|
|
60
|
+
//# sourceMappingURL=policies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../../src/cache/policies.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;CAajB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,cAAc,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,oBAAyB,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAQhG;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAIlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,EAC7B,iBAAiB,CAAC,EAAE,MAAM,GACzB,MAAM,CAoBR"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Cache TTL policies per "tool class".
|
|
4
|
+
*
|
|
5
|
+
* Источник: `todo/error-logs-service/mcp/05-high-load.md` § 2.1.
|
|
6
|
+
*
|
|
7
|
+
* Class имена используются:
|
|
8
|
+
* - как `tool_class` label в Prometheus метриках (`mcp_cache_hit_ratio`);
|
|
9
|
+
* - как первая часть Redis key namespace (`mcp:cache:{class}:...`).
|
|
10
|
+
*
|
|
11
|
+
* Изменения дефолтов делайте здесь, не в `cachedElsClient.ts` — chair pattern.
|
|
12
|
+
*
|
|
13
|
+
* Значения можно override через ENV `MCP_CACHE_TTL_OVERRIDE_{CLASS}=secs`
|
|
14
|
+
* (см. `src/config.ts`).
|
|
15
|
+
*/
|
|
16
|
+
export const CACHE_POLICIES = {
|
|
17
|
+
log_details: 3600,
|
|
18
|
+
traffic_long: 300,
|
|
19
|
+
traffic_short: 60,
|
|
20
|
+
top_messages: 120,
|
|
21
|
+
stats_breakdown: 120,
|
|
22
|
+
search_recent: 15,
|
|
23
|
+
list_apps: 30,
|
|
24
|
+
heatmap: 300,
|
|
25
|
+
histogram: 60,
|
|
26
|
+
baseline: 300,
|
|
27
|
+
version_timeline: 300,
|
|
28
|
+
grouped_errors: 120,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Объединяет дефолтные политики с user overrides из конфига.
|
|
32
|
+
*/
|
|
33
|
+
export function resolvePolicies(overrides = {}) {
|
|
34
|
+
const result = { ...CACHE_POLICIES };
|
|
35
|
+
for (const [k, v] of Object.entries(overrides)) {
|
|
36
|
+
if (typeof v === 'number' && Number.isFinite(v) && v >= 0 && k in result) {
|
|
37
|
+
result[k] = v;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Стабильный sha256 hash от объекта параметров (отсортированные ключи).
|
|
44
|
+
*
|
|
45
|
+
* НЕ криптографически стойкий — это просто детерминированный fingerprint.
|
|
46
|
+
* Используется для построения cache key (paramsHash).
|
|
47
|
+
*
|
|
48
|
+
* Возвращает base64url-encoded 16 chars.
|
|
49
|
+
*/
|
|
50
|
+
export function paramsHash(params) {
|
|
51
|
+
const keys = Object.keys(params).sort();
|
|
52
|
+
const canon = JSON.stringify(params, keys);
|
|
53
|
+
return createHash('sha256').update(canon).digest('base64url').slice(0, 16);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Tenant-isolated cache key builder.
|
|
57
|
+
*
|
|
58
|
+
* **Обязательная** проверка: в key всегда присутствует tenant-идентификатор
|
|
59
|
+
* (`appSlug` или `keyPrefix`). Иначе — `Error`.
|
|
60
|
+
*
|
|
61
|
+
* Формат:
|
|
62
|
+
* `mcp:cache:{scope}:{tenant}:{...parts}`
|
|
63
|
+
*
|
|
64
|
+
* Это **единственный** разрешённый путь создания cache key в проекте.
|
|
65
|
+
*
|
|
66
|
+
* @throws Error если tenant=null AND keyPrefix=empty (cross-tenant leak guard)
|
|
67
|
+
*/
|
|
68
|
+
export function tenantKey(scope, appSlug, parts, fallbackKeyPrefix) {
|
|
69
|
+
// Защита от пустого scope
|
|
70
|
+
if (!scope || /[:\s]/.test(scope)) {
|
|
71
|
+
throw new Error(`Invalid cache scope: "${scope}"`);
|
|
72
|
+
}
|
|
73
|
+
const tenant = (appSlug && appSlug.trim().length > 0 && appSlug) ||
|
|
74
|
+
(fallbackKeyPrefix && fallbackKeyPrefix.trim().length > 0 && `k:${fallbackKeyPrefix}`) ||
|
|
75
|
+
null;
|
|
76
|
+
if (!tenant) {
|
|
77
|
+
// Tenant-isolation violation — это критический баг, см. § 2.1 spec.
|
|
78
|
+
throw new Error('tenantKey() called without appSlug AND without keyPrefix — refusing to build cross-tenant cache key');
|
|
79
|
+
}
|
|
80
|
+
const safeParts = parts.map((p) => sanitizeKeyPart(String(p)));
|
|
81
|
+
return `mcp:cache:${scope}:${tenant}:${safeParts.join(':')}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Удаляет небезопасные для Redis key символы (whitespace, разделители).
|
|
85
|
+
* Заменяет на '_'. Для traceId / hash — операция no-op.
|
|
86
|
+
*/
|
|
87
|
+
function sanitizeKeyPart(s) {
|
|
88
|
+
return s.replace(/[\s:{}\[\]]/g, '_').slice(0, 256);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=policies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies.js","sourceRoot":"","sources":["../../src/cache/policies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,GAAG;IACjB,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,GAAG;IACjB,eAAe,EAAE,GAAG;IACpB,aAAa,EAAE,EAAE;IACjB,SAAS,EAAE,EAAE;IACb,OAAO,EAAE,GAAG;IACZ,SAAS,EAAE,EAAE;IACb,QAAQ,EAAE,GAAG;IACb,gBAAgB,EAAE,GAAG;IACrB,cAAc,EAAE,GAAG;CACX,CAAC;AAQX;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,YAAkC,EAAE;IAClE,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAgC,CAAC;IACnE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;YACzE,MAAM,CAAC,CAAe,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,MAA+B;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACvB,KAAa,EACb,OAAsB,EACtB,KAA6B,EAC7B,iBAA0B;IAE1B,0BAA0B;IAC1B,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,MAAM,GACV,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC;QACjD,CAAC,iBAAiB,IAAI,iBAAiB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACtF,IAAI,CAAC;IAEP,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,oEAAoE;QACpE,MAAM,IAAI,KAAK,CACb,qGAAqG,CACtG,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,OAAO,aAAa,KAAK,IAAI,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
import type { Logger } from 'pino';
|
|
3
|
+
/**
|
|
4
|
+
* Redis singleton-обёртка.
|
|
5
|
+
*
|
|
6
|
+
* Принципы:
|
|
7
|
+
* - Один процесс — один Redis client (singleton).
|
|
8
|
+
* - Reconnect strategy: exponential backoff до 10s.
|
|
9
|
+
* - Graceful degradation: если Redis недоступен, все методы кэша становятся
|
|
10
|
+
* no-op (cache miss → loader fallback). Это решается в `wrapper.ts` —
|
|
11
|
+
* здесь только expose флаг `available`.
|
|
12
|
+
* - `lazyConnect: true` — соединение поднимается асинхронно, не блокирует startup.
|
|
13
|
+
*/
|
|
14
|
+
export interface RedisServiceOptions {
|
|
15
|
+
url: string;
|
|
16
|
+
log?: Logger;
|
|
17
|
+
/** Принудительно использовать готовый client (для тестов с ioredis-mock). */
|
|
18
|
+
client?: Redis;
|
|
19
|
+
/** Disable cache entirely (graceful no-op). */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class RedisService {
|
|
23
|
+
private readonly client;
|
|
24
|
+
private readonly log;
|
|
25
|
+
private connected;
|
|
26
|
+
private readonly disabled;
|
|
27
|
+
constructor(opts: RedisServiceOptions);
|
|
28
|
+
private attachListeners;
|
|
29
|
+
/** True если Redis недоступен или disabled. */
|
|
30
|
+
get unavailable(): boolean;
|
|
31
|
+
get raw(): Redis | null;
|
|
32
|
+
/**
|
|
33
|
+
* Безопасный GET, возвращает null при любых ошибках/недоступности.
|
|
34
|
+
*/
|
|
35
|
+
get(key: string): Promise<string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Безопасный SETEX. Возвращает true если записали, false при ошибке.
|
|
38
|
+
*/
|
|
39
|
+
setex(key: string, ttlSec: number, value: string): Promise<boolean>;
|
|
40
|
+
/** Health-check (ping → PONG). Возвращает true если ответил. */
|
|
41
|
+
ping(): Promise<boolean>;
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Получить (или создать) singleton экземпляр.
|
|
46
|
+
*
|
|
47
|
+
* В тестах используем `setRedisServiceForTests(...)`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getRedisService(opts?: RedisServiceOptions): RedisService;
|
|
50
|
+
export declare function setRedisServiceForTests(service: RedisService | null): void;
|
|
51
|
+
export declare function resetRedisSingleton(): void;
|
|
52
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/cache/redis.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAgB,MAAM,SAAS,CAAC;AAKnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;gBAEvB,IAAI,EAAE,mBAAmB;IAqCrC,OAAO,CAAC,eAAe;IAgBvB,+CAA+C;IAC/C,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,GAAG,IAAI,KAAK,GAAG,IAAI,CAEtB;IAED;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU9C;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWzE,gEAAgE;IAC1D,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAUxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAQxE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAE1E;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import IORedisModule from 'ioredis';
|
|
2
|
+
// ioredis с verbatimModuleSyntax=false и NodeNext: класс находится в default экспорте.
|
|
3
|
+
// `IORedisModule` в зависимости от bundler может быть либо классом, либо { default: class }.
|
|
4
|
+
const IORedis = IORedisModule.default ?? IORedisModule;
|
|
5
|
+
export class RedisService {
|
|
6
|
+
client;
|
|
7
|
+
log;
|
|
8
|
+
connected = false;
|
|
9
|
+
disabled;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.log = opts.log;
|
|
12
|
+
this.disabled = opts.disabled === true;
|
|
13
|
+
if (this.disabled) {
|
|
14
|
+
this.client = null;
|
|
15
|
+
this.log?.info?.('Redis cache disabled via config');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (opts.client) {
|
|
19
|
+
this.client = opts.client;
|
|
20
|
+
this.connected = true;
|
|
21
|
+
this.attachListeners();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const ioOpts = {
|
|
25
|
+
lazyConnect: true,
|
|
26
|
+
maxRetriesPerRequest: 2,
|
|
27
|
+
enableOfflineQueue: false,
|
|
28
|
+
retryStrategy: (times) => Math.min(times * 200, 10_000),
|
|
29
|
+
};
|
|
30
|
+
const client = new IORedis(opts.url, ioOpts);
|
|
31
|
+
this.client = client;
|
|
32
|
+
this.attachListeners();
|
|
33
|
+
// Lazy connect — стартуем коннект в фоне, не валим startup
|
|
34
|
+
client.connect().catch((err) => {
|
|
35
|
+
this.log?.warn?.({ err: err?.message }, 'Redis initial connect failed; cache will be no-op until reconnected');
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
attachListeners() {
|
|
39
|
+
if (!this.client)
|
|
40
|
+
return;
|
|
41
|
+
this.client.on('ready', () => {
|
|
42
|
+
this.connected = true;
|
|
43
|
+
this.log?.info?.('Redis connected');
|
|
44
|
+
});
|
|
45
|
+
this.client.on('end', () => {
|
|
46
|
+
this.connected = false;
|
|
47
|
+
this.log?.warn?.('Redis connection ended');
|
|
48
|
+
});
|
|
49
|
+
this.client.on('error', (err) => {
|
|
50
|
+
this.connected = false;
|
|
51
|
+
this.log?.warn?.({ err: err.message }, 'Redis error');
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** True если Redis недоступен или disabled. */
|
|
55
|
+
get unavailable() {
|
|
56
|
+
return this.disabled || !this.client || !this.connected;
|
|
57
|
+
}
|
|
58
|
+
get raw() {
|
|
59
|
+
return this.client;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Безопасный GET, возвращает null при любых ошибках/недоступности.
|
|
63
|
+
*/
|
|
64
|
+
async get(key) {
|
|
65
|
+
if (this.unavailable || !this.client)
|
|
66
|
+
return null;
|
|
67
|
+
try {
|
|
68
|
+
return await this.client.get(key);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
this.log?.warn?.({ err: err.message, key }, 'Redis GET failed');
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Безопасный SETEX. Возвращает true если записали, false при ошибке.
|
|
77
|
+
*/
|
|
78
|
+
async setex(key, ttlSec, value) {
|
|
79
|
+
if (this.unavailable || !this.client)
|
|
80
|
+
return false;
|
|
81
|
+
try {
|
|
82
|
+
await this.client.setex(key, ttlSec, value);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
this.log?.warn?.({ err: err.message, key }, 'Redis SETEX failed');
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Health-check (ping → PONG). Возвращает true если ответил. */
|
|
91
|
+
async ping() {
|
|
92
|
+
if (this.disabled || !this.client)
|
|
93
|
+
return false;
|
|
94
|
+
try {
|
|
95
|
+
const res = await this.client.ping();
|
|
96
|
+
return res === 'PONG';
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async close() {
|
|
103
|
+
if (!this.client)
|
|
104
|
+
return;
|
|
105
|
+
try {
|
|
106
|
+
await this.client.quit();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
this.client.disconnect();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
let singleton = null;
|
|
114
|
+
/**
|
|
115
|
+
* Получить (или создать) singleton экземпляр.
|
|
116
|
+
*
|
|
117
|
+
* В тестах используем `setRedisServiceForTests(...)`.
|
|
118
|
+
*/
|
|
119
|
+
export function getRedisService(opts) {
|
|
120
|
+
if (!singleton) {
|
|
121
|
+
if (!opts) {
|
|
122
|
+
throw new Error('getRedisService(): первый вызов требует opts');
|
|
123
|
+
}
|
|
124
|
+
singleton = new RedisService(opts);
|
|
125
|
+
}
|
|
126
|
+
return singleton;
|
|
127
|
+
}
|
|
128
|
+
export function setRedisServiceForTests(service) {
|
|
129
|
+
singleton = service;
|
|
130
|
+
}
|
|
131
|
+
export function resetRedisSingleton() {
|
|
132
|
+
singleton = null;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/cache/redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Общие типы для cache-слоя.
|
|
3
|
+
*
|
|
4
|
+
* Phase 4: вводим `RequestContext` — DTO, который Phase 3 (HTTP transport)
|
|
5
|
+
* прокидывает в каждый tool handler и (через декорированный) ElsClient.
|
|
6
|
+
* Содержит tenant-идентификаторы (appSlug / keyPrefix) — это **обязательное**
|
|
7
|
+
* условие tenant-isolation в cache key (см. `05-high-load.md` § 2).
|
|
8
|
+
*
|
|
9
|
+
* До интеграции с Phase 3 контекст можно собрать "вручную" из ELS_API_KEY
|
|
10
|
+
* (см. helper `deriveContextFromKey`) — fallback для stdio-режима, где
|
|
11
|
+
* один процесс = один tenant.
|
|
12
|
+
*/
|
|
13
|
+
export interface RequestContext {
|
|
14
|
+
/** Slug приложения (если известен). Может быть null для master-key. */
|
|
15
|
+
appSlug: string | null;
|
|
16
|
+
/** Первые 16 символов ELS API-ключа — для key-isolation между разными ключами одного app. */
|
|
17
|
+
keyPrefix: string;
|
|
18
|
+
/** Уникальный id запроса (для логов и трейсов). */
|
|
19
|
+
requestId?: string;
|
|
20
|
+
/** id MCP-сессии (Phase 3). */
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
/** Имя tool'а (для метрик / логов). */
|
|
23
|
+
tool?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Хелпер: builds a fallback context из API-key, когда нет HTTP layer.
|
|
27
|
+
* keyPrefix = sha256(apiKey).slice(0,16), appSlug = null.
|
|
28
|
+
*
|
|
29
|
+
* Используется stdio-транспортом и тестами.
|
|
30
|
+
*/
|
|
31
|
+
export declare function deriveContextFromKey(apiKey: string, appSlug?: string | null): RequestContext;
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,IAAW,GAAG,cAAc,CAYlG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Общие типы для cache-слоя.
|
|
3
|
+
*
|
|
4
|
+
* Phase 4: вводим `RequestContext` — DTO, который Phase 3 (HTTP transport)
|
|
5
|
+
* прокидывает в каждый tool handler и (через декорированный) ElsClient.
|
|
6
|
+
* Содержит tenant-идентификаторы (appSlug / keyPrefix) — это **обязательное**
|
|
7
|
+
* условие tenant-isolation в cache key (см. `05-high-load.md` § 2).
|
|
8
|
+
*
|
|
9
|
+
* До интеграции с Phase 3 контекст можно собрать "вручную" из ELS_API_KEY
|
|
10
|
+
* (см. helper `deriveContextFromKey`) — fallback для stdio-режима, где
|
|
11
|
+
* один процесс = один tenant.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Хелпер: builds a fallback context из API-key, когда нет HTTP layer.
|
|
15
|
+
* keyPrefix = sha256(apiKey).slice(0,16), appSlug = null.
|
|
16
|
+
*
|
|
17
|
+
* Используется stdio-транспортом и тестами.
|
|
18
|
+
*/
|
|
19
|
+
export function deriveContextFromKey(apiKey, appSlug = null) {
|
|
20
|
+
// Берём не сам ключ, а его хэш-префикс, чтобы не светить материал в логах/key'ах.
|
|
21
|
+
// crypto доступен в Node 20+ как глобальный объект.
|
|
22
|
+
const enc = new TextEncoder().encode(apiKey);
|
|
23
|
+
let h = 5381;
|
|
24
|
+
for (const b of enc) {
|
|
25
|
+
h = ((h << 5) + h + b) >>> 0;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
appSlug,
|
|
29
|
+
keyPrefix: h.toString(16).padStart(8, '0'),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,UAAyB,IAAI;IAChF,kFAAkF;IAClF,oDAAoD;IACpD,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,GAAG,IAAI,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACpB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO;QACL,OAAO;QACP,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;KAC3C,CAAC;AACJ,CAAC"}
|