adonisjs-server-stats 1.11.7 → 1.12.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 (69) hide show
  1. package/dist/core/explain-utils.d.ts +77 -0
  2. package/dist/core/index.d.ts +8 -2
  3. package/dist/core/index.js +1089 -877
  4. package/dist/core/queries-columns.d.ts +42 -0
  5. package/dist/core/queries-controller.d.ts +94 -0
  6. package/dist/core/query-utils.d.ts +49 -0
  7. package/dist/react/{CacheSection-DOUnyUrw.js → CacheSection-DZFOiOYH.js} +1 -1
  8. package/dist/react/{CacheTab-D9o6bsPc.js → CacheTab-D8js5o69.js} +1 -1
  9. package/dist/react/{ConfigSection-DzNC7pOJ.js → ConfigSection-DoJmvNkF.js} +1 -1
  10. package/dist/react/{ConfigTab-CT-Tu-ct.js → ConfigTab-CDvljuBF.js} +1 -1
  11. package/dist/react/{CustomPaneTab-CvIPe7NP.js → CustomPaneTab-Db9eIQ-9.js} +1 -1
  12. package/dist/react/{EmailsSection-B1LT7Nx3.js → EmailsSection-D7oxvAjA.js} +1 -1
  13. package/dist/react/{EmailsTab-DKxK322z.js → EmailsTab-BITuGTQm.js} +1 -1
  14. package/dist/react/{EventsSection-D3AK0mhw.js → EventsSection-7mDZv70I.js} +1 -1
  15. package/dist/react/{EventsTab-BRRSW6RI.js → EventsTab-DF13BChE.js} +1 -1
  16. package/dist/react/{JobsSection-BMvj5886.js → JobsSection-B_VJFWy6.js} +1 -1
  17. package/dist/react/{JobsTab-CQXWCrl8.js → JobsTab-DK8Fl9nz.js} +1 -1
  18. package/dist/react/{LogEntryRow-DFI52ZEw.js → LogEntryRow-Bdie9SFe.js} +1 -1
  19. package/dist/react/{LogsSection-CW2hQ976.js → LogsSection-CrWqxWRr.js} +2 -2
  20. package/dist/react/{LogsTab-CV4Gf_yb.js → LogsTab-DYSpL637.js} +2 -2
  21. package/dist/react/{OverviewSection-CxF9cabq.js → OverviewSection-CuaccMZt.js} +1 -1
  22. package/dist/react/QueriesContent-BDIx8zFn.js +288 -0
  23. package/dist/react/QueriesSection-CvGGh5Ij.js +117 -0
  24. package/dist/react/QueriesTab-C7CCwhVc.js +87 -0
  25. package/dist/react/{RequestsSection-Cb5YeqvI.js → RequestsSection-mENyeNsk.js} +2 -2
  26. package/dist/react/{RoutesSection-B43olD9v.js → RoutesSection-DjovlaNS.js} +1 -1
  27. package/dist/react/{RoutesTab-RfhCUbkx.js → RoutesTab-CbQECiUc.js} +1 -1
  28. package/dist/react/{SplitPaneWrapper-B05Mg6Sg.js → SplitPaneWrapper-DyRyH5Re.js} +1 -1
  29. package/dist/react/{TimelineTab-Dx4686Ti.js → TimelineTab-BKTFbsPe.js} +2 -2
  30. package/dist/react/components/shared/QueriesContent.d.ts +33 -0
  31. package/dist/react/{index-C4EMJrkH.js → index-CAUHa8QX.js} +2 -2
  32. package/dist/react/index.js +1 -1
  33. package/dist/react/style.css +1 -1
  34. package/dist/src/dashboard/query_explain_handler.d.ts +11 -0
  35. package/dist/src/dashboard/query_explain_handler.js +64 -11
  36. package/dist/src/debug/query_collector.d.ts +1 -0
  37. package/dist/src/debug/query_collector.js +3 -0
  38. package/dist/src/edge/client/dashboard.js +2 -2
  39. package/dist/src/edge/client/debug-panel-deferred.js +1 -1
  40. package/dist/src/edge/client-vue/dashboard.js +4 -4
  41. package/dist/src/edge/client-vue/debug-panel-deferred.js +2 -2
  42. package/dist/src/provider/server_stats_provider.js +2 -0
  43. package/dist/src/routes/debug_routes.d.ts +4 -0
  44. package/dist/src/routes/debug_routes.js +45 -0
  45. package/dist/src/routes/register_routes.d.ts +4 -0
  46. package/dist/src/routes/register_routes.js +2 -0
  47. package/dist/src/styles/components.css +238 -0
  48. package/dist/src/styles/dashboard.css +6 -173
  49. package/dist/src/styles/debug-panel.css +2 -24
  50. package/dist/vue/{CacheSection-BAotiuQq.js → CacheSection-Bx41lpfK.js} +1 -1
  51. package/dist/vue/{ConfigSection-JZjK5E5F.js → ConfigSection-fkfUdCmx.js} +1 -1
  52. package/dist/vue/{EmailsSection-BLwyQO7B.js → EmailsSection-CbcDKF9Q.js} +1 -1
  53. package/dist/vue/{EventsSection-BUEwO-0A.js → EventsSection-CWbTYOBi.js} +1 -1
  54. package/dist/vue/{JobsSection-21A0yQMq.js → JobsSection-CGGO6rtS.js} +1 -1
  55. package/dist/vue/{LogsSection-BmOx8SNa.js → LogsSection-BPB-C-XA.js} +1 -1
  56. package/dist/vue/{OverviewSection-BRDK3Ony.js → OverviewSection-BTcnPeoM.js} +1 -1
  57. package/dist/vue/QueriesSection-2SE3igQJ.js +411 -0
  58. package/dist/vue/QueriesTab-DO5XwJ--.js +112 -0
  59. package/dist/vue/{RequestsSection-DLrjCfcE.js → RequestsSection-DXcG5Zok.js} +1 -1
  60. package/dist/vue/{RoutesSection-CPr9w42B.js → RoutesSection-BkNGWdoP.js} +1 -1
  61. package/dist/vue/components/Dashboard/sections/QueriesSection.vue.d.ts +1 -41
  62. package/dist/vue/{index-CYaqRGIT.js → index-CptF6Qbp.js} +2 -2
  63. package/dist/vue/index.js +1 -1
  64. package/dist/vue/style.css +1 -1
  65. package/package.json +1 -1
  66. package/dist/react/QueriesSection-CZJ-imAb.js +0 -474
  67. package/dist/react/QueriesTab-CQHa1ck3.js +0 -97
  68. package/dist/vue/QueriesSection-D2WGsuR4.js +0 -444
  69. package/dist/vue/QueriesTab-6D_xfi7Q.js +0 -114
