@geekmidas/telescope 9.0.2 → 10.0.0-alpha.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.
Files changed (128) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-DULM1u2L.d.cts} +3 -3
  3. package/dist/{http-Dvca6fC3.d.cts.map → http-DULM1u2L.d.cts.map} +1 -1
  4. package/dist/{http-Dvca6fC3.d.cts → http-GB-Nlvwc.d.mts} +3 -3
  5. package/dist/{http-D5kn3_Mt.d.mts.map → http-GB-Nlvwc.d.mts.map} +1 -1
  6. package/dist/instrumentation/http.d.cts +1 -1
  7. package/dist/instrumentation/http.d.mts +1 -1
  8. package/dist/instrumentation/index.cjs +2 -2
  9. package/dist/instrumentation/index.d.cts +3 -3
  10. package/dist/instrumentation/index.d.mts +3 -3
  11. package/dist/instrumentation/index.mjs +2 -2
  12. package/dist/instrumentation/middleware.cjs +1 -1
  13. package/dist/instrumentation/middleware.d.cts +1 -1
  14. package/dist/instrumentation/middleware.d.mts +1 -1
  15. package/dist/instrumentation/middleware.mjs +1 -1
  16. package/dist/instrumentation/otel.cjs +1 -1
  17. package/dist/instrumentation/otel.d.cts +1 -1
  18. package/dist/instrumentation/otel.d.mts +1 -1
  19. package/dist/instrumentation/otel.mjs +1 -1
  20. package/dist/logger/console.d.cts.map +1 -1
  21. package/dist/logger/console.d.mts.map +1 -1
  22. package/dist/logger/pino.d.cts +2 -2
  23. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  24. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  25. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  26. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  27. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  28. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  29. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  30. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  31. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  32. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-Bk89b1lX.d.cts} +3 -3
  34. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  35. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  36. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  37. package/dist/{otel-SBVoVEHr.d.cts → otel-lPMsM26e.d.mts} +3 -3
  38. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  39. package/dist/storage/kysely.cjs +48 -38
  40. package/dist/storage/kysely.cjs.map +1 -1
  41. package/dist/storage/kysely.d.cts +29 -9
  42. package/dist/storage/kysely.d.cts.map +1 -1
  43. package/dist/storage/kysely.d.mts +29 -9
  44. package/dist/storage/kysely.d.mts.map +1 -1
  45. package/dist/storage/kysely.mjs +48 -38
  46. package/dist/storage/kysely.mjs.map +1 -1
  47. package/dist/tracing-BX8uY2nR.d.cts.map +1 -1
  48. package/dist/types-B1EIa_cQ.d.mts.map +1 -1
  49. package/dist/types-l7Rlb5Mg.d.cts.map +1 -1
  50. package/package.json +6 -3
  51. package/CHANGELOG.md +0 -72
  52. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  53. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  54. package/dist/otel-40udNP5g.mjs.map +0 -1
  55. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  56. package/src/Telescope.ts +0 -561
  57. package/src/__tests__/Telescope.spec.ts +0 -438
  58. package/src/__tests__/pino-transport.spec.ts +0 -115
  59. package/src/__tests__/redact.spec.ts +0 -336
  60. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  61. package/src/adapters/__tests__/middy.spec.ts +0 -741
  62. package/src/adapters/index.ts +0 -22
  63. package/src/adapters/lambda.ts +0 -432
  64. package/src/adapters/middy.ts +0 -402
  65. package/src/adapters/types.ts +0 -181
  66. package/src/core.ts +0 -51
  67. package/src/index.ts +0 -34
  68. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  69. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  70. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  71. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  72. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  73. package/src/instrumentation/core.ts +0 -228
  74. package/src/instrumentation/hono.ts +0 -270
  75. package/src/instrumentation/http.ts +0 -306
  76. package/src/instrumentation/index.ts +0 -90
  77. package/src/instrumentation/middleware.ts +0 -297
  78. package/src/instrumentation/otel.ts +0 -271
  79. package/src/instrumentation/setup.ts +0 -246
  80. package/src/instrumentation/tracing.ts +0 -145
  81. package/src/logger/__tests__/console.spec.ts +0 -366
  82. package/src/logger/__tests__/pino.spec.ts +0 -217
  83. package/src/logger/console.ts +0 -230
  84. package/src/logger/pino.ts +0 -201
  85. package/src/metrics/MetricsAggregator.ts +0 -555
  86. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  87. package/src/metrics/index.ts +0 -2
  88. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  89. package/src/metrics/storage/MetricsStorage.ts +0 -74
  90. package/src/metrics/storage/index.ts +0 -3
  91. package/src/metrics/storage/types.ts +0 -243
  92. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  93. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  94. package/src/otlp/hono.ts +0 -120
  95. package/src/otlp/index.ts +0 -22
  96. package/src/otlp/receiver.ts +0 -162
  97. package/src/otlp/transformer.ts +0 -367
  98. package/src/otlp/types.ts +0 -364
  99. package/src/redact.ts +0 -116
  100. package/src/server/__tests__/hono.spec.ts +0 -814
  101. package/src/server/hono.ts +0 -442
  102. package/src/storage/__tests__/kysely.spec.ts +0 -715
  103. package/src/storage/__tests__/memory.spec.ts +0 -694
  104. package/src/storage/kysely.ts +0 -599
  105. package/src/storage/memory.ts +0 -204
  106. package/src/types.ts +0 -447
  107. package/src/ui-assets.ts +0 -40
  108. package/tsconfig.json +0 -9
  109. package/ui/CHANGELOG.md +0 -7
  110. package/ui/index.html +0 -12
  111. package/ui/node_modules/.bin/tsc +0 -21
  112. package/ui/node_modules/.bin/tsserver +0 -21
  113. package/ui/node_modules/.bin/vite +0 -21
  114. package/ui/package.json +0 -24
  115. package/ui/src/App.tsx +0 -595
  116. package/ui/src/api.ts +0 -88
  117. package/ui/src/components/ExceptionDetail.tsx +0 -100
  118. package/ui/src/components/FilterBar.tsx +0 -162
  119. package/ui/src/components/LogDetail.tsx +0 -91
  120. package/ui/src/components/RequestDetail.tsx +0 -143
  121. package/ui/src/main.tsx +0 -10
  122. package/ui/src/styles.css +0 -11
  123. package/ui/src/types.ts +0 -63
  124. package/ui/src/vite-env.d.ts +0 -1
  125. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  126. package/ui/tsconfig.json +0 -20
  127. package/ui/tsconfig.tsbuildinfo +0 -1
  128. package/ui/vite.config.ts +0 -13
