@geekmidas/telescope 0.0.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.
Files changed (103) hide show
  1. package/README.md +521 -0
  2. package/dist/Telescope-B3Wd82yk.cjs +602 -0
  3. package/dist/Telescope-B3Wd82yk.cjs.map +1 -0
  4. package/dist/Telescope-C5dyDYYB.d.cts +133 -0
  5. package/dist/Telescope-D-uoZB6b.mjs +596 -0
  6. package/dist/Telescope-D-uoZB6b.mjs.map +1 -0
  7. package/dist/Telescope-DyIWgh9-.d.mts +133 -0
  8. package/dist/Telescope.cjs +3 -0
  9. package/dist/Telescope.d.cts +3 -0
  10. package/dist/Telescope.d.mts +3 -0
  11. package/dist/Telescope.mjs +3 -0
  12. package/dist/chunk-CUT6urMc.cjs +30 -0
  13. package/dist/index.cjs +5 -0
  14. package/dist/index.d.cts +4 -0
  15. package/dist/index.d.mts +4 -0
  16. package/dist/index.mjs +4 -0
  17. package/dist/logger/console.cjs +161 -0
  18. package/dist/logger/console.cjs.map +1 -0
  19. package/dist/logger/console.d.cts +109 -0
  20. package/dist/logger/console.d.mts +109 -0
  21. package/dist/logger/console.mjs +159 -0
  22. package/dist/logger/console.mjs.map +1 -0
  23. package/dist/logger/pino.cjs +118 -0
  24. package/dist/logger/pino.cjs.map +1 -0
  25. package/dist/logger/pino.d.cts +89 -0
  26. package/dist/logger/pino.d.mts +89 -0
  27. package/dist/logger/pino.mjs +116 -0
  28. package/dist/logger/pino.mjs.map +1 -0
  29. package/dist/memory-9-B9WACq.cjs +110 -0
  30. package/dist/memory-9-B9WACq.cjs.map +1 -0
  31. package/dist/memory-Cm0eevCS.d.mts +38 -0
  32. package/dist/memory-DiP1a-pp.d.cts +38 -0
  33. package/dist/memory-SdN5vtG9.mjs +104 -0
  34. package/dist/memory-SdN5vtG9.mjs.map +1 -0
  35. package/dist/server/hono.cjs +180 -0
  36. package/dist/server/hono.cjs.map +1 -0
  37. package/dist/server/hono.d.cts +26 -0
  38. package/dist/server/hono.d.mts +26 -0
  39. package/dist/server/hono.mjs +176 -0
  40. package/dist/server/hono.mjs.map +1 -0
  41. package/dist/storage/kysely.cjs +336 -0
  42. package/dist/storage/kysely.cjs.map +1 -0
  43. package/dist/storage/kysely.d.cts +161 -0
  44. package/dist/storage/kysely.d.mts +161 -0
  45. package/dist/storage/kysely.mjs +334 -0
  46. package/dist/storage/kysely.mjs.map +1 -0
  47. package/dist/storage/memory.cjs +3 -0
  48. package/dist/storage/memory.d.cts +3 -0
  49. package/dist/storage/memory.d.mts +3 -0
  50. package/dist/storage/memory.mjs +3 -0
  51. package/dist/types-BGDhFv4R.d.cts +170 -0
  52. package/dist/types-CZbzz8kx.d.mts +170 -0
  53. package/dist/types.cjs +0 -0
  54. package/dist/types.d.cts +2 -0
  55. package/dist/types.d.mts +2 -0
  56. package/dist/types.mjs +0 -0
  57. package/dist/ui-assets-D6-8TAr_.mjs +30 -0
  58. package/dist/ui-assets-D6-8TAr_.mjs.map +1 -0
  59. package/dist/ui-assets-ulevVble.cjs +48 -0
  60. package/dist/ui-assets-ulevVble.cjs.map +1 -0
  61. package/dist/ui-assets.cjs +5 -0
  62. package/dist/ui-assets.d.cts +12 -0
  63. package/dist/ui-assets.d.mts +12 -0
  64. package/dist/ui-assets.mjs +3 -0
  65. package/package.json +83 -0
  66. package/scripts/embed-ui.ts +90 -0
  67. package/src/Telescope.ts +714 -0
  68. package/src/__tests__/Telescope.spec.ts +356 -0
  69. package/src/index.ts +23 -0
  70. package/src/logger/__tests__/console.spec.ts +266 -0
  71. package/src/logger/__tests__/pino.spec.ts +217 -0
  72. package/src/logger/console.ts +230 -0
  73. package/src/logger/pino.ts +191 -0
  74. package/src/server/__tests__/hono.spec.ts +340 -0
  75. package/src/server/hono.ts +247 -0
  76. package/src/storage/__tests__/kysely.spec.ts +715 -0
  77. package/src/storage/__tests__/memory.spec.ts +411 -0
  78. package/src/storage/kysely.ts +572 -0
  79. package/src/storage/memory.ts +168 -0
  80. package/src/types.ts +188 -0
  81. package/src/ui-assets.ts +40 -0
  82. package/ui/index.html +12 -0
  83. package/ui/node_modules/.bin/browserslist +21 -0
  84. package/ui/node_modules/.bin/jiti +21 -0
  85. package/ui/node_modules/.bin/terser +21 -0
  86. package/ui/node_modules/.bin/tsc +21 -0
  87. package/ui/node_modules/.bin/tsserver +21 -0
  88. package/ui/node_modules/.bin/tsx +21 -0
  89. package/ui/node_modules/.bin/vite +21 -0
  90. package/ui/package.json +24 -0
  91. package/ui/src/App.tsx +342 -0
  92. package/ui/src/api.ts +75 -0
  93. package/ui/src/components/ExceptionDetail.tsx +100 -0
  94. package/ui/src/components/LogDetail.tsx +91 -0
  95. package/ui/src/components/RequestDetail.tsx +143 -0
  96. package/ui/src/main.tsx +10 -0
  97. package/ui/src/styles.css +10 -0
  98. package/ui/src/types.ts +63 -0
  99. package/ui/src/vite-env.d.ts +1 -0
  100. package/ui/src/vite-plugin-gkm-config.ts +54 -0
  101. package/ui/tsconfig.json +20 -0
  102. package/ui/tsconfig.tsbuildinfo +14 -0
  103. package/ui/vite.config.ts +13 -0