@@ -88,6 +88,8 @@ export default class ServerStatsProvider {
88
88
  getStatsController: () => this.statsController,
89
89
  getDebugController: () => this.debugController,
90
90
  getDashboardController: () => this.dashboardController,
91
+ getDebugStore: () => this.debugStore,
92
+ getApp: () => this.app,
91
93
  statsEndpoint,
92
94
  debugEndpoint,
93
95
  dashboardPath,
@@ -1,12 +1,16 @@
1
1
  import type { ApiController } from '../controller/api_controller.js';
2
2
  import type DebugController from '../controller/debug_controller.js';
3
+ import type { DebugStore } from '../debug/debug_store.js';
3
4
  import type { AdonisRouter } from './router_types.js';
4
5
  import type { HttpContext } from '@adonisjs/core/http';
6
+ import type { ApplicationService } from '@adonisjs/core/types';
5
7
  interface DebugRoutesOpts {
6
8
  router: AdonisRouter;
7
9
  debugEndpoint: string;
8
10
  getDebugController: () => DebugController | null;
9
11
  getApiController: () => ApiController | null;
12
+ getDebugStore?: () => DebugStore | null;
13
+ getApp?: () => ApplicationService | null;
10
14
  middleware: Array<(ctx: HttpContext, next: () => Promise<void>) => Promise<void>>;
11
15
  whenReady?: () => Promise<void>;
12
16
  }
@@ -1,3 +1,4 @@
1
+ import { getAppDbClient, buildExplainSql, extractPlan } from '../dashboard/query_explain_handler.js';
1
2
  let _whenReady;
2
3
  function bindDebug(getController, method) {
3
4
  return async (ctx) => {
@@ -89,6 +90,49 @@ function registerDebugTraceRoutes(router, getApiController) {
89
90
  }))
90
91
  .as('server-stats.debug.traceDetail');
91
92
  }
