@adonis-agora/telescope 0.2.0 → 0.3.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 (120) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/providers/telescope_provider.d.ts +16 -0
  3. package/dist/providers/telescope_provider.d.ts.map +1 -1
  4. package/dist/providers/telescope_provider.js +33 -2
  5. package/dist/providers/telescope_provider.js.map +1 -1
  6. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  7. package/dist/providers/telescope_ui_provider.js +87 -1
  8. package/dist/providers/telescope_ui_provider.js.map +1 -1
  9. package/dist/providers/telescope_watchers_provider.d.ts +11 -0
  10. package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
  11. package/dist/providers/telescope_watchers_provider.js +58 -0
  12. package/dist/providers/telescope_watchers_provider.js.map +1 -1
  13. package/dist/src/define_config.d.ts +50 -0
  14. package/dist/src/define_config.d.ts.map +1 -1
  15. package/dist/src/define_config.js +9 -0
  16. package/dist/src/define_config.js.map +1 -1
  17. package/dist/src/index.d.ts +23 -2
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +17 -1
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/metrics/metrics_service.d.ts +63 -0
  22. package/dist/src/metrics/metrics_service.d.ts.map +1 -0
  23. package/dist/src/metrics/metrics_service.js +116 -0
  24. package/dist/src/metrics/metrics_service.js.map +1 -0
  25. package/dist/src/metrics/rollup.d.ts +61 -0
  26. package/dist/src/metrics/rollup.d.ts.map +1 -0
  27. package/dist/src/metrics/rollup.js +114 -0
  28. package/dist/src/metrics/rollup.js.map +1 -0
  29. package/dist/src/metrics/stats.d.ts +112 -0
  30. package/dist/src/metrics/stats.d.ts.map +1 -0
  31. package/dist/src/metrics/stats.js +255 -0
  32. package/dist/src/metrics/stats.js.map +1 -0
  33. package/dist/src/metrics/timeseries.d.ts +22 -0
  34. package/dist/src/metrics/timeseries.d.ts.map +1 -0
  35. package/dist/src/metrics/timeseries.js +34 -0
  36. package/dist/src/metrics/timeseries.js.map +1 -0
  37. package/dist/src/metrics/traces.d.ts +27 -0
  38. package/dist/src/metrics/traces.d.ts.map +1 -0
  39. package/dist/src/metrics/traces.js +71 -0
  40. package/dist/src/metrics/traces.js.map +1 -0
  41. package/dist/src/metrics/waterfall.d.ts +44 -0
  42. package/dist/src/metrics/waterfall.d.ts.map +1 -0
  43. package/dist/src/metrics/waterfall.js +99 -0
  44. package/dist/src/metrics/waterfall.js.map +1 -0
  45. package/dist/src/query/n_plus_one.d.ts +60 -0
  46. package/dist/src/query/n_plus_one.d.ts.map +1 -0
  47. package/dist/src/query/n_plus_one.js +102 -0
  48. package/dist/src/query/n_plus_one.js.map +1 -0
  49. package/dist/src/registry.d.ts +9 -0
  50. package/dist/src/registry.d.ts.map +1 -1
  51. package/dist/src/registry.js +6 -0
  52. package/dist/src/registry.js.map +1 -1
  53. package/dist/src/sampling/sampling.d.ts +52 -0
  54. package/dist/src/sampling/sampling.d.ts.map +1 -0
  55. package/dist/src/sampling/sampling.js +111 -0
  56. package/dist/src/sampling/sampling.js.map +1 -0
  57. package/dist/src/sampling/sampling_store.d.ts +33 -0
  58. package/dist/src/sampling/sampling_store.d.ts.map +1 -0
  59. package/dist/src/sampling/sampling_store.js +65 -0
  60. package/dist/src/sampling/sampling_store.js.map +1 -0
  61. package/dist/src/stream/entry_events.d.ts +45 -0
  62. package/dist/src/stream/entry_events.d.ts.map +1 -0
  63. package/dist/src/stream/entry_events.js +56 -0
  64. package/dist/src/stream/entry_events.js.map +1 -0
  65. package/dist/src/stream/index.d.ts +6 -0
  66. package/dist/src/stream/index.d.ts.map +1 -0
  67. package/dist/src/stream/index.js +5 -0
  68. package/dist/src/stream/index.js.map +1 -0
  69. package/dist/src/stream/stream_handler.d.ts +42 -0
  70. package/dist/src/stream/stream_handler.d.ts.map +1 -0
  71. package/dist/src/stream/stream_handler.js +48 -0
  72. package/dist/src/stream/stream_handler.js.map +1 -0
  73. package/dist/src/stream/streaming_store.d.ts +31 -0
  74. package/dist/src/stream/streaming_store.d.ts.map +1 -0
  75. package/dist/src/stream/streaming_store.js +49 -0
  76. package/dist/src/stream/streaming_store.js.map +1 -0
  77. package/dist/src/ui/api.d.ts +39 -1
  78. package/dist/src/ui/api.d.ts.map +1 -1
  79. package/dist/src/ui/api.js +116 -1
  80. package/dist/src/ui/api.js.map +1 -1
  81. package/dist/src/ui/define_config.d.ts +27 -0
  82. package/dist/src/ui/define_config.d.ts.map +1 -1
  83. package/dist/src/ui/define_config.js +6 -0
  84. package/dist/src/ui/define_config.js.map +1 -1
  85. package/dist/src/ui/http.d.ts +40 -0
  86. package/dist/src/ui/http.d.ts.map +1 -1
  87. package/dist/src/ui/http.js +43 -0
  88. package/dist/src/ui/http.js.map +1 -1
  89. package/dist/src/ui/index.d.ts +5 -3
  90. package/dist/src/ui/index.d.ts.map +1 -1
  91. package/dist/src/ui/index.js +3 -1
  92. package/dist/src/ui/index.js.map +1 -1
  93. package/dist/src/ui/request_replay.d.ts +56 -0
  94. package/dist/src/ui/request_replay.d.ts.map +1 -0
  95. package/dist/src/ui/request_replay.js +120 -0
  96. package/dist/src/ui/request_replay.js.map +1 -0
  97. package/dist/src/watchers/define_config.d.ts +17 -1
  98. package/dist/src/watchers/define_config.d.ts.map +1 -1
  99. package/dist/src/watchers/define_config.js +13 -0
  100. package/dist/src/watchers/define_config.js.map +1 -1
  101. package/dist/src/watchers/events_watcher.d.ts +63 -0
  102. package/dist/src/watchers/events_watcher.d.ts.map +1 -0
  103. package/dist/src/watchers/events_watcher.js +81 -0
  104. package/dist/src/watchers/events_watcher.js.map +1 -0
  105. package/dist/src/watchers/index.d.ts +6 -0
  106. package/dist/src/watchers/index.d.ts.map +1 -1
  107. package/dist/src/watchers/index.js +6 -0
  108. package/dist/src/watchers/index.js.map +1 -1
  109. package/dist/src/watchers/queue_watcher.d.ts +97 -0
  110. package/dist/src/watchers/queue_watcher.d.ts.map +1 -0
  111. package/dist/src/watchers/queue_watcher.js +114 -0
  112. package/dist/src/watchers/queue_watcher.js.map +1 -0
  113. package/dist/src/watchers/redis_watcher.d.ts +103 -0
  114. package/dist/src/watchers/redis_watcher.d.ts.map +1 -0
  115. package/dist/src/watchers/redis_watcher.js +161 -0
  116. package/dist/src/watchers/redis_watcher.js.map +1 -0
  117. package/dist/stubs/config/telescope.stub +9 -0
  118. package/dist/stubs/config/telescope_ui.stub +13 -0
  119. package/dist/stubs/config/telescope_watchers.stub +14 -3
  120. package/package.json +1 -1
