@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.
Files changed (122) hide show
  1. package/README.md +14 -4
  2. package/dist/{http-D5kn3_Mt.d.mts → http-BiNtg85L.d.mts} +3 -3
  3. package/dist/{http-D5kn3_Mt.d.mts.map → http-BiNtg85L.d.mts.map} +1 -1
  4. package/dist/instrumentation/http.d.mts +1 -1
  5. package/dist/instrumentation/index.cjs +2 -2
  6. package/dist/instrumentation/index.d.cts +2 -2
  7. package/dist/instrumentation/index.d.mts +3 -3
  8. package/dist/instrumentation/index.mjs +2 -2
  9. package/dist/instrumentation/middleware.cjs +1 -1
  10. package/dist/instrumentation/middleware.d.cts +1 -1
  11. package/dist/instrumentation/middleware.d.mts +1 -1
  12. package/dist/instrumentation/middleware.mjs +1 -1
  13. package/dist/instrumentation/otel.cjs +1 -1
  14. package/dist/instrumentation/otel.d.cts +1 -1
  15. package/dist/instrumentation/otel.d.mts +1 -1
  16. package/dist/instrumentation/otel.mjs +1 -1
  17. package/dist/logger/console.d.cts.map +1 -1
  18. package/dist/logger/pino.d.mts +2 -2
  19. package/dist/{middleware-DqPQpcup.cjs → middleware-Bcq6XXGG.cjs} +2 -2
  20. package/dist/middleware-Bcq6XXGG.cjs.map +1 -0
  21. package/dist/{middleware-4-GrQlOT.mjs → middleware-D1XEHvyy.mjs} +2 -2
  22. package/dist/middleware-D1XEHvyy.mjs.map +1 -0
  23. package/dist/{middleware-CA84nsNN.d.cts → middleware-PeyS-9Ny.d.cts} +2 -2
  24. package/dist/{middleware-CA84nsNN.d.cts.map → middleware-PeyS-9Ny.d.cts.map} +1 -1
  25. package/dist/{middleware-DAceVpfw.d.mts → middleware-oHvo_ZgU.d.mts} +2 -2
  26. package/dist/{middleware-DAceVpfw.d.mts.map → middleware-oHvo_ZgU.d.mts.map} +1 -1
  27. package/dist/{otel-40udNP5g.mjs → otel-B9bNoeZ2.mjs} +3 -3
  28. package/dist/otel-B9bNoeZ2.mjs.map +1 -0
  29. package/dist/{otel-SBVoVEHr.d.cts → otel-Bk89b1lX.d.cts} +3 -3
  30. package/dist/{otel-1bJPb2Z7.d.mts.map → otel-Bk89b1lX.d.cts.map} +1 -1
  31. package/dist/{otel-COrpz_GZ.cjs → otel-BzjOtVxZ.cjs} +3 -3
  32. package/dist/otel-BzjOtVxZ.cjs.map +1 -0
  33. package/dist/{otel-1bJPb2Z7.d.mts → otel-lPMsM26e.d.mts} +3 -3
  34. package/dist/{otel-SBVoVEHr.d.cts.map → otel-lPMsM26e.d.mts.map} +1 -1
  35. package/dist/storage/kysely.cjs +48 -38
  36. package/dist/storage/kysely.cjs.map +1 -1
  37. package/dist/storage/kysely.d.cts +29 -9
  38. package/dist/storage/kysely.d.cts.map +1 -1
  39. package/dist/storage/kysely.d.mts +29 -9
  40. package/dist/storage/kysely.d.mts.map +1 -1
  41. package/dist/storage/kysely.mjs +48 -38
  42. package/dist/storage/kysely.mjs.map +1 -1
  43. package/dist/tracing-PnECEbEu.d.mts.map +1 -1
  44. package/package.json +6 -3
  45. package/CHANGELOG.md +0 -72
  46. package/dist/middleware-4-GrQlOT.mjs.map +0 -1
  47. package/dist/middleware-DqPQpcup.cjs.map +0 -1
  48. package/dist/otel-40udNP5g.mjs.map +0 -1
  49. package/dist/otel-COrpz_GZ.cjs.map +0 -1
  50. package/src/Telescope.ts +0 -561
  51. package/src/__tests__/Telescope.spec.ts +0 -438
  52. package/src/__tests__/pino-transport.spec.ts +0 -115
  53. package/src/__tests__/redact.spec.ts +0 -336
  54. package/src/adapters/__tests__/lambda.spec.ts +0 -318
  55. package/src/adapters/__tests__/middy.spec.ts +0 -741
  56. package/src/adapters/index.ts +0 -22
  57. package/src/adapters/lambda.ts +0 -432
  58. package/src/adapters/middy.ts +0 -402
  59. package/src/adapters/types.ts +0 -181
  60. package/src/core.ts +0 -51
  61. package/src/index.ts +0 -34
  62. package/src/instrumentation/__tests__/core.spec.ts +0 -248
  63. package/src/instrumentation/__tests__/hono.spec.ts +0 -348
  64. package/src/instrumentation/__tests__/middleware.spec.ts +0 -528
  65. package/src/instrumentation/__tests__/otel.spec.ts +0 -291
  66. package/src/instrumentation/__tests__/tracing.spec.ts +0 -208
  67. package/src/instrumentation/core.ts +0 -228
  68. package/src/instrumentation/hono.ts +0 -270
  69. package/src/instrumentation/http.ts +0 -306
  70. package/src/instrumentation/index.ts +0 -90
  71. package/src/instrumentation/middleware.ts +0 -297
  72. package/src/instrumentation/otel.ts +0 -271
  73. package/src/instrumentation/setup.ts +0 -246
  74. package/src/instrumentation/tracing.ts +0 -145
  75. package/src/logger/__tests__/console.spec.ts +0 -366
  76. package/src/logger/__tests__/pino.spec.ts +0 -217
  77. package/src/logger/console.ts +0 -230
  78. package/src/logger/pino.ts +0 -201
  79. package/src/metrics/MetricsAggregator.ts +0 -555
  80. package/src/metrics/__tests__/MetricsAggregator.spec.ts +0 -298
  81. package/src/metrics/index.ts +0 -2
  82. package/src/metrics/storage/InMemoryMetricsStorage.ts +0 -380
  83. package/src/metrics/storage/MetricsStorage.ts +0 -74
  84. package/src/metrics/storage/index.ts +0 -3
  85. package/src/metrics/storage/types.ts +0 -243
  86. package/src/otlp/__tests__/receiver.spec.ts +0 -367
  87. package/src/otlp/__tests__/transformer.spec.ts +0 -455
  88. package/src/otlp/hono.ts +0 -120
  89. package/src/otlp/index.ts +0 -22
  90. package/src/otlp/receiver.ts +0 -162
  91. package/src/otlp/transformer.ts +0 -367
  92. package/src/otlp/types.ts +0 -364
  93. package/src/redact.ts +0 -116
  94. package/src/server/__tests__/hono.spec.ts +0 -814
  95. package/src/server/hono.ts +0 -442
  96. package/src/storage/__tests__/kysely.spec.ts +0 -715
  97. package/src/storage/__tests__/memory.spec.ts +0 -694
  98. package/src/storage/kysely.ts +0 -599
  99. package/src/storage/memory.ts +0 -204
  100. package/src/types.ts +0 -447
  101. package/src/ui-assets.ts +0 -40
  102. package/tsconfig.json +0 -9
  103. package/ui/CHANGELOG.md +0 -7
  104. package/ui/index.html +0 -12
  105. package/ui/node_modules/.bin/tsc +0 -21
  106. package/ui/node_modules/.bin/tsserver +0 -21
  107. package/ui/node_modules/.bin/vite +0 -21
  108. package/ui/package.json +0 -24
  109. package/ui/src/App.tsx +0 -595
  110. package/ui/src/api.ts +0 -88
  111. package/ui/src/components/ExceptionDetail.tsx +0 -100
  112. package/ui/src/components/FilterBar.tsx +0 -162
  113. package/ui/src/components/LogDetail.tsx +0 -91
  114. package/ui/src/components/RequestDetail.tsx +0 -143
  115. package/ui/src/main.tsx +0 -10
  116. package/ui/src/styles.css +0 -11
  117. package/ui/src/types.ts +0 -63
  118. package/ui/src/vite-env.d.ts +0 -1
  119. package/ui/src/vite-plugin-gkm-config.ts +0 -54
  120. package/ui/tsconfig.json +0 -20
  121. package/ui/tsconfig.tsbuildinfo +0 -1
  122. 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
- }