@adonis-agora/telescope 0.9.1 → 0.10.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 (181) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/providers/telescope_ai_provider.d.ts.map +1 -1
  3. package/dist/providers/telescope_ai_provider.js.map +1 -1
  4. package/dist/providers/telescope_alerts_provider.d.ts.map +1 -1
  5. package/dist/providers/telescope_alerts_provider.js.map +1 -1
  6. package/dist/providers/telescope_cpu_profiling_provider.d.ts.map +1 -1
  7. package/dist/providers/telescope_cpu_profiling_provider.js.map +1 -1
  8. package/dist/providers/telescope_mcp_provider.d.ts.map +1 -1
  9. package/dist/providers/telescope_mcp_provider.js.map +1 -1
  10. package/dist/providers/telescope_provider.d.ts.map +1 -1
  11. package/dist/providers/telescope_provider.js.map +1 -1
  12. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  13. package/dist/providers/telescope_ui_provider.js +2 -4
  14. package/dist/providers/telescope_ui_provider.js.map +1 -1
  15. package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
  16. package/dist/providers/telescope_watchers_provider.js.map +1 -1
  17. package/dist/src/ai/define_config.d.ts +1 -1
  18. package/dist/src/ai/define_config.d.ts.map +1 -1
  19. package/dist/src/ai/define_config.js +1 -1
  20. package/dist/src/ai/define_config.js.map +1 -1
  21. package/dist/src/ai/diagnosis_cache.d.ts.map +1 -1
  22. package/dist/src/ai/diagnosis_coordinator.d.ts.map +1 -1
  23. package/dist/src/ai/diagnosis_coordinator.js.map +1 -1
  24. package/dist/src/ai/factory.js.map +1 -1
  25. package/dist/src/ai/index.d.ts +11 -11
  26. package/dist/src/ai/index.d.ts.map +1 -1
  27. package/dist/src/ai/index.js +9 -9
  28. package/dist/src/ai/index.js.map +1 -1
  29. package/dist/src/ai/prompt.js.map +1 -1
  30. package/dist/src/ai/telescope_ai_diagnoser.d.ts.map +1 -1
  31. package/dist/src/ai/telescope_ai_diagnoser.js +1 -1
  32. package/dist/src/ai/telescope_ai_diagnoser.js.map +1 -1
  33. package/dist/src/alerts/alert_channel.d.ts.map +1 -1
  34. package/dist/src/alerts/alert_channel.js.map +1 -1
  35. package/dist/src/alerts/alerter.d.ts.map +1 -1
  36. package/dist/src/alerts/alerter.js.map +1 -1
  37. package/dist/src/alerts/alerter_service.d.ts.map +1 -1
  38. package/dist/src/alerts/alerter_service.js.map +1 -1
  39. package/dist/src/alerts/define_config.js.map +1 -1
  40. package/dist/src/alerts/exception_source.d.ts.map +1 -1
  41. package/dist/src/alerts/exception_source.js.map +1 -1
  42. package/dist/src/alerts/index.d.ts +10 -10
  43. package/dist/src/alerts/index.d.ts.map +1 -1
  44. package/dist/src/alerts/index.js +7 -7
  45. package/dist/src/alerts/index.js.map +1 -1
  46. package/dist/src/alerts/new_exception_tracker.d.ts.map +1 -1
  47. package/dist/src/alerts/new_exception_tracker.js.map +1 -1
  48. package/dist/src/client_errors/index.d.ts +2 -2
  49. package/dist/src/client_errors/index.d.ts.map +1 -1
  50. package/dist/src/client_errors/index.js.map +1 -1
  51. package/dist/src/client_errors/ingestor.d.ts.map +1 -1
  52. package/dist/src/client_errors/rate_limiter.d.ts.map +1 -1
  53. package/dist/src/define_config.d.ts +1 -1
  54. package/dist/src/define_config.d.ts.map +1 -1
  55. package/dist/src/define_config.js.map +1 -1
  56. package/dist/src/diagnostics_watcher.d.ts.map +1 -1
  57. package/dist/src/diagnostics_watcher.js.map +1 -1
  58. package/dist/src/entry.d.ts +16 -16
  59. package/dist/src/entry.d.ts.map +1 -1
  60. package/dist/src/exception_watcher.js.map +1 -1
  61. package/dist/src/extension/registry.d.ts.map +1 -1
  62. package/dist/src/index.d.ts +56 -57
  63. package/dist/src/index.d.ts.map +1 -1
  64. package/dist/src/index.js +39 -40
  65. package/dist/src/index.js.map +1 -1
  66. package/dist/src/mcp/define_config.d.ts +1 -1
  67. package/dist/src/mcp/define_config.d.ts.map +1 -1
  68. package/dist/src/mcp/define_config.js.map +1 -1
  69. package/dist/src/mcp/index.d.ts +3 -3
  70. package/dist/src/mcp/index.d.ts.map +1 -1
  71. package/dist/src/mcp/index.js +2 -2
  72. package/dist/src/mcp/index.js.map +1 -1
  73. package/dist/src/mcp/server.d.ts.map +1 -1
  74. package/dist/src/mcp/server.js.map +1 -1
  75. package/dist/src/metrics/metrics_service.d.ts.map +1 -1
  76. package/dist/src/metrics/metrics_service.js.map +1 -1
  77. package/dist/src/metrics/pulse.d.ts +1 -1
  78. package/dist/src/metrics/pulse.d.ts.map +1 -1
  79. package/dist/src/metrics/pulse.js.map +1 -1
  80. package/dist/src/metrics/stats.d.ts.map +1 -1
  81. package/dist/src/metrics/stats.js.map +1 -1
  82. package/dist/src/metrics/traces.js.map +1 -1
  83. package/dist/src/overload_guard.d.ts.map +1 -1
  84. package/dist/src/overload_guard.js.map +1 -1
  85. package/dist/src/profiling/cpu-profiler.d.ts.map +1 -1
  86. package/dist/src/profiling/cpu-profiler.js.map +1 -1
  87. package/dist/src/profiling/define_config.js.map +1 -1
  88. package/dist/src/profiling/index.d.ts +6 -6
  89. package/dist/src/profiling/index.d.ts.map +1 -1
  90. package/dist/src/profiling/index.js +3 -3
  91. package/dist/src/profiling/index.js.map +1 -1
  92. package/dist/src/profiling/profiler_service.d.ts.map +1 -1
  93. package/dist/src/profiling/profiler_service.js.map +1 -1
  94. package/dist/src/pruner.d.ts.map +1 -1
  95. package/dist/src/pruner.js.map +1 -1
  96. package/dist/src/redaction/redact.js.map +1 -1
  97. package/dist/src/redaction/redacting_store.d.ts.map +1 -1
  98. package/dist/src/redaction/redacting_store.js.map +1 -1
  99. package/dist/src/registry.js.map +1 -1
  100. package/dist/src/request_watcher.js.map +1 -1
  101. package/dist/src/sampling/sampling_store.d.ts.map +1 -1
  102. package/dist/src/sampling/sampling_store.js.map +1 -1
  103. package/dist/src/service.d.ts.map +1 -1
  104. package/dist/src/service.js.map +1 -1
  105. package/dist/src/stores/factory.d.ts.map +1 -1
  106. package/dist/src/stores/factory.js.map +1 -1
  107. package/dist/src/stores/lucid.d.ts.map +1 -1
  108. package/dist/src/stores/lucid.js.map +1 -1
  109. package/dist/src/stores/memory.d.ts.map +1 -1
  110. package/dist/src/stores/memory.js.map +1 -1
  111. package/dist/src/stream/entry_events.d.ts.map +1 -1
  112. package/dist/src/stream/index.d.ts +3 -3
  113. package/dist/src/stream/index.d.ts.map +1 -1
  114. package/dist/src/stream/index.js +1 -1
  115. package/dist/src/stream/index.js.map +1 -1
  116. package/dist/src/stream/stream_handler.d.ts.map +1 -1
  117. package/dist/src/stream/stream_handler.js.map +1 -1
  118. package/dist/src/stream/streaming_store.d.ts.map +1 -1
  119. package/dist/src/stream/streaming_store.js.map +1 -1
  120. package/dist/src/telescope_middleware.d.ts.map +1 -1
  121. package/dist/src/ui/api.d.ts.map +1 -1
  122. package/dist/src/ui/api.js.map +1 -1
  123. package/dist/src/ui/dashboard_auth.d.ts.map +1 -1
  124. package/dist/src/ui/dashboard_auth.js +1 -1
  125. package/dist/src/ui/dashboard_auth.js.map +1 -1
  126. package/dist/src/ui/define_config.js.map +1 -1
  127. package/dist/src/ui/diagnosis_api.d.ts.map +1 -1
  128. package/dist/src/ui/diagnosis_api.js.map +1 -1
  129. package/dist/src/ui/ext_table.d.ts +4 -6
  130. package/dist/src/ui/ext_table.d.ts.map +1 -1
  131. package/dist/src/ui/ext_table.js +4 -6
  132. package/dist/src/ui/ext_table.js.map +1 -1
  133. package/dist/src/ui/extension_api.d.ts.map +1 -1
  134. package/dist/src/ui/extension_api.js.map +1 -1
  135. package/dist/src/ui/http.d.ts.map +1 -1
  136. package/dist/src/ui/http.js.map +1 -1
  137. package/dist/src/ui/index.d.ts +19 -20
  138. package/dist/src/ui/index.d.ts.map +1 -1
  139. package/dist/src/ui/index.js +15 -16
  140. package/dist/src/ui/index.js.map +1 -1
  141. package/dist/src/ui/profiles_api.d.ts.map +1 -1
  142. package/dist/src/ui/profiles_api.js.map +1 -1
  143. package/dist/src/ui/queue_manager_api.d.ts.map +1 -1
  144. package/dist/src/ui/queue_manager_api.js.map +1 -1
  145. package/dist/src/ui/request_replay.js.map +1 -1
  146. package/dist/src/ui/schedules_api.d.ts.map +1 -1
  147. package/dist/src/ui/schedules_api.js.map +1 -1
  148. package/dist/src/watchers/cache_watcher.d.ts.map +1 -1
  149. package/dist/src/watchers/cache_watcher.js.map +1 -1
  150. package/dist/src/watchers/cron_next_run.js.map +1 -1
  151. package/dist/src/watchers/define_config.js.map +1 -1
  152. package/dist/src/watchers/events_watcher.d.ts.map +1 -1
  153. package/dist/src/watchers/events_watcher.js.map +1 -1
  154. package/dist/src/watchers/http_client_watcher.d.ts.map +1 -1
  155. package/dist/src/watchers/http_client_watcher.js.map +1 -1
  156. package/dist/src/watchers/index.d.ts +23 -23
  157. package/dist/src/watchers/index.d.ts.map +1 -1
  158. package/dist/src/watchers/index.js +18 -18
  159. package/dist/src/watchers/index.js.map +1 -1
  160. package/dist/src/watchers/logs_watcher.d.ts +1 -1
  161. package/dist/src/watchers/logs_watcher.d.ts.map +1 -1
  162. package/dist/src/watchers/logs_watcher.js.map +1 -1
  163. package/dist/src/watchers/lucid_query_watcher.d.ts.map +1 -1
  164. package/dist/src/watchers/lucid_query_watcher.js.map +1 -1
  165. package/dist/src/watchers/mail_watcher.d.ts.map +1 -1
  166. package/dist/src/watchers/profiling_watcher.d.ts.map +1 -1
  167. package/dist/src/watchers/profiling_watcher.js.map +1 -1
  168. package/dist/src/watchers/queue_manager.d.ts.map +1 -1
  169. package/dist/src/watchers/queue_manager.js.map +1 -1
  170. package/dist/src/watchers/queue_watcher.d.ts.map +1 -1
  171. package/dist/src/watchers/queue_watcher.js.map +1 -1
  172. package/dist/src/watchers/redis_watcher.d.ts.map +1 -1
  173. package/dist/src/watchers/schedule_watcher.d.ts.map +1 -1
  174. package/dist/src/watchers/schedule_watcher.js.map +1 -1
  175. package/dist/stubs/main.js.map +1 -1
  176. package/package.json +13 -14
  177. package/dist/src/ui/dashboard.d.ts +0 -8
  178. package/dist/src/ui/dashboard.d.ts.map +0 -1
  179. package/dist/src/ui/dashboard.html +0 -674
  180. package/dist/src/ui/dashboard.js +0 -29
  181. package/dist/src/ui/dashboard.js.map +0 -1