package/README.md ADDED
@@ -0,0 +1,521 @@
1
+ # @geekmidas/telescope
2
+
3
+ Laravel Telescope-style debugging and monitoring for web applications. Capture and inspect HTTP requests, exceptions, and logs in real-time.
4
+
5
+ **Framework-agnostic core** with adapters for popular frameworks like Hono (Express coming soon).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @geekmidas/telescope
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### With Hono
16
+
17
+ ```typescript
18
+ import { Hono } from 'hono';
19
+ import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
20
+ import { createMiddleware, createUI } from '@geekmidas/telescope/server/hono';
21
+
22
+ const telescope = new Telescope({
23
+ storage: new InMemoryStorage(),
24
+ });
25
+
26
+ const app = new Hono();
27
+
28
+ // Add middleware to capture requests
29
+ app.use('*', createMiddleware(telescope));
30
+
31
+ // Mount the dashboard
32
+ app.route('/__telescope', createUI(telescope));
33
+
34
+ // Your routes
35
+ app.get('/users', (c) => c.json({ users: [] }));
36
+
37
+ export default app;
38
+ ```
39
+
40
+ Visit `http://localhost:3000/__telescope` to view the dashboard.
41
+
42
+ ### Framework-Agnostic Core
43
+
44
+ The core `Telescope` class is framework-agnostic. You can use it directly for manual recording:
45
+
46
+ ```typescript
47
+ import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
48
+
49
+ const telescope = new Telescope({
50
+ storage: new InMemoryStorage(),
51
+ });
52
+
53
+ // Record a request manually
54
+ await telescope.recordRequest({
55
+ method: 'POST',
56
+ path: '/api/users',
57
+ url: 'http://localhost:3000/api/users',
58
+ headers: { 'content-type': 'application/json' },
59
+ body: { name: 'John' },
60
+ query: {},
61
+ status: 201,
62
+ responseHeaders: {},
63
+ responseBody: { id: '123' },
64
+ duration: 45.2,
65
+ });
66
+
67
+ // Log messages
68
+ await telescope.log('info', 'User created', { userId: '123' });
69
+
70
+ // Record exceptions
71
+ try {
72
+ throw new Error('Something went wrong');
73
+ } catch (error) {
74
+ await telescope.exception(error);
75
+ }
76
+ ```
77
+
78
+ ## Features
79
+
80
+ - **Request Recording**: Capture HTTP requests with headers, body, query params, and response
81
+ - **Exception Tracking**: Record exceptions with stack traces and source context
82
+ - **Log Aggregation**: Collect application logs with context
83
+ - **Real-time Updates**: WebSocket-powered live dashboard
84
+ - **Storage Agnostic**: Use in-memory for dev, database for production
85
+ - **Hono Integration**: First-class middleware and route mounting
86
+
87
+ ## Configuration
88
+
89
+ ```typescript
90
+ const telescope = new Telescope({
91
+ // Required: Storage backend
92
+ storage: new InMemoryStorage(),
93
+
94
+ // Optional: Enable/disable telescope (default: true)
95
+ enabled: true,
96
+
97
+ // Optional: Dashboard path (default: '/__telescope')
98
+ path: '/__telescope',
99
+
100
+ // Optional: Record request/response bodies (default: true)
101
+ recordBody: true,
102
+
103
+ // Optional: Max body size to record in bytes (default: 64KB)
104
+ maxBodySize: 64 * 1024,
105
+
106
+ // Optional: URL patterns to ignore
107
+ ignorePatterns: ['/health', '/metrics', '/__telescope/*'],
108
+
109
+ // Optional: Auto-prune entries older than X hours
110
+ pruneAfterHours: 24,
111
+ });
112
+ ```
113
+
114
+ ## Storage Backends
115
+
116
+ ### InMemoryStorage
117
+
118
+ Best for development and testing. Data is lost on restart.
119
+
120
+ ```typescript
121
+ // From main package
122
+ import { InMemoryStorage } from '@geekmidas/telescope';
123
+ // Or direct import
124
+ import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';
125
+
126
+ const storage = new InMemoryStorage({
127
+ maxEntries: 1000, // Max entries per type (default: 1000)
128
+ });
129
+ ```
130
+
131
+ ### KyselyStorage (Coming Soon)
132
+
133
+ For production use with database persistence.
134
+
135
+ ```typescript
136
+ import { KyselyStorage } from '@geekmidas/telescope/storage/kysely';
137
+
138
+ const storage = new KyselyStorage(db, {
139
+ tablePrefix: 'telescope_', // Table name prefix
140
+ });
141
+ ```
142
+
143
+ ### Custom Storage
144
+
145
+ Implement the `TelescopeStorage` interface:
146
+
147
+ ```typescript
148
+ import type { TelescopeStorage } from '@geekmidas/telescope';
149
+
150
+ class MyStorage implements TelescopeStorage {
151
+ async saveRequest(entry: RequestEntry): Promise<void> { /* ... */ }
152
+ async getRequests(options?: QueryOptions): Promise<RequestEntry[]> { /* ... */ }
153
+ async getRequest(id: string): Promise<RequestEntry | null> { /* ... */ }
154
+
155
+ async saveException(entry: ExceptionEntry): Promise<void> { /* ... */ }
156
+ async getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]> { /* ... */ }
157
+ async getException(id: string): Promise<ExceptionEntry | null> { /* ... */ }
158
+
159
+ async saveLog(entry: LogEntry): Promise<void> { /* ... */ }
160
+ async getLogs(options?: QueryOptions): Promise<LogEntry[]> { /* ... */ }
161
+
162
+ async prune(olderThan: Date): Promise<number> { /* ... */ }
163
+ async getStats(): Promise<TelescopeStats> { /* ... */ }
164
+ }
165
+ ```
166
+
167
+ ## API
168
+
169
+ ### Telescope Class (Core)
170
+
171
+ #### `telescope.recordRequest(entry)`
172
+
173
+ Record a request entry programmatically.
174
+
175
+ ```typescript
176
+ await telescope.recordRequest({
177
+ method: 'GET',
178
+ path: '/api/users',
179
+ url: 'http://localhost:3000/api/users',
180
+ headers: {},
181
+ query: {},
182
+ status: 200,
183
+ responseHeaders: {},
184
+ duration: 25.5,
185
+ });
186
+ ```
187
+
188
+ #### `telescope.log(level, message, context?, requestId?)`
189
+
190
+ Record a log entry programmatically.
191
+
192
+ ```typescript
193
+ await telescope.log('info', 'User logged in', { userId: '123' });
194
+ await telescope.log('error', 'Payment failed', { orderId: '456' });
195
+ ```
196
+
197
+ #### `telescope.exception(error, requestId?)`
198
+
199
+ Record an exception programmatically.
200
+
201
+ ```typescript
202
+ try {
203
+ await riskyOperation();
204
+ } catch (error) {
205
+ await telescope.exception(error);
206
+ throw error;
207
+ }
208
+ ```
209
+
210
+ #### `telescope.broadcast(event)`
211
+
212
+ Send a custom event to all connected WebSocket clients.
213
+
214
+ ```typescript
215
+ telescope.broadcast({
216
+ type: 'custom',
217
+ payload: { message: 'Hello' },
218
+ timestamp: Date.now(),
219
+ });
220
+ ```
221
+
222
+ #### `telescope.prune(olderThan)`
223
+
224
+ Manually prune entries older than a date.
225
+
226
+ ```typescript
227
+ const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
228
+ const deleted = await telescope.prune(oneHourAgo);
229
+ console.log(`Pruned ${deleted} entries`);
230
+ ```
231
+
232
+ #### `telescope.destroy()`
233
+
234
+ Clean up resources (intervals, WebSocket clients).
235
+
236
+ ```typescript
237
+ telescope.destroy();
238
+ ```
239
+
240
+ ### Hono Adapter
241
+
242
+ Import from `@geekmidas/telescope/server/hono`:
243
+
244
+ ```typescript
245
+ import { createMiddleware, createUI, setupWebSocket, getRequestId } from '@geekmidas/telescope/server/hono';
246
+ ```
247
+
248
+ #### `createMiddleware(telescope)`
249
+
250
+ Returns Hono middleware that captures requests and responses.
251
+
252
+ ```typescript
253
+ app.use('*', createMiddleware(telescope));
254
+ ```
255
+
256
+ #### `createUI(telescope)`
257
+
258
+ Returns a Hono app with dashboard UI and API routes.
259
+
260
+ ```typescript
261
+ app.route('/__telescope', createUI(telescope));
262
+ ```
263
+
264
+ #### `setupWebSocket(app, telescope, upgradeWebSocket)`
265
+
266
+ Set up WebSocket routes for real-time updates.
267
+
268
+ ```typescript
269
+ import { createNodeWebSocket } from '@hono/node-ws';
270
+
271
+ const { upgradeWebSocket } = createNodeWebSocket({ app });
272
+ setupWebSocket(createUI(telescope), telescope, upgradeWebSocket);
273
+ ```
274
+
275
+ #### `getRequestId(c)`
276
+
277
+ Get the request ID from Hono context (set by middleware).
278
+
279
+ ```typescript
280
+ app.get('/api/data', (c) => {
281
+ const requestId = getRequestId(c);
282
+ // Use requestId for correlated logging
283
+ telescope.log('info', 'Processing request', { data: 'value' }, requestId);
284
+ return c.json({ success: true });
285
+ });
286
+ ```
287
+
288
+ ### Logger Integrations
289
+
290
+ Telescope integrates with existing logging libraries so you don't need to change your application code.
291
+
292
+ #### Pino Transport
293
+
294
+ Use with Pino's multistream to send logs to both stdout and Telescope:
295
+
296
+ ```typescript
297
+ import pino from 'pino';
298
+ import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
299
+ import { createPinoDestination } from '@geekmidas/telescope/logger/pino';
300
+
301
+ const telescope = new Telescope({ storage: new InMemoryStorage() });
302
+
303
+ const logger = pino(
304
+ { level: 'debug' },
305
+ pino.multistream([
306
+ { stream: process.stdout },
307
+ { stream: createPinoDestination({ telescope }) }
308
+ ])
309
+ );
310
+
311
+ // Logs go to both console and Telescope
312
+ logger.info({ userId: '123' }, 'User logged in');
313
+ ```
314
+
315
+ #### TelescopeLogger (ConsoleLogger wrapper)
316
+
317
+ Wraps any Logger interface and forwards logs to Telescope:
318
+
319
+ ```typescript
320
+ import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
321
+ import { TelescopeLogger, createTelescopeLogger } from '@geekmidas/telescope/logger/console';
322
+ import { ConsoleLogger } from '@geekmidas/logger/console';
323
+
324
+ const telescope = new Telescope({ storage: new InMemoryStorage() });
325
+
326
+ // Option 1: Using the class
327
+ const logger = new TelescopeLogger({
328
+ telescope,
329
+ logger: new ConsoleLogger({ app: 'myApp' }),
330
+ });
331
+
332
+ // Option 2: Using the factory function
333
+ const logger = createTelescopeLogger(telescope, new ConsoleLogger());
334
+
335
+ // Logs go to both console and Telescope
336
+ logger.info({ action: 'startup' }, 'Application started');
337
+
338
+ // Create child loggers with context
339
+ const authLogger = logger.child({ module: 'auth' });
340
+ authLogger.debug({ userId: '123' }, 'User authenticated');
341
+
342
+ // Bind to a request ID for correlation
343
+ const requestLogger = logger.withRequestId('req-abc123');
344
+ requestLogger.info('Processing request');
345
+ ```
346
+
347
+ ## Dashboard API
348
+
349
+ The dashboard UI is served at the configured path. It also exposes a REST API:
350
+
351
+ | Endpoint | Description |
352
+ |----------|-------------|
353
+ | `GET /api/requests` | List requests |
354
+ | `GET /api/requests/:id` | Get request details |
355
+ | `GET /api/exceptions` | List exceptions |
356
+ | `GET /api/exceptions/:id` | Get exception details |
357
+ | `GET /api/logs` | List logs |
358
+ | `GET /api/stats` | Get storage statistics |
359
+
360
+ ### Query Parameters
361
+
362
+ All list endpoints support:
363
+
364
+ - `limit` - Number of entries (default: 50, max: 100)
365
+ - `offset` - Pagination offset (default: 0)
366
+ - `search` - Full-text search
367
+ - `before` - Entries before date (ISO string)
368
+ - `after` - Entries after date (ISO string)
369
+ - `tags` - Comma-separated tags filter
370
+
371
+ Example:
372
+ ```
373
+ GET /__telescope/api/requests?limit=20&search=POST&after=2024-01-01
374
+ ```
375
+
376
+ ## WebSocket
377
+
378
+ Connect to `/__telescope/ws` for real-time updates:
379
+
380
+ ```typescript
381
+ const ws = new WebSocket('ws://localhost:3000/__telescope/ws');
382
+
383
+ ws.onmessage = (event) => {
384
+ const { type, payload, timestamp } = JSON.parse(event.data);
385
+
386
+ switch (type) {
387
+ case 'request':
388
+ console.log('New request:', payload);
389
+ break;
390
+ case 'exception':
391
+ console.log('New exception:', payload);
392
+ break;
393
+ case 'log':
394
+ console.log('New log:', payload);
395
+ break;
396
+ case 'connected':
397
+ console.log('Connected, clients:', payload.clientCount);
398
+ break;
399
+ }
400
+ };
401
+ ```
402
+
403
+ ## Types
404
+
405
+ ### RequestEntry
406
+
407
+ ```typescript
408
+ interface RequestEntry {
409
+ id: string;
410
+ method: string;
411
+ path: string;
412
+ url: string;
413
+ headers: Record<string, string>;
414
+ body?: unknown;
415
+ query?: Record<string, string>;
416
+ status: number;
417
+ responseHeaders: Record<string, string>;
418
+ responseBody?: unknown;
419
+ duration: number;
420
+ timestamp: Date;
421
+ ip?: string;
422
+ userId?: string;
423
+ tags?: string[];
424
+ }
425
+ ```
426
+
427
+ ### ExceptionEntry
428
+
429
+ ```typescript
430
+ interface ExceptionEntry {
431
+ id: string;
432
+ name: string;
433
+ message: string;
434
+ stack: StackFrame[];
435
+ source?: SourceContext;
436
+ requestId?: string;
437
+ timestamp: Date;
438
+ handled: boolean;
439
+ tags?: string[];
440
+ }
441
+ ```
442
+
443
+ ### LogEntry
444
+
445
+ ```typescript
446
+ interface LogEntry {
447
+ id: string;
448
+ level: 'debug' | 'info' | 'warn' | 'error';
449
+ message: string;
450
+ context?: Record<string, unknown>;
451
+ requestId?: string;
452
+ timestamp: Date;
453
+ }
454
+ ```
455
+
456
+ ## Integration with gkm dev
457
+
458
+ When using `gkm dev`, Telescope is automatically integrated:
459
+
460
+ ```bash
461
+ gkm dev --port 3000
462
+
463
+ # Output:
464
+ # API: http://localhost:3000
465
+ # Telescope: http://localhost:3000/__telescope
466
+ ```
467
+
468
+ Configure in `gkm.config.ts`:
469
+
470
+ ```typescript
471
+ export default {
472
+ routes: './src/endpoints/**/*.ts',
473
+ envParser: './src/config/env',
474
+ logger: './src/logger',
475
+
476
+ dev: {
477
+ telescope: {
478
+ enabled: true,
479
+ path: '/__telescope',
480
+ },
481
+ },
482
+ };
483
+ ```
484
+
485
+ ## Best Practices
486
+
487
+ 1. **Disable in Production**: Use environment variables to disable Telescope in production
488
+ ```typescript
489
+ const telescope = new Telescope({
490
+ storage: new InMemoryStorage(),
491
+ enabled: process.env.NODE_ENV === 'development',
492
+ });
493
+ ```
494
+
495
+ 2. **Ignore Health Checks**: Exclude noisy endpoints
496
+ ```typescript
497
+ ignorePatterns: ['/health', '/ready', '/metrics']
498
+ ```
499
+
500
+ 3. **Limit Body Size**: Prevent memory issues with large payloads
501
+ ```typescript
502
+ maxBodySize: 32 * 1024 // 32KB
503
+ ```
504
+
505
+ 4. **Auto-Prune**: Enable automatic cleanup for long-running dev sessions
506
+ ```typescript
507
+ pruneAfterHours: 4
508
+ ```
509
+
510
+ 5. **Use Database Storage in Staging**: For debugging issues in staging environments
511
+ ```typescript
512
+ const storage = process.env.NODE_ENV === 'production'
513
+ ? null // Disabled
514
+ : process.env.NODE_ENV === 'staging'
515
+ ? new KyselyStorage(db)
516
+ : new InMemoryStorage();
517
+ ```
518
+
519
+ ## License
520
+
521
+ MIT