adonisjs-server-stats 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/core/index.js +24 -22
  2. package/dist/core/log-utils.d.ts +14 -0
  3. package/dist/core/split-pane.d.ts +18 -0
  4. package/dist/core/trace-utils.d.ts +5 -0
  5. package/dist/core/types.d.ts +1 -1
  6. package/dist/react/{CacheSection-D5J5moz7.js → CacheSection-UCMptWyn.js} +1 -1
  7. package/dist/react/{CacheTab-F1MkWSZl.js → CacheTab-CA8LB1J5.js} +1 -1
  8. package/dist/react/{ConfigSection-DerLBu4o.js → ConfigSection-DfFd-WRq.js} +1 -1
  9. package/dist/react/{ConfigTab-Bsj7v9JW.js → ConfigTab-Bdg8YMer.js} +1 -1
  10. package/dist/react/{CustomPaneTab-gzdtDEvz.js → CustomPaneTab-Bxtv_8Rw.js} +1 -1
  11. package/dist/react/{EmailsSection-ndH3cvJk.js → EmailsSection-CM7stSyh.js} +1 -1
  12. package/dist/react/{EmailsTab-DVPHRx0L.js → EmailsTab-BDhEiomM.js} +1 -1
  13. package/dist/react/{EventsSection-ClIByDSk.js → EventsSection-ByQ-9blq.js} +1 -1
  14. package/dist/react/{EventsTab-CCzWEKrk.js → EventsTab-CMfY98Rl.js} +1 -1
  15. package/dist/react/{JobsSection-CVMyAs7O.js → JobsSection-DF3qEv9O.js} +1 -1
  16. package/dist/react/{JobsTab-CATUyb9V.js → JobsTab-BbrBWIOb.js} +1 -1
  17. package/dist/react/LogsSection-DcFTZY7b.js +227 -0
  18. package/dist/react/LogsTab-CicucmVk.js +103 -0
  19. package/dist/react/{OverviewSection-ae5AO2RG.js → OverviewSection-C4T1ur51.js} +1 -1
  20. package/dist/react/{QueriesSection-DFFr9Tbb.js → QueriesSection-PswteoF9.js} +1 -1
  21. package/dist/react/{QueriesTab-GrHRAREt.js → QueriesTab-osLUWd4L.js} +1 -1
  22. package/dist/react/RelatedLogs-DFDOyUMr.js +40 -0
  23. package/dist/react/RequestsSection-Nag30rEA.js +341 -0
  24. package/dist/react/{RoutesSection-F7nANhF0.js → RoutesSection-BUSkM6PY.js} +1 -1
  25. package/dist/react/{RoutesTab-rugjhCPH.js → RoutesTab-DgVzd2PZ.js} +1 -1
  26. package/dist/react/TimelineTab-Covg5weo.js +220 -0
  27. package/dist/react/{index-DDzo1bZk.js → index-Cflz9Ebj.js} +390 -395
  28. package/dist/react/index.js +1 -1
  29. package/dist/react/react/components/shared/JsonViewer.d.ts +2 -1
  30. package/dist/react/react/components/shared/RelatedLogs.d.ts +7 -0
  31. package/dist/react/style.css +1 -1
  32. package/dist/src/controller/debug_controller.js +1 -1
  33. package/dist/src/dashboard/dashboard_controller.js +13 -0
  34. package/dist/src/dashboard/dashboard_store.d.ts +1 -0
  35. package/dist/src/dashboard/dashboard_store.js +88 -41
  36. package/dist/src/dashboard/migrator.js +6 -0
  37. package/dist/src/data/data_access.d.ts +7 -0
  38. package/dist/src/data/data_access.js +32 -1
  39. package/dist/src/debug/trace_collector.d.ts +1 -1
  40. package/dist/src/debug/trace_collector.js +2 -1
  41. package/dist/src/debug/types.d.ts +4 -0
  42. package/dist/src/edge/client/dashboard.js +2 -2
  43. package/dist/src/edge/client/debug-panel-deferred.js +1 -1
  44. package/dist/src/edge/client-vue/dashboard.js +4 -4
  45. package/dist/src/edge/client-vue/debug-panel-deferred.js +3 -3
  46. package/dist/src/middleware/request_tracking_middleware.d.ts +1 -0
  47. package/dist/src/middleware/request_tracking_middleware.js +3 -1
  48. package/dist/src/provider/server_stats_provider.d.ts +3 -3
  49. package/dist/src/provider/server_stats_provider.js +29 -15
  50. package/dist/src/routes/register_routes.js +7 -2
  51. package/dist/src/styles/components.css +162 -0
  52. package/dist/src/styles/debug-panel.css +9 -0
  53. package/dist/src/types.d.ts +1 -1
  54. package/dist/vue/{CacheSection-DDvJ7bs2.js → CacheSection-oFAJL3mo.js} +2 -2
  55. package/dist/vue/{ConfigSection-GTCrvsPr.js → ConfigSection-BhfJ4KqL.js} +1 -1
  56. package/dist/vue/{EmailsSection-Ct5vsLCc.js → EmailsSection-BcNyhyHs.js} +1 -1
  57. package/dist/vue/{EventsSection-CRVhtagq.js → EventsSection-r60Q5Lmu.js} +2 -2
  58. package/dist/vue/{EventsTab-DQ4Nd6AK.js → EventsTab-BBM7olXF.js} +1 -1
  59. package/dist/vue/{JobsSection-B_wH2Co7.js → JobsSection-BHL-hkQw.js} +2 -2
  60. package/dist/vue/{JobsTab-BCvhOARO.js → JobsTab-WFnxPdN7.js} +1 -1
  61. package/dist/vue/{JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js → JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js} +25 -23
  62. package/dist/vue/LogsSection-DRMGzJmg.js +252 -0
  63. package/dist/vue/LogsTab-Bg3o0Mm6.js +147 -0
  64. package/dist/vue/{OverviewSection-BqSwuMKH.js → OverviewSection-CXh6Ja1B.js} +1 -1
  65. package/dist/vue/{QueriesSection-D4Fs0YH6.js → QueriesSection-IodIsCJ-.js} +1 -1
  66. package/dist/vue/RelatedLogs.vue_vue_type_script_setup_true_lang-CB2_TzYW.js +84 -0
  67. package/dist/vue/RequestsSection-BPuMdmMc.js +401 -0
  68. package/dist/vue/{RoutesSection-Ys5dTzvF.js → RoutesSection-NKo3Rbq3.js} +1 -1
  69. package/dist/vue/TimelineTab-zj5Z5OdT.js +338 -0
  70. package/dist/vue/components/Dashboard/sections/RequestsSection.vue.d.ts +4 -0
  71. package/dist/vue/components/DebugPanel/tabs/TimelineTab.vue.d.ts +4 -0
  72. package/dist/vue/components/shared/JsonViewer.vue.d.ts +3 -0
  73. package/dist/vue/components/{Dashboard/sections/TimelineSection.vue.d.ts → shared/RelatedLogs.vue.d.ts} +5 -6
  74. package/dist/vue/index-Dtgysd26.js +1229 -0
  75. package/dist/vue/index.js +1 -1
  76. package/dist/vue/style.css +1 -1
  77. package/package.json +1 -1
  78. package/dist/react/LogsSection-hAsLaKOC.js +0 -212
  79. package/dist/react/LogsTab-QouH4NPQ.js +0 -88
  80. package/dist/react/RequestsSection-DtwnJOnM.js +0 -209
  81. package/dist/react/TimelineSection-F5ThmTdy.js +0 -158
  82. package/dist/react/TimelineTab-Dvpf-I5C.js +0 -193
  83. package/dist/react/WaterfallChart-Cj73WdfM.js +0 -100
  84. package/dist/react/react/components/Dashboard/sections/TimelineSection.d.ts +0 -8
  85. package/dist/vue/LogsSection-C4NRFOpA.js +0 -227
  86. package/dist/vue/LogsTab-DpEQ7euu.js +0 -122
  87. package/dist/vue/RequestsSection-B0A5SKcM.js +0 -243
  88. package/dist/vue/TimelineSection-D38iHB08.js +0 -186
  89. package/dist/vue/TimelineTab-Db6lKKsD.js +0 -250
  90. package/dist/vue/WaterfallChart.vue_vue_type_script_setup_true_lang-tZ13cNj1.js +0 -118
  91. package/dist/vue/index-Bj6pm5g3.js +0 -1235