@@ -1,674 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Telescope</title>
7
- <style>
8
- :root {
9
- --bg: #0d1117;
10
- --panel: #161b22;
11
- --panel-2: #1c232c;
12
- --border: #2b333d;
13
- --text: #e6edf3;
14
- --muted: #8b949e;
15
- --accent: #58a6ff;
16
- --accent-dim: #1f6feb;
17
- --green: #3fb950;
18
- --yellow: #d29922;
19
- --red: #f85149;
20
- --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
21
- }
22
- * { box-sizing: border-box; }
23
- html, body { height: 100%; margin: 0; }
24
- body {
25
- background: var(--bg);
26
- color: var(--text);
27
- font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
28
- }
29
- header {
30
- display: flex;
31
- align-items: center;
32
- gap: 16px;
33
- padding: 12px 20px;
34
- border-bottom: 1px solid var(--border);
35
- background: var(--panel);
36
- position: sticky;
37
- top: 0;
38
- z-index: 5;
39
- }
40
- header h1 {
41
- font-size: 16px;
42
- margin: 0;
43
- font-weight: 600;
44
- letter-spacing: 0.2px;
45
- }
46
- header h1 .dot { color: var(--accent); }
47
- .controls { display: flex; align-items: center; gap: 10px; margin-left: auto; flex-wrap: wrap; }
48
- select, input[type="text"] {
49
- background: var(--panel-2);
50
- color: var(--text);
51
- border: 1px solid var(--border);
52
- border-radius: 6px;
53
- padding: 6px 10px;
54
- font-size: 13px;
55
- outline: none;
56
- }
57
- input[type="text"] { min-width: 200px; }
58
- select:focus, input[type="text"]:focus { border-color: var(--accent-dim); }
59
- label.toggle { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 13px; user-select: none; cursor: pointer; }
60
- button {
61
- background: var(--panel-2);
62
- color: var(--text);
63
- border: 1px solid var(--border);
64
- border-radius: 6px;
65
- padding: 6px 12px;
66
- font-size: 13px;
67
- cursor: pointer;
68
- }
69
- button:hover { border-color: var(--accent-dim); }
70
- .layout { display: grid; grid-template-columns: 1fr; height: calc(100% - 53px); }
71
- .layout.detail-open { grid-template-columns: 1fr 1fr; }
72
- main { overflow: auto; }
73
- aside {
74
- overflow: auto;
75
- border-left: 1px solid var(--border);
76
- background: var(--panel);
77
- display: none;
78
- }
79
- .layout.detail-open aside { display: block; }
80
- .stats {
81
- display: flex;
82
- gap: 18px;
83
- padding: 10px 20px;
84
- border-bottom: 1px solid var(--border);
85
- color: var(--muted);
86
- font-size: 12px;
87
- flex-wrap: wrap;
88
- }
89
- .stats b { color: var(--text); font-variant-numeric: tabular-nums; }
90
- table { width: 100%; border-collapse: collapse; }
91
- th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
92
- th { color: var(--muted); font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; position: sticky; top: 0; background: var(--bg); }
93
- tbody tr { cursor: pointer; }
94
- tbody tr:hover { background: var(--panel-2); }
95
- tbody tr.selected { background: rgba(31, 111, 235, 0.18); }
96
- .badge {
97
- display: inline-block;
98
- padding: 1px 7px;
99
- border-radius: 999px;
100
- font-size: 11px;
101
- font-family: var(--mono);
102
- border: 1px solid var(--border);
103
- background: var(--panel-2);
104
- color: var(--muted);
105
- }
106
- .badge.request { color: var(--accent); border-color: var(--accent-dim); }
107
- .badge.diagnostic { color: var(--green); border-color: var(--green); }
108
- .badge.exception { color: var(--red); border-color: var(--red); }
109
- .summary { font-family: var(--mono); font-size: 13px; }
110
- .time, .trace { color: var(--muted); font-size: 12px; font-family: var(--mono); white-space: nowrap; }
111
- .trace { color: var(--accent); }
112
- .detail-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--panel); }
113
- .detail-head h2 { font-size: 14px; margin: 0; }
114
- .detail-head .close { margin-left: auto; }
115
- pre {
116
- margin: 0;
117
- padding: 16px;
118
- font-family: var(--mono);
119
- font-size: 12.5px;
120
- white-space: pre-wrap;
121
- word-break: break-word;
122
- color: var(--text);
123
- }
124
- .kv { padding: 8px 16px; border-bottom: 1px solid var(--border); display: grid; grid-template-columns: 110px 1fr; gap: 6px 12px; font-size: 12.5px; }
125
- .kv dt { color: var(--muted); }
126
- .kv dd { margin: 0; font-family: var(--mono); word-break: break-word; }
127
- .empty, .error { padding: 40px 20px; text-align: center; color: var(--muted); }
128
- .error { color: var(--red); }
129
- .tagline .badge { margin-right: 4px; margin-bottom: 4px; }
130
-
131
- /* — extension dashboards — */
132
- nav.tabs { display: flex; gap: 4px; }
133
- nav.tabs button {
134
- background: transparent;
135
- border: 1px solid transparent;
136
- border-bottom: 2px solid transparent;
137
- border-radius: 6px 6px 0 0;
138
- color: var(--muted);
139
- padding: 6px 12px;
140
- }
141
- nav.tabs button:hover { color: var(--text); }
142
- nav.tabs button.active { color: var(--text); border-bottom-color: var(--accent); }
143
- .dash { padding: 16px 20px; overflow: auto; height: calc(100% - 53px); }
144
- .dash-section { margin-bottom: 24px; }
145
- .dash-section h3 {
146
- margin: 0 0 10px;
147
- font-size: 12px;
148
- text-transform: uppercase;
149
- letter-spacing: 0.5px;
150
- color: var(--muted);
151
- font-weight: 600;
152
- }
153
- .grid { display: grid; gap: 12px; }
154
- .grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
155
- .grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
156
- .grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
157
- @media (max-width: 900px) { .grid { grid-template-columns: 1fr !important; } }
158
- .card {
159
- background: var(--panel);
160
- border: 1px solid var(--border);
161
- border-radius: 8px;
162
- padding: 14px;
163
- min-height: 96px;
164
- display: flex;
165
- flex-direction: column;
166
- }
167
- .card .card-title { color: var(--muted); font-size: 12px; margin-bottom: 8px; }
168
- .card .big {
169
- font-size: 30px;
170
- font-weight: 600;
171
- font-variant-numeric: tabular-nums;
172
- line-height: 1.1;
173
- }
174
- .card .big.ok { color: var(--green); }
175
- .card .big.warn { color: var(--yellow); }
176
- .card .big.bad { color: var(--red); }
177
- .card .delta { font-size: 12px; color: var(--muted); margin-top: 4px; }
178
- .card .delta.up { color: var(--green); }
179
- .card .delta.down { color: var(--red); }
180
- .card table { font-size: 12.5px; }
181
- .card th, .card td { padding: 5px 8px; }
182
- .card td a { color: var(--accent); text-decoration: none; }
183
- .card td a:hover { text-decoration: underline; }
184
- .pager { display: flex; align-items: center; gap: 8px; padding: 8px 10px; font-size: 11px; color: var(--muted); border-top: 1px solid var(--border); }
185
- .pager button { background: var(--panel-2); border: 1px solid var(--border); color: var(--text); border-radius: 5px; padding: 2px 8px; cursor: pointer; font-size: 11px; }
186
- .pager button:hover:not(:disabled) { background: var(--border); }
187
- .pager button:disabled { opacity: 0.4; cursor: not-allowed; }
188
- .barlist { display: flex; flex-direction: column; gap: 6px; }
189
- .barlist .row { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; font-size: 12.5px; }
190
- .barlist .track { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: var(--panel-2); overflow: hidden; }
191
- .barlist .track > span { display: block; height: 100%; background: var(--accent); }
192
- .legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; font-size: 12px; color: var(--muted); }
193
- .legend .swatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 5px; vertical-align: middle; }
194
- .markers { display: flex; gap: 14px; margin-top: 8px; font-size: 12px; color: var(--muted); }
195
- .markers b { color: var(--text); font-variant-numeric: tabular-nums; }
196
- .card svg { display: block; width: 100%; }
197
- .muted-pill { color: var(--muted); font-size: 12px; }
198
- </style>
199
- </head>
200
- <body>
201
- <header>
202
- <h1><span class="dot">●</span> Telescope</h1>
203
- <nav class="tabs" id="tabs"></nav>
204
- <div class="controls" id="entries-controls">
205
- <select id="type-filter" title="Filter by entry type">
206
- <option value="">all types</option>
207
- <option value="request">request</option>
208
- <option value="diagnostic">diagnostic</option>
209
- <option value="query">query</option>
210
- <option value="job">job</option>
211
- <option value="exception">exception</option>
212
- <option value="log">log</option>
213
- </select>
214
- <input type="text" id="search" placeholder="search content / tags…" />
215
- <label class="toggle"><input type="checkbox" id="autorefresh" /> auto-refresh</label>
216
- <button id="refresh">Refresh</button>
217
- </div>
218
- </header>
219
-
220
- <div class="stats" id="stats"><span>Loading…</span></div>
221
-
222
- <div class="layout" id="layout">
223
- <main>
224
- <table>
225
- <thead>
226
- <tr>
227
- <th style="width: 110px">Type</th>
228
- <th>Summary</th>
229
- <th style="width: 150px">Trace</th>
230
- <th style="width: 170px">Time</th>
231
- </tr>
232
- </thead>
233
- <tbody id="rows"></tbody>
234
- </table>
235
- <div class="empty" id="empty" style="display: none">No entries yet.</div>
236
- </main>
237
- <aside id="detail"></aside>
238
- </div>
239
-
240
- <div class="dash" id="dashboard-view" style="display: none"></div>
241
-
242
- <script>
243
- (function () {
244
- var API_BASE = '__TELESCOPE_API_BASE__';
245
- var els = {
246
- rows: document.getElementById('rows'),
247
- empty: document.getElementById('empty'),
248
- stats: document.getElementById('stats'),
249
- detail: document.getElementById('detail'),
250
- layout: document.getElementById('layout'),
251
- type: document.getElementById('type-filter'),
252
- search: document.getElementById('search'),
253
- autorefresh: document.getElementById('autorefresh'),
254
- refresh: document.getElementById('refresh'),
255
- tabs: document.getElementById('tabs'),
256
- entriesControls: document.getElementById('entries-controls'),
257
- dashboardView: document.getElementById('dashboard-view'),
258
- };
259
- var selectedId = null;
260
- var timer = null;
261
- var activeTab = 'entries';
262
-
263
- function esc(s) {
264
- return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
265
- return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
266
- });
267
- }
268
- function qs(params) {
269
- var parts = [];
270
- Object.keys(params).forEach(function (k) {
271
- if (params[k] !== '' && params[k] != null)
272
- parts.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
273
- });
274
- return parts.length ? '?' + parts.join('&') : '';
275
- }
276
- function fetchJson(url) {
277
- return fetch(url, { headers: { accept: 'application/json' }, credentials: 'same-origin' }).then(function (r) {
278
- if (!r.ok) throw new Error('HTTP ' + r.status);
279
- return r.json();
280
- });
281
- }
282
- function fmtTime(iso) {
283
- try {
284
- return new Date(iso).toLocaleTimeString() + ' ' + new Date(iso).toLocaleDateString();
285
- } catch (e) {
286
- return iso;
287
- }
288
- }
289
-
290
- function renderRows(entries) {
291
- els.empty.style.display = entries.length ? 'none' : 'block';
292
- els.rows.innerHTML = entries
293
- .map(function (e) {
294
- var sel = e.id === selectedId ? ' class="selected"' : '';
295
- return (
296
- '<tr data-id="' + esc(e.id) + '"' + sel + '>' +
297
- '<td><span class="badge ' + esc(e.type) + '">' + esc(e.type) + '</span></td>' +
298
- '<td class="summary">' + esc(e.summary) +
299
- (e.durationMs != null ? ' <span class="time">(' + esc(e.durationMs) + 'ms)</span>' : '') +
300
- '</td>' +
301
- '<td class="trace">' + (e.traceId ? esc(e.traceId).slice(0, 12) : '—') + '</td>' +
302
- '<td class="time">' + esc(fmtTime(e.createdAt)) + '</td>' +
303
- '</tr>'
304
- );
305
- })
306
- .join('');
307
- Array.prototype.forEach.call(els.rows.querySelectorAll('tr'), function (tr) {
308
- tr.addEventListener('click', function () {
309
- openDetail(tr.getAttribute('data-id'));
310
- });
311
- });
312
- }
313
-
314
- function renderStats(s) {
315
- var fam = (s.topFamilies || [])
316
- .slice(0, 5)
317
- .map(function (f) { return esc(f.key) + ' (' + f.count + ')'; })
318
- .join(', ') || '—';
319
- var tags = (s.topTags || [])
320
- .slice(0, 5)
321
- .map(function (t) { return esc(t.key) + ' (' + t.count + ')'; })
322
- .join(', ') || '—';
323
- els.stats.innerHTML =
324
- '<span>Total entries: <b>' + esc(s.count) + '</b></span>' +
325
- '<span>Top families: <b>' + fam + '</b></span>' +
326
- '<span>Top tags: <b>' + tags + '</b></span>';
327
- }
328
-
329
- function openDetail(id) {
330
- selectedId = id;
331
- els.layout.classList.add('detail-open');
332
- highlight();
333
- els.detail.innerHTML = '<div class="empty">Loading…</div>';
334
- fetchJson(API_BASE + '/entries/' + encodeURIComponent(id))
335
- .then(function (res) {
336
- var e = res.data;
337
- var tags = (e.tags || [])
338
- .map(function (t) { return '<span class="badge">' + esc(t) + '</span>'; })
339
- .join('');
340
- els.detail.innerHTML =
341
- '<div class="detail-head">' +
342
- '<span class="badge ' + esc(e.type) + '">' + esc(e.type) + '</span>' +
343
- '<h2>' + esc(e.id) + '</h2>' +
344
- '<button class="close" id="close-detail">close</button>' +
345
- '</div>' +
346
- '<dl class="kv">' +
347
- '<dt>type</dt><dd>' + esc(e.type) + '</dd>' +
348
- '<dt>family</dt><dd>' + esc(e.familyHash || '—') + '</dd>' +
349
- '<dt>trace</dt><dd>' + esc(e.traceId || '—') + '</dd>' +
350
- '<dt>origin</dt><dd>' + esc(e.origin) + '</dd>' +
351
- '<dt>duration</dt><dd>' + esc(e.durationMs == null ? '—' : e.durationMs + 'ms') + '</dd>' +
352
- '<dt>created</dt><dd>' + esc(fmtTime(e.createdAt)) + '</dd>' +
353
- '</dl>' +
354
- (tags ? '<div class="kv tagline"><dt>tags</dt><dd>' + tags + '</dd></div>' : '') +
355
- '<pre>' + esc(JSON.stringify(e.content, null, 2)) + '</pre>';
356
- document.getElementById('close-detail').addEventListener('click', closeDetail);
357
- })
358
- .catch(function (err) {
359
- els.detail.innerHTML = '<div class="error">' + esc(err.message) + '</div>';
360
- });
361
- }
362
- function closeDetail() {
363
- selectedId = null;
364
- els.layout.classList.remove('detail-open');
365
- highlight();
366
- }
367
- function highlight() {
368
- Array.prototype.forEach.call(els.rows.querySelectorAll('tr'), function (tr) {
369
- tr.classList.toggle('selected', tr.getAttribute('data-id') === selectedId);
370
- });
371
- }
372
-
373
- // ---- extension dashboards (panel renderer) ----
374
- var SERIES_COLORS = ['#3fb950', '#f85149', '#58a6ff', '#d29922', '#a78bfa'];
375
-
376
- function fmtValue(v, format) {
377
- if (v == null || (typeof v === 'number' && isNaN(v))) return '—';
378
- if (format === 'percent') return (v * 100).toFixed(1) + '%';
379
- if (format === 'duration') return v >= 1000 ? (v / 1000).toFixed(2) + 's' : Math.round(v) + 'ms';
380
- if (format === 'rate') return Math.round(v * 10) / 10 + '/h';
381
- if (typeof v === 'number') return String(Math.round(v * 100) / 100);
382
- return String(v);
383
- }
384
- function thresholdClass(v, t) {
385
- if (!t || v == null) return '';
386
- if (t.direction === 'up-bad') return v >= t.bad ? 'bad' : v >= t.warn ? 'warn' : 'ok';
387
- return v <= t.bad ? 'bad' : v <= t.warn ? 'warn' : 'ok';
388
- }
389
- function clsColor(cls) {
390
- return cls === 'ok' ? 'var(--green)' : cls === 'warn' ? 'var(--yellow)' : cls === 'bad' ? 'var(--red)' : 'var(--accent)';
391
- }
392
- function fillLink(href, row) {
393
- return href.replace(/\{(\w+)\}/g, function (_, k) {
394
- return encodeURIComponent(row[k] == null ? '' : row[k]);
395
- });
396
- }
397
- function sparkline(points) {
398
- if (!points || !points.length) return '';
399
- var w = 100, h = 24, max = Math.max.apply(null, points), min = Math.min.apply(null, points);
400
- var span = max - min || 1, step = points.length > 1 ? w / (points.length - 1) : w;
401
- var d = points
402
- .map(function (p, i) {
403
- return (i ? 'L' : 'M') + (i * step).toFixed(1) + ' ' + (h - ((p - min) / span) * (h - 4) - 2).toFixed(1);
404
- })
405
- .join(' ');
406
- return '<svg viewBox="0 0 ' + w + ' ' + h + '" height="24" preserveAspectRatio="none"><path d="' + d + '" fill="none" stroke="var(--accent)" stroke-width="1.5"/></svg>';
407
- }
408
- function donut(segments) {
409
- var total = segments.reduce(function (s, x) { return s + (x.value || 0); }, 0) || 1;
410
- var r = 30, c = 2 * Math.PI * r, off = 0;
411
- var rings = segments
412
- .map(function (seg) {
413
- var frac = (seg.value || 0) / total;
414
- var dash = (frac * c).toFixed(2) + ' ' + (c - frac * c).toFixed(2);
415
- var ring = '<circle r="' + r + '" cx="40" cy="40" fill="none" stroke="' + (seg.color || 'var(--accent)') + '" stroke-width="12" stroke-dasharray="' + dash + '" stroke-dashoffset="' + (-off * c).toFixed(2) + '" transform="rotate(-90 40 40)"/>';
416
- off += frac;
417
- return ring;
418
- })
419
- .join('');
420
- return '<svg viewBox="0 0 80 80" height="90">' + rings + '</svg>';
421
- }
422
- function columns(rows, series, stacked) {
423
- if (!rows.length) return '<div class="muted-pill">no data</div>';
424
- var w = 100, h = 60, n = rows.length, bw = w / n, max = 1;
425
- rows.forEach(function (r) {
426
- var sum = stacked ? series.reduce(function (s, k) { return s + (r[k] || 0); }, 0) : Math.max.apply(null, series.map(function (k) { return r[k] || 0; }));
427
- if (sum > max) max = sum;
428
- });
429
- var bars = rows
430
- .map(function (r, i) {
431
- var x = i * bw, y = h, out = '';
432
- series.forEach(function (k, si) {
433
- var bh = ((r[k] || 0) / max) * (h - 2);
434
- y -= bh;
435
- out += '<rect x="' + (x + 1).toFixed(1) + '" y="' + y.toFixed(1) + '" width="' + (bw - 2).toFixed(1) + '" height="' + bh.toFixed(1) + '" fill="' + SERIES_COLORS[si % SERIES_COLORS.length] + '"/>';
436
- });
437
- return out;
438
- })
439
- .join('');
440
- return '<svg viewBox="0 0 ' + w + ' ' + h + '" height="70" preserveAspectRatio="none">' + bars + '</svg>';
441
- }
442
- function histogram(buckets) {
443
- if (!buckets.length) return '<div class="muted-pill">no data</div>';
444
- var w = 100, h = 60, n = buckets.length, bw = w / n;
445
- var max = Math.max.apply(null, buckets.map(function (b) { return b.count || 0; })) || 1;
446
- var bars = buckets
447
- .map(function (b, i) {
448
- var bh = ((b.count || 0) / max) * (h - 2);
449
- return '<rect x="' + (i * bw + 1).toFixed(1) + '" y="' + (h - bh).toFixed(1) + '" width="' + (bw - 2).toFixed(1) + '" height="' + bh.toFixed(1) + '" fill="var(--accent)"/>';
450
- })
451
- .join('');
452
- return '<svg viewBox="0 0 ' + w + ' ' + h + '" height="70" preserveAspectRatio="none">' + bars + '</svg>';
453
- }
454
- function barList(items) {
455
- if (!items.length) return '<div class="muted-pill">no data</div>';
456
- var max = Math.max.apply(null, items.map(function (i) { return i.value || 0; })) || 1;
457
- return '<div class="barlist">' + items
458
- .map(function (it) {
459
- var pct = (((it.value || 0) / max) * 100).toFixed(0);
460
- return '<div class="row"><span>' + esc(it.label) + '</span><b>' + esc(it.value) + '</b></div><div class="track"><span style="width:' + pct + '%"></span></div>';
461
- })
462
- .join('') + '</div>';
463
- }
464
- // Pager state for a paged table panel — a faithful mirror of core's
465
- // `tablePagination` (src/ui/ext_table.ts), which is the unit-tested source
466
- // of truth for this math. Keep the two in sync.
467
- function tablePagination(data) {
468
- var rows = data.rows || [];
469
- var page = Math.max(1, data.page || 1);
470
- var limit = data.limit && data.limit > 0 ? data.limit : (rows.length || 1);
471
- var total = data.total != null ? data.total : rows.length;
472
- var totalPages = Math.max(1, Math.ceil(total / limit));
473
- return { page: page, limit: limit, total: total, totalPages: totalPages, hasPrev: page > 1, hasNext: page < totalPages };
474
- }
475
- function panelTable(panel, data) {
476
- var cols = panel.columns || [], rows = data.rows || [];
477
- var head = '<tr>' + cols.map(function (c) { return '<th>' + esc(c.label) + '</th>'; }).join('') + '</tr>';
478
- var body = rows
479
- .map(function (row) {
480
- return '<tr>' + cols
481
- .map(function (c) {
482
- var cell = esc(row[c.key] == null ? '' : row[c.key]);
483
- if (c.link && c.link.href) {
484
- var tgt = c.link.external ? ' target="_blank" rel="noopener"' : '';
485
- cell = '<a href="' + esc(fillLink(c.link.href, row)) + '"' + tgt + '>' + cell + '</a>';
486
- }
487
- return '<td>' + cell + '</td>';
488
- })
489
- .join('') + '</tr>';
490
- })
491
- .join('');
492
- if (!panel.paged) {
493
- if (!rows.length) return '<div class="muted-pill">no data</div>';
494
- return '<table><thead>' + head + '</thead><tbody>' + body + '</tbody></table>';
495
- }
496
- // Paged: render the table (even when empty) + prev/next controls so the
497
- // operator can page through the provider's full result set.
498
- var p = tablePagination(data);
499
- var emptyRow = rows.length ? '' : '<tr><td colspan="' + cols.length + '" class="muted-pill" style="text-align:center">no data</td></tr>';
500
- var pager = '<div class="pager">' +
501
- '<button type="button" class="pager-prev"' + (p.hasPrev ? '' : ' disabled') + '>Prev</button>' +
502
- '<span>Page ' + p.page + ' of ' + p.totalPages + '</span>' +
503
- '<button type="button" class="pager-next"' + (p.hasNext ? '' : ' disabled') + '>Next</button>' +
504
- '</div>';
505
- return '<table><thead>' + head + '</thead><tbody>' + emptyRow + body + '</tbody></table>' + pager;
506
- }
507
- function renderPanelBody(panel, data) {
508
- if (panel.kind === 'stat') {
509
- var cls = thresholdClass(data.value, panel.thresholds);
510
- var delta = data.delta != null
511
- ? '<div class="delta ' + (data.delta >= 0 ? 'up' : 'down') + '">' + (data.delta >= 0 ? '▲' : '▼') + ' ' + esc(fmtValue(Math.abs(data.delta), panel.format)) + '</div>'
512
- : '';
513
- var spark = panel.spark && data.spark ? sparkline(data.spark) : '';
514
- return '<div class="big ' + cls + '">' + esc(fmtValue(data.value, panel.format)) + '</div>' + delta + spark;
515
- }
516
- if (panel.kind === 'gauge') {
517
- var gcls = thresholdClass(data.value, panel.thresholds);
518
- var max = panel.max != null ? panel.max : data.max != null ? data.max : 1;
519
- var pct = max ? Math.max(0, Math.min(100, (data.value / max) * 100)) : 0;
520
- return '<div class="big ' + gcls + '">' + esc(fmtValue(data.value, panel.format)) + '</div>' +
521
- '<div style="height:6px;border-radius:3px;background:var(--panel-2);margin-top:10px;overflow:hidden"><span style="display:block;height:100%;width:' + pct.toFixed(0) + '%;background:' + clsColor(gcls) + '"></span></div>';
522
- }
523
- if (panel.kind === 'table') return panelTable(panel, data);
524
- if (panel.kind === 'topN') return barList((data.items || []).slice(0, panel.limit || 8));
525
- if (panel.kind === 'breakdown') {
526
- var segs = data.segments || [];
527
- var legend = '<div class="legend">' + segs
528
- .map(function (s) { return '<span><span class="swatch" style="background:' + (s.color || 'var(--accent)') + '"></span>' + esc(s.label) + ' ' + esc(s.value) + '</span>'; })
529
- .join('') + '</div>';
530
- return donut(segs) + legend;
531
- }
532
- if (panel.kind === 'timeseries') return columns(data.rows || [], panel.series || [], panel.style === 'stacked');
533
- if (panel.kind === 'distribution') {
534
- var markers = (panel.markers || [])
535
- .map(function (m) { return '<span>' + m + ' <b>' + esc(fmtValue(data[m], panel.format)) + '</b></span>'; })
536
- .join('');
537
- return histogram(data.buckets || []) + (markers ? '<div class="markers">' + markers + '</div>' : '');
538
- }
539
- return '<div class="muted-pill">unsupported panel: ' + esc(panel.kind) + '</div>';
540
- }
541
- function panelCard(panel) {
542
- var card = document.createElement('div');
543
- card.className = 'card';
544
- card.innerHTML = '<div class="card-title">' + esc(panel.title) + '</div><div class="muted-pill">Loading…</div>';
545
- var binding = panel.data || {};
546
- var provider = binding.provider || '';
547
- var ext = provider.split('.')[0];
548
- var baseQuery = binding.query || {};
549
- var isPaged = panel.kind === 'table' && panel.paged;
550
- function wirePager(currentPage) {
551
- var prev = card.querySelector('.pager-prev');
552
- var next = card.querySelector('.pager-next');
553
- if (prev && !prev.disabled) prev.addEventListener('click', function () { load(currentPage - 1); });
554
- if (next && !next.disabled) next.addEventListener('click', function () { load(currentPage + 1); });
555
- }
556
- function load(page) {
557
- // Paged tables re-resolve the provider with query.page (1-based) merged
558
- // over the panel's own static data.query; other panels load once.
559
- var query = baseQuery;
560
- if (isPaged) {
561
- query = {};
562
- Object.keys(baseQuery).forEach(function (k) { query[k] = baseQuery[k]; });
563
- query.page = page;
564
- }
565
- var url = API_BASE + '/ext/' + encodeURIComponent(ext) + '/data/' + encodeURIComponent(provider) + qs(query);
566
- fetchJson(url)
567
- .then(function (res) {
568
- var data = res.data || {};
569
- card.innerHTML = '<div class="card-title">' + esc(panel.title) + '</div>' + renderPanelBody(panel, data);
570
- if (isPaged) wirePager(tablePagination(data).page);
571
- })
572
- .catch(function (err) {
573
- card.innerHTML = '<div class="card-title">' + esc(panel.title) + '</div><div class="error">' + esc(err.message) + '</div>';
574
- });
575
- }
576
- load(1);
577
- return card;
578
- }
579
- function renderDashboardSpec(spec) {
580
- els.dashboardView.innerHTML = '';
581
- var sections = spec.sections && spec.sections.length ? spec.sections : [{ panels: spec.panels || [], cols: 3 }];
582
- sections.forEach(function (section) {
583
- var secEl = document.createElement('div');
584
- secEl.className = 'dash-section';
585
- if (section.title) secEl.innerHTML = '<h3>' + esc(section.title) + '</h3>';
586
- var grid = document.createElement('div');
587
- grid.className = 'grid cols-' + (section.cols || 3);
588
- (section.panels || []).forEach(function (panel) { grid.appendChild(panelCard(panel)); });
589
- secEl.appendChild(grid);
590
- els.dashboardView.appendChild(secEl);
591
- });
592
- }
593
-
594
- function updateTabs() {
595
- Array.prototype.forEach.call(els.tabs.querySelectorAll('button'), function (b) {
596
- b.classList.toggle('active', b.getAttribute('data-tab') === activeTab);
597
- });
598
- }
599
- function showEntries() {
600
- activeTab = 'entries';
601
- els.layout.style.display = '';
602
- els.stats.style.display = '';
603
- els.entriesControls.style.display = '';
604
- els.dashboardView.style.display = 'none';
605
- updateTabs();
606
- load();
607
- }
608
- function showDashboard(spec) {
609
- activeTab = spec.id;
610
- els.layout.style.display = 'none';
611
- els.stats.style.display = 'none';
612
- els.entriesControls.style.display = 'none';
613
- els.dashboardView.style.display = '';
614
- updateTabs();
615
- renderDashboardSpec(spec);
616
- }
617
- function buildTabs(dashboards) {
618
- els.tabs.innerHTML = '';
619
- var entriesBtn = document.createElement('button');
620
- entriesBtn.setAttribute('data-tab', 'entries');
621
- entriesBtn.textContent = 'Entries';
622
- entriesBtn.addEventListener('click', showEntries);
623
- els.tabs.appendChild(entriesBtn);
624
- dashboards.forEach(function (d) {
625
- var b = document.createElement('button');
626
- b.setAttribute('data-tab', d.id);
627
- b.textContent = d.label;
628
- b.addEventListener('click', function () { showDashboard(d); });
629
- els.tabs.appendChild(b);
630
- });
631
- updateTabs();
632
- }
633
-
634
- function load() {
635
- var query = qs({ type: els.type.value, search: els.search.value.trim(), limit: 100 });
636
- fetchJson(API_BASE + '/entries' + query)
637
- .then(function (res) { renderRows(res.data || []); })
638
- .catch(function (err) {
639
- els.rows.innerHTML = '';
640
- els.empty.style.display = 'block';
641
- els.empty.textContent = 'Error: ' + err.message;
642
- });
643
- fetchJson(API_BASE + '/stats')
644
- .then(function (res) { renderStats(res.data || {}); })
645
- .catch(function () {});
646
- }
647
-
648
- function setAutoRefresh(on) {
649
- if (timer) { clearInterval(timer); timer = null; }
650
- if (on) timer = setInterval(load, 3000);
651
- }
652
-
653
- var searchDebounce;
654
- els.search.addEventListener('input', function () {
655
- clearTimeout(searchDebounce);
656
- searchDebounce = setTimeout(load, 250);
657
- });
658
- els.type.addEventListener('change', load);
659
- els.refresh.addEventListener('click', load);
660
- els.autorefresh.addEventListener('change', function () { setAutoRefresh(els.autorefresh.checked); });
661
-
662
- // Discover extension dashboards (entries-only when no extensions / the route is absent).
663
- fetchJson(API_BASE + '/meta')
664
- .then(function (res) {
665
- var dashboards = (res.data && res.data.dashboards) || [];
666
- if (dashboards.length) buildTabs(dashboards);
667
- })
668
- .catch(function () {});
669
-
670
- load();
671
- })();
672
- </script>
673
- </body>
674
- </html>