observability-toolkit 1.8.2 → 1.8.5
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 +60 -0
- package/dist/backends/index.d.ts +43 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +41 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/index.test.d.ts +5 -0
- package/dist/backends/index.test.d.ts.map +1 -0
- package/dist/backends/index.test.js +156 -0
- package/dist/backends/index.test.js.map +1 -0
- package/dist/backends/local-jsonl-boolean-search.test.js +15 -12
- package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
- package/dist/backends/local-jsonl-cache.test.d.ts +2 -0
- package/dist/backends/local-jsonl-cache.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-cache.test.js +295 -0
- package/dist/backends/local-jsonl-cache.test.js.map +1 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.d.ts +2 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.js +180 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.js.map +1 -0
- package/dist/backends/local-jsonl-export.test.d.ts +2 -0
- package/dist/backends/local-jsonl-export.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-export.test.js +704 -0
- package/dist/backends/local-jsonl-export.test.js.map +1 -0
- package/dist/backends/local-jsonl-index.test.d.ts +2 -0
- package/dist/backends/local-jsonl-index.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-index.test.js +554 -0
- package/dist/backends/local-jsonl-index.test.js.map +1 -0
- package/dist/backends/local-jsonl-logs.test.d.ts +2 -0
- package/dist/backends/local-jsonl-logs.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-logs.test.js +612 -0
- package/dist/backends/local-jsonl-logs.test.js.map +1 -0
- package/dist/backends/local-jsonl-metrics.test.d.ts +2 -0
- package/dist/backends/local-jsonl-metrics.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-metrics.test.js +876 -0
- package/dist/backends/local-jsonl-metrics.test.js.map +1 -0
- package/dist/backends/local-jsonl-traces.test.d.ts +2 -0
- package/dist/backends/local-jsonl-traces.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-traces.test.js +1729 -0
- package/dist/backends/local-jsonl-traces.test.js.map +1 -0
- package/dist/backends/local-jsonl.d.ts +9 -0
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +348 -227
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/local-jsonl.test.js +290 -21
- package/dist/backends/local-jsonl.test.js.map +1 -1
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts +6 -0
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-circuit-breaker.test.js +548 -0
- package/dist/backends/signoz-api-circuit-breaker.test.js.map +1 -0
- package/dist/backends/signoz-api-rate-limiter.test.d.ts +6 -0
- package/dist/backends/signoz-api-rate-limiter.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-rate-limiter.test.js +389 -0
- package/dist/backends/signoz-api-rate-limiter.test.js.map +1 -0
- package/dist/backends/signoz-api-ssrf.test.d.ts +6 -0
- package/dist/backends/signoz-api-ssrf.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-ssrf.test.js +216 -0
- package/dist/backends/signoz-api-ssrf.test.js.map +1 -0
- package/dist/backends/signoz-api-test-helpers.d.ts +80 -0
- package/dist/backends/signoz-api-test-helpers.d.ts.map +1 -0
- package/dist/backends/signoz-api-test-helpers.js +79 -0
- package/dist/backends/signoz-api-test-helpers.js.map +1 -0
- package/dist/backends/signoz-api.d.ts +16 -0
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.js +71 -9
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.d.ts +9 -0
- package/dist/backends/signoz-api.test.d.ts.map +1 -1
- package/dist/backends/signoz-api.test.js +14 -1027
- package/dist/backends/signoz-api.test.js.map +1 -1
- package/dist/lib/cache.d.ts +47 -1
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +40 -3
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/circuit-breaker.d.ts +83 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +125 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/circuit-breaker.test.d.ts +2 -0
- package/dist/lib/circuit-breaker.test.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.test.js +263 -0
- package/dist/lib/circuit-breaker.test.js.map +1 -0
- package/dist/lib/constants-symlink.test.d.ts +12 -0
- package/dist/lib/constants-symlink.test.d.ts.map +1 -0
- package/dist/lib/constants-symlink.test.js +357 -0
- package/dist/lib/constants-symlink.test.js.map +1 -0
- package/dist/lib/constants.d.ts +43 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +154 -24
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.js +156 -7
- package/dist/lib/constants.test.js.map +1 -1
- package/dist/lib/edge-cases.test.d.ts +11 -0
- package/dist/lib/edge-cases.test.d.ts.map +1 -0
- package/dist/lib/edge-cases.test.js +634 -0
- package/dist/lib/edge-cases.test.js.map +1 -0
- package/dist/lib/error-sanitizer.d.ts.map +1 -1
- package/dist/lib/error-sanitizer.js +62 -26
- package/dist/lib/error-sanitizer.js.map +1 -1
- package/dist/lib/error-sanitizer.test.js +186 -0
- package/dist/lib/error-sanitizer.test.js.map +1 -1
- package/dist/lib/error-types.d.ts +54 -0
- package/dist/lib/error-types.d.ts.map +1 -0
- package/dist/lib/error-types.js +154 -0
- package/dist/lib/error-types.js.map +1 -0
- package/dist/lib/error-types.test.d.ts +2 -0
- package/dist/lib/error-types.test.d.ts.map +1 -0
- package/dist/lib/error-types.test.js +196 -0
- package/dist/lib/error-types.test.js.map +1 -0
- package/dist/lib/file-utils.test.js +3 -3
- package/dist/lib/file-utils.test.js.map +1 -1
- package/dist/lib/indexer.test.js +157 -24
- package/dist/lib/indexer.test.js.map +1 -1
- package/dist/lib/input-validator.d.ts +17 -0
- package/dist/lib/input-validator.d.ts.map +1 -1
- package/dist/lib/input-validator.fuzz.test.d.ts +12 -0
- package/dist/lib/input-validator.fuzz.test.d.ts.map +1 -0
- package/dist/lib/input-validator.fuzz.test.js +290 -0
- package/dist/lib/input-validator.fuzz.test.js.map +1 -0
- package/dist/lib/input-validator.js +62 -3
- package/dist/lib/input-validator.js.map +1 -1
- package/dist/lib/input-validator.test.js +129 -1
- package/dist/lib/input-validator.test.js.map +1 -1
- package/dist/lib/logger.d.ts +46 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +81 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/logger.test.d.ts +2 -0
- package/dist/lib/logger.test.d.ts.map +1 -0
- package/dist/lib/logger.test.js +122 -0
- package/dist/lib/logger.test.js.map +1 -0
- package/dist/lib/query-sanitizer.d.ts +51 -3
- package/dist/lib/query-sanitizer.d.ts.map +1 -1
- package/dist/lib/query-sanitizer.js +105 -31
- package/dist/lib/query-sanitizer.js.map +1 -1
- package/dist/lib/query-sanitizer.test.js +102 -1
- package/dist/lib/query-sanitizer.test.js.map +1 -1
- package/dist/lib/server-utils.d.ts +88 -0
- package/dist/lib/server-utils.d.ts.map +1 -0
- package/dist/lib/server-utils.js +173 -0
- package/dist/lib/server-utils.js.map +1 -0
- package/dist/lib/shared-schemas.d.ts +81 -0
- package/dist/lib/shared-schemas.d.ts.map +1 -0
- package/dist/lib/shared-schemas.js +80 -0
- package/dist/lib/shared-schemas.js.map +1 -0
- package/dist/lib/shared-schemas.test.d.ts +5 -0
- package/dist/lib/shared-schemas.test.d.ts.map +1 -0
- package/dist/lib/shared-schemas.test.js +106 -0
- package/dist/lib/shared-schemas.test.js.map +1 -0
- package/dist/lib/toon-encoder.d.ts +26 -0
- package/dist/lib/toon-encoder.d.ts.map +1 -0
- package/dist/lib/toon-encoder.js +61 -0
- package/dist/lib/toon-encoder.js.map +1 -0
- package/dist/lib/toon-encoder.test.d.ts +5 -0
- package/dist/lib/toon-encoder.test.d.ts.map +1 -0
- package/dist/lib/toon-encoder.test.js +85 -0
- package/dist/lib/toon-encoder.test.js.map +1 -0
- package/dist/server.d.ts +1 -49
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +154 -162
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +198 -7
- package/dist/server.test.js.map +1 -1
- package/dist/test-helpers/env-utils.d.ts +87 -0
- package/dist/test-helpers/env-utils.d.ts.map +1 -0
- package/dist/test-helpers/env-utils.js +132 -0
- package/dist/test-helpers/env-utils.js.map +1 -0
- package/dist/test-helpers/file-utils.d.ts +67 -0
- package/dist/test-helpers/file-utils.d.ts.map +1 -1
- package/dist/test-helpers/file-utils.js +165 -2
- package/dist/test-helpers/file-utils.js.map +1 -1
- package/dist/test-helpers/fuzz-generators.d.ts +58 -0
- package/dist/test-helpers/fuzz-generators.d.ts.map +1 -0
- package/dist/test-helpers/fuzz-generators.js +216 -0
- package/dist/test-helpers/fuzz-generators.js.map +1 -0
- package/dist/test-helpers/index.d.ts +11 -0
- package/dist/test-helpers/index.d.ts.map +1 -0
- package/dist/test-helpers/index.js +30 -0
- package/dist/test-helpers/index.js.map +1 -0
- package/dist/test-helpers/memfs-utils.d.ts +181 -0
- package/dist/test-helpers/memfs-utils.d.ts.map +1 -0
- package/dist/test-helpers/memfs-utils.js +292 -0
- package/dist/test-helpers/memfs-utils.js.map +1 -0
- package/dist/test-helpers/memfs-utils.test.d.ts +5 -0
- package/dist/test-helpers/memfs-utils.test.d.ts.map +1 -0
- package/dist/test-helpers/memfs-utils.test.js +338 -0
- package/dist/test-helpers/memfs-utils.test.js.map +1 -0
- package/dist/test-helpers/mock-backends.d.ts +113 -2
- package/dist/test-helpers/mock-backends.d.ts.map +1 -1
- package/dist/test-helpers/mock-backends.js +199 -3
- package/dist/test-helpers/mock-backends.js.map +1 -1
- package/dist/test-helpers/mock-backends.test.d.ts +5 -0
- package/dist/test-helpers/mock-backends.test.d.ts.map +1 -0
- package/dist/test-helpers/mock-backends.test.js +368 -0
- package/dist/test-helpers/mock-backends.test.js.map +1 -0
- package/dist/test-helpers/race-condition-helpers.d.ts +85 -0
- package/dist/test-helpers/race-condition-helpers.d.ts.map +1 -0
- package/dist/test-helpers/race-condition-helpers.js +279 -0
- package/dist/test-helpers/race-condition-helpers.js.map +1 -0
- package/dist/test-helpers/schema-validators.d.ts +32 -0
- package/dist/test-helpers/schema-validators.d.ts.map +1 -0
- package/dist/test-helpers/schema-validators.js +125 -0
- package/dist/test-helpers/schema-validators.js.map +1 -0
- package/dist/test-helpers/test-data-builders.d.ts +260 -0
- package/dist/test-helpers/test-data-builders.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.js +337 -0
- package/dist/test-helpers/test-data-builders.js.map +1 -0
- package/dist/test-helpers/test-data-builders.test.d.ts +2 -0
- package/dist/test-helpers/test-data-builders.test.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.test.js +306 -0
- package/dist/test-helpers/test-data-builders.test.js.map +1 -0
- package/dist/test-helpers/tool-validators.d.ts +28 -0
- package/dist/test-helpers/tool-validators.d.ts.map +1 -0
- package/dist/test-helpers/tool-validators.js +71 -0
- package/dist/test-helpers/tool-validators.js.map +1 -0
- package/dist/tools/context-stats.d.ts +1 -0
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +9 -5
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.js +24 -10
- package/dist/tools/context-stats.test.js.map +1 -1
- package/dist/tools/get-trace-url.js +2 -2
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/health-check.js +2 -2
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/query-evaluations.d.ts +21 -18
- package/dist/tools/query-evaluations.d.ts.map +1 -1
- package/dist/tools/query-evaluations.js +33 -19
- package/dist/tools/query-evaluations.js.map +1 -1
- package/dist/tools/query-evaluations.test.js +60 -63
- package/dist/tools/query-evaluations.test.js.map +1 -1
- package/dist/tools/query-llm-events.d.ts +19 -15
- package/dist/tools/query-llm-events.d.ts.map +1 -1
- package/dist/tools/query-llm-events.js +31 -15
- package/dist/tools/query-llm-events.js.map +1 -1
- package/dist/tools/query-llm-events.test.js +277 -12
- package/dist/tools/query-llm-events.test.js.map +1 -1
- package/dist/tools/query-logs.d.ts +22 -22
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +9 -9
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.js +19 -72
- package/dist/tools/query-logs.test.js.map +1 -1
- package/dist/tools/query-metrics.d.ts +14 -14
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +9 -9
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.js +12 -25
- package/dist/tools/query-metrics.test.js.map +1 -1
- package/dist/tools/query-traces.d.ts +28 -28
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +18 -18
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.js +58 -54
- package/dist/tools/query-traces.test.js.map +1 -1
- package/dist/tools/setup-claudeignore.js +7 -7
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.js +4 -25
- package/dist/tools/setup-claudeignore.test.js.map +1 -1
- package/package.json +4 -2
package/dist/server.js
CHANGED
|
@@ -11,112 +11,15 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
11
11
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
12
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
13
13
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
14
|
+
import { createRequire } from 'module';
|
|
14
15
|
import { queryTracesTool, queryMetricsTool, queryLogsTool, queryLLMEventsTool, queryEvaluationsTool, healthCheckTool, contextStatsTool, getTraceUrlTool, setupClaudeignoreTool, } from './tools/index.js';
|
|
15
16
|
import { sanitizeErrorForResponse } from './lib/error-sanitizer.js';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Sliding window counter rate limiter with O(1) operations and bounded memory.
|
|
24
|
-
* Uses fixed-size bucket array to track request counts per time slice.
|
|
25
|
-
*
|
|
26
|
-
* Memory usage: O(bucketCount) - fixed regardless of request volume
|
|
27
|
-
* Time complexity: O(bucketCount) for sum, but bucketCount is constant (60)
|
|
28
|
-
*
|
|
29
|
-
* @exported for testing
|
|
30
|
-
*/
|
|
31
|
-
export class RateLimiter {
|
|
32
|
-
buckets;
|
|
33
|
-
bucketTimestamps;
|
|
34
|
-
bucketMs;
|
|
35
|
-
windowMs;
|
|
36
|
-
maxRequests;
|
|
37
|
-
bucketCount;
|
|
38
|
-
constructor(windowMs = RATE_LIMIT.windowMs, maxRequests = RATE_LIMIT.maxRequests, bucketCount = RATE_LIMIT.bucketCount) {
|
|
39
|
-
this.windowMs = windowMs;
|
|
40
|
-
this.maxRequests = maxRequests;
|
|
41
|
-
this.bucketCount = bucketCount;
|
|
42
|
-
this.bucketMs = Math.floor(windowMs / bucketCount);
|
|
43
|
-
// Pre-allocate fixed-size arrays
|
|
44
|
-
this.buckets = new Array(bucketCount).fill(0);
|
|
45
|
-
this.bucketTimestamps = new Array(bucketCount).fill(0);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get the bucket index for a given timestamp.
|
|
49
|
-
* Uses modulo to create circular buffer behavior.
|
|
50
|
-
*/
|
|
51
|
-
getBucketIndex(timestamp) {
|
|
52
|
-
return Math.floor(timestamp / this.bucketMs) % this.bucketCount;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Clear expired buckets and return the current valid count.
|
|
56
|
-
* A bucket is expired if its timestamp is older than windowMs ago.
|
|
57
|
-
*/
|
|
58
|
-
cleanAndCount(now) {
|
|
59
|
-
const windowStart = now - this.windowMs;
|
|
60
|
-
let count = 0;
|
|
61
|
-
for (let i = 0; i < this.bucketCount; i++) {
|
|
62
|
-
if (this.bucketTimestamps[i] < windowStart) {
|
|
63
|
-
// Bucket is expired, reset it
|
|
64
|
-
this.buckets[i] = 0;
|
|
65
|
-
this.bucketTimestamps[i] = 0;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
count += this.buckets[i];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return count;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Check if a request is allowed and record it if so.
|
|
75
|
-
* O(bucketCount) time complexity where bucketCount is constant.
|
|
76
|
-
*/
|
|
77
|
-
isAllowed() {
|
|
78
|
-
const now = Date.now();
|
|
79
|
-
const currentCount = this.cleanAndCount(now);
|
|
80
|
-
if (currentCount >= this.maxRequests) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
// Record the request in the current bucket
|
|
84
|
-
const bucketIndex = this.getBucketIndex(now);
|
|
85
|
-
// If this bucket is from a different time period, reset it
|
|
86
|
-
const bucketWindowStart = now - this.windowMs;
|
|
87
|
-
if (this.bucketTimestamps[bucketIndex] < bucketWindowStart) {
|
|
88
|
-
this.buckets[bucketIndex] = 0;
|
|
89
|
-
}
|
|
90
|
-
this.buckets[bucketIndex]++;
|
|
91
|
-
this.bucketTimestamps[bucketIndex] = now;
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Get the number of remaining allowed requests in the current window.
|
|
96
|
-
*/
|
|
97
|
-
getRemainingRequests() {
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
const currentCount = this.cleanAndCount(now);
|
|
100
|
-
return Math.max(0, this.maxRequests - currentCount);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Get the current memory footprint (for testing).
|
|
104
|
-
* Returns the fixed size of internal arrays.
|
|
105
|
-
*/
|
|
106
|
-
getMemoryFootprint() {
|
|
107
|
-
return {
|
|
108
|
-
bucketCount: this.bucketCount,
|
|
109
|
-
arraySize: this.buckets.length + this.bucketTimestamps.length,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Reset the rate limiter state (for testing).
|
|
114
|
-
*/
|
|
115
|
-
reset() {
|
|
116
|
-
this.buckets.fill(0);
|
|
117
|
-
this.bucketTimestamps.fill(0);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
17
|
+
import { RateLimiter, ServerInitError } from './lib/server-utils.js';
|
|
18
|
+
// Import version from package.json (L1 fix)
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const { version: packageVersion } = require('../package.json');
|
|
21
|
+
// Re-export for backwards compatibility
|
|
22
|
+
export { RateLimiter, ServerInitError } from './lib/server-utils.js';
|
|
120
23
|
const rateLimiter = new RateLimiter();
|
|
121
24
|
// Tool definitions
|
|
122
25
|
const tools = [
|
|
@@ -142,71 +45,160 @@ function convertZodToJsonSchema(zodSchema) {
|
|
|
142
45
|
const { $schema, ...rest } = schema;
|
|
143
46
|
return rest;
|
|
144
47
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
tools: tools.map(tool => ({
|
|
158
|
-
name: tool.name,
|
|
159
|
-
description: tool.description,
|
|
160
|
-
inputSchema: convertZodToJsonSchema(tool.inputSchema),
|
|
161
|
-
})),
|
|
162
|
-
};
|
|
163
|
-
});
|
|
164
|
-
// Handle tool calls
|
|
165
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
166
|
-
const { name, arguments: args } = request.params;
|
|
167
|
-
// Rate limiting check
|
|
168
|
-
if (!rateLimiter.isAllowed()) {
|
|
169
|
-
return {
|
|
170
|
-
content: [{ type: 'text', text: `Rate limit exceeded. Please wait before making more requests. Remaining: ${rateLimiter.getRemainingRequests()}` }],
|
|
171
|
-
isError: true,
|
|
172
|
-
};
|
|
48
|
+
/**
|
|
49
|
+
* Validate that all tools are properly configured before starting the server.
|
|
50
|
+
* @throws ServerInitError if any tool is misconfigured
|
|
51
|
+
*/
|
|
52
|
+
function validateTools() {
|
|
53
|
+
if (tools.length === 0) {
|
|
54
|
+
throw new ServerInitError('tool-validation', null, 'No tools registered. Server cannot start without at least one tool.');
|
|
55
|
+
}
|
|
56
|
+
for (const tool of tools) {
|
|
57
|
+
if (!tool.name || typeof tool.name !== 'string') {
|
|
58
|
+
throw new ServerInitError('tool-validation', null, `Tool missing required 'name' property`);
|
|
173
59
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
178
|
-
isError: true,
|
|
179
|
-
};
|
|
60
|
+
if (!tool.handler || typeof tool.handler !== 'function') {
|
|
61
|
+
throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'handler' function`);
|
|
180
62
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
188
|
-
};
|
|
63
|
+
if (!tool.inputSchema) {
|
|
64
|
+
throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'inputSchema'`);
|
|
65
|
+
}
|
|
66
|
+
if (!tool.description || typeof tool.description !== 'string') {
|
|
67
|
+
throw new ServerInitError('tool-validation', null, `Tool '${tool.name}' missing required 'description'`);
|
|
189
68
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function main() {
|
|
72
|
+
// Step 1: Validate tool configuration
|
|
73
|
+
console.error('[init] Validating tool configuration...');
|
|
74
|
+
try {
|
|
75
|
+
validateTools();
|
|
76
|
+
console.error(`[init] Validated ${tools.length} tools successfully`);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error instanceof ServerInitError) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
throw new ServerInitError('tool-validation', error, 'Failed to validate tool configuration');
|
|
83
|
+
}
|
|
84
|
+
// Step 2: Create MCP server instance
|
|
85
|
+
console.error('[init] Creating MCP server instance...');
|
|
86
|
+
let server;
|
|
87
|
+
try {
|
|
88
|
+
server = new Server({
|
|
89
|
+
name: 'observability-toolkit',
|
|
90
|
+
version: packageVersion,
|
|
91
|
+
}, {
|
|
92
|
+
capabilities: {
|
|
93
|
+
tools: {},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new ServerInitError('server-creation', error, 'Failed to create MCP server instance. Check @modelcontextprotocol/sdk installation.');
|
|
99
|
+
}
|
|
100
|
+
// Step 3: Register request handlers
|
|
101
|
+
console.error('[init] Registering request handlers...');
|
|
102
|
+
try {
|
|
103
|
+
// List available tools
|
|
104
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
196
105
|
return {
|
|
197
|
-
|
|
198
|
-
|
|
106
|
+
tools: tools.map(tool => ({
|
|
107
|
+
name: tool.name,
|
|
108
|
+
description: tool.description,
|
|
109
|
+
inputSchema: convertZodToJsonSchema(tool.inputSchema),
|
|
110
|
+
})),
|
|
199
111
|
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
112
|
+
});
|
|
113
|
+
// Handle tool calls
|
|
114
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
115
|
+
const { name, arguments: args } = request.params;
|
|
116
|
+
// Rate limiting check
|
|
117
|
+
if (!rateLimiter.isAllowed()) {
|
|
118
|
+
const limitedCount = rateLimiter.getRateLimitedCount();
|
|
119
|
+
console.warn(`[rate-limit] Request rejected for tool '${name}' (total rejected: ${limitedCount})`);
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: 'text', text: `Rate limit exceeded. Please wait before making more requests. Remaining: ${rateLimiter.getRemainingRequests()}` }],
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const tool = tools.find(t => t.name === name);
|
|
126
|
+
if (!tool) {
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
129
|
+
isError: true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
// Parse and validate input
|
|
134
|
+
const input = tool.inputSchema.parse(args || {});
|
|
135
|
+
// Execute handler - use type assertion since we've validated input
|
|
136
|
+
const result = await tool.handler(input);
|
|
137
|
+
return {
|
|
138
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Security: sanitize error messages to prevent information disclosure
|
|
143
|
+
// - Removes absolute file paths
|
|
144
|
+
// - Removes stack traces in production
|
|
145
|
+
// - Returns generic messages for internal errors
|
|
146
|
+
const errorText = sanitizeErrorForResponse(error);
|
|
147
|
+
return {
|
|
148
|
+
content: [{ type: 'text', text: errorText }],
|
|
149
|
+
isError: true,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw new ServerInitError('handler-registration', error, 'Failed to register request handlers');
|
|
156
|
+
}
|
|
157
|
+
// Step 4: Create and connect transport
|
|
158
|
+
console.error('[init] Connecting stdio transport...');
|
|
159
|
+
try {
|
|
160
|
+
const transport = new StdioServerTransport();
|
|
161
|
+
await server.connect(transport);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
throw new ServerInitError('transport-connection', error, 'Failed to connect stdio transport. Ensure stdin/stdout are available.');
|
|
165
|
+
}
|
|
166
|
+
// Log startup success to stderr (stdout is for MCP protocol)
|
|
167
|
+
console.error('[init] Observability Toolkit MCP server started successfully');
|
|
168
|
+
console.error(`[init] Available tools: ${tools.map(t => t.name).join(', ')}`);
|
|
207
169
|
}
|
|
208
170
|
main().catch((error) => {
|
|
209
|
-
|
|
171
|
+
if (error instanceof ServerInitError) {
|
|
172
|
+
console.error(`[FATAL] Server initialization failed at step '${error.step}'`);
|
|
173
|
+
console.error(`[FATAL] Error: ${error.message}`);
|
|
174
|
+
if (error.cause) {
|
|
175
|
+
console.error(`[FATAL] Cause: ${error.cause instanceof Error ? error.cause.message : String(error.cause)}`);
|
|
176
|
+
}
|
|
177
|
+
console.error('[FATAL] Please check:');
|
|
178
|
+
switch (error.step) {
|
|
179
|
+
case 'tool-validation':
|
|
180
|
+
console.error(' - All tool modules are properly imported');
|
|
181
|
+
console.error(' - Each tool has name, description, inputSchema, and handler');
|
|
182
|
+
break;
|
|
183
|
+
case 'server-creation':
|
|
184
|
+
console.error(' - @modelcontextprotocol/sdk is installed correctly');
|
|
185
|
+
console.error(' - Run: npm install @modelcontextprotocol/sdk');
|
|
186
|
+
break;
|
|
187
|
+
case 'handler-registration':
|
|
188
|
+
console.error(' - Request schema imports are correct');
|
|
189
|
+
console.error(' - zod-to-json-schema is installed');
|
|
190
|
+
break;
|
|
191
|
+
case 'transport-connection':
|
|
192
|
+
console.error(' - Server is being run with proper stdin/stdout');
|
|
193
|
+
console.error(' - Not running in a detached process');
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
console.error(' - Check the error message above for details');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.error('[FATAL] Unexpected error during server startup:', error);
|
|
201
|
+
}
|
|
210
202
|
process.exit(1);
|
|
211
203
|
});
|
|
212
204
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.test.js
CHANGED
|
@@ -545,9 +545,10 @@ describe('CallToolRequestSchema handler', () => {
|
|
|
545
545
|
});
|
|
546
546
|
});
|
|
547
547
|
/**
|
|
548
|
-
* Import the RateLimiter for testing
|
|
548
|
+
* Import the RateLimiter and ServerInitError for testing
|
|
549
|
+
* Imported from server-utils to avoid starting the MCP server
|
|
549
550
|
*/
|
|
550
|
-
import { RateLimiter } from './server.js';
|
|
551
|
+
import { RateLimiter, ServerInitError } from './lib/server-utils.js';
|
|
551
552
|
describe('RateLimiter', () => {
|
|
552
553
|
describe('basic functionality', () => {
|
|
553
554
|
it('should allow requests under the limit', () => {
|
|
@@ -704,11 +705,6 @@ describe('RateLimiter', () => {
|
|
|
704
705
|
});
|
|
705
706
|
});
|
|
706
707
|
describe('edge cases', () => {
|
|
707
|
-
it('should handle zero requests allowed', () => {
|
|
708
|
-
const limiter = new RateLimiter(1000, 0, 10);
|
|
709
|
-
assert.strictEqual(limiter.isAllowed(), false);
|
|
710
|
-
assert.strictEqual(limiter.getRemainingRequests(), 0);
|
|
711
|
-
});
|
|
712
708
|
it('should handle single bucket', () => {
|
|
713
709
|
const limiter = new RateLimiter(1000, 5, 1);
|
|
714
710
|
for (let i = 0; i < 5; i++) {
|
|
@@ -735,6 +731,34 @@ describe('RateLimiter', () => {
|
|
|
735
731
|
assert.strictEqual(blocked, 50);
|
|
736
732
|
});
|
|
737
733
|
});
|
|
734
|
+
describe('bounds validation', () => {
|
|
735
|
+
it('should throw error for bucketCount < 1', () => {
|
|
736
|
+
assert.throws(() => new RateLimiter(1000, 10, 0), /bucketCount must be between 1 and/);
|
|
737
|
+
});
|
|
738
|
+
it('should throw error for bucketCount > maxBucketCount', () => {
|
|
739
|
+
assert.throws(() => new RateLimiter(1000, 10, 3601), /bucketCount must be between 1 and/);
|
|
740
|
+
});
|
|
741
|
+
it('should throw error for maxRequests < 1', () => {
|
|
742
|
+
assert.throws(() => new RateLimiter(1000, 0, 10), /maxRequests must be >= 1/);
|
|
743
|
+
});
|
|
744
|
+
it('should throw error for negative maxRequests', () => {
|
|
745
|
+
assert.throws(() => new RateLimiter(1000, -1, 10), /maxRequests must be >= 1/);
|
|
746
|
+
});
|
|
747
|
+
it('should throw error for windowMs < 1', () => {
|
|
748
|
+
assert.throws(() => new RateLimiter(0, 10, 10), /windowMs must be between 1ms and 86400000ms/);
|
|
749
|
+
});
|
|
750
|
+
it('should throw error for windowMs > 86400000 (1 day)', () => {
|
|
751
|
+
assert.throws(() => new RateLimiter(86400001, 10, 10), /windowMs must be between 1ms and 86400000ms/);
|
|
752
|
+
});
|
|
753
|
+
it('should accept valid maximum bucketCount (3600)', () => {
|
|
754
|
+
const limiter = new RateLimiter(3600000, 100, 3600);
|
|
755
|
+
assert.strictEqual(limiter.getMemoryFootprint().bucketCount, 3600);
|
|
756
|
+
});
|
|
757
|
+
it('should accept valid minimum bucketCount (1)', () => {
|
|
758
|
+
const limiter = new RateLimiter(1000, 10, 1);
|
|
759
|
+
assert.strictEqual(limiter.getMemoryFootprint().bucketCount, 1);
|
|
760
|
+
});
|
|
761
|
+
});
|
|
738
762
|
describe('default configuration', () => {
|
|
739
763
|
it('should use default values when not specified', () => {
|
|
740
764
|
const limiter = new RateLimiter();
|
|
@@ -745,5 +769,172 @@ describe('RateLimiter', () => {
|
|
|
745
769
|
assert.strictEqual(limiter.getRemainingRequests(), 100);
|
|
746
770
|
});
|
|
747
771
|
});
|
|
772
|
+
describe('observability', () => {
|
|
773
|
+
it('should track rate-limited requests count', () => {
|
|
774
|
+
const limiter = new RateLimiter(1000, 3, 10);
|
|
775
|
+
// Initially zero
|
|
776
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 0);
|
|
777
|
+
// Use up all requests
|
|
778
|
+
for (let i = 0; i < 3; i++) {
|
|
779
|
+
limiter.isAllowed();
|
|
780
|
+
}
|
|
781
|
+
// Still zero - no rejections yet
|
|
782
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 0);
|
|
783
|
+
// These should be rejected
|
|
784
|
+
limiter.isAllowed();
|
|
785
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 1);
|
|
786
|
+
limiter.isAllowed();
|
|
787
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 2);
|
|
788
|
+
});
|
|
789
|
+
it('should reset rate-limited count on reset', () => {
|
|
790
|
+
const limiter = new RateLimiter(1000, 2, 10);
|
|
791
|
+
// Exhaust limit and trigger rejections
|
|
792
|
+
limiter.isAllowed();
|
|
793
|
+
limiter.isAllowed();
|
|
794
|
+
limiter.isAllowed(); // rejected
|
|
795
|
+
limiter.isAllowed(); // rejected
|
|
796
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 2);
|
|
797
|
+
// Reset should clear counter
|
|
798
|
+
limiter.reset();
|
|
799
|
+
assert.strictEqual(limiter.getRateLimitedCount(), 0);
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
describe('ServerInitError', () => {
|
|
804
|
+
describe('constructor', () => {
|
|
805
|
+
it('should create error with step and cause', () => {
|
|
806
|
+
const cause = new Error('Underlying error');
|
|
807
|
+
const error = new ServerInitError('tool-validation', cause);
|
|
808
|
+
assert.strictEqual(error.step, 'tool-validation');
|
|
809
|
+
assert.strictEqual(error.cause, cause);
|
|
810
|
+
assert.strictEqual(error.name, 'ServerInitError');
|
|
811
|
+
assert.ok(error.message.includes('tool-validation'));
|
|
812
|
+
});
|
|
813
|
+
it('should create error with custom message', () => {
|
|
814
|
+
const error = new ServerInitError('server-creation', null, 'Custom error message');
|
|
815
|
+
assert.strictEqual(error.message, 'Custom error message');
|
|
816
|
+
assert.strictEqual(error.step, 'server-creation');
|
|
817
|
+
assert.strictEqual(error.cause, null);
|
|
818
|
+
});
|
|
819
|
+
it('should create error without cause', () => {
|
|
820
|
+
const error = new ServerInitError('tool-validation', undefined);
|
|
821
|
+
assert.strictEqual(error.step, 'tool-validation');
|
|
822
|
+
assert.strictEqual(error.cause, undefined);
|
|
823
|
+
});
|
|
824
|
+
it('should generate default message from step name', () => {
|
|
825
|
+
const error = new ServerInitError('server-creation', null);
|
|
826
|
+
assert.ok(error.message.includes('server-creation'));
|
|
827
|
+
assert.ok(error.message.includes('initialization failed'));
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
describe('inheritance', () => {
|
|
831
|
+
it('should be instanceof Error', () => {
|
|
832
|
+
const error = new ServerInitError('tool-validation', null);
|
|
833
|
+
assert.ok(error instanceof Error);
|
|
834
|
+
});
|
|
835
|
+
it('should be instanceof ServerInitError', () => {
|
|
836
|
+
const error = new ServerInitError('tool-validation', null);
|
|
837
|
+
assert.ok(error instanceof ServerInitError);
|
|
838
|
+
});
|
|
839
|
+
it('should have correct name property', () => {
|
|
840
|
+
const error = new ServerInitError('tool-validation', null);
|
|
841
|
+
assert.strictEqual(error.name, 'ServerInitError');
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
describe('initialization steps', () => {
|
|
845
|
+
it('should accept tool-validation step', () => {
|
|
846
|
+
const error = new ServerInitError('tool-validation', null, 'Tool validation failed');
|
|
847
|
+
assert.strictEqual(error.step, 'tool-validation');
|
|
848
|
+
});
|
|
849
|
+
it('should accept server-creation step', () => {
|
|
850
|
+
const error = new ServerInitError('server-creation', null, 'Server creation failed');
|
|
851
|
+
assert.strictEqual(error.step, 'server-creation');
|
|
852
|
+
});
|
|
853
|
+
it('should accept handler-registration step', () => {
|
|
854
|
+
const error = new ServerInitError('handler-registration', null, 'Handler registration failed');
|
|
855
|
+
assert.strictEqual(error.step, 'handler-registration');
|
|
856
|
+
});
|
|
857
|
+
it('should accept transport-connection step', () => {
|
|
858
|
+
const error = new ServerInitError('transport-connection', null, 'Transport connection failed');
|
|
859
|
+
assert.strictEqual(error.step, 'transport-connection');
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
describe('cause handling', () => {
|
|
863
|
+
it('should preserve Error cause', () => {
|
|
864
|
+
const originalError = new Error('Original problem');
|
|
865
|
+
const error = new ServerInitError('handler-registration', originalError);
|
|
866
|
+
assert.strictEqual(error.cause, originalError);
|
|
867
|
+
assert.strictEqual(error.cause.message, 'Original problem');
|
|
868
|
+
});
|
|
869
|
+
it('should handle string cause', () => {
|
|
870
|
+
const error = new ServerInitError('handler-registration', 'string cause');
|
|
871
|
+
assert.strictEqual(error.cause, 'string cause');
|
|
872
|
+
});
|
|
873
|
+
it('should handle object cause', () => {
|
|
874
|
+
const cause = { code: 'ENOENT', path: '/missing/file' };
|
|
875
|
+
const error = new ServerInitError('handler-registration', cause);
|
|
876
|
+
assert.deepStrictEqual(error.cause, cause);
|
|
877
|
+
});
|
|
878
|
+
it('should handle null cause', () => {
|
|
879
|
+
const error = new ServerInitError('handler-registration', null);
|
|
880
|
+
assert.strictEqual(error.cause, null);
|
|
881
|
+
});
|
|
882
|
+
it('should handle undefined cause', () => {
|
|
883
|
+
const error = new ServerInitError('handler-registration', undefined);
|
|
884
|
+
assert.strictEqual(error.cause, undefined);
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
describe('error messaging', () => {
|
|
888
|
+
it('should include step in default message', () => {
|
|
889
|
+
const error = new ServerInitError('transport-connection', null);
|
|
890
|
+
assert.ok(error.message.includes('transport-connection'));
|
|
891
|
+
});
|
|
892
|
+
it('should use custom message when provided', () => {
|
|
893
|
+
const error = new ServerInitError('transport-connection', null, 'Custom: Failed to initialize the widget');
|
|
894
|
+
assert.strictEqual(error.message, 'Custom: Failed to initialize the widget');
|
|
895
|
+
});
|
|
896
|
+
it('should support detailed error messages', () => {
|
|
897
|
+
const error = new ServerInitError('tool-validation', null, "Tool 'obs_query_traces' missing required 'handler' function");
|
|
898
|
+
assert.ok(error.message.includes('obs_query_traces'));
|
|
899
|
+
assert.ok(error.message.includes('handler'));
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
describe('error handling patterns', () => {
|
|
903
|
+
it('should work with try-catch', () => {
|
|
904
|
+
let caught = null;
|
|
905
|
+
try {
|
|
906
|
+
throw new ServerInitError('server-creation', null, 'Test error');
|
|
907
|
+
}
|
|
908
|
+
catch (e) {
|
|
909
|
+
if (e instanceof ServerInitError) {
|
|
910
|
+
caught = e;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
assert.ok(caught !== null);
|
|
914
|
+
assert.strictEqual(caught?.step, 'server-creation');
|
|
915
|
+
});
|
|
916
|
+
it('should work with instanceof check', () => {
|
|
917
|
+
const error = new ServerInitError('tool-validation', null);
|
|
918
|
+
if (error instanceof ServerInitError) {
|
|
919
|
+
assert.strictEqual(error.step, 'tool-validation');
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
assert.fail('Should be instanceof ServerInitError');
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
it('should serialize to JSON with step and cause', () => {
|
|
926
|
+
const error = new ServerInitError('transport-connection', { code: 123 }, 'Test message');
|
|
927
|
+
const json = JSON.stringify({
|
|
928
|
+
name: error.name,
|
|
929
|
+
message: error.message,
|
|
930
|
+
step: error.step,
|
|
931
|
+
cause: error.cause,
|
|
932
|
+
});
|
|
933
|
+
const parsed = JSON.parse(json);
|
|
934
|
+
assert.strictEqual(parsed.name, 'ServerInitError');
|
|
935
|
+
assert.strictEqual(parsed.step, 'transport-connection');
|
|
936
|
+
assert.deepStrictEqual(parsed.cause, { code: 123 });
|
|
937
|
+
});
|
|
938
|
+
});
|
|
748
939
|
});
|
|
749
940
|
//# sourceMappingURL=server.test.js.map
|