@@ -21,6 +21,7 @@ export interface RequestCompleteData {
21
21
  statusCode: number;
22
22
  duration: number;
23
23
  trace?: TraceRecord;
24
+ httpRequestId?: string;
24
25
  }
25
26
  export declare function setOnRequestComplete(fn: ((data: RequestCompleteData) => void) | null): void;
26
27
  export default class RequestTrackingMiddleware {
@@ -112,13 +112,15 @@ export default class RequestTrackingMiddleware {
112
112
  metrics.decrementActiveConnections();
113
113
  metrics.recordRequest(duration, ctx.response.getStatus());
114
114
  if (!skipTracing) {
115
- const traceRecord = traceCollector?.finishTrace(ctx.request.method(), ctx.request.url(true), ctx.response.getStatus());
115
+ const reqId = typeof ctx.request.id === 'function' ? String(ctx.request.id()) : undefined;
116
+ const traceRecord = traceCollector?.finishTrace(ctx.request.method(), ctx.request.url(true), ctx.response.getStatus(), reqId);
116
117
  onRequestCompleteFn?.({
117
118
  method: ctx.request.method(),
118
119
  url: ctx.request.url(true),
119
120
  statusCode: ctx.response.getStatus(),
120
121
  duration,
121
122
  trace: traceRecord ?? undefined,
123
+ httpRequestId: reqId,
122
124
  });
123
125
  }
124
126
  }
@@ -39,9 +39,9 @@ export default class ServerStatsProvider {
39
39
  * Hook into the AdonisJS logger's Pino stream to feed log entries
40
40
  * directly into the LogStreamService — no file path needed.
41
41
  *
42
- * Uses `Symbol.for('pino.stream')` (documented Pino API) to access
43
- * the underlying destination stream, then wraps its `write` method
44
- * to tee entries into the log collector.
42
+ * Uses pino's exported `symbols.streamSym` to access the underlying
43
+ * destination stream, then wraps its `write` method to tee entries
44
+ * into the log collector.
45
45
  */
46
46
  private hookPinoLogger;
47
47
  ready(): Promise<void>;
@@ -244,25 +244,39 @@ export default class ServerStatsProvider {
244
244
  * Hook into the AdonisJS logger's Pino stream to feed log entries
245
245
  * directly into the LogStreamService — no file path needed.
246
246
  *
247
- * Uses `Symbol.for('pino.stream')` (documented Pino API) to access
248
- * the underlying destination stream, then wraps its `write` method
249
- * to tee entries into the log collector.
247
+ * Uses pino's exported `symbols.streamSym` to access the underlying
248
+ * destination stream, then wraps its `write` method to tee entries
249
+ * into the log collector.
250
250
  */
251
251
  async hookPinoLogger() {
252
252
  const logStream = getLogStreamService();
253
253
  if (!logStream)
254
- return; // logCollector() not in the config
254
+ return;
255
255
  let logger;
256
256
  try {
257
257
  logger = await this.app.container.make('logger');
258
258
  }
259
259
  catch {
260
- // Logger not available
260
+ // Logger not available yet
261
261
  }
262
262
  const pino = logger?.pino;
263
263
  if (!pino)
264
264
  return;
265
- const streamSym = Symbol.for('pino.stream');
265
+ // Use pino's exported streamSym NOT Symbol.for('pino.stream').
266
+ // Pino uses a local Symbol('pino.stream'), not a global registry symbol.
267
+ let streamSym;
268
+ try {
269
+ const pinoMod = await import('pino');
270
+ streamSym = pinoMod.default?.symbols?.streamSym;
271
+ }
272
+ catch {
273
+ // pino not directly importable — try finding the symbol on the instance
274
+ }
275
+ if (!streamSym) {
276
+ streamSym = Object.getOwnPropertySymbols(pino).find((s) => s.description === 'pino.stream');
277
+ }
278
+ if (!streamSym)
279
+ return;
266
280
  const rawStream = pino[streamSym];
267
281
  if (!rawStream || typeof rawStream.write !== 'function')
268
282
  return;
@@ -341,7 +355,7 @@ export default class ServerStatsProvider {
341
355
  maxEmails: toolbarConfig.maxEmails ?? 100,
342
356
  slowQueryThresholdMs: toolbarConfig.slowQueryThresholdMs ?? 100,
343
357
  persistDebugData: toolbarConfig.persistDebugData ?? false,
344
- tracing: toolbarConfig.tracing ?? false,
358
+ tracing: toolbarConfig.tracing ?? true,
345
359
  maxTraces: toolbarConfig.maxTraces ?? 200,
346
360
  dashboard: toolbarConfig.dashboard ?? false,
347
361
  dashboardPath: toolbarConfig.dashboardPath ?? '/__stats',
@@ -614,11 +628,11 @@ export default class ServerStatsProvider {
614
628
  const DashboardControllerClass = (await import('../dashboard/dashboard_controller.js')).default;
615
629
  this.dashboardController = new DashboardControllerClass(this.dashboardStore, this.app);
616
630
  // ── Log piping ────────────────────────────────────────────────
617
- // If the log collector is already hooked into Pino (zero-config mode),
618
- // piggyback on it instead of creating a separate file poller.
631
+ // If the Pino stream hook is active, piggyback on it for real-time
632
+ // log persistence. Otherwise fall back to polling the log file.
619
633
  log.info('dashboard: setting up log piping...');
620
634
  const existingLogStream = getLogStreamService();
621
- if (existingLogStream && !existingLogStream['logPath']) {
635
+ if (this.pinoHookActive && existingLogStream && !existingLogStream['logPath']) {
622
636
  // Stream mode — add a listener for dashboard persistence
623
637
  const origOnEntry = existingLogStream['onEntry'];
624
638
  existingLogStream['onEntry'] = (entry) => {
@@ -638,7 +652,7 @@ export default class ServerStatsProvider {
638
652
  const debugStore = this.debugStore;
639
653
  const dashStore = this.dashboardStore;
640
654
  let lastQueryId = 0;
641
- setOnRequestComplete(({ method, url, statusCode, duration, trace }) => {
655
+ setOnRequestComplete(({ method, url, statusCode, duration, trace, httpRequestId }) => {
642
656
  if (!dashStore.isReady())
643
657
  return;
644
658
  // O(K) collection of new queries since last seen ID — avoids
@@ -655,6 +669,7 @@ export default class ServerStatsProvider {
655
669
  duration,
656
670
  queries: newQueries,
657
671
  trace: trace ?? null,
672
+ httpRequestId: httpRequestId ?? null,
658
673
  });
659
674
  });
660
675
  // ── Transmit streaming for real-time dashboard updates ────────
@@ -783,10 +798,9 @@ export default class ServerStatsProvider {
783
798
  // Silently ignore broadcast errors
784
799
  }
785
800
  };
786
- // If logCollector() is already hooked into Pino (zero-config),
787
- // piggyback on its stream instead of creating a file poller.
801
+ // If the Pino stream hook is active, piggyback on its onEntry chain.
788
802
  const existing = getLogStreamService();
789
- if (existing) {
803
+ if (this.pinoHookActive && existing) {
790
804
  const internal = existing;
791
805
  const origOnEntry = internal.onEntry;
792
806
  internal.onEntry = (entry) => {
@@ -961,7 +975,7 @@ export default class ServerStatsProvider {
961
975
  maxEmails: toolbarConfig?.maxEmails ?? 100,
962
976
  maxTraces: toolbarConfig?.maxTraces ?? 200,
963
977
  slowQueryThresholdMs: toolbarConfig?.slowQueryThresholdMs ?? 100,
964
- tracing: toolbarConfig?.tracing ?? false,
978
+ tracing: toolbarConfig?.tracing ?? true,
965
979
  dashboard: toolbarConfig?.dashboard ?? false,
966
980
  dashboardPath: toolbarConfig?.dashboardPath ?? '/__stats',
967
981
  debugEndpoint: toolbarConfig?.debugEndpoint ?? '/admin/api/debug',
@@ -95,9 +95,11 @@ export function registerAllRoutes(options) {
95
95
  router.get('/config', bindDebug('config')).as('server-stats.debug.config');
96
96
  router.get('/diagnostics', bindDebug('diagnostics')).as('server-stats.debug.diagnostics');
97
97
  // Data endpoints — unified through ApiController
98
- // Debug panel always reads from ring buffers (source: 'memory')
98
+ // Queries, events, and traces read from ring buffers (source: 'memory')
99
99
  // because SQLite column names (snake_case) differ from the
100
100
  // camelCase QueryRecord/EventRecord/etc. shapes the frontend expects.
101
+ // Logs and emails use 'auto' (SQLite when available) since the
102
+ // frontend resolvers handle both snake_case and camelCase fields.
101
103
  router
102
104
  .get('/queries', bindApi(async (api, ctx) => {
103
105
  const queries = await api.getQueries({ source: 'memory' });
@@ -119,7 +121,10 @@ export function registerAllRoutes(options) {
119
121
  .as('server-stats.debug.routes');
120
122
  router
121
123
  .get('/logs', bindApi(async (api, ctx) => {
122
- const result = await api.getLogs({ source: 'memory' });
124
+ // Request up to 200 entries so the debug panel frontend
125
+ // (which caps at `.slice(0, 200)`) always has enough
126
+ // recent logs instead of the default 50-row page.
127
+ const result = await api.getLogs({ source: 'auto', perPage: 200 });
123
128
  return ctx.response.json(result.data);
124
129
  }))
125
130
  .as('server-stats.debug.logs');
@@ -522,6 +522,87 @@
522
522
  word-break: break-word;
523
523
  }
524
524
 
525
+ /* Expandable log row */
526
+ .ss-log-entry-expandable,
527
+ .ss-dash-log-entry-expandable,
528
+ .ss-dbg-log-entry-expandable {
529
+ cursor: pointer;
530
+ }
531
+ .ss-log-entry-expandable:hover,
532
+ .ss-dash-log-entry-expandable:hover,
533
+ .ss-dbg-log-entry-expandable:hover {
534
+ background: var(--ss-surface-alt);
535
+ }
536
+ .ss-log-expand-icon,
537
+ .ss-dash-log-expand-icon,
538
+ .ss-dbg-log-expand-icon {
539
+ flex-shrink: 0;
540
+ font-size: 10px;
541
+ color: var(--ss-dim);
542
+ width: 14px;
543
+ text-align: center;
544
+ transition: transform 0.15s ease;
545
+ user-select: none;
546
+ }
547
+ .ss-log-expand-icon-open,
548
+ .ss-dash-log-expand-icon-open,
549
+ .ss-dbg-log-expand-icon-open {
550
+ transform: rotate(90deg);
551
+ }
552
+ .ss-log-detail,
553
+ .ss-dash-log-detail,
554
+ .ss-dbg-log-detail {
555
+ padding: 6px var(--ss-log-px, 12px) 10px var(--ss-log-px, 12px);
556
+ border-bottom: 1px solid var(--ss-log-border, var(--ss-input-bg));
557
+ background: var(--ss-surface-alt);
558
+ font-size: 11px;
559
+ }
560
+ .ss-log-detail .ss-dash-data-cell,
561
+ .ss-log-detail .ss-dbg-data-cell,
562
+ .ss-dash-log-detail .ss-dash-data-cell,
563
+ .ss-dbg-log-detail .ss-dbg-data-cell {
564
+ width: 100%;
565
+ max-width: none;
566
+ }
567
+ .ss-log-detail .ss-dash-data-full,
568
+ .ss-log-detail .ss-dbg-data-full,
569
+ .ss-dash-log-detail .ss-dash-data-full,
570
+ .ss-dbg-log-detail .ss-dbg-data-full {
571
+ position: relative;
572
+ margin: 0;
573
+ padding: 8px 12px;
574
+ border-radius: 4px;
575
+ background: var(--ss-bg);
576
+ border: 1px solid var(--ss-border-dim);
577
+ width: 100%;
578
+ box-sizing: border-box;
579
+ cursor: pointer;
580
+ }
581
+ .ss-log-detail .ss-dash-data-full pre,
582
+ .ss-log-detail .ss-dbg-data-full pre,
583
+ .ss-dash-log-detail .ss-dash-data-full pre,
584
+ .ss-dbg-log-detail .ss-dbg-data-full pre {
585
+ margin: 0;
586
+ white-space: pre-wrap;
587
+ word-break: break-all;
588
+ font-size: 11px;
589
+ line-height: 1.5;
590
+ color: var(--ss-text-secondary);
591
+ }
592
+ .ss-log-detail .ss-dash-copy-btn,
593
+ .ss-log-detail .ss-dbg-copy-btn,
594
+ .ss-dash-log-detail .ss-dash-copy-btn,
595
+ .ss-dbg-log-detail .ss-dbg-copy-btn {
596
+ position: absolute;
597
+ top: 4px;
598
+ right: 4px;
599
+ padding: 2px 8px;
600
+ font-size: 10px;
601
+ border: 1px solid var(--ss-border-dim);
602
+ border-radius: 3px;
603
+ background: var(--ss-surface-alt);
604
+ }
605
+
525
606
  /* ── 4. Email preview ────────────────────────────────────────── */
526
607
  /* Dashboard uses 16px padding, debug uses 12px — override via
527
608
  * --ss-email-px on a wrapper. */
@@ -1049,3 +1130,84 @@ tr:hover .ss-dbg-copy-row-btn,
1049
1130
  .ss-dbg-btn-group .ss-dbg-btn:last-child {
1050
1131
  border-radius: 0 4px 4px 0;
1051
1132
  }
1133
+
1134
+ /* ── 8. Split pane layout ────────────────────────────────────── */
1135
+ .ss-split-container,
1136
+ .ss-dash-split-container,
1137
+ .ss-dbg-split-container {
1138
+ display: flex;
1139
+ flex-direction: column;
1140
+ flex: 1;
1141
+ min-height: 0;
1142
+ overflow: hidden;
1143
+ }
1144
+ .ss-split-top,
1145
+ .ss-dash-split-top,
1146
+ .ss-dbg-split-top {
1147
+ flex: 0.6;
1148
+ min-height: 60px;
1149
+ overflow: auto;
1150
+ }
1151
+ .ss-split-handle,
1152
+ .ss-dash-split-handle,
1153
+ .ss-dbg-split-handle {
1154
+ height: 6px;
1155
+ background: var(--ss-border-dim);
1156
+ cursor: row-resize;
1157
+ flex-shrink: 0;
1158
+ position: relative;
1159
+ user-select: none;
1160
+ touch-action: none;
1161
+ }
1162
+ .ss-split-handle:hover,
1163
+ .ss-dash-split-handle:hover,
1164
+ .ss-dbg-split-handle:hover {
1165
+ background: var(--ss-accent, #6d28d9);
1166
+ opacity: 0.8;
1167
+ }
1168
+ .ss-split-handle::after,
1169
+ .ss-dash-split-handle::after,
1170
+ .ss-dbg-split-handle::after {
1171
+ content: '';
1172
+ position: absolute;
1173
+ left: 50%;
1174
+ top: 50%;
1175
+ transform: translate(-50%, -50%);
1176
+ width: 32px;
1177
+ height: 2px;
1178
+ background: var(--ss-muted);
1179
+ border-radius: 1px;
1180
+ opacity: 0.5;
1181
+ }
1182
+ .ss-split-bottom,
1183
+ .ss-dash-split-bottom,
1184
+ .ss-dbg-split-bottom {
1185
+ flex: 0.4;
1186
+ min-height: 60px;
1187
+ overflow: auto;
1188
+ border-top: 1px solid var(--ss-border-dim);
1189
+ }
1190
+ .ss-related-logs-title,
1191
+ .ss-dash-related-logs-title,
1192
+ .ss-dbg-related-logs-title {
1193
+ position: sticky;
1194
+ top: 0;
1195
+ z-index: 2;
1196
+ display: flex;
1197
+ align-items: center;
1198
+ gap: 6px;
1199
+ padding: 6px 12px;
1200
+ font-size: 10px;
1201
+ font-weight: 600;
1202
+ text-transform: uppercase;
1203
+ letter-spacing: 0.05em;
1204
+ color: var(--ss-muted);
1205
+ background: var(--ss-surface);
1206
+ border-bottom: 1px solid var(--ss-border-faint);
1207
+ }
1208
+ .ss-related-logs-count,
1209
+ .ss-dash-related-logs-count,
1210
+ .ss-dbg-related-logs-count {
1211
+ font-weight: 400;
1212
+ color: var(--ss-dim);
1213
+ }
@@ -432,6 +432,15 @@
432
432
  }
433
433
 
434
434
  /* Timeline base → components.css */
435
+ /* Trace detail wrapper: flex column to let split pane fill available height */
436
+ .ss-dbg-tl-detail-wrapper {
437
+ display: flex;
438
+ flex-direction: column;
439
+ height: 100%;
440
+ min-height: 0;
441
+ overflow: hidden;
442
+ }
443
+
435
444
  /* Debug-specific timeline overrides */
436
445
  .ss-dbg-tl-detail-url {
437
446
  color: var(--ss-text);
@@ -237,7 +237,7 @@ export interface DevToolbarOptions {
237
237
  * of all operations (DB queries, events, mail) that occurred
238
238
  * during the request.
239
239
  *
240
- * @default false
240
+ * @default true
241
241
  */
242
242
  tracing?: boolean;
243
243
  /**
@@ -1,9 +1,9 @@
1
1
  import { defineComponent as E, inject as v, ref as h, computed as C, openBlock as a, createElementBlock as l, createElementVNode as e, toDisplayString as c, createCommentVNode as g, createVNode as F, unref as y, Fragment as N, renderList as A, withModifiers as B, createBlock as U } from "vue";
2
- import { u as H } from "./index-Bj6pm5g3.js";
2
+ import { u as H } from "./index-Dtgysd26.js";
3
3
  import { u as M } from "./useResizableTable-BoivAevK.js";
4
4
  import { DashboardApi as j, formatCacheSize as q, formatTtl as I } from "adonisjs-server-stats/core";
5
5
  import { u as G } from "./useApiClient-BQQ9CF-q.js";
6
- import { _ as J } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js";
6
+ import { _ as J } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
7
7
  import { _ as O } from "./FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js";
8
8
  const P = {
9
9
  key: 0,
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as re, inject as m, ref as w, computed as x, openBlock as i, createElementBlock as r, createElementVNode as n, normalizeClass as a, createCommentVNode as b, Fragment as _, toDisplayString as u, unref as c, renderList as A, withModifiers as I, normalizeStyle as R } from "vue";
2
- import { u as ue } from "./index-Bj6pm5g3.js";
2
+ import { u as ue } from "./index-Dtgysd26.js";
3
3
  import { isRedactedValue as d, flattenConfig as z, TAB_ICONS as g, countLeaves as ce, collectTopLevelObjectKeys as pe, copyWithFeedback as de, formatFlatValue as ve } from "adonisjs-server-stats/core";
4
4
  const fe = { style: { position: "relative", flex: 1 } }, he = ["value"], ge = ["title", "onClick"], ye = ["viewBox", "innerHTML"], be = ["viewBox", "innerHTML"], $e = ["onClick"], we = { key: 0 }, xe = ["title", "onClick"], ke = ["viewBox", "innerHTML"], Ce = ["viewBox", "innerHTML"], _e = ["onClick"], Se = { key: 0 }, Be = { style: { padding: "4px 16px", fontSize: "10px", color: "var(--ss-muted)" } }, Le = ["onClick"], He = ["title"], Te = ["title"], je = ["title", "onClick"], Me = ["viewBox", "innerHTML"], me = ["viewBox", "innerHTML"], Ae = ["onClick"], Ie = ["title", "onClick"], Ee = ["viewBox", "innerHTML"], Ve = ["viewBox", "innerHTML"], Oe = {
5
5
  key: 1,
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as V, inject as h, ref as f, computed as A, openBlock as o, createElementBlock as i, createElementVNode as e, Fragment as c, createTextVNode as u, toDisplayString as n, createCommentVNode as y, normalizeClass as _, createVNode as F, unref as r, renderList as B, createBlock as D } from "vue";
2
2
  import { formatTime as R, timeAgo as z } from "adonisjs-server-stats/core";
3
- import { u as L } from "./index-Bj6pm5g3.js";
3
+ import { u as L } from "./index-Dtgysd26.js";
4
4
  import { u as M } from "./useResizableTable-BoivAevK.js";
5
5
  import { _ as U } from "./FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js";
6
6
  import { _ as q } from "./PaginationControls.vue_vue_type_script_setup_true_lang-CuN7g_8Z.js";
@@ -1,10 +1,10 @@
1
1
  import { defineComponent as E, inject as i, ref as p, computed as P, openBlock as s, createElementBlock as l, createVNode as v, unref as a, Fragment as h, createElementVNode as e, renderList as T, toDisplayString as c, createBlock as C, createCommentVNode as D } from "vue";
2
2
  import { formatTime as S, timeAgo as V } from "adonisjs-server-stats/core";
3
- import { u as $ } from "./index-Bj6pm5g3.js";
3
+ import { u as $ } from "./index-Dtgysd26.js";
4
4
  import { u as A } from "./useResizableTable-BoivAevK.js";
5
5
  import { _ as B } from "./FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js";
6
6
  import { _ as R } from "./PaginationControls.vue_vue_type_script_setup_true_lang-CuN7g_8Z.js";
7
- import { _ as F } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js";
7
+ import { _ as F } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
8
8
  const L = {
9
9
  key: 0,
10
10
  class: "ss-dash-empty"
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as _, ref as f, computed as v, openBlock as n, createElementBlock as r, createElementVNode as e, withDirectives as g, vModelText as y, toDisplayString as o, Fragment as k, renderList as w, createTextVNode as x, unref as d, createCommentVNode as T, createVNode as C } from "vue";
2
2
  import { TAB_ICONS as h, formatTime as A, timeAgo as B } from "adonisjs-server-stats/core";
3
3
  import { u as L } from "./useResizableTable-BoivAevK.js";
4
- import { _ as N } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js";
4
+ import { _ as N } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
5
5
  const V = { class: "ss-dbg-search-bar" }, E = { class: "ss-dbg-summary" }, $ = {
6
6
  key: 0,
7
7
  class: "ss-dbg-empty"
@@ -1,8 +1,8 @@
1
1
  import { defineComponent as L, inject as h, ref as p, computed as g, openBlock as o, createElementBlock as d, createElementVNode as s, toDisplayString as n, createCommentVNode as v, createVNode as k, unref as a, withCtx as z, Fragment as _, renderList as x, normalizeClass as w, withModifiers as K, createBlock as M } from "vue";
2
2
  import { extractJobs as O, extractJobStats as Q, JOB_STATUS_FILTERS as W, getJobStatusBadgeColor as q, formatDuration as G, formatTime as H, timeAgo as X } from "adonisjs-server-stats/core";
3
- import { u as Y } from "./index-Bj6pm5g3.js";
3
+ import { u as Y } from "./index-Dtgysd26.js";
4
4
  import { u as Z } from "./useResizableTable-BoivAevK.js";
5
- import { _ as I } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js";
5
+ import { _ as I } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
6
6
  import { _ as j } from "./FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js";
7
7
  import { _ as ss } from "./PaginationControls.vue_vue_type_script_setup_true_lang-CuN7g_8Z.js";
8
8
  const ts = {
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as T, ref as j, computed as c, openBlock as l, createElementBlock as o, createElementVNode as s, toDisplayString as a, Fragment as _, renderList as g, unref as i, normalizeClass as m, createVNode as D, createCommentVNode as R } from "vue";
2
2
  import { JOB_STATUS_FILTERS as S, getJobStatusCssClass as x, formatDuration as A, formatTime as J, timeAgo as N } from "adonisjs-server-stats/core";
3
3
  import { u as F } from "./useResizableTable-BoivAevK.js";
4
- import { _ as $ } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js";
4
+ import { _ as $ } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
5
5
  const B = { class: "ss-dbg-job-stats-area" }, E = { class: "ss-dbg-job-stats" }, V = { class: "ss-dbg-job-stat" }, w = { class: "ss-dbg-job-stat-value" }, z = { class: "ss-dbg-job-stat" }, I = { class: "ss-dbg-job-stat-value" }, L = { class: "ss-dbg-job-stat" }, q = { class: "ss-dbg-job-stat-value" }, O = { class: "ss-dbg-job-stat" }, P = { class: "ss-dbg-job-stat-value" }, U = { class: "ss-dbg-job-stat" }, W = { class: "ss-dbg-job-stat-value ss-dbg-c-red" }, G = { class: "ss-dbg-log-filters" }, H = ["onClick"], K = {
6
6
  key: 0,
7
7
  class: "ss-dbg-empty"
@@ -1,14 +1,15 @@
1
- import { defineComponent as y, ref as x, computed as c, openBlock as r, createElementBlock as o, normalizeClass as l, createElementVNode as s, toDisplayString as v, withModifiers as g, createCommentVNode as k } from "vue";
1
+ import { defineComponent as x, ref as k, computed as i, openBlock as t, createElementBlock as n, normalizeClass as l, toDisplayString as c, createCommentVNode as v, createElementVNode as d, withModifiers as g } from "vue";
2
2
  import { compactPreview as p } from "adonisjs-server-stats/core";
3
- const P = /* @__PURE__ */ y({
3
+ const P = /* @__PURE__ */ x({
4
4
  __name: "JsonViewer",
5
5
  props: {
6
6
  value: {},
7
7
  maxLen: {},
8
- classPrefix: { default: "ss-dash" }
8
+ classPrefix: { default: "ss-dash" },
9
+ defaultExpanded: { type: Boolean, default: !1 }
9
10
  },
10
- setup(a) {
11
- const e = a, t = x(!1), f = c(() => {
11
+ setup(r) {
12
+ const e = r, a = k(e.defaultExpanded), f = i(() => {
12
13
  if (e.value === null || e.value === void 0) return "-";
13
14
  if (typeof e.value == "string")
14
15
  try {
@@ -17,7 +18,7 @@ const P = /* @__PURE__ */ y({
17
18
  return e.value.length > 100 ? e.value.slice(0, 100) + "..." : e.value;
18
19
  }
19
20
  return p(e.value, e.maxLen || 100);
20
- }), i = c(() => {
21
+ }), u = i(() => {
21
22
  if (e.value === null || e.value === void 0) return "";
22
23
  if (typeof e.value == "string")
23
24
  try {
@@ -27,38 +28,39 @@ const P = /* @__PURE__ */ y({
27
28
  }
28
29
  return JSON.stringify(e.value, null, 2);
29
30
  });
30
- function n() {
31
- t.value = !t.value;
31
+ function o() {
32
+ a.value = !a.value;
32
33
  }
33
- function d() {
34
- navigator.clipboard?.writeText(i.value);
34
+ function m() {
35
+ navigator.clipboard?.writeText(u.value);
35
36
  }
36
- return (C, u) => a.value === null || a.value === void 0 ? (r(), o("span", {
37
+ return (C, s) => r.value === null || r.value === void 0 ? (t(), n("span", {
37
38
  key: 0,
38
39
  class: l(`ss-dim ${e.classPrefix}-c-dim`)
39
- }, "-", 2)) : (r(), o("div", {
40
+ }, "-", 2)) : (t(), n("div", {
40
41
  key: 1,
41
42
  class: l(`${e.classPrefix}-data-cell`)
42
43
  }, [
43
- s("span", {
44
+ a.value ? v("", !0) : (t(), n("span", {
45
+ key: 0,
44
46
  class: l(`${e.classPrefix}-data-preview`),
45
47
  role: "button",
46
48
  tabindex: 0,
47
- onClick: n,
48
- onKeydown: u[0] || (u[0] = (m) => m.key === "Enter" && n())
49
- }, v(f.value), 35),
50
- t.value ? (r(), o("div", {
51
- key: 0,
49
+ onClick: o,
50
+ onKeydown: s[0] || (s[0] = (y) => y.key === "Enter" && o())
51
+ }, c(f.value), 35)),
52
+ a.value ? (t(), n("div", {
53
+ key: 1,
52
54
  class: l(`${e.classPrefix}-data-full`),
53
- onClick: n
55
+ onClick: o
54
56
  }, [
55
- s("button", {
57
+ d("button", {
56
58
  class: l(`${e.classPrefix}-copy-btn`),
57
59
  title: "Copy to clipboard",
58
- onClick: g(d, ["stop"])
60
+ onClick: g(m, ["stop"])
59
61
  }, " Copy ", 2),
60
- s("pre", null, v(i.value), 1)
61
- ], 2)) : k("", !0)
62
+ d("pre", null, c(u.value), 1)
63
+ ], 2)) : v("", !0)
62
64
  ], 2));
63
65
  }
64
66
  });