driftdetect-mcp 0.4.0 → 0.4.3
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/LICENSE +21 -0
- package/dist/bin/server.d.ts +12 -2
- package/dist/bin/server.d.ts.map +1 -1
- package/dist/bin/server.js +25 -5
- package/dist/bin/server.js.map +1 -1
- package/dist/enterprise-server.d.ts +78 -0
- package/dist/enterprise-server.d.ts.map +1 -0
- package/dist/enterprise-server.js +201 -0
- package/dist/enterprise-server.js.map +1 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/cache.d.ts +86 -0
- package/dist/infrastructure/cache.d.ts.map +1 -0
- package/dist/infrastructure/cache.js +271 -0
- package/dist/infrastructure/cache.js.map +1 -0
- package/dist/infrastructure/cursor-manager.d.ts +86 -0
- package/dist/infrastructure/cursor-manager.d.ts.map +1 -0
- package/dist/infrastructure/cursor-manager.js +175 -0
- package/dist/infrastructure/cursor-manager.js.map +1 -0
- package/dist/infrastructure/error-handler.d.ts +82 -0
- package/dist/infrastructure/error-handler.d.ts.map +1 -0
- package/dist/infrastructure/error-handler.js +226 -0
- package/dist/infrastructure/error-handler.js.map +1 -0
- package/dist/infrastructure/index.d.ts +19 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +26 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/metrics.d.ts +104 -0
- package/dist/infrastructure/metrics.d.ts.map +1 -0
- package/dist/infrastructure/metrics.js +291 -0
- package/dist/infrastructure/metrics.js.map +1 -0
- package/dist/infrastructure/rate-limiter.d.ts +59 -0
- package/dist/infrastructure/rate-limiter.d.ts.map +1 -0
- package/dist/infrastructure/rate-limiter.js +132 -0
- package/dist/infrastructure/rate-limiter.js.map +1 -0
- package/dist/infrastructure/response-builder.d.ts +104 -0
- package/dist/infrastructure/response-builder.d.ts.map +1 -0
- package/dist/infrastructure/response-builder.js +207 -0
- package/dist/infrastructure/response-builder.js.map +1 -0
- package/dist/infrastructure/token-estimator.d.ts +48 -0
- package/dist/infrastructure/token-estimator.d.ts.map +1 -0
- package/dist/infrastructure/token-estimator.js +131 -0
- package/dist/infrastructure/token-estimator.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1074 -17
- package/dist/server.js.map +1 -1
- package/dist/tools/detail/code-examples.d.ts +33 -0
- package/dist/tools/detail/code-examples.d.ts.map +1 -0
- package/dist/tools/detail/code-examples.js +126 -0
- package/dist/tools/detail/code-examples.js.map +1 -0
- package/dist/tools/detail/dna-check.d.ts +32 -0
- package/dist/tools/detail/dna-check.d.ts.map +1 -0
- package/dist/tools/detail/dna-check.js +231 -0
- package/dist/tools/detail/dna-check.js.map +1 -0
- package/dist/tools/detail/dna-profile.d.ts +37 -0
- package/dist/tools/detail/dna-profile.d.ts.map +1 -0
- package/dist/tools/detail/dna-profile.js +101 -0
- package/dist/tools/detail/dna-profile.js.map +1 -0
- package/dist/tools/detail/file-patterns.d.ts +39 -0
- package/dist/tools/detail/file-patterns.d.ts.map +1 -0
- package/dist/tools/detail/file-patterns.js +103 -0
- package/dist/tools/detail/file-patterns.js.map +1 -0
- package/dist/tools/detail/files-list.d.ts +30 -0
- package/dist/tools/detail/files-list.d.ts.map +1 -0
- package/dist/tools/detail/files-list.js +99 -0
- package/dist/tools/detail/files-list.js.map +1 -0
- package/dist/tools/detail/impact-analysis.d.ts +53 -0
- package/dist/tools/detail/impact-analysis.d.ts.map +1 -0
- package/dist/tools/detail/impact-analysis.js +130 -0
- package/dist/tools/detail/impact-analysis.js.map +1 -0
- package/dist/tools/detail/index.d.ts +23 -0
- package/dist/tools/detail/index.d.ts.map +1 -0
- package/dist/tools/detail/index.js +200 -0
- package/dist/tools/detail/index.js.map +1 -0
- package/dist/tools/detail/pattern-get.d.ts +45 -0
- package/dist/tools/detail/pattern-get.d.ts.map +1 -0
- package/dist/tools/detail/pattern-get.js +87 -0
- package/dist/tools/detail/pattern-get.js.map +1 -0
- package/dist/tools/detail/reachability.d.ts +60 -0
- package/dist/tools/detail/reachability.d.ts.map +1 -0
- package/dist/tools/detail/reachability.js +168 -0
- package/dist/tools/detail/reachability.js.map +1 -0
- package/dist/tools/discovery/capabilities.d.ts +28 -0
- package/dist/tools/discovery/capabilities.d.ts.map +1 -0
- package/dist/tools/discovery/capabilities.js +112 -0
- package/dist/tools/discovery/capabilities.js.map +1 -0
- package/dist/tools/discovery/index.d.ts +13 -0
- package/dist/tools/discovery/index.d.ts.map +1 -0
- package/dist/tools/discovery/index.js +30 -0
- package/dist/tools/discovery/index.js.map +1 -0
- package/dist/tools/discovery/projects.d.ts +26 -0
- package/dist/tools/discovery/projects.d.ts.map +1 -0
- package/dist/tools/discovery/projects.js +210 -0
- package/dist/tools/discovery/projects.js.map +1 -0
- package/dist/tools/discovery/status.d.ts +42 -0
- package/dist/tools/discovery/status.d.ts.map +1 -0
- package/dist/tools/discovery/status.js +157 -0
- package/dist/tools/discovery/status.js.map +1 -0
- package/dist/tools/exploration/contracts-list.d.ts +35 -0
- package/dist/tools/exploration/contracts-list.d.ts.map +1 -0
- package/dist/tools/exploration/contracts-list.js +106 -0
- package/dist/tools/exploration/contracts-list.js.map +1 -0
- package/dist/tools/exploration/files-list.d.ts +29 -0
- package/dist/tools/exploration/files-list.d.ts.map +1 -0
- package/dist/tools/exploration/files-list.js +94 -0
- package/dist/tools/exploration/files-list.js.map +1 -0
- package/dist/tools/exploration/index.d.ts +17 -0
- package/dist/tools/exploration/index.d.ts.map +1 -0
- package/dist/tools/exploration/index.js +126 -0
- package/dist/tools/exploration/index.js.map +1 -0
- package/dist/tools/exploration/patterns-list.d.ts +40 -0
- package/dist/tools/exploration/patterns-list.d.ts.map +1 -0
- package/dist/tools/exploration/patterns-list.js +172 -0
- package/dist/tools/exploration/patterns-list.js.map +1 -0
- package/dist/tools/exploration/security-summary.d.ts +49 -0
- package/dist/tools/exploration/security-summary.d.ts.map +1 -0
- package/dist/tools/exploration/security-summary.js +111 -0
- package/dist/tools/exploration/security-summary.js.map +1 -0
- package/dist/tools/exploration/trends.d.ts +49 -0
- package/dist/tools/exploration/trends.d.ts.map +1 -0
- package/dist/tools/exploration/trends.js +147 -0
- package/dist/tools/exploration/trends.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/orchestration/context.d.ts +72 -0
- package/dist/tools/orchestration/context.d.ts.map +1 -0
- package/dist/tools/orchestration/context.js +499 -0
- package/dist/tools/orchestration/context.js.map +1 -0
- package/dist/tools/orchestration/index.d.ts +11 -0
- package/dist/tools/orchestration/index.d.ts.map +1 -0
- package/dist/tools/orchestration/index.js +56 -0
- package/dist/tools/orchestration/index.js.map +1 -0
- package/dist/tools/registry.d.ts +41 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +64 -0
- package/dist/tools/registry.js.map +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Error Handler
|
|
3
|
+
*
|
|
4
|
+
* Provides structured error responses with:
|
|
5
|
+
* - Consistent error codes
|
|
6
|
+
* - Recovery suggestions for AI
|
|
7
|
+
* - Request tracking
|
|
8
|
+
*/
|
|
9
|
+
export var DriftErrorCode;
|
|
10
|
+
(function (DriftErrorCode) {
|
|
11
|
+
// Client errors (4xx equivalent)
|
|
12
|
+
DriftErrorCode["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
|
|
13
|
+
DriftErrorCode["PATTERN_NOT_FOUND"] = "PATTERN_NOT_FOUND";
|
|
14
|
+
DriftErrorCode["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
|
|
15
|
+
DriftErrorCode["INVALID_CURSOR"] = "INVALID_CURSOR";
|
|
16
|
+
DriftErrorCode["INVALID_CATEGORY"] = "INVALID_CATEGORY";
|
|
17
|
+
DriftErrorCode["MISSING_REQUIRED_PARAM"] = "MISSING_REQUIRED_PARAM";
|
|
18
|
+
// Server errors (5xx equivalent)
|
|
19
|
+
DriftErrorCode["SCAN_REQUIRED"] = "SCAN_REQUIRED";
|
|
20
|
+
DriftErrorCode["STORE_UNAVAILABLE"] = "STORE_UNAVAILABLE";
|
|
21
|
+
DriftErrorCode["ANALYSIS_FAILED"] = "ANALYSIS_FAILED";
|
|
22
|
+
DriftErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
23
|
+
// Rate limiting
|
|
24
|
+
DriftErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
|
|
25
|
+
// Resource errors
|
|
26
|
+
DriftErrorCode["CALLGRAPH_NOT_BUILT"] = "CALLGRAPH_NOT_BUILT";
|
|
27
|
+
DriftErrorCode["DNA_NOT_ANALYZED"] = "DNA_NOT_ANALYZED";
|
|
28
|
+
})(DriftErrorCode || (DriftErrorCode = {}));
|
|
29
|
+
export class DriftError extends Error {
|
|
30
|
+
code;
|
|
31
|
+
details;
|
|
32
|
+
recovery;
|
|
33
|
+
constructor(errorDetails) {
|
|
34
|
+
super(errorDetails.message);
|
|
35
|
+
this.name = 'DriftError';
|
|
36
|
+
this.code = errorDetails.code;
|
|
37
|
+
this.details = errorDetails.details;
|
|
38
|
+
this.recovery = errorDetails.recovery;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Convert to MCP error response format
|
|
42
|
+
*/
|
|
43
|
+
toMCPResponse(requestId) {
|
|
44
|
+
const errorResponse = {
|
|
45
|
+
error: {
|
|
46
|
+
code: this.code,
|
|
47
|
+
message: this.message,
|
|
48
|
+
details: this.details,
|
|
49
|
+
recovery: this.recovery,
|
|
50
|
+
},
|
|
51
|
+
meta: {
|
|
52
|
+
requestId: requestId || `err_${Date.now().toString(36)}`,
|
|
53
|
+
timestamp: new Date().toISOString(),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
content: [{
|
|
58
|
+
type: 'text',
|
|
59
|
+
text: JSON.stringify(errorResponse, null, 2),
|
|
60
|
+
}],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Error factory functions for common errors
|
|
67
|
+
*/
|
|
68
|
+
export const Errors = {
|
|
69
|
+
invalidArgument(param, reason, suggestion) {
|
|
70
|
+
return new DriftError({
|
|
71
|
+
code: DriftErrorCode.INVALID_ARGUMENT,
|
|
72
|
+
message: `Invalid argument '${param}': ${reason}`,
|
|
73
|
+
details: { param, reason },
|
|
74
|
+
recovery: suggestion ? { suggestion } : undefined,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
missingRequired(param) {
|
|
78
|
+
return new DriftError({
|
|
79
|
+
code: DriftErrorCode.MISSING_REQUIRED_PARAM,
|
|
80
|
+
message: `Missing required parameter: ${param}`,
|
|
81
|
+
details: { param },
|
|
82
|
+
recovery: {
|
|
83
|
+
suggestion: `Provide the '${param}' parameter`,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
missingParameter(param) {
|
|
88
|
+
return this.missingRequired(param);
|
|
89
|
+
},
|
|
90
|
+
notFound(type, id) {
|
|
91
|
+
if (type === 'pattern') {
|
|
92
|
+
return this.patternNotFound(id);
|
|
93
|
+
}
|
|
94
|
+
if (type === 'file') {
|
|
95
|
+
return this.fileNotFound(id);
|
|
96
|
+
}
|
|
97
|
+
return new DriftError({
|
|
98
|
+
code: DriftErrorCode.FILE_NOT_FOUND,
|
|
99
|
+
message: `${type} not found: ${id}`,
|
|
100
|
+
details: { type, id },
|
|
101
|
+
recovery: {
|
|
102
|
+
suggestion: `Check that the ${type} exists`,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
invalidParameter(param, reason) {
|
|
107
|
+
return this.invalidArgument(param, reason);
|
|
108
|
+
},
|
|
109
|
+
patternNotFound(patternId) {
|
|
110
|
+
return new DriftError({
|
|
111
|
+
code: DriftErrorCode.PATTERN_NOT_FOUND,
|
|
112
|
+
message: `Pattern not found: ${patternId}`,
|
|
113
|
+
details: { patternId },
|
|
114
|
+
recovery: {
|
|
115
|
+
suggestion: 'Use drift_patterns_list to find valid pattern IDs',
|
|
116
|
+
alternativeTools: ['drift_patterns_list', 'drift_status'],
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
fileNotFound(path) {
|
|
121
|
+
return new DriftError({
|
|
122
|
+
code: DriftErrorCode.FILE_NOT_FOUND,
|
|
123
|
+
message: `File not found: ${path}`,
|
|
124
|
+
details: { path },
|
|
125
|
+
recovery: {
|
|
126
|
+
suggestion: 'Check the file path is relative to project root',
|
|
127
|
+
alternativeTools: ['drift_files_list'],
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
invalidCursor() {
|
|
132
|
+
return new DriftError({
|
|
133
|
+
code: DriftErrorCode.INVALID_CURSOR,
|
|
134
|
+
message: 'Invalid or expired pagination cursor',
|
|
135
|
+
recovery: {
|
|
136
|
+
suggestion: 'Start pagination from the beginning without a cursor',
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
invalidCategory(category, validCategories) {
|
|
141
|
+
return new DriftError({
|
|
142
|
+
code: DriftErrorCode.INVALID_CATEGORY,
|
|
143
|
+
message: `Invalid category: ${category}`,
|
|
144
|
+
details: {
|
|
145
|
+
provided: category,
|
|
146
|
+
valid: validCategories,
|
|
147
|
+
},
|
|
148
|
+
recovery: {
|
|
149
|
+
suggestion: `Use one of: ${validCategories.join(', ')}`,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
scanRequired() {
|
|
154
|
+
return new DriftError({
|
|
155
|
+
code: DriftErrorCode.SCAN_REQUIRED,
|
|
156
|
+
message: 'No pattern data found. The codebase needs to be scanned first.',
|
|
157
|
+
recovery: {
|
|
158
|
+
suggestion: "Run 'drift scan' in the project root to analyze patterns",
|
|
159
|
+
command: 'drift scan',
|
|
160
|
+
alternativeTools: ['drift_status'],
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
callgraphNotBuilt() {
|
|
165
|
+
return new DriftError({
|
|
166
|
+
code: DriftErrorCode.CALLGRAPH_NOT_BUILT,
|
|
167
|
+
message: 'Call graph has not been built yet.',
|
|
168
|
+
recovery: {
|
|
169
|
+
suggestion: "Use drift_callgraph with action='build' first",
|
|
170
|
+
alternativeTools: ['drift_callgraph'],
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
dnaNotAnalyzed() {
|
|
175
|
+
return new DriftError({
|
|
176
|
+
code: DriftErrorCode.DNA_NOT_ANALYZED,
|
|
177
|
+
message: 'Styling DNA has not been analyzed yet.',
|
|
178
|
+
recovery: {
|
|
179
|
+
suggestion: "Run 'drift dna scan' to analyze styling patterns",
|
|
180
|
+
command: 'drift dna scan',
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
rateLimited(retryAfterMs) {
|
|
185
|
+
return new DriftError({
|
|
186
|
+
code: DriftErrorCode.RATE_LIMITED,
|
|
187
|
+
message: 'Rate limit exceeded. Please wait before making more requests.',
|
|
188
|
+
recovery: {
|
|
189
|
+
suggestion: 'Wait before retrying',
|
|
190
|
+
retryAfterMs,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
internal(message, details) {
|
|
195
|
+
return new DriftError({
|
|
196
|
+
code: DriftErrorCode.INTERNAL_ERROR,
|
|
197
|
+
message: `Internal error: ${message}`,
|
|
198
|
+
details,
|
|
199
|
+
recovery: {
|
|
200
|
+
suggestion: 'This is an unexpected error. Please report it if it persists.',
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
custom(code, message, alternativeTools) {
|
|
205
|
+
return new DriftError({
|
|
206
|
+
code: code,
|
|
207
|
+
message,
|
|
208
|
+
recovery: alternativeTools ? {
|
|
209
|
+
suggestion: message,
|
|
210
|
+
alternativeTools,
|
|
211
|
+
} : undefined,
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Error handler middleware
|
|
217
|
+
*/
|
|
218
|
+
export function handleError(error, requestId) {
|
|
219
|
+
if (error instanceof DriftError) {
|
|
220
|
+
return error.toMCPResponse(requestId);
|
|
221
|
+
}
|
|
222
|
+
// Convert unknown errors to DriftError
|
|
223
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
224
|
+
return Errors.internal(message).toMCPResponse(requestId);
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/infrastructure/error-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise MCP Infrastructure
|
|
3
|
+
*
|
|
4
|
+
* Core infrastructure components for building enterprise-grade MCP servers:
|
|
5
|
+
* - Response building with token budgets
|
|
6
|
+
* - Cursor-based pagination
|
|
7
|
+
* - Structured error handling
|
|
8
|
+
* - Caching with invalidation
|
|
9
|
+
* - Rate limiting
|
|
10
|
+
* - Metrics collection
|
|
11
|
+
*/
|
|
12
|
+
export { ResponseBuilder, createResponseBuilder, type MCPResponse, type MCPResponseMeta, type PaginationInfo, type ResponseHints, type ResponseBuilderConfig, } from './response-builder.js';
|
|
13
|
+
export { TokenEstimator, tokenEstimator, type TokenEstimate, } from './token-estimator.js';
|
|
14
|
+
export { CursorManager, cursorManager, createCursor, parseCursor, type CursorData, type CursorConfig, } from './cursor-manager.js';
|
|
15
|
+
export { DriftError, DriftErrorCode, Errors, handleError, type DriftErrorDetails, type RecoveryHint, } from './error-handler.js';
|
|
16
|
+
export { ResponseCache, createCache, type CachedResponse, type CacheConfig, } from './cache.js';
|
|
17
|
+
export { RateLimiter, rateLimiter, type RateLimitConfig, } from './rate-limiter.js';
|
|
18
|
+
export { MetricsCollector, metrics, type Metric, type MetricLabels, type HistogramBuckets, } from './metrics.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/infrastructure/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,EACX,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,UAAU,EACV,cAAc,EACd,MAAM,EACN,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,aAAa,EACb,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,WAAW,EACX,WAAW,EACX,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,gBAAgB,EAChB,OAAO,EACP,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise MCP Infrastructure
|
|
3
|
+
*
|
|
4
|
+
* Core infrastructure components for building enterprise-grade MCP servers:
|
|
5
|
+
* - Response building with token budgets
|
|
6
|
+
* - Cursor-based pagination
|
|
7
|
+
* - Structured error handling
|
|
8
|
+
* - Caching with invalidation
|
|
9
|
+
* - Rate limiting
|
|
10
|
+
* - Metrics collection
|
|
11
|
+
*/
|
|
12
|
+
// Response Building
|
|
13
|
+
export { ResponseBuilder, createResponseBuilder, } from './response-builder.js';
|
|
14
|
+
// Token Estimation
|
|
15
|
+
export { TokenEstimator, tokenEstimator, } from './token-estimator.js';
|
|
16
|
+
// Cursor Pagination
|
|
17
|
+
export { CursorManager, cursorManager, createCursor, parseCursor, } from './cursor-manager.js';
|
|
18
|
+
// Error Handling
|
|
19
|
+
export { DriftError, DriftErrorCode, Errors, handleError, } from './error-handler.js';
|
|
20
|
+
// Caching
|
|
21
|
+
export { ResponseCache, createCache, } from './cache.js';
|
|
22
|
+
// Rate Limiting
|
|
23
|
+
export { RateLimiter, rateLimiter, } from './rate-limiter.js';
|
|
24
|
+
// Metrics
|
|
25
|
+
export { MetricsCollector, metrics, } from './metrics.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/infrastructure/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,oBAAoB;AACpB,OAAO,EACL,eAAe,EACf,qBAAqB,GAMtB,MAAM,uBAAuB,CAAC;AAE/B,mBAAmB;AACnB,OAAO,EACL,cAAc,EACd,cAAc,GAEf,MAAM,sBAAsB,CAAC;AAE9B,oBAAoB;AACpB,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,GAGZ,MAAM,qBAAqB,CAAC;AAE7B,iBAAiB;AACjB,OAAO,EACL,UAAU,EACV,cAAc,EACd,MAAM,EACN,WAAW,GAGZ,MAAM,oBAAoB,CAAC;AAE5B,UAAU;AACV,OAAO,EACL,aAAa,EACb,WAAW,GAGZ,MAAM,YAAY,CAAC;AAEpB,gBAAgB;AAChB,OAAO,EACL,WAAW,EACX,WAAW,GAEZ,MAAM,mBAAmB,CAAC;AAE3B,UAAU;AACV,OAAO,EACL,gBAAgB,EAChB,OAAO,GAIR,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Collector
|
|
3
|
+
*
|
|
4
|
+
* Collects operational metrics for observability:
|
|
5
|
+
* - Request counts and durations
|
|
6
|
+
* - Cache hit/miss rates
|
|
7
|
+
* - Token usage estimates
|
|
8
|
+
* - Error rates
|
|
9
|
+
*/
|
|
10
|
+
export interface MetricLabels {
|
|
11
|
+
tool?: string;
|
|
12
|
+
success?: string;
|
|
13
|
+
cached?: string;
|
|
14
|
+
errorCode?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface Metric {
|
|
17
|
+
name: string;
|
|
18
|
+
type: 'counter' | 'gauge' | 'histogram';
|
|
19
|
+
value: number;
|
|
20
|
+
labels: MetricLabels;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
}
|
|
23
|
+
export interface HistogramBuckets {
|
|
24
|
+
le_10: number;
|
|
25
|
+
le_50: number;
|
|
26
|
+
le_100: number;
|
|
27
|
+
le_250: number;
|
|
28
|
+
le_500: number;
|
|
29
|
+
le_1000: number;
|
|
30
|
+
le_2500: number;
|
|
31
|
+
le_5000: number;
|
|
32
|
+
le_inf: number;
|
|
33
|
+
sum: number;
|
|
34
|
+
count: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class MetricsCollector {
|
|
37
|
+
private counters;
|
|
38
|
+
private gauges;
|
|
39
|
+
private histograms;
|
|
40
|
+
private metrics;
|
|
41
|
+
private maxMetrics;
|
|
42
|
+
/**
|
|
43
|
+
* Increment a counter
|
|
44
|
+
*/
|
|
45
|
+
increment(name: string, labels?: MetricLabels, value?: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Set a gauge value
|
|
48
|
+
*/
|
|
49
|
+
gauge(name: string, value: number, labels?: MetricLabels): void;
|
|
50
|
+
/**
|
|
51
|
+
* Record a histogram observation
|
|
52
|
+
*/
|
|
53
|
+
observe(name: string, value: number, labels?: MetricLabels): void;
|
|
54
|
+
/**
|
|
55
|
+
* Record a request
|
|
56
|
+
*/
|
|
57
|
+
recordRequest(tool: string, durationMs: number, success: boolean, cached?: boolean): void;
|
|
58
|
+
/**
|
|
59
|
+
* Record an error
|
|
60
|
+
*/
|
|
61
|
+
recordError(tool: string, errorCode: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Record token estimate
|
|
64
|
+
*/
|
|
65
|
+
recordTokens(tool: string, tokens: number): void;
|
|
66
|
+
/**
|
|
67
|
+
* Record patterns queried
|
|
68
|
+
*/
|
|
69
|
+
recordPatternsQueried(count: number, tool: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Get all metrics
|
|
72
|
+
*/
|
|
73
|
+
getMetrics(): Metric[];
|
|
74
|
+
/**
|
|
75
|
+
* Get summary statistics
|
|
76
|
+
*/
|
|
77
|
+
getSummary(): {
|
|
78
|
+
totalRequests: number;
|
|
79
|
+
totalErrors: number;
|
|
80
|
+
cacheHitRate: number;
|
|
81
|
+
avgDurationMs: number;
|
|
82
|
+
avgTokens: number;
|
|
83
|
+
requestsByTool: Record<string, number>;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Export metrics in Prometheus format
|
|
87
|
+
*/
|
|
88
|
+
toPrometheus(): string;
|
|
89
|
+
/**
|
|
90
|
+
* Clear all metrics
|
|
91
|
+
*/
|
|
92
|
+
clear(): void;
|
|
93
|
+
private makeKey;
|
|
94
|
+
private parseKey;
|
|
95
|
+
private formatLabels;
|
|
96
|
+
private addLabel;
|
|
97
|
+
private createEmptyBuckets;
|
|
98
|
+
private recordMetric;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Singleton instance for convenience
|
|
102
|
+
*/
|
|
103
|
+
export declare const metrics: MetricsCollector;
|
|
104
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/infrastructure/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAiB;IAEnC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,YAAiB,EAAE,KAAK,GAAE,MAAU,GAAG,IAAI;IAc3E;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,YAAiB,GAAG,IAAI;IAanE;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,YAAiB,GAAG,IAAI;IAiCrE;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAgBhG;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIlD;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhD;;OAEG;IACH,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxD;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAItB;;OAEG;IACH,UAAU,IAAI;QACZ,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC;IA2DD;;OAEG;IACH,YAAY,IAAI,MAAM;IAsCtB;;OAEG;IACH,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,QAAQ;IAqBhB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,YAAY;CAQrB;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Collector
|
|
3
|
+
*
|
|
4
|
+
* Collects operational metrics for observability:
|
|
5
|
+
* - Request counts and durations
|
|
6
|
+
* - Cache hit/miss rates
|
|
7
|
+
* - Token usage estimates
|
|
8
|
+
* - Error rates
|
|
9
|
+
*/
|
|
10
|
+
export class MetricsCollector {
|
|
11
|
+
counters = new Map();
|
|
12
|
+
gauges = new Map();
|
|
13
|
+
histograms = new Map();
|
|
14
|
+
metrics = [];
|
|
15
|
+
maxMetrics = 10000;
|
|
16
|
+
/**
|
|
17
|
+
* Increment a counter
|
|
18
|
+
*/
|
|
19
|
+
increment(name, labels = {}, value = 1) {
|
|
20
|
+
const key = this.makeKey(name, labels);
|
|
21
|
+
const current = this.counters.get(key) ?? 0;
|
|
22
|
+
this.counters.set(key, current + value);
|
|
23
|
+
this.recordMetric({
|
|
24
|
+
name,
|
|
25
|
+
type: 'counter',
|
|
26
|
+
value: current + value,
|
|
27
|
+
labels,
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Set a gauge value
|
|
33
|
+
*/
|
|
34
|
+
gauge(name, value, labels = {}) {
|
|
35
|
+
const key = this.makeKey(name, labels);
|
|
36
|
+
this.gauges.set(key, value);
|
|
37
|
+
this.recordMetric({
|
|
38
|
+
name,
|
|
39
|
+
type: 'gauge',
|
|
40
|
+
value,
|
|
41
|
+
labels,
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Record a histogram observation
|
|
47
|
+
*/
|
|
48
|
+
observe(name, value, labels = {}) {
|
|
49
|
+
const key = this.makeKey(name, labels);
|
|
50
|
+
let buckets = this.histograms.get(key);
|
|
51
|
+
if (!buckets) {
|
|
52
|
+
buckets = this.createEmptyBuckets();
|
|
53
|
+
this.histograms.set(key, buckets);
|
|
54
|
+
}
|
|
55
|
+
// Update buckets
|
|
56
|
+
if (value <= 10)
|
|
57
|
+
buckets.le_10++;
|
|
58
|
+
if (value <= 50)
|
|
59
|
+
buckets.le_50++;
|
|
60
|
+
if (value <= 100)
|
|
61
|
+
buckets.le_100++;
|
|
62
|
+
if (value <= 250)
|
|
63
|
+
buckets.le_250++;
|
|
64
|
+
if (value <= 500)
|
|
65
|
+
buckets.le_500++;
|
|
66
|
+
if (value <= 1000)
|
|
67
|
+
buckets.le_1000++;
|
|
68
|
+
if (value <= 2500)
|
|
69
|
+
buckets.le_2500++;
|
|
70
|
+
if (value <= 5000)
|
|
71
|
+
buckets.le_5000++;
|
|
72
|
+
buckets.le_inf++;
|
|
73
|
+
buckets.sum += value;
|
|
74
|
+
buckets.count++;
|
|
75
|
+
this.recordMetric({
|
|
76
|
+
name,
|
|
77
|
+
type: 'histogram',
|
|
78
|
+
value,
|
|
79
|
+
labels,
|
|
80
|
+
timestamp: Date.now(),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Convenience methods for common metrics
|
|
84
|
+
/**
|
|
85
|
+
* Record a request
|
|
86
|
+
*/
|
|
87
|
+
recordRequest(tool, durationMs, success, cached = false) {
|
|
88
|
+
this.increment('drift_mcp_requests_total', {
|
|
89
|
+
tool,
|
|
90
|
+
success: String(success),
|
|
91
|
+
cached: String(cached),
|
|
92
|
+
});
|
|
93
|
+
this.observe('drift_mcp_request_duration_ms', durationMs, { tool });
|
|
94
|
+
if (cached) {
|
|
95
|
+
this.increment('drift_mcp_cache_hits_total', { tool });
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.increment('drift_mcp_cache_misses_total', { tool });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Record an error
|
|
103
|
+
*/
|
|
104
|
+
recordError(tool, errorCode) {
|
|
105
|
+
this.increment('drift_mcp_errors_total', { tool, errorCode });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Record token estimate
|
|
109
|
+
*/
|
|
110
|
+
recordTokens(tool, tokens) {
|
|
111
|
+
this.observe('drift_mcp_response_tokens', tokens, { tool });
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Record patterns queried
|
|
115
|
+
*/
|
|
116
|
+
recordPatternsQueried(count, tool) {
|
|
117
|
+
this.increment('drift_mcp_patterns_queried_total', { tool }, count);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get all metrics
|
|
121
|
+
*/
|
|
122
|
+
getMetrics() {
|
|
123
|
+
return [...this.metrics];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get summary statistics
|
|
127
|
+
*/
|
|
128
|
+
getSummary() {
|
|
129
|
+
let totalRequests = 0;
|
|
130
|
+
let totalErrors = 0;
|
|
131
|
+
let cacheHits = 0;
|
|
132
|
+
let cacheMisses = 0;
|
|
133
|
+
const requestsByTool = {};
|
|
134
|
+
// Count requests
|
|
135
|
+
for (const [key, value] of this.counters) {
|
|
136
|
+
if (key.startsWith('drift_mcp_requests_total')) {
|
|
137
|
+
totalRequests += value;
|
|
138
|
+
// Extract tool from key
|
|
139
|
+
const toolMatch = key.match(/tool:(\w+)/);
|
|
140
|
+
if (toolMatch) {
|
|
141
|
+
const tool = toolMatch[1];
|
|
142
|
+
requestsByTool[tool] = (requestsByTool[tool] ?? 0) + value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (key.startsWith('drift_mcp_errors_total')) {
|
|
146
|
+
totalErrors += value;
|
|
147
|
+
}
|
|
148
|
+
if (key.startsWith('drift_mcp_cache_hits_total')) {
|
|
149
|
+
cacheHits += value;
|
|
150
|
+
}
|
|
151
|
+
if (key.startsWith('drift_mcp_cache_misses_total')) {
|
|
152
|
+
cacheMisses += value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Calculate averages from histograms
|
|
156
|
+
let totalDuration = 0;
|
|
157
|
+
let durationCount = 0;
|
|
158
|
+
let totalTokens = 0;
|
|
159
|
+
let tokenCount = 0;
|
|
160
|
+
for (const [key, buckets] of this.histograms) {
|
|
161
|
+
if (key.startsWith('drift_mcp_request_duration_ms')) {
|
|
162
|
+
totalDuration += buckets.sum;
|
|
163
|
+
durationCount += buckets.count;
|
|
164
|
+
}
|
|
165
|
+
if (key.startsWith('drift_mcp_response_tokens')) {
|
|
166
|
+
totalTokens += buckets.sum;
|
|
167
|
+
tokenCount += buckets.count;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
totalRequests,
|
|
172
|
+
totalErrors,
|
|
173
|
+
cacheHitRate: cacheHits + cacheMisses > 0
|
|
174
|
+
? cacheHits / (cacheHits + cacheMisses)
|
|
175
|
+
: 0,
|
|
176
|
+
avgDurationMs: durationCount > 0 ? totalDuration / durationCount : 0,
|
|
177
|
+
avgTokens: tokenCount > 0 ? totalTokens / tokenCount : 0,
|
|
178
|
+
requestsByTool,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Export metrics in Prometheus format
|
|
183
|
+
*/
|
|
184
|
+
toPrometheus() {
|
|
185
|
+
const lines = [];
|
|
186
|
+
// Counters
|
|
187
|
+
for (const [key, value] of this.counters) {
|
|
188
|
+
const { name, labels } = this.parseKey(key);
|
|
189
|
+
const labelStr = this.formatLabels(labels);
|
|
190
|
+
lines.push(`${name}${labelStr} ${value}`);
|
|
191
|
+
}
|
|
192
|
+
// Gauges
|
|
193
|
+
for (const [key, value] of this.gauges) {
|
|
194
|
+
const { name, labels } = this.parseKey(key);
|
|
195
|
+
const labelStr = this.formatLabels(labels);
|
|
196
|
+
lines.push(`${name}${labelStr} ${value}`);
|
|
197
|
+
}
|
|
198
|
+
// Histograms
|
|
199
|
+
for (const [key, buckets] of this.histograms) {
|
|
200
|
+
const { name, labels } = this.parseKey(key);
|
|
201
|
+
const labelStr = this.formatLabels(labels);
|
|
202
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '10')} ${buckets.le_10}`);
|
|
203
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '50')} ${buckets.le_50}`);
|
|
204
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '100')} ${buckets.le_100}`);
|
|
205
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '250')} ${buckets.le_250}`);
|
|
206
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '500')} ${buckets.le_500}`);
|
|
207
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '1000')} ${buckets.le_1000}`);
|
|
208
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '2500')} ${buckets.le_2500}`);
|
|
209
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '5000')} ${buckets.le_5000}`);
|
|
210
|
+
lines.push(`${name}_bucket${this.addLabel(labelStr, 'le', '+Inf')} ${buckets.le_inf}`);
|
|
211
|
+
lines.push(`${name}_sum${labelStr} ${buckets.sum}`);
|
|
212
|
+
lines.push(`${name}_count${labelStr} ${buckets.count}`);
|
|
213
|
+
}
|
|
214
|
+
return lines.join('\n');
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Clear all metrics
|
|
218
|
+
*/
|
|
219
|
+
clear() {
|
|
220
|
+
this.counters.clear();
|
|
221
|
+
this.gauges.clear();
|
|
222
|
+
this.histograms.clear();
|
|
223
|
+
this.metrics = [];
|
|
224
|
+
}
|
|
225
|
+
// Private methods
|
|
226
|
+
makeKey(name, labels) {
|
|
227
|
+
const labelParts = Object.entries(labels)
|
|
228
|
+
.filter(([, v]) => v !== undefined)
|
|
229
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
230
|
+
.map(([k, v]) => `${k}:${v}`);
|
|
231
|
+
return labelParts.length > 0
|
|
232
|
+
? `${name}{${labelParts.join(',')}}`
|
|
233
|
+
: name;
|
|
234
|
+
}
|
|
235
|
+
parseKey(key) {
|
|
236
|
+
const match = key.match(/^([^{]+)(?:\{(.+)\})?$/);
|
|
237
|
+
if (!match) {
|
|
238
|
+
return { name: key, labels: {} };
|
|
239
|
+
}
|
|
240
|
+
const name = match[1];
|
|
241
|
+
const labels = {};
|
|
242
|
+
if (match[2]) {
|
|
243
|
+
for (const part of match[2].split(',')) {
|
|
244
|
+
const [k, v] = part.split(':');
|
|
245
|
+
if (k && v) {
|
|
246
|
+
labels[k] = v;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return { name, labels };
|
|
251
|
+
}
|
|
252
|
+
formatLabels(labels) {
|
|
253
|
+
const parts = Object.entries(labels)
|
|
254
|
+
.filter(([, v]) => v !== undefined)
|
|
255
|
+
.map(([k, v]) => `${k}="${v}"`);
|
|
256
|
+
return parts.length > 0 ? `{${parts.join(',')}}` : '';
|
|
257
|
+
}
|
|
258
|
+
addLabel(labelStr, key, value) {
|
|
259
|
+
if (labelStr === '') {
|
|
260
|
+
return `{${key}="${value}"}`;
|
|
261
|
+
}
|
|
262
|
+
return labelStr.replace('}', `,${key}="${value}"}`);
|
|
263
|
+
}
|
|
264
|
+
createEmptyBuckets() {
|
|
265
|
+
return {
|
|
266
|
+
le_10: 0,
|
|
267
|
+
le_50: 0,
|
|
268
|
+
le_100: 0,
|
|
269
|
+
le_250: 0,
|
|
270
|
+
le_500: 0,
|
|
271
|
+
le_1000: 0,
|
|
272
|
+
le_2500: 0,
|
|
273
|
+
le_5000: 0,
|
|
274
|
+
le_inf: 0,
|
|
275
|
+
sum: 0,
|
|
276
|
+
count: 0,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
recordMetric(metric) {
|
|
280
|
+
this.metrics.push(metric);
|
|
281
|
+
// Trim old metrics if over limit
|
|
282
|
+
if (this.metrics.length > this.maxMetrics) {
|
|
283
|
+
this.metrics = this.metrics.slice(-this.maxMetrics / 2);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Singleton instance for convenience
|
|
289
|
+
*/
|
|
290
|
+
export const metrics = new MetricsCollector();
|
|
291
|
+
//# sourceMappingURL=metrics.js.map
|