93
+ function registerDebugExplainRoute(router, getDebugStore, getApp) {
94
+ if (!getDebugStore || !getApp)
95
+ return;
96
+ router
97
+ .get('/queries/:id/explain', async (ctx) => {
98
+ if (_whenReady)
99
+ await _whenReady();
100
+ const store = getDebugStore();
101
+ const app = getApp();
102
+ if (!store || !app) {
103
+ return ctx.response.serviceUnavailable({ error: 'Debug store not ready' });
104
+ }
105
+ const query = store.queries.getQueryById(Number(ctx.params.id));
106
+ if (!query)
107
+ return ctx.response.notFound({ error: 'Query not found' });
108
+ if (!query.sql.trim().toUpperCase().startsWith('SELECT')) {
109
+ return ctx.response.badRequest({ error: 'EXPLAIN is only supported for SELECT queries' });
110
+ }
111
+ try {
112
+ const appDb = await getAppDbClient(app, query.connection || undefined);
113
+ if (!appDb) {
114
+ return ctx.response.serviceUnavailable({ error: 'App database connection not available' });
115
+ }
116
+ const bindings = Array.isArray(query.bindings) ? query.bindings : [];
117
+ const explainSql = buildExplainSql(query.sql, appDb.dialect);
118
+ if (!explainSql) {
119
+ return ctx.response.badRequest({
120
+ error: `EXPLAIN is not supported for ${appDb.dialect} databases`,
121
+ });
122
+ }
123
+ const explainResult = await appDb.raw(explainSql, bindings);
124
+ const plan = extractPlan(explainResult, appDb.dialect);
125
+ return ctx.response.json({ queryId: query.id, sql: query.sql, plan });
126
+ }
127
+ catch (error) {
128
+ return ctx.response.internalServerError({
129
+ error: 'EXPLAIN failed',
130
+ message: error?.message ?? 'Unknown error',
131
+ });
132
+ }
133
+ })
134
+ .as('server-stats.debug.queryExplain');
135
+ }
92
136
  /** Register debug panel API routes. */
