@thotischner/observability-mcp 1.7.1 → 3.0.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/config/products.yaml.example +48 -0
- package/dist/analysis/history.d.ts +70 -0
- package/dist/analysis/history.js +170 -0
- package/dist/analysis/history.test.d.ts +1 -0
- package/dist/analysis/history.test.js +141 -0
- package/dist/audit/log.d.ts +108 -0
- package/dist/audit/log.js +200 -0
- package/dist/audit/log.test.d.ts +1 -0
- package/dist/audit/log.test.js +147 -0
- package/dist/audit/middleware.d.ts +20 -0
- package/dist/audit/middleware.js +50 -0
- package/dist/audit/redaction-bypass.d.ts +67 -0
- package/dist/audit/redaction-bypass.js +64 -0
- package/dist/audit/redaction-bypass.test.d.ts +1 -0
- package/dist/audit/redaction-bypass.test.js +72 -0
- package/dist/audit/sinks/types.d.ts +18 -0
- package/dist/audit/sinks/types.js +1 -0
- package/dist/audit/sinks/webhook.d.ts +45 -0
- package/dist/audit/sinks/webhook.js +111 -0
- package/dist/audit/sinks/webhook.test.d.ts +1 -0
- package/dist/audit/sinks/webhook.test.js +162 -0
- package/dist/auth/credentials.d.ts +29 -0
- package/dist/auth/credentials.js +53 -1
- package/dist/auth/credentials.test.js +46 -1
- package/dist/auth/csrf.d.ts +26 -0
- package/dist/auth/csrf.js +128 -0
- package/dist/auth/csrf.test.d.ts +1 -0
- package/dist/auth/csrf.test.js +143 -0
- package/dist/auth/local-users.d.ts +68 -0
- package/dist/auth/local-users.js +154 -0
- package/dist/auth/local-users.test.d.ts +1 -0
- package/dist/auth/local-users.test.js +121 -0
- package/dist/auth/middleware.d.ts +49 -0
- package/dist/auth/middleware.js +65 -0
- package/dist/auth/middleware.test.d.ts +1 -0
- package/dist/auth/middleware.test.js +90 -0
- package/dist/auth/oidc/client.d.ts +73 -0
- package/dist/auth/oidc/client.js +104 -0
- package/dist/auth/oidc/client.test.d.ts +1 -0
- package/dist/auth/oidc/client.test.js +121 -0
- package/dist/auth/oidc/dcr.d.ts +70 -0
- package/dist/auth/oidc/dcr.js +160 -0
- package/dist/auth/oidc/dcr.test.d.ts +1 -0
- package/dist/auth/oidc/dcr.test.js +109 -0
- package/dist/auth/oidc/discovery.d.ts +38 -0
- package/dist/auth/oidc/discovery.js +48 -0
- package/dist/auth/oidc/discovery.test.d.ts +1 -0
- package/dist/auth/oidc/discovery.test.js +68 -0
- package/dist/auth/oidc/endpoints.d.ts +20 -0
- package/dist/auth/oidc/endpoints.js +168 -0
- package/dist/auth/oidc/endpoints.test.d.ts +7 -0
- package/dist/auth/oidc/endpoints.test.js +304 -0
- package/dist/auth/oidc/flow-cookie.d.ts +57 -0
- package/dist/auth/oidc/flow-cookie.js +142 -0
- package/dist/auth/oidc/flow-cookie.test.d.ts +1 -0
- package/dist/auth/oidc/flow-cookie.test.js +0 -0
- package/dist/auth/oidc/index.d.ts +7 -0
- package/dist/auth/oidc/index.js +6 -0
- package/dist/auth/oidc/jwks.d.ts +36 -0
- package/dist/auth/oidc/jwks.js +69 -0
- package/dist/auth/oidc/jwks.test.d.ts +1 -0
- package/dist/auth/oidc/jwks.test.js +65 -0
- package/dist/auth/oidc/jwt.d.ts +62 -0
- package/dist/auth/oidc/jwt.js +113 -0
- package/dist/auth/oidc/jwt.test.d.ts +1 -0
- package/dist/auth/oidc/jwt.test.js +141 -0
- package/dist/auth/oidc/pkce.d.ts +19 -0
- package/dist/auth/oidc/pkce.js +43 -0
- package/dist/auth/oidc/pkce.test.d.ts +1 -0
- package/dist/auth/oidc/pkce.test.js +55 -0
- package/dist/auth/oidc/profiles.d.ts +22 -0
- package/dist/auth/oidc/profiles.js +95 -0
- package/dist/auth/oidc/profiles.test.d.ts +1 -0
- package/dist/auth/oidc/profiles.test.js +51 -0
- package/dist/auth/oidc/runtime.d.ts +66 -0
- package/dist/auth/oidc/runtime.js +142 -0
- package/dist/auth/oidc/runtime.test.d.ts +1 -0
- package/dist/auth/oidc/runtime.test.js +181 -0
- package/dist/auth/policy/batch-dry-run.d.ts +56 -0
- package/dist/auth/policy/batch-dry-run.js +129 -0
- package/dist/auth/policy/batch-dry-run.test.d.ts +1 -0
- package/dist/auth/policy/batch-dry-run.test.js +140 -0
- package/dist/auth/policy/engine.d.ts +64 -0
- package/dist/auth/policy/engine.js +87 -0
- package/dist/auth/policy/engine.test.d.ts +1 -0
- package/dist/auth/policy/engine.test.js +98 -0
- package/dist/auth/policy/loader.d.ts +45 -0
- package/dist/auth/policy/loader.js +137 -0
- package/dist/auth/policy/loader.test.d.ts +1 -0
- package/dist/auth/policy/loader.test.js +86 -0
- package/dist/auth/policy/opa.d.ts +69 -0
- package/dist/auth/policy/opa.js +173 -0
- package/dist/auth/policy/opa.test.d.ts +1 -0
- package/dist/auth/policy/opa.test.js +206 -0
- package/dist/auth/rbac.d.ts +62 -0
- package/dist/auth/rbac.js +162 -0
- package/dist/auth/rbac.test.d.ts +1 -0
- package/dist/auth/rbac.test.js +183 -0
- package/dist/auth/session.d.ts +66 -0
- package/dist/auth/session.js +146 -0
- package/dist/auth/session.test.d.ts +1 -0
- package/dist/auth/session.test.js +90 -0
- package/dist/catalog/loader.d.ts +67 -0
- package/dist/catalog/loader.js +122 -0
- package/dist/catalog/loader.test.d.ts +1 -0
- package/dist/catalog/loader.test.js +108 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/inspector-config.d.ts +9 -0
- package/dist/cli/inspector-config.js +28 -0
- package/dist/cli/inspector-config.test.d.ts +1 -0
- package/dist/cli/inspector-config.test.js +33 -0
- package/dist/cli/lib.d.ts +1 -1
- package/dist/cli/lib.js +1 -0
- package/dist/conformance/mcp-2025-11-25.test.d.ts +1 -0
- package/dist/conformance/mcp-2025-11-25.test.js +206 -0
- package/dist/connectors/interface.d.ts +5 -1
- package/dist/connectors/loader.js +6 -4
- package/dist/connectors/loader.test.d.ts +1 -0
- package/dist/connectors/loader.test.js +78 -0
- package/dist/connectors/prometheus.test.js +31 -13
- package/dist/connectors/registry.d.ts +13 -0
- package/dist/connectors/registry.js +30 -0
- package/dist/connectors/registry.test.js +56 -2
- package/dist/context.d.ts +45 -1
- package/dist/context.js +40 -1
- package/dist/context.test.d.ts +1 -0
- package/dist/context.test.js +58 -0
- package/dist/federation/registry.d.ts +32 -0
- package/dist/federation/registry.js +77 -0
- package/dist/federation/registry.test.d.ts +1 -0
- package/dist/federation/registry.test.js +130 -0
- package/dist/federation/upstream.d.ts +60 -0
- package/dist/federation/upstream.js +114 -0
- package/dist/index.js +2124 -73
- package/dist/middleware/ssrfGuard.d.ts +15 -0
- package/dist/middleware/ssrfGuard.js +103 -0
- package/dist/middleware/ssrfGuard.test.d.ts +1 -0
- package/dist/middleware/ssrfGuard.test.js +81 -0
- package/dist/net/egress-policy.js +2 -0
- package/dist/observability/otel.d.ts +20 -0
- package/dist/observability/otel.js +118 -0
- package/dist/observability/otel.test.d.ts +1 -0
- package/dist/observability/otel.test.js +56 -0
- package/dist/openapi.js +654 -6
- package/dist/openapi.test.d.ts +1 -0
- package/dist/openapi.test.js +98 -0
- package/dist/policy/redact.d.ts +44 -0
- package/dist/policy/redact.js +144 -0
- package/dist/policy/redact.test.d.ts +1 -0
- package/dist/policy/redact.test.js +172 -0
- package/dist/postmortem/synthesizer.d.ts +83 -0
- package/dist/postmortem/synthesizer.js +205 -0
- package/dist/postmortem/synthesizer.test.d.ts +1 -0
- package/dist/postmortem/synthesizer.test.js +141 -0
- package/dist/products/loader.d.ts +112 -0
- package/dist/products/loader.js +289 -0
- package/dist/products/loader.test.d.ts +1 -0
- package/dist/products/loader.test.js +257 -0
- package/dist/quota/charge.d.ts +28 -0
- package/dist/quota/charge.js +30 -0
- package/dist/quota/charge.test.d.ts +1 -0
- package/dist/quota/charge.test.js +83 -0
- package/dist/quota/limiter.d.ts +97 -0
- package/dist/quota/limiter.js +161 -0
- package/dist/quota/limiter.test.d.ts +1 -0
- package/dist/quota/limiter.test.js +205 -0
- package/dist/quota/token-budget.d.ts +119 -0
- package/dist/quota/token-budget.js +297 -0
- package/dist/quota/token-budget.test.d.ts +1 -0
- package/dist/quota/token-budget.test.js +215 -0
- package/dist/scim/group-role-map.d.ts +4 -0
- package/dist/scim/group-role-map.js +33 -0
- package/dist/scim/group-role-map.test.d.ts +1 -0
- package/dist/scim/group-role-map.test.js +33 -0
- package/dist/scim/routes.d.ts +15 -0
- package/dist/scim/routes.js +249 -0
- package/dist/scim/store.d.ts +37 -0
- package/dist/scim/store.js +178 -0
- package/dist/scim/store.test.d.ts +1 -0
- package/dist/scim/store.test.js +121 -0
- package/dist/scim/types.d.ts +73 -0
- package/dist/scim/types.js +29 -0
- package/dist/sdk/hooks.d.ts +77 -0
- package/dist/sdk/hooks.js +72 -0
- package/dist/sdk/hooks.test.d.ts +1 -0
- package/dist/sdk/hooks.test.js +159 -0
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/manifest-schema.d.ts +17 -0
- package/dist/sdk/manifest-schema.js +21 -0
- package/dist/tenancy/context.d.ts +45 -0
- package/dist/tenancy/context.js +97 -0
- package/dist/tenancy/context.test.d.ts +1 -0
- package/dist/tenancy/context.test.js +72 -0
- package/dist/tenancy/migration.test.d.ts +7 -0
- package/dist/tenancy/migration.test.js +75 -0
- package/dist/tools/context-seam.test.js +6 -1
- package/dist/tools/detect-anomalies.d.ts +1 -1
- package/dist/tools/detect-anomalies.js +5 -4
- package/dist/tools/generate-postmortem.d.ts +35 -0
- package/dist/tools/generate-postmortem.js +191 -0
- package/dist/tools/get-anomaly-history.d.ts +35 -0
- package/dist/tools/get-anomaly-history.js +126 -0
- package/dist/tools/get-service-health.d.ts +1 -1
- package/dist/tools/get-service-health.js +4 -3
- package/dist/tools/list-services.d.ts +1 -1
- package/dist/tools/list-services.js +3 -2
- package/dist/tools/list-sources.d.ts +1 -1
- package/dist/tools/list-sources.js +6 -2
- package/dist/tools/query-logs.d.ts +1 -1
- package/dist/tools/query-logs.js +2 -2
- package/dist/tools/query-metrics.d.ts +1 -1
- package/dist/tools/query-metrics.js +19 -6
- package/dist/tools/query-traces.d.ts +47 -0
- package/dist/tools/query-traces.js +145 -0
- package/dist/tools/query-traces.test.d.ts +1 -0
- package/dist/tools/query-traces.test.js +110 -0
- package/dist/tools/registry-names.d.ts +35 -0
- package/dist/tools/registry-names.js +54 -0
- package/dist/tools/registry-names.test.d.ts +1 -0
- package/dist/tools/registry-names.test.js +61 -0
- package/dist/tools/topology.d.ts +3 -3
- package/dist/tools/topology.js +10 -6
- package/dist/topology/merge.d.ts +22 -0
- package/dist/topology/merge.js +178 -0
- package/dist/topology/merge.test.d.ts +1 -0
- package/dist/topology/merge.test.js +110 -0
- package/dist/transport/sessionStore.d.ts +66 -0
- package/dist/transport/sessionStore.js +138 -0
- package/dist/transport/sessionStore.test.d.ts +1 -0
- package/dist/transport/sessionStore.test.js +118 -0
- package/dist/transport/websocket.d.ts +35 -0
- package/dist/transport/websocket.js +133 -0
- package/dist/transport/websocket.test.d.ts +1 -0
- package/dist/transport/websocket.test.js +124 -0
- package/dist/types.d.ts +51 -0
- package/dist/ui/index.html +3083 -88
- package/package.json +32 -5
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// WebSocket transport for MCP.
|
|
2
|
+
//
|
|
3
|
+
// Implements the @modelcontextprotocol/sdk Transport interface so the
|
|
4
|
+
// existing McpServer can speak JSON-RPC over a single WS connection
|
|
5
|
+
// without duplicating any tool registration code.
|
|
6
|
+
//
|
|
7
|
+
// Wire-format: one JSON-RPC message per WS frame (text). No batching,
|
|
8
|
+
// no framing tricks — the same shape an HTTP POST body carries today.
|
|
9
|
+
//
|
|
10
|
+
// Lifecycle:
|
|
11
|
+
// - The HTTP upgrade handler authenticates the connection, creates
|
|
12
|
+
// a WebSocketServerTransport wrapping the accepted socket, then
|
|
13
|
+
// calls McpServer.connect(transport).
|
|
14
|
+
// - The transport surfaces incoming messages via onmessage, sends
|
|
15
|
+
// outgoing messages via ws.send(), and reports closure / errors
|
|
16
|
+
// through onclose / onerror.
|
|
17
|
+
// - A heartbeat ping is sent every PING_INTERVAL_MS; if no pong
|
|
18
|
+
// arrives within PING_TIMEOUT_MS the connection is closed and the
|
|
19
|
+
// session is reaped (same shape as a normal close).
|
|
20
|
+
import { randomUUID } from "node:crypto";
|
|
21
|
+
export const PING_INTERVAL_MS = 30_000;
|
|
22
|
+
export const PING_TIMEOUT_MS = 90_000;
|
|
23
|
+
/**
|
|
24
|
+
* Per-WS-connection MCP transport. One instance per accepted socket.
|
|
25
|
+
*/
|
|
26
|
+
export class WebSocketServerTransport {
|
|
27
|
+
sessionId;
|
|
28
|
+
onclose;
|
|
29
|
+
onerror;
|
|
30
|
+
onmessage;
|
|
31
|
+
ws;
|
|
32
|
+
pingTimer;
|
|
33
|
+
lastPongAt = Date.now();
|
|
34
|
+
pingIntervalMs;
|
|
35
|
+
pingTimeoutMs;
|
|
36
|
+
closed = false;
|
|
37
|
+
constructor(ws, opts = {}) {
|
|
38
|
+
this.ws = ws;
|
|
39
|
+
this.sessionId = opts.sessionId ?? randomUUID();
|
|
40
|
+
this.pingIntervalMs = opts.pingIntervalMs ?? PING_INTERVAL_MS;
|
|
41
|
+
this.pingTimeoutMs = opts.pingTimeoutMs ?? PING_TIMEOUT_MS;
|
|
42
|
+
}
|
|
43
|
+
async start() {
|
|
44
|
+
this.ws.on("message", (data, isBinary) => {
|
|
45
|
+
if (isBinary) {
|
|
46
|
+
// MCP frames are JSON text. A binary frame is a client bug —
|
|
47
|
+
// report it but keep the socket open: the SDK contract says
|
|
48
|
+
// errors are non-fatal unless we explicitly close.
|
|
49
|
+
this.onerror?.(new Error("WebSocket binary frame rejected (MCP expects text JSON)"));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let payload;
|
|
53
|
+
try {
|
|
54
|
+
payload = JSON.parse(typeof data === "string" ? data : data.toString("utf-8"));
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
this.onerror?.(err instanceof Error
|
|
58
|
+
? err
|
|
59
|
+
: new Error(`WebSocket frame parse failed: ${String(err)}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Batched frame -> dispatch each entry; matches Streamable HTTP semantics.
|
|
63
|
+
const items = Array.isArray(payload) ? payload : [payload];
|
|
64
|
+
for (const item of items) {
|
|
65
|
+
this.onmessage?.(item);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
this.ws.on("pong", () => {
|
|
69
|
+
this.lastPongAt = Date.now();
|
|
70
|
+
});
|
|
71
|
+
this.ws.on("close", () => this.handleClose());
|
|
72
|
+
this.ws.on("error", (err) => this.onerror?.(err));
|
|
73
|
+
// Heartbeat: drives close-on-stale via lack of pongs.
|
|
74
|
+
this.pingTimer = setInterval(() => {
|
|
75
|
+
if (this.closed)
|
|
76
|
+
return;
|
|
77
|
+
const idleMs = Date.now() - this.lastPongAt;
|
|
78
|
+
if (idleMs > this.pingTimeoutMs) {
|
|
79
|
+
// 1001 = going away; matches the "stale connection" semantics
|
|
80
|
+
// some collectors special-case.
|
|
81
|
+
try {
|
|
82
|
+
this.ws.close(1001, "heartbeat timeout");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* socket already gone */
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
this.ws.ping();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
/* socket already gone */
|
|
94
|
+
}
|
|
95
|
+
}, this.pingIntervalMs).unref();
|
|
96
|
+
}
|
|
97
|
+
async send(message, _options) {
|
|
98
|
+
if (this.closed)
|
|
99
|
+
return;
|
|
100
|
+
const text = JSON.stringify(message);
|
|
101
|
+
await new Promise((resolve, reject) => {
|
|
102
|
+
this.ws.send(text, (err) => {
|
|
103
|
+
if (err)
|
|
104
|
+
reject(err);
|
|
105
|
+
else
|
|
106
|
+
resolve();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async close() {
|
|
111
|
+
if (this.closed)
|
|
112
|
+
return;
|
|
113
|
+
this.closed = true;
|
|
114
|
+
if (this.pingTimer)
|
|
115
|
+
clearInterval(this.pingTimer);
|
|
116
|
+
try {
|
|
117
|
+
// 1000 = normal closure.
|
|
118
|
+
this.ws.close(1000, "server closing");
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
/* already gone */
|
|
122
|
+
}
|
|
123
|
+
this.onclose?.();
|
|
124
|
+
}
|
|
125
|
+
handleClose() {
|
|
126
|
+
if (this.closed)
|
|
127
|
+
return;
|
|
128
|
+
this.closed = true;
|
|
129
|
+
if (this.pingTimer)
|
|
130
|
+
clearInterval(this.pingTimer);
|
|
131
|
+
this.onclose?.();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
import { WebSocketServerTransport } from "./websocket.js";
|
|
5
|
+
// Minimal fake ws that satisfies the methods + events
|
|
6
|
+
// WebSocketServerTransport actually touches. Avoids pulling the real
|
|
7
|
+
// `ws` module into the unit-test runner (which is npm install + a TCP
|
|
8
|
+
// socket pair away).
|
|
9
|
+
class FakeWS extends EventEmitter {
|
|
10
|
+
sent = [];
|
|
11
|
+
closed;
|
|
12
|
+
pingCount = 0;
|
|
13
|
+
send(text, cb) {
|
|
14
|
+
this.sent.push(text);
|
|
15
|
+
cb();
|
|
16
|
+
}
|
|
17
|
+
close(code, reason) {
|
|
18
|
+
this.closed = { code, reason };
|
|
19
|
+
this.emit("close");
|
|
20
|
+
}
|
|
21
|
+
ping() {
|
|
22
|
+
this.pingCount++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function rpc(method, id) {
|
|
26
|
+
return { jsonrpc: "2.0", id, method };
|
|
27
|
+
}
|
|
28
|
+
test("WebSocketServerTransport: parses one JSON message per text frame", async () => {
|
|
29
|
+
const ws = new FakeWS();
|
|
30
|
+
const t = new WebSocketServerTransport(ws);
|
|
31
|
+
const received = [];
|
|
32
|
+
t.onmessage = (m) => received.push(m);
|
|
33
|
+
await t.start();
|
|
34
|
+
ws.emit("message", JSON.stringify(rpc("tools/list", 1)), false);
|
|
35
|
+
ws.emit("message", JSON.stringify(rpc("tools/call", 2)), false);
|
|
36
|
+
assert.equal(received.length, 2);
|
|
37
|
+
assert.equal(received[0].method, "tools/list");
|
|
38
|
+
assert.equal(received[1].method, "tools/call");
|
|
39
|
+
await t.close();
|
|
40
|
+
});
|
|
41
|
+
test("WebSocketServerTransport: batched JSON arrays fan out", async () => {
|
|
42
|
+
const ws = new FakeWS();
|
|
43
|
+
const t = new WebSocketServerTransport(ws);
|
|
44
|
+
const received = [];
|
|
45
|
+
t.onmessage = (m) => received.push(m);
|
|
46
|
+
await t.start();
|
|
47
|
+
ws.emit("message", JSON.stringify([rpc("a", 1), rpc("b", 2), rpc("c", 3)]), false);
|
|
48
|
+
assert.equal(received.length, 3);
|
|
49
|
+
await t.close();
|
|
50
|
+
});
|
|
51
|
+
test("WebSocketServerTransport: malformed JSON surfaces onerror, socket stays open", async () => {
|
|
52
|
+
const ws = new FakeWS();
|
|
53
|
+
const t = new WebSocketServerTransport(ws);
|
|
54
|
+
const errs = [];
|
|
55
|
+
t.onerror = (e) => errs.push(e);
|
|
56
|
+
await t.start();
|
|
57
|
+
ws.emit("message", "not-json{", false);
|
|
58
|
+
assert.equal(errs.length, 1);
|
|
59
|
+
assert.equal(ws.closed, undefined, "socket must NOT be closed on parse error");
|
|
60
|
+
await t.close();
|
|
61
|
+
});
|
|
62
|
+
test("WebSocketServerTransport: binary frame rejected with onerror, socket stays open", async () => {
|
|
63
|
+
const ws = new FakeWS();
|
|
64
|
+
const t = new WebSocketServerTransport(ws);
|
|
65
|
+
const errs = [];
|
|
66
|
+
t.onerror = (e) => errs.push(e);
|
|
67
|
+
await t.start();
|
|
68
|
+
ws.emit("message", Buffer.from([0x01, 0x02]), true);
|
|
69
|
+
assert.equal(errs.length, 1);
|
|
70
|
+
assert.match(errs[0]?.message ?? "", /binary frame/i);
|
|
71
|
+
assert.equal(ws.closed, undefined);
|
|
72
|
+
await t.close();
|
|
73
|
+
});
|
|
74
|
+
test("WebSocketServerTransport: send() writes serialized message", async () => {
|
|
75
|
+
const ws = new FakeWS();
|
|
76
|
+
const t = new WebSocketServerTransport(ws);
|
|
77
|
+
await t.start();
|
|
78
|
+
await t.send({ jsonrpc: "2.0", id: 1, result: { ok: true } });
|
|
79
|
+
assert.equal(ws.sent.length, 1);
|
|
80
|
+
assert.match(ws.sent[0] ?? "", /"result":\{"ok":true\}/);
|
|
81
|
+
await t.close();
|
|
82
|
+
});
|
|
83
|
+
test("WebSocketServerTransport: ws-close triggers onclose", async () => {
|
|
84
|
+
const ws = new FakeWS();
|
|
85
|
+
const t = new WebSocketServerTransport(ws);
|
|
86
|
+
let closed = false;
|
|
87
|
+
t.onclose = () => {
|
|
88
|
+
closed = true;
|
|
89
|
+
};
|
|
90
|
+
await t.start();
|
|
91
|
+
ws.emit("close");
|
|
92
|
+
assert.equal(closed, true);
|
|
93
|
+
// Subsequent send must be a no-op (not throw).
|
|
94
|
+
await t.send({ jsonrpc: "2.0", id: 99, result: {} });
|
|
95
|
+
});
|
|
96
|
+
test("WebSocketServerTransport: heartbeat pings the socket on its interval", async () => {
|
|
97
|
+
const ws = new FakeWS();
|
|
98
|
+
const t = new WebSocketServerTransport(ws, { pingIntervalMs: 20, pingTimeoutMs: 10_000 });
|
|
99
|
+
await t.start();
|
|
100
|
+
await new Promise((r) => setTimeout(r, 65));
|
|
101
|
+
assert.ok(ws.pingCount >= 2, `expected >=2 pings, got ${ws.pingCount}`);
|
|
102
|
+
await t.close();
|
|
103
|
+
});
|
|
104
|
+
test("WebSocketServerTransport: stale connection (no pong) gets closed with 1001", async () => {
|
|
105
|
+
const ws = new FakeWS();
|
|
106
|
+
const t = new WebSocketServerTransport(ws, { pingIntervalMs: 10, pingTimeoutMs: 25 });
|
|
107
|
+
await t.start();
|
|
108
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
109
|
+
assert.equal(ws.closed?.code, 1001);
|
|
110
|
+
assert.match(ws.closed?.reason ?? "", /heartbeat/);
|
|
111
|
+
});
|
|
112
|
+
test("WebSocketServerTransport: generates a sessionId by default", () => {
|
|
113
|
+
const ws = new FakeWS();
|
|
114
|
+
const t = new WebSocketServerTransport(ws);
|
|
115
|
+
assert.ok(t.sessionId, "sessionId must be set");
|
|
116
|
+
assert.match(t.sessionId, /^[0-9a-f-]{36}$/);
|
|
117
|
+
});
|
|
118
|
+
test("WebSocketServerTransport: sessionId override is honored", () => {
|
|
119
|
+
const ws = new FakeWS();
|
|
120
|
+
const t = new WebSocketServerTransport(ws, {
|
|
121
|
+
sessionId: "test-session",
|
|
122
|
+
});
|
|
123
|
+
assert.equal(t.sessionId, "test-session");
|
|
124
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface SourceConfig {
|
|
|
31
31
|
/** @deprecated Use tls.skipVerify instead */
|
|
32
32
|
tlsSkipVerify?: boolean;
|
|
33
33
|
metrics?: MetricDefinition[];
|
|
34
|
+
/** Tenant this source belongs to. When set, only requests in that
|
|
35
|
+
* tenant see / can target the source — cross-tenant access returns
|
|
36
|
+
* the same not-found posture as the rest of the tenancy layer.
|
|
37
|
+
* Unset = global source, available to every tenant (preserves
|
|
38
|
+
* pre-E7 single-tenant behaviour as the default). */
|
|
39
|
+
tenant?: string;
|
|
34
40
|
}
|
|
35
41
|
export interface GeneralSettings {
|
|
36
42
|
checkIntervalMs: number;
|
|
@@ -151,6 +157,51 @@ export interface LogResult {
|
|
|
151
157
|
entries: LogEntry[];
|
|
152
158
|
summary: LogSummary;
|
|
153
159
|
}
|
|
160
|
+
export interface TraceQuery {
|
|
161
|
+
/** Service name to filter by — required. */
|
|
162
|
+
service: string;
|
|
163
|
+
/** Rolling time window, same shape as MetricQuery.duration ("5m", "1h", "24h"). */
|
|
164
|
+
duration: string;
|
|
165
|
+
/** Free-form filter pattern interpreted by the backend (TraceQL,
|
|
166
|
+
* Jaeger tag query, etc.). Optional. */
|
|
167
|
+
filter?: string;
|
|
168
|
+
/** Soft cap on the number of trace summaries returned. Default 50. */
|
|
169
|
+
limit?: number;
|
|
170
|
+
/** Only return spans tagged as errors (`status: error` / span status 2). */
|
|
171
|
+
errorsOnly?: boolean;
|
|
172
|
+
}
|
|
173
|
+
export interface TraceSpanSummary {
|
|
174
|
+
/** Stable trace id (hex). */
|
|
175
|
+
traceId: string;
|
|
176
|
+
/** Root or first span name. */
|
|
177
|
+
rootName: string;
|
|
178
|
+
/** Service that emitted the root span. */
|
|
179
|
+
rootService: string;
|
|
180
|
+
/** Total trace duration in milliseconds. */
|
|
181
|
+
durationMs: number;
|
|
182
|
+
/** Span count. */
|
|
183
|
+
spanCount: number;
|
|
184
|
+
/** Whether any span in this trace has an error status. */
|
|
185
|
+
hasError: boolean;
|
|
186
|
+
/** Span start timestamp (RFC-3339). */
|
|
187
|
+
startTs: string;
|
|
188
|
+
/** Optional backend-specific link to the trace view. */
|
|
189
|
+
url?: string;
|
|
190
|
+
}
|
|
191
|
+
export interface TraceSummary {
|
|
192
|
+
total: number;
|
|
193
|
+
errorCount: number;
|
|
194
|
+
/** Median trace duration (ms) across the returned set. */
|
|
195
|
+
p50DurationMs: number;
|
|
196
|
+
/** 95th-percentile trace duration (ms). */
|
|
197
|
+
p95DurationMs: number;
|
|
198
|
+
}
|
|
199
|
+
export interface TraceResult {
|
|
200
|
+
source: string;
|
|
201
|
+
service: string;
|
|
202
|
+
traces: TraceSpanSummary[];
|
|
203
|
+
summary: TraceSummary;
|
|
204
|
+
}
|
|
154
205
|
/**
|
|
155
206
|
* A discrete infrastructure entity discovered by a topology-aware connector.
|
|
156
207
|
*
|