brakit 0.8.7 → 0.9.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.
- package/README.md +15 -3
- package/dist/api.d.ts +83 -46
- package/dist/api.js +776 -767
- package/dist/bin/brakit.js +305 -432
- package/dist/dashboard-client.global.js +465 -267
- package/dist/dashboard.html +584 -310
- package/dist/mcp/server.js +7 -15
- package/dist/runtime/index.js +1566 -1700
- package/package.json +1 -1
package/dist/dashboard.html
CHANGED
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
--red:#dc2626;
|
|
19
19
|
--cyan:#0891b2;
|
|
20
20
|
--green-bg:rgba(22,163,74,0.08);--green-bg-subtle:rgba(22,163,74,0.05);--green-border:rgba(22,163,74,0.2);--green-border-subtle:rgba(22,163,74,0.15);
|
|
21
|
+
--amber-bg:rgba(217,119,6,0.07);--red-bg:rgba(220,38,38,0.07);--blue-bg:rgba(37,99,235,0.08);--cyan-bg:rgba(8,145,178,0.07);
|
|
21
22
|
--sidebar-width:232px;--header-height:52px;
|
|
22
23
|
--radius:8px;--radius-sm:6px;
|
|
23
|
-
--shadow-sm:0 1px 2px rgba(0,0,0,0.
|
|
24
|
-
--shadow-md:0
|
|
25
|
-
--shadow-lg:0 4px
|
|
24
|
+
--shadow-sm:0 1px 3px rgba(0,0,0,0.06),0 1px 2px rgba(0,0,0,0.03);
|
|
25
|
+
--shadow-md:0 2px 6px rgba(0,0,0,0.08),0 1px 3px rgba(0,0,0,0.04);
|
|
26
|
+
--shadow-lg:0 4px 16px rgba(0,0,0,0.1),0 2px 6px rgba(0,0,0,0.05);
|
|
27
|
+
--transition:0.15s ease;
|
|
26
28
|
--breakdown-db:#6366f1;--breakdown-fetch:#f59e0b;--breakdown-app:#94a3b8;
|
|
27
29
|
--mono:'JetBrains Mono',ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;
|
|
28
30
|
--sans:Inter,system-ui,-apple-system,sans-serif;
|
|
@@ -120,8 +122,8 @@ bk-toast{display:block;position:fixed;top:0;left:0;right:0;z-index:100;pointer-e
|
|
|
120
122
|
.flow-req-count{font-family:var(--mono);font-size:12px;color:var(--text-muted);flex-shrink:0;text-align:right}
|
|
121
123
|
.flow-badge-pill{font-size:11px;flex-shrink:0;font-family:var(--mono);font-weight:600;padding:2px 10px;border-radius:10px;text-align:center}
|
|
122
124
|
.flow-badge-pill.badge-clean{background:var(--green-bg);color:var(--green)}
|
|
123
|
-
.flow-badge-pill.badge-warn{background:
|
|
124
|
-
.flow-badge-pill.badge-error{background:
|
|
125
|
+
.flow-badge-pill.badge-warn{background:var(--amber-bg);color:var(--amber)}
|
|
126
|
+
.flow-badge-pill.badge-error{background:var(--red-bg);color:var(--red)}
|
|
125
127
|
.flow-duration{font-family:var(--mono);font-size:12px;color:var(--text-muted);flex-shrink:0;width:60px;text-align:right}
|
|
126
128
|
|
|
127
129
|
/* Flow expand panel */
|
|
@@ -140,23 +142,23 @@ bk-toast{display:block;position:fixed;top:0;left:0;right:0;z-index:100;pointer-e
|
|
|
140
142
|
/* Method badges */
|
|
141
143
|
.method-badge{display:inline-flex;align-items:center;justify-content:center;padding:3px 8px;border-radius:5px;font-size:10px;font-weight:700;font-family:var(--mono);letter-spacing:.3px;flex-shrink:0}
|
|
142
144
|
.method-badge-GET{background:var(--green-bg);color:var(--green)}
|
|
143
|
-
.method-badge-POST{background:
|
|
144
|
-
.method-badge-PUT,.method-badge-PATCH{background:
|
|
145
|
-
.method-badge-DELETE{background:
|
|
145
|
+
.method-badge-POST{background:var(--blue-bg);color:var(--blue)}
|
|
146
|
+
.method-badge-PUT,.method-badge-PATCH{background:var(--amber-bg);color:var(--amber)}
|
|
147
|
+
.method-badge-DELETE{background:var(--red-bg);color:var(--red)}
|
|
146
148
|
.method-badge-HEAD,.method-badge-OPTIONS{background:var(--bg-muted);color:var(--text-muted)}
|
|
147
149
|
|
|
148
150
|
/* Status pills */
|
|
149
151
|
.status-pill{display:inline-flex;align-items:center;padding:1px 7px;border-radius:4px;font-size:11px;font-weight:600;font-family:var(--mono);flex-shrink:0}
|
|
150
152
|
.status-pill-2xx{background:var(--green-bg);color:var(--green)}
|
|
151
|
-
.status-pill-3xx{background:
|
|
152
|
-
.status-pill-4xx{background:
|
|
153
|
-
.status-pill-5xx{background:
|
|
153
|
+
.status-pill-3xx{background:var(--cyan-bg);color:var(--cyan)}
|
|
154
|
+
.status-pill-4xx{background:var(--amber-bg);color:var(--amber)}
|
|
155
|
+
.status-pill-5xx{background:var(--red-bg);color:var(--red)}
|
|
154
156
|
|
|
155
157
|
.traffic-card-path{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text);font-weight:500;font-size:13px}
|
|
156
158
|
.traffic-card-path.is-dup{color:var(--text-muted);font-weight:400}
|
|
157
159
|
.traffic-card-dur{color:var(--text-muted);font-size:12px;flex-shrink:0}
|
|
158
160
|
.traffic-card-size{color:var(--text-muted);font-size:11px;flex-shrink:0}
|
|
159
|
-
.traffic-card-dup{font-size:10px;color:var(--amber);flex-shrink:0;font-weight:600;background:
|
|
161
|
+
.traffic-card-dup{font-size:10px;color:var(--amber);flex-shrink:0;font-weight:600;background:var(--amber-bg);padding:1px 7px;border-radius:4px}
|
|
160
162
|
|
|
161
163
|
/* Body toggles */
|
|
162
164
|
.traffic-body{padding:0;margin-top:8px}
|
|
@@ -185,7 +187,7 @@ bk-toast{display:block;position:fixed;top:0;left:0;right:0;z-index:100;pointer-e
|
|
|
185
187
|
.flow-subreq .subreq-label.is-dup{color:var(--text-muted);font-weight:400}
|
|
186
188
|
.flow-subreq .subreq-status{flex-shrink:0}
|
|
187
189
|
.flow-subreq .subreq-dur{color:var(--text-muted);font-size:12px;text-align:right;flex-shrink:0}
|
|
188
|
-
.flow-subreq .subreq-dup-tag{font-size:10px;color:var(--amber);flex-shrink:0;font-weight:600;background:
|
|
190
|
+
.flow-subreq .subreq-dup-tag{font-size:10px;color:var(--amber);flex-shrink:0;font-weight:600;background:var(--amber-bg);padding:1px 7px;border-radius:4px}
|
|
189
191
|
.flow-subreq-detail{display:none;padding:12px 0;border-bottom:1px solid var(--border-subtle)}
|
|
190
192
|
.flow-subreq-detail.open{display:block}
|
|
191
193
|
|
|
@@ -215,6 +217,41 @@ bk-toast{display:block;position:fixed;top:0;left:0;right:0;z-index:100;pointer-e
|
|
|
215
217
|
.strict-mode-dupe{opacity:0.55}
|
|
216
218
|
.strict-mode-banner{font-size:11px;color:var(--text-muted);padding:6px 0 0;font-family:var(--mono)}
|
|
217
219
|
|
|
220
|
+
/* Flow detail tabs */
|
|
221
|
+
.flow-detail-tabs{display:flex;gap:0;margin-bottom:14px;border-bottom:1px solid var(--border)}
|
|
222
|
+
.flow-tab{padding:8px 16px;font-size:12px;font-family:var(--mono);font-weight:600;color:var(--text-muted);background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;transition:all .15s;letter-spacing:.3px}
|
|
223
|
+
.flow-tab:hover{color:var(--text)}
|
|
224
|
+
.flow-tab.active{color:var(--accent);border-bottom-color:var(--accent)}
|
|
225
|
+
|
|
226
|
+
/* Waterfall chart — request bars on time axis, sub-events as text rows */
|
|
227
|
+
.flow-waterfall{padding:0;font-family:var(--mono);font-size:11px}
|
|
228
|
+
.wf-time-axis{display:flex;justify-content:space-between;font-size:9px;color:var(--text-muted);padding:0 0 6px;margin-left:180px;margin-right:56px;border-bottom:1px solid var(--border);margin-bottom:2px}
|
|
229
|
+
.wf-rows{display:flex;flex-direction:column;gap:0}
|
|
230
|
+
|
|
231
|
+
/* Request group — request bar + its sub-events */
|
|
232
|
+
.wf-request-group{border-bottom:1px solid var(--border-subtle);padding:2px 0}
|
|
233
|
+
.wf-request-group:last-child{border-bottom:none}
|
|
234
|
+
|
|
235
|
+
/* Request row — label | bar on time axis | duration */
|
|
236
|
+
.wf-req-row{display:flex;align-items:center;gap:0;height:24px;transition:background .1s}
|
|
237
|
+
.wf-req-row:hover{background:var(--bg-hover)}
|
|
238
|
+
.wf-req-label{width:180px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text);font-weight:500;padding:0 10px 0 0;font-size:11px}
|
|
239
|
+
.wf-bar-track{flex:1;position:relative;height:14px;min-width:0;overflow:hidden}
|
|
240
|
+
.wf-bar{position:absolute;top:1px;height:12px;border-radius:3px;opacity:0.8;min-width:3px}
|
|
241
|
+
.wf-req-row:hover .wf-bar{opacity:1}
|
|
242
|
+
.wf-req-dur{width:56px;flex-shrink:0;text-align:right;color:var(--text-muted);font-size:10px;padding-left:8px}
|
|
243
|
+
|
|
244
|
+
/* Sub-event rows — same layout as request rows: label | bar track | duration */
|
|
245
|
+
.wf-sub-row{display:flex;align-items:center;gap:0;height:20px;transition:background .1s}
|
|
246
|
+
.wf-sub-row:hover{background:var(--bg-hover)}
|
|
247
|
+
.wf-sub-label{width:180px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted);font-size:10px;padding-left:14px;display:flex;align-items:center;gap:6px}
|
|
248
|
+
.wf-sub-dot{width:6px;height:6px;border-radius:2px;flex-shrink:0}
|
|
249
|
+
.wf-sub-bar-sized{height:8px !important;top:3px !important;opacity:0.65}
|
|
250
|
+
.wf-sub-row:hover .wf-sub-bar-sized{opacity:0.9}
|
|
251
|
+
.wf-sub-dur{width:56px;flex-shrink:0;text-align:right;color:var(--text-dim);font-size:9px;padding-left:8px}
|
|
252
|
+
|
|
253
|
+
.wf-loading{color:var(--text-muted);padding:12px 0;font-size:11px;font-family:var(--mono)}
|
|
254
|
+
|
|
218
255
|
/* Request rows */
|
|
219
256
|
.req-row{display:flex;align-items:center;gap:16px;padding:12px 28px;border-bottom:1px solid var(--border-subtle);cursor:pointer;transition:background .1s;font-family:var(--mono);font-size:14px}
|
|
220
257
|
.req-row:hover{background:var(--bg-hover)}
|
|
@@ -302,22 +339,39 @@ bk-toast{display:block;position:fixed;top:0;left:0;right:0;z-index:100;pointer-e
|
|
|
302
339
|
.perf-badge-lg{padding:4px 12px;font-size:13px;border-radius:var(--radius-sm)}
|
|
303
340
|
.perf-badge-sm{padding:1px 6px;font-size:9px}
|
|
304
341
|
|
|
305
|
-
/* Overview:
|
|
306
|
-
.perf-
|
|
307
|
-
.perf-
|
|
308
|
-
.perf-
|
|
309
|
-
.perf-
|
|
310
|
-
.perf-
|
|
311
|
-
.perf-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
.perf-
|
|
315
|
-
.perf-
|
|
316
|
-
.perf-
|
|
342
|
+
/* Overview: summary cards */
|
|
343
|
+
.perf-overview{padding:16px 28px}
|
|
344
|
+
.perf-summary-row{display:flex;gap:8px;margin-bottom:16px}
|
|
345
|
+
.perf-summary-card{flex:1;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;display:flex;flex-direction:column;gap:4px;box-shadow:var(--shadow-sm)}
|
|
346
|
+
.perf-summary-label{font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);font-family:var(--sans);font-weight:600}
|
|
347
|
+
.perf-summary-value{font-size:20px;font-weight:700;font-family:var(--mono);color:var(--text)}
|
|
348
|
+
.perf-summary-value-sm{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
349
|
+
|
|
350
|
+
/* Shared table styles */
|
|
351
|
+
.perf-table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:12px}
|
|
352
|
+
.perf-table thead th{text-align:left;font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);font-weight:600;font-family:var(--sans);padding:10px 14px;border-bottom:2px solid var(--border);white-space:nowrap}
|
|
353
|
+
.perf-table tbody td{padding:11px 14px;border-bottom:1px solid var(--border-subtle);color:var(--text)}
|
|
354
|
+
.perf-table-row{cursor:pointer;transition:background var(--transition, .15s ease)}
|
|
355
|
+
.perf-table-row:hover{background:var(--bg-hover)}
|
|
356
|
+
.perf-table tbody tr:last-child td{border-bottom:none}
|
|
357
|
+
.perf-th-right{text-align:right !important}
|
|
358
|
+
.perf-th-center{text-align:center !important}
|
|
359
|
+
.perf-td-right{text-align:right}
|
|
360
|
+
.perf-td-center{text-align:center}
|
|
361
|
+
.perf-td-name{font-weight:600;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
362
|
+
.perf-td-muted{color:var(--text-dim)}
|
|
363
|
+
.perf-row-err{background:var(--red-bg)}
|
|
364
|
+
.perf-row-err:hover{background:rgba(220,38,38,0.1)}
|
|
365
|
+
|
|
366
|
+
/* Heat map table wrapper */
|
|
367
|
+
.perf-heatmap{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm)}
|
|
368
|
+
.perf-hm-p95{display:inline-flex;align-items:center;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;border:1px solid}
|
|
369
|
+
.perf-hm-split-bar{display:flex;height:8px;border-radius:4px;overflow:hidden;background:var(--bg-muted);width:100%;min-width:80px}
|
|
317
370
|
|
|
318
371
|
/* Detail view */
|
|
319
372
|
.perf-detail-header{padding:20px 28px 16px;border-bottom:1px solid var(--border-subtle)}
|
|
320
373
|
.perf-detail-title{display:flex;align-items:center;gap:12px;font-size:17px;font-weight:600;color:var(--text);font-family:var(--mono)}
|
|
374
|
+
.perf-baseline-hint{font-size:11px;font-weight:400;color:var(--text-muted);padding:2px 8px;background:var(--bg-muted);border:1px solid var(--border);border-radius:var(--radius-sm)}
|
|
321
375
|
.perf-metric-row{display:flex;gap:4px;padding:16px 28px;border-bottom:1px solid var(--border-subtle)}
|
|
322
376
|
.perf-metric-card{flex:1;background:var(--bg-muted);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 16px;display:flex;flex-direction:column;gap:4px}
|
|
323
377
|
.perf-metric-label{font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);font-family:var(--sans);font-weight:600}
|
|
@@ -352,30 +406,51 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
352
406
|
.perf-canvas{border-radius:var(--radius);background:var(--bg-muted);border:1px solid var(--border)}
|
|
353
407
|
.perf-section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);margin-bottom:10px}
|
|
354
408
|
|
|
355
|
-
/* Request history
|
|
409
|
+
/* Request history */
|
|
356
410
|
.perf-history-wrap{padding:0 28px 20px}
|
|
357
|
-
.perf-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.perf-
|
|
361
|
-
.perf-
|
|
362
|
-
.perf-
|
|
363
|
-
.perf-
|
|
364
|
-
.perf-
|
|
365
|
-
.perf-
|
|
366
|
-
.perf-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.perf-
|
|
370
|
-
.perf-
|
|
411
|
+
.perf-hist-row-hl{background:rgba(37,99,235,0.1) !important;border-left:3px solid #4ade80}
|
|
412
|
+
|
|
413
|
+
/* Callers section */
|
|
414
|
+
.perf-callers{padding:16px 28px;border-bottom:1px solid var(--border-subtle)}
|
|
415
|
+
.perf-callers-list{display:flex;flex-direction:column;gap:0}
|
|
416
|
+
.perf-caller-row{display:flex;align-items:center;gap:12px;padding:8px 12px;border-bottom:1px solid var(--border-subtle);font-family:var(--mono);font-size:12px}
|
|
417
|
+
.perf-caller-row:last-child{border-bottom:none}
|
|
418
|
+
.perf-caller-name{flex:1;font-weight:500;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
419
|
+
.perf-caller-count{color:var(--text-muted);font-size:11px;flex-shrink:0}
|
|
420
|
+
.perf-caller-avg{color:var(--text-dim);font-size:11px;flex-shrink:0}
|
|
421
|
+
|
|
422
|
+
/* Query breakdown section */
|
|
423
|
+
.perf-queries{padding:16px 28px;border-bottom:1px solid var(--border-subtle)}
|
|
424
|
+
.perf-queries-loading{font-size:11px;color:var(--text-muted);font-family:var(--mono)}
|
|
425
|
+
.perf-queries-list{display:flex;flex-direction:column;gap:0}
|
|
426
|
+
.perf-query-row{display:flex;align-items:center;gap:12px;padding:8px 12px;border-bottom:1px solid var(--border-subtle);font-family:var(--mono);font-size:12px}
|
|
427
|
+
.perf-query-row:last-child{border-bottom:none}
|
|
428
|
+
.perf-query-label{flex:1;font-weight:500;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
429
|
+
.perf-query-avg{color:var(--text-muted);font-size:11px;flex-shrink:0}
|
|
430
|
+
.perf-query-count{color:var(--text-dim);font-size:11px;flex-shrink:0}
|
|
431
|
+
|
|
432
|
+
/* Session trends */
|
|
433
|
+
.perf-trends{padding:16px 28px;border-bottom:1px solid var(--border-subtle)}
|
|
434
|
+
.perf-trends-list{display:flex;flex-direction:column;gap:0}
|
|
435
|
+
.perf-trend-row{display:flex;align-items:center;gap:14px;padding:8px 12px;border-bottom:1px solid var(--border-subtle);font-family:var(--mono);font-size:12px}
|
|
436
|
+
.perf-trend-row:last-child{border-bottom:none}
|
|
437
|
+
.perf-trend-current{background:var(--bg-muted);border-radius:var(--radius-sm);font-weight:600}
|
|
438
|
+
.perf-trend-time{width:80px;color:var(--text-dim);font-size:11px;flex-shrink:0}
|
|
439
|
+
.perf-trend-p95{flex-shrink:0}
|
|
440
|
+
.perf-trend-reqs{color:var(--text-muted);font-size:11px;flex-shrink:0}
|
|
441
|
+
.perf-trend-errs{font-size:11px;flex-shrink:0}
|
|
442
|
+
.perf-trend-arrow{font-size:10px;font-weight:600;flex-shrink:0}
|
|
443
|
+
.perf-trend-slower{color:var(--red)}
|
|
444
|
+
.perf-trend-faster{color:var(--green)}
|
|
371
445
|
|
|
372
446
|
/* Overview */
|
|
373
447
|
.ov-container{padding:24px 28px}
|
|
374
448
|
|
|
375
449
|
/* Summary banner */
|
|
376
|
-
.ov-summary{display:flex;gap:
|
|
377
|
-
.ov-stat{display:flex;flex-direction:column;gap:
|
|
378
|
-
.ov-stat
|
|
450
|
+
.ov-summary{display:flex;gap:10px;margin-bottom:24px;flex-wrap:wrap}
|
|
451
|
+
.ov-stat{display:flex;flex-direction:column;gap:4px;flex:1;min-width:100px;padding:16px 18px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-sm);transition:box-shadow var(--transition, .15s ease)}
|
|
452
|
+
.ov-stat:hover{box-shadow:var(--shadow-md)}
|
|
453
|
+
.ov-stat-value{font-size:22px;font-weight:700;font-family:var(--mono);color:var(--text);line-height:1.2}
|
|
379
454
|
.ov-stat-label{font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);font-weight:600}
|
|
380
455
|
|
|
381
456
|
/* Section header */
|
|
@@ -384,8 +459,8 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
384
459
|
|
|
385
460
|
/* Insight cards */
|
|
386
461
|
.ov-cards{display:flex;flex-direction:column;gap:8px}
|
|
387
|
-
.ov-card{display:flex;align-items:flex-start;gap:14px;padding:
|
|
388
|
-
.ov-card:hover{background:var(--bg-hover);border-color:var(--border-light);box-shadow:var(--shadow-md)}
|
|
462
|
+
.ov-card{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:all var(--transition, .15s ease);box-shadow:var(--shadow-sm)}
|
|
463
|
+
.ov-card:hover{background:var(--bg-hover);border-color:var(--border-light);box-shadow:var(--shadow-md);transform:translateY(-1px)}
|
|
389
464
|
.ov-card-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;border-radius:50%;margin-top:2px}
|
|
390
465
|
.ov-card-icon.critical{background:rgba(220,38,38,.08);color:var(--red)}
|
|
391
466
|
.ov-card-icon.warning{background:rgba(217,119,6,.08);color:var(--amber)}
|
|
@@ -394,6 +469,7 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
394
469
|
.ov-card-body{flex:1;min-width:0}
|
|
395
470
|
.ov-card-title{font-size:13px;font-weight:600;color:var(--text);margin-bottom:2px}
|
|
396
471
|
.ov-card-desc{font-size:12px;color:var(--text-dim);line-height:1.5}
|
|
472
|
+
.ov-card-detail{font-size:11px;font-family:var(--mono);color:var(--text-muted);margin-top:6px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);line-height:1.5}
|
|
397
473
|
.ov-card-desc strong{color:var(--text);font-family:var(--mono);font-weight:600}
|
|
398
474
|
.ov-card-arrow{color:var(--text-muted);font-size:12px;flex-shrink:0;margin-top:2px;font-family:var(--mono);transition:transform .15s}
|
|
399
475
|
|
|
@@ -672,33 +748,33 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
672
748
|
<body>
|
|
673
749
|
<bk-dashboard></bk-dashboard>
|
|
674
750
|
<script>window.__BRAKIT_CONFIG__={port:{{PORT}},version:"{{VERSION}}"};</script>
|
|
675
|
-
<script>(function(){'use strict';var
|
|
676
|
-
\f\r]`,
|
|
677
|
-
\f\r"'\`<>=]|("|')|))|$)`,"g"),De=/'/g,Ne=/"/g,Ue=/^(?:script|style|textarea|title)$/i,oe=o=>(e,...t)=>({_$litType$:o,strings:e,values:t}),n=oe(1),Q=Symbol.for("lit-noChange"),d=Symbol.for("lit-nothing"),qe=new WeakMap,G=j.createTreeWalker(j,129);function Fe(o,e){if(!ie(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return Ie!==void 0?Ie.createHTML(e):e}var Is=(o,e)=>{let t=o.length-1,s=[],r,i=e===2?"<svg>":e===3?"<math>":"",a=gt;for(let c=0;c<t;c++){let l=o[c],h,m,p=-1,v=0;for(;v<l.length&&(a.lastIndex=v,m=a.exec(l),m!==null);)v=a.lastIndex,a===gt?m[1]==="!--"?a=Me:m[1]!==void 0?a=Oe:m[2]!==void 0?(Ue.test(m[2])&&(r=RegExp("</"+m[2],"g")),a=B):m[3]!==void 0&&(a=B):a===B?m[0]===">"?(a=r??gt,p=-1):m[1]===void 0?p=-2:(p=a.lastIndex-m[2].length,h=m[1],a=m[3]===void 0?B:m[3]==='"'?Ne:De):a===Ne||a===De?a=B:a===Me||a===Oe?a=gt:(a=B,r=void 0);let E=a===B&&o[c+1].startsWith("/>")?" ":"";i+=a===gt?l+ks:p>=0?(s.push(h),l.slice(0,p)+He+l.slice(p)+N+E):l+N+(p===-2?c:E);}return [Fe(o,i+(o[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),s]},yt=class o{constructor({strings:e,_$litType$:t},s){let r;this.parts=[];let i=0,a=0,c=e.length-1,l=this.parts,[h,m]=Is(e,t);if(this.el=o.createElement(h,s),G.currentNode=this.el.content,t===2||t===3){let p=this.el.content.firstChild;p.replaceWith(...p.childNodes);}for(;(r=G.nextNode())!==null&&l.length<c;){if(r.nodeType===1){if(r.hasAttributes())for(let p of r.getAttributeNames())if(p.endsWith(He)){let v=m[a++],E=r.getAttribute(p).split(N),C=/([.?@])?(.*)/.exec(v);l.push({type:1,index:i,name:C[2],strings:E,ctor:C[1]==="."?te:C[1]==="?"?ee:C[1]==="@"?se:st}),r.removeAttribute(p);}else p.startsWith(N)&&(l.push({type:6,index:i}),r.removeAttribute(p));if(Ue.test(r.tagName)){let p=r.textContent.split(N),v=p.length-1;if(v>0){r.textContent=qt?qt.emptyScript:"";for(let E=0;E<v;E++)r.append(p[E],Et()),G.nextNode(),l.push({type:2,index:++i});r.append(p[v],Et());}}}else if(r.nodeType===8)if(r.data===Pe)l.push({type:2,index:i});else {let p=-1;for(;(p=r.data.indexOf(N,p+1))!==-1;)l.push({type:7,index:i}),p+=N.length-1;}i++;}}static createElement(e,t){let s=j.createElement("template");return s.innerHTML=e,s}};function et(o,e,t=o,s){if(e===Q)return e;let r=s!==void 0?t._$Co?.[s]:t._$Cl,i=$t(e)?void 0:e._$litDirective$;return r?.constructor!==i&&(r?._$AO?.(false),i===void 0?r=void 0:(r=new i(o),r._$AT(o,t,s)),s!==void 0?(t._$Co??(t._$Co=[]))[s]=r:t._$Cl=r),r!==void 0&&(e=et(o,r._$AS(o,e.values),r,s)),e}var Zt=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:s}=this._$AD,r=(e?.creationScope??j).importNode(t,true);G.currentNode=r;let i=G.nextNode(),a=0,c=0,l=s[0];for(;l!==void 0;){if(a===l.index){let h;l.type===2?h=new St(i,i.nextSibling,this,e):l.type===1?h=new l.ctor(i,l.name,l.strings,this,e):l.type===6&&(h=new re(i,this,e)),this._$AV.push(h),l=s[++c];}a!==l?.index&&(i=G.nextNode(),a++);}return G.currentNode=j,r}p(e){let t=0;for(let s of this._$AV)s!==void 0&&(s.strings!==void 0?(s._$AI(e,s,t),t+=s.strings.length-2):s._$AI(e[t])),t++;}},St=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,s,r){this.type=2,this._$AH=d,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=s,this.options=r,this._$Cv=r?.isConnected??true;}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=et(this,e,t),$t(e)?e===d||e==null||e===""?(this._$AH!==d&&this._$AR(),this._$AH=d):e!==this._$AH&&e!==Q&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Ls(e)?this.k(e):this._(e);}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e));}_(e){this._$AH!==d&&$t(this._$AH)?this._$AA.nextSibling.data=e:this.T(j.createTextNode(e)),this._$AH=e;}$(e){let{values:t,_$litType$:s}=e,r=typeof s=="number"?this._$AC(e):(s.el===void 0&&(s.el=yt.createElement(Fe(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===r)this._$AH.p(t);else {let i=new Zt(r,this),a=i.u(this.options);i.p(t),this.T(a),this._$AH=i;}}_$AC(e){let t=qe.get(e.strings);return t===void 0&&qe.set(e.strings,t=new yt(e)),t}k(e){ie(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,s,r=0;for(let i of e)r===t.length?t.push(s=new o(this.O(Et()),this.O(Et()),this,this.options)):s=t[r],s._$AI(i),r++;r<t.length&&(this._$AR(s&&s._$AB.nextSibling,r),t.length=r);}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);e!==this._$AB;){let s=Le(e).nextSibling;Le(e).remove(),e=s;}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e));}},st=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,s,r,i){this.type=1,this._$AH=d,this._$AN=void 0,this.element=e,this.name=t,this._$AM=r,this.options=i,s.length>2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=d;}_$AI(e,t=this,s,r){let i=this.strings,a=false;if(i===void 0)e=et(this,e,t,0),a=!$t(e)||e!==this._$AH&&e!==Q,a&&(this._$AH=e);else {let c=e,l,h;for(e=i[0],l=0;l<i.length-1;l++)h=et(this,c[s+l],t,l),h===Q&&(h=this._$AH[l]),a||(a=!$t(h)||h!==this._$AH[l]),h===d?e=d:e!==d&&(e+=(h??"")+i[l+1]),this._$AH[l]=h;}a&&!r&&this.j(e);}j(e){e===d?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"");}},te=class extends st{constructor(){super(...arguments),this.type=3;}j(e){this.element[this.name]=e===d?void 0:e;}},ee=class extends st{constructor(){super(...arguments),this.type=4;}j(e){this.element.toggleAttribute(this.name,!!e&&e!==d);}},se=class extends st{constructor(e,t,s,r,i){super(e,t,s,r,i),this.type=5;}_$AI(e,t=this){if((e=et(this,e,t,0)??d)===Q)return;let s=this._$AH,r=e===d&&s!==d||e.capture!==s.capture||e.once!==s.once||e.passive!==s.passive,i=e!==d&&(s===d||r);r&&this.element.removeEventListener(this.name,this,s),i&&this.element.addEventListener(this.name,this,e),this._$AH=e;}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e);}},re=class{constructor(e,t,s){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(e){et(this,e);}};var Ms=bt.litHtmlPolyfillSupport;Ms?.(yt,St),(bt.litHtmlVersions??(bt.litHtmlVersions=[])).push("3.3.2");var Be=(o,e,t)=>{let s=t?.renderBefore??e,r=s._$litPart$;if(r===void 0){let i=t?.renderBefore??null;s._$litPart$=r=new St(e.insertBefore(Et(),i),i,void 0,t??{});}return r._$AI(o),r};var _t=globalThis,f=class extends M{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let e=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=e.firstChild),e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Be(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return Q}};f._$litElement$=true,f.finalized=true,_t.litElementHydrateSupport?.({LitElement:f});var Os=_t.litElementPolyfillSupport;Os?.({LitElement:f});(_t.litElementVersions??(_t.litElementVersions=[])).push("4.2.2");var g=o=>(e,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,e);}):customElements.define(o,e);};var Ds={attribute:true,type:String,converter:ft,reflect:false,hasChanged:Nt},Ns=(o=Ds,e,t)=>{let{kind:s,metadata:r}=t,i=globalThis.litPropertyMetadata.get(r);if(i===void 0&&globalThis.litPropertyMetadata.set(r,i=new Map),s==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),s==="accessor"){let{name:a}=t;return {set(c){let l=e.get.call(this);e.set.call(this,c),this.requestUpdate(a,l,o,true,c);},init(c){return c!==void 0&&this.C(a,void 0,o,c),c}}}if(s==="setter"){let{name:a}=t;return function(c){let l=this[a];e.call(this,c),this.requestUpdate(a,l,o,true,c);}}throw Error("Unsupported decorator location: "+s)};function y(o){return (e,t)=>typeof t=="object"?Ns(o,e,t):((s,r,i)=>{let a=r.hasOwnProperty(i);return r.constructor.createProperty(i,s),a?Object.getOwnPropertyDescriptor(r,i):void 0})(o,e,t)}function $(o){return y({...o,state:true,attribute:false})}var xt=class extends f{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return n`<span class="method-badge method-badge-${t}">${t}</span>`}};u([y()],xt.prototype,"method",2),xt=u([g("bk-method-badge")],xt);var L="/__brakit/api",O="/__brakit",R={flows:`${L}/flows`,requests:`${L}/requests`,events:`${L}/events`,clear:`${L}/clear`,fetches:`${L}/fetches`,errors:`${L}/errors`,logs:`${L}/logs`,queries:`${L}/queries`,metricsLive:`${L}/metrics/live`,insights:`${L}/insights`,tab:`${L}/tab`,activity:`${L}/activity`};var Tt="polling",Pt="static",qs="auth-handshake",Hs="auth-check",Ps="middleware",Ut={[qs]:1,[Hs]:1,[Ps]:1};var ne="fetch";var ae="error_event",ce="query",le="issues";var de={overview:"Overview",queries:"Queries",requests:"Requests",actions:"Actions",errors:"Errors",security:"Security",fetches:"Fetches",logs:"Logs",performance:"Performance"};var Rt={overview:"Overview",actions:"Actions",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",performance:"Performance",security:"Security"},pe={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",performance:"Endpoint health and response time trends",security:"Security findings and recommendations"};var he=100,V=300,W=800,me=2e3,ve=100,fe=50,ge=500;var q="__all__",jt={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},ze={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},be=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],wt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},Qt=[{max:he,label:"Fast",color:"var(--green)",bg:"rgba(22,163,74,0.08)",border:"rgba(22,163,74,0.2)"},{max:V,label:"Good",color:"var(--green)",bg:"rgba(22,163,74,0.06)",border:"rgba(22,163,74,0.15)"},{max:W,label:"OK",color:"var(--amber)",bg:"rgba(217,119,6,0.06)",border:"rgba(217,119,6,0.15)"},{max:me,label:"Slow",color:"var(--red)",bg:"rgba(220,38,38,0.06)",border:"rgba(220,38,38,0.15)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"rgba(220,38,38,0.08)",border:"rgba(220,38,38,0.2)"}],Je="rgba(228,228,231,0.8)",Ee="rgba(113,113,122,0.7)",Ze="10px monospace",$e="9px monospace",ts="8px monospace",es={top:16,right:16,bottom:28,left:52},ss={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},rs={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},is=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),os={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},ns=new Set(["host","connection","accept-encoding"]),H={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function S(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function Y(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function P(o){return o?o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}function rt(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function as(o){return os[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function Us(o,e){if(is.has(o.toLowerCase())){let t=String(e);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(e)}function it(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([e,t])=>'<span class="json-key">'+P(e)+"</span>: "+P(Us(e,t))).join(`
|
|
678
|
-
`)}function
|
|
751
|
+
<script>(function(){'use strict';var Rs=Object.defineProperty;var ws=Object.getOwnPropertyDescriptor;var u=(i,e,t,s)=>{for(var r=s>1?void 0:s?ws(e,t):e,o=i.length-1,n;o>=0;o--)(n=i[o])&&(r=(s?n(e,t,r):n(r))||r);return s&&r&&Rs(e,t,r),r};var Mt=globalThis,Ot=Mt.ShadowRoot&&(Mt.ShadyCSS===void 0||Mt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,xe=Symbol(),Te=new WeakMap,Lt=class{constructor(e,t,s){if(this._$cssResult$=true,s!==xe)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t;}get styleSheet(){let e=this.o,t=this.t;if(Ot&&e===void 0){let s=t!==void 0&&t.length===1;s&&(e=Te.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),s&&Te.set(t,e));}return e}toString(){return this.cssText}},Re=i=>new Lt(typeof i=="string"?i:i+"",void 0,xe);var we=(i,e)=>{if(Ot)i.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let s=document.createElement("style"),r=Mt.litNonce;r!==void 0&&s.setAttribute("nonce",r),s.textContent=t.cssText,i.appendChild(s);}},Qt=Ot?i=>i:i=>i instanceof CSSStyleSheet?(e=>{let t="";for(let s of e.cssRules)t+=s.cssText;return Re(t)})(i):i;var{is:As,defineProperty:Cs,getOwnPropertyDescriptor:Is,getOwnPropertyNames:Ms,getOwnPropertySymbols:Ls,getPrototypeOf:Os}=Object,N=globalThis,Ae=N.trustedTypes,ks=Ae?Ae.emptyScript:"",Ns=N.reactiveElementPolyfillSupport,ht=(i,e)=>i,mt={toAttribute(i,e){switch(e){case Boolean:i=i?ks:null;break;case Object:case Array:i=i==null?i:JSON.stringify(i);}return i},fromAttribute(i,e){let t=i;switch(e){case Boolean:t=i!==null;break;case Number:t=i===null?null:Number(i);break;case Object:case Array:try{t=JSON.parse(i);}catch{t=null;}}return t}},kt=(i,e)=>!As(i,e),Ce={attribute:true,type:String,converter:mt,reflect:false,useDefault:false,hasChanged:kt};Symbol.metadata??(Symbol.metadata=Symbol("metadata")),N.litPropertyMetadata??(N.litPropertyMetadata=new WeakMap);var O=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??(this.l=[])).push(e);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=Ce){if(t.state&&(t.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=true),this.elementProperties.set(e,t),!t.noAccessor){let s=Symbol(),r=this.getPropertyDescriptor(e,s,t);r!==void 0&&Cs(this.prototype,e,r);}}static getPropertyDescriptor(e,t,s){let{get:r,set:o}=Is(this.prototype,e)??{get(){return this[t]},set(n){this[t]=n;}};return {get:r,set(n){let l=r?.call(this);o?.call(this,n),this.requestUpdate(e,l,s);},configurable:true,enumerable:true}}static getPropertyOptions(e){return this.elementProperties.get(e)??Ce}static _$Ei(){if(this.hasOwnProperty(ht("elementProperties")))return;let e=Os(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties);}static finalize(){if(this.hasOwnProperty(ht("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(ht("properties"))){let t=this.properties,s=[...Ms(t),...Ls(t)];for(let r of s)this.createProperty(r,t[r]);}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[s,r]of t)this.elementProperties.set(s,r);}this._$Eh=new Map;for(let[t,s]of this.elementProperties){let r=this._$Eu(t,s);r!==void 0&&this._$Eh.set(r,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let s=new Set(e.flat(1/0).reverse());for(let r of s)t.unshift(Qt(r));}else e!==void 0&&t.push(Qt(e));return t}static _$Eu(e,t){let s=t.attribute;return s===false?void 0:typeof s=="string"?s:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this));}addController(e){(this._$EO??(this._$EO=new Set)).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.();}removeController(e){this._$EO?.delete(e);}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let s of t.keys())this.hasOwnProperty(s)&&(e.set(s,this[s]),delete this[s]);e.size>0&&(this._$Ep=e);}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return we(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??(this.renderRoot=this.createRenderRoot()),this.enableUpdating(true),this._$EO?.forEach(e=>e.hostConnected?.());}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.());}attributeChangedCallback(e,t,s){this._$AK(e,s);}_$ET(e,t){let s=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,s);if(r!==void 0&&s.reflect===true){let o=(s.converter?.toAttribute!==void 0?s.converter:mt).toAttribute(t,s.type);this._$Em=e,o==null?this.removeAttribute(r):this.setAttribute(r,o),this._$Em=null;}}_$AK(e,t){let s=this.constructor,r=s._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let o=s.getPropertyOptions(r),n=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:mt;this._$Em=r;let l=n.fromAttribute(t,o.type);this[r]=l??this._$Ej?.get(r)??l,this._$Em=null;}}requestUpdate(e,t,s,r=false,o){if(e!==void 0){let n=this.constructor;if(r===false&&(o=this[e]),s??(s=n.getPropertyOptions(e)),!((s.hasChanged??kt)(o,t)||s.useDefault&&s.reflect&&o===this._$Ej?.get(e)&&!this.hasAttribute(n._$Eu(e,s))))return;this.C(e,t,s);}this.isUpdatePending===false&&(this._$ES=this._$EP());}C(e,t,{useDefault:s,reflect:r,wrapped:o},n){s&&!(this._$Ej??(this._$Ej=new Map)).has(e)&&(this._$Ej.set(e,n??t??this[e]),o!==true||n!==void 0)||(this._$AL.has(e)||(this.hasUpdated||s||(t=void 0),this._$AL.set(e,t)),r===true&&this._$Em!==e&&(this._$Eq??(this._$Eq=new Set)).add(e));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??(this.renderRoot=this.createRenderRoot()),this._$Ep){for(let[r,o]of this._$Ep)this[r]=o;this._$Ep=void 0;}let s=this.constructor.elementProperties;if(s.size>0)for(let[r,o]of s){let{wrapped:n}=o,l=this[r];n!==true||this._$AL.has(r)||l===void 0||this.C(r,void 0,o,l);}}let e=false,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(s=>s.hostUpdate?.()),this.update(t)):this._$EM();}catch(s){throw e=false,this._$EM(),s}e&&this._$AE(t);}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(e)),this.updated(e);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return true}update(e){this._$Eq&&(this._$Eq=this._$Eq.forEach(t=>this._$ET(t,this[t]))),this._$EM();}updated(e){}firstUpdated(e){}};O.elementStyles=[],O.shadowRootOptions={mode:"open"},O[ht("elementProperties")]=new Map,O[ht("finalized")]=new Map,Ns?.({ReactiveElement:O}),(N.reactiveElementVersions??(N.reactiveElementVersions=[])).push("2.1.2");var ft=globalThis,Ie=i=>i,Nt=ft.trustedTypes,Me=Nt?Nt.createPolicy("lit-html",{createHTML:i=>i}):void 0,He="$lit$",D=`lit$${Math.random().toFixed(9).slice(2)}$`,qe="?"+D,Ds=`<${qe}>`,W=document,gt=()=>W.createComment(""),bt=i=>i===null||typeof i!="object"&&typeof i!="function",Zt=Array.isArray,Hs=i=>Zt(i)||typeof i?.[Symbol.iterator]=="function",Vt=`[
|
|
752
|
+
\f\r]`,vt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Le=/-->/g,Oe=/>/g,B=RegExp(`>|${Vt}(?:([^\\s"'>=/]+)(${Vt}*=${Vt}*(?:[^
|
|
753
|
+
\f\r"'\`<>=]|("|')|))|$)`,"g"),ke=/'/g,Ne=/"/g,Pe=/^(?:script|style|textarea|title)$/i,te=i=>(e,...t)=>({_$litType$:i,strings:e,values:t}),a=te(1),j=Symbol.for("lit-noChange"),d=Symbol.for("lit-nothing"),De=new WeakMap,G=W.createTreeWalker(W,129);function Ue(i,e){if(!Zt(i)||!i.hasOwnProperty("raw"))throw Error("invalid template strings array");return Me!==void 0?Me.createHTML(e):e}var qs=(i,e)=>{let t=i.length-1,s=[],r,o=e===2?"<svg>":e===3?"<math>":"",n=vt;for(let l=0;l<t;l++){let c=i[l],p,h,m=-1,b=0;for(;b<c.length&&(n.lastIndex=b,h=n.exec(c),h!==null);)b=n.lastIndex,n===vt?h[1]==="!--"?n=Le:h[1]!==void 0?n=Oe:h[2]!==void 0?(Pe.test(h[2])&&(r=RegExp("</"+h[2],"g")),n=B):h[3]!==void 0&&(n=B):n===B?h[0]===">"?(n=r??vt,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,p=h[1],n=h[3]===void 0?B:h[3]==='"'?Ne:ke):n===Ne||n===ke?n=B:n===Le||n===Oe?n=vt:(n=B,r=void 0);let $=n===B&&i[l+1].startsWith("/>")?" ":"";o+=n===vt?c+Ds:m>=0?(s.push(p),c.slice(0,m)+He+c.slice(m)+D+$):c+D+(m===-2?l:$);}return [Ue(i,o+(i[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),s]},Et=class i{constructor({strings:e,_$litType$:t},s){let r;this.parts=[];let o=0,n=0,l=e.length-1,c=this.parts,[p,h]=qs(e,t);if(this.el=i.createElement(p,s),G.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(r=G.nextNode())!==null&&c.length<l;){if(r.nodeType===1){if(r.hasAttributes())for(let m of r.getAttributeNames())if(m.endsWith(He)){let b=h[n++],$=r.getAttribute(m).split(D),E=/([.?@])?(.*)/.exec(b);c.push({type:1,index:o,name:E[2],strings:$,ctor:E[1]==="."?Xt:E[1]==="?"?Kt:E[1]==="@"?zt:z}),r.removeAttribute(m);}else m.startsWith(D)&&(c.push({type:6,index:o}),r.removeAttribute(m));if(Pe.test(r.tagName)){let m=r.textContent.split(D),b=m.length-1;if(b>0){r.textContent=Nt?Nt.emptyScript:"";for(let $=0;$<b;$++)r.append(m[$],gt()),G.nextNode(),c.push({type:2,index:++o});r.append(m[b],gt());}}}else if(r.nodeType===8)if(r.data===qe)c.push({type:2,index:o});else {let m=-1;for(;(m=r.data.indexOf(D,m+1))!==-1;)c.push({type:7,index:o}),m+=D.length-1;}o++;}}static createElement(e,t){let s=W.createElement("template");return s.innerHTML=e,s}};function K(i,e,t=i,s){if(e===j)return e;let r=s!==void 0?t._$Co?.[s]:t._$Cl,o=bt(e)?void 0:e._$litDirective$;return r?.constructor!==o&&(r?._$AO?.(false),o===void 0?r=void 0:(r=new o(i),r._$AT(i,t,s)),s!==void 0?(t._$Co??(t._$Co=[]))[s]=r:t._$Cl=r),r!==void 0&&(e=K(i,r._$AS(i,e.values),r,s)),e}var Yt=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:s}=this._$AD,r=(e?.creationScope??W).importNode(t,true);G.currentNode=r;let o=G.nextNode(),n=0,l=0,c=s[0];for(;c!==void 0;){if(n===c.index){let p;c.type===2?p=new _t(o,o.nextSibling,this,e):c.type===1?p=new c.ctor(o,c.name,c.strings,this,e):c.type===6&&(p=new Jt(o,this,e)),this._$AV.push(p),c=s[++l];}n!==c?.index&&(o=G.nextNode(),n++);}return G.currentNode=W,r}p(e){let t=0;for(let s of this._$AV)s!==void 0&&(s.strings!==void 0?(s._$AI(e,s,t),t+=s.strings.length-2):s._$AI(e[t])),t++;}},_t=class i{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,s,r){this.type=2,this._$AH=d,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=s,this.options=r,this._$Cv=r?.isConnected??true;}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=K(this,e,t),bt(e)?e===d||e==null||e===""?(this._$AH!==d&&this._$AR(),this._$AH=d):e!==this._$AH&&e!==j&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Hs(e)?this.k(e):this._(e);}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e));}_(e){this._$AH!==d&&bt(this._$AH)?this._$AA.nextSibling.data=e:this.T(W.createTextNode(e)),this._$AH=e;}$(e){let{values:t,_$litType$:s}=e,r=typeof s=="number"?this._$AC(e):(s.el===void 0&&(s.el=Et.createElement(Ue(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===r)this._$AH.p(t);else {let o=new Yt(r,this),n=o.u(this.options);o.p(t),this.T(n),this._$AH=o;}}_$AC(e){let t=De.get(e.strings);return t===void 0&&De.set(e.strings,t=new Et(e)),t}k(e){Zt(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,s,r=0;for(let o of e)r===t.length?t.push(s=new i(this.O(gt()),this.O(gt()),this,this.options)):s=t[r],s._$AI(o),r++;r<t.length&&(this._$AR(s&&s._$AB.nextSibling,r),t.length=r);}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);e!==this._$AB;){let s=Ie(e).nextSibling;Ie(e).remove(),e=s;}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e));}},z=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,s,r,o){this.type=1,this._$AH=d,this._$AN=void 0,this.element=e,this.name=t,this._$AM=r,this.options=o,s.length>2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=d;}_$AI(e,t=this,s,r){let o=this.strings,n=false;if(o===void 0)e=K(this,e,t,0),n=!bt(e)||e!==this._$AH&&e!==j,n&&(this._$AH=e);else {let l=e,c,p;for(e=o[0],c=0;c<o.length-1;c++)p=K(this,l[s+c],t,c),p===j&&(p=this._$AH[c]),n||(n=!bt(p)||p!==this._$AH[c]),p===d?e=d:e!==d&&(e+=(p??"")+o[c+1]),this._$AH[c]=p;}n&&!r&&this.j(e);}j(e){e===d?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"");}},Xt=class extends z{constructor(){super(...arguments),this.type=3;}j(e){this.element[this.name]=e===d?void 0:e;}},Kt=class extends z{constructor(){super(...arguments),this.type=4;}j(e){this.element.toggleAttribute(this.name,!!e&&e!==d);}},zt=class extends z{constructor(e,t,s,r,o){super(e,t,s,r,o),this.type=5;}_$AI(e,t=this){if((e=K(this,e,t,0)??d)===j)return;let s=this._$AH,r=e===d&&s!==d||e.capture!==s.capture||e.once!==s.once||e.passive!==s.passive,o=e!==d&&(s===d||r);r&&this.element.removeEventListener(this.name,this,s),o&&this.element.addEventListener(this.name,this,e),this._$AH=e;}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e);}},Jt=class{constructor(e,t,s){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(e){K(this,e);}};var Ps=ft.litHtmlPolyfillSupport;Ps?.(Et,_t),(ft.litHtmlVersions??(ft.litHtmlVersions=[])).push("3.3.2");var Fe=(i,e,t)=>{let s=t?.renderBefore??e,r=s._$litPart$;if(r===void 0){let o=t?.renderBefore??null;s._$litPart$=r=new _t(e.insertBefore(gt(),o),o,void 0,t??{});}return r._$AI(i),r};var St=globalThis,f=class extends O{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let e=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=e.firstChild),e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Fe(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return j}};f._$litElement$=true,f.finalized=true,St.litElementHydrateSupport?.({LitElement:f});var Us=St.litElementPolyfillSupport;Us?.({LitElement:f});(St.litElementVersions??(St.litElementVersions=[])).push("4.2.2");var g=i=>(e,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(i,e);}):customElements.define(i,e);};var Fs={attribute:true,type:String,converter:mt,reflect:false,hasChanged:kt},Bs=(i=Fs,e,t)=>{let{kind:s,metadata:r}=t,o=globalThis.litPropertyMetadata.get(r);if(o===void 0&&globalThis.litPropertyMetadata.set(r,o=new Map),s==="setter"&&((i=Object.create(i)).wrapped=true),o.set(t.name,i),s==="accessor"){let{name:n}=t;return {set(l){let c=e.get.call(this);e.set.call(this,l),this.requestUpdate(n,c,i,true,l);},init(l){return l!==void 0&&this.C(n,void 0,i,l),l}}}if(s==="setter"){let{name:n}=t;return function(l){let c=this[n];e.call(this,l),this.requestUpdate(n,c,i,true,l);}}throw Error("Unsupported decorator location: "+s)};function T(i){return (e,t)=>typeof t=="object"?Bs(i,e,t):((s,r,o)=>{let n=r.hasOwnProperty(o);return r.constructor.createProperty(o,s),n?Object.getOwnPropertyDescriptor(r,o):void 0})(i,e,t)}function _(i){return T({...i,state:true,attribute:false})}var $t=class extends f{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return a`<span class="method-badge method-badge-${t}">${t}</span>`}};u([T()],$t.prototype,"method",2),$t=u([g("bk-method-badge")],$t);var I="/__brakit/api",k="/__brakit",y={flows:`${I}/flows`,requests:`${I}/requests`,events:`${I}/events`,clear:`${I}/clear`,fetches:`${I}/fetches`,errors:`${I}/errors`,logs:`${I}/logs`,queries:`${I}/queries`,metricsLive:`${I}/metrics/live`,insights:`${I}/insights`,tab:`${I}/tab`,activity:`${I}/activity`};var J="polling",Ht="static",Gs="auth-handshake",Ws="auth-check",js="middleware",yt={[Gs]:1,[Ws]:1,[js]:1};var ee="fetch";var se="error_event",re="query",oe="issues";var ie={overview:"Overview",actions:"Actions",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",performance:"Performance",security:"Security"},ne={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",performance:"Endpoint health and response time trends",security:"Security findings and recommendations"};var le=100,Z=300,tt=800,ce=2e3,de=100,pe=50,ue=500;var H="__all__",Ft={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},Ke={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},me=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Tt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},et=[{max:le,label:"Fast",color:"var(--green)",bg:"rgba(22,163,74,0.08)",border:"rgba(22,163,74,0.2)"},{max:Z,label:"Good",color:"var(--green)",bg:"rgba(22,163,74,0.06)",border:"rgba(22,163,74,0.15)"},{max:tt,label:"OK",color:"var(--amber)",bg:"rgba(217,119,6,0.06)",border:"rgba(217,119,6,0.15)"},{max:ce,label:"Slow",color:"var(--red)",bg:"rgba(220,38,38,0.06)",border:"rgba(220,38,38,0.15)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"rgba(220,38,38,0.08)",border:"rgba(220,38,38,0.2)"}],ze="rgba(228,228,231,0.8)",ve="rgba(113,113,122,0.7)",Je="10px monospace",fe="9px monospace";var Ze={top:16,right:16,bottom:28,left:52},ts={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},es={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},ss=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),rs={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},os=new Set(["host","connection","accept-encoding"]),q={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function v(i){return i<1e3?i+"ms":(i/1e3).toFixed(1)+"s"}function U(i){return !i||i===0?"":i<1024?i+"b":(i/1024).toFixed(1)+"kb"}function P(i){return i?i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}function st(i){return i>=500?"status-pill-5xx":i>=400?"status-pill-4xx":i>=300?"status-pill-3xx":"status-pill-2xx"}function is(i){return rs[i]||(i>=500?"Server Error":i>=400?"Client Error":"OK")}function Qs(i,e){if(ss.has(i.toLowerCase())){let t=String(e);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(e)}function rt(i){return !i||Object.keys(i).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(i).map(([e,t])=>'<span class="json-key">'+P(e)+"</span>: "+P(Qs(e,t))).join(`
|
|
754
|
+
`)}function Q(i){if(!i)return '<span style="color:var(--text-muted)">No body</span>';try{let e=JSON.parse(i);return Vs(JSON.stringify(e,null,2))}catch{return P(i)}}function Vs(i){return P(i).replace(/("(?:[^"\\]|\\.)*")(\s*:)?|\b(true|false)\b|\bnull\b|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)/g,(e,t,s,r,o)=>t?s?'<span class="json-key">'+t+"</span>"+s:'<span class="json-str">'+t+"</span>":r?'<span class="json-bool">'+e+"</span>":o?'<span class="json-num">'+e+"</span>":e==="null"?'<span class="json-null">null</span>':e)}var xt=class extends f{constructor(){super(...arguments);this.code=0;}createRenderRoot(){return this}render(){let t=st(this.code);return a`<span class="status-pill ${t}">${this.code}</span>`}};u([T({type:Number})],xt.prototype,"code",2),xt=u([g("bk-status-pill")],xt);var Rt=class extends f{constructor(){super(...arguments);this.ms=0;}createRenderRoot(){return this}render(){return a`<span class="req-duration">${v(this.ms)}</span>`}};u([T({type:Number})],Rt.prototype,"ms",2),Rt=u([g("bk-duration-label")],Rt);var ot=class extends f{constructor(){super(...arguments);this.title="";this.subtitle="";}createRenderRoot(){return this}render(){return a`
|
|
679
755
|
<div class="empty">
|
|
680
756
|
<span class="empty-title">${this.title}</span>
|
|
681
757
|
<span class="empty-sub">${this.subtitle}</span>
|
|
682
758
|
</div>
|
|
683
|
-
`}};u([
|
|
759
|
+
`}};u([T()],ot.prototype,"title",2),u([T()],ot.prototype,"subtitle",2),ot=u([g("bk-empty-state")],ot);var C=class extends f{constructor(){super(...arguments);this.message="";this.visible=false;}createRenderRoot(){return this}static show(t){let s=document.querySelector("bk-toast");s&&s.showMessage(t);}showMessage(t){this.hideTimer&&clearTimeout(this.hideTimer),this.message=t,this.visible=true,this.hideTimer=setTimeout(()=>{this.visible=false;},2e3);}render(){return a`<div class="toast ${this.visible?"show":""}">${this.message}</div>`}};u([_()],C.prototype,"message",2),u([_()],C.prototype,"visible",2),C=u([g("bk-toast")],C);var V=class extends f{constructor(){super(...arguments);this.text="";this.label="Copy";this.toastMessage="Copied";}createRenderRoot(){return this}async copy(t){t.stopPropagation();try{await navigator.clipboard.writeText(this.text),C.show(this.toastMessage);}catch{}}render(){return a`<button class="query-detail-copy" @click=${this.copy}>${this.label}</button>`}};u([T()],V.prototype,"text",2),u([T()],V.prototype,"label",2),u([T({attribute:"toast-message"})],V.prototype,"toastMessage",2),V=u([g("bk-copy-button")],V);var Y=class extends f{constructor(){super(...arguments);this.value="";this.label="";this.color="";}createRenderRoot(){return this}render(){return a`
|
|
684
760
|
<div class="fetch-stat">
|
|
685
761
|
<span class="fetch-stat-value" style="color:${this.color}">${this.value}</span>
|
|
686
762
|
<span class="fetch-stat-label">${this.label}</span>
|
|
687
763
|
</div>
|
|
688
|
-
`}};u([
|
|
764
|
+
`}};u([T()],Y.prototype,"value",2),u([T()],Y.prototype,"label",2),u([T()],Y.prototype,"color",2),Y=u([g("bk-stat-card")],Y);var F=class extends Event{constructor(e,t,s,r){super("context-request",{bubbles:true,composed:true}),this.context=e,this.contextTarget=t,this.callback=s,this.subscribe=r??false;}};var it=class{constructor(e,t,s,r){if(this.subscribe=false,this.provided=false,this.value=void 0,this.t=(o,n)=>{this.unsubscribe&&(this.unsubscribe!==n&&(this.provided=false,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=o,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=true,this.callback&&this.callback(o,n)),this.unsubscribe=n;},this.host=e,t.context!==void 0){let o=t;this.context=o.context,this.callback=o.callback,this.subscribe=o.subscribe??false;}else this.context=t,this.callback=s,this.subscribe=r??false;this.host.addController(this);}hostConnected(){this.dispatchRequest();}hostDisconnected(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0);}dispatchRequest(){this.host.dispatchEvent(new F(this.context,this.host,this.t,this.subscribe));}};var Bt=class{get value(){return this.o}set value(e){this.setValue(e);}setValue(e,t=false){let s=t||!Object.is(e,this.o);this.o=e,s&&this.updateObservers();}constructor(e){this.subscriptions=new Map,this.updateObservers=()=>{for(let[t,{disposer:s}]of this.subscriptions)t(this.o,s);},e!==void 0&&(this.value=e);}addCallback(e,t,s){if(!s)return void e(this.value);this.subscriptions.has(e)||this.subscriptions.set(e,{disposer:()=>{this.subscriptions.delete(e);},consumerHost:t});let{disposer:r}=this.subscriptions.get(e);e(this.value,r);}clearCallbacks(){this.subscriptions.clear();}};var ge=class extends Event{constructor(e,t){super("context-provider",{bubbles:true,composed:true}),this.context=e,this.contextTarget=t;}},nt=class extends Bt{constructor(e,t,s){super(t.context!==void 0?t.initialValue:s),this.onContextRequest=r=>{if(r.context!==this.context)return;let o=r.contextTarget??r.composedPath()[0];o!==this.host&&(r.stopPropagation(),this.addCallback(r.callback,o,r.subscribe));},this.onProviderRequest=r=>{if(r.context!==this.context||(r.contextTarget??r.composedPath()[0])===this.host)return;let o=new Set;for(let[n,{consumerHost:l}]of this.subscriptions)o.has(n)||(o.add(n),l.dispatchEvent(new F(this.context,l,n,true)));r.stopPropagation();},this.host=e,t.context!==void 0?this.context=t.context:this.context=t,this.attachListeners(),this.host.addController?.(this);}attachListeners(){this.host.addEventListener("context-request",this.onContextRequest),this.host.addEventListener("context-provider",this.onProviderRequest);}hostConnected(){this.host.dispatchEvent(new ge(this.context,this.host));}};function be({context:i}){return (e,t)=>{let s=new WeakMap;if(typeof t=="object")return {get(){return e.get.call(this)},set(r){return s.get(this).setValue(r),e.set.call(this,r)},init(r){return s.set(this,new nt(this,{context:i,initialValue:r})),r}};{e.constructor.addInitializer((n=>{s.set(n,new nt(n,{context:i}));}));let r=Object.getOwnPropertyDescriptor(e,t),o;if(r===void 0){let n=new WeakMap;o={get(){return n.get(this)},set(l){s.get(this).setValue(l),n.set(this,l);},configurable:true,enumerable:true};}else {let n=r.set;o={...r,set(l){s.get(this).setValue(l),n?.call(this,l);}};}return void Object.defineProperty(e,t,o)}}}function R({context:i,subscribe:e}){return (t,s)=>{typeof s=="object"?s.addInitializer((function(){new it(this,{context:i,callback:r=>{t.set.call(this,r);},subscribe:e});})):t.constructor.addInitializer((r=>{new it(r,{context:i,callback:o=>{r[s]=o;},subscribe:e});}));}}var x="dashboard-store",Gt=class extends EventTarget{constructor(){super(...arguments);this._state={flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[],viewMode:"simple",activeView:"overview"};}get state(){return this._state}setFlows(t){this._state={...this._state,flows:t},this.notify("flows");}setRequests(t){this._state={...this._state,requests:t},this.notify("requests");}setFetches(t){this._state={...this._state,fetches:t},this.notify("fetches");}setErrors(t){this._state={...this._state,errors:t},this.notify("errors");}setLogs(t){this._state={...this._state,logs:t},this.notify("logs");}setQueries(t){this._state={...this._state,queries:t},this.notify("queries");}setIssues(t){this._state={...this._state,issues:t},this.notify("issues");}setMetrics(t){this._state={...this._state,metrics:t},this.notify("metrics");}prependRequest(t){let s=[t,...this._state.requests.slice(0,999)];this._state={...this._state,requests:s},this.notify("requests");}prependFetch(t){this._state={...this._state,fetches:[t,...this._state.fetches]},this.notify("fetches");}prependError(t){this._state={...this._state,errors:[t,...this._state.errors]},this.notify("errors");}prependLog(t){this._state={...this._state,logs:[t,...this._state.logs]},this.notify("logs");}prependQuery(t){this._state={...this._state,queries:[t,...this._state.queries]},this.notify("queries");}setActiveView(t){this._state={...this._state,activeView:t},this.notify("activeView");}setViewMode(t){this._state={...this._state,viewMode:t},this.notify("viewMode");}clearAll(){this._state={...this._state,flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[]},this.notify("all");}notify(t){this.dispatchEvent(new CustomEvent("state-changed",{detail:t}));}};var at=class extends f{constructor(){super(...arguments);this.expandedIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleError(t){this.expandedIdx=this.expandedIdx===t?-1:t;}renderErrorRow(t,s){let r=new Date(t.timestamp).toLocaleTimeString(),o=this.expandedIdx===s;return a`
|
|
689
765
|
<div
|
|
690
|
-
class="req-row tel-clickable ${
|
|
766
|
+
class="req-row tel-clickable ${o?"expanded":""}"
|
|
691
767
|
@click=${()=>this.toggleError(s)}
|
|
692
768
|
>
|
|
693
769
|
<span class="tel-error-name" title=${t.name}>${t.name}</span>
|
|
694
770
|
<span class="tel-message" title=${t.message}>${t.message}</span>
|
|
695
771
|
<span class="tel-timestamp">${r}</span>
|
|
696
772
|
</div>
|
|
697
|
-
${
|
|
698
|
-
`}render(){let t=this.store.state.errors;return t.length===0?
|
|
773
|
+
${o&&t.stack?a`<div class="error-stack">${t.stack}</div>`:d}
|
|
774
|
+
`}render(){let t=this.store.state.errors;return t.length===0?a`<bk-empty-state
|
|
699
775
|
title="No errors"
|
|
700
776
|
subtitle="No errors have been captured yet"
|
|
701
|
-
></bk-empty-state>`:
|
|
777
|
+
></bk-empty-state>`:a`
|
|
702
778
|
<div class="col-header">
|
|
703
779
|
<span style="width:180px">Type</span>
|
|
704
780
|
<span style="flex:1">Message</span>
|
|
@@ -707,27 +783,27 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
707
783
|
<div id="error-list">
|
|
708
784
|
${t.map((s,r)=>this.renderErrorRow(s,r))}
|
|
709
785
|
</div>
|
|
710
|
-
`}};u([
|
|
786
|
+
`}};u([R({context:x})],at.prototype,"store",2),u([_()],at.prototype,"expandedIdx",2),at=u([g("bk-errors-view")],at);var wt=class extends f{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}renderAnalysis(e){if(e.length===0)return d;let t={error:0,warn:0,info:0,debug:0,log:0};for(let s of e)t[s.level]!==void 0&&t[s.level]++;return a`
|
|
711
787
|
<div id="log-analysis">
|
|
712
788
|
<div class="fetch-summary">
|
|
713
789
|
<bk-stat-card value=${String(e.length)} label="Total Logs"></bk-stat-card>
|
|
714
|
-
${t.error>0?
|
|
715
|
-
${t.warn>0?
|
|
790
|
+
${t.error>0?a`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:d}
|
|
791
|
+
${t.warn>0?a`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:d}
|
|
716
792
|
<bk-stat-card value=${String(t.info)} label="Info"></bk-stat-card>
|
|
717
|
-
${t.debug>0?
|
|
718
|
-
${t.log>0?
|
|
793
|
+
${t.debug>0?a`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:d}
|
|
794
|
+
${t.log>0?a`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:d}
|
|
719
795
|
</div>
|
|
720
796
|
</div>
|
|
721
|
-
`}renderLogRow(e){let t=new Date(e.timestamp).toLocaleTimeString();return
|
|
797
|
+
`}renderLogRow(e){let t=new Date(e.timestamp).toLocaleTimeString();return a`
|
|
722
798
|
<div class="req-row">
|
|
723
799
|
<span class="tel-level tel-level-${e.level}">${e.level.toUpperCase()}</span>
|
|
724
800
|
<span class="tel-message tel-mono" title=${e.message}>${e.message}</span>
|
|
725
801
|
<span class="tel-timestamp">${t}</span>
|
|
726
802
|
</div>
|
|
727
|
-
`}render(){let e=this.store.state.logs;return e.length===0?
|
|
803
|
+
`}render(){let e=this.store.state.logs;return e.length===0?a`<bk-empty-state
|
|
728
804
|
title="No logs"
|
|
729
805
|
subtitle="No console output has been captured yet"
|
|
730
|
-
></bk-empty-state>`:
|
|
806
|
+
></bk-empty-state>`:a`
|
|
731
807
|
${this.renderAnalysis(e)}
|
|
732
808
|
<div class="col-header">
|
|
733
809
|
<span style="width:52px">Level</span>
|
|
@@ -737,28 +813,28 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
737
813
|
<div id="log-list">
|
|
738
814
|
${e.map(t=>this.renderLogRow(t))}
|
|
739
815
|
</div>
|
|
740
|
-
`}};u([
|
|
816
|
+
`}};u([R({context:x})],wt.prototype,"store",2),wt=u([g("bk-logs-view")],wt);var Xs=new Set(["SELECT","FROM","WHERE","AND","OR","INSERT","INTO","VALUES","UPDATE","SET","DELETE","JOIN","LEFT","RIGHT","INNER","OUTER","ON","GROUP","BY","ORDER","HAVING","LIMIT","OFFSET","AS","IN","NOT","NULL","IS","LIKE","BETWEEN","EXISTS","CASE","WHEN","THEN","ELSE","END","COUNT","SUM","AVG","MIN","MAX","DISTINCT","UNION","ALL","CREATE","TABLE","ALTER","DROP","INDEX","RETURNING","WITH","RECURSIVE","OVER","PARTITION","WINDOW","FETCH","NEXT","ROWS","ONLY","CAST","COALESCE","NULLIF","EXTRACT","INTERVAL","TRUE","FALSE","ASC","DESC","USING","NATURAL","CROSS","FULL","ROLLBACK","COMMIT","BEGIN","TRANSACTION","SAVEPOINT","RELEASE"]);function ns(i){let e=i.trim().match(/^(\w+)/);return e?e[1].toUpperCase():"?"}function as(i){let e=i.replace(/\s+/g," ").trim(),t=e.match(/\bFROM\s+["'`]?(\w+)["'`]?/i);if(t)return t[1];let s=e.match(/\bINTO\s+["'`]?(\w+)["'`]?/i);if(s)return s[1];let r=e.match(/\bUPDATE\s+["'`]?(\w+)["'`]?/i);return r?r[1]:""}function ls(i){return P(i).replace(/\b\w+\b/g,e=>Xs.has(e.toUpperCase())?'<span class="sql-kw">'+e+"</span>":e)}var lt=class extends f{constructor(){super(...arguments);this.expandedIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleQuery(t){this.expandedIdx=this.expandedIdx===t?-1:t;}queryDuration(t){return t===0?"<1ms":v(t)}getQueryInfo(t){let s=(t.normalizedOp||t.operation||(t.sql?ns(t.sql):"?")).toUpperCase(),r=t.table||t.model||(t.sql?as(t.sql):""),o=t.sql||s+" "+r;return {op:s,table:r,sqlText:o}}renderQueryRow(t,s){let{op:r,table:o,sqlText:n}=this.getQueryInfo(t),l=Ft[r]||"var(--text-dim)",c=t.durationMs>de,p=t.sql||r+" "+o,h=this.expandedIdx===s;return a`
|
|
741
817
|
<div>
|
|
742
818
|
<div
|
|
743
|
-
class="req-row query-row tel-clickable ${
|
|
819
|
+
class="req-row query-row tel-clickable ${h?"expanded":""}"
|
|
744
820
|
@click=${()=>this.toggleQuery(s)}
|
|
745
821
|
>
|
|
746
|
-
<span class="query-op" title=${r} style="color:${
|
|
747
|
-
<span class="query-table" title=${
|
|
748
|
-
<span class="query-preview" title=${
|
|
749
|
-
<span class="query-dur${
|
|
822
|
+
<span class="query-op" title=${r} style="color:${l}">${r}</span>
|
|
823
|
+
<span class="query-table" title=${o}>${o}</span>
|
|
824
|
+
<span class="query-preview" title=${p}>${p}</span>
|
|
825
|
+
<span class="query-dur${c?" query-slow":""}">${this.queryDuration(t.durationMs)}</span>
|
|
750
826
|
</div>
|
|
751
|
-
<div class="query-detail ${
|
|
752
|
-
${
|
|
753
|
-
<pre class="query-detail-sql" .innerHTML=${
|
|
754
|
-
<bk-copy-button .text=${
|
|
827
|
+
<div class="query-detail ${h?"open":""}">
|
|
828
|
+
${h?a`
|
|
829
|
+
<pre class="query-detail-sql" .innerHTML=${ls(n)}></pre>
|
|
830
|
+
<bk-copy-button .text=${n} label="Copy"></bk-copy-button>
|
|
755
831
|
`:d}
|
|
756
832
|
</div>
|
|
757
833
|
</div>
|
|
758
|
-
`}render(){let t=this.store.state.queries;return t.length===0?
|
|
834
|
+
`}render(){let t=this.store.state.queries;return t.length===0?a`<bk-empty-state
|
|
759
835
|
title="No queries"
|
|
760
836
|
subtitle="No database queries have been captured yet"
|
|
761
|
-
></bk-empty-state>`:
|
|
837
|
+
></bk-empty-state>`:a`
|
|
762
838
|
<div class="col-header">
|
|
763
839
|
<span style="width:70px;border-right:1px solid var(--border);padding-right:16px">Operation</span>
|
|
764
840
|
<span style="width:170px;border-right:1px solid var(--border);padding-right:16px">Table</span>
|
|
@@ -768,35 +844,35 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
768
844
|
<div id="query-list">
|
|
769
845
|
${t.map((s,r)=>this.renderQueryRow(s,r))}
|
|
770
846
|
</div>
|
|
771
|
-
`}};u([
|
|
847
|
+
`}};u([R({context:x})],lt.prototype,"store",2),u([_()],lt.prototype,"expandedIdx",2),lt=u([g("bk-queries-view")],lt);function Ee(i){return i.replace(/'/g,"'\\''")}function Ks(i,e){let t=Object.entries(i.headers||{}).filter(([o])=>!os.has(o)).map(([o,n])=>`-H '${Ee(o)}: ${Ee(n)}'`).join(" "),s=i.requestBody?` -d '${Ee(i.requestBody)}'`:"",r=e?`http://localhost:${e}`:"";return `curl -X ${i.method} ${t}${s} '${r}${i.url}'`}function ct(i){let e=window.__BRAKIT_CONFIG__?.port??"",t=Ks(i,e);navigator.clipboard.writeText(t).then(()=>C.show("Copied cURL command"));}var dt=class extends f{constructor(){super(...arguments);this.expandedId=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleRequest(t){this.expandedId=this.expandedId===t?null:t;}handleCopyAsCurl(t,s){s.stopPropagation(),ct(t);}renderDetail(t){return a`
|
|
772
848
|
<div class="detail-meta">
|
|
773
849
|
<span><bk-method-badge .method=${t.method}></bk-method-badge> ${t.url}</span>
|
|
774
850
|
<span><bk-status-pill .code=${t.statusCode}></bk-status-pill></span>
|
|
775
851
|
<span>${t.durationMs}ms</span>
|
|
776
|
-
${t.responseSize?
|
|
852
|
+
${t.responseSize?a`<span>${U(t.responseSize)}</span>`:d}
|
|
777
853
|
</div>
|
|
778
854
|
<div class="request-timeline tl-hidden" data-request-id=${t.id} data-request-started=${String(t.startedAt)}></div>
|
|
779
855
|
<div class="detail-grid">
|
|
780
|
-
<div class="detail-section"><h4>Request Headers</h4><pre .innerHTML=${
|
|
781
|
-
<div class="detail-section"><h4>Response Headers</h4><pre .innerHTML=${
|
|
782
|
-
<div class="detail-section"><h4>Request Body</h4><pre .innerHTML=${
|
|
783
|
-
<div class="detail-section"><h4>Response Body</h4><pre .innerHTML=${
|
|
856
|
+
<div class="detail-section"><h4>Request Headers</h4><pre .innerHTML=${rt(t.headers)}></pre></div>
|
|
857
|
+
<div class="detail-section"><h4>Response Headers</h4><pre .innerHTML=${rt(t.responseHeaders)}></pre></div>
|
|
858
|
+
<div class="detail-section"><h4>Request Body</h4><pre .innerHTML=${Q(t.requestBody)}></pre></div>
|
|
859
|
+
<div class="detail-section"><h4>Response Body</h4><pre .innerHTML=${Q(t.responseBody)}></pre></div>
|
|
784
860
|
</div>
|
|
785
861
|
<div class="detail-actions">
|
|
786
862
|
<button class="btn btn-curl" @click=${s=>this.handleCopyAsCurl(t,s)}>Copy cURL</button>
|
|
787
863
|
</div>
|
|
788
|
-
`}renderRequestRow(t){let s=this.expandedId===t.id;return
|
|
864
|
+
`}renderRequestRow(t){let s=this.expandedId===t.id;return a`
|
|
789
865
|
<div class="req-row ${s?"expanded":""}" @click=${()=>this.toggleRequest(t.id)}>
|
|
790
866
|
<div class="req-summary">
|
|
791
867
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
792
868
|
<span class="req-url">${t.url}</span>
|
|
793
869
|
<bk-status-pill .code=${t.statusCode}></bk-status-pill>
|
|
794
870
|
<bk-duration-label .ms=${t.durationMs}></bk-duration-label>
|
|
795
|
-
<span class="req-size">${
|
|
871
|
+
<span class="req-size">${U(t.responseSize)}</span>
|
|
796
872
|
</div>
|
|
797
873
|
</div>
|
|
798
874
|
<div class="req-detail ${s?"open":""}">${s?this.renderDetail(t):d}</div>
|
|
799
|
-
`}render(){let t=this.store.state.requests.filter(s=>!s.path?.startsWith(
|
|
875
|
+
`}render(){let t=this.store.state.requests.filter(s=>!s.path?.startsWith(k));return t.length===0?a`<bk-empty-state title="No requests" subtitle="No HTTP requests have been captured yet"></bk-empty-state>`:a`
|
|
800
876
|
<div class="col-header">
|
|
801
877
|
<span style="width:60px">Method</span>
|
|
802
878
|
<span style="flex:1">URL</span>
|
|
@@ -805,50 +881,53 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
805
881
|
<span style="width:60px;text-align:right">Size</span>
|
|
806
882
|
</div>
|
|
807
883
|
<div id="request-list">${t.map(s=>this.renderRequestRow(s))}</div>
|
|
808
|
-
`}};u([
|
|
884
|
+
`}};u([R({context:x})],dt.prototype,"store",2),u([_()],dt.prototype,"expandedId",2),dt=u([g("bk-requests-view")],dt);var At=class extends f{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}buildGroups(e,t){let s=new Map;for(let o of t)s.set(o.id,o);let r={};for(let o of e){let n=o.method+" "+o.url;r[n]||(r[n]={method:o.method,url:o.url,count:0,totalDur:0,maxDur:0,errors:0,callers:{},statusCodes:{},firstTs:o.timestamp,lastTs:o.timestamp});let l=r[n];if(l.count++,l.totalDur+=o.durationMs,o.durationMs>l.maxDur&&(l.maxDur=o.durationMs),o.statusCode>=400&&l.errors++,l.statusCodes[o.statusCode]=(l.statusCodes[o.statusCode]||0)+1,o.timestamp<l.firstTs&&(l.firstTs=o.timestamp),o.timestamp>l.lastTs&&(l.lastTs=o.timestamp),o.parentRequestId){let c=s.get(o.parentRequestId);c&&(l.callers[c.method+" "+(c.path||c.url)]=1);}}return Object.values(r).sort((o,n)=>n.count-o.count)}renderSummary(e){let t=new Set,s=0,r=0;for(let n of e)t.add(n.url),n.statusCode>=400&&s++,r+=n.durationMs;let o=Math.round(r/e.length);return a`
|
|
809
885
|
<div class="fetch-summary">
|
|
810
886
|
<bk-stat-card value=${String(e.length)} label="Total Fetches"></bk-stat-card>
|
|
811
887
|
<bk-stat-card value=${String(t.size)} label="Unique URLs"></bk-stat-card>
|
|
812
888
|
<bk-stat-card value=${String(s)} label="Errors" color=${s>0?"var(--red)":""}></bk-stat-card>
|
|
813
|
-
<bk-stat-card value=${
|
|
889
|
+
<bk-stat-card value=${v(o)} label="Avg Duration"></bk-stat-card>
|
|
814
890
|
</div>
|
|
815
|
-
`}formatTime(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}renderGroup(e){let t=Math.round(e.totalDur/e.count),s=e.count>0?Math.round(e.errors/e.count*100):0,r=Object.keys(e.callers),
|
|
891
|
+
`}formatTime(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}renderGroup(e){let t=Math.round(e.totalDur/e.count),s=e.count>0?Math.round(e.errors/e.count*100):0,r=Object.keys(e.callers),o=Object.entries(e.statusCodes),n=o.length>0?Number(o.sort((l,c)=>c[1]-l[1])[0][0]):0;return a`
|
|
816
892
|
<div class="fetch-group">
|
|
817
893
|
<div class="fetch-group-header">
|
|
818
894
|
<bk-method-badge .method=${e.method}></bk-method-badge>
|
|
819
895
|
<span class="fetch-group-url" title=${e.url}>${e.url}</span>
|
|
820
|
-
${
|
|
896
|
+
${n>0?a`<bk-status-pill .code=${n}></bk-status-pill>`:d}
|
|
821
897
|
<span class="fetch-group-count">${e.count}x</span>
|
|
822
898
|
</div>
|
|
823
899
|
<div class="fetch-group-meta">
|
|
824
|
-
<span>avg ${
|
|
900
|
+
<span>avg ${v(t)}</span>
|
|
825
901
|
<span class="fetch-group-sep">\u00b7</span>
|
|
826
|
-
<span>max ${
|
|
902
|
+
<span>max ${v(e.maxDur)}</span>
|
|
827
903
|
<span class="fetch-group-sep">\u00b7</span>
|
|
828
|
-
${s>0?
|
|
904
|
+
${s>0?a`<span class="fetch-group-err">${s}% errors</span>`:a`<span class="fetch-group-ok">0% errors</span>`}
|
|
829
905
|
</div>
|
|
830
|
-
${e.firstTs>0?
|
|
906
|
+
${e.firstTs>0?a`
|
|
831
907
|
<div class="fetch-group-timeline">
|
|
832
908
|
<span class="fetch-group-timeline-dot"></span>
|
|
833
909
|
<span class="fetch-group-timeline-range">
|
|
834
|
-
${this.formatTime(e.firstTs)}${e.firstTs!==e.lastTs?
|
|
910
|
+
${this.formatTime(e.firstTs)}${e.firstTs!==e.lastTs?a` \u2192 ${this.formatTime(e.lastTs)}`:d}
|
|
835
911
|
</span>
|
|
836
912
|
</div>`:d}
|
|
837
|
-
${r.length>0?
|
|
913
|
+
${r.length>0?a`
|
|
838
914
|
<div class="fetch-group-callers">
|
|
839
915
|
<span class="fetch-group-callers-label">Called by</span>
|
|
840
|
-
${r.map(
|
|
916
|
+
${r.map(l=>a`<span class="fetch-group-caller-pill">${l}</span>`)}
|
|
841
917
|
</div>`:d}
|
|
842
918
|
</div>
|
|
843
|
-
`}render(){let e=this.store.state.fetches,t=this.store.state.requests;if(e.length===0)return
|
|
919
|
+
`}render(){let e=this.store.state.fetches,t=this.store.state.requests;if(e.length===0)return a`<bk-empty-state title="No fetches" subtitle="No outbound HTTP calls have been captured yet"></bk-empty-state>`;let s=this.buildGroups(e,t);return a`
|
|
844
920
|
<div class="fetch-analysis" id="fetch-analysis">
|
|
845
921
|
${this.renderSummary(e)}
|
|
846
|
-
${s.length>0?
|
|
922
|
+
${s.length>0?a`
|
|
847
923
|
<div class="fetch-groups-title">Grouped by URL (${s.length})</div>
|
|
848
924
|
<div class="fetch-groups">${s.map(r=>this.renderGroup(r))}</div>
|
|
849
925
|
`:d}
|
|
850
926
|
</div>
|
|
851
|
-
`}};u([
|
|
927
|
+
`}};u([R({context:x})],At.prototype,"store",2),At=u([g("bk-fetches-view")],At);function cs(i,e){if(e>=400)return "var(--red)";switch(i){case "GET":return "var(--green)";case "POST":return "var(--blue)";case "PUT":case "PATCH":return "var(--amber)";case "DELETE":return "var(--red)";default:return "var(--text-muted)"}}function _e(i){return i==="query"?"var(--accent)":"var(--cyan)"}function tr(i){return i.type==="query"||i.type==="fetch"}function er(i){let e=(i.normalizedOp||i.operation||"QUERY").toUpperCase(),t=i.table||i.model||"";return {label:`${e} ${t}`,tooltip:i.sql||`${e} ${t}`}}function sr(i){return {label:`${i.method} ${i.url}`,tooltip:`${i.method} ${i.url}`}}function rr(i,e,t,s,r,o){let n=i.data.durationMs||0,l,c;if(o){let p=Math.max(i.timestamp-s,0);l=Math.min(p/r*100,95),c=Math.max(n/r*100,1.5);}else {let p=t[0].timestamp,m=t[t.length-1].timestamp-p;l=m>0?(i.timestamp-p)/m*85:e/Math.max(t.length-1,1)*85,c=Math.max(n/r*100,1.5);}return l+c>100&&(c=Math.max(100-l,1.5)),{leftPct:l,widthPct:c}}function or(i,e){let t=i.timeline.filter(tr);if(t.length===0)return [];let s=e.startedAt,r=e.durationMs||1,o=Math.abs(t[0].timestamp-s)<r*10;return t.map((n,l)=>{let c=n.data.durationMs||0,{leftPct:p,widthPct:h}=rr(n,l,t,s,r,o),m=n.type==="query"?er(n.data):sr(n.data);return {type:n.type,label:m.label,durMs:c,durLabel:v(c),tooltip:m.tooltip,leftPct:p,widthPct:h}})}function ps(i,e){let t=i.requests.filter(l=>!l.isStrictModeDupe);if(t.length===0)return {rows:[],totalMs:0};let s=Math.min(...t.map(l=>l.startedAt)),o=Math.max(...t.map(l=>l.startedAt+l.durationMs))-s;return o===0?{rows:[],totalMs:0}:{rows:t.map(l=>{let c=(l.startedAt-s)/o*100,p=Math.max(l.durationMs/o*100,.5),h=e?.activities?.[l.id],m=h?or(h,l):[];return {label:`${l.method} ${l.label}`,leftPct:c,widthPct:p,color:cs(l.method,l.statusCode),durMs:l.durationMs,durLabel:v(l.durationMs),tooltip:`${l.method} ${l.label} (${v(l.durationMs)})`,subEvents:m}}),totalMs:o}}function us(i){let e=i.requests,t=[],s=[],r=[],o=[],n=new Map;for(let p of e){let h=p.label,m=p.pollingDurationMs||p.durationMs;if(!yt[p.category||""]){if(p.isDuplicate){let b=n.get(h);b?(b.count++,b.wastedMs+=m):n.set(h,{name:h,count:2,wastedMs:m});continue}if(p.statusCode>=400){s.push(h+" ("+is(p.statusCode)+")");continue}p.responseSize>51200&&r.push("Large response: "+h+" returned "+U(p.responseSize)),t.push(h);}}for(let p of n.values())o.push(p);let l="";if(o.length>0){let p=o.map(m=>m.name).join(", "),h=o.reduce((m,b)=>m+b.wastedMs,0);l="Your app fetches "+p+" multiple times on this page. This wastes ~"+v(h)+". Try caching these calls, deduplicating with React Query/SWR, or moving them to a shared layout.";}else s.length>0&&(l="Some requests are failing. Check your API routes and make sure the endpoints exist.");let c=e.filter(p=>p.durationMs>2e3&&p.category!==J);return c.length>0&&!l&&(l=c.map(p=>p.label).join(", ")+` is taking over ${v(2e3)}. Consider adding caching or optimizing the backend query.`),{successes:t,errors:s,warnings:r,duplicates:o,tip:l}}var M=class extends f{constructor(){super(...arguments);this.expandedFlowIdx=-1;this.expandedSubReqIdx=-1;this.flowDetailTab="insights";this.flowTimeline=null;this.flowTimelineLoading=false;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}get flows(){return this.store.state.flows}get viewMode(){return this.store.state.viewMode}flowDotClass(t){return t.hasErrors?"dot-error":t.redundancyPct>0?"dot-warn":"dot-clean"}flowBadgeInfo(t){if(t.hasErrors){let s=t.requests.filter(r=>r.statusCode>=400).length;return {text:s+" error"+(s!==1?"s":""),cls:"badge-error"}}return t.redundancyPct>0?{text:t.redundancyPct+"% redundant",cls:"badge-warn"}:{text:"clean",cls:"badge-clean"}}toggleFlow(t){this.expandedFlowIdx===t?this.expandedFlowIdx=-1:(this.expandedFlowIdx=t,this.expandedSubReqIdx=-1,this.flowDetailTab="insights",this.flowTimeline=null);}toggleSubReq(t,s){s.stopPropagation(),this.expandedSubReqIdx=this.expandedSubReqIdx===t?-1:t;}toggleBodyBlock(t){t.stopPropagation();let s=t.currentTarget,r=s.parentElement;if(!r)return;s.classList.toggle("open");let o=r.querySelector("pre");o&&o.classList.toggle("open");}switchTab(t,s,r){r.stopPropagation(),this.flowDetailTab=t,t==="timeline"&&!this.flowTimeline&&this.loadFlowTimeline(s);}async loadFlowTimeline(t){if(this.flowTimelineLoading)return;let s=t.requests.map(r=>r.id).filter(Boolean);if(s.length!==0){this.flowTimelineLoading=true;try{let r=await fetch(`${y.activity}?requestIds=${s.join(",")}`);if(!r.ok){this.flowTimelineLoading=!1;return}this.flowTimeline=await r.json();}catch{}this.flowTimelineLoading=false;}}loadTimelineForContainer(t){let s=t.querySelectorAll(".request-timeline");for(let r of s){let o=r.getAttribute("data-request-id");if(o&&!r.hasAttribute("data-loaded")){r.setAttribute("data-loaded","1");let n=document.createElement("bk-timeline-panel");n.setAttribute("request-id",o),n.setAttribute("request-started",r.getAttribute("data-request-started")||"0"),r.appendChild(n),r.classList.remove("tl-hidden");}}}updated(){if(this.expandedFlowIdx>=0&&this.flowDetailTab==="insights"){let t=this.querySelector(".flow-expand.open");t&&this.loadTimelineForContainer(t);}}render(){let t=this.flows;return t.length===0?a`<bk-empty-state
|
|
928
|
+
title="No actions yet"
|
|
929
|
+
subtitle="Start using your app to see user action flows here"
|
|
930
|
+
></bk-empty-state>`:a`
|
|
852
931
|
<div id="flow-col-header" class="col-header">
|
|
853
932
|
<span style="width:8px"></span>
|
|
854
933
|
<span style="flex:1">Action</span>
|
|
@@ -856,83 +935,203 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
856
935
|
<span style="width:120px;text-align:right">Status</span>
|
|
857
936
|
<span style="width:70px;text-align:right">Time</span>
|
|
858
937
|
</div>
|
|
859
|
-
<div id="flow-list"
|
|
860
|
-
|
|
861
|
-
|
|
938
|
+
<div id="flow-list">
|
|
939
|
+
${t.map((s,r)=>this.renderFlowRow(s,r))}
|
|
940
|
+
</div>
|
|
941
|
+
`}renderFlowRow(t,s){let r=this.expandedFlowIdx===s,o=this.flowDotClass(t),n=this.flowBadgeInfo(t);return a`
|
|
942
|
+
<div
|
|
943
|
+
class="flow-row ${r?"expanded":""}"
|
|
944
|
+
@click=${()=>this.toggleFlow(s)}
|
|
945
|
+
>
|
|
862
946
|
<div class="flow-summary-row">
|
|
863
|
-
<span class="flow-status-dot ${
|
|
947
|
+
<span class="flow-status-dot ${o}"></span>
|
|
864
948
|
<span class="flow-label">${t.label}</span>
|
|
865
|
-
<span class="flow-req-count"
|
|
866
|
-
|
|
867
|
-
|
|
949
|
+
<span class="flow-req-count"
|
|
950
|
+
>${t.requests.length}
|
|
951
|
+
req${t.requests.length!==1?"s":""}</span
|
|
952
|
+
>
|
|
953
|
+
<span class="flow-badge-pill ${n.cls}">${n.text}</span>
|
|
954
|
+
<span class="flow-duration"
|
|
955
|
+
>${v(t.totalDurationMs)}</span
|
|
956
|
+
>
|
|
868
957
|
</div>
|
|
869
958
|
</div>
|
|
870
959
|
<div class="flow-expand ${r?"open":""}">
|
|
871
|
-
${r?this.
|
|
960
|
+
${r?this.renderFlowDetail(t):d}
|
|
872
961
|
</div>
|
|
873
|
-
`}
|
|
874
|
-
<div>
|
|
875
|
-
<
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
962
|
+
`}renderFlowDetail(t){let s=this.viewMode==="simple"?"Insights":"Details";return a`
|
|
963
|
+
<div class="flow-detail-tabs">
|
|
964
|
+
<button
|
|
965
|
+
class="flow-tab ${this.flowDetailTab==="insights"?"active":""}"
|
|
966
|
+
@click=${r=>this.switchTab("insights",t,r)}
|
|
967
|
+
>
|
|
968
|
+
${s}
|
|
969
|
+
</button>
|
|
970
|
+
<button
|
|
971
|
+
class="flow-tab ${this.flowDetailTab==="timeline"?"active":""}"
|
|
972
|
+
@click=${r=>this.switchTab("timeline",t,r)}
|
|
973
|
+
>
|
|
974
|
+
Timeline
|
|
975
|
+
</button>
|
|
976
|
+
</div>
|
|
977
|
+
${this.flowDetailTab==="insights"?this.viewMode==="simple"?this.renderFlowInsights(t):this.renderFlowSubReqs(t):this.renderFlowWaterfall(t)}
|
|
978
|
+
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return a`<div class="wf-loading">Loading timeline...</div>`;let{rows:s,totalMs:r}=ps(t,this.flowTimeline);if(s.length===0)return d;let o=[];for(let n=0;n<=5;n++)o.push(v(r/5*n));return a`
|
|
979
|
+
<div class="flow-waterfall">
|
|
980
|
+
<div class="wf-time-axis">
|
|
981
|
+
${o.map(n=>a`<span>${n}</span>`)}
|
|
982
|
+
</div>
|
|
983
|
+
<div class="wf-rows">
|
|
984
|
+
${s.map(n=>this.renderWaterfallGroup(n))}
|
|
985
|
+
</div>
|
|
986
|
+
</div>
|
|
987
|
+
`}renderWaterfallGroup(t){return a`
|
|
988
|
+
<div class="wf-request-group">
|
|
989
|
+
<div class="wf-req-row" title="${t.tooltip}">
|
|
990
|
+
<div class="wf-req-label">${t.label}</div>
|
|
991
|
+
<div class="wf-bar-track">
|
|
992
|
+
<div
|
|
993
|
+
class="wf-bar"
|
|
994
|
+
style="left:${t.leftPct}%;width:${t.widthPct}%;background:${t.color}"
|
|
995
|
+
></div>
|
|
883
996
|
</div>
|
|
884
|
-
|
|
997
|
+
<div class="wf-req-dur">${t.durLabel}</div>
|
|
998
|
+
</div>
|
|
999
|
+
${t.subEvents.length>0?t.subEvents.map(s=>a`
|
|
1000
|
+
<div class="wf-sub-row" title="${s.tooltip}">
|
|
1001
|
+
<div class="wf-sub-label">
|
|
1002
|
+
<span
|
|
1003
|
+
class="wf-sub-dot"
|
|
1004
|
+
style="background:${_e(s.type)}"
|
|
1005
|
+
></span>
|
|
1006
|
+
${s.label}
|
|
1007
|
+
</div>
|
|
1008
|
+
<div class="wf-bar-track">
|
|
1009
|
+
<div
|
|
1010
|
+
class="wf-bar wf-sub-bar-sized"
|
|
1011
|
+
style="left:${t.leftPct+s.leftPct/100*t.widthPct}%;width:${s.widthPct/100*t.widthPct}%;background:${_e(s.type)}"
|
|
1012
|
+
></div>
|
|
1013
|
+
</div>
|
|
1014
|
+
<div class="wf-sub-dur">${s.durLabel}</div>
|
|
1015
|
+
</div>
|
|
1016
|
+
`):d}
|
|
885
1017
|
</div>
|
|
886
|
-
`}
|
|
887
|
-
<div
|
|
888
|
-
<div class="traffic
|
|
1018
|
+
`}renderFlowInsights(t){let s=us(t),r=s.errors.length>0||s.duplicates.length>0||s.warnings.length>0||!!s.tip;return a`
|
|
1019
|
+
<div>
|
|
1020
|
+
<div class="flow-traffic">
|
|
1021
|
+
${t.requests.map(o=>this.renderTrafficCard(o))}
|
|
1022
|
+
</div>
|
|
1023
|
+
${r?a`
|
|
1024
|
+
<div class="flow-divider"></div>
|
|
1025
|
+
<div class="flow-insights">
|
|
1026
|
+
${s.errors.map(o=>a`<div class="insight-line insight-error">
|
|
1027
|
+
✗ ${o}
|
|
1028
|
+
</div>`)}
|
|
1029
|
+
${s.duplicates.map(o=>a`<div class="insight-line insight-warn">
|
|
1030
|
+
⚠ ${o.name} — loaded ${o.count}x (wasting
|
|
1031
|
+
~${v(o.wastedMs)})
|
|
1032
|
+
</div>`)}
|
|
1033
|
+
${s.warnings.map(o=>a`<div class="insight-line insight-warn">⚠ ${o}</div>`)}
|
|
1034
|
+
${s.tip?a`<div class="insight-line insight-tip">
|
|
1035
|
+
Tip: ${s.tip}
|
|
1036
|
+
</div>`:d}
|
|
1037
|
+
</div>
|
|
1038
|
+
`:d}
|
|
1039
|
+
</div>
|
|
1040
|
+
`}renderTrafficCard(t){if(yt[t.category||""])return d;let s=st(t.statusCode),r=v(t.pollingDurationMs||t.durationMs),o=!t.isDuplicate&&t.category!==Ht&&t.category!==J||t.requestBody&&t.method!=="GET"||!!t.responseBody;return a`
|
|
1041
|
+
<div
|
|
1042
|
+
class="traffic-card ${t.isStrictModeDupe?"strict-mode-dupe":""}"
|
|
1043
|
+
>
|
|
1044
|
+
<div class="traffic-card-header ${o?"has-details":""}">
|
|
889
1045
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
890
|
-
<span class="traffic-card-path ${t.isDuplicate?"is-dup":""}"
|
|
1046
|
+
<span class="traffic-card-path ${t.isDuplicate?"is-dup":""}"
|
|
1047
|
+
>${t.label}</span
|
|
1048
|
+
>
|
|
891
1049
|
<span class="status-pill ${s}">${t.statusCode}</span>
|
|
892
1050
|
<span class="traffic-card-dur">${r}</span>
|
|
893
|
-
${t.isDuplicate?
|
|
1051
|
+
${t.isDuplicate?a`<span class="traffic-card-dup">duplicate</span>`:a`<span class="traffic-card-size"
|
|
1052
|
+
>${U(t.responseSize)}</span
|
|
1053
|
+
>`}
|
|
894
1054
|
</div>
|
|
895
|
-
${t.isStrictModeDupe?
|
|
896
|
-
|
|
1055
|
+
${t.isStrictModeDupe?a`<div class="strict-mode-banner">
|
|
1056
|
+
React Strict Mode duplicate — does not happen in production
|
|
1057
|
+
</div>`:d}
|
|
1058
|
+
${!t.isDuplicate&&t.category!==Ht&&t.category!==J?a`<div
|
|
1059
|
+
class="request-timeline tl-hidden"
|
|
1060
|
+
data-request-id=${t.id}
|
|
1061
|
+
data-request-started=${String(t.startedAt)}
|
|
1062
|
+
></div>`:d}
|
|
897
1063
|
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):d}
|
|
898
1064
|
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):d}
|
|
899
1065
|
</div>
|
|
900
|
-
`}renderBodyToggle(t,s,r){let
|
|
1066
|
+
`}renderBodyToggle(t,s,r){let o=t==="out"?"\u2192":"\u2190";return a`
|
|
901
1067
|
<div class="traffic-body">
|
|
902
1068
|
<button class="traffic-body-toggle" @click=${this.toggleBodyBlock}>
|
|
903
|
-
<span class="chevron"
|
|
1069
|
+
<span class="chevron">▸</span
|
|
1070
|
+
><span class="arrow-${t}">${o}</span> ${s}
|
|
904
1071
|
</button>
|
|
905
|
-
<pre .innerHTML=${
|
|
1072
|
+
<pre .innerHTML=${Q(r)}></pre>
|
|
906
1073
|
</div>
|
|
907
|
-
`}renderFlowSubReqs(t){return
|
|
908
|
-
|
|
1074
|
+
`}renderFlowSubReqs(t){return a`<div class="flow-subreqs">
|
|
1075
|
+
${t.requests.map((s,r)=>this.renderSubReqRow(s,r))}
|
|
1076
|
+
</div>`}renderSubReqRow(t,s){let r=this.expandedSubReqIdx===s,o=st(t.statusCode),n=t.pollingDurationMs?v(t.pollingDurationMs):v(t.durationMs);return a`
|
|
1077
|
+
<div
|
|
1078
|
+
class="flow-subreq ${r?"expanded":""}"
|
|
1079
|
+
@click=${l=>this.toggleSubReq(s,l)}
|
|
1080
|
+
>
|
|
909
1081
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
910
|
-
<span class="subreq-label ${t.isDuplicate?"is-dup":""}"
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1082
|
+
<span class="subreq-label ${t.isDuplicate?"is-dup":""}"
|
|
1083
|
+
>${t.path||t.url}</span
|
|
1084
|
+
>
|
|
1085
|
+
${t.isDuplicate?a`<span class="subreq-dup-tag">duplicate</span>`:d}
|
|
1086
|
+
<span class="status-pill ${o}">${t.statusCode}</span>
|
|
1087
|
+
<span class="subreq-dur">${n}</span>
|
|
914
1088
|
</div>
|
|
915
1089
|
<div class="flow-subreq-detail ${r?"open":""}">
|
|
916
1090
|
${r?this.renderSubReqDetail(t):d}
|
|
917
1091
|
</div>
|
|
918
|
-
`}renderSubReqDetail(t){let s=
|
|
1092
|
+
`}renderSubReqDetail(t){let s=st(t.statusCode);return a`
|
|
919
1093
|
<div class="detail-meta">
|
|
920
|
-
<span
|
|
921
|
-
|
|
1094
|
+
<span
|
|
1095
|
+
><bk-method-badge .method=${t.method}></bk-method-badge> ${P(t.url)}</span
|
|
1096
|
+
>
|
|
1097
|
+
<span
|
|
1098
|
+
><span class="status-pill ${s}">${t.statusCode}</span></span
|
|
1099
|
+
>
|
|
922
1100
|
<span>${t.durationMs}ms</span>
|
|
923
|
-
${t.responseSize?
|
|
1101
|
+
${t.responseSize?a`<span>${U(t.responseSize)}</span>`:d}
|
|
924
1102
|
</div>
|
|
925
|
-
<div
|
|
1103
|
+
<div
|
|
1104
|
+
class="request-timeline tl-hidden"
|
|
1105
|
+
data-request-id=${t.id}
|
|
1106
|
+
data-request-started=${String(t.startedAt)}
|
|
1107
|
+
></div>
|
|
926
1108
|
<div class="detail-grid">
|
|
927
|
-
<div class="detail-section"
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1109
|
+
<div class="detail-section">
|
|
1110
|
+
<h4>Request Headers</h4>
|
|
1111
|
+
<pre .innerHTML=${rt(t.headers)}></pre>
|
|
1112
|
+
</div>
|
|
1113
|
+
<div class="detail-section">
|
|
1114
|
+
<h4>Response Headers</h4>
|
|
1115
|
+
<pre .innerHTML=${rt(t.responseHeaders)}></pre>
|
|
1116
|
+
</div>
|
|
1117
|
+
<div class="detail-section">
|
|
1118
|
+
<h4>Request Body</h4>
|
|
1119
|
+
<pre .innerHTML=${Q(t.requestBody)}></pre>
|
|
1120
|
+
</div>
|
|
1121
|
+
<div class="detail-section">
|
|
1122
|
+
<h4>Response Body</h4>
|
|
1123
|
+
<pre .innerHTML=${Q(t.responseBody)}></pre>
|
|
1124
|
+
</div>
|
|
931
1125
|
</div>
|
|
932
1126
|
<div class="detail-actions">
|
|
933
|
-
<button
|
|
1127
|
+
<button
|
|
1128
|
+
class="btn btn-curl"
|
|
1129
|
+
@click=${r=>{r.stopPropagation(),ct(t);}}
|
|
1130
|
+
>
|
|
1131
|
+
Copy cURL
|
|
1132
|
+
</button>
|
|
934
1133
|
</div>
|
|
935
|
-
`}};u([
|
|
1134
|
+
`}};u([R({context:x})],M.prototype,"store",2),u([_()],M.prototype,"expandedFlowIdx",2),u([_()],M.prototype,"expandedSubReqIdx",2),u([_()],M.prototype,"flowDetailTab",2),u([_()],M.prototype,"flowTimeline",2),u([_()],M.prototype,"flowTimelineLoading",2),M=u([g("bk-flows-view")],M);var Ct=class extends f{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}render(){let e=(this.store.state.issues||[]).slice(),t=e.filter(l=>l.state==="open"||l.state==="fixing"||l.state==="regressed"),s=e.filter(l=>l.state==="resolved");if(t.length===0&&s.length===0)return this.store.state.requests.length>0||this.store.state.logs.length>0||this.store.state.queries.length>0?a`
|
|
936
1135
|
<div class="sec-clear">
|
|
937
1136
|
<span class="sec-clear-icon">\u2713</span>
|
|
938
1137
|
<div class="sec-clear-text">
|
|
@@ -940,10 +1139,10 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
940
1139
|
<div class="sec-clear-sub">No security or quality issues detected this session</div>
|
|
941
1140
|
</div>
|
|
942
1141
|
</div>
|
|
943
|
-
`:
|
|
1142
|
+
`:a`<bk-empty-state title="Waiting for requests..." subtitle="Start using your app to see security findings here"></bk-empty-state>`;let r=0,o=0,n=0;for(let l of t){let c=l.issue.severity;c==="critical"?r++:c==="info"?n++:o++;}return a`
|
|
944
1143
|
<div id="security-content">
|
|
945
|
-
${this.renderSummary(t.length,s.length,r,
|
|
946
|
-
${t.length===0&&s.length>0?
|
|
1144
|
+
${this.renderSummary(t.length,s.length,r,o,n)}
|
|
1145
|
+
${t.length===0&&s.length>0?a`
|
|
947
1146
|
<div class="sec-clear">
|
|
948
1147
|
<span class="sec-clear-icon">\u2713</span>
|
|
949
1148
|
<div class="sec-clear-text">
|
|
@@ -955,105 +1154,102 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
955
1154
|
${t.length>0?this.renderOpenGroups(t):d}
|
|
956
1155
|
${s.length>0?this.renderResolved(s):d}
|
|
957
1156
|
</div>
|
|
958
|
-
`}renderSummary(e,t,s,r,
|
|
1157
|
+
`}renderSummary(e,t,s,r,o){return a`
|
|
959
1158
|
<div class="sec-summary">
|
|
960
1159
|
<div class="sec-summary-left">
|
|
961
1160
|
<span class="sec-summary-count">${e}</span>
|
|
962
1161
|
<span class="sec-summary-label">open issue${e!==1?"s":""}</span>
|
|
963
|
-
${t>0?
|
|
1162
|
+
${t>0?a`<span class="sec-resolved-badge">${t} resolved</span>`:d}
|
|
964
1163
|
</div>
|
|
965
1164
|
<div class="sec-summary-right">
|
|
966
|
-
${s>0?
|
|
967
|
-
${r>0?
|
|
968
|
-
${
|
|
1165
|
+
${s>0?a`<span class="sec-badge critical">${s} critical</span>`:d}
|
|
1166
|
+
${r>0?a`<span class="sec-badge warning">${r} warning</span>`:d}
|
|
1167
|
+
${o>0?a`<span class="sec-badge info">${o} info</span>`:d}
|
|
969
1168
|
</div>
|
|
970
1169
|
</div>
|
|
971
|
-
`}renderOpenGroups(e){let t={},s=[];for(let r of e){let
|
|
1170
|
+
`}renderOpenGroups(e){let t={},s=[];for(let r of e){let o=r.issue,n=o.rule||o.type;t[n]||(t[n]={rule:n,title:o.title,severity:o.severity,hint:o.hint,items:[]},s.push(n)),t[n].items.push(r);}return s.sort((r,o)=>{let n=q[t[r].severity]?.sort??2,l=q[t[o].severity]?.sort??2;return n!==l?n-l:t[o].items.length-t[r].items.length}),a`${s.map(r=>this.renderGroup(t[r]))}`}renderGroup(e){let t=q[e.severity]||q.info;return a`
|
|
972
1171
|
<div class="sec-group">
|
|
973
1172
|
<div class="sec-group-header">
|
|
974
1173
|
<span class="sec-group-icon ${t.cls}">${t.icon}</span>
|
|
975
1174
|
<span class="sec-group-title">${e.title}</span>
|
|
976
1175
|
<span class="sec-group-count">${e.items.length}</span>
|
|
977
1176
|
</div>
|
|
978
|
-
${e.hint?
|
|
1177
|
+
${e.hint?a`<div class="sec-hint">${e.hint}</div>`:d}
|
|
979
1178
|
<div class="sec-items">${e.items.map(s=>this.renderIssueItem(s))}</div>
|
|
980
1179
|
</div>
|
|
981
|
-
`}renderIssueItem(e){let t=e.issue;return
|
|
1180
|
+
`}renderIssueItem(e){let t=e.issue;return a`
|
|
982
1181
|
<div class="sec-item">
|
|
983
1182
|
<div class="sec-item-desc">${t.desc}</div>
|
|
984
|
-
${e.occurrences>1?
|
|
985
|
-
${e.state==="fixing"&&e.aiStatus==="fixed"?
|
|
986
|
-
${e.aiNotes?
|
|
1183
|
+
${e.occurrences>1?a`<span class="sec-item-count">${e.occurrences}x</span>`:d}
|
|
1184
|
+
${e.state==="fixing"&&e.aiStatus==="fixed"?a`<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>`:e.aiStatus==="wont_fix"?a`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:e.state==="regressed"?a`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:d}
|
|
1185
|
+
${e.aiNotes?a`<div class="sec-ai-notes">${e.aiNotes}</div>`:d}
|
|
987
1186
|
</div>
|
|
988
|
-
`}renderResolved(e){return
|
|
1187
|
+
`}renderResolved(e){return a`
|
|
989
1188
|
<div class="sec-resolved-title">
|
|
990
1189
|
<span class="sec-resolved-check">\u2713</span> Resolved
|
|
991
1190
|
<span class="sec-resolved-count">${e.length}</span>
|
|
992
1191
|
</div>
|
|
993
1192
|
<div class="sec-group sec-group-resolved">
|
|
994
1193
|
<div class="sec-items">
|
|
995
|
-
${e.map(t=>
|
|
1194
|
+
${e.map(t=>a`
|
|
996
1195
|
<div class="sec-item sec-item-resolved">
|
|
997
1196
|
<span class="sec-resolved-item-icon">\u2713</span>
|
|
998
1197
|
<div class="sec-item-desc">${t.issue.title} \u2014 ${t.issue.endpoint||"global"}</div>
|
|
999
|
-
${t.aiStatus==="fixed"?
|
|
1000
|
-
${t.aiNotes?
|
|
1198
|
+
${t.aiStatus==="fixed"?a`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:d}
|
|
1199
|
+
${t.aiNotes?a`<div class="sec-ai-notes">${t.aiNotes}</div>`:d}
|
|
1001
1200
|
</div>
|
|
1002
1201
|
`)}
|
|
1003
1202
|
</div>
|
|
1004
1203
|
</div>
|
|
1005
|
-
`}};u([
|
|
1204
|
+
`}};u([R({context:x})],Ct.prototype,"store",2),Ct=u([g("bk-security-view")],Ct);var pt=class extends f{constructor(){super(...arguments);this.expandedCardIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleCard(t){this.expandedCardIdx=this.expandedCardIdx===t?-1:t;}render(){let t=this.store.state,s=t.requests.filter(E=>!E.isStatic&&!E.isHealthCheck&&(!E.path||E.path.indexOf(k)!==0));if(!(s.length>0||t.queries.length>0||t.errors.length>0))return a`<bk-empty-state
|
|
1006
1205
|
title="Waiting for requests..."
|
|
1007
1206
|
subtitle="Start using your app to see insights here"
|
|
1008
|
-
></bk-empty-state>`;let
|
|
1207
|
+
></bk-empty-state>`;let o=s.filter(E=>E.statusCode>=400).length,n=new Set(["data-fetch","api-call","server-action","page-load"]),l=s.filter(E=>E.category&&n.has(E.category)),c=l.length>0?l:s,p=c.length>0?Math.round(c.reduce((E,It)=>E+It.durationMs,0)/c.length):0,h=t.issues||[],m=h.filter(E=>E.state==="open"||E.state==="regressed"),b=h.filter(E=>E.state==="fixing"),$=h.filter(E=>E.state==="resolved");return a`
|
|
1009
1208
|
<div class="ov-container" id="overview-content">
|
|
1010
|
-
${this.renderSummary(s.length,t.flows.length,
|
|
1011
|
-
${
|
|
1209
|
+
${this.renderSummary(s.length,t.flows.length,p,t.queries.length,o,t.fetches.length)}
|
|
1210
|
+
${m.length===0&&b.length===0&&$.length===0?a`<div class="ov-clear">
|
|
1012
1211
|
<span class="ov-clear-icon">\u2713</span>All clear \u2014 no issues detected
|
|
1013
1212
|
</div>`:d}
|
|
1014
|
-
${
|
|
1213
|
+
${m.length===0&&$.length>0?a`<div class="ov-clear">
|
|
1015
1214
|
<span class="ov-clear-icon">\u2713</span>All issues resolved \u2014
|
|
1016
|
-
${
|
|
1215
|
+
${$.length} finding${$.length!==1?"s were":" was"} detected and
|
|
1017
1216
|
fixed
|
|
1018
1217
|
</div>`:d}
|
|
1019
|
-
${
|
|
1020
|
-
${
|
|
1021
|
-
${
|
|
1218
|
+
${m.length>0?this.renderOpenIssues(m):d}
|
|
1219
|
+
${b.length>0?this.renderVerifying(b):d}
|
|
1220
|
+
${$.length>0?this.renderResolvedIssues($):d}
|
|
1022
1221
|
</div>
|
|
1023
|
-
`}renderSummary(t,s,r,
|
|
1222
|
+
`}renderSummary(t,s,r,o,n,l){return a`
|
|
1024
1223
|
<div class="ov-summary">
|
|
1025
1224
|
<div class="ov-stat"><span class="ov-stat-value">${t}</span><span class="ov-stat-label">Requests</span></div>
|
|
1026
1225
|
<div class="ov-stat"><span class="ov-stat-value">${s}</span><span class="ov-stat-label">Actions</span></div>
|
|
1027
|
-
<div class="ov-stat"><span class="ov-stat-value">${
|
|
1028
|
-
<div class="ov-stat"><span class="ov-stat-value">${
|
|
1029
|
-
<div class="ov-stat"><span class="ov-stat-value" style="color:${
|
|
1030
|
-
<div class="ov-stat"><span class="ov-stat-value">${
|
|
1226
|
+
<div class="ov-stat"><span class="ov-stat-value">${v(r)}</span><span class="ov-stat-label">Avg Response</span></div>
|
|
1227
|
+
<div class="ov-stat"><span class="ov-stat-value">${o}</span><span class="ov-stat-label">Queries</span></div>
|
|
1228
|
+
<div class="ov-stat"><span class="ov-stat-value" style="color:${n>0?"var(--red)":"var(--green)"}">${n}</span><span class="ov-stat-label">Errors</span></div>
|
|
1229
|
+
<div class="ov-stat"><span class="ov-stat-value">${l}</span><span class="ov-stat-label">Fetches</span></div>
|
|
1031
1230
|
</div>
|
|
1032
|
-
`}renderOpenIssues(t){return
|
|
1231
|
+
`}renderOpenIssues(t){return a`
|
|
1033
1232
|
<div class="ov-section-title">Issues Found <span class="ov-issue-count">${t.length}</span></div>
|
|
1034
1233
|
<div class="ov-cards">${t.map((s,r)=>this.renderIssueCard(s,r))}</div>
|
|
1035
|
-
`}renderIssueCard(t,s){let r=t.issue,
|
|
1036
|
-
<div class="ov-card ${
|
|
1037
|
-
<span class="ov-card-icon ${
|
|
1234
|
+
`}renderIssueCard(t,s){let r=t.issue,o=q[r.severity]||q.info,n=this.expandedCardIdx===s,l=t.aiStatus==="wont_fix"?a`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:t.state==="regressed"?a`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:d,c=t.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Resolving\u2026 ${t.cleanHitsSinceLastSeen}/${5} clean requests</div>`:d;return a`
|
|
1235
|
+
<div class="ov-card ${n?"expanded":""}" @click=${()=>this.toggleCard(s)}>
|
|
1236
|
+
<span class="ov-card-icon ${o.cls}">${o.icon}</span>
|
|
1038
1237
|
<div class="ov-card-body">
|
|
1039
|
-
<div class="ov-card-title">${r.title}${
|
|
1238
|
+
<div class="ov-card-title">${r.title}${l}</div>
|
|
1040
1239
|
<div class="ov-card-desc">${r.desc}</div>
|
|
1041
|
-
${
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
${r.hint?n`<div class="ov-card-hint">${r.hint}</div>`:d}
|
|
1045
|
-
${r.nav?n`<span class="ov-card-link" data-nav=${r.nav}>View in ${de[r.nav]||r.nav} \u2192</span>`:d}
|
|
1046
|
-
</div>
|
|
1240
|
+
${r.detail?a`<div class="ov-card-detail">${r.detail}</div>`:d}
|
|
1241
|
+
${c}
|
|
1242
|
+
${n&&r.hint?a`<div class="ov-card-hint">${r.hint}</div>`:d}
|
|
1047
1243
|
</div>
|
|
1048
|
-
|
|
1244
|
+
${r.hint?a`<span class="ov-card-arrow">${n?"\u2193":"\u2192"}</span>`:d}
|
|
1049
1245
|
</div>
|
|
1050
|
-
`}renderVerifying(t){return
|
|
1246
|
+
`}renderVerifying(t){return a`
|
|
1051
1247
|
<div class="ov-section-title ov-resolved-title">
|
|
1052
1248
|
<span style="color:var(--yellow,#f5a623)">\u29d7</span> Awaiting Verification
|
|
1053
1249
|
<span class="ov-issue-count">${t.length}</span>
|
|
1054
1250
|
</div>
|
|
1055
1251
|
<div class="ov-cards">
|
|
1056
|
-
${t.map(s=>{let r=s.issue,
|
|
1252
|
+
${t.map(s=>{let r=s.issue,o=s.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Verifying\u2026 ${s.cleanHitsSinceLastSeen}/${5} clean requests</div>`:d;return a`
|
|
1057
1253
|
<div class="ov-card ov-card-resolved">
|
|
1058
1254
|
<span class="ov-card-icon resolved">\u29d7</span>
|
|
1059
1255
|
<div class="ov-card-body">
|
|
@@ -1062,18 +1258,18 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1062
1258
|
<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>
|
|
1063
1259
|
</div>
|
|
1064
1260
|
<div class="ov-card-desc">${r.desc}</div>
|
|
1065
|
-
${
|
|
1261
|
+
${o}
|
|
1066
1262
|
</div>
|
|
1067
1263
|
</div>
|
|
1068
1264
|
`})}
|
|
1069
1265
|
</div>
|
|
1070
|
-
`}renderResolvedIssues(t){return
|
|
1266
|
+
`}renderResolvedIssues(t){return a`
|
|
1071
1267
|
<div class="ov-section-title ov-resolved-title">
|
|
1072
1268
|
<span style="color:var(--green)">\u2713</span> Resolved
|
|
1073
1269
|
<span class="ov-issue-count">${t.length}</span>
|
|
1074
1270
|
</div>
|
|
1075
1271
|
<div class="ov-cards">
|
|
1076
|
-
${t.map(s=>
|
|
1272
|
+
${t.map(s=>a`
|
|
1077
1273
|
<div class="ov-card ov-card-resolved">
|
|
1078
1274
|
<span class="ov-card-icon resolved">\u2713</span>
|
|
1079
1275
|
<div class="ov-card-body">
|
|
@@ -1083,71 +1279,98 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1083
1279
|
</div>
|
|
1084
1280
|
`)}
|
|
1085
1281
|
</div>
|
|
1086
|
-
`}};u([
|
|
1282
|
+
`}};u([R({context:x})],pt.prototype,"store",2),u([_()],pt.prototype,"expandedCardIdx",2),pt=u([g("bk-overview-view")],pt);function hs(i){return i<1?"<1ms":i<1e3?Math.round(i)+"ms":(i/1e3).toFixed(1)+"s"}function ir(i){return i<Z?Tt.green:i<tt?Tt.amber:Tt.red}function nr(i){return i.statusCode>=400?Tt.red:ir(i.durationMs)}function ms(i){return [parseInt(i.slice(1,3),16),parseInt(i.slice(3,5),16),parseInt(i.slice(5,7),16)]}function ar(i){let e=i.getContext("2d");if(!e)return null;let t=window.devicePixelRatio||1,s=i.clientWidth,r=i.clientHeight;return i.width=s*t,i.height=r*t,e.scale(t,t),{ctx:e,w:s,h:r}}function lr(i,e,t,s,r){let[o,n,l]=ms(r);i.beginPath(),i.arc(e,t,s+2,0,Math.PI*2),i.fillStyle=`rgba(${o},${n},${l},0.25)`,i.fill(),i.beginPath(),i.arc(e,t,s,0,Math.PI*2),i.fillStyle=r,i.fill();}function cr(i,e,t,s,r,o){let[n,l,c]=ms(r);i.strokeStyle=`rgba(${n},${l},${c},0.3)`,i.lineWidth=o+2,i.beginPath(),i.moveTo(e-s,t-s),i.lineTo(e+s,t+s),i.moveTo(e+s,t-s),i.lineTo(e-s,t+s),i.stroke(),i.strokeStyle=r,i.lineWidth=o,i.beginPath(),i.moveTo(e-s,t-s),i.lineTo(e+s,t+s),i.moveTo(e+s,t-s),i.lineTo(e-s,t+s),i.stroke();}function vs(i,e){let t=[],s=ar(i);if(!s||e.length===0)return t;let{ctx:r,w:o,h:n}=s,l=Ze,c=o-l.left-l.right,p=n-l.top-l.bottom,h=0,m=e[0].timestamp,b=e[0].timestamp;for(let S of e)S.durationMs>h&&(h=S.durationMs),S.timestamp<m&&(m=S.timestamp),S.timestamp>b&&(b=S.timestamp);h=Math.max(h,10),h=Math.ceil(h*1.15/10)*10;let $=b-m||1;r.strokeStyle=ze,r.lineWidth=1;let E=4;for(let S=0;S<=E;S++){let A=l.top+p-S/E*p;r.beginPath(),r.moveTo(l.left,A),r.lineTo(l.left+c,A),r.stroke(),r.fillStyle=ve,r.font=Je,r.textAlign="right",r.fillText(hs(Math.round(S/E*h)),l.left-8,A+3);}for(let S of [{ms:Z},{ms:tt}]){if(S.ms>=h)continue;let A=l.top+p-S.ms/h*p;r.beginPath(),r.setLineDash([4,4]),r.strokeStyle="rgba(113,113,122,0.3)",r.lineWidth=1,r.moveTo(l.left,A),r.lineTo(l.left+c,A),r.stroke(),r.setLineDash([]),r.fillStyle="rgba(113,113,122,0.5)",r.font=fe,r.textAlign="left",r.fillText(hs(S.ms),l.left+c+2,A+3);}for(let S=0;S<e.length;S++){let A=e[S],ut=e.length===1?l.left+c/2:l.left+(A.timestamp-m)/$*c,jt=l.top+p-A.durationMs/h*p,ye=nr(A);t.push({x:ut,y:jt,idx:S,r:A}),A.statusCode>=400?cr(r,ut,jt,4,ye,2):lr(r,ut,jt,4,ye);}r.fillStyle=ve,r.font=fe,r.textAlign="center";let It=[m,m+$/2,b];for(let S=0;S<It.length;S++){let A=l.left+S/2*c,ut=new Date(It[S]);r.fillText(ut.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),A,l.top+p+14);}return t}var vr={max:1/0,label:"Pending",color:"var(--text-muted)",bg:"var(--bg-muted)",border:"var(--border)"};function fs(i,e,t){return t>=20?i:e}function $e(i,e){if(!e||e<=0)return vr;let t=i/e;return t<.7?et[0]:t<1.2?et[1]:t<2?et[2]:t<3?et[3]:et[4]}var L=class extends f{constructor(){super(...arguments);this.selectedEndpoint=H;this.graphData=[];this.loadError=false;this.queryBreakdown=[];this.queryBreakdownLoading=false;this.scatterDots=[];}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate()),this.loadMetrics();}async loadMetrics(){try{let s=await(await fetch(y.metricsLive)).json();this.graphData=s.endpoints||[],this.loadError=!1,(!this.selectedEndpoint||this.selectedEndpoint===H)&&(this.selectedEndpoint=H);}catch{this.loadError=true;}}healthGradeForEndpoint(t){let s=fs(t.summary.p95Ms,t.summary.medianMs,t.summary.totalRequests);return $e(s,t.baselineP95Ms)}healthGradeForDuration(t,s){return $e(t,s)}getCallers(t){let s=this.store.state.flows,r=new Map;for(let o of s)for(let n of o.requests)if(`${n.method} ${n.path}`===t||this.normalizeEndpoint(n)===t){let c=r.get(o.label);c?(c.count++,c.totalMs+=n.durationMs):r.set(o.label,{count:1,totalMs:n.durationMs});}return [...r.entries()].map(([o,n])=>({label:o,count:n.count,avgMs:Math.round(n.totalMs/n.count)})).sort((o,n)=>n.count-o.count)}normalizeEndpoint(t){let s=t.path.replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,"/:id").replace(/\/\d+/g,"/:id");return `${t.method} ${s}`}async loadQueryBreakdown(t){if(this.queryBreakdownLoading)return;let r=this.store.state.requests.filter(o=>`${o.method} ${o.path}`===t||this.normalizeEndpoint(o)===t).slice(-20).map(o=>o.id).filter(Boolean);if(r.length===0){this.queryBreakdown=[];return}this.queryBreakdownLoading=true;try{let o=await fetch(`${y.activity}?requestIds=${r.join(",")}`);if(!o.ok){this.queryBreakdownLoading=!1;return}let n=await o.json(),l=new Map;for(let c of Object.values(n.activities))for(let p of c.timeline){if(p.type!=="query")continue;let h=p.data,m=(h.normalizedOp||h.operation||"QUERY").toUpperCase(),b=h.table||h.model||"",$=`${m} ${b}`.trim(),E=l.get($);E?(E.totalMs+=h.durationMs,E.count++):l.set($,{label:$,totalMs:h.durationMs,count:1});}this.queryBreakdown=[...l.values()].map(c=>({...c,avgMs:Math.round(c.totalMs/c.count)})).sort((c,p)=>p.totalMs-c.totalMs);}catch{}this.queryBreakdownLoading=false;}renderScatterChart(t,s){this.scatterDots=vs(t,s),t.style.cursor="pointer",t.onclick=r=>{let o=t.getBoundingClientRect(),n=r.clientX-o.left,l=r.clientY-o.top,c=null,p=1/0;for(let h of this.scatterDots){let m=Math.sqrt((h.x-n)**2+(h.y-l)**2);m<p&&(p=m,c=h);}c&&p<16&&this.highlightRow(c.idx);};}highlightRow(t){let s=this.querySelector(".perf-hist-row-hl");s&&s.classList.remove("perf-hist-row-hl");let r=this.querySelector(`[data-req-idx="${t}"]`);r&&(r.classList.add("perf-hist-row-hl"),r.scrollIntoView({behavior:"smooth",block:"center"}));}updated(){if(this.selectedEndpoint===H)return;let t=this.querySelector("#perf-detail-canvas");if(t){let s=this.graphData.find(r=>r.endpoint===this.selectedEndpoint);s&&this.renderScatterChart(t,s.requests);}}render(){return !this.graphData||this.graphData.length===0?a`<bk-empty-state title="No performance data yet" subtitle="Hit some endpoints and data will appear here"></bk-empty-state>`:a`
|
|
1087
1283
|
<div id="graph-content">
|
|
1088
1284
|
${this.renderSelector()}
|
|
1089
|
-
${this.selectedEndpoint===
|
|
1285
|
+
${this.selectedEndpoint===H?this.renderOverview():this.renderDetail()}
|
|
1090
1286
|
</div>
|
|
1091
|
-
`}renderSelector(){return
|
|
1287
|
+
`}renderSelector(){return a`
|
|
1092
1288
|
<div class="perf-selector">
|
|
1093
|
-
<button class="perf-selector-btn ${this.selectedEndpoint===
|
|
1094
|
-
@click=${()=>{this.selectedEndpoint=
|
|
1095
|
-
${this.graphData.map((t,s)=>
|
|
1289
|
+
<button class="perf-selector-btn ${this.selectedEndpoint===H?"active":""}"
|
|
1290
|
+
@click=${()=>{this.selectedEndpoint=H;}}>Overview</button>
|
|
1291
|
+
${this.graphData.map((t,s)=>a`
|
|
1096
1292
|
<button class="perf-selector-btn ${t.endpoint===this.selectedEndpoint?"active":""}"
|
|
1097
|
-
@click=${()=>{this.selectedEndpoint=t.endpoint;}}>
|
|
1098
|
-
<span class="perf-dot" style="background:${
|
|
1293
|
+
@click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
1294
|
+
<span class="perf-dot" style="background:${me[s%me.length]}"></span>${t.endpoint}
|
|
1099
1295
|
</button>
|
|
1100
1296
|
`)}
|
|
1101
1297
|
</div>
|
|
1102
|
-
`}renderOverview(){return n`
|
|
1103
|
-
<div class="perf-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1298
|
+
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return d;let s=t.reduce((c,p)=>c+p.summary.totalRequests,0),r=s>0?Math.round(t.reduce((c,p)=>c+p.summary.p95Ms*p.summary.totalRequests,0)/s):0,o=t.reduce((c,p)=>c+Math.round(p.summary.errorRate*p.summary.totalRequests),0),n=s>0?o/s:0,l=t[0];return a`
|
|
1299
|
+
<div class="perf-overview">
|
|
1300
|
+
<div class="perf-summary-row">
|
|
1301
|
+
<div class="perf-summary-card">
|
|
1302
|
+
<span class="perf-summary-label">Total Requests</span>
|
|
1303
|
+
<span class="perf-summary-value">${s}</span>
|
|
1304
|
+
</div>
|
|
1305
|
+
<div class="perf-summary-card">
|
|
1306
|
+
<span class="perf-summary-label">Avg P95</span>
|
|
1307
|
+
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(r).color}">${v(r)}</span>
|
|
1308
|
+
</div>
|
|
1309
|
+
<div class="perf-summary-card">
|
|
1310
|
+
<span class="perf-summary-label">Error Rate</span>
|
|
1311
|
+
<span class="perf-summary-value" style="color:${o>0?"var(--red)":"var(--green)"}">${Math.round(n*100)}%</span>
|
|
1312
|
+
</div>
|
|
1313
|
+
<div class="perf-summary-card">
|
|
1314
|
+
<span class="perf-summary-label">Slowest</span>
|
|
1315
|
+
<span class="perf-summary-value perf-summary-value-sm">${l?.endpoint??"-"}</span>
|
|
1112
1316
|
</div>
|
|
1113
|
-
<span class="perf-breakdown-labels">
|
|
1114
|
-
${h>0?n`<span class="perf-breakdown-lbl"><span class="perf-breakdown-dot perf-breakdown-db"></span>${this.fmtMs(r.avgQueryTimeMs||0)}</span>`:d}
|
|
1115
|
-
${m>0?n`<span class="perf-breakdown-lbl"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>${this.fmtMs(r.avgFetchTimeMs||0)}</span>`:d}
|
|
1116
|
-
<span class="perf-breakdown-lbl"><span class="perf-breakdown-dot perf-breakdown-app"></span>${this.fmtMs(r.avgAppTimeMs||0)}</span>
|
|
1117
|
-
</span>
|
|
1118
|
-
</div>
|
|
1119
|
-
`;}return n`
|
|
1120
|
-
<div class="perf-endpoint-card" @click=${()=>{this.selectedEndpoint=t.endpoint;}}>
|
|
1121
|
-
<div class="perf-ep-header">
|
|
1122
|
-
<span class="perf-ep-name">${t.endpoint}</span>
|
|
1123
|
-
<span class="perf-ep-stats">
|
|
1124
|
-
<span class="perf-ep-stat" style="color:${i.color}">p95: ${this.fmtMs(r.p95Ms)}</span>
|
|
1125
|
-
<span class="perf-ep-stat ${a>0?"perf-ep-stat-err":""}">${a} err</span>
|
|
1126
|
-
${r.avgQueryCount>0?n`<span class="perf-ep-stat ${r.avgQueryCount>5?"perf-ep-stat-warn":""}">${r.avgQueryCount} q/req</span>`:d}
|
|
1127
|
-
<span class="perf-ep-stat perf-ep-stat-muted">${r.totalRequests} req${r.totalRequests!==1?"s":""}</span>
|
|
1128
|
-
</span>
|
|
1129
1317
|
</div>
|
|
1130
|
-
|
|
1131
|
-
<
|
|
1318
|
+
|
|
1319
|
+
<table class="perf-table perf-heatmap">
|
|
1320
|
+
<thead>
|
|
1321
|
+
<tr>
|
|
1322
|
+
<th>Endpoint</th>
|
|
1323
|
+
<th class="perf-th-right">Calls</th>
|
|
1324
|
+
<th class="perf-th-center">P95</th>
|
|
1325
|
+
<th class="perf-th-center">Errors</th>
|
|
1326
|
+
<th class="perf-th-center">Q/Req</th>
|
|
1327
|
+
<th>Time Split</th>
|
|
1328
|
+
</tr>
|
|
1329
|
+
</thead>
|
|
1330
|
+
<tbody>
|
|
1331
|
+
${t.map(c=>this.renderHeatmapRow(c))}
|
|
1332
|
+
</tbody>
|
|
1333
|
+
</table>
|
|
1132
1334
|
</div>
|
|
1133
|
-
`}
|
|
1335
|
+
`}renderHeatmapRow(t){let s=t.summary,r=this.healthGradeForEndpoint(t),o=Math.round(s.errorRate*s.totalRequests),n=(s.avgQueryTimeMs||0)+(s.avgFetchTimeMs||0)+(s.avgAppTimeMs||0),l=0,c=0,p=100;return n>0&&(l=Math.round((s.avgQueryTimeMs||0)/n*100),c=Math.round((s.avgFetchTimeMs||0)/n*100),p=Math.max(0,100-l-c)),a`
|
|
1336
|
+
<tr class="perf-table-row" @click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
1337
|
+
<td class="perf-td-name">${t.endpoint}</td>
|
|
1338
|
+
<td class="perf-td-right">${s.totalRequests}</td>
|
|
1339
|
+
<td class="perf-td-center">
|
|
1340
|
+
<span class="perf-hm-p95" style="color:${r.color};background:${r.bg};border-color:${r.border}">${v(s.p95Ms)}</span>
|
|
1341
|
+
</td>
|
|
1342
|
+
<td class="perf-td-center" style="color:${o>0?"var(--red)":"var(--text-muted)"}">${o>0?o:"-"}</td>
|
|
1343
|
+
<td class="perf-td-center" style="color:${s.avgQueryCount>5?"var(--amber)":"var(--text-muted)"}">${s.avgQueryCount}</td>
|
|
1344
|
+
<td>
|
|
1345
|
+
<span class="perf-hm-split-bar">
|
|
1346
|
+
${l>0?a`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${l}%"></span>`:d}
|
|
1347
|
+
${c>0?a`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:d}
|
|
1348
|
+
${p>0?a`<span class="perf-breakdown-seg perf-breakdown-app" style="width:${p}%"></span>`:d}
|
|
1349
|
+
</span>
|
|
1350
|
+
</td>
|
|
1351
|
+
</tr>
|
|
1352
|
+
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return a`<bk-empty-state subtitle="No data for this endpoint"></bk-empty-state>`;let s=t.summary,r=this.healthGradeForEndpoint(t),o=Math.round(s.errorRate*s.totalRequests);return a`
|
|
1134
1353
|
${this.renderDetailHeader(t,r)}
|
|
1135
|
-
${this.renderDetailMetrics(s,r,
|
|
1354
|
+
${this.renderDetailMetrics(s,r,o)}
|
|
1136
1355
|
${this.renderDetailBreakdown(s)}
|
|
1356
|
+
${this.renderCallers(t.endpoint)}
|
|
1357
|
+
${this.renderQueryBreakdown()}
|
|
1358
|
+
${this.renderTrends(t)}
|
|
1137
1359
|
${this.renderDetailChart()}
|
|
1138
1360
|
${this.renderDetailHistory(t)}
|
|
1139
|
-
`}renderDetailHeader(t,s){return
|
|
1361
|
+
`}renderDetailHeader(t,s){return a`
|
|
1140
1362
|
<div class="perf-detail-header">
|
|
1141
1363
|
<div class="perf-detail-title">
|
|
1142
1364
|
<span class="perf-badge perf-badge-lg" style="color:${s.color};background:${s.bg};border-color:${s.border}">${s.label}</span>
|
|
1143
1365
|
<span>${t.endpoint}</span>
|
|
1366
|
+
${t.baselineP95Ms?a`<span class="perf-baseline-hint">Baseline: ${v(t.baselineP95Ms)}</span>`:d}
|
|
1144
1367
|
</div>
|
|
1145
1368
|
</div>
|
|
1146
|
-
`}renderDetailMetrics(t,s,r){return
|
|
1369
|
+
`}renderDetailMetrics(t,s,r){return a`
|
|
1147
1370
|
<div class="perf-metric-row">
|
|
1148
1371
|
<div class="perf-metric-card">
|
|
1149
1372
|
<span class="perf-metric-label">P95</span>
|
|
1150
|
-
<span class="perf-metric-value" style="color:${s.color}">${
|
|
1373
|
+
<span class="perf-metric-value" style="color:${s.color}">${v(t.p95Ms)}</span>
|
|
1151
1374
|
</div>
|
|
1152
1375
|
<div class="perf-metric-card">
|
|
1153
1376
|
<span class="perf-metric-label">Errors</span>
|
|
@@ -1160,119 +1383,170 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1160
1383
|
<span class="perf-metric-value" style="color:${t.avgQueryCount>5?"var(--amber)":"var(--text)"}">${t.avgQueryCount}</span>
|
|
1161
1384
|
</div>
|
|
1162
1385
|
</div>
|
|
1163
|
-
`}renderDetailBreakdown(t){let s=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(s<=0)return d;let r=Math.round((t.avgQueryTimeMs||0)/s*100),
|
|
1386
|
+
`}renderDetailBreakdown(t){let s=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(s<=0)return d;let r=Math.round((t.avgQueryTimeMs||0)/s*100),o=Math.round((t.avgFetchTimeMs||0)/s*100),n=Math.max(0,100-r-o);return a`
|
|
1164
1387
|
<div class="perf-breakdown">
|
|
1165
1388
|
<div class="perf-section-title">Time Breakdown</div>
|
|
1166
1389
|
<div class="perf-breakdown-bar">
|
|
1167
|
-
${r>0?
|
|
1168
|
-
${
|
|
1169
|
-
${
|
|
1390
|
+
${r>0?a`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${r}%"></div>`:d}
|
|
1391
|
+
${o>0?a`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${o}%"></div>`:d}
|
|
1392
|
+
${n>0?a`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:d}
|
|
1170
1393
|
</div>
|
|
1171
1394
|
<div class="perf-breakdown-legend">
|
|
1172
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${
|
|
1173
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${
|
|
1174
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${
|
|
1395
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${v(t.avgQueryTimeMs||0)} (${r}%)</span>
|
|
1396
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${v(t.avgFetchTimeMs||0)} (${o}%)</span>
|
|
1397
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${v(t.avgAppTimeMs||0)} (${n}%)</span>
|
|
1175
1398
|
</div>
|
|
1176
1399
|
</div>
|
|
1177
|
-
`}
|
|
1400
|
+
`}renderCallers(t){let s=this.getCallers(t);return s.length===0?d:a`
|
|
1401
|
+
<div class="perf-callers">
|
|
1402
|
+
<div class="perf-section-title">Called By</div>
|
|
1403
|
+
<div class="perf-callers-list">
|
|
1404
|
+
${s.map(r=>a`
|
|
1405
|
+
<div class="perf-caller-row">
|
|
1406
|
+
<span class="perf-caller-name">${r.label}</span>
|
|
1407
|
+
<span class="perf-caller-count">${r.count} call${r.count!==1?"s":""}</span>
|
|
1408
|
+
<span class="perf-caller-avg">avg ${v(r.avgMs)}</span>
|
|
1409
|
+
</div>
|
|
1410
|
+
`)}
|
|
1411
|
+
</div>
|
|
1412
|
+
</div>
|
|
1413
|
+
`}renderQueryBreakdown(){return this.queryBreakdownLoading?a`<div class="perf-queries"><div class="perf-section-title">DB Queries</div><div class="perf-queries-loading">Loading...</div></div>`:this.queryBreakdown.length===0?d:a`
|
|
1414
|
+
<div class="perf-queries">
|
|
1415
|
+
<div class="perf-section-title">DB Queries</div>
|
|
1416
|
+
<div class="perf-queries-list">
|
|
1417
|
+
${this.queryBreakdown.map(t=>a`
|
|
1418
|
+
<div class="perf-query-row">
|
|
1419
|
+
<span class="perf-query-label">${t.label}</span>
|
|
1420
|
+
<span class="perf-query-avg">avg ${v(t.avgMs)}</span>
|
|
1421
|
+
<span class="perf-query-count">${t.count} call${t.count!==1?"s":""}</span>
|
|
1422
|
+
</div>
|
|
1423
|
+
`)}
|
|
1424
|
+
</div>
|
|
1425
|
+
</div>
|
|
1426
|
+
`}renderTrends(t){let s=t.sessions;if(!s||s.length===0)return d;let r=s.slice(-10);return a`
|
|
1427
|
+
<div class="perf-trends">
|
|
1428
|
+
<div class="perf-section-title">Session Trend</div>
|
|
1429
|
+
<div class="perf-trends-list">
|
|
1430
|
+
${r.map((o,n)=>{let l=n>0?r[n-1].p95DurationMs:null,c=l!==null?o.p95DurationMs>l*1.2?"slower":o.p95DurationMs<l*.8?"faster":"":"",p=this.formatTimeAgo(o.startedAt),h=n===r.length-1,m=this.healthGradeForDuration(o.p95DurationMs,t.baselineP95Ms);return a`
|
|
1431
|
+
<div class="perf-trend-row ${h?"perf-trend-current":""}">
|
|
1432
|
+
<span class="perf-trend-time">${h?"Current":p}</span>
|
|
1433
|
+
<span class="perf-trend-p95">
|
|
1434
|
+
<span class="perf-hm-p95" style="color:${m.color};background:${m.bg};border-color:${m.border}">
|
|
1435
|
+
p95: ${v(o.p95DurationMs)}
|
|
1436
|
+
</span>
|
|
1437
|
+
</span>
|
|
1438
|
+
<span class="perf-trend-reqs">${o.requestCount} req${o.requestCount!==1?"s":""}</span>
|
|
1439
|
+
<span class="perf-trend-errs" style="color:${o.errorCount>0?"var(--red)":"var(--text-dim)"}">${o.errorCount} err${o.errorCount!==1?"s":""}</span>
|
|
1440
|
+
${c?a`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:d}
|
|
1441
|
+
</div>
|
|
1442
|
+
`})}
|
|
1443
|
+
</div>
|
|
1444
|
+
</div>
|
|
1445
|
+
`}formatTimeAgo(t){let s=Date.now()-t,r=Math.round(s/6e4);if(r<1)return "just now";if(r<60)return `${r}m ago`;let o=Math.round(r/60);return o<24?`${o}h ago`:`${Math.round(o/24)}d ago`}renderDetailChart(){return a`
|
|
1178
1446
|
<div class="perf-chart-wrap">
|
|
1179
1447
|
<div class="perf-section-title">Response Time</div>
|
|
1180
1448
|
<canvas id="perf-detail-canvas" class="perf-canvas" style="width:100%;height:240px"></canvas>
|
|
1181
1449
|
</div>
|
|
1182
|
-
`}renderDetailHistory(t){if(t.requests.length===0)return d;let s=[];for(let r=t.requests.length-1;r>=0&&s.length<50;r--)s.push({
|
|
1450
|
+
`}renderDetailHistory(t){if(t.requests.length===0)return d;let s=[];for(let r=t.requests.length-1;r>=0&&s.length<50;r--)s.push({point:t.requests[r],originalIndex:r});return a`
|
|
1183
1451
|
<div class="perf-history-wrap">
|
|
1184
|
-
<
|
|
1185
|
-
<
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
</span>
|
|
1200
|
-
<span class="perf-col perf-col-avg">${this.fmtMs(t.durationMs)}</span>
|
|
1201
|
-
<span class="perf-col perf-col-breakdown">
|
|
1202
|
-
${c>0?n`<span class="perf-bd-tag perf-bd-tag-db">DB ${this.fmtMs(c)}</span>`:d}
|
|
1203
|
-
${l>0?n`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${this.fmtMs(l)}</span>`:d}
|
|
1204
|
-
<span class="perf-bd-tag perf-bd-tag-app">App ${this.fmtMs(h)}</span>
|
|
1205
|
-
</span>
|
|
1206
|
-
<span class="perf-col perf-col-status" style="color:${a?"var(--red)":"var(--text-muted)"}">${t.statusCode}</span>
|
|
1207
|
-
<span class="perf-col perf-col-qpr">${t.queryCount}</span>
|
|
1452
|
+
<table class="perf-table">
|
|
1453
|
+
<thead>
|
|
1454
|
+
<tr>
|
|
1455
|
+
<th>Time</th>
|
|
1456
|
+
<th>Health</th>
|
|
1457
|
+
<th>Duration</th>
|
|
1458
|
+
<th>Breakdown</th>
|
|
1459
|
+
<th class="perf-th-center">Status</th>
|
|
1460
|
+
<th class="perf-th-right">Queries</th>
|
|
1461
|
+
</tr>
|
|
1462
|
+
</thead>
|
|
1463
|
+
<tbody>
|
|
1464
|
+
${s.map(r=>this.renderHistoryRow(r.point,r.originalIndex,t.baselineP95Ms))}
|
|
1465
|
+
</tbody>
|
|
1466
|
+
</table>
|
|
1208
1467
|
</div>
|
|
1209
|
-
`}
|
|
1468
|
+
`}renderHistoryRow(t,s,r){let o=this.healthGradeForDuration(t.durationMs,r),n=new Date(t.timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),l=t.statusCode>=400,c=t.queryTimeMs||0,p=t.fetchTimeMs||0,h=Math.max(0,t.durationMs-c-p);return a`
|
|
1469
|
+
<tr class="perf-table-row ${l?"perf-row-err":""}" data-req-idx=${s}>
|
|
1470
|
+
<td class="perf-td-muted">${n}</td>
|
|
1471
|
+
<td>
|
|
1472
|
+
<span class="perf-badge perf-badge-sm" style="color:${o.color};background:${o.bg};border-color:${o.border}">${o.label}</span>
|
|
1473
|
+
</td>
|
|
1474
|
+
<td>${v(t.durationMs)}</td>
|
|
1475
|
+
<td>
|
|
1476
|
+
${c>0?a`<span class="perf-bd-tag perf-bd-tag-db">DB ${v(c)}</span>`:d}
|
|
1477
|
+
${p>0?a`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${v(p)}</span>`:d}
|
|
1478
|
+
<span class="perf-bd-tag perf-bd-tag-app">App ${v(h)}</span>
|
|
1479
|
+
</td>
|
|
1480
|
+
<td class="perf-td-center" style="color:${l?"var(--red)":"var(--text-muted)"}">${t.statusCode}</td>
|
|
1481
|
+
<td class="perf-td-right perf-td-muted">${t.queryCount}</td>
|
|
1482
|
+
</tr>
|
|
1483
|
+
`}};u([R({context:x})],L.prototype,"store",2),u([_()],L.prototype,"selectedEndpoint",2),u([_()],L.prototype,"graphData",2),u([_()],L.prototype,"loadError",2),u([_()],L.prototype,"queryBreakdown",2),u([_()],L.prototype,"queryBreakdownLoading",2),L=u([g("bk-performance-view")],L);function _r(i){return i===0?"<1ms":v(i)}var w=class extends f{constructor(){super(...arguments);this.requestId="";this.requestStarted=0;this.data=null;this.loading=false;this.failed=false;this.expandedSqlIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate()),this.requestId&&this.loadTimeline();}async loadTimeline(){if(!this.requestId)return;let t=w.cache.get(this.requestId);if(t){this.data=t;return}this.loading=true;try{let s=await fetch(`${y.activity}?requestId=${this.requestId}`);if(!s.ok){this.failed=!0,this.loading=!1;return}let r=await s.json();if(w.cache.size>=pe){let o=w.cache.keys().next().value;o!==void 0&&w.cache.delete(o);}w.cache.set(this.requestId,r),this.data=r,this.loading=!1;}catch(s){console.debug("[brakit] timeline load failed:",s),this.failed=true,this.loading=false;}}toggleSql(t,s){s.stopPropagation(),this.expandedSqlIdx=this.expandedSqlIdx===t?-1:t;}copySql(t,s){s.stopPropagation(),navigator.clipboard.writeText(t).then(()=>C.show("SQL copied")).catch(()=>C.show("Copy failed"));}render(){if(this.loading)return a`<div class="tl-loading">Loading activity...</div>`;if(this.failed||!this.data||this.data.total===0)return d;let t=this.data,s=t.timeline[0]?.timestamp??0;return a`
|
|
1210
1484
|
<div class="tl-header">
|
|
1211
1485
|
<span class="tl-title">Activity Timeline</span>
|
|
1212
1486
|
<span class="tl-counts">
|
|
1213
|
-
${t.counts.queries>0?
|
|
1214
|
-
${t.counts.fetches>0?
|
|
1215
|
-
${t.counts.logs>0?
|
|
1216
|
-
${t.counts.errors>0?
|
|
1487
|
+
${t.counts.queries>0?a`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:d}
|
|
1488
|
+
${t.counts.fetches>0?a`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:d}
|
|
1489
|
+
${t.counts.logs>0?a`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:d}
|
|
1490
|
+
${t.counts.errors>0?a`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:d}
|
|
1217
1491
|
</span>
|
|
1218
1492
|
</div>
|
|
1219
1493
|
<div class="tl-events">${this.renderTimeline(t.timeline,s)}</div>
|
|
1220
|
-
`}renderTimeline(t,s){let r=new Map,
|
|
1221
|
-
${this.renderEvent(c,
|
|
1494
|
+
`}renderTimeline(t,s){let r=new Map,o=[];for(let l of t){let c=l.type==="query"?l.data.parentFetchId:void 0;if(l.type==="query"&&c){let p=r.get(c);p||(p=[],r.set(c,p)),p.push(l);}else o.push(l);}let n=0;return o.map(l=>{let c=n++,p=l.type==="fetch"?l.data.fetchId:void 0,h=p?r.get(p):void 0;if(h&&h.length>0){let m=h.length;return a`
|
|
1495
|
+
${this.renderEvent(l,c,s)}
|
|
1222
1496
|
<div class="tl-nested">
|
|
1223
|
-
<span class="tl-nested-label">${
|
|
1224
|
-
${
|
|
1497
|
+
<span class="tl-nested-label">${m} nested quer${m===1?"y":"ies"}</span>
|
|
1498
|
+
${h.map(b=>{let $=n++;return this.renderEvent(b,$,s,true)})}
|
|
1225
1499
|
</div>
|
|
1226
|
-
`}return this.renderEvent(c,
|
|
1227
|
-
<div class="tl-event ${
|
|
1228
|
-
style="${
|
|
1229
|
-
@click=${
|
|
1230
|
-
<span class="tl-event-time">${
|
|
1231
|
-
<span class="tl-event-type" style="color:${
|
|
1500
|
+
`}return this.renderEvent(l,c,s)})}renderEvent(t,s,r,o=false){let n=ts[t.type]||"var(--text-dim)",l=es[t.type]||t.type,c="+"+v(Math.round(t.timestamp-r)),p=t.type==="query"?t.data.sql:void 0,h=!!p,m=this.expandedSqlIdx===s;return a`
|
|
1501
|
+
<div class="tl-event ${h?"tl-clickable":""} ${o?"tl-nested-event":""}"
|
|
1502
|
+
style="${h?"":`border-left-color:${n}`}"
|
|
1503
|
+
@click=${h?b=>this.toggleSql(s,b):d}>
|
|
1504
|
+
<span class="tl-event-time">${c}</span>
|
|
1505
|
+
<span class="tl-event-type" style="color:${n}">${l}</span>
|
|
1232
1506
|
${this.renderEventContent(t)}
|
|
1233
|
-
${
|
|
1234
|
-
<div class="tl-event-sql ${
|
|
1235
|
-
<button class="tl-sql-copy" @click=${
|
|
1236
|
-
${
|
|
1507
|
+
${p?a`
|
|
1508
|
+
<div class="tl-event-sql ${m?"open":""}">
|
|
1509
|
+
<button class="tl-sql-copy" @click=${b=>this.copySql(p,b)}>Copy</button>
|
|
1510
|
+
${p}
|
|
1237
1511
|
</div>`:d}
|
|
1238
1512
|
</div>
|
|
1239
|
-
`}renderEventContent(t){switch(t.type){case "fetch":{let s=t.data,r=s.statusCode>=400;return
|
|
1513
|
+
`}renderEventContent(t){switch(t.type){case "fetch":{let s=t.data,r=s.statusCode>=400;return a`
|
|
1240
1514
|
<span class="tl-event-summary">${s.method} ${s.url}</span>
|
|
1241
1515
|
<span class="tl-event-status" style="${r?"color:var(--red)":""}">${s.statusCode}</span>
|
|
1242
|
-
<span class="tl-event-dur">${
|
|
1243
|
-
`}case "query":{let s=t.data,r=(s.normalizedOp||s.operation||"?").toUpperCase(),
|
|
1244
|
-
<span class="tl-event-summary"><span style="color:${
|
|
1245
|
-
<span class="tl-event-dur">${
|
|
1246
|
-
`}case "log":{let s=t.data,r=
|
|
1516
|
+
<span class="tl-event-dur">${v(s.durationMs)}</span>
|
|
1517
|
+
`}case "query":{let s=t.data,r=(s.normalizedOp||s.operation||"?").toUpperCase(),o=s.table||s.model||"",n=Ft[r]||"var(--text-dim)";return a`
|
|
1518
|
+
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${r}</span> ${o}</span>
|
|
1519
|
+
<span class="tl-event-dur">${_r(s.durationMs)}</span>
|
|
1520
|
+
`}case "log":{let s=t.data,r=Ke[s.level]||"var(--text-dim)";return a`<span class="tl-event-summary"><span style="color:${r}">${s.level.toUpperCase()}</span> ${s.message}</span>`}case "error":{let s=t.data;return a`<span class="tl-event-summary" style="color:var(--red)">${s.name}: ${s.message}</span>`}default:return d}}};w.cache=new Map,u([R({context:x})],w.prototype,"store",2),u([T({attribute:"request-id"})],w.prototype,"requestId",2),u([T({attribute:"request-started",type:Number})],w.prototype,"requestStarted",2),u([_()],w.prototype,"data",2),u([_()],w.prototype,"loading",2),u([_()],w.prototype,"failed",2),u([_()],w.prototype,"expandedSqlIdx",2),w=u([g("bk-timeline-panel")],w);var Wt=class{constructor(e,t){this.host=e;this.store=t;this.retryCount=0;e.addController(this);}hostConnected(){this.connect();}hostDisconnected(){this.eventSource?.close(),clearTimeout(this.reloadTimer),clearTimeout(this.perfReloadTimer),clearTimeout(this.reconnectTimer);}connect(){this.eventSource?.close(),this.eventSource=new EventSource(y.events),this.eventSource.onopen=()=>{this.retryCount=0;},this.eventSource.onerror=()=>{this.eventSource?.close(),this.scheduleReconnect();},this.eventSource.onmessage=e=>{let t=JSON.parse(e.data);t.path?.startsWith(k)||(this.store.prependRequest(t),clearTimeout(this.reloadTimer),this.reloadTimer=setTimeout(()=>this.reloadFlows(),300),this.store.state.activeView==="performance"&&(clearTimeout(this.perfReloadTimer),this.perfReloadTimer=setTimeout(()=>this.reloadMetrics(),ue)));},this.eventSource.addEventListener(ee,e=>{this.store.prependFetch(JSON.parse(e.data));}),this.eventSource.addEventListener("log",e=>{this.store.prependLog(JSON.parse(e.data));}),this.eventSource.addEventListener(se,e=>{this.store.prependError(JSON.parse(e.data));}),this.eventSource.addEventListener(re,e=>{this.store.prependQuery(JSON.parse(e.data));}),this.eventSource.addEventListener(oe,e=>{this.store.setIssues(JSON.parse(e.data));});}scheduleReconnect(){if(this.retryCount>=10)return;let e=Math.min(1e3*2**this.retryCount,3e4);this.retryCount++,this.reconnectTimer=setTimeout(()=>this.connect(),e);}async reloadFlows(){try{let t=await(await fetch(y.flows)).json();this.store.setFlows(t.flows);}catch{}}async reloadMetrics(){try{let t=await(await fetch(y.metricsLive)).json();this.store.setMetrics(t.endpoints||[]);}catch{}}};function gs(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>`}function bs(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`}function Es(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>`}function _s(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>`}function Ss(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>`}function $s(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>`}function ys(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>`}function Ts(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>`}function xs(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>`}var X=class extends f{constructor(){super(...arguments);this.store=new Gt;this.activeView="overview";this.viewMode="simple";this.sse=new Wt(this,this.store);}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadInitialData(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}async loadInitialData(){try{let[t,s]=await Promise.all([fetch(y.flows),fetch(y.requests)]),[r,o]=await Promise.all([t.json(),s.json()]);this.store.setFlows(r.flows),this.store.setRequests(o.requests);}catch(t){console.warn("[brakit]",t);}try{let[t,s,r,o,n]=await Promise.all([fetch(y.fetches),fetch(y.errors),fetch(y.logs),fetch(y.queries),fetch(y.metricsLive)]),[l,c,p,h,m]=await Promise.all([t.json(),s.json(),r.json(),o.json(),n.json()]);this.store.setFetches(l.entries),this.store.setErrors(c.entries),this.store.setLogs(p.entries),this.store.setQueries(h.entries),this.store.setMetrics(m.endpoints||[]);}catch(t){console.warn("[brakit]",t);}try{let s=await(await fetch(y.insights)).json();this.store.setIssues(s.issues||[]);}catch(t){console.warn("[brakit]",t);}}switchView(t){t!==this.activeView&&(this.activeView=t,this.store.setActiveView(t),fetch(`${y.tab}?tab=${encodeURIComponent(t)}`).catch(()=>{}),t==="performance"&&this.sse.reloadMetrics());}async handleClear(){confirm("This will clear all data including performance metrics history. Continue?")&&(await fetch(y.clear,{method:"POST"}),this.store.clearAll(),C.show("Cleared"));}handleCopyAsCurl(t){ct(t);}render(){let t=this.store.state,s=t.requests.filter(c=>!c.path?.startsWith(k)),r=s.filter(c=>c.statusCode>=400).length,o=s.length>0?Math.round(s.reduce((c,p)=>c+p.durationMs,0)/s.length):0,n=(t.issues||[]).filter(c=>c.state!=="resolved"&&c.state!=="stale").length,l=window.__BRAKIT_CONFIG__;return a`
|
|
1247
1521
|
<div class="app" id="app">
|
|
1248
1522
|
<aside class="sidebar">
|
|
1249
1523
|
<div class="sidebar-logo">
|
|
1250
1524
|
<span class="logo-text">brakit</span>
|
|
1251
|
-
<span class="logo-version">v${
|
|
1525
|
+
<span class="logo-version">v${l?.version??""}</span>
|
|
1252
1526
|
</div>
|
|
1253
1527
|
<nav class="sidebar-nav">
|
|
1254
|
-
${this.renderSidebarItem("overview","Overview",
|
|
1528
|
+
${this.renderSidebarItem("overview","Overview",gs(),void 0)}
|
|
1255
1529
|
<div class="sidebar-section">Monitor</div>
|
|
1256
|
-
${this.renderSidebarItem("actions","Actions",
|
|
1257
|
-
${this.renderSidebarItem("requests","Requests",
|
|
1258
|
-
${this.renderSidebarItem("fetches","Fetches",
|
|
1530
|
+
${this.renderSidebarItem("actions","Actions",bs(),t.flows.length)}
|
|
1531
|
+
${this.renderSidebarItem("requests","Requests",Es(),s.length)}
|
|
1532
|
+
${this.renderSidebarItem("fetches","Fetches",_s(),t.fetches.length)}
|
|
1259
1533
|
<div class="sidebar-section">Insights</div>
|
|
1260
|
-
${this.renderSidebarItem("queries","Queries",
|
|
1261
|
-
${this.renderSidebarItem("errors","Errors"
|
|
1262
|
-
${this.renderSidebarItem("logs","Logs",
|
|
1263
|
-
${this.renderSidebarItem("security","Security",
|
|
1264
|
-
${this.renderSidebarItem("performance","Performance",
|
|
1534
|
+
${this.renderSidebarItem("queries","Queries",Ss(),t.queries.length)}
|
|
1535
|
+
${this.renderSidebarItem("errors","Errors",$s(),t.errors.length)}
|
|
1536
|
+
${this.renderSidebarItem("logs","Logs",ys(),t.logs.length)}
|
|
1537
|
+
${this.renderSidebarItem("security","Security",Ts(),n,n===0)}
|
|
1538
|
+
${this.renderSidebarItem("performance","Performance",xs(),void 0)}
|
|
1265
1539
|
</nav>
|
|
1266
|
-
<div class="sidebar-footer">:${
|
|
1540
|
+
<div class="sidebar-footer">:${l?.port??""}</div>
|
|
1267
1541
|
</aside>
|
|
1268
1542
|
<div class="main-panel">
|
|
1269
1543
|
<div class="header">
|
|
1270
1544
|
<div class="header-left">
|
|
1271
|
-
<span class="header-title" id="header-title">${
|
|
1272
|
-
<span class="header-sub" id="header-sub">${
|
|
1545
|
+
<span class="header-title" id="header-title">${ie[this.activeView]||this.activeView}</span>
|
|
1546
|
+
<span class="header-sub" id="header-sub">${ne[this.activeView]||""}</span>
|
|
1273
1547
|
</div>
|
|
1274
1548
|
<div class="header-right">
|
|
1275
|
-
${this.activeView==="actions"?
|
|
1549
|
+
${this.activeView==="actions"?a`
|
|
1276
1550
|
<div class="segmented-control" id="mode-toggle">
|
|
1277
1551
|
<button class="segmented-btn ${this.viewMode==="simple"?"active":""}" @click=${()=>{this.viewMode="simple",this.store.setViewMode("simple");}}>Quick</button>
|
|
1278
1552
|
<button class="segmented-btn ${this.viewMode==="detailed"?"active":""}" @click=${()=>{this.viewMode="detailed",this.store.setViewMode("detailed");}}>Detailed</button>
|
|
@@ -1314,18 +1588,18 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1314
1588
|
<span id="stat-total">${s.length} request${s.length!==1?"s":""}</span>
|
|
1315
1589
|
<span id="stat-flows">${t.flows.length} action${t.flows.length!==1?"s":""}</span>
|
|
1316
1590
|
<span id="stat-errors" class="error-count">${r} error${r!==1?"s":""}</span>
|
|
1317
|
-
<span id="stat-avg">Avg: ${
|
|
1591
|
+
<span id="stat-avg">Avg: ${o}ms</span>
|
|
1318
1592
|
</div>
|
|
1319
1593
|
</div>
|
|
1320
1594
|
</div>
|
|
1321
1595
|
<bk-toast></bk-toast>
|
|
1322
|
-
`}renderSidebarItem(t,s,r,
|
|
1596
|
+
`}renderSidebarItem(t,s,r,o,n=false){return a`
|
|
1323
1597
|
<button class="sidebar-item ${this.activeView===t?"active":""}" @click=${()=>this.switchView(t)}>
|
|
1324
1598
|
<span class="item-icon">${r}</span>
|
|
1325
1599
|
<span class="item-label">${s}</span>
|
|
1326
|
-
${
|
|
1600
|
+
${o!==void 0?a`<span class="item-count" style="display:${n?"none":""}">${o}</span>`:d}
|
|
1327
1601
|
</button>
|
|
1328
|
-
`}};u([
|
|
1602
|
+
`}};u([be({context:x})],X.prototype,"store",2),u([_()],X.prototype,"activeView",2),u([_()],X.prototype,"viewMode",2),X=u([g("bk-dashboard")],X);
|
|
1329
1603
|
/*! Bundled license information:
|
|
1330
1604
|
|
|
1331
1605
|
@lit/reactive-element/css-tag.js:
|