93
137
  export function registerDebugRoutes(opts) {
94
138
  const { router, debugEndpoint, getDebugController, getApiController, middleware } = opts;
@@ -99,6 +143,7 @@ export function registerDebugRoutes(opts) {
99
143
  .group(() => {
100
144
  registerDebugConfigRoutes(router, getDebugController);
101
145
  registerDebugQueryAndEventRoutes(router, getApiController);
146
+ registerDebugExplainRoute(router, opts.getDebugStore, opts.getApp);
102
147
  registerDebugLogRoutes(router, getApiController);
103
148
  registerDebugEmailRoutes(router, getApiController);
104
149
  registerDebugTraceRoutes(router, getApiController);
@@ -1,9 +1,11 @@
1
1
  import type { ApiController } from '../controller/api_controller.js';
2
2
  import type DebugController from '../controller/debug_controller.js';
3
+ import type { DebugStore } from '../debug/debug_store.js';
3
4
  import type ServerStatsController from '../controller/server_stats_controller.js';
4
5
  import type DashboardController from '../dashboard/dashboard_controller.js';
5
6
  import type { AdonisRouter } from './router_types.js';
6
7
  import type { HttpContext } from '@adonisjs/core/http';
8
+ import type { ApplicationService } from '@adonisjs/core/types';
7
9
  /**
8
10
  * Options for the unified route registration function.
9
11
  */
@@ -13,6 +15,8 @@ export interface RegisterRoutesOptions {
13
15
  getStatsController: () => ServerStatsController | null;
14
16
  getDebugController: () => DebugController | null;
15
17
  getDashboardController: () => DashboardController | null;
18
+ getDebugStore?: () => DebugStore | null;
19
+ getApp?: () => ApplicationService | null;
16
20
  statsEndpoint?: string | false;
17
21
  debugEndpoint?: string;
18
22
  dashboardPath?: string;
@@ -16,6 +16,8 @@ export function registerAllRoutes(options) {
16
16
  debugEndpoint: options.debugEndpoint,
17
17
  getDebugController: options.getDebugController,
18
18
  getApiController: options.getApiController,
19
+ getDebugStore: options.getDebugStore,
20
+ getApp: options.getApp,
19
21
  middleware,
20
22
  whenReady: options.whenReady,
21
23
  });
@@ -451,6 +451,7 @@
451
451
  background: var(--ss-surface);
452
452
  border-bottom: 1px solid var(--ss-border);
453
453
  white-space: nowrap;
454
+ z-index: 2;
454
455
  }
455
456
  .ss-table td,
456
457
  .ss-dash-table td,
@@ -462,6 +463,7 @@
462
463
  text-overflow: ellipsis;
463
464
  white-space: nowrap;
464
465
  color: var(--ss-text);
466
+ max-width: 0;
465
467
  }
466
468
  .ss-table tr:hover td,
467
469
  .ss-dash-table tr:hover td,
@@ -533,6 +535,242 @@
533
535
  background: var(--ss-red-bg);
534
536
  color: var(--ss-red-fg);
535
537
  }
538
+ /* SQL method variants (query tables) */
539
+ .ss-method-select,
540
+ .ss-dash-method-select,
541
+ .ss-dbg-method-select {
542
+ background: var(--ss-green-bg);
543
+ color: var(--ss-green-fg);
544
+ }
545
+ .ss-method-insert,
546
+ .ss-dash-method-insert,
547
+ .ss-dbg-method-insert {
548
+ background: var(--ss-blue-bg);
549
+ color: var(--ss-blue-fg);
550
+ }
551
+ .ss-method-update,
552
+ .ss-dash-method-update,
553
+ .ss-dbg-method-update {
554
+ background: var(--ss-amber-bg);
555
+ color: var(--ss-amber-fg);
556
+ }
557
+ .ss-method-head,
558
+ .ss-method-options,
559
+ .ss-method-raw,
560
+ .ss-dash-method-head,
561
+ .ss-dash-method-options,
562
+ .ss-dash-method-raw,
563
+ .ss-dbg-method-head,
564
+ .ss-dbg-method-options,
565
+ .ss-dbg-method-raw {
566
+ background: var(--ss-surface);
567
+ color: var(--ss-dim);
568
+ }
569
+
570
+ /* ── SQL cell text ───────────────────────────────────────────── */
571
+ .ss-sql,
572
+ .ss-dash-sql,
573
+ .ss-dbg-sql {
574
+ font-size: 11px;
575
+ color: var(--ss-sql-color);
576
+ word-break: break-all;
577
+ cursor: pointer;
578
+ max-width: 600px;
579
+ display: inline-block;
580
+ }
581
+ .ss-sql.ss-expanded,
582
+ .ss-dash-sql.ss-dash-expanded,
583
+ .ss-dbg-sql.ss-dbg-expanded {
584
+ white-space: pre-wrap;
585
+ max-width: none;
586
+ }
587
+
588
+ /* ── Duplicate query badge ───────────────────────────────────── */
589
+ .ss-dup,
590
+ .ss-dash-dup,
591
+ .ss-dbg-dup {
592
+ color: var(--ss-purple-fg);
593
+ font-size: 10px;
594
+ font-weight: 600;
595
+ }
596
+
597
+ /* ── Scrollable table container ──────────────────────────────── */
598
+ .ss-table-wrap,
599
+ .ss-dash-table-wrap,
600
+ .ss-dbg-table-wrap {
601
+ flex: 1;
602
+ overflow-x: auto;
603
+ overflow-y: visible;
604
+ margin: 0 -2px;
605
+ padding: 0 2px;
606
+ scrollbar-width: thin;
607
+ scrollbar-color: var(--ss-border) transparent;
608
+ }
609
+
610
+ /* ── Sortable column headers ─────────────────────────────────── */
611
+ .ss-sortable,
612
+ .ss-dash-sortable,
613
+ .ss-dbg-sortable {
614
+ cursor: pointer;
615
+ user-select: none;
616
+ }
617
+ .ss-sortable:hover,
618
+ .ss-dash-sortable:hover,
619
+ .ss-dbg-sortable:hover {
620
+ color: var(--ss-accent);
621
+ }
622
+ .ss-sort-arrow,
623
+ .ss-dash-sort-arrow,
624
+ .ss-dbg-sort-arrow {
625
+ font-size: 10px;
626
+ color: var(--ss-accent);
627
+ margin-left: 2px;
628
+ }
629
+
630
+ /* ── Clickable table rows ────────────────────────────────────── */
631
+ .ss-table tr.ss-clickable,
632
+ .ss-dash-table tr.ss-dash-clickable,
633
+ .ss-dbg-table tr.ss-dbg-clickable {
634
+ cursor: pointer;
635
+ }
636
+ .ss-table tr.ss-clickable:hover td,
637
+ .ss-dash-table tr.ss-dash-clickable:hover td,
638
+ .ss-dbg-table tr.ss-dbg-clickable:hover td {
639
+ background: var(--ss-hover-accent);
640
+ }
641
+
642
+ /* ── Event time ──────────────────────────────────────────────── */
643
+ .ss-event-time,
644
+ .ss-dash-event-time,
645
+ .ss-dbg-event-time {
646
+ color: var(--ss-dim);
647
+ font-size: 10px;
648
+ }
649
+
650
+ /* ── EXPLAIN panel ───────────────────────────────────────────── */
651
+ .ss-explain-row td,
652
+ .ss-dash-explain-row td,
653
+ .ss-dbg-explain-row td {
654
+ white-space: normal;
655
+ overflow: visible;
656
+ max-width: none;
657
+ }
658
+ .ss-table tr.ss-explain-row:hover td,
659
+ .ss-dash-table tr.ss-dash-explain-row:hover td,
660
+ .ss-dbg-table tr.ss-dbg-explain-row:hover td {
661
+ background: var(--ss-surface-alt);
662
+ }
663
+ .ss-explain,
664
+ .ss-dash-explain,
665
+ .ss-dbg-explain {
666
+ padding: 12px 16px;
667
+ background: var(--ss-surface-alt);
668
+ border-top: 1px solid var(--ss-border-dim);
669
+ font-size: 11px;
670
+ overflow: hidden;
671
+ word-break: break-word;
672
+ }
673
+ .ss-explain-btn,
674
+ .ss-dash-explain-btn,
675
+ .ss-dbg-explain-btn {
676
+ padding: 2px 8px;
677
+ font-size: 9px;
678
+ color: var(--ss-purple-fg);
679
+ background: var(--ss-purple-bg);
680
+ border: none;
681
+ border-radius: 3px;
682
+ cursor: pointer;
683
+ text-transform: uppercase;
684
+ letter-spacing: 0.04em;
685
+ font-weight: 600;
686
+ }
687
+ .ss-explain-btn:hover,
688
+ .ss-dash-explain-btn:hover,
689
+ .ss-dbg-explain-btn:hover {
690
+ opacity: 0.8;
691
+ }
692
+ .ss-explain-btn-active,
693
+ .ss-dash-explain-btn-active,
694
+ .ss-dbg-explain-btn-active {
695
+ background: var(--ss-purple-fg);
696
+ color: #fff;
697
+ }
698
+ .ss-explain-result,
699
+ .ss-dash-explain-result,
700
+ .ss-dbg-explain-result {
701
+ margin-top: 6px;
702
+ font-size: 11px;
703
+ color: var(--ss-text-secondary);
704
+ }
705
+ .ss-explain-error,
706
+ .ss-dash-explain-error,
707
+ .ss-dbg-explain-error {
708
+ color: var(--ss-red-fg);
709
+ padding: 8px;
710
+ background: var(--ss-red-bg);
711
+ border-radius: 4px;
712
+ }
713
+ .ss-explain-node,
714
+ .ss-dash-explain-node,
715
+ .ss-dbg-explain-node {
716
+ padding: 4px 0;
717
+ border-left: 2px solid var(--ss-border-dim);
718
+ padding-left: 10px;
719
+ margin-top: 2px;
720
+ }
721
+ .ss-explain-node:first-child,
722
+ .ss-dash-explain-node:first-child,
723
+ .ss-dbg-explain-node:first-child {
724
+ border-left: 2px solid var(--ss-purple-fg);
725
+ }
726
+ .ss-explain-node-header,
727
+ .ss-dash-explain-node-header,
728
+ .ss-dbg-explain-node-header {
729
+ font-weight: 600;
730
+ color: var(--ss-text-primary);
731
+ font-size: 11px;
732
+ }
733
+ .ss-explain-node-type,
734
+ .ss-dash-explain-node-type,
735
+ .ss-dbg-explain-node-type {
736
+ color: var(--ss-purple-fg);
737
+ font-weight: 700;
738
+ }
739
+ .ss-explain-metrics,
740
+ .ss-dash-explain-metrics,
741
+ .ss-dbg-explain-metrics {
742
+ font-size: 10px;
743
+ color: var(--ss-dim);
744
+ margin-top: 1px;
745
+ font-family: var(--ss-font-mono);
746
+ word-break: break-all;
747
+ overflow-wrap: break-word;
748
+ }
749
+ .ss-explain-result table,
750
+ .ss-dash-explain-result table,
751
+ .ss-dbg-explain-result table {
752
+ width: 100%;
753
+ border-collapse: collapse;
754
+ margin-top: 4px;
755
+ }
756
+ .ss-explain-result th,
757
+ .ss-dash-explain-result th,
758
+ .ss-dbg-explain-result th {
759
+ padding: 3px 8px;
760
+ text-align: left;
761
+ font-weight: 600;
762
+ color: var(--ss-muted);
763
+ border-bottom: 1px solid var(--ss-border);
764
+ font-size: 9px;
765
+ text-transform: uppercase;
766
+ }
767
+ .ss-explain-result td,
768
+ .ss-dash-explain-result td,
769
+ .ss-dbg-explain-result td {
770
+ padding: 3px 8px;
771
+ border-bottom: 1px solid var(--ss-border-faint);
772
+ color: var(--ss-text-secondary);
773
+ }
536
774
 
537
775
  /* ── 3. Log system ───────────────────────────────────────────── */
538
776
  /* Log entry row: dashboard uses 16px horizontal padding, debug uses 12px.
@@ -366,48 +366,12 @@ html:has(#ss-dash) body {
366
366
  }
367
367
 
368
368
  /* ── Tables ────────────────────────────────────────────────────── */
369
+ /* Table base / table-wrap / sortable / clickable → components.css */
370
+ /* Dashboard overrides: wider cell padding */
369
371
  .ss-dash-table-wrap {
370
372
  --ss-table-cell-px: 12px;
371
373
  --ss-table-cell-py: 7px;
372
374
  --ss-table-td-py: 6px;
373
- flex: 1;
374
- overflow-x: auto;
375
- overflow-y: visible;
376
- margin: 0 -2px;
377
- padding: 0 2px;
378
- scrollbar-width: thin;
379
- scrollbar-color: var(--ss-border) transparent;
380
- }
381
- /* Table base → components.css */
382
- /* Column resize → components.css */
383
- .ss-dash-table th {
384
- z-index: 2;
385
- }
386
- .ss-dash-table td {
387
- max-width: 0;
388
- }
389
- .ss-dash-table tr.ss-dash-explain-row:hover td {
390
- background: var(--ss-surface-alt);
391
- }
392
- .ss-dash-table tr.ss-dash-clickable {
393
- cursor: pointer;
394
- }
395
- .ss-dash-table tr.ss-dash-clickable:hover td {
396
- background: var(--ss-hover-accent);
397
- }
398
-
399
- .ss-dash-sortable {
400
- cursor: pointer;
401
- user-select: none;
402
- }
403
- .ss-dash-sortable:hover {
404
- color: var(--ss-accent);
405
- }
406
-
407
- .ss-dash-sort-arrow {
408
- font-size: 10px;
409
- color: var(--ss-accent);
410
- margin-left: 2px;
411
375
  }
412
376
 
413
377
  .ss-dash-data-cell {
@@ -415,27 +379,7 @@ html:has(#ss-dash) body {
415
379
  max-width: 300px;
416
380
  }
417
381
 
418
- /* ── Method badges ─────────────────────────────────────────────── */
419
- /* Base + GET/POST/PUT/PATCH/DELETE → components.css */
420
- /* Dashboard-only SQL method variants */
421
- .ss-dash-method-select {
422
- background: var(--ss-green-bg);
423
- color: var(--ss-green-fg);
424
- }
425
- .ss-dash-method-insert {
426
- background: var(--ss-blue-bg);
427
- color: var(--ss-blue-fg);
428
- }
429
- .ss-dash-method-update {
430
- background: var(--ss-amber-bg);
431
- color: var(--ss-amber-fg);
432
- }
433
- .ss-dash-method-head,
434
- .ss-dash-method-options,
435
- .ss-dash-method-raw {
436
- background: var(--ss-surface);
437
- color: var(--ss-dim);
438
- }
382
+ /* ── Method badges components.css ────────────────────────────── */
439
383
 
440
384
  /* Status badges → components.css */
441
385
  /* Badge generic → components.css */
@@ -447,26 +391,7 @@ html:has(#ss-dash) body {
447
391
 
448
392
  /* Duration → components.css */
449
393
 
450
- /* ── SQL ───────────────────────────────────────────────────────── */
451
- .ss-dash-sql {
452
- font-size: 11px;
453
- color: var(--ss-sql-color);
454
- word-break: break-all;
455
- cursor: pointer;
456
- max-width: 600px;
457
- /* truncation → utilities.css */
458
- display: inline-block;
459
- }
460
- .ss-dash-sql.ss-dash-expanded {
461
- white-space: pre-wrap;
462
- max-width: none;
463
- }
464
-
465
- .ss-dash-dup {
466
- color: var(--ss-purple-fg);
467
- font-size: 10px;
468
- font-weight: 600;
469
- }
394
+ /* ── SQL / Dup → components.css ────────────────────────────────── */
470
395
 
471
396
  /* ── Current route highlight ───────────────────────────────────── */
472
397
  .ss-dash-current-route td {
@@ -520,11 +445,7 @@ html:has(#ss-dash) body {
520
445
  font-size: 10px;
521
446
  position: relative;
522
447
  }
523
- .ss-dash-event-time {
524
- color: var(--ss-dim);
525
- font-size: 10px;
526
- /* tabular-nums → utilities.css */
527
- }
448
+ /* .ss-dash-event-time → components.css */
528
449
 
529
450
  /* ── Logs ──────────────────────────────────────────────────────── */
530
451
  /* Log entry / level / time / reqid / msg → components.css */
@@ -734,95 +655,7 @@ html:has(#ss-dash) body {
734
655
  flex-shrink: 0;
735
656
  }
736
657
 
737
- /* ── EXPLAIN panel ─────────────────────────────────────────────── */
738
- .ss-dash-explain-row td {
739
- white-space: normal;
740
- overflow: visible;
741
- max-width: none;
742
- }
743
- .ss-dash-explain {
744
- padding: 12px 16px;
745
- background: var(--ss-surface-alt);
746
- border-top: 1px solid var(--ss-border-dim);
747
- font-size: 11px;
748
- overflow: hidden;
749
- word-break: break-word;
750
- }
751
- .ss-dash-explain-btn {
752
- padding: 2px 8px;
753
- font-size: 9px;
754
- color: var(--ss-purple-fg);
755
- background: var(--ss-purple-bg);
756
- border: none;
757
- border-radius: 3px;
758
- cursor: pointer;
759
- text-transform: uppercase;
760
- letter-spacing: 0.04em;
761
- font-weight: 600;
762
- }
763
- .ss-dash-explain-btn:hover {
764
- opacity: 0.8;
765
- }
766
- .ss-dash-explain-btn-active {
767
- background: var(--ss-purple-fg);
768
- color: #fff;
769
- }
770
- .ss-dash-explain-result {
771
- margin-top: 6px;
772
- font-size: 11px;
773
- color: var(--ss-text-secondary);
774
- }
775
- .ss-dash-explain-error {
776
- color: var(--ss-red-fg);
777
- padding: 8px;
778
- background: var(--ss-red-bg);
779
- border-radius: 4px;
780
- }
781
- .ss-dash-explain-node {
782
- padding: 4px 0;
783
- border-left: 2px solid var(--ss-border-dim);
784
- padding-left: 10px;
785
- margin-top: 2px;
786
- }
787
- .ss-dash-explain-node:first-child {
788
- border-left: 2px solid var(--ss-purple-fg);
789
- }
790
- .ss-dash-explain-node-header {
791
- font-weight: 600;
792
- color: var(--ss-text-primary);
793
- font-size: 11px;
794
- }
795
- .ss-dash-explain-node-type {
796
- color: var(--ss-purple-fg);
797
- font-weight: 700;
798
- }
799
- .ss-dash-explain-metrics {
800
- font-size: 10px;
801
- color: var(--ss-dim);
802
- margin-top: 1px;
803
- font-family: var(--ss-font-mono);
804
- word-break: break-all;
805
- overflow-wrap: break-word;
806
- }
807
- .ss-dash-explain-result table {
808
- width: 100%;
809
- border-collapse: collapse;
810
- margin-top: 4px;
811
- }
812
- .ss-dash-explain-result th {
813
- padding: 3px 8px;
814
- text-align: left;
815
- font-weight: 600;
816
- color: var(--ss-muted);
817
- border-bottom: 1px solid var(--ss-border);
818
- font-size: 9px;
819
- text-transform: uppercase;
820
- }
821
- .ss-dash-explain-result td {
822
- padding: 3px 8px;
823
- border-bottom: 1px solid var(--ss-border-faint);
824
- color: var(--ss-text-secondary);
825
- }
658
+ /* ── EXPLAIN panel components.css ────────────────────────────── */
826
659
 
827
660
  /* ── Overview cards ────────────────────────────────────────────── */
828
661
  .ss-dash-overview {
@@ -272,24 +272,7 @@
272
272
 
273
273
  /* Table base / column resize → components.css */
274
274
 
275
- /* Query-specific styles */
276
- .ss-dbg-sql {
277
- font-size: 11px;
278
- color: var(--ss-sql-color);
279
- word-break: break-all;
280
- cursor: pointer;
281
- max-width: 500px;
282
- /* truncation → utilities.css */
283
- }
284
- .ss-dbg-sql.ss-dbg-expanded {
285
- white-space: pre-wrap;
286
- max-width: none;
287
- }
288
- /* Duration → components.css */
289
- .ss-dbg-dup {
290
- color: var(--ss-purple-fg);
291
- font-size: 10px;
292
- }
275
+ /* Query-specific styles → components.css (sql, dup, explain, etc.) */
293
276
 
294
277
  /* Method badges → components.css */
295
278
 
@@ -309,12 +292,7 @@
309
292
  position: relative;
310
293
  /* truncation → utilities.css */
311
294
  }
312
- .ss-dbg-event-time {
313
- color: var(--ss-dim);
314
- font-size: 10px;
315
- /* tabular-nums → utilities.css */
316
- white-space: nowrap;
317
- }
295
+ /* .ss-dbg-event-time → components.css */
318
296
  .ss-dbg-data-preview {
319
297
  cursor: pointer;
320
298
  color: var(--ss-text-secondary);
@@ -1,5 +1,5 @@
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-CYaqRGIT.js";
2
+ import { u as H } from "./index-CptF6Qbp.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";
@@ -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-CYaqRGIT.js";
2
+ import { u as ue } from "./index-CptF6Qbp.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-CYaqRGIT.js";
3
+ import { u as L } from "./index-CptF6Qbp.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,6 +1,6 @@
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-CYaqRGIT.js";
3
+ import { u as $ } from "./index-CptF6Qbp.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";
@@ -1,6 +1,6 @@
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-CYaqRGIT.js";
3
+ import { u as Y } from "./index-CptF6Qbp.js";
4
4
  import { u as Z } from "./useResizableTable-BoivAevK.js";
5
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";
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as ie, inject as C, ref as u, computed as B, openBlock as a, createElementBlock as o, createVNode as O, unref as s, withCtx as ue, createElementVNode as n, Fragment as q, renderList as x, normalizeClass as $, toDisplayString as i, withKeys as V, createCommentVNode as _, createStaticVNode as re, createTextVNode as w, withModifiers as U, createBlock as de } from "vue";
2
2
  import { LOG_LEVELS as ce, getStructuredData as m, getLogLevelCssClass as ve, resolveLogLevel as W, resolveLogTimestamp as F, formatTime as pe, timeAgo as he, resolveLogRequestId as g, resolveLogMessage as _e } from "adonisjs-server-stats/core";
3
3
  import { _ as fe } from "./JsonViewer.vue_vue_type_script_setup_true_lang-Bid05zpm.js";
4
- import { u as me } from "./index-CYaqRGIT.js";
4
+ import { u as me } from "./index-CptF6Qbp.js";
5
5
  import { _ as ge } from "./FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js";
6
6
  import { _ as ye } from "./PaginationControls.vue_vue_type_script_setup_true_lang-CuN7g_8Z.js";
7
7
  const ke = { class: "ss-dash-log-filters" }, be = ["onClick"], Ce = ["value"], qe = { class: "ss-dash-structured-search" }, $e = ["value"], Fe = ["value"], Le = ["value"], Se = {