package/src/Telescope.ts DELETED
@@ -1,561 +0,0 @@
1
- import { nanoid } from 'nanoid';
2
- import {
3
- MetricsAggregator,
4
- type MetricsAggregatorOptions,
5
- } from './metrics/MetricsAggregator';
6
- import { createRedactor, type Redactor } from './redact';
7
- import type {
8
- EndpointDetails,
9
- EndpointMetrics,
10
- ExceptionEntry,
11
- LogEntry,
12
- MetricsQueryOptions,
13
- MetricsSnapshot,
14
- NormalizedTelescopeOptions,
15
- QueryOptions,
16
- RequestEntry,
17
- RequestMetrics,
18
- StackFrame,
19
- StatusDistribution,
20
- TelescopeEvent,
21
- TelescopeOptions,
22
- TelescopeStorage,
23
- } from './types';
24
-
25
- /**
26
- * Framework-agnostic Telescope class for debugging and monitoring applications.
27
- * Use framework-specific adapters (e.g., @geekmidas/telescope/hono) for integration.
28
- */
29
- export class Telescope {
30
- private storage: TelescopeStorage;
31
- private options: NormalizedTelescopeOptions;
32
- private wsClients = new Set<WebSocket>();
33
- private pruneInterval?: ReturnType<typeof setInterval>;
34
- private metricsBroadcastInterval?: ReturnType<typeof setInterval>;
35
- private redactor?: Redactor;
36
- private metricsAggregator: MetricsAggregator;
37
-
38
- constructor(
39
- options: TelescopeOptions & { metrics?: MetricsAggregatorOptions },
40
- ) {
41
- this.storage = options.storage;
42
- this.options = this.normalizeOptions(options);
43
- this.redactor = createRedactor(options.redact);
44
- this.metricsAggregator = new MetricsAggregator(options.metrics);
45
-
46
- // Set up auto-pruning if configured
47
- if (this.options.pruneAfterHours) {
48
- const intervalMs = 60 * 60 * 1000; // 1 hour
49
- this.pruneInterval = setInterval(() => {
50
- this.autoPrune().catch(console.error);
51
- }, intervalMs);
52
- }
53
- }
54
-
55
- // ============================================
56
- // Public API - Recording
57
- // ============================================
58
-
59
- /**
60
- * Record a request entry
61
- */
62
- async recordRequest(
63
- entry: Omit<RequestEntry, 'id' | 'timestamp'>,
64
- ): Promise<string> {
65
- if (!this.options.enabled) return '';
66
-
67
- // Use injected request ID from context if available, otherwise generate one
68
- const id = this.options.getRequestId?.() ?? nanoid();
69
- let fullEntry: RequestEntry = {
70
- ...entry,
71
- id,
72
- timestamp: new Date(),
73
- };
74
-
75
- // Apply redaction if configured
76
- if (this.redactor) {
77
- fullEntry = this.redactor(fullEntry);
78
- }
79
-
80
- // Record metrics (before storage for real-time aggregation)
81
- this.metricsAggregator.record(fullEntry);
82
-
83
- await this.storage.saveRequest(fullEntry);
84
- this.broadcast({
85
- type: 'request',
86
- payload: fullEntry,
87
- timestamp: Date.now(),
88
- });
89
- return id;
90
- }
91
-
92
- /**
93
- * Log entry input for batch operations
94
- */
95
- private async saveLogEntries(entries: LogEntry[]): Promise<void> {
96
- if (this.storage.saveLogs) {
97
- await this.storage.saveLogs(entries);
98
- } else {
99
- await Promise.all(entries.map((entry) => this.storage.saveLog(entry)));
100
- }
101
-
102
- for (const entry of entries) {
103
- this.broadcast({ type: 'log', payload: entry, timestamp: Date.now() });
104
- }
105
- }
106
-
107
- /**
108
- * Record log entries in batch.
109
- * More efficient than individual calls for database storage.
110
- *
111
- * @example
112
- * await telescope.log([
113
- * { level: 'info', message: 'Request started' },
114
- * { level: 'debug', message: 'Processing...', context: { step: 1 } },
115
- * ]);
116
- */
117
- async log(
118
- entries: Array<{
119
- level: 'debug' | 'info' | 'warn' | 'error';
120
- message: string;
121
- context?: Record<string, unknown>;
122
- requestId?: string;
123
- }>,
124
- ): Promise<void> {
125
- if (!this.options.enabled || entries.length === 0) return;
126
-
127
- const timestamp = new Date();
128
- const logEntries: LogEntry[] = entries.map((e) => {
129
- const entry: LogEntry = {
130
- id: nanoid(),
131
- level: e.level,
132
- message: e.message,
133
- context: e.context,
134
- requestId: e.requestId,
135
- timestamp,
136
- };
137
- return this.redactor ? this.redactor(entry) : entry;
138
- });
139
-
140
- await this.saveLogEntries(logEntries);
141
- }
142
-
143
- /**
144
- * Log a debug message
145
- */
146
- async debug(
147
- message: string,
148
- context?: Record<string, unknown>,
149
- requestId?: string,
150
- ): Promise<void> {
151
- return this.logSingle('debug', message, context, requestId);
152
- }
153
-
154
- /**
155
- * Log an info message
156
- */
157
- async info(
158
- message: string,
159
- context?: Record<string, unknown>,
160
- requestId?: string,
161
- ): Promise<void> {
162
- return this.logSingle('info', message, context, requestId);
163
- }
164
-
165
- /**
166
- * Log a warning message
167
- */
168
- async warn(
169
- message: string,
170
- context?: Record<string, unknown>,
171
- requestId?: string,
172
- ): Promise<void> {
173
- return this.logSingle('warn', message, context, requestId);
174
- }
175
-
176
- /**
177
- * Log an error message
178
- */
179
- async error(
180
- message: string,
181
- context?: Record<string, unknown>,
182
- requestId?: string,
183
- ): Promise<void> {
184
- return this.logSingle('error', message, context, requestId);
185
- }
186
-
187
- private async logSingle(
188
- level: LogEntry['level'],
189
- message: string,
190
- context?: Record<string, unknown>,
191
- requestId?: string,
192
- ): Promise<void> {
193
- if (!this.options.enabled) return;
194
-
195
- let entry: LogEntry = {
196
- id: nanoid(),
197
- level,
198
- message,
199
- context,
200
- requestId,
201
- timestamp: new Date(),
202
- };
203
-
204
- if (this.redactor) {
205
- entry = this.redactor(entry);
206
- }
207
-
208
- await this.storage.saveLog(entry);
209
- this.broadcast({ type: 'log', payload: entry, timestamp: Date.now() });
210
- }
211
-
212
- /**
213
- * Record an exception
214
- */
215
- async exception(error: Error, requestId?: string): Promise<void> {
216
- if (!this.options.enabled) return;
217
-
218
- const stack = this.parseStack(error.stack || '');
219
-
220
- const entry: ExceptionEntry = {
221
- id: nanoid(),
222
- name: error.name,
223
- message: error.message,
224
- stack,
225
- requestId,
226
- timestamp: new Date(),
227
- handled: false,
228
- };
229
-
230
- await this.storage.saveException(entry);
231
- this.broadcast({
232
- type: 'exception',
233
- payload: entry,
234
- timestamp: Date.now(),
235
- });
236
- }
237
-
238
- // ============================================
239
- // Public API - Data Access
240
- // ============================================
241
-
242
- /**
243
- * Get requests from storage
244
- */
245
- async getRequests(options?: QueryOptions): Promise<RequestEntry[]> {
246
- return this.storage.getRequests(options);
247
- }
248
-
249
- /**
250
- * Get a single request by ID
251
- */
252
- async getRequest(id: string): Promise<RequestEntry | null> {
253
- return this.storage.getRequest(id);
254
- }
255
-
256
- /**
257
- * Get exceptions from storage
258
- */
259
- async getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]> {
260
- return this.storage.getExceptions(options);
261
- }
262
-
263
- /**
264
- * Get a single exception by ID
265
- */
266
- async getException(id: string): Promise<ExceptionEntry | null> {
267
- return this.storage.getException(id);
268
- }
269
-
270
- /**
271
- * Get logs from storage
272
- */
273
- async getLogs(options?: QueryOptions): Promise<LogEntry[]> {
274
- return this.storage.getLogs(options);
275
- }
276
-
277
- /**
278
- * Get storage statistics
279
- */
280
- async getStats() {
281
- return this.storage.getStats();
282
- }
283
-
284
- // ============================================
285
- // Public API - Metrics
286
- // ============================================
287
-
288
- /**
289
- * Get aggregated request metrics
290
- */
291
- getMetrics(options?: MetricsQueryOptions): RequestMetrics {
292
- return this.metricsAggregator.getMetrics(options);
293
- }
294
-
295
- /**
296
- * Get metrics for individual endpoints
297
- */
298
- getEndpointMetrics(options?: MetricsQueryOptions): EndpointMetrics[] {
299
- return this.metricsAggregator.getEndpointMetrics(options);
300
- }
301
-
302
- /**
303
- * Get detailed metrics for a specific endpoint
304
- */
305
- getEndpointDetails(
306
- method: string,
307
- path: string,
308
- options?: MetricsQueryOptions,
309
- ): EndpointDetails | null {
310
- return this.metricsAggregator.getEndpointDetails(method, path, options);
311
- }
312
-
313
- /**
314
- * Get status code distribution
315
- */
316
- getStatusDistribution(options?: MetricsQueryOptions): StatusDistribution {
317
- return this.metricsAggregator.getStatusDistribution(options);
318
- }
319
-
320
- /**
321
- * Reset metrics data
322
- */
323
- resetMetrics(): void {
324
- this.metricsAggregator.reset();
325
- }
326
-
327
- /**
328
- * Get a real-time metrics snapshot for broadcasting
329
- */
330
- getMetricsSnapshot(): MetricsSnapshot {
331
- const metrics = this.metricsAggregator.getMetrics();
332
- const statusDist = this.metricsAggregator.getStatusDistribution();
333
-
334
- return {
335
- timestamp: Date.now(),
336
- totalRequests: metrics.totalRequests,
337
- requestsPerSecond: metrics.requestsPerSecond,
338
- avgDuration: metrics.avgDuration,
339
- errorRate: metrics.errorRate,
340
- p50: metrics.p50Duration,
341
- p95: metrics.p95Duration,
342
- p99: metrics.p99Duration,
343
- statusDistribution: statusDist,
344
- };
345
- }
346
-
347
- /**
348
- * Start broadcasting metrics to WebSocket clients at a given interval.
349
- * Call this after setting up WebSocket to enable real-time metrics updates.
350
- *
351
- * @param intervalMs - Broadcast interval in milliseconds (default: 5000)
352
- */
353
- startMetricsBroadcast(intervalMs = 5000): void {
354
- if (this.metricsBroadcastInterval) {
355
- return; // Already running
356
- }
357
-
358
- this.metricsBroadcastInterval = setInterval(() => {
359
- if (this.wsClients.size > 0) {
360
- const snapshot = this.getMetricsSnapshot();
361
- this.broadcast({
362
- type: 'metrics',
363
- payload: snapshot,
364
- timestamp: Date.now(),
365
- });
366
- }
367
- }, intervalMs);
368
- }
369
-
370
- /**
371
- * Stop broadcasting metrics to WebSocket clients
372
- */
373
- stopMetricsBroadcast(): void {
374
- if (this.metricsBroadcastInterval) {
375
- clearInterval(this.metricsBroadcastInterval);
376
- this.metricsBroadcastInterval = undefined;
377
- }
378
- }
379
-
380
- // ============================================
381
- // Public API - WebSocket
382
- // ============================================
383
-
384
- /**
385
- * Add a WebSocket client for real-time updates
386
- */
387
- addWsClient(ws: WebSocket): void {
388
- this.wsClients.add(ws);
389
- this.broadcast({
390
- type: 'connected',
391
- payload: { clientCount: this.wsClients.size },
392
- timestamp: Date.now(),
393
- });
394
- }
395
-
396
- /**
397
- * Remove a WebSocket client
398
- */
399
- removeWsClient(ws: WebSocket): void {
400
- this.wsClients.delete(ws);
401
- }
402
-
403
- /**
404
- * Broadcast an event to all connected WebSocket clients
405
- */
406
- broadcast(event: TelescopeEvent): void {
407
- const data = JSON.stringify(event);
408
- for (const client of this.wsClients) {
409
- try {
410
- client.send(data);
411
- } catch {
412
- this.wsClients.delete(client);
413
- }
414
- }
415
- }
416
-
417
- // ============================================
418
- // Public API - Lifecycle
419
- // ============================================
420
-
421
- /**
422
- * Manually prune old entries
423
- */
424
- async prune(olderThan: Date): Promise<number> {
425
- return this.storage.prune(olderThan);
426
- }
427
-
428
- /**
429
- * Clean up resources
430
- */
431
- destroy(): void {
432
- if (this.pruneInterval) {
433
- clearInterval(this.pruneInterval);
434
- }
435
- this.stopMetricsBroadcast();
436
- this.wsClients.clear();
437
- }
438
-
439
- // ============================================
440
- // Public API - Configuration
441
- // ============================================
442
-
443
- /**
444
- * Get the telescope path
445
- */
446
- get path(): string {
447
- return this.options.path;
448
- }
449
-
450
- /**
451
- * Check if telescope is enabled
452
- */
453
- get enabled(): boolean {
454
- return this.options.enabled;
455
- }
456
-
457
- /**
458
- * Check if body recording is enabled
459
- */
460
- get recordBody(): boolean {
461
- return this.options.recordBody;
462
- }
463
-
464
- /**
465
- * Get max body size
466
- */
467
- get maxBodySize(): number {
468
- return this.options.maxBodySize;
469
- }
470
-
471
- /**
472
- * Check if a path should be ignored
473
- */
474
- shouldIgnore(path: string): boolean {
475
- // Always ignore telescope's own routes
476
- if (path.startsWith(this.options.path)) {
477
- return true;
478
- }
479
-
480
- return this.options.ignorePatterns.some((pattern) => {
481
- if (pattern.includes('*')) {
482
- const regex = new RegExp(
483
- `^${pattern.replace(/\*/g, '.*').replace(/\?/g, '.')}$`,
484
- );
485
- return regex.test(path);
486
- }
487
- return path.startsWith(pattern);
488
- });
489
- }
490
-
491
- // ============================================
492
- // Private Methods
493
- // ============================================
494
-
495
- private normalizeOptions(
496
- options: TelescopeOptions,
497
- ): NormalizedTelescopeOptions {
498
- return {
499
- storage: options.storage,
500
- enabled: options.enabled ?? true,
501
- path: options.path ?? '/__telescope',
502
- recordBody: options.recordBody ?? true,
503
- maxBodySize: options.maxBodySize ?? 64 * 1024, // 64KB
504
- ignorePatterns: options.ignorePatterns ?? [],
505
- pruneAfterHours: options.pruneAfterHours,
506
- getRequestId: options.getRequestId,
507
- };
508
- }
509
-
510
- private parseStack(stack: string): StackFrame[] {
511
- const lines = stack.split('\n').slice(1);
512
- const frames: StackFrame[] = [];
513
-
514
- for (const line of lines) {
515
- // Match: " at functionName (file:line:column)"
516
- // or: " at file:line:column"
517
- const match =
518
- line.match(/at\s+(.+?)\s+\((.+):(\d+):(\d+)\)/) ||
519
- line.match(/at\s+(.+):(\d+):(\d+)/);
520
-
521
- if (match) {
522
- if (
523
- match.length === 5 &&
524
- match[1] &&
525
- match[2] &&
526
- match[3] &&
527
- match[4]
528
- ) {
529
- // Has function name
530
- frames.push({
531
- function: match[1],
532
- file: match[2],
533
- line: parseInt(match[3], 10),
534
- column: parseInt(match[4], 10),
535
- isApp: !match[2].includes('node_modules'),
536
- });
537
- } else if (match.length === 4 && match[1] && match[2] && match[3]) {
538
- // No function name
539
- frames.push({
540
- function: '<anonymous>',
541
- file: match[1],
542
- line: parseInt(match[2], 10),
543
- column: parseInt(match[3], 10),
544
- isApp: !match[1].includes('node_modules'),
545
- });
546
- }
547
- }
548
- }
549
-
550
- return frames;
551
- }
552
-
553
- private async autoPrune(): Promise<void> {
554
- if (!this.options.pruneAfterHours) return;
555
-
556
- const olderThan = new Date(
557
- Date.now() - this.options.pruneAfterHours * 60 * 60 * 1000,
558
- );
559
- await this.storage.prune(olderThan);
560
- }
561
- }