@@ -0,0 +1,255 @@
1
+ import { EntryType } from '../entry.js';
2
+ import { buildHistogram, estimatePercentileFromHistogram } from './rollup.js';
3
+ import { bucketTimeseries } from './timeseries.js';
4
+ const DEFAULT_TOP_FAMILIES = 8;
5
+ const DEFAULT_TOP_KEYS = 8;
6
+ const DEFAULT_TOP_EXCEPTIONS = 8;
7
+ const MAX_FAMILY_LABEL_LENGTH = 60;
8
+ /** Nearest-rank percentile over a NON-EMPTY ascending array; 0 for empty.
9
+ * `q` in [0,1]; `idx = clamp(ceil(q*n)-1, 0, n-1)`. */
10
+ export function percentile(sortedAscending, q) {
11
+ const n = sortedAscending.length;
12
+ if (n === 0)
13
+ return 0;
14
+ const rawIndex = Math.ceil(q * n) - 1;
15
+ const index = Math.min(n - 1, Math.max(0, rawIndex));
16
+ return sortedAscending[index] ?? 0;
17
+ }
18
+ /**
19
+ * Estimate p50/p95/p99 over a set of durations using the latency histogram,
20
+ * clamped to the exact max so a percentile is never reported above the observed
21
+ * maximum. Returns `undefined` when there are no samples (shape parity with the
22
+ * raw path's "no durations ⇒ no latency"). This is the storage-agnostic stand-in
23
+ * for the NestJS RollupStore fast path; the equivalence spec proves it agrees
24
+ * with the raw-scan percentile within one bucket-width.
25
+ */
26
+ export function estimateLatencyPercentiles(durations) {
27
+ if (durations.length === 0)
28
+ return undefined;
29
+ const histogram = buildHistogram(durations);
30
+ const max = Math.max(...durations);
31
+ return {
32
+ p50: Math.min(estimatePercentileFromHistogram(histogram, 0.5), max),
33
+ p95: Math.min(estimatePercentileFromHistogram(histogram, 0.95), max),
34
+ p99: Math.min(estimatePercentileFromHistogram(histogram, 0.99), max),
35
+ };
36
+ }
37
+ function isRecord(value) {
38
+ return typeof value === 'object' && value !== null;
39
+ }
40
+ function computeLatency(entries, slowMs) {
41
+ const durations = [];
42
+ let slow = 0;
43
+ for (const entry of entries) {
44
+ if (typeof entry.durationMs === 'number') {
45
+ durations.push(entry.durationMs);
46
+ if (entry.durationMs >= slowMs)
47
+ slow += 1;
48
+ }
49
+ }
50
+ if (durations.length === 0)
51
+ return undefined;
52
+ durations.sort((a, b) => a - b);
53
+ return {
54
+ count: durations.length,
55
+ p50: percentile(durations, 0.5),
56
+ p95: percentile(durations, 0.95),
57
+ p99: percentile(durations, 0.99),
58
+ max: durations[durations.length - 1] ?? 0,
59
+ slow,
60
+ };
61
+ }
62
+ function queryLabel(content) {
63
+ if (isRecord(content) && typeof content.sql === 'string') {
64
+ return content.sql.slice(0, MAX_FAMILY_LABEL_LENGTH);
65
+ }
66
+ return '';
67
+ }
68
+ function computeFamilies(entries, topFamilies) {
69
+ const groups = new Map();
70
+ for (const entry of entries) {
71
+ if (entry.familyHash === null)
72
+ continue;
73
+ const existing = groups.get(entry.familyHash);
74
+ if (existing) {
75
+ if (typeof entry.durationMs === 'number')
76
+ existing.durations.push(entry.durationMs);
77
+ }
78
+ else {
79
+ groups.set(entry.familyHash, {
80
+ durations: typeof entry.durationMs === 'number' ? [entry.durationMs] : [],
81
+ label: queryLabel(entry.content),
82
+ });
83
+ }
84
+ }
85
+ const families = [];
86
+ for (const [familyHash, group] of groups) {
87
+ const durations = [...group.durations].sort((a, b) => a - b);
88
+ families.push({
89
+ familyHash,
90
+ label: group.label,
91
+ count: durations.length,
92
+ p50: percentile(durations, 0.5),
93
+ p99: percentile(durations, 0.99),
94
+ });
95
+ }
96
+ families.sort((a, b) => b.p99 - a.p99 || b.count - a.count || a.familyHash.localeCompare(b.familyHash));
97
+ return families.slice(0, topFamilies);
98
+ }
99
+ function computeCache(entries, topKeys) {
100
+ let hits = 0;
101
+ let misses = 0;
102
+ let sets = 0;
103
+ const keyCounts = new Map();
104
+ for (const entry of entries) {
105
+ const content = entry.content;
106
+ if (!isRecord(content))
107
+ continue;
108
+ const operation = content.operation;
109
+ if (typeof operation !== 'string')
110
+ continue;
111
+ const key = typeof content.key === 'string' ? content.key : null;
112
+ if (key !== null)
113
+ keyCounts.set(key, (keyCounts.get(key) ?? 0) + 1);
114
+ // Adonis cache ops: 'hit' | 'miss' | 'write' | 'delete' | 'clear'.
115
+ if (operation === 'write')
116
+ sets += 1;
117
+ else if (operation === 'hit')
118
+ hits += 1;
119
+ else if (operation === 'miss')
120
+ misses += 1;
121
+ }
122
+ const denominator = hits + misses;
123
+ const ranked = [...keyCounts.entries()]
124
+ .map(([key, count]) => ({ key, count }))
125
+ .sort((a, b) => b.count - a.count || a.key.localeCompare(b.key))
126
+ .slice(0, topKeys);
127
+ return {
128
+ hits,
129
+ misses,
130
+ sets,
131
+ hitRatio: denominator === 0 ? 0 : hits / denominator,
132
+ topKeys: ranked,
133
+ };
134
+ }
135
+ /** The HTTP status code from a request entry's content (Adonis records `status`). */
136
+ function statusOf(content) {
137
+ if (!isRecord(content))
138
+ return null;
139
+ const status = content.status;
140
+ return typeof status === 'number' ? status : null;
141
+ }
142
+ function computeStatus(entries) {
143
+ const breakdown = { '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, other: 0 };
144
+ for (const entry of entries) {
145
+ const code = statusOf(entry.content);
146
+ if (code !== null && code >= 200 && code < 300)
147
+ breakdown['2xx'] += 1;
148
+ else if (code !== null && code >= 300 && code < 400)
149
+ breakdown['3xx'] += 1;
150
+ else if (code !== null && code >= 400 && code < 500)
151
+ breakdown['4xx'] += 1;
152
+ else if (code !== null && code >= 500 && code < 600)
153
+ breakdown['5xx'] += 1;
154
+ else
155
+ breakdown.other += 1;
156
+ }
157
+ return breakdown;
158
+ }
159
+ /** Adonis exception content fields (`name` is the error class, plus `message`). */
160
+ function exceptionFields(content) {
161
+ if (!isRecord(content))
162
+ return null;
163
+ const className = typeof content.name === 'string' ? content.name : 'Error';
164
+ const message = typeof content.message === 'string' ? content.message : '';
165
+ return { class: className, message };
166
+ }
167
+ function exceptionKey(entry, fields) {
168
+ return entry.familyHash ?? `${fields.class}: ${fields.message}`;
169
+ }
170
+ /** The bucket index a timestamp falls into; mirrors {@link bucketTimeseries}. */
171
+ function bucketIndexFor(createdAt, windowStart, windowEnd, bucketCount) {
172
+ const count = Math.max(1, Math.floor(bucketCount));
173
+ const startMs = windowStart.getTime();
174
+ const spanMs = Math.max(1, windowEnd.getTime() - startMs);
175
+ const bucketMs = Math.max(1, Math.floor(spanMs / count));
176
+ const rawIndex = Math.floor((createdAt.getTime() - startMs) / bucketMs);
177
+ return Math.min(count - 1, Math.max(0, rawIndex));
178
+ }
179
+ function computeExceptions(entries, windowStart, windowEnd, buckets, topExceptions) {
180
+ const bucketCount = Math.max(1, Math.floor(buckets));
181
+ const groups = new Map();
182
+ for (const entry of entries) {
183
+ const fields = exceptionFields(entry.content);
184
+ if (fields === null)
185
+ continue;
186
+ const key = exceptionKey(entry, fields);
187
+ const index = bucketIndexFor(entry.createdAt, windowStart, windowEnd, bucketCount);
188
+ const existing = groups.get(key);
189
+ if (existing) {
190
+ existing.count += 1;
191
+ if (entry.createdAt > existing.lastAt)
192
+ existing.lastAt = entry.createdAt;
193
+ existing.overTime[index] = (existing.overTime[index] ?? 0) + 1;
194
+ }
195
+ else {
196
+ const overTime = new Array(bucketCount).fill(0);
197
+ overTime[index] = 1;
198
+ groups.set(key, {
199
+ class: fields.class,
200
+ message: fields.message,
201
+ count: 1,
202
+ lastAt: entry.createdAt,
203
+ overTime,
204
+ });
205
+ }
206
+ }
207
+ return [...groups.entries()]
208
+ .map(([key, group]) => ({ key, ...group }))
209
+ .sort((a, b) => b.count - a.count || b.lastAt.getTime() - a.lastAt.getTime() || a.key.localeCompare(b.key))
210
+ .slice(0, topExceptions);
211
+ }
212
+ /**
213
+ * Aggregate a window of entries into per-type analytics: latency percentiles,
214
+ * query-family breakdown, cache hit/miss, request status breakdown, exception
215
+ * groups, and a throughput time-series. Pure.
216
+ */
217
+ export function summarizeStats(input) {
218
+ const { entries, type, windowStart, windowEnd, windowMs, buckets, slowMs, truncated, topFamilies = DEFAULT_TOP_FAMILIES, topKeys = DEFAULT_TOP_KEYS, topExceptions = DEFAULT_TOP_EXCEPTIONS, latencyPercentiles, } = input;
219
+ const overTime = bucketTimeseries(entries, windowStart, windowEnd, buckets);
220
+ const latency = computeLatency(entries, slowMs);
221
+ const result = {
222
+ type,
223
+ windowMs,
224
+ total: entries.length,
225
+ overTime,
226
+ truncated,
227
+ };
228
+ // When the caller supplied histogram percentile estimates, substitute
229
+ // p50/p95/p99 (count/max/slow stay raw-derived). This only fires when the raw
230
+ // scan also found latency samples, keeping the "no durations ⇒ no latency"
231
+ // shape identical to the pure raw path.
232
+ if (latency !== undefined && latencyPercentiles !== undefined) {
233
+ latency.p50 = latencyPercentiles.p50;
234
+ latency.p95 = latencyPercentiles.p95;
235
+ latency.p99 = latencyPercentiles.p99;
236
+ }
237
+ if (latency !== undefined)
238
+ result.latency = latency;
239
+ if (type === EntryType.Query) {
240
+ const families = computeFamilies(entries, topFamilies);
241
+ if (families.length > 0)
242
+ result.families = families;
243
+ }
244
+ if (type === EntryType.Cache)
245
+ result.cache = computeCache(entries, topKeys);
246
+ if (type === EntryType.Request)
247
+ result.status = computeStatus(entries);
248
+ if (type === EntryType.Exception) {
249
+ const exceptions = computeExceptions(entries, windowStart, windowEnd, buckets, topExceptions);
250
+ if (exceptions.length > 0)
251
+ result.exceptions = exceptions;
252
+ }
253
+ return result;
254
+ }
255
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/metrics/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Entry } from '../entry.js';
2
+ /** A single time-bucket of a {@link TimeseriesReport}. */
3
+ export interface TimeseriesBucket {
4
+ /** ISO timestamp of the bucket's start. */
5
+ t: string;
6
+ total: number;
7
+ byType: Record<string, number>;
8
+ }
9
+ export interface TimeseriesReport {
10
+ windowStart: string;
11
+ windowEnd: string;
12
+ bucketMs: number;
13
+ buckets: TimeseriesBucket[];
14
+ }
15
+ /**
16
+ * Group entries into `bucketCount` equal time buckets across [windowStart,
17
+ * windowEnd], counting total + per-type per bucket. Ported from `nestjs-telescope`'s
18
+ * `metrics/timeseries.ts`. Pure: callers fetch the windowed entries.
19
+ * Out-of-range entries are clamped into the edge buckets.
20
+ */
21
+ export declare function bucketTimeseries(entries: Entry[], windowStart: Date, windowEnd: Date, bucketCount: number): TimeseriesReport;
22
+ //# sourceMappingURL=timeseries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeseries.d.ts","sourceRoot":"","sources":["../../../src/metrics/timeseries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,KAAK,EAAE,EAChB,WAAW,EAAE,IAAI,EACjB,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,MAAM,GAClB,gBAAgB,CA4BlB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Group entries into `bucketCount` equal time buckets across [windowStart,
3
+ * windowEnd], counting total + per-type per bucket. Ported from `nestjs-telescope`'s
4
+ * `metrics/timeseries.ts`. Pure: callers fetch the windowed entries.
5
+ * Out-of-range entries are clamped into the edge buckets.
6
+ */
7
+ export function bucketTimeseries(entries, windowStart, windowEnd, bucketCount) {
8
+ const count = Math.max(1, Math.floor(bucketCount));
9
+ const startMs = windowStart.getTime();
10
+ const spanMs = Math.max(1, windowEnd.getTime() - startMs);
11
+ const bucketMs = Math.max(1, Math.floor(spanMs / count));
12
+ const buckets = Array.from({ length: count }, (_, index) => ({
13
+ t: new Date(startMs + index * bucketMs).toISOString(),
14
+ total: 0,
15
+ byType: {},
16
+ }));
17
+ for (const entry of entries) {
18
+ const offset = entry.createdAt.getTime() - startMs;
19
+ const rawIndex = Math.floor(offset / bucketMs);
20
+ const index = Math.min(count - 1, Math.max(0, rawIndex));
21
+ const bucket = buckets[index];
22
+ if (!bucket)
23
+ continue;
24
+ bucket.total += 1;
25
+ bucket.byType[entry.type] = (bucket.byType[entry.type] ?? 0) + 1;
26
+ }
27
+ return {
28
+ windowStart: windowStart.toISOString(),
29
+ windowEnd: windowEnd.toISOString(),
30
+ bucketMs,
31
+ buckets,
32
+ };
33
+ }
34
+ //# sourceMappingURL=timeseries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeseries.js","sourceRoot":"","sources":["../../../src/metrics/timeseries.ts"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAgB,EAChB,WAAiB,EACjB,SAAe,EACf,WAAmB;IAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;IAEzD,MAAM,OAAO,GAAuB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/E,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE;QACrD,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,EAAE;KACX,CAAC,CAAC,CAAC;IAEJ,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;QAClB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACnE,CAAC;IAED,OAAO;QACL,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE;QACtC,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;QAClC,QAAQ;QACR,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type Entry } from '../entry.js';
2
+ /** A per-trace summary row for the dashboard's trace list. */
3
+ export interface TraceSummary {
4
+ traceId: string;
5
+ /** Number of entries that share this traceId. */
6
+ entryCount: number;
7
+ /** Distinct entry types in the trace, sorted ascending. */
8
+ types: string[];
9
+ /** Earliest `createdAt` across the trace's entries. */
10
+ firstAt: Date;
11
+ /** Latest `createdAt` across the trace's entries. */
12
+ lastAt: Date;
13
+ /** Sum of non-null `durationMs` across the trace's entries. */
14
+ totalDurationMs: number;
15
+ /** The request entry's "METHOD url" label, when the trace has one. */
16
+ rootLabel?: string;
17
+ }
18
+ export interface SummarizeTracesOptions {
19
+ limit?: number;
20
+ }
21
+ /**
22
+ * Groups window entries by `traceId` (null trace ids skipped) into a summary per
23
+ * distinct trace, sorted by `lastAt` desc and sliced to `limit`. Ported from
24
+ * `nestjs-telescope`'s `metrics/traces.ts`. Pure.
25
+ */
26
+ export declare function summarizeTraces(entries: Entry[], options?: SummarizeTracesOptions): TraceSummary[];
27
+ //# sourceMappingURL=traces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../../src/metrics/traces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,aAAa,CAAC;AAEpD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uDAAuD;IACvD,OAAO,EAAE,IAAI,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,IAAI,CAAC;IACb,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyBD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,KAAK,EAAE,EAChB,OAAO,GAAE,sBAA2B,GACnC,YAAY,EAAE,CAqDhB"}
@@ -0,0 +1,71 @@
1
+ import { EntryType } from '../entry.js';
2
+ const DEFAULT_LIMIT = 50;
3
+ /** Narrows an entry's content to a request label (`METHOD url`). The Adonis
4
+ * request watcher records `{ method, url }` content. */
5
+ function asRequestLabel(content) {
6
+ if (typeof content !== 'object' || content === null)
7
+ return undefined;
8
+ const record = content;
9
+ const url = typeof record.url === 'string' ? record.url : undefined;
10
+ if (url === undefined)
11
+ return undefined;
12
+ const method = typeof record.method === 'string' ? record.method : undefined;
13
+ return method !== undefined ? `${method} ${url}` : url;
14
+ }
15
+ /**
16
+ * Groups window entries by `traceId` (null trace ids skipped) into a summary per
17
+ * distinct trace, sorted by `lastAt` desc and sliced to `limit`. Ported from
18
+ * `nestjs-telescope`'s `metrics/traces.ts`. Pure.
19
+ */
20
+ export function summarizeTraces(entries, options = {}) {
21
+ const limit = Math.max(0, Math.floor(options.limit ?? DEFAULT_LIMIT));
22
+ const byTrace = new Map();
23
+ for (const entry of entries) {
24
+ const traceId = entry.traceId;
25
+ if (traceId === null)
26
+ continue;
27
+ let accumulator = byTrace.get(traceId);
28
+ if (accumulator === undefined) {
29
+ accumulator = {
30
+ traceId,
31
+ entryCount: 0,
32
+ types: new Set(),
33
+ firstAt: entry.createdAt,
34
+ lastAt: entry.createdAt,
35
+ totalDurationMs: 0,
36
+ };
37
+ byTrace.set(traceId, accumulator);
38
+ }
39
+ accumulator.entryCount += 1;
40
+ accumulator.types.add(entry.type);
41
+ if (entry.createdAt.getTime() < accumulator.firstAt.getTime()) {
42
+ accumulator.firstAt = entry.createdAt;
43
+ }
44
+ if (entry.createdAt.getTime() > accumulator.lastAt.getTime()) {
45
+ accumulator.lastAt = entry.createdAt;
46
+ }
47
+ if (entry.durationMs !== null) {
48
+ accumulator.totalDurationMs += entry.durationMs;
49
+ }
50
+ if (accumulator.rootLabel === undefined && entry.type === EntryType.Request) {
51
+ const label = asRequestLabel(entry.content);
52
+ if (label !== undefined)
53
+ accumulator.rootLabel = label;
54
+ }
55
+ }
56
+ const summaries = [];
57
+ for (const accumulator of byTrace.values()) {
58
+ summaries.push({
59
+ traceId: accumulator.traceId,
60
+ entryCount: accumulator.entryCount,
61
+ types: [...accumulator.types].sort(),
62
+ firstAt: accumulator.firstAt,
63
+ lastAt: accumulator.lastAt,
64
+ totalDurationMs: accumulator.totalDurationMs,
65
+ ...(accumulator.rootLabel !== undefined ? { rootLabel: accumulator.rootLabel } : {}),
66
+ });
67
+ }
68
+ summaries.sort((a, b) => b.lastAt.getTime() - a.lastAt.getTime());
69
+ return summaries.slice(0, limit);
70
+ }
71
+ //# sourceMappingURL=traces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traces.js","sourceRoot":"","sources":["../../../src/metrics/traces.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type Entry } from '../entry.js';
2
+ /**
3
+ * A single node in a trace waterfall. Offsets are relative to the trace's
4
+ * earliest start so the UI can lay each bar out as
5
+ * `left = offsetMs / totalDurationMs`, `width = durationMs / totalDurationMs`.
6
+ */
7
+ export interface WaterfallSpan {
8
+ id: string;
9
+ type: string;
10
+ /** A human label derived from the entry's content (route, sql, queue:job, …). */
11
+ label: string;
12
+ /** Start offset (ms) from the trace start. */
13
+ offsetMs: number;
14
+ /** Span duration (ms); a null `durationMs` becomes a zero-width instant span. */
15
+ durationMs: number;
16
+ /** Nesting depth (0 for roots). */
17
+ depth: number;
18
+ /** Stable record order within the trace (the entry's `sequence`). */
19
+ sequence: number;
20
+ children: WaterfallSpan[];
21
+ }
22
+ export interface Waterfall {
23
+ /** Absolute trace start (epoch ms) — the earliest entry start. */
24
+ traceStartMs: number;
25
+ /** Wall-clock span of the whole trace (latest end − earliest start), >= 0. */
26
+ totalDurationMs: number;
27
+ /** Root spans (those not contained by any other), ordered by start then sequence. */
28
+ spans: WaterfallSpan[];
29
+ }
30
+ /**
31
+ * Reconstruct a nested span waterfall from a trace's entries. Ported from
32
+ * `nestjs-telescope`'s `metrics/waterfall.ts`.
33
+ *
34
+ * DESIGN: the entry model carries `traceId` but NOT a parent-span pointer, so we
35
+ * cannot rebuild the exact span tree from explicit links. Instead — exactly as
36
+ * Sentry/Tempo do when parent links are missing — we infer nesting from
37
+ * **time-interval containment**: a span is a child of the tightest enclosing span
38
+ * whose `[start, end]` strictly contains it. `sequence` provides a stable
39
+ * tie-break for spans that start at the same instant.
40
+ *
41
+ * Returns `null` for an empty input. Pure.
42
+ */
43
+ export declare function buildWaterfall(entries: Entry[]): Waterfall | null;
44
+ //# sourceMappingURL=waterfall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waterfall.d.ts","sourceRoot":"","sources":["../../../src/metrics/waterfall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,aAAa,CAAC;AAEpD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAkCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,SAAS,GAAG,IAAI,CAwDjE"}
@@ -0,0 +1,99 @@
1
+ import { EntryType } from '../entry.js';
2
+ function asRecord(content) {
3
+ return typeof content === 'object' && content !== null
4
+ ? content
5
+ : null;
6
+ }
7
+ /** A human label for a span, derived from the entry's content by type. */
8
+ function labelFor(entry) {
9
+ const record = asRecord(entry.content);
10
+ if (record === null)
11
+ return entry.type;
12
+ if (typeof record.url === 'string') {
13
+ return typeof record.method === 'string' ? `${record.method} ${record.url}` : record.url;
14
+ }
15
+ if (typeof record.sql === 'string')
16
+ return record.sql;
17
+ if (typeof record.queue === 'string' && typeof record.name === 'string') {
18
+ return `${record.queue}:${record.name}`;
19
+ }
20
+ if (entry.type === EntryType.HttpClient && typeof record.url === 'string') {
21
+ return typeof record.method === 'string' ? `${record.method} ${record.url}` : record.url;
22
+ }
23
+ if (typeof record.name === 'string')
24
+ return record.name;
25
+ return entry.type;
26
+ }
27
+ /**
28
+ * Reconstruct a nested span waterfall from a trace's entries. Ported from
29
+ * `nestjs-telescope`'s `metrics/waterfall.ts`.
30
+ *
31
+ * DESIGN: the entry model carries `traceId` but NOT a parent-span pointer, so we
32
+ * cannot rebuild the exact span tree from explicit links. Instead — exactly as
33
+ * Sentry/Tempo do when parent links are missing — we infer nesting from
34
+ * **time-interval containment**: a span is a child of the tightest enclosing span
35
+ * whose `[start, end]` strictly contains it. `sequence` provides a stable
36
+ * tie-break for spans that start at the same instant.
37
+ *
38
+ * Returns `null` for an empty input. Pure.
39
+ */
40
+ export function buildWaterfall(entries) {
41
+ if (entries.length === 0)
42
+ return null;
43
+ const intervals = entries.map((entry) => {
44
+ const startMs = entry.createdAt.getTime();
45
+ const durationMs = typeof entry.durationMs === 'number' ? Math.max(0, entry.durationMs) : 0;
46
+ return { entry, startMs, durationMs, endMs: startMs + durationMs };
47
+ });
48
+ const traceStartMs = Math.min(...intervals.map((i) => i.startMs));
49
+ const traceEndMs = Math.max(...intervals.map((i) => i.endMs));
50
+ const totalDurationMs = Math.max(0, traceEndMs - traceStartMs);
51
+ // Order so that enclosing spans come first: earliest start, then the LONGER
52
+ // span first (a parent starts no later and ends no earlier than its child),
53
+ // then sequence for a stable tie-break.
54
+ const ordered = [...intervals].sort((a, b) => a.startMs - b.startMs || b.durationMs - a.durationMs || a.entry.sequence - b.entry.sequence);
55
+ const nodeById = new Map();
56
+ const roots = [];
57
+ const openStack = [];
58
+ for (const interval of ordered) {
59
+ while (openStack.length > 0) {
60
+ const top = openStack[openStack.length - 1];
61
+ if (top !== undefined && contains(top.interval, interval))
62
+ break;
63
+ openStack.pop();
64
+ }
65
+ const parent = openStack[openStack.length - 1];
66
+ const depth = parent === undefined ? 0 : parent.node.depth + 1;
67
+ const node = {
68
+ id: interval.entry.id,
69
+ type: interval.entry.type,
70
+ label: labelFor(interval.entry),
71
+ offsetMs: interval.startMs - traceStartMs,
72
+ durationMs: interval.durationMs,
73
+ depth,
74
+ sequence: interval.entry.sequence,
75
+ children: [],
76
+ };
77
+ nodeById.set(node.id, node);
78
+ if (parent === undefined)
79
+ roots.push(node);
80
+ else
81
+ parent.node.children.push(node);
82
+ openStack.push({ interval, node });
83
+ }
84
+ // Order children (and roots) by start offset, then sequence.
85
+ const byOffset = (a, b) => a.offsetMs - b.offsetMs || a.sequence - b.sequence;
86
+ for (const node of nodeById.values())
87
+ node.children.sort(byOffset);
88
+ roots.sort(byOffset);
89
+ return { traceStartMs, totalDurationMs, spans: roots };
90
+ }
91
+ /** True when `outer` strictly encloses `inner` — contains the interval AND is
92
+ * genuinely larger on at least one side. Two spans with the SAME interval are
93
+ * siblings, not nested; a zero-width child inside a parent's bounds still nests. */
94
+ function contains(outer, inner) {
95
+ const encloses = outer.startMs <= inner.startMs && outer.endMs >= inner.endMs;
96
+ const strictlyLarger = outer.startMs < inner.startMs || outer.endMs > inner.endMs;
97
+ return encloses && strictlyLarger;
98
+ }
99
+ //# sourceMappingURL=waterfall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waterfall.js","sourceRoot":"","sources":["../../../src/metrics/waterfall.ts"],"names":[],"mappings":"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"}