@geekmidas/telescope 9.0.2 → 10.0.0-alpha.1
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 +14 -4
- package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
- package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
- package/dist/instrumentation/http.d.mts +1 -1
- package/dist/instrumentation/index.cjs +2 -2
- package/dist/instrumentation/index.d.cts +2 -2
- package/dist/instrumentation/index.d.mts +3 -3
- package/dist/instrumentation/index.mjs +2 -2
- package/dist/instrumentation/middleware.cjs +1 -1
- package/dist/instrumentation/middleware.d.cts +1 -1
- package/dist/instrumentation/middleware.d.mts +1 -1
- package/dist/instrumentation/middleware.mjs +1 -1
- package/dist/instrumentation/otel.cjs +1 -1
- package/dist/instrumentation/otel.d.cts +1 -1
- package/dist/instrumentation/otel.d.mts +1 -1
- package/dist/instrumentation/otel.mjs +1 -1
- package/dist/logger/console.d.cts.map +1 -1
- package/dist/logger/pino.d.mts +2 -2
- package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
- package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
- package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
- package/dist/middleware-D1XEHvyy.mjs.map +1 -0
- package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
- package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
- package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
- package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
- package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
- package/dist/otel-B9bNoeZ2.mjs.map +1 -0
- package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
- package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
- package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
- package/dist/otel-BzjOtVxZ.cjs.map +1 -0
- package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
- package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
- package/dist/storage/kysely.cjs +48 -38
- package/dist/storage/kysely.cjs.map +1 -1
- package/dist/storage/kysely.d.cts +29 -9
- package/dist/storage/kysely.d.cts.map +1 -1
- package/dist/storage/kysely.d.mts +29 -9
- package/dist/storage/kysely.d.mts.map +1 -1
- package/dist/storage/kysely.mjs +48 -38
- package/dist/storage/kysely.mjs.map +1 -1
- package/dist/tracing-PnECEbEu.d.mts.map +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -72
- package/dist/middleware-4-GrQlOT.mjs.map +0 -1
- package/dist/middleware-DqPQpcup.cjs.map +0 -1
- package/dist/otel-40udNP5g.mjs.map +0 -1
- package/dist/otel-COrpz_GZ.cjs.map +0 -1
- package/src/Telescope.ts +0 -561
- package/src/__tests__/Telescope.spec.ts +0 -438
- package/src/__tests__/pino-transport.spec.ts +0 -115
- package/src/__tests__/redact.spec.ts +0 -336
- package/src/adapters/__tests__/lambda.spec.ts +0 -318
- package/src/adapters/__tests__/middy.spec.ts +0 -741
- package/src/adapters/index.ts +0 -22
- package/src/adapters/lambda.ts +0 -432
- package/src/adapters/middy.ts +0 -402
- package/src/adapters/types.ts +0 -181
- package/src/core.ts +0 -51
- package/src/index.ts +0 -34
- package/src/instrumentation/__tests__/core.spec.ts +0 -248
- package/src/instrumentation/__tests__/hono.spec.ts +0 -348
- package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
- package/src/instrumentation/__tests__/otel.spec.ts +0 -291
- package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
- package/src/instrumentation/core.ts +0 -228
- package/src/instrumentation/hono.ts +0 -270
- package/src/instrumentation/http.ts +0 -306
- package/src/instrumentation/index.ts +0 -90
- package/src/instrumentation/middleware.ts +0 -297
- package/src/instrumentation/otel.ts +0 -271
- package/src/instrumentation/setup.ts +0 -246
- package/src/instrumentation/tracing.ts +0 -145
- package/src/logger/__tests__/console.spec.ts +0 -366
- package/src/logger/__tests__/pino.spec.ts +0 -217
- package/src/logger/console.ts +0 -230
- package/src/logger/pino.ts +0 -201
- package/src/metrics/MetricsAggregator.ts +0 -555
- package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
- package/src/metrics/index.ts +0 -2
- package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
- package/src/metrics/storage/MetricsStorage.ts +0 -74
- package/src/metrics/storage/index.ts +0 -3
- package/src/metrics/storage/types.ts +0 -243
- package/src/otlp/__tests__/receiver.spec.ts +0 -367
- package/src/otlp/__tests__/transformer.spec.ts +0 -455
- package/src/otlp/hono.ts +0 -120
- package/src/otlp/index.ts +0 -22
- package/src/otlp/receiver.ts +0 -162
- package/src/otlp/transformer.ts +0 -367
- package/src/otlp/types.ts +0 -364
- package/src/redact.ts +0 -116
- package/src/server/__tests__/hono.spec.ts +0 -814
- package/src/server/hono.ts +0 -442
- package/src/storage/__tests__/kysely.spec.ts +0 -715
- package/src/storage/__tests__/memory.spec.ts +0 -694
- package/src/storage/kysely.ts +0 -599
- package/src/storage/memory.ts +0 -204
- package/src/types.ts +0 -447
- package/src/ui-assets.ts +0 -40
- package/tsconfig.json +0 -9
- package/ui/CHANGELOG.md +0 -7
- package/ui/index.html +0 -12
- package/ui/node_modules/.bin/tsc +0 -21
- package/ui/node_modules/.bin/tsserver +0 -21
- package/ui/node_modules/.bin/vite +0 -21
- package/ui/package.json +0 -24
- package/ui/src/App.tsx +0 -595
- package/ui/src/api.ts +0 -88
- package/ui/src/components/ExceptionDetail.tsx +0 -100
- package/ui/src/components/FilterBar.tsx +0 -162
- package/ui/src/components/LogDetail.tsx +0 -91
- package/ui/src/components/RequestDetail.tsx +0 -143
- package/ui/src/main.tsx +0 -10
- package/ui/src/styles.css +0 -11
- package/ui/src/types.ts +0 -63
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/src/vite-plugin-gkm-config.ts +0 -54
- package/ui/tsconfig.json +0 -20
- package/ui/tsconfig.tsbuildinfo +0 -1
- package/ui/vite.config.ts +0 -13
package/src/server/hono.ts
DELETED
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
import type { Context, MiddlewareHandler, Next } from 'hono';
|
|
2
|
-
import { Hono } from 'hono';
|
|
3
|
-
import type {
|
|
4
|
-
HonoAdapterConfig,
|
|
5
|
-
TelescopeHonoContext,
|
|
6
|
-
} from '../adapters/types';
|
|
7
|
-
import type { Telescope } from '../Telescope';
|
|
8
|
-
import type { MetricsQueryOptions, QueryOptions } from '../types';
|
|
9
|
-
import { getAsset, getIndexHtml } from '../ui-assets';
|
|
10
|
-
|
|
11
|
-
// Lazy-loaded telemetry flush function (only loads @opentelemetry/* when actually used)
|
|
12
|
-
let _flushTelemetry: (() => Promise<void>) | null = null;
|
|
13
|
-
async function getFlushTelemetry(): Promise<() => Promise<void>> {
|
|
14
|
-
if (!_flushTelemetry) {
|
|
15
|
-
const { flushTelemetry } = await import('../instrumentation/core');
|
|
16
|
-
_flushTelemetry = flushTelemetry;
|
|
17
|
-
}
|
|
18
|
-
return _flushTelemetry;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const CONTEXT_KEY = 'telescope-context';
|
|
22
|
-
|
|
23
|
-
/** Default Hono adapter configuration */
|
|
24
|
-
const _DEFAULT_CONFIG: HonoAdapterConfig = {
|
|
25
|
-
environment: 'server',
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Options for the Telescope Hono middleware
|
|
30
|
-
*/
|
|
31
|
-
export interface TelescopeMiddlewareOptions {
|
|
32
|
-
/**
|
|
33
|
-
* Whether to flush telemetry after each request.
|
|
34
|
-
* Enable this for serverless environments (Lambda, Edge).
|
|
35
|
-
* @default false
|
|
36
|
-
*/
|
|
37
|
-
flushOnResponse?: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Create Hono middleware that captures requests and responses
|
|
42
|
-
*/
|
|
43
|
-
export function createMiddleware(
|
|
44
|
-
telescope: Telescope,
|
|
45
|
-
options: TelescopeMiddlewareOptions = {},
|
|
46
|
-
): MiddlewareHandler {
|
|
47
|
-
const { flushOnResponse = false } = options;
|
|
48
|
-
|
|
49
|
-
return async (c: Context, next: Next) => {
|
|
50
|
-
if (!telescope.enabled) {
|
|
51
|
-
return next();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (telescope.shouldIgnore(c.req.path)) {
|
|
55
|
-
return next();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const startTime = performance.now();
|
|
59
|
-
|
|
60
|
-
// Capture request data
|
|
61
|
-
const headers: Record<string, string> = {};
|
|
62
|
-
c.req.raw.headers.forEach((value, key) => {
|
|
63
|
-
headers[key] = value;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const url = new URL(c.req.url);
|
|
67
|
-
const query: Record<string, string> = {};
|
|
68
|
-
url.searchParams.forEach((value, key) => {
|
|
69
|
-
query[key] = value;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
let body: unknown;
|
|
73
|
-
let requestSize: number | undefined;
|
|
74
|
-
|
|
75
|
-
// Get request size from Content-Length header
|
|
76
|
-
const contentLength = c.req.header('content-length');
|
|
77
|
-
if (contentLength) {
|
|
78
|
-
requestSize = parseInt(contentLength, 10);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
telescope.recordBody &&
|
|
83
|
-
['POST', 'PUT', 'PATCH'].includes(c.req.method)
|
|
84
|
-
) {
|
|
85
|
-
try {
|
|
86
|
-
const contentType = c.req.header('content-type') || '';
|
|
87
|
-
// Clone the request to avoid consuming the body stream
|
|
88
|
-
const clonedRequest = c.req.raw.clone();
|
|
89
|
-
if (contentType.includes('application/json')) {
|
|
90
|
-
body = await clonedRequest.json();
|
|
91
|
-
} else if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
92
|
-
body = Object.fromEntries((await clonedRequest.formData()).entries());
|
|
93
|
-
} else if (contentType.includes('text/')) {
|
|
94
|
-
body = await clonedRequest.text();
|
|
95
|
-
}
|
|
96
|
-
} catch {
|
|
97
|
-
// Ignore body parsing errors
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const ip = c.req.header('x-forwarded-for') || c.req.header('x-real-ip');
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
await next();
|
|
105
|
-
|
|
106
|
-
// Capture response data
|
|
107
|
-
const duration = performance.now() - startTime;
|
|
108
|
-
|
|
109
|
-
const responseHeaders: Record<string, string> = {};
|
|
110
|
-
c.res.headers.forEach((value, key) => {
|
|
111
|
-
responseHeaders[key] = value;
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
let responseBody: unknown;
|
|
115
|
-
let responseSize: number | undefined;
|
|
116
|
-
|
|
117
|
-
// Get response size - prefer Content-Length header, fallback to body size
|
|
118
|
-
const responseContentLength = c.res.headers.get('content-length');
|
|
119
|
-
if (responseContentLength) {
|
|
120
|
-
responseSize = parseInt(responseContentLength, 10);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (telescope.recordBody) {
|
|
124
|
-
try {
|
|
125
|
-
const contentType = c.res.headers.get('content-type') || '';
|
|
126
|
-
if (contentType.includes('application/json')) {
|
|
127
|
-
const cloned = c.res.clone();
|
|
128
|
-
const text = await cloned.text();
|
|
129
|
-
responseSize ??= Buffer.byteLength(text, 'utf8');
|
|
130
|
-
try {
|
|
131
|
-
responseBody = JSON.parse(text);
|
|
132
|
-
} catch {
|
|
133
|
-
responseBody = text;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
} catch {
|
|
137
|
-
// Ignore body parsing errors
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const requestId = await telescope.recordRequest({
|
|
142
|
-
method: c.req.method,
|
|
143
|
-
path: c.req.path,
|
|
144
|
-
url: c.req.url,
|
|
145
|
-
headers,
|
|
146
|
-
body,
|
|
147
|
-
query,
|
|
148
|
-
status: c.res.status,
|
|
149
|
-
responseHeaders,
|
|
150
|
-
responseBody,
|
|
151
|
-
duration,
|
|
152
|
-
ip,
|
|
153
|
-
requestSize,
|
|
154
|
-
responseSize,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Store context for access by other middleware
|
|
158
|
-
const ctx: TelescopeHonoContext = {
|
|
159
|
-
requestId,
|
|
160
|
-
startTime,
|
|
161
|
-
};
|
|
162
|
-
c.set(CONTEXT_KEY, ctx);
|
|
163
|
-
|
|
164
|
-
// Flush telemetry for serverless environments
|
|
165
|
-
if (flushOnResponse) {
|
|
166
|
-
const flushTelemetry = await getFlushTelemetry();
|
|
167
|
-
await flushTelemetry();
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
await telescope.exception(error as Error);
|
|
171
|
-
throw error;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Parse query options from Hono context
|
|
178
|
-
*/
|
|
179
|
-
function parseQueryOptions(c: Context): QueryOptions {
|
|
180
|
-
const limit = parseInt(c.req.query('limit') || '50', 10);
|
|
181
|
-
const offset = parseInt(c.req.query('offset') || '0', 10);
|
|
182
|
-
const search = c.req.query('search');
|
|
183
|
-
const before = c.req.query('before');
|
|
184
|
-
const after = c.req.query('after');
|
|
185
|
-
const tags = c.req.query('tags')?.split(',').filter(Boolean);
|
|
186
|
-
const method = c.req.query('method');
|
|
187
|
-
const status = c.req.query('status');
|
|
188
|
-
const level = c.req.query('level') as
|
|
189
|
-
| 'debug'
|
|
190
|
-
| 'info'
|
|
191
|
-
| 'warn'
|
|
192
|
-
| 'error'
|
|
193
|
-
| undefined;
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
limit: Math.min(limit, 100),
|
|
197
|
-
offset,
|
|
198
|
-
search,
|
|
199
|
-
before: before ? new Date(before) : undefined,
|
|
200
|
-
after: after ? new Date(after) : undefined,
|
|
201
|
-
tags,
|
|
202
|
-
method: method || undefined,
|
|
203
|
-
status: status || undefined,
|
|
204
|
-
level: level || undefined,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Parse metrics query options from Hono context
|
|
210
|
-
*/
|
|
211
|
-
function parseMetricsQueryOptions(c: Context): MetricsQueryOptions {
|
|
212
|
-
const start = c.req.query('start');
|
|
213
|
-
const end = c.req.query('end');
|
|
214
|
-
const bucketSize = c.req.query('bucketSize');
|
|
215
|
-
const limit = c.req.query('limit');
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
range:
|
|
219
|
-
start && end ? { start: new Date(start), end: new Date(end) } : undefined,
|
|
220
|
-
bucketSize: bucketSize ? parseInt(bucketSize, 10) : undefined,
|
|
221
|
-
limit: limit ? parseInt(limit, 10) : undefined,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Create Hono app with dashboard UI and API routes
|
|
227
|
-
*/
|
|
228
|
-
export function createUI(telescope: Telescope): Hono {
|
|
229
|
-
const app = new Hono();
|
|
230
|
-
|
|
231
|
-
// API routes
|
|
232
|
-
app.get('/api/requests', async (c) => {
|
|
233
|
-
const options = parseQueryOptions(c);
|
|
234
|
-
const requests = await telescope.getRequests(options);
|
|
235
|
-
return c.json(requests);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
app.get('/api/requests/:id', async (c) => {
|
|
239
|
-
const request = await telescope.getRequest(c.req.param('id'));
|
|
240
|
-
if (!request) {
|
|
241
|
-
return c.json({ error: 'Request not found' }, 404);
|
|
242
|
-
}
|
|
243
|
-
return c.json(request);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
app.get('/api/exceptions', async (c) => {
|
|
247
|
-
const options = parseQueryOptions(c);
|
|
248
|
-
const exceptions = await telescope.getExceptions(options);
|
|
249
|
-
return c.json(exceptions);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
app.get('/api/exceptions/:id', async (c) => {
|
|
253
|
-
const exception = await telescope.getException(c.req.param('id'));
|
|
254
|
-
if (!exception) {
|
|
255
|
-
return c.json({ error: 'Exception not found' }, 404);
|
|
256
|
-
}
|
|
257
|
-
return c.json(exception);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
app.get('/api/logs', async (c) => {
|
|
261
|
-
const options = parseQueryOptions(c);
|
|
262
|
-
const logs = await telescope.getLogs(options);
|
|
263
|
-
return c.json(logs);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
app.get('/api/stats', async (c) => {
|
|
267
|
-
const stats = await telescope.getStats();
|
|
268
|
-
return c.json(stats);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// Metrics API
|
|
272
|
-
app.get('/api/metrics', (c) => {
|
|
273
|
-
const options = parseMetricsQueryOptions(c);
|
|
274
|
-
const metrics = telescope.getMetrics(options);
|
|
275
|
-
return c.json(metrics);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
app.get('/api/metrics/endpoints', (c) => {
|
|
279
|
-
const options = parseMetricsQueryOptions(c);
|
|
280
|
-
const endpoints = telescope.getEndpointMetrics(options);
|
|
281
|
-
return c.json(endpoints);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// Endpoint details - method and path are query params to avoid URL encoding issues
|
|
285
|
-
app.get('/api/metrics/endpoint', (c) => {
|
|
286
|
-
const method = c.req.query('method');
|
|
287
|
-
const path = c.req.query('path');
|
|
288
|
-
|
|
289
|
-
if (!method || !path) {
|
|
290
|
-
return c.json({ error: 'method and path are required' }, 400);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const options = parseMetricsQueryOptions(c);
|
|
294
|
-
const details = telescope.getEndpointDetails(method, path, options);
|
|
295
|
-
|
|
296
|
-
if (!details) {
|
|
297
|
-
return c.json({ error: 'Endpoint not found' }, 404);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return c.json(details);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
app.get('/api/metrics/status', (c) => {
|
|
304
|
-
const options = parseMetricsQueryOptions(c);
|
|
305
|
-
const distribution = telescope.getStatusDistribution(options);
|
|
306
|
-
return c.json(distribution);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
app.delete('/api/metrics', (c) => {
|
|
310
|
-
telescope.resetMetrics();
|
|
311
|
-
return c.json({ success: true });
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// Static assets
|
|
315
|
-
app.get('/assets/:filename', (c) => {
|
|
316
|
-
const filename = c.req.param('filename');
|
|
317
|
-
const assetPath = `assets/${filename}`;
|
|
318
|
-
const asset = getAsset(assetPath);
|
|
319
|
-
if (asset) {
|
|
320
|
-
return c.body(asset.content, 200, {
|
|
321
|
-
'Content-Type': asset.contentType,
|
|
322
|
-
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
return c.notFound();
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Dashboard UI - serve React app
|
|
329
|
-
app.get('/', (c) => {
|
|
330
|
-
const html = getIndexHtml();
|
|
331
|
-
if (!html) {
|
|
332
|
-
return c.text(
|
|
333
|
-
'Telescope UI not available. Run "pnpm build:ui" first.',
|
|
334
|
-
500,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
return c.html(html);
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
app.get('/*', (c) => {
|
|
341
|
-
// SPA fallback - serve index.html for client-side routing
|
|
342
|
-
const html = getIndexHtml();
|
|
343
|
-
if (!html) {
|
|
344
|
-
return c.text(
|
|
345
|
-
'Telescope UI not available. Run "pnpm build:ui" first.',
|
|
346
|
-
500,
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
return c.html(html);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
return app;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Options for setupWebSocket
|
|
357
|
-
*/
|
|
358
|
-
export interface WebSocketOptions {
|
|
359
|
-
/**
|
|
360
|
-
* Enable real-time metrics broadcasting (default: true)
|
|
361
|
-
*/
|
|
362
|
-
broadcastMetrics?: boolean;
|
|
363
|
-
/**
|
|
364
|
-
* Metrics broadcast interval in milliseconds (default: 5000)
|
|
365
|
-
*/
|
|
366
|
-
metricsBroadcastInterval?: number;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Set up WebSocket routes for real-time updates.
|
|
371
|
-
* Requires @hono/node-ws for Node.js or Bun's built-in WebSocket.
|
|
372
|
-
*/
|
|
373
|
-
export function setupWebSocket(
|
|
374
|
-
app: Hono,
|
|
375
|
-
telescope: Telescope,
|
|
376
|
-
upgradeWebSocket: (handler: any) => any,
|
|
377
|
-
options: WebSocketOptions = {},
|
|
378
|
-
): void {
|
|
379
|
-
const { broadcastMetrics = true, metricsBroadcastInterval = 5000 } = options;
|
|
380
|
-
|
|
381
|
-
// Start metrics broadcast if enabled
|
|
382
|
-
if (broadcastMetrics) {
|
|
383
|
-
telescope.startMetricsBroadcast(metricsBroadcastInterval);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
app.get(
|
|
387
|
-
'/ws',
|
|
388
|
-
upgradeWebSocket(() => ({
|
|
389
|
-
onOpen: (_event: Event, ws: WebSocket) => {
|
|
390
|
-
telescope.addWsClient(ws);
|
|
391
|
-
},
|
|
392
|
-
onClose: (_event: Event, ws: WebSocket) => {
|
|
393
|
-
telescope.removeWsClient(ws);
|
|
394
|
-
},
|
|
395
|
-
onMessage: (event: MessageEvent, ws: WebSocket) => {
|
|
396
|
-
try {
|
|
397
|
-
const data = JSON.parse(event.data);
|
|
398
|
-
if (data.type === 'ping') {
|
|
399
|
-
ws.send(JSON.stringify({ type: 'pong' }));
|
|
400
|
-
}
|
|
401
|
-
} catch {
|
|
402
|
-
// Ignore invalid messages
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
})),
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Get the Telescope context from Hono context (set by middleware)
|
|
411
|
-
*/
|
|
412
|
-
export function getTelescopeContext(
|
|
413
|
-
c: Context,
|
|
414
|
-
): TelescopeHonoContext | undefined {
|
|
415
|
-
return c.get(CONTEXT_KEY);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Get the request ID from Hono context (set by middleware)
|
|
420
|
-
*/
|
|
421
|
-
export function getRequestId(c: Context): string | undefined {
|
|
422
|
-
return getTelescopeContext(c)?.requestId;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Re-export types and utilities
|
|
426
|
-
export type { Telescope };
|
|
427
|
-
export type {
|
|
428
|
-
HonoAdapterConfig,
|
|
429
|
-
TelescopeHonoContext,
|
|
430
|
-
} from '../adapters/types';
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Get the flushTelemetry function (lazy-loaded to avoid requiring @opentelemetry/* at import time).
|
|
434
|
-
* Use this for serverless environments where you need to flush telemetry before the process freezes.
|
|
435
|
-
*
|
|
436
|
-
* @example
|
|
437
|
-
* ```typescript
|
|
438
|
-
* const flushTelemetry = await getFlushTelemetryFn();
|
|
439
|
-
* await flushTelemetry();
|
|
440
|
-
* ```
|
|
441
|
-
*/
|
|
442
|
-
export { getFlushTelemetry as getFlushTelemetryFn };
|