adonisjs-server-stats 1.5.4 → 1.6.2
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 +351 -171
- package/dist/configure.d.ts.map +1 -1
- package/dist/core/api-client.d.ts +73 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/config-utils.d.ts +109 -0
- package/dist/core/config-utils.d.ts.map +1 -0
- package/dist/core/constants.d.ts +11 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/dashboard-api.d.ts +65 -0
- package/dist/core/dashboard-api.d.ts.map +1 -0
- package/dist/core/dashboard-data-controller.d.ts +157 -0
- package/dist/core/dashboard-data-controller.d.ts.map +1 -0
- package/dist/core/debug-data-controller.d.ts +89 -0
- package/dist/core/debug-data-controller.d.ts.map +1 -0
- package/dist/core/feature-detect.d.ts +67 -0
- package/dist/core/feature-detect.d.ts.map +1 -0
- package/dist/core/formatters.d.ts +189 -0
- package/dist/core/formatters.d.ts.map +1 -0
- package/dist/core/history-buffer.d.ts +23 -0
- package/dist/core/history-buffer.d.ts.map +1 -0
- package/dist/core/icons.d.ts +36 -0
- package/dist/core/icons.d.ts.map +1 -0
- package/dist/core/index.d.ts +39 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1961 -0
- package/dist/core/internals-utils.d.ts +71 -0
- package/dist/core/internals-utils.d.ts.map +1 -0
- package/dist/core/job-utils.d.ts +45 -0
- package/dist/core/job-utils.d.ts.map +1 -0
- package/dist/core/log-utils.d.ts +34 -0
- package/dist/core/log-utils.d.ts.map +1 -0
- package/dist/core/metrics.d.ts +41 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/pagination.d.ts +128 -0
- package/dist/core/pagination.d.ts.map +1 -0
- package/dist/core/query-utils.d.ts +35 -0
- package/dist/core/query-utils.d.ts.map +1 -0
- package/dist/core/resizable-columns.d.ts +18 -0
- package/dist/core/resizable-columns.d.ts.map +1 -0
- package/dist/core/routes.d.ts +12 -0
- package/dist/core/routes.d.ts.map +1 -0
- package/dist/core/server-stats-controller.d.ts +106 -0
- package/dist/core/server-stats-controller.d.ts.map +1 -0
- package/dist/core/sparkline.d.ts +80 -0
- package/dist/core/sparkline.d.ts.map +1 -0
- package/dist/core/theme.d.ts +42 -0
- package/dist/core/theme.d.ts.map +1 -0
- package/dist/core/trace-utils.d.ts +62 -0
- package/dist/core/trace-utils.d.ts.map +1 -0
- package/dist/core/transmit-adapter.d.ts +59 -0
- package/dist/core/transmit-adapter.d.ts.map +1 -0
- package/dist/core/types.d.ts +619 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/react/CacheSection-DGxMDlWK.js +146 -0
- package/dist/react/CacheTab-CnVW5PLs.js +123 -0
- package/dist/react/ConfigContent-CnsEI4j3.js +397 -0
- package/dist/react/ConfigSection-DPcrfqXY.js +11 -0
- package/dist/react/ConfigTab-BSWq_o2p.js +15 -0
- package/dist/react/CustomPaneTab-xjkYwTvH.js +104 -0
- package/dist/react/DataTable-YyShr5B-.js +55 -0
- package/dist/react/EmailsSection-CSyTg1aX.js +262 -0
- package/dist/react/EmailsTab-Dh2YSa_f.js +131 -0
- package/dist/react/EventsSection-C1pbJDfW.js +86 -0
- package/dist/react/EventsTab-eCh02cdd.js +63 -0
- package/dist/react/FilterBar-DQRXpWrb.js +50 -0
- package/dist/react/InternalsContent-DBzsI0CG.js +346 -0
- package/dist/react/InternalsSection-t7ihcWO-.js +32 -0
- package/dist/react/InternalsTab-Oij0A2fN.js +30 -0
- package/dist/react/JobsSection-CLAin5vU.js +187 -0
- package/dist/react/JobsTab-Dl5nrj2z.js +141 -0
- package/dist/react/LogsSection-C1p81fXO.js +212 -0
- package/dist/react/LogsTab-D-kR7PjX.js +88 -0
- package/dist/react/OverviewSection-nm3xdACz.js +539 -0
- package/dist/react/Pagination-BkmzUDY8.js +64 -0
- package/dist/react/QueriesSection-DB12HMfQ.js +461 -0
- package/dist/react/QueriesTab-fyBB1u_Y.js +90 -0
- package/dist/react/RequestsSection-DTqB81ac.js +209 -0
- package/dist/react/RoutesSection-DJWa4NPV.js +74 -0
- package/dist/react/RoutesTab-D3l8TOpu.js +74 -0
- package/dist/react/TimelineSection-C4d-jRX1.js +158 -0
- package/dist/react/TimelineTab-C5TFaSmQ.js +193 -0
- package/dist/react/WaterfallChart-Cj73WdfM.js +100 -0
- package/dist/react/core/api-client.d.ts +73 -0
- package/dist/react/core/api-client.d.ts.map +1 -0
- package/dist/react/core/config-utils.d.ts +109 -0
- package/dist/react/core/config-utils.d.ts.map +1 -0
- package/dist/react/core/constants.d.ts +11 -0
- package/dist/react/core/constants.d.ts.map +1 -0
- package/dist/react/core/dashboard-api.d.ts +65 -0
- package/dist/react/core/dashboard-api.d.ts.map +1 -0
- package/dist/react/core/dashboard-data-controller.d.ts +157 -0
- package/dist/react/core/dashboard-data-controller.d.ts.map +1 -0
- package/dist/react/core/debug-data-controller.d.ts +89 -0
- package/dist/react/core/debug-data-controller.d.ts.map +1 -0
- package/dist/react/core/feature-detect.d.ts +67 -0
- package/dist/react/core/feature-detect.d.ts.map +1 -0
- package/dist/react/core/formatters.d.ts +189 -0
- package/dist/react/core/formatters.d.ts.map +1 -0
- package/dist/react/core/history-buffer.d.ts +23 -0
- package/dist/react/core/history-buffer.d.ts.map +1 -0
- package/dist/react/core/icons.d.ts +36 -0
- package/dist/react/core/icons.d.ts.map +1 -0
- package/dist/react/core/index.d.ts +39 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/core/internals-utils.d.ts +71 -0
- package/dist/react/core/internals-utils.d.ts.map +1 -0
- package/dist/react/core/job-utils.d.ts +45 -0
- package/dist/react/core/job-utils.d.ts.map +1 -0
- package/dist/react/core/log-utils.d.ts +34 -0
- package/dist/react/core/log-utils.d.ts.map +1 -0
- package/dist/react/core/metrics.d.ts +41 -0
- package/dist/react/core/metrics.d.ts.map +1 -0
- package/dist/react/core/pagination.d.ts +128 -0
- package/dist/react/core/pagination.d.ts.map +1 -0
- package/dist/react/core/query-utils.d.ts +35 -0
- package/dist/react/core/query-utils.d.ts.map +1 -0
- package/dist/react/core/resizable-columns.d.ts +18 -0
- package/dist/react/core/resizable-columns.d.ts.map +1 -0
- package/dist/react/core/routes.d.ts +12 -0
- package/dist/react/core/routes.d.ts.map +1 -0
- package/dist/react/core/server-stats-controller.d.ts +106 -0
- package/dist/react/core/server-stats-controller.d.ts.map +1 -0
- package/dist/react/core/sparkline.d.ts +80 -0
- package/dist/react/core/sparkline.d.ts.map +1 -0
- package/dist/react/core/theme.d.ts +42 -0
- package/dist/react/core/theme.d.ts.map +1 -0
- package/dist/react/core/trace-utils.d.ts +62 -0
- package/dist/react/core/trace-utils.d.ts.map +1 -0
- package/dist/react/core/transmit-adapter.d.ts +59 -0
- package/dist/react/core/transmit-adapter.d.ts.map +1 -0
- package/dist/react/core/types.d.ts +619 -0
- package/dist/react/core/types.d.ts.map +1 -0
- package/dist/react/index-UdTfSvtO.js +1074 -0
- package/dist/react/index.js +18 -0
- package/dist/react/react/components/Dashboard/DashboardPage.d.ts +17 -0
- package/dist/react/react/components/Dashboard/DashboardPage.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/CacheSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/CacheSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/ConfigSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/ConfigSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/EmailsSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/EmailsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/EventsSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/EventsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/InternalsSection.d.ts +14 -0
- package/dist/react/react/components/Dashboard/sections/InternalsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/JobsSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/JobsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/LogsSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/LogsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/OverviewSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/OverviewSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/QueriesSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/QueriesSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/RequestsSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/RequestsSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/RoutesSection.d.ts +7 -0
- package/dist/react/react/components/Dashboard/sections/RoutesSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/sections/TimelineSection.d.ts +9 -0
- package/dist/react/react/components/Dashboard/sections/TimelineSection.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/shared/DataTable.d.ts +27 -0
- package/dist/react/react/components/Dashboard/shared/DataTable.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/shared/FilterBar.d.ts +17 -0
- package/dist/react/react/components/Dashboard/shared/FilterBar.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/shared/Pagination.d.ts +13 -0
- package/dist/react/react/components/Dashboard/shared/Pagination.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/shared/TimeRangeSelector.d.ts +12 -0
- package/dist/react/react/components/Dashboard/shared/TimeRangeSelector.d.ts.map +1 -0
- package/dist/react/react/components/Dashboard/shared/WaterfallChart.d.ts +16 -0
- package/dist/react/react/components/Dashboard/shared/WaterfallChart.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/DebugPanel.d.ts +16 -0
- package/dist/react/react/components/DebugPanel/DebugPanel.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/CacheTab.d.ts +8 -0
- package/dist/react/react/components/DebugPanel/tabs/CacheTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/ConfigTab.d.ts +8 -0
- package/dist/react/react/components/DebugPanel/tabs/ConfigTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/CustomPaneTab.d.ts +14 -0
- package/dist/react/react/components/DebugPanel/tabs/CustomPaneTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/EmailsTab.d.ts +7 -0
- package/dist/react/react/components/DebugPanel/tabs/EmailsTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/EventsTab.d.ts +7 -0
- package/dist/react/react/components/DebugPanel/tabs/EventsTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/InternalsTab.d.ts +12 -0
- package/dist/react/react/components/DebugPanel/tabs/InternalsTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/JobsTab.d.ts +9 -0
- package/dist/react/react/components/DebugPanel/tabs/JobsTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/LogsTab.d.ts +7 -0
- package/dist/react/react/components/DebugPanel/tabs/LogsTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/QueriesTab.d.ts +7 -0
- package/dist/react/react/components/DebugPanel/tabs/QueriesTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/RoutesTab.d.ts +8 -0
- package/dist/react/react/components/DebugPanel/tabs/RoutesTab.d.ts.map +1 -0
- package/dist/react/react/components/DebugPanel/tabs/TimelineTab.d.ts +7 -0
- package/dist/react/react/components/DebugPanel/tabs/TimelineTab.d.ts.map +1 -0
- package/dist/react/react/components/StatsBar/MetricCard.d.ts +21 -0
- package/dist/react/react/components/StatsBar/MetricCard.d.ts.map +1 -0
- package/dist/react/react/components/StatsBar/Sparkline.d.ts +15 -0
- package/dist/react/react/components/StatsBar/Sparkline.d.ts.map +1 -0
- package/dist/react/react/components/StatsBar/StatsBar.d.ts +22 -0
- package/dist/react/react/components/StatsBar/StatsBar.d.ts.map +1 -0
- package/dist/react/react/components/shared/Badge.d.ts +32 -0
- package/dist/react/react/components/shared/Badge.d.ts.map +1 -0
- package/dist/react/react/components/shared/ConfigContent.d.ts +13 -0
- package/dist/react/react/components/shared/ConfigContent.d.ts.map +1 -0
- package/dist/react/react/components/shared/InternalsContent.d.ts +10 -0
- package/dist/react/react/components/shared/InternalsContent.d.ts.map +1 -0
- package/dist/react/react/components/shared/JsonViewer.d.ts +12 -0
- package/dist/react/react/components/shared/JsonViewer.d.ts.map +1 -0
- package/dist/react/react/components/shared/ThemeToggle.d.ts +13 -0
- package/dist/react/react/components/shared/ThemeToggle.d.ts.map +1 -0
- package/dist/react/react/components/shared/Tooltip.d.ts +16 -0
- package/dist/react/react/components/shared/Tooltip.d.ts.map +1 -0
- package/dist/react/react/hooks/useApiClient.d.ts +10 -0
- package/dist/react/react/hooks/useApiClient.d.ts.map +1 -0
- package/dist/react/react/hooks/useDashboardApiBase.d.ts +19 -0
- package/dist/react/react/hooks/useDashboardApiBase.d.ts.map +1 -0
- package/dist/react/react/hooks/useDashboardData.d.ts +27 -0
- package/dist/react/react/hooks/useDashboardData.d.ts.map +1 -0
- package/dist/react/react/hooks/useDebugData.d.ts +17 -0
- package/dist/react/react/hooks/useDebugData.d.ts.map +1 -0
- package/dist/react/react/hooks/useFeatures.d.ts +13 -0
- package/dist/react/react/hooks/useFeatures.d.ts.map +1 -0
- package/dist/react/react/hooks/useResizableTable.d.ts +13 -0
- package/dist/react/react/hooks/useResizableTable.d.ts.map +1 -0
- package/dist/react/react/hooks/useServerStats.d.ts +21 -0
- package/dist/react/react/hooks/useServerStats.d.ts.map +1 -0
- package/dist/react/react/hooks/useTheme.d.ts +12 -0
- package/dist/react/react/hooks/useTheme.d.ts.map +1 -0
- package/dist/react/react/index.d.ts +14 -0
- package/dist/react/react/index.d.ts.map +1 -0
- package/dist/react/style.css +1 -0
- package/dist/react/useApiClient-BVtNCmnL.js +9 -0
- package/dist/react/useDashboardApiBase-Bi36pJ2L.js +14 -0
- package/dist/react/useResizableTable-CNJmACdt.js +13 -0
- package/dist/src/collectors/app_collector.d.ts.map +1 -1
- package/dist/src/collectors/app_collector.js +30 -2
- package/dist/src/collectors/auto_detect.d.ts +31 -0
- package/dist/src/collectors/auto_detect.d.ts.map +1 -0
- package/dist/src/collectors/auto_detect.js +120 -0
- package/dist/src/collectors/collector.d.ts +17 -0
- package/dist/src/collectors/collector.d.ts.map +1 -1
- package/dist/src/collectors/db_pool_collector.d.ts.map +1 -1
- package/dist/src/collectors/db_pool_collector.js +35 -1
- package/dist/src/collectors/http_collector.d.ts +4 -3
- package/dist/src/collectors/http_collector.d.ts.map +1 -1
- package/dist/src/collectors/http_collector.js +28 -11
- package/dist/src/collectors/index.d.ts +2 -0
- package/dist/src/collectors/index.d.ts.map +1 -1
- package/dist/src/collectors/index.js +1 -0
- package/dist/src/collectors/log_collector.d.ts +11 -4
- package/dist/src/collectors/log_collector.d.ts.map +1 -1
- package/dist/src/collectors/log_collector.js +51 -5
- package/dist/src/collectors/process_collector.d.ts.map +1 -1
- package/dist/src/collectors/process_collector.js +4 -0
- package/dist/src/collectors/queue_collector.d.ts.map +1 -1
- package/dist/src/collectors/queue_collector.js +55 -1
- package/dist/src/collectors/redis_collector.d.ts.map +1 -1
- package/dist/src/collectors/redis_collector.js +42 -3
- package/dist/src/collectors/system_collector.d.ts.map +1 -1
- package/dist/src/collectors/system_collector.js +4 -0
- package/dist/src/controller/api_controller.d.ts +101 -0
- package/dist/src/controller/api_controller.d.ts.map +1 -0
- package/dist/src/controller/api_controller.js +131 -0
- package/dist/src/controller/debug_controller.d.ts +19 -10
- package/dist/src/controller/debug_controller.d.ts.map +1 -1
- package/dist/src/controller/debug_controller.js +118 -101
- package/dist/src/core/theme.d.ts +42 -0
- package/dist/src/core/theme.d.ts.map +1 -0
- package/dist/src/core/theme.js +115 -0
- package/dist/src/dashboard/chart_aggregator.d.ts.map +1 -1
- package/dist/src/dashboard/chart_aggregator.js +3 -2
- package/dist/src/dashboard/dashboard_controller.d.ts +10 -14
- package/dist/src/dashboard/dashboard_controller.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_controller.js +132 -250
- package/dist/src/dashboard/dashboard_store.d.ts +62 -19
- package/dist/src/dashboard/dashboard_store.d.ts.map +1 -1
- package/dist/src/dashboard/dashboard_store.js +242 -53
- package/dist/src/dashboard/integrations/cache_inspector.d.ts +19 -1
- package/dist/src/dashboard/integrations/cache_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/integrations/config_inspector.d.ts +1 -1
- package/dist/src/dashboard/integrations/config_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/integrations/config_inspector.js +3 -2
- package/dist/src/dashboard/integrations/queue_inspector.d.ts +55 -10
- package/dist/src/dashboard/integrations/queue_inspector.d.ts.map +1 -1
- package/dist/src/dashboard/integrations/queue_inspector.js +70 -24
- package/dist/src/dashboard/migrator.d.ts +5 -0
- package/dist/src/dashboard/migrator.d.ts.map +1 -1
- package/dist/src/dashboard/migrator.js +44 -9
- package/dist/src/dashboard/models/stats_event.d.ts +1 -1
- package/dist/src/dashboard/models/stats_event.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_log.d.ts +1 -1
- package/dist/src/dashboard/models/stats_log.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts +1 -1
- package/dist/src/dashboard/models/stats_query.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_saved_filter.d.ts +1 -1
- package/dist/src/dashboard/models/stats_saved_filter.d.ts.map +1 -1
- package/dist/src/dashboard/models/stats_trace.d.ts +2 -1
- package/dist/src/dashboard/models/stats_trace.d.ts.map +1 -1
- package/dist/src/data/data_access.d.ts +105 -0
- package/dist/src/data/data_access.d.ts.map +1 -0
- package/dist/src/data/data_access.js +310 -0
- package/dist/src/data/index.d.ts +3 -0
- package/dist/src/data/index.d.ts.map +1 -0
- package/dist/src/data/index.js +1 -0
- package/dist/src/debug/debug_store.d.ts +20 -1
- package/dist/src/debug/debug_store.d.ts.map +1 -1
- package/dist/src/debug/debug_store.js +43 -15
- package/dist/src/debug/email_collector.d.ts +6 -2
- package/dist/src/debug/email_collector.d.ts.map +1 -1
- package/dist/src/debug/email_collector.js +3 -0
- package/dist/src/debug/event_collector.d.ts +6 -2
- package/dist/src/debug/event_collector.d.ts.map +1 -1
- package/dist/src/debug/event_collector.js +12 -8
- package/dist/src/debug/query_collector.d.ts +6 -2
- package/dist/src/debug/query_collector.d.ts.map +1 -1
- package/dist/src/debug/query_collector.js +3 -0
- package/dist/src/debug/ring_buffer.d.ts +1 -0
- package/dist/src/debug/ring_buffer.d.ts.map +1 -1
- package/dist/src/debug/ring_buffer.js +5 -2
- package/dist/src/debug/route_inspector.d.ts +2 -2
- package/dist/src/debug/route_inspector.d.ts.map +1 -1
- package/dist/src/debug/route_inspector.js +4 -3
- package/dist/src/debug/trace_collector.d.ts +7 -3
- package/dist/src/debug/trace_collector.d.ts.map +1 -1
- package/dist/src/debug/trace_collector.js +7 -5
- package/dist/src/debug/types.d.ts +107 -2
- package/dist/src/debug/types.d.ts.map +1 -1
- package/dist/src/debug/types.js +1 -1
- package/dist/src/define_config.d.ts +49 -5
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +361 -4
- package/dist/src/edge/bootstrap.d.ts +17 -0
- package/dist/src/edge/bootstrap.d.ts.map +1 -0
- package/dist/src/edge/bootstrap.js +29 -0
- package/dist/src/edge/client/dashboard.js +2 -3619
- package/dist/src/edge/client/debug-panel-deferred.js +1 -0
- package/dist/src/edge/client/debug-panel.js +1 -2140
- package/dist/src/edge/client/stats-bar.js +1 -801
- package/dist/src/edge/client-vue/dashboard.js +5 -0
- package/dist/src/edge/client-vue/debug-panel-deferred.js +4 -0
- package/dist/src/edge/client-vue/debug-panel.js +4 -0
- package/dist/src/edge/client-vue/stats-bar.js +4 -0
- package/dist/src/edge/plugin.d.ts +35 -2
- package/dist/src/edge/plugin.d.ts.map +1 -1
- package/dist/src/edge/plugin.js +30 -66
- package/dist/src/edge/types.d.ts +46 -0
- package/dist/src/edge/types.d.ts.map +1 -0
- package/dist/src/edge/types.js +4 -0
- package/dist/src/edge/views/dashboard.edge +1 -358
- package/dist/src/edge/views/debug-panel.edge +2 -154
- package/dist/src/edge/views/stats-bar.edge +15 -48
- package/dist/src/engine/stats_engine.d.ts +18 -0
- package/dist/src/engine/stats_engine.d.ts.map +1 -1
- package/dist/src/engine/stats_engine.js +45 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/log_stream/log_stream_provider.d.ts.map +1 -1
- package/dist/src/log_stream/log_stream_provider.js +21 -4
- package/dist/src/log_stream/log_stream_service.d.ts +8 -1
- package/dist/src/log_stream/log_stream_service.d.ts.map +1 -1
- package/dist/src/log_stream/log_stream_service.js +27 -3
- package/dist/src/middleware/request_tracking_middleware.d.ts +1 -1
- package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -1
- package/dist/src/middleware/request_tracking_middleware.js +21 -6
- package/dist/src/prometheus/prometheus_collector.d.ts +2 -1
- package/dist/src/prometheus/prometheus_collector.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.d.ts +100 -0
- package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
- package/dist/src/provider/server_stats_provider.js +460 -104
- package/dist/src/routes/access_middleware.d.ts +2 -1
- package/dist/src/routes/access_middleware.d.ts.map +1 -1
- package/dist/src/routes/access_middleware.js +7 -1
- package/dist/src/routes/index.d.ts +4 -0
- package/dist/src/routes/index.d.ts.map +1 -0
- package/dist/src/routes/index.js +1 -0
- package/dist/src/routes/register_routes.d.ts +103 -0
- package/dist/src/routes/register_routes.d.ts.map +1 -0
- package/dist/src/routes/register_routes.js +356 -0
- package/dist/src/routes/router_types.d.ts +29 -0
- package/dist/src/routes/router_types.d.ts.map +1 -0
- package/dist/src/routes/router_types.js +1 -0
- package/dist/src/stubs/config.stub +12 -32
- package/dist/src/styles/components.css +1048 -0
- package/dist/src/{edge/client → styles}/dashboard.css +299 -736
- package/dist/src/{edge/client → styles}/debug-panel.css +117 -633
- package/dist/src/{edge/client → styles}/stats-bar.css +28 -10
- package/dist/src/styles/tokens.css +153 -0
- package/dist/src/styles/utilities.css +75 -0
- package/dist/src/types.d.ts +119 -16
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/app_import.d.ts +23 -0
- package/dist/src/utils/app_import.d.ts.map +1 -0
- package/dist/src/utils/app_import.js +44 -0
- package/dist/src/utils/json_helpers.d.ts +2 -2
- package/dist/src/utils/json_helpers.d.ts.map +1 -1
- package/dist/src/utils/logger.d.ts +17 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +27 -0
- package/dist/src/utils/mail_helpers.d.ts +1 -1
- package/dist/src/utils/mail_helpers.d.ts.map +1 -1
- package/dist/src/utils/mail_helpers.js +1 -1
- package/dist/vue/CacheSection-C788Yfai.js +149 -0
- package/dist/vue/CacheTab-BPisYYiQ.js +104 -0
- package/dist/vue/ConfigSection-CRzYxqW2.js +576 -0
- package/dist/vue/ConfigTab-C8cafGUj.js +361 -0
- package/dist/vue/CustomPaneTab-BJxT5Dp7.js +172 -0
- package/dist/vue/EmailsSection-C8JFMtW7.js +206 -0
- package/dist/vue/EmailsTab-DhFhoNmU.js +157 -0
- package/dist/vue/EventsSection-C4wXUgxG.js +107 -0
- package/dist/vue/EventsTab-DQ4Nd6AK.js +97 -0
- package/dist/vue/FilterBar.vue_vue_type_script_setup_true_lang-ClJ37hhT.js +62 -0
- package/dist/vue/InternalsSection-BJUXE-5F.js +468 -0
- package/dist/vue/InternalsTab-DEMjqtlw.js +471 -0
- package/dist/vue/JobsSection-CsKWTjgN.js +187 -0
- package/dist/vue/JobsTab-BCvhOARO.js +117 -0
- package/dist/vue/JsonViewer.vue_vue_type_script_setup_true_lang-Vsqar1zx.js +67 -0
- package/dist/vue/LogsSection-BFVjSZ24.js +227 -0
- package/dist/vue/LogsTab-DpEQ7euu.js +122 -0
- package/dist/vue/OverviewSection-CbMdAido.js +849 -0
- package/dist/vue/PaginationControls.vue_vue_type_script_setup_true_lang-CuN7g_8Z.js +50 -0
- package/dist/vue/QueriesSection-BPiv7u3r.js +429 -0
- package/dist/vue/QueriesTab-C8_7oprC.js +107 -0
- package/dist/vue/RequestsSection-LtImH4rD.js +243 -0
- package/dist/vue/RoutesSection-CrxOxmzx.js +106 -0
- package/dist/vue/RoutesTab-Dz0MkZuF.js +80 -0
- package/dist/vue/TimelineSection-DLxMW2J_.js +186 -0
- package/dist/vue/TimelineTab-Db6lKKsD.js +250 -0
- package/dist/vue/WaterfallChart.vue_vue_type_script_setup_true_lang-tZ13cNj1.js +118 -0
- package/dist/vue/components/Dashboard/DashboardPage.vue.d.ts +18 -0
- package/dist/vue/components/Dashboard/DashboardPage.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/CacheSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/CacheSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/ConfigSection.vue.d.ts +3 -0
- package/dist/vue/components/Dashboard/sections/ConfigSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/EmailsSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/EmailsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/EventsSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/EventsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/InternalsSection.vue.d.ts +3 -0
- package/dist/vue/components/Dashboard/sections/InternalsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/JobsSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/JobsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/LogsSection.vue.d.ts +3 -0
- package/dist/vue/components/Dashboard/sections/LogsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/OverviewSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/OverviewSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/QueriesSection.vue.d.ts +45 -0
- package/dist/vue/components/Dashboard/sections/QueriesSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/RequestsSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/RequestsSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/RoutesSection.vue.d.ts +5 -0
- package/dist/vue/components/Dashboard/sections/RoutesSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/sections/TimelineSection.vue.d.ts +11 -0
- package/dist/vue/components/Dashboard/sections/TimelineSection.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/shared/FilterBar.vue.d.ts +29 -0
- package/dist/vue/components/Dashboard/shared/FilterBar.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/shared/PaginationControls.vue.d.ts +12 -0
- package/dist/vue/components/Dashboard/shared/PaginationControls.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/shared/TimeRangeSelector.vue.d.ts +11 -0
- package/dist/vue/components/Dashboard/shared/TimeRangeSelector.vue.d.ts.map +1 -0
- package/dist/vue/components/Dashboard/shared/WaterfallChart.vue.d.ts +10 -0
- package/dist/vue/components/Dashboard/shared/WaterfallChart.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/DebugPanel.vue.d.ts +21 -0
- package/dist/vue/components/DebugPanel/DebugPanel.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/CacheTab.vue.d.ts +12 -0
- package/dist/vue/components/DebugPanel/tabs/CacheTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/ConfigTab.vue.d.ts +11 -0
- package/dist/vue/components/DebugPanel/tabs/ConfigTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/CustomPaneTab.vue.d.ts +11 -0
- package/dist/vue/components/DebugPanel/tabs/CustomPaneTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/EmailsTab.vue.d.ts +12 -0
- package/dist/vue/components/DebugPanel/tabs/EmailsTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/EventsTab.vue.d.ts +12 -0
- package/dist/vue/components/DebugPanel/tabs/EventsTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/InternalsTab.vue.d.ts +11 -0
- package/dist/vue/components/DebugPanel/tabs/InternalsTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/JobsTab.vue.d.ts +13 -0
- package/dist/vue/components/DebugPanel/tabs/JobsTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/LogsTab.vue.d.ts +15 -0
- package/dist/vue/components/DebugPanel/tabs/LogsTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/QueriesTab.vue.d.ts +12 -0
- package/dist/vue/components/DebugPanel/tabs/QueriesTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/RoutesTab.vue.d.ts +12 -0
- package/dist/vue/components/DebugPanel/tabs/RoutesTab.vue.d.ts.map +1 -0
- package/dist/vue/components/DebugPanel/tabs/TimelineTab.vue.d.ts +15 -0
- package/dist/vue/components/DebugPanel/tabs/TimelineTab.vue.d.ts.map +1 -0
- package/dist/vue/components/StatsBar/MetricCard.vue.d.ts +19 -0
- package/dist/vue/components/StatsBar/MetricCard.vue.d.ts.map +1 -0
- package/dist/vue/components/StatsBar/Sparkline.vue.d.ts +17 -0
- package/dist/vue/components/StatsBar/Sparkline.vue.d.ts.map +1 -0
- package/dist/vue/components/StatsBar/StatsBar.vue.d.ts +22 -0
- package/dist/vue/components/StatsBar/StatsBar.vue.d.ts.map +1 -0
- package/dist/vue/components/shared/JsonViewer.vue.d.ts +13 -0
- package/dist/vue/components/shared/JsonViewer.vue.d.ts.map +1 -0
- package/dist/vue/components/shared/ThemeToggle.vue.d.ts +8 -0
- package/dist/vue/components/shared/ThemeToggle.vue.d.ts.map +1 -0
- package/dist/vue/composables/useApiClient.d.ts +9 -0
- package/dist/vue/composables/useApiClient.d.ts.map +1 -0
- package/dist/vue/composables/useDashboardData.d.ts +53 -0
- package/dist/vue/composables/useDashboardData.d.ts.map +1 -0
- package/dist/vue/composables/useDebugData.d.ts +25 -0
- package/dist/vue/composables/useDebugData.d.ts.map +1 -0
- package/dist/vue/composables/useFeatures.d.ts +80 -0
- package/dist/vue/composables/useFeatures.d.ts.map +1 -0
- package/dist/vue/composables/useResizableTable.d.ts +16 -0
- package/dist/vue/composables/useResizableTable.d.ts.map +1 -0
- package/dist/vue/composables/useServerStats.d.ts +104 -0
- package/dist/vue/composables/useServerStats.d.ts.map +1 -0
- package/dist/vue/composables/useTheme.d.ts +6 -0
- package/dist/vue/composables/useTheme.d.ts.map +1 -0
- package/dist/vue/index-qCQpBftQ.js +1233 -0
- package/dist/vue/index.d.ts +10 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +11 -0
- package/dist/vue/style.css +1 -0
- package/dist/vue/useApiClient-BQQ9CF-q.js +10 -0
- package/dist/vue/useResizableTable-BoivAevK.js +17 -0
- package/package.json +72 -10
- package/dist/src/dashboard/dashboard_routes.d.ts +0 -16
- package/dist/src/dashboard/dashboard_routes.d.ts.map +0 -1
- package/dist/src/dashboard/dashboard_routes.js +0 -77
- package/dist/src/routes/debug_routes.d.ts +0 -14
- package/dist/src/routes/debug_routes.d.ts.map +0 -1
- package/dist/src/routes/debug_routes.js +0 -42
- package/dist/src/routes/stats_routes.d.ts +0 -14
- package/dist/src/routes/stats_routes.d.ts.map +0 -1
- package/dist/src/routes/stats_routes.js +0 -27
|
@@ -1,2140 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side script for the debug panel.
|
|
3
|
-
*
|
|
4
|
-
* Handles panel toggle, tab switching, lazy data fetching,
|
|
5
|
-
* query/event/route table rendering, and log streaming.
|
|
6
|
-
*
|
|
7
|
-
* Config is read from data-* attributes on #ss-dbg-panel:
|
|
8
|
-
* data-logs-endpoint — logs API URL
|
|
9
|
-
*/
|
|
10
|
-
;(function () {
|
|
11
|
-
const REFRESH_INTERVAL = 3000
|
|
12
|
-
const panel = document.getElementById('ss-dbg-panel')
|
|
13
|
-
const wrench = document.getElementById('ss-dbg-wrench')
|
|
14
|
-
const BASE = (panel && panel.dataset.debugEndpoint) || '/admin/api/debug'
|
|
15
|
-
const closeBtn = document.getElementById('ss-dbg-close')
|
|
16
|
-
|
|
17
|
-
if (!panel || !wrench) return
|
|
18
|
-
|
|
19
|
-
// ── Theme detection & toggle ────────────────────────────────────
|
|
20
|
-
let themeOverride = localStorage.getItem('ss-dash-theme')
|
|
21
|
-
const themeBtn = document.getElementById('ss-dbg-theme-btn')
|
|
22
|
-
|
|
23
|
-
const applyPanelTheme = () => {
|
|
24
|
-
if (themeOverride) {
|
|
25
|
-
panel.setAttribute('data-ss-theme', themeOverride)
|
|
26
|
-
} else {
|
|
27
|
-
panel.removeAttribute('data-ss-theme')
|
|
28
|
-
}
|
|
29
|
-
if (themeBtn) {
|
|
30
|
-
const isDark =
|
|
31
|
-
themeOverride === 'dark' ||
|
|
32
|
-
(!themeOverride && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
33
|
-
themeBtn.textContent = isDark ? '\u2600' : '\u263D'
|
|
34
|
-
themeBtn.title = isDark ? 'Switch to light theme' : 'Switch to dark theme'
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (themeBtn) {
|
|
39
|
-
themeBtn.addEventListener('click', function () {
|
|
40
|
-
const isDark =
|
|
41
|
-
themeOverride === 'dark' ||
|
|
42
|
-
(!themeOverride && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
43
|
-
themeOverride = isDark ? 'light' : 'dark'
|
|
44
|
-
localStorage.setItem('ss-dash-theme', themeOverride)
|
|
45
|
-
applyPanelTheme()
|
|
46
|
-
// Sync stats bar if applyBarTheme exists globally
|
|
47
|
-
if (typeof window.__ssApplyBarTheme === 'function') window.__ssApplyBarTheme()
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
applyPanelTheme()
|
|
52
|
-
|
|
53
|
-
// Listen for cross-tab theme changes
|
|
54
|
-
window.addEventListener('storage', function (e) {
|
|
55
|
-
if (e.key === 'ss-dash-theme') {
|
|
56
|
-
themeOverride = e.newValue
|
|
57
|
-
applyPanelTheme()
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
const LOGS_ENDPOINT = panel.dataset.logsEndpoint || BASE + '/logs'
|
|
62
|
-
|
|
63
|
-
const tracingEnabled = panel.dataset.tracing === '1'
|
|
64
|
-
const dashboardPath = panel.dataset.dashboardPath || null
|
|
65
|
-
const DASH_API = dashboardPath ? dashboardPath.replace(/\/+$/, '') + '/api' : null
|
|
66
|
-
|
|
67
|
-
/** Build an SVG external-link icon for deep links. */
|
|
68
|
-
const deepLinkSvg =
|
|
69
|
-
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>'
|
|
70
|
-
|
|
71
|
-
/** Build a deep link anchor element HTML string. */
|
|
72
|
-
const deepLink = (section, id) => {
|
|
73
|
-
if (!dashboardPath) return ''
|
|
74
|
-
const href = dashboardPath + '#' + section + (id != null ? '?id=' + id : '')
|
|
75
|
-
return (
|
|
76
|
-
' <a href="' +
|
|
77
|
-
esc(href) +
|
|
78
|
-
'" target="_blank" class="ss-dbg-deeplink" title="Open in dashboard" onclick="event.stopPropagation()">' +
|
|
79
|
-
deepLinkSvg +
|
|
80
|
-
'</a>'
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let isOpen = false
|
|
85
|
-
let activeTab = tracingEnabled ? 'timeline' : 'queries'
|
|
86
|
-
const fetched = {}
|
|
87
|
-
let refreshTimer = null
|
|
88
|
-
let logFilter = 'all'
|
|
89
|
-
let cachedLogs = []
|
|
90
|
-
const currentPath = window.location.pathname
|
|
91
|
-
let isLive = false
|
|
92
|
-
let transmitSub = null
|
|
93
|
-
|
|
94
|
-
// ── Helpers ──────────────────────────────────────────────────────
|
|
95
|
-
const esc = (s) => {
|
|
96
|
-
if (typeof s !== 'string') s = '' + s
|
|
97
|
-
return s
|
|
98
|
-
.replace(/&/g, '&')
|
|
99
|
-
.replace(/</g, '<')
|
|
100
|
-
.replace(/>/g, '>')
|
|
101
|
-
.replace(/"/g, '"')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const timeAgo = (ts) => {
|
|
105
|
-
const diff = Math.floor((Date.now() - ts) / 1000)
|
|
106
|
-
if (diff < 60) return diff + 's ago'
|
|
107
|
-
if (diff < 3600) return Math.floor(diff / 60) + 'm ago'
|
|
108
|
-
return Math.floor(diff / 3600) + 'h ago'
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const formatTime = (ts) => {
|
|
112
|
-
const d = new Date(ts)
|
|
113
|
-
return (
|
|
114
|
-
d.toLocaleTimeString('en-US', {
|
|
115
|
-
hour12: false,
|
|
116
|
-
hour: '2-digit',
|
|
117
|
-
minute: '2-digit',
|
|
118
|
-
second: '2-digit',
|
|
119
|
-
}) +
|
|
120
|
-
'.' +
|
|
121
|
-
String(d.getMilliseconds()).padStart(3, '0')
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const eventPreview = (data) => {
|
|
126
|
-
if (!data) return '-'
|
|
127
|
-
try {
|
|
128
|
-
const parsed = JSON.parse(data)
|
|
129
|
-
return compactPreview(parsed, 100)
|
|
130
|
-
} catch {
|
|
131
|
-
return data.length > 100 ? data.slice(0, 100) + '...' : data
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const compactPreview = (val, maxLen) => {
|
|
136
|
-
if (val === null) return 'null'
|
|
137
|
-
if (typeof val === 'string')
|
|
138
|
-
return '"' + (val.length > 40 ? val.slice(0, 40) + '...' : val) + '"'
|
|
139
|
-
if (typeof val === 'number' || typeof val === 'boolean') return String(val)
|
|
140
|
-
if (Array.isArray(val)) {
|
|
141
|
-
if (val.length === 0) return '[]'
|
|
142
|
-
const items = val.slice(0, 3).map((v) => compactPreview(v, 30))
|
|
143
|
-
const s =
|
|
144
|
-
'[' + items.join(', ') + (val.length > 3 ? ', ...' + val.length + ' items' : '') + ']'
|
|
145
|
-
return s.length > maxLen ? '[' + val.length + ' items]' : s
|
|
146
|
-
}
|
|
147
|
-
if (typeof val === 'object') {
|
|
148
|
-
const keys = Object.keys(val)
|
|
149
|
-
if (keys.length === 0) return '{}'
|
|
150
|
-
const pairs = []
|
|
151
|
-
for (let i = 0; i < Math.min(keys.length, 4); i++) {
|
|
152
|
-
const k = keys[i]
|
|
153
|
-
const v = compactPreview(val[k], 30)
|
|
154
|
-
pairs.push(k + ': ' + v)
|
|
155
|
-
}
|
|
156
|
-
const s =
|
|
157
|
-
'{ ' + pairs.join(', ') + (keys.length > 4 ? ', ...+' + (keys.length - 4) : '') + ' }'
|
|
158
|
-
return s.length > maxLen
|
|
159
|
-
? '{ ' + keys.slice(0, 6).join(', ') + (keys.length > 6 ? ', ...' : '') + ' }'
|
|
160
|
-
: s
|
|
161
|
-
}
|
|
162
|
-
return String(val)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const methodClass = (m) =>
|
|
166
|
-
'ss-dbg-method ss-dbg-method-' + (typeof m === 'string' ? m.toLowerCase() : '')
|
|
167
|
-
|
|
168
|
-
const durationClass = (ms) => {
|
|
169
|
-
if (ms > 500) return 'ss-dbg-very-slow'
|
|
170
|
-
if (ms > 100) return 'ss-dbg-slow'
|
|
171
|
-
return ''
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ── Custom pane cell formatter ────────────────────────────────────
|
|
175
|
-
const formatCell = (value, col) => {
|
|
176
|
-
if (value === null || value === undefined) return '<span class="ss-dbg-c-dim">-</span>'
|
|
177
|
-
const fmt = col.format || 'text'
|
|
178
|
-
switch (fmt) {
|
|
179
|
-
case 'time':
|
|
180
|
-
return typeof value === 'number' ? formatTime(value) : esc(value)
|
|
181
|
-
case 'timeAgo':
|
|
182
|
-
return (
|
|
183
|
-
'<span class="ss-dbg-event-time">' +
|
|
184
|
-
(typeof value === 'number' ? timeAgo(value) : esc(value)) +
|
|
185
|
-
'</span>'
|
|
186
|
-
)
|
|
187
|
-
case 'duration': {
|
|
188
|
-
const ms = typeof value === 'number' ? value : parseFloat(value)
|
|
189
|
-
if (isNaN(ms)) return esc(value)
|
|
190
|
-
return (
|
|
191
|
-
'<span class="ss-dbg-duration ' + durationClass(ms) + '">' + ms.toFixed(2) + 'ms</span>'
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
case 'method':
|
|
195
|
-
return '<span class="' + methodClass(value) + '">' + esc(value) + '</span>'
|
|
196
|
-
case 'json': {
|
|
197
|
-
if (typeof value === 'string') {
|
|
198
|
-
try {
|
|
199
|
-
value = JSON.parse(value)
|
|
200
|
-
} catch {
|
|
201
|
-
/* use as-is */
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const preview = typeof value === 'object' ? compactPreview(value, 100) : String(value)
|
|
205
|
-
return (
|
|
206
|
-
'<span class="ss-dbg-data-preview" style="cursor:default">' + esc(preview) + '</span>'
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
case 'badge': {
|
|
210
|
-
const sv = String(value).toLowerCase()
|
|
211
|
-
const colorMap = col.badgeColorMap || {}
|
|
212
|
-
const color = colorMap[sv] || 'muted'
|
|
213
|
-
return (
|
|
214
|
-
'<span class="ss-dbg-badge ss-dbg-badge-' + esc(color) + '">' + esc(value) + '</span>'
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
default:
|
|
218
|
-
return esc(value)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// ── Toggle panel ────────────────────────────────────────────────
|
|
223
|
-
const togglePanel = () => {
|
|
224
|
-
isOpen = !isOpen
|
|
225
|
-
panel.classList.toggle('ss-dbg-open', isOpen)
|
|
226
|
-
wrench.classList.toggle('ss-dbg-active', isOpen)
|
|
227
|
-
|
|
228
|
-
if (isOpen) {
|
|
229
|
-
loadTab(activeTab)
|
|
230
|
-
startRefresh()
|
|
231
|
-
} else {
|
|
232
|
-
stopRefresh()
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
wrench.addEventListener('click', (e) => {
|
|
237
|
-
e.stopPropagation()
|
|
238
|
-
togglePanel()
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
if (closeBtn) {
|
|
242
|
-
closeBtn.addEventListener('click', () => {
|
|
243
|
-
if (isOpen) togglePanel()
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
document.addEventListener('keydown', (e) => {
|
|
248
|
-
if (e.key === 'Escape' && isOpen) togglePanel()
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
// ── Custom panes config ─────────────────────────────────────────
|
|
252
|
-
let customPanes = []
|
|
253
|
-
const customPaneState = {}
|
|
254
|
-
try {
|
|
255
|
-
const cfgEl = document.getElementById('ss-dbg-custom-panes-config')
|
|
256
|
-
if (cfgEl) customPanes = JSON.parse(cfgEl.textContent || '[]')
|
|
257
|
-
} catch {
|
|
258
|
-
/* ignore */
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
for (let i = 0; i < customPanes.length; i++) {
|
|
262
|
-
const cp = customPanes[i]
|
|
263
|
-
customPaneState[cp.id] = { data: [], fetched: false, filter: '' }
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ── Tab switching ───────────────────────────────────────────────
|
|
267
|
-
const tabs = panel.querySelectorAll('[data-ss-dbg-tab]')
|
|
268
|
-
tabs.forEach((tab) => {
|
|
269
|
-
tab.addEventListener('click', () => {
|
|
270
|
-
const name = tab.getAttribute('data-ss-dbg-tab')
|
|
271
|
-
if (name === activeTab) return
|
|
272
|
-
|
|
273
|
-
tabs.forEach((t) => t.classList.remove('ss-dbg-active'))
|
|
274
|
-
panel.querySelectorAll('.ss-dbg-pane').forEach((p) => p.classList.remove('ss-dbg-active'))
|
|
275
|
-
|
|
276
|
-
tab.classList.add('ss-dbg-active')
|
|
277
|
-
tab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })
|
|
278
|
-
const pane = document.getElementById('ss-dbg-pane-' + name)
|
|
279
|
-
if (pane) pane.classList.add('ss-dbg-active')
|
|
280
|
-
|
|
281
|
-
activeTab = name
|
|
282
|
-
loadTab(name)
|
|
283
|
-
})
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
// ── Data loading ────────────────────────────────────────────────
|
|
287
|
-
const loadTab = (name) => {
|
|
288
|
-
if (name === 'timeline') fetchTraces()
|
|
289
|
-
else if (name === 'queries') fetchQueries()
|
|
290
|
-
else if (name === 'events') fetchEvents()
|
|
291
|
-
else if (name === 'routes' && !fetched.routes) fetchRoutes()
|
|
292
|
-
else if (name === 'logs') fetchLogs()
|
|
293
|
-
else if (name === 'emails') fetchEmails()
|
|
294
|
-
else if (name === 'cache') fetchCache()
|
|
295
|
-
else if (name === 'jobs') fetchJobs()
|
|
296
|
-
else if (name === 'config' && !fetched.config) fetchConfig()
|
|
297
|
-
else {
|
|
298
|
-
const cp = customPanes.find((p) => p.id === name)
|
|
299
|
-
if (cp) {
|
|
300
|
-
if (cp.fetchOnce && customPaneState[cp.id].fetched) return
|
|
301
|
-
fetchCustomPane(cp)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const fetchJSON = (url) =>
|
|
307
|
-
fetch(url, { credentials: 'same-origin' }).then((r) => {
|
|
308
|
-
if (!r.ok) throw new Error(r.status)
|
|
309
|
-
return r.json()
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
// ── Queries Tab ─────────────────────────────────────────────────
|
|
313
|
-
const querySearchInput = document.getElementById('ss-dbg-search-queries')
|
|
314
|
-
const querySummaryEl = document.getElementById('ss-dbg-queries-summary')
|
|
315
|
-
const queryBodyEl = document.getElementById('ss-dbg-queries-body')
|
|
316
|
-
const queryClearBtn = document.getElementById('ss-dbg-queries-clear')
|
|
317
|
-
let cachedQueries = { queries: [], summary: {} }
|
|
318
|
-
|
|
319
|
-
const fetchQueries = () => {
|
|
320
|
-
fetchJSON(BASE + '/queries')
|
|
321
|
-
.then((data) => {
|
|
322
|
-
cachedQueries = data
|
|
323
|
-
renderQueries()
|
|
324
|
-
})
|
|
325
|
-
.catch(() => {
|
|
326
|
-
queryBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load queries</div>'
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const renderQueries = () => {
|
|
331
|
-
const filter = (querySearchInput ? querySearchInput.value : '').toLowerCase()
|
|
332
|
-
const queries = cachedQueries.queries || []
|
|
333
|
-
const summary = cachedQueries.summary || {}
|
|
334
|
-
|
|
335
|
-
if (querySummaryEl) {
|
|
336
|
-
querySummaryEl.textContent =
|
|
337
|
-
summary.total +
|
|
338
|
-
' queries' +
|
|
339
|
-
(summary.slow > 0 ? ', ' + summary.slow + ' slow' : '') +
|
|
340
|
-
(summary.duplicates > 0 ? ', ' + summary.duplicates + ' dup' : '') +
|
|
341
|
-
', avg ' +
|
|
342
|
-
(summary.avgDuration || 0).toFixed(1) +
|
|
343
|
-
'ms'
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const badge = document.getElementById('ss-dbg-query-badge')
|
|
347
|
-
if (badge && activeTab === 'queries') {
|
|
348
|
-
badge.textContent =
|
|
349
|
-
summary.total + ' queries, avg ' + (summary.avgDuration || 0).toFixed(1) + 'ms'
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let filtered = queries
|
|
353
|
-
if (filter) {
|
|
354
|
-
filtered = queries.filter(
|
|
355
|
-
(q) =>
|
|
356
|
-
q.sql.toLowerCase().indexOf(filter) !== -1 ||
|
|
357
|
-
(q.model || '').toLowerCase().indexOf(filter) !== -1 ||
|
|
358
|
-
q.method.toLowerCase().indexOf(filter) !== -1
|
|
359
|
-
)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (filtered.length === 0) {
|
|
363
|
-
queryBodyEl.innerHTML =
|
|
364
|
-
'<div class="ss-dbg-empty">' +
|
|
365
|
-
(filter ? 'No matching queries' : 'No queries recorded yet') +
|
|
366
|
-
'</div>'
|
|
367
|
-
return
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const sqlCounts = {}
|
|
371
|
-
for (let i = 0; i < queries.length; i++) {
|
|
372
|
-
sqlCounts[queries[i].sql] = (sqlCounts[queries[i].sql] || 0) + 1
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
let html =
|
|
376
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
377
|
-
'<th style="width:64px">#</th>' +
|
|
378
|
-
'<th>SQL</th>' +
|
|
379
|
-
'<th style="width:70px">Duration</th>' +
|
|
380
|
-
'<th style="width:60px">Method</th>' +
|
|
381
|
-
'<th style="width:100px">Model</th>' +
|
|
382
|
-
'<th style="width:60px">Time</th>' +
|
|
383
|
-
'</tr></thead><tbody>'
|
|
384
|
-
|
|
385
|
-
for (let j = 0; j < filtered.length; j++) {
|
|
386
|
-
const q = filtered[j]
|
|
387
|
-
const durClass = durationClass(q.duration)
|
|
388
|
-
const dupCount = sqlCounts[q.sql] || 1
|
|
389
|
-
html +=
|
|
390
|
-
'<tr>' +
|
|
391
|
-
'<td class="ss-dbg-c-dim" style="white-space:nowrap">' +
|
|
392
|
-
q.id +
|
|
393
|
-
deepLink('queries', q.id) +
|
|
394
|
-
'</td>' +
|
|
395
|
-
'<td><span class="ss-dbg-sql" title="Click to expand" onclick="this.classList.toggle(\'ss-dbg-expanded\')">' +
|
|
396
|
-
esc(q.sql) +
|
|
397
|
-
'</span>' +
|
|
398
|
-
(dupCount > 1 ? ' <span class="ss-dbg-dup">x' + dupCount + '</span>' : '') +
|
|
399
|
-
'</td>' +
|
|
400
|
-
'<td class="ss-dbg-duration ' +
|
|
401
|
-
durClass +
|
|
402
|
-
'">' +
|
|
403
|
-
q.duration.toFixed(2) +
|
|
404
|
-
'ms</td>' +
|
|
405
|
-
'<td><span class="' +
|
|
406
|
-
methodClass(q.method) +
|
|
407
|
-
'">' +
|
|
408
|
-
esc(q.method) +
|
|
409
|
-
'</span></td>' +
|
|
410
|
-
'<td class="ss-dbg-c-muted">' +
|
|
411
|
-
esc(q.model || '-') +
|
|
412
|
-
'</td>' +
|
|
413
|
-
'<td class="ss-dbg-event-time">' +
|
|
414
|
-
timeAgo(q.timestamp) +
|
|
415
|
-
'</td>' +
|
|
416
|
-
'</tr>'
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
html += '</tbody></table>'
|
|
420
|
-
queryBodyEl.innerHTML = html
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (querySearchInput) querySearchInput.addEventListener('input', renderQueries)
|
|
424
|
-
if (queryClearBtn) {
|
|
425
|
-
queryClearBtn.addEventListener('click', () => {
|
|
426
|
-
cachedQueries = { queries: [], summary: { total: 0, slow: 0, duplicates: 0, avgDuration: 0 } }
|
|
427
|
-
renderQueries()
|
|
428
|
-
})
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// ── Events Tab ──────────────────────────────────────────────────
|
|
432
|
-
const eventSearchInput = document.getElementById('ss-dbg-search-events')
|
|
433
|
-
const eventSummaryEl = document.getElementById('ss-dbg-events-summary')
|
|
434
|
-
const eventBodyEl = document.getElementById('ss-dbg-events-body')
|
|
435
|
-
const eventClearBtn = document.getElementById('ss-dbg-events-clear')
|
|
436
|
-
let cachedEvents = { events: [], total: 0 }
|
|
437
|
-
|
|
438
|
-
const fetchEvents = () => {
|
|
439
|
-
fetchJSON(BASE + '/events')
|
|
440
|
-
.then((data) => {
|
|
441
|
-
cachedEvents = data
|
|
442
|
-
renderEvents()
|
|
443
|
-
})
|
|
444
|
-
.catch(() => {
|
|
445
|
-
eventBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load events</div>'
|
|
446
|
-
})
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const renderEvents = () => {
|
|
450
|
-
const filter = (eventSearchInput ? eventSearchInput.value : '').toLowerCase()
|
|
451
|
-
const events = cachedEvents.events || []
|
|
452
|
-
|
|
453
|
-
if (eventSummaryEl) {
|
|
454
|
-
eventSummaryEl.textContent = cachedEvents.total + ' events'
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
let filtered = events
|
|
458
|
-
if (filter) {
|
|
459
|
-
filtered = events.filter(
|
|
460
|
-
(e) =>
|
|
461
|
-
e.event.toLowerCase().indexOf(filter) !== -1 ||
|
|
462
|
-
(e.data || '').toLowerCase().indexOf(filter) !== -1
|
|
463
|
-
)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (filtered.length === 0) {
|
|
467
|
-
eventBodyEl.innerHTML =
|
|
468
|
-
'<div class="ss-dbg-empty">' +
|
|
469
|
-
(filter ? 'No matching events' : 'No events recorded yet') +
|
|
470
|
-
'</div>'
|
|
471
|
-
return
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
let html =
|
|
475
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
476
|
-
'<th style="width:64px">#</th>' +
|
|
477
|
-
'<th>Event</th>' +
|
|
478
|
-
'<th>Data</th>' +
|
|
479
|
-
'<th style="width:100px">Time</th>' +
|
|
480
|
-
'</tr></thead><tbody>'
|
|
481
|
-
|
|
482
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
483
|
-
const ev = filtered[i]
|
|
484
|
-
const hasData = ev.data && ev.data !== '-'
|
|
485
|
-
const preview = hasData ? eventPreview(ev.data) : '-'
|
|
486
|
-
html +=
|
|
487
|
-
'<tr>' +
|
|
488
|
-
'<td class="ss-dbg-c-dim" style="white-space:nowrap">' +
|
|
489
|
-
ev.id +
|
|
490
|
-
deepLink('events', ev.id) +
|
|
491
|
-
'</td>' +
|
|
492
|
-
'<td class="ss-dbg-event-name">' +
|
|
493
|
-
esc(ev.event) +
|
|
494
|
-
'</td>' +
|
|
495
|
-
'<td class="ss-dbg-event-data">' +
|
|
496
|
-
(hasData
|
|
497
|
-
? '<span class="ss-dbg-data-preview" data-ev-idx="' +
|
|
498
|
-
i +
|
|
499
|
-
'">' +
|
|
500
|
-
esc(preview) +
|
|
501
|
-
'</span>' +
|
|
502
|
-
'<pre class="ss-dbg-data-full" id="ss-dbg-evdata-' +
|
|
503
|
-
i +
|
|
504
|
-
'" style="display:none">' +
|
|
505
|
-
esc(ev.data) +
|
|
506
|
-
'</pre>' +
|
|
507
|
-
'<button type="button" class="ss-dbg-copy-btn" data-copy-idx="' +
|
|
508
|
-
i +
|
|
509
|
-
'" title="Copy JSON">⎘</button>'
|
|
510
|
-
: '<span class="ss-dbg-c-dim">-</span>') +
|
|
511
|
-
'</td>' +
|
|
512
|
-
'<td class="ss-dbg-event-time">' +
|
|
513
|
-
formatTime(ev.timestamp) +
|
|
514
|
-
'</td>' +
|
|
515
|
-
'</tr>'
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
html += '</tbody></table>'
|
|
519
|
-
eventBodyEl.innerHTML = html
|
|
520
|
-
|
|
521
|
-
// Toggle expand on preview click
|
|
522
|
-
eventBodyEl.querySelectorAll('.ss-dbg-data-preview').forEach((el) => {
|
|
523
|
-
el.addEventListener('click', () => {
|
|
524
|
-
const idx = el.getAttribute('data-ev-idx')
|
|
525
|
-
const pre = document.getElementById('ss-dbg-evdata-' + idx)
|
|
526
|
-
if (pre) {
|
|
527
|
-
const open = pre.style.display !== 'none'
|
|
528
|
-
pre.style.display = open ? 'none' : 'block'
|
|
529
|
-
el.style.display = open ? '' : 'none'
|
|
530
|
-
}
|
|
531
|
-
})
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
// Collapse on full-data click
|
|
535
|
-
eventBodyEl.querySelectorAll('.ss-dbg-data-full').forEach((el) => {
|
|
536
|
-
el.addEventListener('click', () => {
|
|
537
|
-
el.style.display = 'none'
|
|
538
|
-
const idx = el.id.replace('ss-dbg-evdata-', '')
|
|
539
|
-
const preview = eventBodyEl.querySelector('[data-ev-idx="' + idx + '"]')
|
|
540
|
-
if (preview) preview.style.display = ''
|
|
541
|
-
})
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
// Copy button
|
|
545
|
-
eventBodyEl.querySelectorAll('.ss-dbg-copy-btn').forEach((btn) => {
|
|
546
|
-
btn.addEventListener('click', (e) => {
|
|
547
|
-
e.stopPropagation()
|
|
548
|
-
const idx = btn.getAttribute('data-copy-idx')
|
|
549
|
-
const data = filtered[idx]?.data || ''
|
|
550
|
-
navigator.clipboard.writeText(data).then(() => {
|
|
551
|
-
btn.textContent = '\u2713'
|
|
552
|
-
setTimeout(() => {
|
|
553
|
-
btn.innerHTML = '⎘'
|
|
554
|
-
}, 1200)
|
|
555
|
-
})
|
|
556
|
-
})
|
|
557
|
-
})
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
if (eventSearchInput) eventSearchInput.addEventListener('input', renderEvents)
|
|
561
|
-
if (eventClearBtn) {
|
|
562
|
-
eventClearBtn.addEventListener('click', () => {
|
|
563
|
-
cachedEvents = { events: [], total: 0 }
|
|
564
|
-
renderEvents()
|
|
565
|
-
})
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// ── Routes Tab ──────────────────────────────────────────────────
|
|
569
|
-
const routeSearchInput = document.getElementById('ss-dbg-search-routes')
|
|
570
|
-
const routeSummaryEl = document.getElementById('ss-dbg-routes-summary')
|
|
571
|
-
const routeBodyEl = document.getElementById('ss-dbg-routes-body')
|
|
572
|
-
let cachedRoutes = { routes: [], total: 0 }
|
|
573
|
-
|
|
574
|
-
const fetchRoutes = () => {
|
|
575
|
-
fetchJSON(BASE + '/routes')
|
|
576
|
-
.then((data) => {
|
|
577
|
-
cachedRoutes = data
|
|
578
|
-
fetched.routes = true
|
|
579
|
-
renderRoutes()
|
|
580
|
-
})
|
|
581
|
-
.catch(() => {
|
|
582
|
-
routeBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load routes</div>'
|
|
583
|
-
})
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const renderRoutes = () => {
|
|
587
|
-
const filter = (routeSearchInput ? routeSearchInput.value : '').toLowerCase()
|
|
588
|
-
const routes = cachedRoutes.routes || []
|
|
589
|
-
|
|
590
|
-
if (routeSummaryEl) {
|
|
591
|
-
routeSummaryEl.textContent = cachedRoutes.total + ' routes'
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
let filtered = routes
|
|
595
|
-
if (filter) {
|
|
596
|
-
filtered = routes.filter(
|
|
597
|
-
(r) =>
|
|
598
|
-
r.pattern.toLowerCase().indexOf(filter) !== -1 ||
|
|
599
|
-
r.method.toLowerCase().indexOf(filter) !== -1 ||
|
|
600
|
-
(r.name || '').toLowerCase().indexOf(filter) !== -1 ||
|
|
601
|
-
r.handler.toLowerCase().indexOf(filter) !== -1 ||
|
|
602
|
-
r.middleware.join(' ').toLowerCase().indexOf(filter) !== -1
|
|
603
|
-
)
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
if (filtered.length === 0) {
|
|
607
|
-
routeBodyEl.innerHTML =
|
|
608
|
-
'<div class="ss-dbg-empty">' +
|
|
609
|
-
(filter ? 'No matching routes' : 'No routes available') +
|
|
610
|
-
'</div>'
|
|
611
|
-
return
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
let html =
|
|
615
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
616
|
-
'<th style="width:60px">Method</th>' +
|
|
617
|
-
'<th>Pattern</th>' +
|
|
618
|
-
'<th style="width:140px">Name</th>' +
|
|
619
|
-
'<th>Handler</th>' +
|
|
620
|
-
'<th>Middleware</th>' +
|
|
621
|
-
'</tr></thead><tbody>'
|
|
622
|
-
|
|
623
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
624
|
-
const r = filtered[i]
|
|
625
|
-
const isCurrent =
|
|
626
|
-
currentPath === r.pattern ||
|
|
627
|
-
currentPath.match(new RegExp('^' + r.pattern.replace(/:[^/]+/g, '[^/]+') + '$'))
|
|
628
|
-
html +=
|
|
629
|
-
'<tr' +
|
|
630
|
-
(isCurrent ? ' class="ss-dbg-current-route"' : '') +
|
|
631
|
-
'>' +
|
|
632
|
-
'<td><span class="' +
|
|
633
|
-
methodClass(r.method) +
|
|
634
|
-
'">' +
|
|
635
|
-
esc(r.method) +
|
|
636
|
-
'</span></td>' +
|
|
637
|
-
'<td>' +
|
|
638
|
-
esc(r.pattern) +
|
|
639
|
-
'</td>' +
|
|
640
|
-
'<td class="ss-dbg-c-muted">' +
|
|
641
|
-
esc(r.name || '-') +
|
|
642
|
-
'</td>' +
|
|
643
|
-
'<td class="ss-dbg-c-sql">' +
|
|
644
|
-
esc(r.handler) +
|
|
645
|
-
'</td>' +
|
|
646
|
-
'<td class="ss-dbg-c-dim" style="font-size:10px">' +
|
|
647
|
-
(r.middleware.length ? esc(r.middleware.join(', ')) : '-') +
|
|
648
|
-
'</td>' +
|
|
649
|
-
'</tr>'
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
html += '</tbody></table>'
|
|
653
|
-
routeBodyEl.innerHTML = html
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (routeSearchInput) routeSearchInput.addEventListener('input', renderRoutes)
|
|
657
|
-
|
|
658
|
-
// ── Logs Tab ────────────────────────────────────────────────────
|
|
659
|
-
const logBodyEl = document.getElementById('ss-dbg-logs-body')
|
|
660
|
-
const logFilters = panel.querySelectorAll('[data-ss-dbg-level]')
|
|
661
|
-
const logReqIdInput = document.getElementById('ss-dbg-log-reqid')
|
|
662
|
-
const logReqIdClear = document.getElementById('ss-dbg-log-reqid-clear')
|
|
663
|
-
let logReqIdFilter = ''
|
|
664
|
-
|
|
665
|
-
const setReqIdFilter = (id) => {
|
|
666
|
-
logReqIdFilter = id || ''
|
|
667
|
-
if (logReqIdInput) logReqIdInput.value = logReqIdFilter
|
|
668
|
-
if (logReqIdClear) logReqIdClear.style.display = logReqIdFilter ? '' : 'none'
|
|
669
|
-
renderLogs()
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
if (logReqIdInput) {
|
|
673
|
-
logReqIdInput.addEventListener('input', () => {
|
|
674
|
-
logReqIdFilter = logReqIdInput.value.trim()
|
|
675
|
-
if (logReqIdClear) logReqIdClear.style.display = logReqIdFilter ? '' : 'none'
|
|
676
|
-
renderLogs()
|
|
677
|
-
})
|
|
678
|
-
}
|
|
679
|
-
if (logReqIdClear) {
|
|
680
|
-
logReqIdClear.addEventListener('click', () => setReqIdFilter(''))
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
const fetchLogs = () => {
|
|
684
|
-
fetchJSON(LOGS_ENDPOINT)
|
|
685
|
-
.then((data) => {
|
|
686
|
-
cachedLogs = Array.isArray(data) ? data : data.logs || data.entries || []
|
|
687
|
-
renderLogs()
|
|
688
|
-
})
|
|
689
|
-
.catch(() => {
|
|
690
|
-
logBodyEl.innerHTML = '<div class="ss-dbg-empty">No log endpoint available</div>'
|
|
691
|
-
})
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
const shortReqId = (id) => (id ? id.slice(0, 8) : '')
|
|
695
|
-
|
|
696
|
-
const renderLogs = () => {
|
|
697
|
-
let entries = cachedLogs
|
|
698
|
-
|
|
699
|
-
if (logFilter !== 'all') {
|
|
700
|
-
entries = entries.filter((e) => {
|
|
701
|
-
const level = (e.levelName || e.level_name || '').toLowerCase()
|
|
702
|
-
if (logFilter === 'error') return level === 'error' || level === 'fatal'
|
|
703
|
-
return level === logFilter
|
|
704
|
-
})
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
if (logReqIdFilter) {
|
|
708
|
-
const f = logReqIdFilter.toLowerCase()
|
|
709
|
-
entries = entries.filter((e) => {
|
|
710
|
-
const rid = (e.request_id || e['x-request-id'] || '').toLowerCase()
|
|
711
|
-
return rid.indexOf(f) !== -1
|
|
712
|
-
})
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
if (entries.length === 0) {
|
|
716
|
-
let hint = ''
|
|
717
|
-
if (logReqIdFilter) hint = ' matching request ' + logReqIdFilter
|
|
718
|
-
else if (logFilter !== 'all') hint = ' for ' + logFilter
|
|
719
|
-
logBodyEl.innerHTML = '<div class="ss-dbg-empty">No log entries' + hint + '</div>'
|
|
720
|
-
return
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
const shown = entries.slice(-200).reverse()
|
|
724
|
-
let html = ''
|
|
725
|
-
|
|
726
|
-
for (let i = 0; i < shown.length; i++) {
|
|
727
|
-
const e = shown[i]
|
|
728
|
-
const level = (e.levelName || e.level_name || 'info').toLowerCase()
|
|
729
|
-
const msg = e.msg || e.message || JSON.stringify(e)
|
|
730
|
-
const ts = e.time || e.timestamp || 0
|
|
731
|
-
const reqId = e.request_id || e['x-request-id'] || ''
|
|
732
|
-
|
|
733
|
-
html +=
|
|
734
|
-
'<div class="ss-dbg-log-entry">' +
|
|
735
|
-
'<span class="ss-dbg-log-level ss-dbg-log-level-' +
|
|
736
|
-
esc(level) +
|
|
737
|
-
'">' +
|
|
738
|
-
esc(level.toUpperCase()) +
|
|
739
|
-
'</span>' +
|
|
740
|
-
'<span class="ss-dbg-log-time">' +
|
|
741
|
-
(ts ? formatTime(ts) : '-') +
|
|
742
|
-
'</span>' +
|
|
743
|
-
(reqId
|
|
744
|
-
? '<span class="ss-dbg-log-reqid" data-reqid="' +
|
|
745
|
-
esc(reqId) +
|
|
746
|
-
'" title="' +
|
|
747
|
-
esc(reqId) +
|
|
748
|
-
'">' +
|
|
749
|
-
esc(shortReqId(reqId)) +
|
|
750
|
-
'</span>'
|
|
751
|
-
: '<span class="ss-dbg-log-reqid-empty">-</span>') +
|
|
752
|
-
'<span class="ss-dbg-log-msg">' +
|
|
753
|
-
esc(msg) +
|
|
754
|
-
'</span>' +
|
|
755
|
-
'</div>'
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
logBodyEl.innerHTML = html
|
|
759
|
-
|
|
760
|
-
// Click request ID to filter
|
|
761
|
-
logBodyEl.querySelectorAll('.ss-dbg-log-reqid').forEach((el) => {
|
|
762
|
-
el.addEventListener('click', () => {
|
|
763
|
-
setReqIdFilter(el.getAttribute('data-reqid'))
|
|
764
|
-
})
|
|
765
|
-
})
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
logFilters.forEach((btn) => {
|
|
769
|
-
btn.addEventListener('click', () => {
|
|
770
|
-
logFilters.forEach((b) => b.classList.remove('ss-dbg-active'))
|
|
771
|
-
btn.classList.add('ss-dbg-active')
|
|
772
|
-
logFilter = btn.getAttribute('data-ss-dbg-level')
|
|
773
|
-
renderLogs()
|
|
774
|
-
})
|
|
775
|
-
})
|
|
776
|
-
|
|
777
|
-
// ── Emails Tab ─────────────────────────────────────────────────
|
|
778
|
-
const emailSearchInput = document.getElementById('ss-dbg-search-emails')
|
|
779
|
-
const emailSummaryEl = document.getElementById('ss-dbg-emails-summary')
|
|
780
|
-
const emailBodyEl = document.getElementById('ss-dbg-emails-body')
|
|
781
|
-
const emailClearBtn = document.getElementById('ss-dbg-emails-clear')
|
|
782
|
-
const emailPreviewEl = document.getElementById('ss-dbg-email-preview')
|
|
783
|
-
const emailPreviewMeta = document.getElementById('ss-dbg-email-preview-meta')
|
|
784
|
-
const emailPreviewClose = document.getElementById('ss-dbg-email-preview-close')
|
|
785
|
-
const emailIframe = document.getElementById('ss-dbg-email-iframe')
|
|
786
|
-
let cachedEmails = { emails: [], total: 0 }
|
|
787
|
-
|
|
788
|
-
const fetchEmails = () => {
|
|
789
|
-
fetchJSON(BASE + '/emails')
|
|
790
|
-
.then((data) => {
|
|
791
|
-
cachedEmails = data
|
|
792
|
-
renderEmails()
|
|
793
|
-
})
|
|
794
|
-
.catch(() => {
|
|
795
|
-
if (emailBodyEl)
|
|
796
|
-
emailBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load emails</div>'
|
|
797
|
-
})
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const renderEmails = () => {
|
|
801
|
-
if (!emailBodyEl) return
|
|
802
|
-
const filter = (emailSearchInput ? emailSearchInput.value : '').toLowerCase()
|
|
803
|
-
const emails = cachedEmails.emails || []
|
|
804
|
-
|
|
805
|
-
if (emailSummaryEl) {
|
|
806
|
-
emailSummaryEl.textContent = cachedEmails.total + ' emails'
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
let filtered = emails
|
|
810
|
-
if (filter) {
|
|
811
|
-
filtered = emails.filter(
|
|
812
|
-
(e) =>
|
|
813
|
-
(e.from || '').toLowerCase().indexOf(filter) !== -1 ||
|
|
814
|
-
(e.to || '').toLowerCase().indexOf(filter) !== -1 ||
|
|
815
|
-
(e.subject || '').toLowerCase().indexOf(filter) !== -1 ||
|
|
816
|
-
(e.mailer || '').toLowerCase().indexOf(filter) !== -1
|
|
817
|
-
)
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
if (filtered.length === 0) {
|
|
821
|
-
emailBodyEl.innerHTML =
|
|
822
|
-
'<div class="ss-dbg-empty">' +
|
|
823
|
-
(filter ? 'No matching emails' : 'No emails captured yet') +
|
|
824
|
-
'</div>'
|
|
825
|
-
return
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
let html =
|
|
829
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
830
|
-
'<th style="width:64px">#</th>' +
|
|
831
|
-
'<th style="width:160px">From</th>' +
|
|
832
|
-
'<th style="width:160px">To</th>' +
|
|
833
|
-
'<th>Subject</th>' +
|
|
834
|
-
'<th style="width:60px">Status</th>' +
|
|
835
|
-
'<th style="width:60px">Mailer</th>' +
|
|
836
|
-
'<th style="width:30px" title="Attachments">📎</th>' +
|
|
837
|
-
'<th style="width:70px">Time</th>' +
|
|
838
|
-
'</tr></thead><tbody>'
|
|
839
|
-
|
|
840
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
841
|
-
const e = filtered[i]
|
|
842
|
-
html +=
|
|
843
|
-
'<tr class="ss-dbg-email-row" data-email-id="' +
|
|
844
|
-
e.id +
|
|
845
|
-
'">' +
|
|
846
|
-
'<td class="ss-dbg-c-dim" style="white-space:nowrap">' +
|
|
847
|
-
e.id +
|
|
848
|
-
deepLink('emails', e.id) +
|
|
849
|
-
'</td>' +
|
|
850
|
-
'<td class="ss-dbg-c-secondary" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px" title="' +
|
|
851
|
-
esc(e.from) +
|
|
852
|
-
'">' +
|
|
853
|
-
esc(e.from) +
|
|
854
|
-
'</td>' +
|
|
855
|
-
'<td class="ss-dbg-c-secondary" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px" title="' +
|
|
856
|
-
esc(e.to) +
|
|
857
|
-
'">' +
|
|
858
|
-
esc(e.to) +
|
|
859
|
-
'</td>' +
|
|
860
|
-
'<td class="ss-dbg-c-sql" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' +
|
|
861
|
-
esc(e.subject) +
|
|
862
|
-
'</td>' +
|
|
863
|
-
'<td><span class="ss-dbg-email-status ss-dbg-email-status-' +
|
|
864
|
-
esc(e.status) +
|
|
865
|
-
'">' +
|
|
866
|
-
esc(e.status) +
|
|
867
|
-
'</span></td>' +
|
|
868
|
-
'<td class="ss-dbg-c-muted">' +
|
|
869
|
-
esc(e.mailer) +
|
|
870
|
-
'</td>' +
|
|
871
|
-
'<td class="ss-dbg-c-dim" style="text-align:center">' +
|
|
872
|
-
(e.attachmentCount > 0 ? e.attachmentCount : '-') +
|
|
873
|
-
'</td>' +
|
|
874
|
-
'<td class="ss-dbg-event-time">' +
|
|
875
|
-
timeAgo(e.timestamp) +
|
|
876
|
-
'</td>' +
|
|
877
|
-
'</tr>'
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
html += '</tbody></table>'
|
|
881
|
-
emailBodyEl.innerHTML = html
|
|
882
|
-
|
|
883
|
-
// Click row to open preview
|
|
884
|
-
emailBodyEl.querySelectorAll('.ss-dbg-email-row').forEach((row) => {
|
|
885
|
-
row.addEventListener('click', () => {
|
|
886
|
-
const id = row.getAttribute('data-email-id')
|
|
887
|
-
showEmailPreview(id, filtered)
|
|
888
|
-
})
|
|
889
|
-
})
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
const showEmailPreview = (id, emails) => {
|
|
893
|
-
if (!emailPreviewEl || !emailIframe || !emailPreviewMeta) return
|
|
894
|
-
const email = emails.find((e) => String(e.id) === String(id))
|
|
895
|
-
|
|
896
|
-
if (emailPreviewMeta && email) {
|
|
897
|
-
emailPreviewMeta.innerHTML =
|
|
898
|
-
'<strong>Subject:</strong> ' +
|
|
899
|
-
esc(email.subject) +
|
|
900
|
-
' | <strong>From:</strong> ' +
|
|
901
|
-
esc(email.from) +
|
|
902
|
-
' | <strong>To:</strong> ' +
|
|
903
|
-
esc(email.to) +
|
|
904
|
-
(email.cc ? ' | <strong>CC:</strong> ' + esc(email.cc) : '') +
|
|
905
|
-
' | <strong>Status:</strong> <span class="ss-dbg-email-status ss-dbg-email-status-' +
|
|
906
|
-
esc(email.status) +
|
|
907
|
-
'">' +
|
|
908
|
-
esc(email.status) +
|
|
909
|
-
'</span>' +
|
|
910
|
-
' | <strong>Mailer:</strong> ' +
|
|
911
|
-
esc(email.mailer)
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
emailIframe.src = BASE + '/emails/' + id + '/preview'
|
|
915
|
-
emailPreviewEl.style.display = 'flex'
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (emailPreviewClose) {
|
|
919
|
-
emailPreviewClose.addEventListener('click', () => {
|
|
920
|
-
if (emailPreviewEl) emailPreviewEl.style.display = 'none'
|
|
921
|
-
if (emailIframe) emailIframe.src = 'about:blank'
|
|
922
|
-
})
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
if (emailSearchInput) emailSearchInput.addEventListener('input', renderEmails)
|
|
926
|
-
if (emailClearBtn) {
|
|
927
|
-
emailClearBtn.addEventListener('click', () => {
|
|
928
|
-
cachedEmails = { emails: [], total: 0 }
|
|
929
|
-
renderEmails()
|
|
930
|
-
})
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// ── Timeline Tab ────────────────────────────────────────────────
|
|
934
|
-
const tlSearchInput = document.getElementById('ss-dbg-search-timeline')
|
|
935
|
-
const tlSummaryEl = document.getElementById('ss-dbg-timeline-summary')
|
|
936
|
-
const tlBodyEl = document.getElementById('ss-dbg-timeline-body')
|
|
937
|
-
const tlListEl = document.getElementById('ss-dbg-timeline-list')
|
|
938
|
-
const tlDetailEl = document.getElementById('ss-dbg-timeline-detail')
|
|
939
|
-
const tlBackBtn = document.getElementById('ss-dbg-tl-back')
|
|
940
|
-
const tlDetailTitle = document.getElementById('ss-dbg-tl-detail-title')
|
|
941
|
-
const tlWaterfall = document.getElementById('ss-dbg-tl-waterfall')
|
|
942
|
-
let cachedTraces = { traces: [], total: 0 }
|
|
943
|
-
|
|
944
|
-
const statusClass = (code) => {
|
|
945
|
-
if (code >= 500) return 'ss-dbg-status-5xx'
|
|
946
|
-
if (code >= 400) return 'ss-dbg-status-4xx'
|
|
947
|
-
if (code >= 300) return 'ss-dbg-status-3xx'
|
|
948
|
-
return 'ss-dbg-status-2xx'
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
const fetchTraces = () => {
|
|
952
|
-
if (!tracingEnabled) return
|
|
953
|
-
fetchJSON(BASE + '/traces')
|
|
954
|
-
.then((data) => {
|
|
955
|
-
cachedTraces = data
|
|
956
|
-
renderTraces()
|
|
957
|
-
})
|
|
958
|
-
.catch(() => {
|
|
959
|
-
if (tlBodyEl) tlBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load traces</div>'
|
|
960
|
-
})
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
const renderTraces = () => {
|
|
964
|
-
if (!tlBodyEl) return
|
|
965
|
-
const filter = (tlSearchInput ? tlSearchInput.value : '').toLowerCase()
|
|
966
|
-
const traces = cachedTraces.traces || []
|
|
967
|
-
|
|
968
|
-
if (tlSummaryEl) {
|
|
969
|
-
tlSummaryEl.textContent = cachedTraces.total + ' requests'
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
let filtered = traces
|
|
973
|
-
if (filter) {
|
|
974
|
-
filtered = traces.filter(
|
|
975
|
-
(t) =>
|
|
976
|
-
t.url.toLowerCase().indexOf(filter) !== -1 ||
|
|
977
|
-
t.method.toLowerCase().indexOf(filter) !== -1
|
|
978
|
-
)
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
if (filtered.length === 0) {
|
|
982
|
-
tlBodyEl.innerHTML =
|
|
983
|
-
'<div class="ss-dbg-empty">' +
|
|
984
|
-
(filter ? 'No matching requests' : 'No requests traced yet') +
|
|
985
|
-
'</div>'
|
|
986
|
-
return
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
let html =
|
|
990
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
991
|
-
'<th style="width:64px">#</th>' +
|
|
992
|
-
'<th style="width:60px">Method</th>' +
|
|
993
|
-
'<th>URL</th>' +
|
|
994
|
-
'<th style="width:55px">Status</th>' +
|
|
995
|
-
'<th style="width:70px">Duration</th>' +
|
|
996
|
-
'<th style="width:50px">Spans</th>' +
|
|
997
|
-
'<th style="width:30px" title="Warnings">⚠</th>' +
|
|
998
|
-
'<th style="width:70px">Time</th>' +
|
|
999
|
-
'</tr></thead><tbody>'
|
|
1000
|
-
|
|
1001
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
1002
|
-
const t = filtered[i]
|
|
1003
|
-
html +=
|
|
1004
|
-
'<tr class="ss-dbg-email-row" data-trace-id="' +
|
|
1005
|
-
t.id +
|
|
1006
|
-
'">' +
|
|
1007
|
-
'<td class="ss-dbg-c-dim" style="white-space:nowrap">' +
|
|
1008
|
-
t.id +
|
|
1009
|
-
deepLink('traces', t.id) +
|
|
1010
|
-
'</td>' +
|
|
1011
|
-
'<td><span class="' +
|
|
1012
|
-
methodClass(t.method) +
|
|
1013
|
-
'">' +
|
|
1014
|
-
esc(t.method) +
|
|
1015
|
-
'</span></td>' +
|
|
1016
|
-
'<td style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:300px" title="' +
|
|
1017
|
-
esc(t.url) +
|
|
1018
|
-
'">' +
|
|
1019
|
-
esc(t.url) +
|
|
1020
|
-
'</td>' +
|
|
1021
|
-
'<td><span class="ss-dbg-status ' +
|
|
1022
|
-
statusClass(t.statusCode) +
|
|
1023
|
-
'">' +
|
|
1024
|
-
t.statusCode +
|
|
1025
|
-
'</span></td>' +
|
|
1026
|
-
'<td class="ss-dbg-duration ' +
|
|
1027
|
-
durationClass(t.totalDuration) +
|
|
1028
|
-
'">' +
|
|
1029
|
-
t.totalDuration.toFixed(1) +
|
|
1030
|
-
'ms</td>' +
|
|
1031
|
-
'<td class="ss-dbg-c-muted" style="text-align:center">' +
|
|
1032
|
-
t.spanCount +
|
|
1033
|
-
'</td>' +
|
|
1034
|
-
'<td style="text-align:center">' +
|
|
1035
|
-
(t.warningCount > 0
|
|
1036
|
-
? '<span class="ss-dbg-c-amber">' + t.warningCount + '</span>'
|
|
1037
|
-
: '<span class="ss-dbg-c-border">-</span>') +
|
|
1038
|
-
'</td>' +
|
|
1039
|
-
'<td class="ss-dbg-event-time">' +
|
|
1040
|
-
timeAgo(t.timestamp) +
|
|
1041
|
-
'</td>' +
|
|
1042
|
-
'</tr>'
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
html += '</tbody></table>'
|
|
1046
|
-
tlBodyEl.innerHTML = html
|
|
1047
|
-
|
|
1048
|
-
// Click row to open detail
|
|
1049
|
-
tlBodyEl.querySelectorAll('[data-trace-id]').forEach((row) => {
|
|
1050
|
-
row.addEventListener('click', () => {
|
|
1051
|
-
const id = row.getAttribute('data-trace-id')
|
|
1052
|
-
fetchTraceDetail(id)
|
|
1053
|
-
})
|
|
1054
|
-
})
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
const fetchTraceDetail = (id) => {
|
|
1058
|
-
fetchJSON(BASE + '/traces/' + id)
|
|
1059
|
-
.then((trace) => {
|
|
1060
|
-
showTimeline(trace)
|
|
1061
|
-
})
|
|
1062
|
-
.catch(() => {
|
|
1063
|
-
if (tlWaterfall)
|
|
1064
|
-
tlWaterfall.innerHTML = '<div class="ss-dbg-empty">Failed to load trace</div>'
|
|
1065
|
-
})
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const showTimeline = (trace) => {
|
|
1069
|
-
if (!tlListEl || !tlDetailEl || !tlDetailTitle || !tlWaterfall) return
|
|
1070
|
-
|
|
1071
|
-
tlListEl.style.display = 'none'
|
|
1072
|
-
tlDetailEl.style.display = ''
|
|
1073
|
-
|
|
1074
|
-
tlDetailTitle.innerHTML =
|
|
1075
|
-
'<span class="' +
|
|
1076
|
-
methodClass(trace.method) +
|
|
1077
|
-
'">' +
|
|
1078
|
-
esc(trace.method) +
|
|
1079
|
-
'</span> ' +
|
|
1080
|
-
esc(trace.url) +
|
|
1081
|
-
' ' +
|
|
1082
|
-
'<span class="ss-dbg-status ' +
|
|
1083
|
-
statusClass(trace.statusCode) +
|
|
1084
|
-
'">' +
|
|
1085
|
-
trace.statusCode +
|
|
1086
|
-
'</span>' +
|
|
1087
|
-
'<span class="ss-dbg-tl-meta">' +
|
|
1088
|
-
trace.totalDuration.toFixed(1) +
|
|
1089
|
-
'ms · ' +
|
|
1090
|
-
trace.spanCount +
|
|
1091
|
-
' spans · ' +
|
|
1092
|
-
formatTime(trace.timestamp) +
|
|
1093
|
-
'</span>'
|
|
1094
|
-
|
|
1095
|
-
const spans = trace.spans || []
|
|
1096
|
-
const total = trace.totalDuration || 1
|
|
1097
|
-
|
|
1098
|
-
// Legend
|
|
1099
|
-
let html =
|
|
1100
|
-
'<div class="ss-dbg-tl-legend">' +
|
|
1101
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#6d28d9"></span>DB</div>' +
|
|
1102
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#1e3a5f"></span>Request</div>' +
|
|
1103
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#059669"></span>Mail</div>' +
|
|
1104
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#b45309"></span>Event</div>' +
|
|
1105
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#0e7490"></span>View</div>' +
|
|
1106
|
-
'<div class="ss-dbg-tl-legend-item"><span class="ss-dbg-tl-legend-dot" style="background:#525252"></span>Custom</div>' +
|
|
1107
|
-
'</div>'
|
|
1108
|
-
|
|
1109
|
-
if (spans.length === 0) {
|
|
1110
|
-
html += '<div class="ss-dbg-empty">No spans captured for this request</div>'
|
|
1111
|
-
} else {
|
|
1112
|
-
// Build nesting depth from parentId
|
|
1113
|
-
const depthMap = {}
|
|
1114
|
-
for (let i = 0; i < spans.length; i++) {
|
|
1115
|
-
const s = spans[i]
|
|
1116
|
-
if (!s.parentId) {
|
|
1117
|
-
depthMap[s.id] = 0
|
|
1118
|
-
} else {
|
|
1119
|
-
depthMap[s.id] = (depthMap[s.parentId] || 0) + 1
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
// Sort by startOffset
|
|
1124
|
-
const sorted = spans.slice().sort((a, b) => a.startOffset - b.startOffset)
|
|
1125
|
-
|
|
1126
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
1127
|
-
const s = sorted[i]
|
|
1128
|
-
const depth = depthMap[s.id] || 0
|
|
1129
|
-
const leftPct = ((s.startOffset / total) * 100).toFixed(2)
|
|
1130
|
-
const widthPct = Math.max((s.duration / total) * 100, 0.5).toFixed(2)
|
|
1131
|
-
const indent = depth * 16
|
|
1132
|
-
const catLabel = s.category === 'db' ? 'DB' : s.category
|
|
1133
|
-
const metaStr = s.metadata
|
|
1134
|
-
? Object.entries(s.metadata)
|
|
1135
|
-
.filter(([, v]) => v != null)
|
|
1136
|
-
.map(([k, v]) => k + '=' + v)
|
|
1137
|
-
.join(', ')
|
|
1138
|
-
: ''
|
|
1139
|
-
const tooltip =
|
|
1140
|
-
s.label + ' (' + s.duration.toFixed(2) + 'ms)' + (metaStr ? '\n' + metaStr : '')
|
|
1141
|
-
|
|
1142
|
-
html +=
|
|
1143
|
-
'<div class="ss-dbg-tl-row">' +
|
|
1144
|
-
'<div class="ss-dbg-tl-label" style="padding-left:' +
|
|
1145
|
-
(8 + indent) +
|
|
1146
|
-
'px" title="' +
|
|
1147
|
-
esc(tooltip) +
|
|
1148
|
-
'">' +
|
|
1149
|
-
'<span class="ss-dbg-badge ss-dbg-badge-' +
|
|
1150
|
-
(s.category === 'db'
|
|
1151
|
-
? 'purple'
|
|
1152
|
-
: s.category === 'mail'
|
|
1153
|
-
? 'green'
|
|
1154
|
-
: s.category === 'event'
|
|
1155
|
-
? 'amber'
|
|
1156
|
-
: s.category === 'view'
|
|
1157
|
-
? 'blue'
|
|
1158
|
-
: 'muted') +
|
|
1159
|
-
'" style="font-size:9px;margin-right:4px">' +
|
|
1160
|
-
esc(catLabel) +
|
|
1161
|
-
'</span>' +
|
|
1162
|
-
esc(s.label.length > 40 ? s.label.slice(0, 40) + '...' : s.label) +
|
|
1163
|
-
'</div>' +
|
|
1164
|
-
'<div class="ss-dbg-tl-track">' +
|
|
1165
|
-
'<div class="ss-dbg-tl-bar ss-dbg-tl-bar-' +
|
|
1166
|
-
esc(s.category) +
|
|
1167
|
-
'" style="left:' +
|
|
1168
|
-
leftPct +
|
|
1169
|
-
'%;width:' +
|
|
1170
|
-
widthPct +
|
|
1171
|
-
'%" title="' +
|
|
1172
|
-
esc(tooltip) +
|
|
1173
|
-
'"></div>' +
|
|
1174
|
-
'</div>' +
|
|
1175
|
-
'<span class="ss-dbg-tl-dur">' +
|
|
1176
|
-
s.duration.toFixed(2) +
|
|
1177
|
-
'ms</span>' +
|
|
1178
|
-
'</div>'
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
// Warnings
|
|
1183
|
-
if (trace.warnings && trace.warnings.length > 0) {
|
|
1184
|
-
html +=
|
|
1185
|
-
'<div class="ss-dbg-tl-warnings">' +
|
|
1186
|
-
'<div class="ss-dbg-tl-warnings-title">Warnings (' +
|
|
1187
|
-
trace.warnings.length +
|
|
1188
|
-
')</div>'
|
|
1189
|
-
for (let w = 0; w < trace.warnings.length; w++) {
|
|
1190
|
-
html += '<div class="ss-dbg-tl-warning">' + esc(trace.warnings[w]) + '</div>'
|
|
1191
|
-
}
|
|
1192
|
-
html += '</div>'
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
tlWaterfall.innerHTML = html
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
if (tlBackBtn) {
|
|
1199
|
-
tlBackBtn.addEventListener('click', () => {
|
|
1200
|
-
if (tlListEl) tlListEl.style.display = ''
|
|
1201
|
-
if (tlDetailEl) tlDetailEl.style.display = 'none'
|
|
1202
|
-
})
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
if (tlSearchInput) tlSearchInput.addEventListener('input', renderTraces)
|
|
1206
|
-
|
|
1207
|
-
// ── Mini Stats Bar ─────────────────────────────────────────────
|
|
1208
|
-
const miniStatsEl = document.getElementById('ss-dbg-mini-stats')
|
|
1209
|
-
let miniStatsTimer = null
|
|
1210
|
-
|
|
1211
|
-
const fetchMiniStats = () => {
|
|
1212
|
-
if (!DASH_API || !miniStatsEl) return
|
|
1213
|
-
fetchJSON(DASH_API + '/overview?range=1h')
|
|
1214
|
-
.then((data) => {
|
|
1215
|
-
const avg = data.avgResponseTime || 0
|
|
1216
|
-
const err = data.errorRate || 0
|
|
1217
|
-
const rpm = data.requestsPerMinute || 0
|
|
1218
|
-
const hasData = (data.totalRequests || 0) > 0
|
|
1219
|
-
|
|
1220
|
-
if (!hasData) {
|
|
1221
|
-
miniStatsEl.innerHTML = ''
|
|
1222
|
-
return
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
const avgClass =
|
|
1226
|
-
avg > 500 ? 'ss-dbg-stat-red' : avg > 200 ? 'ss-dbg-stat-amber' : 'ss-dbg-stat-green'
|
|
1227
|
-
const errClass =
|
|
1228
|
-
err > 5 ? 'ss-dbg-stat-red' : err > 1 ? 'ss-dbg-stat-amber' : 'ss-dbg-stat-green'
|
|
1229
|
-
|
|
1230
|
-
miniStatsEl.innerHTML =
|
|
1231
|
-
'<span class="ss-dbg-mini-stat"><span class="ss-dbg-mini-stat-value ' +
|
|
1232
|
-
avgClass +
|
|
1233
|
-
'">' +
|
|
1234
|
-
avg.toFixed(1) +
|
|
1235
|
-
'ms</span> avg</span>' +
|
|
1236
|
-
'<span class="ss-dbg-mini-stat"><span class="ss-dbg-mini-stat-value ' +
|
|
1237
|
-
errClass +
|
|
1238
|
-
'">' +
|
|
1239
|
-
err.toFixed(1) +
|
|
1240
|
-
'%</span> err</span>' +
|
|
1241
|
-
'<span class="ss-dbg-mini-stat"><span class="ss-dbg-mini-stat-value">' +
|
|
1242
|
-
Math.round(rpm) +
|
|
1243
|
-
'</span> req/m</span>'
|
|
1244
|
-
})
|
|
1245
|
-
.catch(() => {
|
|
1246
|
-
miniStatsEl.innerHTML = ''
|
|
1247
|
-
})
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
// ── Cache Tab ─────────────────────────────────────────────────
|
|
1251
|
-
const cacheSearchInput = document.getElementById('ss-dbg-search-cache')
|
|
1252
|
-
const cacheSummaryEl = document.getElementById('ss-dbg-cache-summary')
|
|
1253
|
-
const cacheBodyEl = document.getElementById('ss-dbg-cache-body')
|
|
1254
|
-
const cacheStatsArea = document.getElementById('ss-dbg-cache-stats-area')
|
|
1255
|
-
let cachedCacheData = { stats: {}, keys: [] }
|
|
1256
|
-
|
|
1257
|
-
const fetchCache = () => {
|
|
1258
|
-
if (!DASH_API) return
|
|
1259
|
-
fetchJSON(DASH_API + '/cache')
|
|
1260
|
-
.then((data) => {
|
|
1261
|
-
cachedCacheData = data
|
|
1262
|
-
renderCache()
|
|
1263
|
-
})
|
|
1264
|
-
.catch(() => {
|
|
1265
|
-
if (cacheBodyEl)
|
|
1266
|
-
cacheBodyEl.innerHTML = '<div class="ss-dbg-empty">Cache not available</div>'
|
|
1267
|
-
if (cacheStatsArea) cacheStatsArea.innerHTML = ''
|
|
1268
|
-
})
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
const renderCache = () => {
|
|
1272
|
-
if (!cacheBodyEl) return
|
|
1273
|
-
const stats = cachedCacheData.stats || {}
|
|
1274
|
-
const keys = cachedCacheData.keys || cachedCacheData.data || []
|
|
1275
|
-
const filter = (cacheSearchInput ? cacheSearchInput.value : '').toLowerCase()
|
|
1276
|
-
|
|
1277
|
-
// Stats area
|
|
1278
|
-
if (cacheStatsArea) {
|
|
1279
|
-
cacheStatsArea.innerHTML =
|
|
1280
|
-
'<div class="ss-dbg-cache-stat"><span class="ss-dbg-cache-stat-label">Hit Rate:</span><span class="ss-dbg-cache-stat-value">' +
|
|
1281
|
-
(stats.hitRate || 0).toFixed(1) +
|
|
1282
|
-
'%</span></div>' +
|
|
1283
|
-
'<div class="ss-dbg-cache-stat"><span class="ss-dbg-cache-stat-label">Hits:</span><span class="ss-dbg-cache-stat-value">' +
|
|
1284
|
-
(stats.hits || 0) +
|
|
1285
|
-
'</span></div>' +
|
|
1286
|
-
'<div class="ss-dbg-cache-stat"><span class="ss-dbg-cache-stat-label">Misses:</span><span class="ss-dbg-cache-stat-value">' +
|
|
1287
|
-
(stats.misses || 0) +
|
|
1288
|
-
'</span></div>' +
|
|
1289
|
-
'<div class="ss-dbg-cache-stat"><span class="ss-dbg-cache-stat-label">Keys:</span><span class="ss-dbg-cache-stat-value">' +
|
|
1290
|
-
(stats.keyCount || keys.length || 0) +
|
|
1291
|
-
'</span></div>'
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
if (cacheSummaryEl) {
|
|
1295
|
-
cacheSummaryEl.textContent = (stats.keyCount || keys.length || 0) + ' keys'
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
let filtered = keys
|
|
1299
|
-
if (filter) {
|
|
1300
|
-
filtered = keys.filter((k) => (k.key || '').toLowerCase().indexOf(filter) !== -1)
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
if (filtered.length === 0) {
|
|
1304
|
-
cacheBodyEl.innerHTML =
|
|
1305
|
-
'<div class="ss-dbg-empty">' +
|
|
1306
|
-
(filter ? 'No matching cache keys' : 'No cache keys found') +
|
|
1307
|
-
'</div>'
|
|
1308
|
-
return
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
let html =
|
|
1312
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
1313
|
-
'<th>Key</th>' +
|
|
1314
|
-
'<th style="width:80px">Type</th>' +
|
|
1315
|
-
'<th style="width:80px">TTL</th>' +
|
|
1316
|
-
'<th style="width:80px">Size</th>' +
|
|
1317
|
-
'</tr></thead><tbody>'
|
|
1318
|
-
|
|
1319
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
1320
|
-
const k = filtered[i]
|
|
1321
|
-
html +=
|
|
1322
|
-
'<tr class="ss-dbg-email-row" data-cache-key="' +
|
|
1323
|
-
esc(k.key || '') +
|
|
1324
|
-
'">' +
|
|
1325
|
-
'<td class="ss-dbg-c-sql">' +
|
|
1326
|
-
esc(k.key || '') +
|
|
1327
|
-
'</td>' +
|
|
1328
|
-
'<td class="ss-dbg-c-muted">' +
|
|
1329
|
-
esc(k.type || '-') +
|
|
1330
|
-
'</td>' +
|
|
1331
|
-
'<td class="ss-dbg-c-muted">' +
|
|
1332
|
-
(k.ttl != null ? k.ttl + 's' : '-') +
|
|
1333
|
-
'</td>' +
|
|
1334
|
-
'<td class="ss-dbg-c-dim">' +
|
|
1335
|
-
(k.size != null ? k.size + 'B' : '-') +
|
|
1336
|
-
'</td>' +
|
|
1337
|
-
'</tr>'
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
html += '</tbody></table>'
|
|
1341
|
-
cacheBodyEl.innerHTML = html
|
|
1342
|
-
|
|
1343
|
-
// Click row to show cache detail
|
|
1344
|
-
cacheBodyEl.querySelectorAll('[data-cache-key]').forEach((row) => {
|
|
1345
|
-
row.addEventListener('click', () => {
|
|
1346
|
-
const key = row.getAttribute('data-cache-key')
|
|
1347
|
-
fetchJSON(DASH_API + '/cache/' + encodeURIComponent(key))
|
|
1348
|
-
.then((data) => {
|
|
1349
|
-
cacheBodyEl.innerHTML =
|
|
1350
|
-
'<div class="ss-dbg-cache-detail">' +
|
|
1351
|
-
'<button type="button" class="ss-dbg-btn-clear" id="ss-dbg-cache-back">← Back</button>' +
|
|
1352
|
-
' <strong>' +
|
|
1353
|
-
esc(key) +
|
|
1354
|
-
'</strong>' +
|
|
1355
|
-
'<pre>' +
|
|
1356
|
-
esc(JSON.stringify(data.value || data, null, 2)) +
|
|
1357
|
-
'</pre>' +
|
|
1358
|
-
'</div>'
|
|
1359
|
-
const backBtn = document.getElementById('ss-dbg-cache-back')
|
|
1360
|
-
if (backBtn) backBtn.addEventListener('click', () => renderCache())
|
|
1361
|
-
})
|
|
1362
|
-
.catch(() => {
|
|
1363
|
-
/* ignore */
|
|
1364
|
-
})
|
|
1365
|
-
})
|
|
1366
|
-
})
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
if (cacheSearchInput) cacheSearchInput.addEventListener('input', renderCache)
|
|
1370
|
-
|
|
1371
|
-
// ── Jobs Tab ──────────────────────────────────────────────────
|
|
1372
|
-
const jobsBodyEl = document.getElementById('ss-dbg-jobs-body')
|
|
1373
|
-
const jobsSummaryEl = document.getElementById('ss-dbg-jobs-summary')
|
|
1374
|
-
const jobsStatsArea = document.getElementById('ss-dbg-jobs-stats-area')
|
|
1375
|
-
const jobFilters = panel.querySelectorAll('[data-ss-dbg-job-status]')
|
|
1376
|
-
let jobStatusFilter = 'all'
|
|
1377
|
-
let cachedJobsData = { data: [], stats: {} }
|
|
1378
|
-
|
|
1379
|
-
const fetchJobs = () => {
|
|
1380
|
-
if (!DASH_API) return
|
|
1381
|
-
let url = DASH_API + '/jobs?limit=100'
|
|
1382
|
-
if (jobStatusFilter && jobStatusFilter !== 'all') url += '&status=' + jobStatusFilter
|
|
1383
|
-
|
|
1384
|
-
fetchJSON(url)
|
|
1385
|
-
.then((data) => {
|
|
1386
|
-
cachedJobsData = data
|
|
1387
|
-
renderJobs()
|
|
1388
|
-
})
|
|
1389
|
-
.catch(() => {
|
|
1390
|
-
if (jobsBodyEl)
|
|
1391
|
-
jobsBodyEl.innerHTML = '<div class="ss-dbg-empty">Jobs/Queue not available</div>'
|
|
1392
|
-
if (jobsStatsArea) jobsStatsArea.innerHTML = ''
|
|
1393
|
-
})
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
const renderJobs = () => {
|
|
1397
|
-
if (!jobsBodyEl) return
|
|
1398
|
-
const items = cachedJobsData.data || cachedJobsData.jobs || []
|
|
1399
|
-
const stats = cachedJobsData.stats || {}
|
|
1400
|
-
|
|
1401
|
-
// Stats area
|
|
1402
|
-
if (jobsStatsArea) {
|
|
1403
|
-
jobsStatsArea.innerHTML =
|
|
1404
|
-
'<div class="ss-dbg-job-stats">' +
|
|
1405
|
-
'<div class="ss-dbg-job-stat"><span class="ss-dbg-job-stat-label">Active:</span><span class="ss-dbg-job-stat-value">' +
|
|
1406
|
-
(stats.active || 0) +
|
|
1407
|
-
'</span></div>' +
|
|
1408
|
-
'<div class="ss-dbg-job-stat"><span class="ss-dbg-job-stat-label">Waiting:</span><span class="ss-dbg-job-stat-value">' +
|
|
1409
|
-
(stats.waiting || 0) +
|
|
1410
|
-
'</span></div>' +
|
|
1411
|
-
'<div class="ss-dbg-job-stat"><span class="ss-dbg-job-stat-label">Delayed:</span><span class="ss-dbg-job-stat-value">' +
|
|
1412
|
-
(stats.delayed || 0) +
|
|
1413
|
-
'</span></div>' +
|
|
1414
|
-
'<div class="ss-dbg-job-stat"><span class="ss-dbg-job-stat-label">Completed:</span><span class="ss-dbg-job-stat-value">' +
|
|
1415
|
-
(stats.completed || 0) +
|
|
1416
|
-
'</span></div>' +
|
|
1417
|
-
'<div class="ss-dbg-job-stat"><span class="ss-dbg-job-stat-label">Failed:</span><span class="ss-dbg-job-stat-value ss-dbg-c-red">' +
|
|
1418
|
-
(stats.failed || 0) +
|
|
1419
|
-
'</span></div>' +
|
|
1420
|
-
'</div>'
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
if (jobsSummaryEl) {
|
|
1424
|
-
const total = (cachedJobsData.meta ? cachedJobsData.meta.total : null) || items.length
|
|
1425
|
-
jobsSummaryEl.textContent = total + ' jobs'
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
if (items.length === 0) {
|
|
1429
|
-
jobsBodyEl.innerHTML = '<div class="ss-dbg-empty">No jobs found</div>'
|
|
1430
|
-
return
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
let html =
|
|
1434
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
1435
|
-
'<th style="width:50px">ID</th>' +
|
|
1436
|
-
'<th>Name</th>' +
|
|
1437
|
-
'<th style="width:80px">Status</th>' +
|
|
1438
|
-
'<th style="width:60px">Attempts</th>' +
|
|
1439
|
-
'<th style="width:80px">Duration</th>' +
|
|
1440
|
-
'<th style="width:70px">Time</th>' +
|
|
1441
|
-
'<th style="width:50px"></th>' +
|
|
1442
|
-
'</tr></thead><tbody>'
|
|
1443
|
-
|
|
1444
|
-
for (let i = 0; i < items.length; i++) {
|
|
1445
|
-
const j = items[i]
|
|
1446
|
-
const statusBadge =
|
|
1447
|
-
j.status === 'failed'
|
|
1448
|
-
? 'red'
|
|
1449
|
-
: j.status === 'completed'
|
|
1450
|
-
? 'green'
|
|
1451
|
-
: j.status === 'active'
|
|
1452
|
-
? 'blue'
|
|
1453
|
-
: 'amber'
|
|
1454
|
-
html +=
|
|
1455
|
-
'<tr>' +
|
|
1456
|
-
'<td class="ss-dbg-c-dim">' +
|
|
1457
|
-
j.id +
|
|
1458
|
-
'</td>' +
|
|
1459
|
-
'<td class="ss-dbg-c-sql">' +
|
|
1460
|
-
esc(j.name || '') +
|
|
1461
|
-
'</td>' +
|
|
1462
|
-
'<td><span class="ss-dbg-badge ss-dbg-badge-' +
|
|
1463
|
-
statusBadge +
|
|
1464
|
-
'">' +
|
|
1465
|
-
esc(j.status || '') +
|
|
1466
|
-
'</span></td>' +
|
|
1467
|
-
'<td class="ss-dbg-c-muted" style="text-align:center">' +
|
|
1468
|
-
(j.attempts || j.attemptsMade || 0) +
|
|
1469
|
-
'</td>' +
|
|
1470
|
-
'<td class="ss-dbg-duration">' +
|
|
1471
|
-
(j.duration != null ? j.duration.toFixed(0) + 'ms' : '-') +
|
|
1472
|
-
'</td>' +
|
|
1473
|
-
'<td class="ss-dbg-event-time">' +
|
|
1474
|
-
timeAgo(j.timestamp || j.processedOn || j.created_at) +
|
|
1475
|
-
'</td>' +
|
|
1476
|
-
'<td>' +
|
|
1477
|
-
(j.status === 'failed'
|
|
1478
|
-
? '<button class="ss-dbg-retry-btn" data-retry-id="' + j.id + '">Retry</button>'
|
|
1479
|
-
: '') +
|
|
1480
|
-
'</td>' +
|
|
1481
|
-
'</tr>'
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
html += '</tbody></table>'
|
|
1485
|
-
jobsBodyEl.innerHTML = html
|
|
1486
|
-
|
|
1487
|
-
// Retry buttons
|
|
1488
|
-
jobsBodyEl.querySelectorAll('.ss-dbg-retry-btn').forEach((btn) => {
|
|
1489
|
-
btn.addEventListener('click', (e) => {
|
|
1490
|
-
e.stopPropagation()
|
|
1491
|
-
const id = btn.getAttribute('data-retry-id')
|
|
1492
|
-
btn.textContent = '...'
|
|
1493
|
-
btn.disabled = true
|
|
1494
|
-
fetch(DASH_API + '/jobs/' + id + '/retry', { method: 'POST', credentials: 'same-origin' })
|
|
1495
|
-
.then(() => {
|
|
1496
|
-
btn.textContent = 'OK'
|
|
1497
|
-
setTimeout(fetchJobs, 1000)
|
|
1498
|
-
})
|
|
1499
|
-
.catch(() => {
|
|
1500
|
-
btn.textContent = 'Retry'
|
|
1501
|
-
btn.disabled = false
|
|
1502
|
-
})
|
|
1503
|
-
})
|
|
1504
|
-
})
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
jobFilters.forEach((btn) => {
|
|
1508
|
-
btn.addEventListener('click', () => {
|
|
1509
|
-
jobFilters.forEach((b) => b.classList.remove('ss-dbg-active'))
|
|
1510
|
-
btn.classList.add('ss-dbg-active')
|
|
1511
|
-
jobStatusFilter = btn.getAttribute('data-ss-dbg-job-status')
|
|
1512
|
-
fetchJobs()
|
|
1513
|
-
})
|
|
1514
|
-
})
|
|
1515
|
-
|
|
1516
|
-
// ── Config Tab ────────────────────────────────────────────────
|
|
1517
|
-
const configBodyEl = document.getElementById('ss-dbg-config-body')
|
|
1518
|
-
const configSummaryEl = document.getElementById('ss-dbg-config-summary')
|
|
1519
|
-
const configSearchInput = document.getElementById('ss-dbg-search-config')
|
|
1520
|
-
const configTabs = panel.querySelectorAll('[data-ss-dbg-config-tab]')
|
|
1521
|
-
let configRawData = null
|
|
1522
|
-
let configActiveTab = 'config'
|
|
1523
|
-
let configSearchTerm = ''
|
|
1524
|
-
|
|
1525
|
-
const flattenConfig = (obj, prefix) => {
|
|
1526
|
-
const results = []
|
|
1527
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
1528
|
-
results.push({ path: prefix, value: obj })
|
|
1529
|
-
return results
|
|
1530
|
-
}
|
|
1531
|
-
const keys = Object.keys(obj)
|
|
1532
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1533
|
-
const fullPath = prefix ? prefix + '.' + keys[i] : keys[i]
|
|
1534
|
-
const val = obj[keys[i]]
|
|
1535
|
-
if (typeof val === 'object' && val !== null && !Array.isArray(val) && !val.__redacted) {
|
|
1536
|
-
const nested = flattenConfig(val, fullPath)
|
|
1537
|
-
for (let n = 0; n < nested.length; n++) results.push(nested[n])
|
|
1538
|
-
} else {
|
|
1539
|
-
results.push({ path: fullPath, value: val })
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
return results
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
const countLeaves = (obj) => {
|
|
1546
|
-
if (typeof obj !== 'object' || obj === null || obj.__redacted) return 1
|
|
1547
|
-
let count = 0
|
|
1548
|
-
const keys = Object.keys(obj)
|
|
1549
|
-
for (let i = 0; i < keys.length; i++) count += countLeaves(obj[keys[i]])
|
|
1550
|
-
return count
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
const formatConfigValue = (val) => {
|
|
1554
|
-
if (val === null || val === undefined) return '<span class="ss-dbg-config-val-null">null</span>'
|
|
1555
|
-
if (val === true) return '<span class="ss-dbg-config-val-true">true</span>'
|
|
1556
|
-
if (val === false) return '<span class="ss-dbg-config-val-false">false</span>'
|
|
1557
|
-
if (typeof val === 'number') return '<span class="ss-dbg-config-val-number">' + val + '</span>'
|
|
1558
|
-
if (Array.isArray(val)) {
|
|
1559
|
-
const items = val.map((item) => {
|
|
1560
|
-
if (item === null || item === undefined) return 'null'
|
|
1561
|
-
if (typeof item === 'object') {
|
|
1562
|
-
try {
|
|
1563
|
-
return JSON.stringify(item)
|
|
1564
|
-
} catch {
|
|
1565
|
-
return String(item)
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
return String(item)
|
|
1569
|
-
})
|
|
1570
|
-
return '<span class="ss-dbg-config-val-array">[' + esc(items.join(', ')) + ']</span>'
|
|
1571
|
-
}
|
|
1572
|
-
if (typeof val === 'object') {
|
|
1573
|
-
try {
|
|
1574
|
-
return (
|
|
1575
|
-
'<span class="ss-dbg-config-val-null">' + esc(JSON.stringify(val, null, 2)) + '</span>'
|
|
1576
|
-
)
|
|
1577
|
-
} catch {
|
|
1578
|
-
/* fall through */
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
return esc(String(val))
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
const highlightMatch = (text, term) => {
|
|
1585
|
-
if (!term) return text
|
|
1586
|
-
const idx = text.toLowerCase().indexOf(term.toLowerCase())
|
|
1587
|
-
if (idx === -1) return text
|
|
1588
|
-
return (
|
|
1589
|
-
text.slice(0, idx) +
|
|
1590
|
-
'<mark class="ss-dbg-config-match">' +
|
|
1591
|
-
text.slice(idx, idx + term.length) +
|
|
1592
|
-
'</mark>' +
|
|
1593
|
-
text.slice(idx + term.length)
|
|
1594
|
-
)
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
const isRedactedObj = (val) => val && typeof val === 'object' && val.__redacted === true
|
|
1598
|
-
|
|
1599
|
-
const renderRedacted = (val, prefix) => {
|
|
1600
|
-
const cls = prefix + '-config-redacted'
|
|
1601
|
-
const realVal = esc(val.value || '')
|
|
1602
|
-
return (
|
|
1603
|
-
'<span class="' +
|
|
1604
|
-
cls +
|
|
1605
|
-
' ' +
|
|
1606
|
-
prefix +
|
|
1607
|
-
'-redacted-wrap" data-redacted-value="' +
|
|
1608
|
-
realVal +
|
|
1609
|
-
'">' +
|
|
1610
|
-
'<span class="' +
|
|
1611
|
-
prefix +
|
|
1612
|
-
'-redacted-display">' +
|
|
1613
|
-
esc(val.display) +
|
|
1614
|
-
'</span>' +
|
|
1615
|
-
'<span class="' +
|
|
1616
|
-
prefix +
|
|
1617
|
-
'-redacted-real" style="display:none">' +
|
|
1618
|
-
realVal +
|
|
1619
|
-
'</span>' +
|
|
1620
|
-
'<button type="button" class="' +
|
|
1621
|
-
prefix +
|
|
1622
|
-
'-redacted-reveal" title="Reveal value">' +
|
|
1623
|
-
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>' +
|
|
1624
|
-
'</button>' +
|
|
1625
|
-
'<button type="button" class="' +
|
|
1626
|
-
prefix +
|
|
1627
|
-
'-redacted-copy" title="Copy value">' +
|
|
1628
|
-
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>' +
|
|
1629
|
-
'</button>' +
|
|
1630
|
-
'</span>'
|
|
1631
|
-
)
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
const bindRedactedButtons = (container, prefix) => {
|
|
1635
|
-
container.querySelectorAll('.' + prefix + '-redacted-reveal').forEach((btn) => {
|
|
1636
|
-
btn.addEventListener('click', (e) => {
|
|
1637
|
-
e.stopPropagation()
|
|
1638
|
-
const wrap = btn.closest('.' + prefix + '-redacted-wrap')
|
|
1639
|
-
if (!wrap) return
|
|
1640
|
-
const display = wrap.querySelector('.' + prefix + '-redacted-display')
|
|
1641
|
-
const real = wrap.querySelector('.' + prefix + '-redacted-real')
|
|
1642
|
-
if (!display || !real) return
|
|
1643
|
-
const isHidden = real.style.display === 'none'
|
|
1644
|
-
display.style.display = isHidden ? 'none' : ''
|
|
1645
|
-
real.style.display = isHidden ? '' : 'none'
|
|
1646
|
-
btn.innerHTML = isHidden
|
|
1647
|
-
? '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>'
|
|
1648
|
-
: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>'
|
|
1649
|
-
btn.title = isHidden ? 'Hide value' : 'Reveal value'
|
|
1650
|
-
})
|
|
1651
|
-
})
|
|
1652
|
-
|
|
1653
|
-
container.querySelectorAll('.' + prefix + '-redacted-copy').forEach((btn) => {
|
|
1654
|
-
btn.addEventListener('click', (e) => {
|
|
1655
|
-
e.stopPropagation()
|
|
1656
|
-
const wrap = btn.closest('.' + prefix + '-redacted-wrap')
|
|
1657
|
-
if (!wrap) return
|
|
1658
|
-
const val = wrap.getAttribute('data-redacted-value')
|
|
1659
|
-
if (!val) return
|
|
1660
|
-
navigator.clipboard.writeText(val).then(() => {
|
|
1661
|
-
btn.innerHTML = '\u2713'
|
|
1662
|
-
setTimeout(() => {
|
|
1663
|
-
btn.innerHTML =
|
|
1664
|
-
'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'
|
|
1665
|
-
}, 1200)
|
|
1666
|
-
})
|
|
1667
|
-
})
|
|
1668
|
-
})
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
const fetchConfig = () => {
|
|
1672
|
-
if (!DASH_API) return
|
|
1673
|
-
fetchJSON(DASH_API + '/config')
|
|
1674
|
-
.then((data) => {
|
|
1675
|
-
configRawData = data
|
|
1676
|
-
fetched.config = true
|
|
1677
|
-
renderConfig()
|
|
1678
|
-
})
|
|
1679
|
-
.catch(() => {
|
|
1680
|
-
if (configBodyEl)
|
|
1681
|
-
configBodyEl.innerHTML = '<div class="ss-dbg-empty">Config not available</div>'
|
|
1682
|
-
})
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
const renderConfigTable = (obj, prefix) => {
|
|
1686
|
-
const flat = flattenConfig(obj, prefix)
|
|
1687
|
-
let html =
|
|
1688
|
-
'<table class="ss-dbg-table"><thead><tr>' +
|
|
1689
|
-
'<th style="width:320px">Key</th><th>Value</th>' +
|
|
1690
|
-
'</tr></thead><tbody>'
|
|
1691
|
-
for (let i = 0; i < flat.length; i++) {
|
|
1692
|
-
const item = flat[i]
|
|
1693
|
-
const relPath =
|
|
1694
|
-
item.path.indexOf(prefix + '.') === 0 ? item.path.slice(prefix.length + 1) : item.path
|
|
1695
|
-
const redacted = isRedactedObj(item.value)
|
|
1696
|
-
html +=
|
|
1697
|
-
'<tr>' +
|
|
1698
|
-
'<td><span class="ss-dbg-config-key">' +
|
|
1699
|
-
esc(relPath) +
|
|
1700
|
-
'</span></td>' +
|
|
1701
|
-
'<td>' +
|
|
1702
|
-
(redacted
|
|
1703
|
-
? renderRedacted(item.value, 'ss-dbg')
|
|
1704
|
-
: '<span class="ss-dbg-config-val">' + formatConfigValue(item.value) + '</span>') +
|
|
1705
|
-
'</td>' +
|
|
1706
|
-
'</tr>'
|
|
1707
|
-
}
|
|
1708
|
-
html += '</tbody></table>'
|
|
1709
|
-
return html
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
const renderConfig = () => {
|
|
1713
|
-
if (!configBodyEl || !configRawData) return
|
|
1714
|
-
|
|
1715
|
-
const source =
|
|
1716
|
-
configActiveTab === 'env' ? configRawData.env || {} : configRawData.config || configRawData
|
|
1717
|
-
const flat = flattenConfig(source, '')
|
|
1718
|
-
const term = configSearchTerm.toLowerCase()
|
|
1719
|
-
let filtered = flat
|
|
1720
|
-
if (term) {
|
|
1721
|
-
filtered = flat.filter((item) => {
|
|
1722
|
-
var valStr = isRedactedObj(item.value) ? item.value.display : String(item.value)
|
|
1723
|
-
return (
|
|
1724
|
-
item.path.toLowerCase().indexOf(term) !== -1 || valStr.toLowerCase().indexOf(term) !== -1
|
|
1725
|
-
)
|
|
1726
|
-
})
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
if (configSummaryEl) {
|
|
1730
|
-
configSummaryEl.textContent =
|
|
1731
|
-
filtered.length + (term ? ' of ' + flat.length : '') + ' entries'
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
let html = ''
|
|
1735
|
-
|
|
1736
|
-
if (configActiveTab === 'env') {
|
|
1737
|
-
// Env vars: simple table
|
|
1738
|
-
html +=
|
|
1739
|
-
'<div class="ss-dbg-config-table-wrap"><table class="ss-dbg-table"><thead><tr>' +
|
|
1740
|
-
'<th>Variable</th><th>Value</th>' +
|
|
1741
|
-
'</tr></thead><tbody>'
|
|
1742
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
1743
|
-
const item = filtered[i]
|
|
1744
|
-
const redacted = isRedactedObj(item.value)
|
|
1745
|
-
const displayVal = redacted ? item.value.display : String(item.value)
|
|
1746
|
-
html +=
|
|
1747
|
-
'<tr>' +
|
|
1748
|
-
'<td><span class="ss-dbg-config-key">' +
|
|
1749
|
-
highlightMatch(esc(item.path), term) +
|
|
1750
|
-
'</span></td>' +
|
|
1751
|
-
'<td>' +
|
|
1752
|
-
(redacted
|
|
1753
|
-
? renderRedacted(item.value, 'ss-dbg')
|
|
1754
|
-
: '<span class="ss-dbg-config-val">' +
|
|
1755
|
-
highlightMatch(esc(displayVal), term) +
|
|
1756
|
-
'</span>') +
|
|
1757
|
-
'</td>' +
|
|
1758
|
-
'</tr>'
|
|
1759
|
-
}
|
|
1760
|
-
html += '</tbody></table></div>'
|
|
1761
|
-
} else {
|
|
1762
|
-
if (term) {
|
|
1763
|
-
// Search mode: flat list
|
|
1764
|
-
html +=
|
|
1765
|
-
'<div class="ss-dbg-config-table-wrap"><table class="ss-dbg-table"><thead><tr>' +
|
|
1766
|
-
'<th>Path</th><th>Value</th>' +
|
|
1767
|
-
'</tr></thead><tbody>'
|
|
1768
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
1769
|
-
const item = filtered[i]
|
|
1770
|
-
const redacted = isRedactedObj(item.value)
|
|
1771
|
-
const displayVal = redacted ? item.value.display : String(item.value)
|
|
1772
|
-
html +=
|
|
1773
|
-
'<tr>' +
|
|
1774
|
-
'<td><span class="ss-dbg-config-key" style="white-space:nowrap">' +
|
|
1775
|
-
highlightMatch(esc(item.path), term) +
|
|
1776
|
-
'</span></td>' +
|
|
1777
|
-
'<td>' +
|
|
1778
|
-
(redacted
|
|
1779
|
-
? renderRedacted(item.value, 'ss-dbg')
|
|
1780
|
-
: '<span class="ss-dbg-config-val" style="word-break:break-all">' +
|
|
1781
|
-
highlightMatch(esc(displayVal), term) +
|
|
1782
|
-
'</span>') +
|
|
1783
|
-
'</td>' +
|
|
1784
|
-
'</tr>'
|
|
1785
|
-
}
|
|
1786
|
-
html += '</tbody></table></div>'
|
|
1787
|
-
} else {
|
|
1788
|
-
// Browse mode: collapsible sections
|
|
1789
|
-
const topKeys = Object.keys(source)
|
|
1790
|
-
html += '<div class="ss-dbg-config-sections">'
|
|
1791
|
-
for (let t = 0; t < topKeys.length; t++) {
|
|
1792
|
-
const sectionKey = topKeys[t]
|
|
1793
|
-
const sectionVal = source[sectionKey]
|
|
1794
|
-
const childCount = countLeaves(sectionVal)
|
|
1795
|
-
const isObj =
|
|
1796
|
-
typeof sectionVal === 'object' && sectionVal !== null && !sectionVal.__redacted
|
|
1797
|
-
|
|
1798
|
-
html += '<div class="ss-dbg-config-section">'
|
|
1799
|
-
if (isObj) {
|
|
1800
|
-
html +=
|
|
1801
|
-
'<div class="ss-dbg-config-section-header" data-config-section="' +
|
|
1802
|
-
esc(sectionKey) +
|
|
1803
|
-
'">' +
|
|
1804
|
-
'<span class="ss-dbg-config-toggle">\u25B6</span>' +
|
|
1805
|
-
'<span class="ss-dbg-config-key">' +
|
|
1806
|
-
esc(sectionKey) +
|
|
1807
|
-
'</span>' +
|
|
1808
|
-
'<span class="ss-dbg-config-count">' +
|
|
1809
|
-
childCount +
|
|
1810
|
-
' entries</span>' +
|
|
1811
|
-
'</div>'
|
|
1812
|
-
html += '<div class="ss-dbg-config-section-body" style="display:none">'
|
|
1813
|
-
html += renderConfigTable(sectionVal, sectionKey)
|
|
1814
|
-
html += '</div>'
|
|
1815
|
-
} else {
|
|
1816
|
-
html +=
|
|
1817
|
-
'<div class="ss-dbg-config-section-header ss-dbg-config-leaf">' +
|
|
1818
|
-
'<span class="ss-dbg-config-key">' +
|
|
1819
|
-
esc(sectionKey) +
|
|
1820
|
-
'</span>' +
|
|
1821
|
-
'<span class="ss-dbg-config-val" style="margin-left:8px">' +
|
|
1822
|
-
formatConfigValue(sectionVal) +
|
|
1823
|
-
'</span>' +
|
|
1824
|
-
'</div>'
|
|
1825
|
-
}
|
|
1826
|
-
html += '</div>'
|
|
1827
|
-
}
|
|
1828
|
-
html += '</div>'
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
configBodyEl.innerHTML = html
|
|
1833
|
-
|
|
1834
|
-
// Bind section toggles
|
|
1835
|
-
configBodyEl.querySelectorAll('[data-config-section]').forEach((header) => {
|
|
1836
|
-
header.addEventListener('click', () => {
|
|
1837
|
-
const sectionBody = header.nextElementSibling
|
|
1838
|
-
if (!sectionBody) return
|
|
1839
|
-
const isHidden = sectionBody.style.display === 'none'
|
|
1840
|
-
sectionBody.style.display = isHidden ? '' : 'none'
|
|
1841
|
-
const toggle = header.querySelector('.ss-dbg-config-toggle')
|
|
1842
|
-
if (toggle) toggle.textContent = isHidden ? '\u25BC' : '\u25B6'
|
|
1843
|
-
})
|
|
1844
|
-
})
|
|
1845
|
-
|
|
1846
|
-
// Bind redacted reveal/copy buttons
|
|
1847
|
-
bindRedactedButtons(configBodyEl, 'ss-dbg')
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
configTabs.forEach((btn) => {
|
|
1851
|
-
btn.addEventListener('click', () => {
|
|
1852
|
-
configTabs.forEach((b) => b.classList.remove('ss-dbg-active'))
|
|
1853
|
-
btn.classList.add('ss-dbg-active')
|
|
1854
|
-
configActiveTab = btn.getAttribute('data-ss-dbg-config-tab')
|
|
1855
|
-
renderConfig()
|
|
1856
|
-
})
|
|
1857
|
-
})
|
|
1858
|
-
|
|
1859
|
-
if (configSearchInput) {
|
|
1860
|
-
configSearchInput.addEventListener('input', () => {
|
|
1861
|
-
configSearchTerm = configSearchInput.value.trim()
|
|
1862
|
-
renderConfig()
|
|
1863
|
-
})
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
// ── Custom panes: fetch, render, bind ───────────────────────────
|
|
1867
|
-
const getNestedValue = (obj, path) => {
|
|
1868
|
-
const parts = path.split('.')
|
|
1869
|
-
let cur = obj
|
|
1870
|
-
for (let i = 0; i < parts.length; i++) {
|
|
1871
|
-
if (cur == null) return undefined
|
|
1872
|
-
cur = cur[parts[i]]
|
|
1873
|
-
}
|
|
1874
|
-
return cur
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
const fetchCustomPane = (pane) => {
|
|
1878
|
-
const bodyEl = document.getElementById('ss-dbg-' + pane.id + '-body')
|
|
1879
|
-
fetchJSON(pane.endpoint)
|
|
1880
|
-
.then((data) => {
|
|
1881
|
-
const key = pane.dataKey || pane.id
|
|
1882
|
-
const rows = getNestedValue(data, key) || (Array.isArray(data) ? data : [])
|
|
1883
|
-
customPaneState[pane.id].data = rows
|
|
1884
|
-
customPaneState[pane.id].fetched = true
|
|
1885
|
-
renderCustomPane(pane)
|
|
1886
|
-
})
|
|
1887
|
-
.catch(() => {
|
|
1888
|
-
if (bodyEl)
|
|
1889
|
-
bodyEl.innerHTML =
|
|
1890
|
-
'<div class="ss-dbg-empty">Failed to load ' + esc(pane.label) + '</div>'
|
|
1891
|
-
})
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
const renderCustomPane = (pane) => {
|
|
1895
|
-
const state = customPaneState[pane.id]
|
|
1896
|
-
if (!state) return
|
|
1897
|
-
const bodyEl = document.getElementById('ss-dbg-' + pane.id + '-body')
|
|
1898
|
-
const summaryEl = document.getElementById('ss-dbg-' + pane.id + '-summary')
|
|
1899
|
-
if (!bodyEl) return
|
|
1900
|
-
|
|
1901
|
-
const filter = state.filter.toLowerCase()
|
|
1902
|
-
let rows = state.data
|
|
1903
|
-
|
|
1904
|
-
if (summaryEl) {
|
|
1905
|
-
summaryEl.textContent = rows.length + ' ' + pane.label.toLowerCase()
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
if (filter) {
|
|
1909
|
-
const searchCols = pane.columns.filter((c) => c.searchable)
|
|
1910
|
-
if (searchCols.length > 0) {
|
|
1911
|
-
rows = rows.filter((row) =>
|
|
1912
|
-
searchCols.some((c) => {
|
|
1913
|
-
const v = row[c.key]
|
|
1914
|
-
return v != null && String(v).toLowerCase().indexOf(filter) !== -1
|
|
1915
|
-
})
|
|
1916
|
-
)
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
if (rows.length === 0) {
|
|
1921
|
-
bodyEl.innerHTML =
|
|
1922
|
-
'<div class="ss-dbg-empty">' +
|
|
1923
|
-
(filter
|
|
1924
|
-
? 'No matching ' + esc(pane.label.toLowerCase())
|
|
1925
|
-
: 'No ' + esc(pane.label.toLowerCase()) + ' recorded yet') +
|
|
1926
|
-
'</div>'
|
|
1927
|
-
return
|
|
1928
|
-
}
|
|
1929
|
-
|
|
1930
|
-
let html = '<table class="ss-dbg-table"><thead><tr>'
|
|
1931
|
-
for (let c = 0; c < pane.columns.length; c++) {
|
|
1932
|
-
const col = pane.columns[c]
|
|
1933
|
-
html +=
|
|
1934
|
-
'<th' +
|
|
1935
|
-
(col.width ? ' style="width:' + col.width + '"' : '') +
|
|
1936
|
-
'>' +
|
|
1937
|
-
esc(col.label) +
|
|
1938
|
-
'</th>'
|
|
1939
|
-
}
|
|
1940
|
-
html += '</tr></thead><tbody>'
|
|
1941
|
-
|
|
1942
|
-
for (let r = 0; r < rows.length; r++) {
|
|
1943
|
-
html += '<tr>'
|
|
1944
|
-
for (let c = 0; c < pane.columns.length; c++) {
|
|
1945
|
-
const col = pane.columns[c]
|
|
1946
|
-
const val = rows[r][col.key]
|
|
1947
|
-
const cellHtml = formatCell(val, col)
|
|
1948
|
-
if (col.filterable && val != null) {
|
|
1949
|
-
html +=
|
|
1950
|
-
'<td class="ss-dbg-filterable" data-ss-filter-key="' +
|
|
1951
|
-
esc(col.key) +
|
|
1952
|
-
'" data-ss-filter-val="' +
|
|
1953
|
-
esc(String(val)) +
|
|
1954
|
-
'">' +
|
|
1955
|
-
cellHtml +
|
|
1956
|
-
'</td>'
|
|
1957
|
-
} else {
|
|
1958
|
-
html += '<td>' + cellHtml + '</td>'
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
html += '</tr>'
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
html += '</tbody></table>'
|
|
1965
|
-
bodyEl.innerHTML = html
|
|
1966
|
-
|
|
1967
|
-
// Bind click-to-filter on filterable cells
|
|
1968
|
-
bodyEl.querySelectorAll('.ss-dbg-filterable').forEach((td) => {
|
|
1969
|
-
td.style.cursor = 'pointer'
|
|
1970
|
-
td.addEventListener('click', () => {
|
|
1971
|
-
const val = td.getAttribute('data-ss-filter-val')
|
|
1972
|
-
const searchInput = document.getElementById('ss-dbg-search-' + pane.id)
|
|
1973
|
-
if (searchInput) {
|
|
1974
|
-
searchInput.value = val
|
|
1975
|
-
state.filter = val
|
|
1976
|
-
renderCustomPane(pane)
|
|
1977
|
-
}
|
|
1978
|
-
})
|
|
1979
|
-
})
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
// Bind search + clear for each custom pane
|
|
1983
|
-
for (let i = 0; i < customPanes.length; i++) {
|
|
1984
|
-
const cp = customPanes[i]
|
|
1985
|
-
const searchInput = document.getElementById('ss-dbg-search-' + cp.id)
|
|
1986
|
-
const clearBtn = document.getElementById('ss-dbg-' + cp.id + '-clear')
|
|
1987
|
-
|
|
1988
|
-
if (searchInput) {
|
|
1989
|
-
searchInput.addEventListener('input', () => {
|
|
1990
|
-
customPaneState[cp.id].filter = searchInput.value
|
|
1991
|
-
renderCustomPane(cp)
|
|
1992
|
-
})
|
|
1993
|
-
}
|
|
1994
|
-
if (clearBtn) {
|
|
1995
|
-
clearBtn.addEventListener('click', () => {
|
|
1996
|
-
customPaneState[cp.id].data = []
|
|
1997
|
-
customPaneState[cp.id].fetched = false
|
|
1998
|
-
if (searchInput) searchInput.value = ''
|
|
1999
|
-
customPaneState[cp.id].filter = ''
|
|
2000
|
-
renderCustomPane(cp)
|
|
2001
|
-
})
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
// ── Connection mode indicator ──────────────────────────────────
|
|
2006
|
-
const POLL_INTERVAL_NORMAL = REFRESH_INTERVAL
|
|
2007
|
-
const POLL_INTERVAL_LIVE = 15000 // slow polling as fallback when live
|
|
2008
|
-
|
|
2009
|
-
const updateConnectionIndicator = () => {
|
|
2010
|
-
const el = document.getElementById('ss-dbg-conn-mode')
|
|
2011
|
-
if (!el) return
|
|
2012
|
-
if (isLive) {
|
|
2013
|
-
el.textContent = 'live'
|
|
2014
|
-
el.className = 'ss-dbg-conn-mode ss-dbg-conn-live'
|
|
2015
|
-
el.title = 'Connected via Transmit (SSE) — real-time updates'
|
|
2016
|
-
} else {
|
|
2017
|
-
el.textContent = 'polling'
|
|
2018
|
-
el.className = 'ss-dbg-conn-mode ss-dbg-conn-polling'
|
|
2019
|
-
el.title = 'Polling every ' + POLL_INTERVAL_NORMAL / 1000 + 's'
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
// ── Auto-refresh ────────────────────────────────────────────────
|
|
2024
|
-
const startRefresh = () => {
|
|
2025
|
-
stopRefresh()
|
|
2026
|
-
fetchMiniStats()
|
|
2027
|
-
const interval = isLive ? POLL_INTERVAL_LIVE : POLL_INTERVAL_NORMAL
|
|
2028
|
-
refreshTimer = setInterval(() => {
|
|
2029
|
-
if (!isOpen) return
|
|
2030
|
-
loadTab(activeTab)
|
|
2031
|
-
fetchMiniStats()
|
|
2032
|
-
}, interval)
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
const stopRefresh = () => {
|
|
2036
|
-
if (refreshTimer) {
|
|
2037
|
-
clearInterval(refreshTimer)
|
|
2038
|
-
refreshTimer = null
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
|
|
2042
|
-
// ── Transmit (SSE) support ─────────────────────────────────────
|
|
2043
|
-
const initTransmit = () => {
|
|
2044
|
-
// window.Transmit is set by the inline IIFE injected before this module
|
|
2045
|
-
const TransmitClass = typeof window !== 'undefined' && window.Transmit ? window.Transmit : null
|
|
2046
|
-
|
|
2047
|
-
if (!TransmitClass) return // Transmit client not available
|
|
2048
|
-
|
|
2049
|
-
try {
|
|
2050
|
-
const transmit = new TransmitClass({
|
|
2051
|
-
baseUrl: window.location.origin,
|
|
2052
|
-
onSubscription: () => {
|
|
2053
|
-
isLive = true
|
|
2054
|
-
updateConnectionIndicator()
|
|
2055
|
-
// Restart refresh with slower interval now that we have live updates
|
|
2056
|
-
if (isOpen) startRefresh()
|
|
2057
|
-
},
|
|
2058
|
-
onReconnectFailed: () => {
|
|
2059
|
-
isLive = false
|
|
2060
|
-
updateConnectionIndicator()
|
|
2061
|
-
if (isOpen) startRefresh()
|
|
2062
|
-
},
|
|
2063
|
-
onSubscribeFailed: () => {
|
|
2064
|
-
isLive = false
|
|
2065
|
-
updateConnectionIndicator()
|
|
2066
|
-
},
|
|
2067
|
-
})
|
|
2068
|
-
|
|
2069
|
-
transmitSub = transmit.subscription('server-stats/debug')
|
|
2070
|
-
|
|
2071
|
-
transmitSub.onMessage((message) => {
|
|
2072
|
-
try {
|
|
2073
|
-
const event = typeof message === 'string' ? JSON.parse(message) : message
|
|
2074
|
-
handleLiveEvent(event)
|
|
2075
|
-
} catch {
|
|
2076
|
-
/* ignore */
|
|
2077
|
-
}
|
|
2078
|
-
})
|
|
2079
|
-
|
|
2080
|
-
transmitSub.create().catch(() => {
|
|
2081
|
-
isLive = false
|
|
2082
|
-
updateConnectionIndicator()
|
|
2083
|
-
})
|
|
2084
|
-
} catch {
|
|
2085
|
-
// Transmit init failed — stay on polling
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
const handleLiveEvent = (event) => {
|
|
2090
|
-
if (!isOpen) return
|
|
2091
|
-
|
|
2092
|
-
// Backend sends { types: ['query', 'event', ...] }
|
|
2093
|
-
const types = event.types || (event.type ? [event.type] : [])
|
|
2094
|
-
const tabMap = {
|
|
2095
|
-
query: 'queries',
|
|
2096
|
-
event: 'events',
|
|
2097
|
-
email: 'emails',
|
|
2098
|
-
trace: 'timeline',
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
let shouldRefresh = false
|
|
2102
|
-
for (let i = 0; i < types.length; i++) {
|
|
2103
|
-
const targetTab = tabMap[types[i]]
|
|
2104
|
-
if (targetTab && targetTab === activeTab) {
|
|
2105
|
-
shouldRefresh = true
|
|
2106
|
-
}
|
|
2107
|
-
if (types[i] === 'query') {
|
|
2108
|
-
updateBarQueryBadge()
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
|
-
if (shouldRefresh) {
|
|
2113
|
-
loadTab(activeTab)
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
// Initialize Transmit after a short delay to let the page fully load
|
|
2118
|
-
setTimeout(initTransmit, 500)
|
|
2119
|
-
updateConnectionIndicator()
|
|
2120
|
-
|
|
2121
|
-
// ── Stats bar query badge (always visible) ──────────────────────
|
|
2122
|
-
const updateBarQueryBadge = () => {
|
|
2123
|
-
const el = document.getElementById('ss-b-dbg-queries')
|
|
2124
|
-
if (!el) return
|
|
2125
|
-
|
|
2126
|
-
fetchJSON(BASE + '/queries')
|
|
2127
|
-
.then((data) => {
|
|
2128
|
-
const s = data.summary || {}
|
|
2129
|
-
const valEl = el.querySelector('.ss-value')
|
|
2130
|
-
if (valEl) {
|
|
2131
|
-
valEl.textContent = (s.total || 0) + ' / ' + (s.avgDuration || 0).toFixed(1) + 'ms'
|
|
2132
|
-
valEl.className = 'ss-value ' + (s.slow > 0 ? 'ss-amber' : 'ss-green')
|
|
2133
|
-
}
|
|
2134
|
-
})
|
|
2135
|
-
.catch(() => {})
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
updateBarQueryBadge()
|
|
2139
|
-
setInterval(updateBarQueryBadge, 5000)
|
|
2140
|
-
})()
|
|
1
|
+
(function(){"use strict";var ne,N,Ie,J,De,Be,je,Re,ge,be,ye,re={},se=[],Vt=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,G=Array.isArray;function U(e,n){for(var r in n)e[r]=n[r];return e}function ve(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function le(e,n,r){var s,i,l,o={};for(l in n)l=="key"?s=n[l]:l=="ref"?i=n[l]:o[l]=n[l];if(arguments.length>2&&(o.children=arguments.length>3?ne.call(arguments,2):r),typeof e=="function"&&e.defaultProps!=null)for(l in e.defaultProps)o[l]===void 0&&(o[l]=e.defaultProps[l]);return ie(e,o,s,i,null)}function ie(e,n,r,s,i){var l={type:e,props:n,key:r,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:i??++Ie,__i:-1,__u:0};return i==null&&N.vnode!=null&&N.vnode(l),l}function I(e){return e.children}function H(e,n){this.props=e,this.context=n}function K(e,n){if(n==null)return e.__?K(e.__,e.__i+1):null;for(var r;n<e.__k.length;n++)if((r=e.__k[n])!=null&&r.__e!=null)return r.__e;return typeof e.type=="function"?K(e):null}function Wt(e){if(e.__P&&e.__d){var n=e.__v,r=n.__e,s=[],i=[],l=U({},n);l.__v=n.__v+1,N.vnode&&N.vnode(l),we(e.__P,l,n,e.__n,e.__P.namespaceURI,32&n.__u?[r]:null,s,r??K(n),!!(32&n.__u),i),l.__v=n.__v,l.__.__k[l.__i]=l,qe(s,l,i),n.__e=n.__=null,l.__e!=r&&Fe(l)}}function Fe(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(n){if(n!=null&&n.__e!=null)return e.__e=e.__c.base=n.__e}),Fe(e)}function Ue(e){(!e.__d&&(e.__d=!0)&&J.push(e)&&!ae.__r++||De!=N.debounceRendering)&&((De=N.debounceRendering)||Be)(ae)}function ae(){for(var e,n=1;J.length;)J.length>n&&J.sort(je),e=J.shift(),n=J.length,Wt(e);ae.__r=0}function He(e,n,r,s,i,l,o,h,c,a,p){var d,u,f,_,m,y,b,g=s&&s.__k||se,k=n.length;for(c=qt(r,n,g,c,k),d=0;d<k;d++)(f=r.__k[d])!=null&&(u=f.__i!=-1&&g[f.__i]||re,f.__i=d,y=we(e,f,u,i,l,o,h,c,a,p),_=f.__e,f.ref&&u.ref!=f.ref&&(u.ref&&xe(u.ref,null,f),p.push(f.ref,f.__c||_,f)),m==null&&_!=null&&(m=_),(b=!!(4&f.__u))||u.__k===f.__k?c=ze(f,c,e,b):typeof f.type=="function"&&y!==void 0?c=y:_&&(c=_.nextSibling),f.__u&=-7);return r.__e=m,c}function qt(e,n,r,s,i){var l,o,h,c,a,p=r.length,d=p,u=0;for(e.__k=new Array(i),l=0;l<i;l++)(o=n[l])!=null&&typeof o!="boolean"&&typeof o!="function"?(typeof o=="string"||typeof o=="number"||typeof o=="bigint"||o.constructor==String?o=e.__k[l]=ie(null,o,null,null,null):G(o)?o=e.__k[l]=ie(I,{children:o},null,null,null):o.constructor===void 0&&o.__b>0?o=e.__k[l]=ie(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):e.__k[l]=o,c=l+u,o.__=e,o.__b=e.__b+1,h=null,(a=o.__i=Jt(o,r,c,d))!=-1&&(d--,(h=r[a])&&(h.__u|=2)),h==null||h.__v==null?(a==-1&&(i>p?u--:i<p&&u++),typeof o.type!="function"&&(o.__u|=4)):a!=c&&(a==c-1?u--:a==c+1?u++:(a>c?u--:u++,o.__u|=4))):e.__k[l]=null;if(d)for(l=0;l<p;l++)(h=r[l])!=null&&(2&h.__u)==0&&(h.__e==s&&(s=K(h)),Ke(h,h));return s}function ze(e,n,r,s){var i,l;if(typeof e.type=="function"){for(i=e.__k,l=0;i&&l<i.length;l++)i[l]&&(i[l].__=e,n=ze(i[l],n,r,s));return n}e.__e!=n&&(s&&(n&&e.type&&!n.parentNode&&(n=K(e)),r.insertBefore(e.__e,n||null)),n=e.__e);do n=n&&n.nextSibling;while(n!=null&&n.nodeType==8);return n}function oe(e,n){return n=n||[],e==null||typeof e=="boolean"||(G(e)?e.some(function(r){oe(r,n)}):n.push(e)),n}function Jt(e,n,r,s){var i,l,o,h=e.key,c=e.type,a=n[r],p=a!=null&&(2&a.__u)==0;if(a===null&&h==null||p&&h==a.key&&c==a.type)return r;if(s>(p?1:0)){for(i=r-1,l=r+1;i>=0||l<n.length;)if((a=n[o=i>=0?i--:l++])!=null&&(2&a.__u)==0&&h==a.key&&c==a.type)return o}return-1}function Ve(e,n,r){n[0]=="-"?e.setProperty(n,r??""):e[n]=r==null?"":typeof r!="number"||Vt.test(n)?r:r+"px"}function ce(e,n,r,s,i){var l,o;e:if(n=="style")if(typeof r=="string")e.style.cssText=r;else{if(typeof s=="string"&&(e.style.cssText=s=""),s)for(n in s)r&&n in r||Ve(e.style,n,"");if(r)for(n in r)s&&r[n]==s[n]||Ve(e.style,n,r[n])}else if(n[0]=="o"&&n[1]=="n")l=n!=(n=n.replace(Re,"$1")),o=n.toLowerCase(),n=o in e||n=="onFocusOut"||n=="onFocusIn"?o.slice(2):n.slice(2),e.l||(e.l={}),e.l[n+l]=r,r?s?r.u=s.u:(r.u=ge,e.addEventListener(n,l?ye:be,l)):e.removeEventListener(n,l?ye:be,l);else{if(i=="http://www.w3.org/2000/svg")n=n.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(n!="width"&&n!="height"&&n!="href"&&n!="list"&&n!="form"&&n!="tabIndex"&&n!="download"&&n!="rowSpan"&&n!="colSpan"&&n!="role"&&n!="popover"&&n in e)try{e[n]=r??"";break e}catch{}typeof r=="function"||(r==null||r===!1&&n[4]!="-"?e.removeAttribute(n):e.setAttribute(n,n=="popover"&&r==1?"":r))}}function We(e){return function(n){if(this.l){var r=this.l[n.type+e];if(n.t==null)n.t=ge++;else if(n.t<r.u)return;return r(N.event?N.event(n):n)}}}function we(e,n,r,s,i,l,o,h,c,a){var p,d,u,f,_,m,y,b,g,k,C,v,w,P,$,M=n.type;if(n.constructor!==void 0)return null;128&r.__u&&(c=!!(32&r.__u),l=[h=n.__e=r.__e]),(p=N.__b)&&p(n);e:if(typeof M=="function")try{if(b=n.props,g="prototype"in M&&M.prototype.render,k=(p=M.contextType)&&s[p.__c],C=p?k?k.props.value:p.__:s,r.__c?y=(d=n.__c=r.__c).__=d.__E:(g?n.__c=d=new M(b,C):(n.__c=d=new H(b,C),d.constructor=M,d.render=Qt),k&&k.sub(d),d.state||(d.state={}),d.__n=s,u=d.__d=!0,d.__h=[],d._sb=[]),g&&d.__s==null&&(d.__s=d.state),g&&M.getDerivedStateFromProps!=null&&(d.__s==d.state&&(d.__s=U({},d.__s)),U(d.__s,M.getDerivedStateFromProps(b,d.__s))),f=d.props,_=d.state,d.__v=n,u)g&&M.getDerivedStateFromProps==null&&d.componentWillMount!=null&&d.componentWillMount(),g&&d.componentDidMount!=null&&d.__h.push(d.componentDidMount);else{if(g&&M.getDerivedStateFromProps==null&&b!==f&&d.componentWillReceiveProps!=null&&d.componentWillReceiveProps(b,C),n.__v==r.__v||!d.__e&&d.shouldComponentUpdate!=null&&d.shouldComponentUpdate(b,d.__s,C)===!1){n.__v!=r.__v&&(d.props=b,d.state=d.__s,d.__d=!1),n.__e=r.__e,n.__k=r.__k,n.__k.some(function(q){q&&(q.__=n)}),se.push.apply(d.__h,d._sb),d._sb=[],d.__h.length&&o.push(d);break e}d.componentWillUpdate!=null&&d.componentWillUpdate(b,d.__s,C),g&&d.componentDidUpdate!=null&&d.__h.push(function(){d.componentDidUpdate(f,_,m)})}if(d.context=C,d.props=b,d.__P=e,d.__e=!1,v=N.__r,w=0,g)d.state=d.__s,d.__d=!1,v&&v(n),p=d.render(d.props,d.state,d.context),se.push.apply(d.__h,d._sb),d._sb=[];else do d.__d=!1,v&&v(n),p=d.render(d.props,d.state,d.context),d.state=d.__s;while(d.__d&&++w<25);d.state=d.__s,d.getChildContext!=null&&(s=U(U({},s),d.getChildContext())),g&&!u&&d.getSnapshotBeforeUpdate!=null&&(m=d.getSnapshotBeforeUpdate(f,_)),P=p!=null&&p.type===I&&p.key==null?Je(p.props.children):p,h=He(e,G(P)?P:[P],n,r,s,i,l,o,h,c,a),d.base=n.__e,n.__u&=-161,d.__h.length&&o.push(d),y&&(d.__E=d.__=null)}catch(q){if(n.__v=null,c||l!=null)if(q.then){for(n.__u|=c?160:128;h&&h.nodeType==8&&h.nextSibling;)h=h.nextSibling;l[l.indexOf(h)]=null,n.__e=h}else{for($=l.length;$--;)ve(l[$]);Ne(n)}else n.__e=r.__e,n.__k=r.__k,q.then||Ne(n);N.__e(q,n,r)}else l==null&&n.__v==r.__v?(n.__k=r.__k,n.__e=r.__e):h=n.__e=Kt(r.__e,n,r,s,i,l,o,c,a);return(p=N.diffed)&&p(n),128&n.__u?void 0:h}function Ne(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Ne))}function qe(e,n,r){for(var s=0;s<r.length;s++)xe(r[s],r[++s],r[++s]);N.__c&&N.__c(n,e),e.some(function(i){try{e=i.__h,i.__h=[],e.some(function(l){l.call(i)})}catch(l){N.__e(l,i.__v)}})}function Je(e){return typeof e!="object"||e==null||e.__b>0?e:G(e)?e.map(Je):U({},e)}function Kt(e,n,r,s,i,l,o,h,c){var a,p,d,u,f,_,m,y=r.props||re,b=n.props,g=n.type;if(g=="svg"?i="http://www.w3.org/2000/svg":g=="math"?i="http://www.w3.org/1998/Math/MathML":i||(i="http://www.w3.org/1999/xhtml"),l!=null){for(a=0;a<l.length;a++)if((f=l[a])&&"setAttribute"in f==!!g&&(g?f.localName==g:f.nodeType==3)){e=f,l[a]=null;break}}if(e==null){if(g==null)return document.createTextNode(b);e=document.createElementNS(i,g,b.is&&b),h&&(N.__m&&N.__m(n,l),h=!1),l=null}if(g==null)y===b||h&&e.data==b||(e.data=b);else{if(l=l&&ne.call(e.childNodes),!h&&l!=null)for(y={},a=0;a<e.attributes.length;a++)y[(f=e.attributes[a]).name]=f.value;for(a in y)f=y[a],a=="dangerouslySetInnerHTML"?d=f:a=="children"||a in b||a=="value"&&"defaultValue"in b||a=="checked"&&"defaultChecked"in b||ce(e,a,null,f,i);for(a in b)f=b[a],a=="children"?u=f:a=="dangerouslySetInnerHTML"?p=f:a=="value"?_=f:a=="checked"?m=f:h&&typeof f!="function"||y[a]===f||ce(e,a,f,y[a],i);if(p)h||d&&(p.__html==d.__html||p.__html==e.innerHTML)||(e.innerHTML=p.__html),n.__k=[];else if(d&&(e.innerHTML=""),He(n.type=="template"?e.content:e,G(u)?u:[u],n,r,s,g=="foreignObject"?"http://www.w3.org/1999/xhtml":i,l,o,l?l[0]:r.__k&&K(r,0),h,c),l!=null)for(a=l.length;a--;)ve(l[a]);h||(a="value",g=="progress"&&_==null?e.removeAttribute("value"):_!=null&&(_!==e[a]||g=="progress"&&!_||g=="option"&&_!=y[a])&&ce(e,a,_,y[a],i),a="checked",m!=null&&m!=e[a]&&ce(e,a,m,y[a],i))}return e}function xe(e,n,r){try{if(typeof e=="function"){var s=typeof e.__u=="function";s&&e.__u(),s&&n==null||(e.__u=e(n))}else e.current=n}catch(i){N.__e(i,r)}}function Ke(e,n,r){var s,i;if(N.unmount&&N.unmount(e),(s=e.ref)&&(s.current&&s.current!=e.__e||xe(s,null,n)),(s=e.__c)!=null){if(s.componentWillUnmount)try{s.componentWillUnmount()}catch(l){N.__e(l,n)}s.base=s.__P=null}if(s=e.__k)for(i=0;i<s.length;i++)s[i]&&Ke(s[i],n,r||typeof e.type!="function");r||ve(e.__e),e.__c=e.__=e.__e=void 0}function Qt(e,n,r){return this.constructor(e,r)}function Gt(e,n,r){var s,i,l,o;n==document&&(n=document.documentElement),N.__&&N.__(e,n),i=(s=!1)?null:n.__k,l=[],o=[],we(n,e=n.__k=le(I,null,[e]),i||re,re,n.namespaceURI,i?null:n.firstChild?ne.call(n.childNodes):null,l,i?i.__e:n.firstChild,s,o),qe(l,e,o)}ne=se.slice,N={__e:function(e,n,r,s){for(var i,l,o;n=n.__;)if((i=n.__c)&&!i.__)try{if((l=i.constructor)&&l.getDerivedStateFromError!=null&&(i.setState(l.getDerivedStateFromError(e)),o=i.__d),i.componentDidCatch!=null&&(i.componentDidCatch(e,s||{}),o=i.__d),o)return i.__E=i}catch(h){e=h}throw e}},Ie=0,H.prototype.setState=function(e,n){var r;r=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=U({},this.state),typeof e=="function"&&(e=e(U({},r),this.props)),e&&U(r,e),e!=null&&this.__v&&(n&&this._sb.push(n),Ue(this))},H.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),Ue(this))},H.prototype.render=I,J=[],Be=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,je=function(e,n){return e.__v.__b-n.__v.__b},ae.__r=0,Re=/(PointerCapture)$|Capture$/i,ge=0,be=We(!1),ye=We(!0);var Xt=0;function t(e,n,r,s,i,l){n||(n={});var o,h,c=n;if("ref"in c)for(h in c={},n)h=="ref"?o=n[h]:c[h]=n[h];var a={type:e,props:c,key:r,ref:o,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--Xt,__i:-1,__u:0,__source:i,__self:l};if(typeof e=="function"&&(o=e.defaultProps))for(h in o)c[h]===void 0&&(c[h]=o[h]);return N.vnode&&N.vnode(a),a}var X,L,$e,Qe,Y=0,Ge=[],E=N,Xe=E.__b,Ye=E.__r,Ze=E.diffed,et=E.__c,tt=E.unmount,nt=E.__;function ke(e,n){E.__h&&E.__h(L,e,Y||n),Y=0;var r=L.__H||(L.__H={__:[],__h:[]});return e>=r.__.length&&r.__.push({}),r.__[e]}function x(e){return Y=1,Yt(lt,e)}function Yt(e,n,r){var s=ke(X++,2);if(s.t=e,!s.__c&&(s.__=[r?r(n):lt(void 0,n),function(h){var c=s.__N?s.__N[0]:s.__[0],a=s.t(c,h);c!==a&&(s.__N=[a,s.__[1]],s.__c.setState({}))}],s.__c=L,!L.__f)){var i=function(h,c,a){if(!s.__c.__H)return!0;var p=s.__c.__H.__.filter(function(u){return u.__c});if(p.every(function(u){return!u.__N}))return!l||l.call(this,h,c,a);var d=s.__c.props!==h;return p.some(function(u){if(u.__N){var f=u.__[0];u.__=u.__N,u.__N=void 0,f!==u.__[0]&&(d=!0)}}),l&&l.call(this,h,c,a)||d};L.__f=!0;var l=L.shouldComponentUpdate,o=L.componentWillUpdate;L.componentWillUpdate=function(h,c,a){if(this.__e){var p=l;l=void 0,i(h,c,a),l=p}o&&o.call(this,h,c,a)},L.shouldComponentUpdate=i}return s.__N||s.__}function z(e,n){var r=ke(X++,3);!E.__s&&st(r.__H,n)&&(r.__=e,r.u=n,L.__H.__h.push(r))}function B(e){return Y=5,T(function(){return{current:e}},[])}function T(e,n){var r=ke(X++,7);return st(r.__H,n)&&(r.__=e(),r.__H=n,r.__h=e),r.__}function S(e,n){return Y=8,T(function(){return e},n)}function Zt(){for(var e;e=Ge.shift();){var n=e.__H;if(e.__P&&n)try{n.__h.some(de),n.__h.some(Ce),n.__h=[]}catch(r){n.__h=[],E.__e(r,e.__v)}}}E.__b=function(e){L=null,Xe&&Xe(e)},E.__=function(e,n){e&&n.__k&&n.__k.__m&&(e.__m=n.__k.__m),nt&&nt(e,n)},E.__r=function(e){Ye&&Ye(e),X=0;var n=(L=e.__c).__H;n&&($e===L?(n.__h=[],L.__h=[],n.__.some(function(r){r.__N&&(r.__=r.__N),r.u=r.__N=void 0})):(n.__h.some(de),n.__h.some(Ce),n.__h=[],X=0)),$e=L},E.diffed=function(e){Ze&&Ze(e);var n=e.__c;n&&n.__H&&(n.__H.__h.length&&(Ge.push(n)!==1&&Qe===E.requestAnimationFrame||((Qe=E.requestAnimationFrame)||en)(Zt)),n.__H.__.some(function(r){r.u&&(r.__H=r.u),r.u=void 0})),$e=L=null},E.__c=function(e,n){n.some(function(r){try{r.__h.some(de),r.__h=r.__h.filter(function(s){return!s.__||Ce(s)})}catch(s){n.some(function(i){i.__h&&(i.__h=[])}),n=[],E.__e(s,r.__v)}}),et&&et(e,n)},E.unmount=function(e){tt&&tt(e);var n,r=e.__c;r&&r.__H&&(r.__H.__.some(function(s){try{de(s)}catch(i){n=i}}),r.__H=void 0,n&&E.__e(n,r.__v))};var rt=typeof requestAnimationFrame=="function";function en(e){var n,r=function(){clearTimeout(s),rt&&cancelAnimationFrame(n),setTimeout(e)},s=setTimeout(r,35);rt&&(n=requestAnimationFrame(r))}function de(e){var n=L,r=e.__c;typeof r=="function"&&(e.__c=void 0,r()),L=n}function Ce(e){var n=L;e.__c=e.__(),L=n}function st(e,n){return!e||e.length!==n.length||n.some(function(r,s){return r!==e[s]})}function lt(e,n){return typeof n=="function"?n(e):n}function tn(e,n){for(var r in n)e[r]=n[r];return e}function it(e,n){for(var r in e)if(r!=="__source"&&!(r in n))return!0;for(var s in n)if(s!=="__source"&&e[s]!==n[s])return!0;return!1}function at(e,n){this.props=e,this.context=n}(at.prototype=new H).isPureReactComponent=!0,at.prototype.shouldComponentUpdate=function(e,n){return it(this.props,e)||it(this.state,n)};var ot=N.__b;N.__b=function(e){e.type&&e.type.__f&&e.ref&&(e.props.ref=e.ref,e.ref=null),ot&&ot(e)};var nn=N.__e;N.__e=function(e,n,r,s){if(e.then){for(var i,l=n;l=l.__;)if((i=l.__c)&&i.__c)return n.__e==null&&(n.__e=r.__e,n.__k=r.__k),i.__c(e,n)}nn(e,n,r,s)};var ct=N.unmount;function dt(e,n,r){return e&&(e.__c&&e.__c.__H&&(e.__c.__H.__.forEach(function(s){typeof s.__c=="function"&&s.__c()}),e.__c.__H=null),(e=tn({},e)).__c!=null&&(e.__c.__P===r&&(e.__c.__P=n),e.__c.__e=!0,e.__c=null),e.__k=e.__k&&e.__k.map(function(s){return dt(s,n,r)})),e}function ht(e,n,r){return e&&r&&(e.__v=null,e.__k=e.__k&&e.__k.map(function(s){return ht(s,n,r)}),e.__c&&e.__c.__P===n&&(e.__e&&r.appendChild(e.__e),e.__c.__e=!0,e.__c.__P=r)),e}function Z(){this.__u=0,this.o=null,this.__b=null}function ut(e){if(!e.__)return null;var n=e.__.__c;return n&&n.__a&&n.__a(e)}function D(e){var n,r,s,i=null;function l(o){if(n||(n=e()).then(function(h){h&&(i=h.default||h),s=!0},function(h){r=h,s=!0}),r)throw r;if(!s)throw n;return i?le(i,o):null}return l.displayName="Lazy",l.__f=!0,l}function he(){this.i=null,this.l=null}N.unmount=function(e){var n=e.__c;n&&(n.__z=!0),n&&n.__R&&n.__R(),n&&32&e.__u&&(e.type=null),ct&&ct(e)},(Z.prototype=new H).__c=function(e,n){var r=n.__c,s=this;s.o==null&&(s.o=[]),s.o.push(r);var i=ut(s.__v),l=!1,o=function(){l||s.__z||(l=!0,r.__R=null,i?i(c):c())};r.__R=o;var h=r.__P;r.__P=null;var c=function(){if(!--s.__u){if(s.state.__a){var a=s.state.__a;s.__v.__k[0]=ht(a,a.__c.__P,a.__c.__O)}var p;for(s.setState({__a:s.__b=null});p=s.o.pop();)p.__P=h,p.forceUpdate()}};s.__u++||32&n.__u||s.setState({__a:s.__b=s.__v.__k[0]}),e.then(o,o)},Z.prototype.componentWillUnmount=function(){this.o=[]},Z.prototype.render=function(e,n){if(this.__b){if(this.__v.__k){var r=document.createElement("div"),s=this.__v.__k[0].__c;this.__v.__k[0]=dt(this.__b,r,s.__O=s.__P)}this.__b=null}var i=n.__a&&le(I,null,e.fallback);return i&&(i.__u&=-33),[le(I,null,n.__a?null:e.children),i]};var ft=function(e,n,r){if(++r[1]===r[0]&&e.l.delete(n),e.props.revealOrder&&(e.props.revealOrder[0]!=="t"||!e.l.size))for(r=e.i;r;){for(;r.length>3;)r.pop()();if(r[1]<r[0])break;e.i=r=r[2]}};(he.prototype=new H).__a=function(e){var n=this,r=ut(n.__v),s=n.l.get(e);return s[0]++,function(i){var l=function(){n.props.revealOrder?(s.push(i),ft(n,e,s)):i()};r?r(l):l()}},he.prototype.render=function(e){this.i=null,this.l=new Map;var n=oe(e.children);e.revealOrder&&e.revealOrder[0]==="b"&&n.reverse();for(var r=n.length;r--;)this.l.set(n[r],this.i=[1,0,this.i]);return e.children},he.prototype.componentDidUpdate=he.prototype.componentDidMount=function(){var e=this;this.l.forEach(function(n,r){ft(e,r,n)})};var rn=typeof Symbol<"u"&&Symbol.for&&Symbol.for("react.element")||60103,sn=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,ln=/^on(Ani|Tra|Tou|BeforeInp|Compo)/,an=/[A-Z0-9]/g,on=typeof document<"u",cn=function(e){return(typeof Symbol<"u"&&typeof Symbol()=="symbol"?/fil|che|rad/:/fil|che|ra/).test(e)};H.prototype.isReactComponent={},["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(e){Object.defineProperty(H.prototype,e,{configurable:!0,get:function(){return this["UNSAFE_"+e]},set:function(n){Object.defineProperty(this,e,{configurable:!0,writable:!0,value:n})}})});var pt=N.event;function dn(){}function hn(){return this.cancelBubble}function un(){return this.defaultPrevented}N.event=function(e){return pt&&(e=pt(e)),e.persist=dn,e.isPropagationStopped=hn,e.isDefaultPrevented=un,e.nativeEvent=e};var fn={enumerable:!1,configurable:!0,get:function(){return this.class}},_t=N.vnode;N.vnode=function(e){typeof e.type=="string"&&(function(n){var r=n.props,s=n.type,i={},l=s.indexOf("-")===-1;for(var o in r){var h=r[o];if(!(o==="value"&&"defaultValue"in r&&h==null||on&&o==="children"&&s==="noscript"||o==="class"||o==="className")){var c=o.toLowerCase();o==="defaultValue"&&"value"in r&&r.value==null?o="value":o==="download"&&h===!0?h="":c==="translate"&&h==="no"?h=!1:c[0]==="o"&&c[1]==="n"?c==="ondoubleclick"?o="ondblclick":c!=="onchange"||s!=="input"&&s!=="textarea"||cn(r.type)?c==="onfocus"?o="onfocusin":c==="onblur"?o="onfocusout":ln.test(o)&&(o=c):c=o="oninput":l&&sn.test(o)?o=o.replace(an,"-$&").toLowerCase():h===null&&(h=void 0),c==="oninput"&&i[o=c]&&(o="oninputCapture"),i[o]=h}}s=="select"&&i.multiple&&Array.isArray(i.value)&&(i.value=oe(r.children).forEach(function(a){a.props.selected=i.value.indexOf(a.props.value)!=-1})),s=="select"&&i.defaultValue!=null&&(i.value=oe(r.children).forEach(function(a){a.props.selected=i.multiple?i.defaultValue.indexOf(a.props.value)!=-1:i.defaultValue==a.props.value})),r.class&&!r.className?(i.class=r.class,Object.defineProperty(i,"className",fn)):r.className&&(i.class=i.className=r.className),n.props=i})(e),e.$$typeof=rn,_t&&_t(e)};var mt=N.__r;N.__r=function(e){mt&&mt(e),e.__c};var gt=N.diffed;N.diffed=function(e){gt&>(e);var n=e.props,r=e.__e;r!=null&&e.type==="textarea"&&"value"in n&&n.value!==r.value&&(r.value=n.value==null?"":n.value)};const A={queries:{viewBox:"0 0 24 24",elements:['<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"/>']},events:{viewBox:"0 0 24 24",elements:['<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>']},emails:{viewBox:"0 0 24 24",elements:['<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>','<polyline points="22,6 12,13 2,6"/>']},routes:{viewBox:"0 0 24 24",elements:['<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"/>']},logs:{viewBox:"0 0 24 24",elements:['<line x1="8" y1="6" x2="21" y2="6"/>','<line x1="8" y1="12" x2="21" y2="12"/>','<line x1="8" y1="18" x2="21" y2="18"/>','<line x1="3" y1="6" x2="3.01" y2="6"/>','<line x1="3" y1="12" x2="3.01" y2="12"/>','<line x1="3" y1="18" x2="3.01" y2="18"/>']},timeline:{viewBox:"0 0 24 24",elements:['<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>']},cache:{viewBox:"0 0 24 24",elements:['<rect x="2" y="2" width="20" height="8" rx="2" ry="2"/>','<rect x="2" y="14" width="20" height="8" rx="2" ry="2"/>','<line x1="6" y1="6" x2="6.01" y2="6"/>','<line x1="6" y1="18" x2="6.01" y2="18"/>']},jobs:{viewBox:"0 0 24 24",elements:['<rect x="2" y="7" width="20" height="14" rx="2" ry="2"/>','<path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/>']},config:{viewBox:"0 0 24 24",elements:['<circle cx="12" cy="12" r="3"/>','<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>']},internals:{viewBox:"0 0 24 24",elements:['<rect x="4" y="4" width="16" height="16" rx="2"/>','<rect x="9" y="9" width="6" height="6"/>','<line x1="9" y1="1" x2="9" y2="4"/>','<line x1="15" y1="1" x2="15" y2="4"/>','<line x1="9" y1="20" x2="9" y2="23"/>','<line x1="15" y1="20" x2="15" y2="23"/>','<line x1="20" y1="9" x2="23" y2="9"/>','<line x1="20" y1="14" x2="23" y2="14"/>','<line x1="1" y1="9" x2="4" y2="9"/>','<line x1="1" y1="14" x2="4" y2="14"/>']},overview:{viewBox:"0 0 24 24",elements:['<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"/>']},requests:{viewBox:"0 0 24 24",elements:['<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>']},"dashboard-timeline":{viewBox:"0 0 24 24",elements:['<circle cx="12" cy="12" r="10"/>','<polyline points="12 6 12 12 16 14"/>']},"custom-pane":{viewBox:"0 0 24 24",elements:['<rect x="3" y="3" width="18" height="18" rx="2"/>','<path d="M9 3v18"/>']},wrench:{viewBox:"0 0 24 24",elements:['<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>']},"external-link":{viewBox:"0 0 24 24",elements:['<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>','<polyline points="15 3 21 3 21 9"/>','<line x1="10" y1="14" x2="21" y2="3"/>']},sun:{viewBox:"0 0 24 24",elements:['<circle cx="12" cy="12" r="5"/>','<line x1="12" y1="1" x2="12" y2="3"/>','<line x1="12" y1="21" x2="12" y2="23"/>','<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>','<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>','<line x1="1" y1="12" x2="3" y2="12"/>','<line x1="21" y1="12" x2="23" y2="12"/>','<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>','<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>']},moon:{viewBox:"0 0 24 24",elements:['<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>']},search:{viewBox:"0 0 24 24",elements:['<circle cx="11" cy="11" r="8"/>','<line x1="21" y1="21" x2="16.65" y2="16.65"/>']},eye:{viewBox:"0 0 24 24",elements:['<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>','<circle cx="12" cy="12" r="3"/>']},"eye-off":{viewBox:"0 0 24 24",elements:['<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>','<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>','<line x1="1" y1="1" x2="23" y2="23"/>','<path d="M14.12 14.12a3 3 0 1 1-4.24-4.24"/>']},"chevron-right":{viewBox:"0 0 24 24",elements:['<path d="M9 18l6-6-6-6"/>']},"chevron-left":{viewBox:"0 0 24 24",elements:['<path d="M15 18l-6-6 6-6"/>']},"open-external":{viewBox:"0 0 16 16",elements:['<path d="M6 3H3v10h10v-3M9 1h6v6M7 9L15 1"/>']}};class ue extends Error{status;constructor(n=403){super(`Unauthorized (HTTP ${n})`),this.name="UnauthorizedError",this.status=n}}class pn extends Error{status;body;constructor(n,r){super(`API error (HTTP ${n})`),this.name="ApiError",this.status=n,this.body=r}}class Se{baseUrl;authToken;constructor(n){this.baseUrl=n.baseUrl.replace(/\/+$/,""),this.authToken=n.authToken}async fetch(n,r){const i={...{Accept:"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}},...r?.headers},l=await globalThis.fetch(`${this.baseUrl}${n}`,{...r,headers:i,credentials:this.authToken?"omit":"include"});if(l.status===401||l.status===403)throw new ue(l.status);if(!l.ok){const o=await l.text().catch(()=>"");throw new pn(l.status,o)}return l.json()}async get(n,r){const s=r?`${n}?${r}`:n;return this.fetch(s)}async post(n,r){const s={method:"POST",...r!==void 0?{body:JSON.stringify(r),headers:{"Content-Type":"application/json"}}:{}};return this.fetch(n,s)}async delete(n){return this.fetch(n,{method:"DELETE"})}}const bt="/admin/api/debug",yt={tracing:!1,process:!1,system:!1,http:!1,db:!1,redis:!1,queues:!1,cache:!1,app:!1,log:!1,emails:!1,dashboard:!1,customPanes:[]};function _n(e){return{tracing:e.features?.tracing??!1,process:e.features?.process??!1,system:e.features?.system??!1,http:e.features?.http??!1,db:e.features?.db??!1,redis:e.features?.redis??!1,queues:e.features?.queues??!1,cache:e.features?.cache??!1,app:e.features?.app??!1,log:e.features?.log??!1,emails:e.features?.emails??!1,dashboard:e.features?.dashboard??!1,customPanes:e.customPanes??[]}}async function mn(e,n=bt){const r=`${n.replace(/\/+$/,"")}/config`;return e.fetch(r)}async function gn(e){const{baseUrl:n="",debugEndpoint:r=bt,authToken:s}=e,i=new Se({baseUrl:n,authToken:s});try{const l=await mn(i,r);return _n(l)}catch{return yt}}function bn(e={}){const{baseUrl:n="",debugEndpoint:r="/admin/api/debug",authToken:s}=e,[i,l]=x(yt),[o,h]=x(!0),[c,a]=x(null),p=B(!1);return z(()=>{if(p.current)return;p.current=!0;let d=!1;return(async()=>{try{const f=await gn({baseUrl:n,debugEndpoint:r,authToken:s});d||(l(f),h(!1))}catch(f){d||(a(f instanceof Error?f:new Error(String(f))),h(!1))}})(),()=>{d=!0}},[n,r,s]),{features:i,isLoading:o,error:c}}const fe="ss-dash-theme",Te="ss-theme-change";function Le(){if(typeof window>"u")return"light";const e=localStorage.getItem(fe);return e==="dark"||e==="light"?e:window.matchMedia?.("(prefers-color-scheme: dark)").matches?"dark":"light"}function yn(e){typeof window>"u"||(localStorage.setItem(fe,e),window.dispatchEvent(new CustomEvent(Te,{detail:e})))}function vn(){const n=Le()==="dark"?"light":"dark";return yn(n),n}function wn(e){if(typeof window>"u")return()=>{};const n=l=>{const o=l.detail;(o==="dark"||o==="light")&&e(o)},r=l=>{if(l.key===fe){const o=l.newValue;e(o==="dark"||o==="light"?o:Le())}},s=window.matchMedia("(prefers-color-scheme: dark)"),i=l=>{localStorage.getItem(fe)||e(l.matches?"dark":"light")};return window.addEventListener(Te,n),window.addEventListener("storage",r),s.addEventListener("change",i),()=>{window.removeEventListener(Te,n),window.removeEventListener("storage",r),s.removeEventListener("change",i)}}function Nn(){const[e,n]=x(()=>Le());z(()=>wn(i=>{n(i)}),[]);const r=S(()=>{const s=vn();return n(s),s},[]);return{theme:e,toggleTheme:r}}function xn({theme:e,onToggle:n,className:r="",classPrefix:s="ss-dash"}){const i=e==="dark";return t("button",{type:"button",className:`${s==="ss-dbg"?"ss-dbg-theme-toggle":"ss-dash-theme-btn"} ${r}`,onClick:n,title:i?"Switch to light theme":"Switch to dark theme","aria-label":i?"Switch to light theme":"Switch to dark theme",children:i?t("svg",{width:"16",height:"16",viewBox:A.sun.viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A.sun.elements.join("")}}):t("svg",{width:"16",height:"16",viewBox:A.moon.viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A.moon.elements.join("")}})})}const $n=D(()=>Promise.resolve().then(()=>Gn)),kn=D(()=>Promise.resolve().then(()=>Xn)),Cn=D(()=>Promise.resolve().then(()=>Yn)),Sn=D(()=>Promise.resolve().then(()=>Zn)),Tn=D(()=>Promise.resolve().then(()=>sr)),Ln=D(()=>Promise.resolve().then(()=>ir)),En=D(()=>Promise.resolve().then(()=>ar)),An=D(()=>Promise.resolve().then(()=>ur)),Pn=D(()=>Promise.resolve().then(()=>vr)),Mn=D(()=>Promise.resolve().then(()=>Or)),On=D(()=>Promise.resolve().then(()=>Ir));function In(e){const{defaultOpen:n=!1,dashboardPath:r,isOpen:s,onOpenChange:i,isLive:l=!1,...o}=e,[h,c]=x(n),a=s!==void 0?s:h,p=v=>{i?i(v):c(v)},[d,u]=x("queries"),{features:f}=bn(o),{theme:_,toggleTheme:m}=Nn(),y=f.customPanes||[];z(()=>{const v=w=>{w.key==="Escape"&&a&&p(!1)};return document.addEventListener("keydown",v),()=>document.removeEventListener("keydown",v)},[a]);const b=T(()=>[{id:"queries",label:"Queries",visible:!0},{id:"events",label:"Events",visible:!0},{id:"emails",label:"Emails",visible:!0},{id:"routes",label:"Routes",visible:!0},{id:"logs",label:"Logs",visible:!0},{id:"timeline",label:"Timeline",visible:f.tracing},{id:"cache",label:"Cache",visible:f.cache},{id:"jobs",label:"Jobs",visible:f.queues},{id:"config",label:"Config",visible:!0},{id:"internals",label:"Internals",visible:!0}],[f]),g=T(()=>b.filter(v=>v.visible),[b]);z(()=>{const v=[...g.map(w=>w.id),...y.map(w=>w.id)];!v.includes(d)&&v.length>0&&u(v[0])},[g,y,d]);const k=S(()=>{p(!a)},[a]),C=S(()=>{const v={options:o},w=y.find($=>$.id===d);if(w)return t(Z,{fallback:t("div",{className:"ss-dbg-empty",children:"Loading..."}),children:t(On,{pane:w,options:o})});const P={queries:t($n,{...v}),events:t(kn,{...v}),emails:t(Cn,{...v}),routes:t(Sn,{...v,currentPath:typeof window<"u"?window.location.pathname:""}),logs:t(Tn,{...v}),timeline:t(Ln,{...v}),cache:t(En,{...v,dashboardPath:r}),jobs:t(An,{...v,dashboardPath:r}),config:t(Pn,{...v,dashboardPath:r}),internals:t(Mn,{...v})};return t(Z,{fallback:t("div",{className:"ss-dbg-empty",children:"Loading..."}),children:P[d]||t("div",{className:"ss-dbg-empty",children:"Unknown tab"})})},[d,o,y]);return t(I,{children:[s===void 0&&t("button",{type:"button",className:`ss-dbg-btn ${a?"ss-dbg-active":""}`,onClick:k,title:"Toggle debug panel",id:"ss-dbg-wrench",children:t("svg",{width:"14",height:"14",viewBox:A.wrench.viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A.wrench.elements.join("")}})}),t("div",{className:`ss-dbg-panel ${a?"ss-dbg-open":""}`,"data-ss-theme":_,id:"ss-dbg-panel",children:[t("div",{className:"ss-dbg-tabs",children:[t("div",{className:"ss-dbg-tabs-scroll",children:[g.map(v=>t("button",{type:"button",className:`ss-dbg-tab ${d===v.id?"ss-dbg-active":""}`,onClick:()=>u(v.id),children:[A[v.id]?t("svg",{className:"ss-dbg-tab-icon",viewBox:A[v.id].viewBox,dangerouslySetInnerHTML:{__html:A[v.id].elements.join("")}}):null,v.label]},v.id)),y.map(v=>t("button",{type:"button",className:`ss-dbg-tab ${d===v.id?"ss-dbg-active":""}`,onClick:()=>u(v.id),children:v.label},v.id))]}),t("div",{className:"ss-dbg-tabs-right",children:[t("span",{className:`ss-dbg-conn-mode ${l?"ss-dbg-conn-live":"ss-dbg-conn-polling"}`,title:l?"Connected via Transmit (SSE) — real-time updates":"Polling every 3s",children:l?"live":"polling"}),t(xn,{theme:_,onToggle:m,classPrefix:"ss-dbg"}),r&&t("a",{href:r,target:"_blank",rel:"noopener noreferrer",className:"ss-dbg-dashboard-link",title:"Open dashboard",children:t("svg",{width:"14",height:"14",viewBox:A["external-link"].viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A["external-link"].elements.join("")}})}),t("button",{type:"button",className:"ss-dbg-close",onClick:()=>p(!1),title:"Close panel",children:"×"})]})]}),t("div",{className:"ss-dbg-content",children:a&&C()})]})]})}function Dn(e){const n=document.getElementById(e);return n?JSON.parse(n.textContent||"{}"):{}}const Ee=Dn("ss-dbg-config"),vt=document.getElementById("ss-dbg-panel");vt&&Gt(t(In,{debugEndpoint:Ee.debugEndpoint,authToken:Ee.authToken,dashboardPath:Ee.dashboardPath||void 0,defaultOpen:!1}),vt);const Bn=3e3,jn=100,Rn=500;function Fn(e){if(!e&&e!==0)return"-";const n=Math.floor(e),r=Math.floor(n/86400),s=Math.floor(n%86400/3600),i=Math.floor(n%3600/60);return r>0?`${r}d ${s}h`:s>0?`${s}h ${i}m`:i>0?`${i}m ${n%60}s`:`${n}s`}function j(e){return e>=1e3?`${(e/1e3).toFixed(2)}s`:e>=1?`${e.toFixed(0)}ms`:`${e.toFixed(2)}ms`}function V(e){if(!e)return"-";const n=typeof e=="string"?new Date(e):new Date(e);return Number.isNaN(n.getTime())?"-":n.toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})+"."+String(n.getMilliseconds()).padStart(3,"0")}function R(e){if(!e)return"-";const n=typeof e=="string"?new Date(e).getTime():e,r=Math.floor((Date.now()-n)/1e3);return r<0?"just now":r<60?`${r}s ago`:r<3600?`${Math.floor(r/60)}m ago`:r<86400?`${Math.floor(r/3600)}h ago`:`${Math.floor(r/86400)}d ago`}function Q(e){return e>Rn?"very-slow":e>jn?"slow":"normal"}function pe(e,n=100){if(e===null)return"null";if(e===void 0)return"-";if(typeof e=="string")return'"'+(e.length>40?e.slice(0,40)+"...":e)+'"';if(typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e)){if(e.length===0)return"[]";const s="["+e.slice(0,3).map(i=>pe(i,30)).join(", ")+(e.length>3?", ..."+e.length+" items":"")+"]";return s.length>n?"["+e.length+" items]":s}if(typeof e=="object"){const r=Object.keys(e);if(r.length===0)return"{}";const s=[];for(let l=0;l<Math.min(r.length,4);l++)s.push(r[l]+": "+pe(e[r[l]],30));const i="{ "+s.join(", ")+(r.length>4?", ...+"+(r.length-4):"")+" }";return i.length>n?"{ "+r.slice(0,6).join(", ")+(r.length>6?", ...":"")+" }":i}return String(e)}function Un(e){return e<0?"no expiry":e<60?`${e}s`:e<3600?`${Math.floor(e/60)}m`:e<86400?`${Math.floor(e/3600)}h`:`${Math.floor(e/86400)}d`}function Hn(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:`${(e/(1024*1024)).toFixed(1)}MB`}function zn(e,n){if(!n)return e;const r=n.toLowerCase();return e.filter(s=>s.sql.toLowerCase().includes(r)||s.model&&s.model.toLowerCase().includes(r)||s.method.toLowerCase().includes(r))}function Vn(e){const n={};for(const r of e)n[r.sql]=(n[r.sql]||0)+1;return n}function Wn(e,n){const r=e.filter(l=>l.duration>100).length,s=Object.values(n).filter(l=>l>1).length,i=e.length>0?e.reduce((l,o)=>l+o.duration,0)/e.length:0;return{slowCount:r,dupCount:s,avgDuration:i,totalCount:e.length}}const qn={queries:"/queries",events:"/events",emails:"/emails",routes:"/routes",logs:"/logs",timeline:"/traces",cache:"/cache",jobs:"/jobs",config:"/config",internals:"/diagnostics"};function Jn(e){return qn[e]||`/${e}`}class Kn{client;endpoint;refreshInterval;callbacks;timer=null;currentTab=null;fetchOnceCache={};constructor(n){this.client=new Se({baseUrl:n.baseUrl,authToken:n.authToken}),this.endpoint=n.endpoint??"/admin/api/debug",this.refreshInterval=n.refreshInterval??Bn,this.callbacks={onData:n.onData,onLoading:n.onLoading,onError:n.onError,onUnauthorized:n.onUnauthorized}}start(n){this.stop(),this.currentTab=n,this.callbacks.onLoading(!0),this.callbacks.onError(null),this.fetchData(),this.timer=setInterval(()=>this.fetchData(),this.refreshInterval)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}switchTab(n){this.start(n)}refresh(){this.currentTab&&this.fetchData()}async fetchCustomPane(n,r=!1){if(r&&this.fetchOnceCache[n]!==void 0){this.callbacks.onData(this.fetchOnceCache[n]),this.callbacks.onLoading(!1);return}this.callbacks.onLoading(!0);try{const s=await this.client.fetch(n);this.callbacks.onData(s),this.callbacks.onError(null),r&&(this.fetchOnceCache[n]=s)}catch(s){if(s instanceof ue){this.callbacks.onUnauthorized(s);return}this.callbacks.onError(s instanceof Error?s:new Error(String(s)))}finally{this.callbacks.onLoading(!1)}}cacheForTab(n,r){this.fetchOnceCache[n]=r}clearCache(){this.fetchOnceCache={}}async fetchData(){const n=this.currentTab;if(n){if(this.fetchOnceCache[n]!==void 0){this.callbacks.onData(this.fetchOnceCache[n]),this.callbacks.onLoading(!1);return}try{const r=`${this.endpoint}${Jn(n)}`,s=await this.client.fetch(r);this.callbacks.onData(s),this.callbacks.onError(null),this.callbacks.onLoading(!1)}catch(r){if(r instanceof ue){this.callbacks.onError(r),this.callbacks.onLoading(!1),this.stop(),this.callbacks.onUnauthorized(r);return}this.callbacks.onError(r instanceof Error?r:new Error(String(r))),this.callbacks.onLoading(!1)}}}}function F(e,n={}){const{baseUrl:r="",debugEndpoint:s="/admin/api/debug",authToken:i}=n,[l,o]=x(null),[h,c]=x(!0),[a,p]=x(null),d=B(null);d.current||(d.current=new Kn({baseUrl:r,endpoint:s,authToken:i,onData:m=>o(m),onLoading:m=>c(m),onError:m=>p(m),onUnauthorized:m=>p(m)})),z(()=>{const m=d.current;return m.start(e),()=>m.stop()},[e]);const u=S(()=>{d.current?.refresh()},[]),f=S(()=>{o(null)},[]),_=S((m,y)=>{d.current?.cacheForTab(m,y)},[]);return{data:l,isLoading:h,error:a,refresh:u,clearData:f,cacheForTab:_}}const Qn="ss-col-resize",wt="ss-resizing";function Nt(e){const n=Array.from(e.querySelectorAll("thead th"));if(n.length===0)return()=>{};const r=[];let s=!1;function i(){if(!s){s=!0;for(const l of n)l.style.width=l.offsetWidth+"px";e.style.tableLayout="fixed"}}for(const l of n){let o=function(c){c.preventDefault(),c.stopPropagation(),i();const a=c.clientX,p=l.offsetWidth;h.classList.add(wt),h.setPointerCapture(c.pointerId);function d(f){const _=f.clientX-a,m=Math.max(30,p+_);l.style.width=m+"px"}function u(){h.classList.remove(wt),h.removeEventListener("pointermove",d),h.removeEventListener("pointerup",u)}h.addEventListener("pointermove",d),h.addEventListener("pointerup",u)};if(!l.textContent?.trim())continue;const h=document.createElement("div");h.className=Qn,l.appendChild(h),h.addEventListener("pointerdown",o),r.push(()=>{h.removeEventListener("pointerdown",o),h.remove()})}return()=>{for(const l of r)l()}}function W(e=[]){const n=B(null),r=B(null);return z(()=>(n.current&&(r.current?.(),r.current=Nt(n.current)),()=>{r.current?.(),r.current=null}),e),S(i=>{r.current?.(),r.current=null,n.current=i,i&&(r.current=Nt(i))},[])}function xt({options:e}){const{data:n,isLoading:r,error:s}=F("queries",e),[i,l]=x(""),[o,h]=x(null),c=T(()=>n?.queries||[],[n]),a=T(()=>zn(c,i),[c,i]),p=T(()=>Vn(c),[c]),d=T(()=>Wn(c,p),[c,p]),u=S(_=>{h(m=>m===_?null:_)},[]),f=W([a]);return r&&!n?t("div",{className:"ss-dbg-empty",children:"Loading queries..."}):s?t("div",{className:"ss-dbg-empty",children:["Error: ",s.message]}):t("div",{children:[t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter queries...",value:i,onChange:_=>l(_.target.value)}),t("span",{className:"ss-dbg-summary",children:[a.length," queries",d.slowCount>0&&` | ${d.slowCount} slow`,d.dupCount>0&&` | ${d.dupCount} dup`,a.length>0&&` | avg ${j(d.avgDuration)}`]})]}),a.length===0?t("div",{className:"ss-dbg-empty",children:"No queries captured"}):t("table",{ref:f,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"50px"}}),t("col",{}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"70px"}}),t("col",{style:{width:"100px"}}),t("col",{style:{width:"80px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"#"}),t("th",{children:"SQL"}),t("th",{children:"Duration"}),t("th",{children:"Method"}),t("th",{children:"Model"}),t("th",{children:"Time"})]})}),t("tbody",{children:a.map(_=>t("tr",{children:[t("td",{className:"ss-dbg-c-dim",style:{whiteSpace:"nowrap"},children:_.id}),t("td",{children:[t("span",{className:`ss-dbg-sql ${o===_.id?"ss-dbg-expanded":""}`,onClick:()=>u(_.id),role:"button",tabIndex:0,onKeyDown:m=>m.key==="Enter"&&u(_.id),children:_.sql}),p[_.sql]>1&&t("span",{className:"ss-dbg-dup",children:[" x",p[_.sql]]}),_.inTransaction&&t("span",{className:"ss-dbg-dup",children:" TXN"})]}),t("td",{className:`ss-dbg-duration ${Q(_.duration)==="very-slow"?"ss-dbg-very-slow":Q(_.duration)==="slow"?"ss-dbg-slow":""}`,children:j(_.duration)}),t("td",{children:t("span",{className:`ss-dbg-method ss-dbg-method-${_.method.toLowerCase()}`,children:_.method})}),t("td",{className:"ss-dbg-c-muted",children:_.model||"-"}),t("td",{className:"ss-dbg-event-time",title:V(_.timestamp),children:R(_.timestamp)})]},_.id))})]})]})}const Gn=Object.freeze(Object.defineProperty({__proto__:null,QueriesTab:xt,default:xt},Symbol.toStringTag,{value:"Module"}));function Ae({data:e,maxPreviewLength:n=100,className:r="",classPrefix:s="ss-dash"}){const[i,l]=x(!1),o=T(()=>{if(typeof e=="string")try{return JSON.parse(e)}catch{return e}return e},[e]),h=T(()=>typeof o=="object"&&o!==null?pe(o,n):String(o??"-"),[o,n]),c=T(()=>typeof o=="object"&&o!==null?JSON.stringify(o,null,2):String(o),[o]),a=S(()=>{l(d=>!d)},[]),p=S(async()=>{try{await navigator.clipboard.writeText(c)}catch{}},[c]);return!e&&e!==0&&e!==!1?t("span",{className:`ss-dim ${s}-c-dim`,children:"-"}):t("div",{className:`${s}-data-cell ${r}`,children:[t("span",{className:`${s}-data-preview`,onClick:a,role:"button",tabIndex:0,onKeyDown:d=>d.key==="Enter"&&a(),children:h}),i&&t("div",{className:`${s}-data-full`,onClick:a,children:[t("button",{className:`${s}-copy-btn`,onClick:d=>{d.stopPropagation(),p()},title:"Copy to clipboard",type:"button",children:"Copy"}),t("pre",{children:c})]})]})}function $t({options:e}){const{data:n,isLoading:r,error:s}=F("events",e),[i,l]=x(""),o=T(()=>{const c=n?.events||[];if(!i)return c;const a=i.toLowerCase();return c.filter(p=>(p.event||"").toLowerCase().includes(a)||(p.data||"").toLowerCase().includes(a))},[n,i]),h=W([o]);return r&&!n?t("div",{className:"ss-dbg-empty",children:"Loading events..."}):s?t("div",{className:"ss-dbg-empty",children:["Error: ",s.message]}):t("div",{children:[t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter events...",value:i,onChange:c=>l(c.target.value)}),t("span",{className:"ss-dbg-summary",children:[o.length," events"]})]}),o.length===0?t("div",{className:"ss-dbg-empty",children:"No events captured"}):t("table",{ref:h,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"50px"}}),t("col",{style:{width:"20%"}}),t("col",{}),t("col",{style:{width:"80px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"#"}),t("th",{children:"Event"}),t("th",{children:"Data"}),t("th",{children:"Time"})]})}),t("tbody",{children:o.map(c=>{const a=c.timestamp||c.created_at||c.createdAt;return t("tr",{children:[t("td",{className:"ss-dbg-c-dim",style:{whiteSpace:"nowrap"},children:c.id}),t("td",{className:"ss-dbg-event-name",children:c.event}),t("td",{className:"ss-dbg-event-data",children:t(Ae,{data:c.data,maxPreviewLength:80,classPrefix:"ss-dbg"})}),t("td",{className:"ss-dbg-event-time",title:V(a),children:R(a)})]},c.id)})})]})]})}const Xn=Object.freeze(Object.defineProperty({__proto__:null,EventsTab:$t,default:$t},Symbol.toStringTag,{value:"Module"}));function kt({options:e}){const{data:n,isLoading:r,error:s}=F("emails",e),[i,l]=x(""),[o,h]=x(null),c=T(()=>{const f=n?.emails||[];if(!i)return f;const _=i.toLowerCase();return f.filter(m=>(m.subject||"").toLowerCase().includes(_)||(m.to||"").toLowerCase().includes(_)||(m.from||"").toLowerCase().includes(_)||(m.mailer||"").toLowerCase().includes(_))},[n,i]),a=T(()=>c.find(f=>f.id===o),[c,o]),p=S(()=>h(null),[]),d={sent:"ss-dbg-email-status-sent",sending:"ss-dbg-email-status-sending",queued:"ss-dbg-email-status-queued",failed:"ss-dbg-email-status-failed"},u=W([c]);return r&&!n?t("div",{className:"ss-dbg-empty",children:"Loading emails..."}):s?t("div",{className:"ss-dbg-empty",children:["Error: ",s.message]}):a?t("div",{className:"ss-dbg-email-preview",children:[t("div",{className:"ss-dbg-email-preview-header",children:[t("div",{className:"ss-dbg-email-preview-meta",children:[t("div",{children:[t("strong",{children:"Subject:"})," ",a.subject]}),t("div",{children:[t("strong",{children:"From:"})," ",a.from]}),t("div",{children:[t("strong",{children:"To:"})," ",a.to]}),a.cc&&t("div",{children:[t("strong",{children:"CC:"})," ",a.cc]})]}),t("button",{className:"ss-dbg-btn-clear",onClick:p,type:"button",children:"×"})]}),a.html?t("iframe",{className:"ss-dbg-email-iframe",srcDoc:a.html,title:"Email preview",sandbox:""}):t("div",{style:{padding:"12px",whiteSpace:"pre-wrap"},children:a.text||"No content"})]}):t("div",{children:[t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter emails...",value:i,onChange:f=>l(f.target.value)}),t("span",{className:"ss-dbg-summary",children:[c.length," emails"]})]}),c.length===0?t("div",{className:"ss-dbg-empty",children:"No emails captured"}):t("table",{ref:u,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"50px"}}),t("col",{style:{width:"140px"}}),t("col",{style:{width:"140px"}}),t("col",{}),t("col",{style:{width:"70px"}}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"40px"}}),t("col",{style:{width:"80px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"#"}),t("th",{children:"From"}),t("th",{children:"To"}),t("th",{children:"Subject"}),t("th",{children:"Status"}),t("th",{children:"Mailer"}),t("th",{title:"Attachments",children:"📎"}),t("th",{children:"Time"})]})}),t("tbody",{children:c.map(f=>t("tr",{className:"ss-dbg-email-row",onClick:()=>h(f.id),children:[t("td",{className:"ss-dbg-c-dim",style:{whiteSpace:"nowrap"},children:f.id}),t("td",{className:"ss-dbg-c-secondary",title:f.from,children:f.from}),t("td",{className:"ss-dbg-c-secondary",title:f.to,children:f.to}),t("td",{className:"ss-dbg-c-sql",children:f.subject}),t("td",{children:t("span",{className:`ss-dbg-email-status ${d[f.status]||""}`,children:f.status})}),t("td",{className:"ss-dbg-c-muted",children:f.mailer}),t("td",{className:"ss-dbg-c-dim",style:{textAlign:"center"},children:f.attachmentCount>0?f.attachmentCount:"-"}),t("td",{className:"ss-dbg-event-time",title:V(f.timestamp||f.created_at||f.createdAt),children:R(f.timestamp||f.created_at||f.createdAt)})]},f.id))})]})]})}const Yn=Object.freeze(Object.defineProperty({__proto__:null,EmailsTab:kt,default:kt},Symbol.toStringTag,{value:"Module"}));function Ct({options:e,currentPath:n}){const{data:r,isLoading:s,error:i}=F("routes",e),[l,o]=x(""),h=T(()=>{const a=r?.routes||[];if(!l)return a;const p=l.toLowerCase();return a.filter(d=>d.pattern.toLowerCase().includes(p)||d.handler.toLowerCase().includes(p)||d.method.toLowerCase().includes(p)||d.name&&d.name.toLowerCase().includes(p))},[r,l]),c=W([h]);return s&&!r?t("div",{className:"ss-dbg-empty",children:"Loading routes..."}):i?t("div",{className:"ss-dbg-empty",children:["Error: ",i.message]}):t("div",{children:[t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter routes...",value:l,onChange:a=>o(a.target.value)}),t("span",{className:"ss-dbg-summary",children:[h.length," routes"]})]}),h.length===0?t("div",{className:"ss-dbg-empty",children:"No routes found"}):t("table",{ref:c,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"70px"}}),t("col",{style:{width:"25%"}}),t("col",{style:{width:"15%"}}),t("col",{}),t("col",{style:{width:"20%"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"Method"}),t("th",{children:"Pattern"}),t("th",{children:"Name"}),t("th",{children:"Handler"}),t("th",{children:"Middleware"})]})}),t("tbody",{children:h.map((a,p)=>{const d=n&&(a.pattern===n||new RegExp("^"+a.pattern.replace(/:[^/]+/g,"[^/]+")+"$").test(n));return t("tr",{className:d?"ss-dbg-current-route":"",children:[t("td",{children:t("span",{className:`ss-dbg-method ss-dbg-method-${a.method.toLowerCase()}`,children:a.method})}),t("td",{className:"ss-dbg-c-text",children:a.pattern}),t("td",{className:"ss-dbg-c-muted",children:a.name||"-"}),t("td",{className:"ss-dbg-c-sql",children:a.handler}),t("td",{className:"ss-dbg-c-dim",style:{fontSize:"10px"},children:a.middleware.length>0?a.middleware.join(", "):"-"})]},`${a.method}-${a.pattern}-${p}`)})})]})]})}const Zn=Object.freeze(Object.defineProperty({__proto__:null,RoutesTab:Ct,default:Ct},Symbol.toStringTag,{value:"Module"})),er=["all","error","warn","info","debug"];function St(e){return(e.levelName||e.level_name||(typeof e.level=="string"?e.level:"")||"info").toLowerCase()}function Tt(e){return e.msg||e.message||JSON.stringify(e)}function tr(e){return e.createdAt||e.created_at||e.time||e.timestamp||0}function Lt(e){const n=e.data||{};return e.requestId||e.request_id||e["x-request-id"]||n.requestId||n.request_id||n["x-request-id"]||""}function nr(e,n="ss-dbg-log-level"){switch(e){case"error":case"fatal":return`${n}-error`;case"warn":return`${n}-warn`;case"info":return`${n}-info`;case"debug":return`${n}-debug`;case"trace":return`${n}-trace`;default:return`${n}-info`}}function rr(e,n){return n==="all"?e:e.filter(r=>{const s=St(r);return n==="error"?s==="error"||s==="fatal":s===n})}function Et({options:e}){const{data:n,isLoading:r,error:s}=F("logs",e),[i,l]=x("all"),[o,h]=x(""),[c,a]=x(""),p=T(()=>{let u=Array.isArray(n)?n:n?.logs||n?.entries||[];if(u=rr(u,i),c){const f=c.toLowerCase();u=u.filter(_=>Lt(_).toLowerCase().includes(f))}if(o){const f=o.toLowerCase();u=u.filter(_=>Tt(_).toLowerCase().includes(f))}return u},[n,i,o,c]),d=S(u=>{a(f=>f===u?"":u)},[]);return r&&!n?t("div",{className:"ss-dbg-empty",children:"Loading logs..."}):s?t("div",{className:"ss-dbg-empty",children:["Error: ",s.message]}):t("div",{children:[t("div",{className:"ss-dbg-log-filters",children:[er.map(u=>t("button",{type:"button",className:`ss-dbg-log-filter ${i===u?"ss-dbg-active":""}`,onClick:()=>l(u),children:u},u)),c&&t("button",{type:"button",className:"ss-dbg-log-filter ss-dbg-active",onClick:()=>a(""),children:["req: ",c.slice(0,8)," x"]}),t("span",{className:"ss-dbg-summary",style:{marginLeft:"auto"},children:[p.length," entries"]})]}),t("div",{className:"ss-dbg-search-bar",children:t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter log messages...",value:o,onChange:u=>h(u.target.value)})}),t("div",{style:{overflow:"auto",flex:1},children:p.length===0?t("div",{className:"ss-dbg-empty",children:"No log entries"}):p.slice(-200).reverse().map((u,f)=>{const _=St(u),m=Tt(u),y=tr(u),b=Lt(u);return t("div",{className:"ss-dbg-log-entry",children:[t("span",{className:`ss-dbg-log-level ${nr(_)}`,children:_.toUpperCase()}),t("span",{className:"ss-dbg-log-time",title:y?V(y):"",children:y?R(y):"-"}),b?t("span",{className:"ss-dbg-log-reqid",onClick:()=>d(b),role:"button",tabIndex:0,title:b,onKeyDown:g=>g.key==="Enter"&&d(b),children:b.slice(0,8)}):t("span",{className:"ss-dbg-log-reqid-empty",children:"-"}),t("span",{className:"ss-dbg-log-msg",children:m})]},f)})})]})}const sr=Object.freeze(Object.defineProperty({__proto__:null,LogsTab:Et,default:Et},Symbol.toStringTag,{value:"Module"}));function At(e="",n){const r=B(null);return S(()=>(r.current||(r.current=new Se({baseUrl:e,authToken:n})),r.current),[e,n])}const Pt={request:"#1e3a5f",middleware:"rgba(30, 58, 95, 0.7)",db:"#6d28d9",view:"#0e7490",mail:"#059669",event:"#b45309",custom:"#525252"},lr=[{label:"Request",color:"#1e3a5f"},{label:"Middleware",color:"rgba(30, 58, 95, 0.7)"},{label:"Database",color:"#6d28d9"},{label:"View",color:"#0e7490"},{label:"Mail",color:"#059669"},{label:"Event",color:"#b45309"}];function Mt({options:e}){const{baseUrl:n="",debugEndpoint:r="/admin/api/debug",authToken:s}=e||{},{data:i,isLoading:l,error:o}=F("timeline",e),[h,c]=x(""),[a,p]=x(null),[d,u]=x(null),[f,_]=x(!1),[m,y]=x(null),b=T(()=>{const w=i?.traces||[];if(!h)return w;const P=h.toLowerCase();return w.filter($=>$.url.toLowerCase().includes(P)||$.method.toLowerCase().includes(P)||String($.statusCode).includes(P))},[i,h]),g=At(n,s);z(()=>{if(a===null){u(null),y(null);return}let w=!1;return _(!0),y(null),g().get(`${r}/traces/${a}`).then($=>{w||(u($),_(!1))}).catch($=>{w||(y($ instanceof Error?$.message:"Failed to load trace"),_(!1))}),()=>{w=!0}},[a,r,g]);const k=S(w=>{p(P=>P===w?null:w)},[]),C=S(w=>w>=500?"ss-dbg-status-5xx":w>=400?"ss-dbg-status-4xx":w>=300?"ss-dbg-status-3xx":"ss-dbg-status-2xx",[]),v=W([b]);if(l&&!i)return t("div",{className:"ss-dbg-empty",children:"Loading traces..."});if(o)return t("div",{className:"ss-dbg-empty",children:["Error: ",o.message]});if(a!==null){if(f)return t("div",{className:"ss-dbg-empty",children:"Loading trace detail..."});if(m)return t("div",{children:[t("div",{className:"ss-dbg-tl-detail-header",children:t("button",{type:"button",className:"ss-dbg-btn-clear",onClick:()=>p(null),children:"← Back"})}),t("div",{className:"ss-dbg-empty",children:["Error: ",m]})]});if(!d)return t("div",{className:"ss-dbg-empty",children:"Loading trace detail..."});const w=d.spans||[],P=d.warnings||[];return t("div",{children:[t("div",{className:"ss-dbg-tl-detail-header",children:[t("button",{type:"button",className:"ss-dbg-btn-clear",onClick:()=>p(null),children:"← Back"}),t("span",{className:`ss-dbg-method ss-dbg-method-${d.method.toLowerCase()}`,children:d.method}),t("span",{className:"ss-dbg-tl-detail-url",children:d.url}),t("span",{className:`ss-dbg-status ${C(d.statusCode)}`,children:d.statusCode}),t("span",{className:"ss-dbg-tl-meta",children:[j(d.totalDuration)," · ",d.spanCount," spans"]})]}),t("div",{className:"ss-dbg-tl-legend",children:lr.map($=>t("div",{className:"ss-dbg-tl-legend-item",children:[t("div",{className:"ss-dbg-tl-legend-dot",style:{background:$.color}}),t("span",{children:$.label})]},$.label))}),t("div",{style:{padding:"8px 12px",overflow:"auto"},children:w.length===0?t("div",{className:"ss-dbg-empty",children:"No spans captured for this request"}):w.map($=>{const M=d.totalDuration||1,q=$.startOffset/M*100,Dr=Math.max($.duration/M*100,.5);return t("div",{className:"ss-dbg-tl-row",children:[t("div",{className:"ss-dbg-tl-label",title:$.label,children:$.label}),t("div",{className:"ss-dbg-tl-track",children:t("div",{className:`ss-dbg-tl-bar ss-dbg-tl-bar-${$.category}`,style:{left:`${q}%`,width:`${Dr}%`,background:Pt[$.category]||Pt.custom},title:`${$.label}: ${j($.duration)}`})}),t("span",{className:"ss-dbg-tl-dur",children:j($.duration)})]},$.id)})}),P.length>0&&t("div",{className:"ss-dbg-tl-warnings",children:[t("div",{className:"ss-dbg-tl-warnings-title",children:"Warnings"}),P.map(($,M)=>t("div",{className:"ss-dbg-tl-warning",children:$},M))]})]})}return t("div",{children:[t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter traces...",value:h,onChange:w=>c(w.target.value)}),t("span",{className:"ss-dbg-summary",children:[b.length," traces"]})]}),b.length===0?t("div",{className:"ss-dbg-empty",children:"No traces captured. Enable tracing in config."}):t("table",{ref:v,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"50px"}}),t("col",{style:{width:"70px"}}),t("col",{}),t("col",{style:{width:"60px"}}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"50px"}}),t("col",{style:{width:"80px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"#"}),t("th",{children:"Method"}),t("th",{children:"URL"}),t("th",{children:"Status"}),t("th",{children:"Duration"}),t("th",{children:"Spans"}),t("th",{children:"Time"})]})}),t("tbody",{children:b.map(w=>t("tr",{className:"ss-dbg-email-row",onClick:()=>k(w.id),children:[t("td",{className:"ss-dbg-c-dim",style:{whiteSpace:"nowrap"},children:w.id}),t("td",{children:t("span",{className:`ss-dbg-method ss-dbg-method-${w.method.toLowerCase()}`,children:w.method})}),t("td",{title:w.url,children:w.url}),t("td",{children:t("span",{className:`ss-dbg-status ${C(w.statusCode)}`,children:w.statusCode})}),t("td",{className:`ss-dbg-duration ${Q(w.totalDuration)==="very-slow"?"ss-dbg-very-slow":Q(w.totalDuration)==="slow"?"ss-dbg-slow":""}`,children:j(w.totalDuration)}),t("td",{className:"ss-dbg-c-muted",style:{textAlign:"center"},children:w.spanCount}),t("td",{className:"ss-dbg-event-time",title:V(w.timestamp),children:R(w.timestamp)})]},w.id))})]})]})}const ir=Object.freeze(Object.defineProperty({__proto__:null,TimelineTab:Mt,default:Mt},Symbol.toStringTag,{value:"Module"}));function Pe(e,n){const r=T(()=>e?e.replace(/\/+$/,"")+"/api":null,[e]),s=T(()=>r?{...n,debugEndpoint:r}:n,[r,n]);return{dashApiBase:r,resolvedOptions:s}}function Ot({options:e,dashboardPath:n}){const{dashApiBase:r,resolvedOptions:s}=Pe(n,e),{data:i,isLoading:l,error:o}=F("cache",s),[h,c]=x(""),[a,p]=x(null),[d,u]=x(null),f=T(()=>{const g=i?.keys||[];if(!h)return g;const k=h.toLowerCase();return g.filter(C=>C.key.toLowerCase().includes(k))},[i,h]),_=S(async g=>{if(a===g){p(null),u(null);return}p(g);try{const{baseUrl:k="",authToken:C}=e||{},v=r||e?.debugEndpoint||"/admin/api/debug",w=`${k}${v}/cache/${encodeURIComponent(g)}`,P={Accept:"application/json"};C&&(P.Authorization=`Bearer ${C}`);const M=await(await fetch(w,{headers:P,credentials:C?"omit":"same-origin"})).json();u(M)}catch{u({error:"Failed to fetch key value"})}},[a,e,r]),m=W([f]);if(l&&!i)return t("div",{className:"ss-dbg-empty",children:"Loading cache data..."});if(o)return t("div",{className:"ss-dbg-empty",children:["Error: ",o.message]});if(!i)return t("div",{className:"ss-dbg-empty",children:"Cache inspector not available"});const b=i.stats||i;return t("div",{children:[t("div",{className:"ss-dbg-cache-stats",children:[t("div",{className:"ss-dbg-cache-stat",children:[t("span",{className:"ss-dbg-cache-stat-label",children:"Hit Rate:"}),t("span",{className:"ss-dbg-cache-stat-value",children:[b.hitRate!==null&&b.hitRate!==void 0?b.hitRate.toFixed(1):"0","%"]})]}),t("div",{className:"ss-dbg-cache-stat",children:[t("span",{className:"ss-dbg-cache-stat-label",children:"Hits:"}),t("span",{className:"ss-dbg-cache-stat-value",children:b.totalHits??0})]}),t("div",{className:"ss-dbg-cache-stat",children:[t("span",{className:"ss-dbg-cache-stat-label",children:"Misses:"}),t("span",{className:"ss-dbg-cache-stat-value",children:b.totalMisses??0})]}),t("div",{className:"ss-dbg-cache-stat",children:[t("span",{className:"ss-dbg-cache-stat-label",children:"Keys:"}),t("span",{className:"ss-dbg-cache-stat-value",children:b.keyCount??"-"})]})]}),t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:"Filter keys...",value:h,onChange:g=>c(g.target.value)}),t("span",{className:"ss-dbg-summary",children:[f.length," keys"]})]}),a&&!!d&&t("div",{className:"ss-dbg-cache-detail",children:[t("strong",{children:a}),t("button",{type:"button",className:"ss-dbg-btn-clear",onClick:()=>p(null),children:["←"," Back"]}),t(Ae,{data:d,classPrefix:"ss-dbg"})]}),f.length===0?t("div",{className:"ss-dbg-empty",children:"No cache keys found"}):t("table",{ref:m,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"80px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"Key"}),t("th",{children:"Type"}),t("th",{children:"TTL"}),t("th",{children:"Size"})]})}),t("tbody",{children:f.map(g=>t("tr",{className:"ss-dbg-email-row",onClick:()=>_(g.key),children:[t("td",{className:"ss-dbg-c-sql",children:g.key}),t("td",{className:"ss-dbg-c-muted",children:g.type}),t("td",{className:"ss-dbg-c-muted",children:g.ttl>0?Un(g.ttl):"-"}),t("td",{className:"ss-dbg-c-dim",children:g.size>0?Hn(g.size):"-"})]},g.key))})]})]})}const ar=Object.freeze(Object.defineProperty({__proto__:null,CacheTab:Ot,default:Ot},Symbol.toStringTag,{value:"Module"})),or=["all","active","waiting","delayed","completed","failed"];function cr(e,n="ss-dbg-job-status"){switch(e){case"completed":case"failed":case"active":case"waiting":case"delayed":return`${n}-${e}`;default:return"ss-dbg-badge-muted"}}function dr(e){if(!e)return[];if(Array.isArray(e))return e;const n=e;return n.jobs||n.data||[]}function hr(e){if(!e||Array.isArray(e))return null;const n=e;return n.stats||n.overview||null}function It({options:e,dashboardPath:n}){const{dashApiBase:r,resolvedOptions:s}=Pe(n,e),{data:i,isLoading:l,error:o}=F("jobs",s),[h,c]=x("all"),[a,p]=x(null),d=T(()=>{const m=dr(i);return h==="all"?m:m.filter(y=>y.status===h)},[i,h]),u=S(async m=>{p(m);try{const{baseUrl:y="",authToken:b}=e||{},g=r||e?.debugEndpoint||"/admin/api/debug",k=`${y}${g}/jobs/${m}/retry`,C={Accept:"application/json"};b&&(C.Authorization=`Bearer ${b}`),await fetch(k,{method:"POST",headers:C,credentials:b?"omit":"same-origin"})}catch{}p(null)},[e,r]),f=W([d]);if(!r)return t("div",{className:"ss-dbg-empty",children:"Queue inspector not available (no dashboard configured)"});if(l&&!i)return t("div",{className:"ss-dbg-empty",children:"Loading jobs..."});if(o)return t("div",{className:"ss-dbg-empty",children:["Error: ",o.message]});if(!i)return t("div",{className:"ss-dbg-empty",children:"Queue inspector not available"});const _=hr(i);return t("div",{children:[t("div",{className:"ss-dbg-job-stats-area",children:[t("div",{className:"ss-dbg-job-stats",children:[t("div",{className:"ss-dbg-job-stat",children:[t("span",{className:"ss-dbg-job-stat-label",children:"Active:"}),t("span",{className:"ss-dbg-job-stat-value",children:_?.active??0})]}),t("div",{className:"ss-dbg-job-stat",children:[t("span",{className:"ss-dbg-job-stat-label",children:"Waiting:"}),t("span",{className:"ss-dbg-job-stat-value",children:_?.waiting??0})]}),t("div",{className:"ss-dbg-job-stat",children:[t("span",{className:"ss-dbg-job-stat-label",children:"Delayed:"}),t("span",{className:"ss-dbg-job-stat-value",children:_?.delayed??0})]}),t("div",{className:"ss-dbg-job-stat",children:[t("span",{className:"ss-dbg-job-stat-label",children:"Completed:"}),t("span",{className:"ss-dbg-job-stat-value",children:_?.completed??0})]}),t("div",{className:"ss-dbg-job-stat",children:[t("span",{className:"ss-dbg-job-stat-label",children:"Failed:"}),t("span",{className:"ss-dbg-job-stat-value ss-dbg-c-red",children:_?.failed??0})]})]}),t("div",{className:"ss-dbg-log-filters",children:or.map(m=>t("button",{type:"button",className:`ss-dbg-job-filter ${h===m?"ss-dbg-active":""}`,onClick:()=>c(m),children:m},m))})]}),d.length===0?t("div",{className:"ss-dbg-empty",children:"No jobs found"}):t("table",{ref:f,className:"ss-dbg-table",children:[t("colgroup",{children:[t("col",{style:{width:"50px"}}),t("col",{style:{width:"15%"}}),t("col",{style:{width:"80px"}}),t("col",{}),t("col",{style:{width:"50px"}}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"80px"}}),t("col",{style:{width:"60px"}})]}),t("thead",{children:t("tr",{children:[t("th",{children:"ID"}),t("th",{children:"Name"}),t("th",{children:"Status"}),t("th",{children:"Payload"}),t("th",{children:"Tries"}),t("th",{children:"Duration"}),t("th",{children:"Time"}),t("th",{})]})}),t("tbody",{children:d.map(m=>{const y=m;return t("tr",{children:[t("td",{className:"ss-dbg-c-dim",children:m.id}),t("td",{className:"ss-dbg-c-sql",title:m.name,children:m.name}),t("td",{children:t("span",{className:`ss-dbg-badge ${cr(m.status)}`,children:m.status})}),t("td",{children:t(Ae,{data:m.payload||m.data,maxPreviewLength:60,classPrefix:"ss-dbg"})}),t("td",{className:"ss-dbg-c-muted",style:{textAlign:"center"},children:m.attempts||y.attemptsMade||0}),t("td",{className:"ss-dbg-duration",children:m.duration!==null?j(m.duration):"-"}),t("td",{className:"ss-dbg-event-time",title:V(m.timestamp||m.createdAt||y.processedAt||y.created_at),children:R(m.timestamp||m.createdAt||y.processedAt||y.created_at)}),t("td",{children:m.status==="failed"&&t("button",{type:"button",className:"ss-dbg-retry-btn",onClick:()=>u(m.id),disabled:a===m.id,children:a===m.id?"...":"Retry"})})]},m.id)})})]})]})}const ur=Object.freeze(Object.defineProperty({__proto__:null,JobsTab:It,default:It},Symbol.toStringTag,{value:"Module"}));function O(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)&&e.__redacted===!0}function Me(e,n=""){if(typeof e!="object"||e===null||e===void 0)return[{path:n,value:e}];if(Array.isArray(e)||O(e))return[{path:n,value:e}];const r=[];for(const s of Object.keys(e)){const i=n?`${n}.${s}`:s,l=e[s];typeof l=="object"&&l!==null&&!Array.isArray(l)&&!O(l)?r.push(...Me(l,i)):r.push({path:i,value:l})}return r}function Oe(e){return e==null?{text:"null",color:"var(--ss-dim)"}:typeof e=="boolean"?{text:String(e),color:e?"var(--ss-green-fg)":"var(--ss-red-fg)"}:typeof e=="number"?{text:String(e),color:"var(--ss-amber-fg)"}:Array.isArray(e)?{text:`[${e.map(r=>r==null?"null":typeof r=="object"?JSON.stringify(r):String(r)).join(", ")}]`,color:"var(--ss-purple-fg)"}:typeof e=="object"?{text:JSON.stringify(e),color:"var(--ss-dim)"}:{text:String(e)}}function Dt(e){if(e==null||typeof e!="object"||Array.isArray(e)||O(e))return 1;let n=0;for(const r of Object.keys(e))n+=Dt(e[r]);return n}function fr(e){if(e==null||typeof e!="object"||Array.isArray(e)||O(e))return[];const n=[];for(const r of Object.keys(e)){const s=e[r];s!==null&&typeof s=="object"&&!Array.isArray(s)&&!O(s)&&n.push(r)}return n}function _e(e,n,r){n&&navigator.clipboard.writeText(e).then(()=>{const s=n.textContent;n.textContent="✓",n.classList.add(`${r}-copy-row-ok`),setTimeout(()=>{n.textContent=s,n.classList.remove(`${r}-copy-row-ok`)},1200)}).catch(()=>{})}function me({redacted:e,p:n}){const[r,s]=x(!1);return t("span",{className:`${n}-config-redacted`,style:{display:"inline-flex",alignItems:"center",gap:"4px"},children:[t("span",{children:r?e.value:e.display}),t("button",{type:"button",className:`${n}-btn`,title:r?"Hide":"Reveal",style:{padding:"0 4px",fontSize:"0.85em",lineHeight:1,minWidth:"auto"},onClick:i=>{i.stopPropagation(),s(l=>!l)},children:r?t("svg",{width:"14",height:"14",viewBox:A["eye-off"].viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A["eye-off"].elements.join("")}}):t("svg",{width:"14",height:"14",viewBox:A.eye.viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A.eye.elements.join("")}})})]})}function pr({env:e,search:n,p:r}){const s=B(new Map),i=n.toLowerCase(),l=Object.entries(e).filter(([o,h])=>{if(!i)return!0;const c=O(h)?h.display:h==null?"":String(h);return o.toLowerCase().includes(i)||c.toLowerCase().includes(i)});return t("div",{className:`${r}-config-table-wrap`,children:t("table",{className:`${r}-table ${r}-config-env-table`,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Variable"}),t("th",{children:"Value"}),t("th",{style:{width:36}})]})}),t("tbody",{children:[l.map(([o,h])=>{const c=O(h),a=c?h.display:h==null?"null":String(h),p=`${o}=${a}`;return t("tr",{children:[t("td",{className:`${r}-env-key`,children:t("span",{className:`${r}-config-key`,children:o})}),t("td",{className:`${r}-env-val`,children:c?t(me,{redacted:h,p:r}):t("span",{className:`${r}-config-val`,children:a})}),t("td",{children:!c&&t("button",{type:"button",className:`${r}-copy-row-btn`,title:"Copy",ref:d=>{s.current.set(o,d)},onClick:d=>{d.stopPropagation(),_e(p,s.current.get(o)??null,r)},children:"⎘"})})]},o)}),l.length===0&&t("tr",{children:t("td",{colSpan:3,style:{textAlign:"center",color:"var(--ss-dim)"},children:"No matching variables"})})]})]})})}function _r({source:e,search:n,p:r}){const s=B(new Map),i=n.toLowerCase(),l=Me(e,""),o=l.filter(h=>{const c=O(h.value)?h.value.display:h.value===null||h.value===void 0?"":String(h.value);return h.path.toLowerCase().includes(i)||c.toLowerCase().includes(i)});return t("div",{className:`${r}-config-table-wrap`,children:[t("table",{className:`${r}-table`,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Path"}),t("th",{children:"Value"}),t("th",{style:{width:36}})]})}),t("tbody",{children:[o.map(h=>{const c=O(h.value),a=c?null:Oe(h.value),p=c?h.value.display:a.text,d=`${h.path}: ${p}`;return t("tr",{children:[t("td",{children:t("span",{className:`${r}-config-key`,style:{whiteSpace:"nowrap"},children:h.path})}),t("td",{children:c?t(me,{redacted:h.value,p:r}):t("span",{className:`${r}-config-val`,style:{wordBreak:"break-all",color:a.color},children:a.text})}),t("td",{children:!c&&t("button",{type:"button",className:`${r}-copy-row-btn`,title:"Copy",ref:u=>{s.current.set(h.path,u)},onClick:u=>{u.stopPropagation(),_e(d,s.current.get(h.path)??null,r)},children:"⎘"})})]},h.path)}),o.length===0&&t("tr",{children:t("td",{colSpan:3,style:{textAlign:"center",color:"var(--ss-dim)"},children:"No matching entries"})})]})]}),t("div",{style:{padding:"4px 16px",fontSize:"10px",color:"var(--ss-muted)"},children:[o.length," of ",l.length," entries"]})]})}function mr({obj:e,prefix:n,p:r}){const s=B(new Map),i=Me(e,n);return t("table",{className:`${r}-table ${r}-config-inner-table`,children:[t("thead",{children:t("tr",{children:[t("th",{style:{width:"35%"},children:"Key"}),t("th",{children:"Value"}),t("th",{style:{width:36}})]})}),t("tbody",{children:i.map(l=>{const o=l.path.indexOf(n+".")===0?l.path.slice(n.length+1):l.path,h=O(l.value),c=h?null:Oe(l.value),a=h?l.value.display:c.text,p=`${l.path}: ${a}`;return t("tr",{children:[t("td",{title:o,children:t("span",{className:`${r}-config-key`,children:o})}),t("td",{title:a,children:h?t(me,{redacted:l.value,p:r}):t("span",{className:`${r}-config-val`,style:{color:c.color},children:c.text})}),t("td",{children:!h&&t("button",{type:"button",className:`${r}-copy-row-btn`,title:"Copy",ref:d=>{s.current.set(l.path,d)},onClick:d=>{d.stopPropagation(),_e(p,s.current.get(l.path)??null,r)},children:"⎘"})})]},l.path)})})]})}function gr({value:e,p:n}){if(e==null)return t("span",{className:`${n}-config-val`,style:{color:"var(--ss-dim)"},children:"null"});if(O(e))return t(me,{redacted:e,p:n});if(typeof e=="boolean")return t("span",{className:`${n}-config-val`,style:{color:e?"var(--ss-green-fg)":"var(--ss-red-fg)"},children:String(e)});if(typeof e=="number")return t("span",{className:`${n}-config-val`,style:{color:"var(--ss-amber-fg)"},children:String(e)});if(Array.isArray(e)){const r=e.map(s=>s==null?"null":typeof s=="object"?JSON.stringify(s):String(s));return t("span",{className:`${n}-config-val`,style:{color:"var(--ss-purple-fg)"},children:["[",r.join(", "),"]"]})}return typeof e=="object"?t("span",{className:`${n}-config-val`,style:{color:"var(--ss-dim)"},children:JSON.stringify(e)}):t("span",{className:`${n}-config-val`,children:String(e)})}function br({obj:e,expandedPaths:n,onToggle:r,p:s}){if(e==null||typeof e!="object"||Array.isArray(e)||O(e))return null;const i=Object.keys(e),l=B(new Map);return t("div",{className:`${s}-config-sections`,children:i.map(o=>{const h=e[o],c=h!==null&&typeof h=="object"&&!Array.isArray(h)&&!O(h),a=n.has(o),p=O(h);return t("div",{className:`${s}-config-section`,children:[t("div",{className:`${s}-config-section-header${c?"":` ${s}-config-leaf`}`,onClick:c?()=>r(o):void 0,style:{cursor:c?"pointer":"default"},children:[c?t("span",{className:`${s}-config-toggle`,children:a?"▼":"▶"}):t("span",{className:`${s}-config-toggle`,style:{visibility:"hidden"},children:"•"}),t("span",{className:`${s}-config-key`,children:o}),c?t("span",{className:`${s}-config-count`,children:[Dt(h)," entries"]}):t(I,{children:[t("span",{className:`${s}-config-val`,style:{marginLeft:"8px"},children:t(gr,{value:h,p:s})}),!p&&t("button",{type:"button",className:`${s}-copy-row-btn`,style:{marginLeft:"4px"},title:"Copy",ref:d=>{l.current.set(o,d)},onClick:d=>{d.stopPropagation();const u=Oe(h);_e(`${o}: ${u.text}`,l.current.get(o)??null,s)},children:"⎘"})]})]}),c&&a&&t("div",{className:`${s}-config-section-body`,children:t(mr,{obj:h,prefix:o,p:s})})]},o)})})}function yr({data:e,isLoading:n,classPrefix:r}){const s=r,[i,l]=x(""),[o,h]=x(""),[c,a]=x("app"),[p,d]=x(new Set),[u,f]=x("Copy JSON");z(()=>{const k=setTimeout(()=>h(i),200);return()=>clearTimeout(k)},[i]);const _=e,m=S(k=>{d(C=>{const v=new Set(C);return v.has(k)?v.delete(k):v.add(k),v})},[]),y=S(()=>{if(!_)return;const k=c==="app"?_.app:_.env;if(!k)return;const C=fr(k);d(new Set(C))},[_,c]),b=S(()=>{d(new Set)},[]),g=S(async()=>{if(_)try{const k=c==="app"?_.app:_.env;await navigator.clipboard.writeText(JSON.stringify(k,null,2)),f("Copied!"),setTimeout(()=>f("Copy JSON"),1500)}catch{}},[_,c]);return t("div",{children:[t("div",{className:`${s}-config-toolbar`,style:{display:"flex",alignItems:"center",gap:"8px",padding:"8px 12px"},children:[t("button",{type:"button",className:`${s}-config-tab${c==="app"?` ${s}-active`:""}`,onClick:()=>a("app"),children:"App Config"}),t("button",{type:"button",className:`${s}-config-tab${c==="env"?` ${s}-active`:""}`,onClick:()=>a("env"),children:"Env"}),t("div",{style:{position:"relative",flex:1},children:[t("input",{type:"text",className:`${s}-search`,placeholder:"Search keys and values...",value:i,onChange:k=>l(k.target.value),style:{width:"100%"}}),i&&t("button",{type:"button",onClick:()=>l(""),style:{position:"absolute",right:"6px",top:"50%",transform:"translateY(-50%)",background:"none",border:"none",cursor:"pointer",fontSize:"14px",color:"var(--ss-dim)",padding:"0 2px",lineHeight:1},children:"×"})]}),c==="app"&&!o&&t(I,{children:[t("button",{type:"button",className:`${s}-btn`,onClick:y,children:"Expand All"}),t("button",{type:"button",className:`${s}-btn`,onClick:b,children:"Collapse All"})]}),t("button",{type:"button",className:`${s}-btn`,onClick:g,children:u})]}),n&&!e?t("div",{className:`${s}-empty`,children:"Loading config..."}):_?c==="env"?t(pr,{env:_.env??{},search:o,p:s}):o?t(_r,{source:_.app??{},search:o,p:s}):t("div",{className:`${s}-config-table-wrap`,children:t(br,{obj:_.app,expandedPaths:p,onToggle:m,p:s})}):t("div",{className:`${s}-empty`,children:"Config not available"})]})}function Bt({options:e,dashboardPath:n}){const{resolvedOptions:r}=Pe(n,e),{data:s,isLoading:i,error:l}=F("config",r);return l?t("div",{className:"ss-dbg-empty",children:["Error: ",l.message]}):t(yr,{data:s,isLoading:i,classPrefix:"ss-dbg"})}const vr=Object.freeze(Object.defineProperty({__proto__:null,ConfigTab:Bt,default:Bt},Symbol.toStringTag,{value:"Module"})),wr=["password","secret","token","key","credential","auth"];function jt(e){const n=e.toLowerCase();return wr.some(r=>n.includes(r))}function Rt(e){if(e==null)return"-";if(typeof e=="string"||typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e))return e.join(", ")||"-";try{return JSON.stringify(e)}catch{return String(e)}}const Nr={collectionInterval:"Stats Collection",dashboardBroadcast:"Dashboard Broadcast",debugBroadcast:"Debug Broadcast",persistFlush:"Persist Flush",retentionCleanup:"Retention Cleanup"};function xr(e){return Nr[e]||e}const $r={prometheus:"Prometheus",pinoHook:"Pino Log Hook",edgePlugin:"Edge Plugin",cacheInspector:"Cache Inspector",queueInspector:"Queue Inspector"};function kr(e){return $r[e]||e}const Cr=["healthy","active","connected","available","ready"],Sr=["errored","unavailable"];function Tr(e){return Cr.includes(e)?"ok":Sr.includes(e)?"err":""}function ee({status:e,prefix:n}){const r=Tr(e);let s=`${n}-dot`;return r==="ok"?s+=` ${n}-dot-ok`:r==="err"&&(s+=` ${n}-dot-err`),t("span",{className:s})}const Ft=()=>t("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"12",viewBox:A.eye.viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A.eye.elements.join("")}}),Ut=()=>t("svg",{xmlns:"http://www.w3.org/2000/svg",width:"12",height:"12",viewBox:A["eye-off"].viewBox,fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",dangerouslySetInnerHTML:{__html:A["eye-off"].elements.join("")}});function Lr({value:e}){const[n,r]=x(!1);return t("span",{children:[n?e:"••••••••"," ",t("button",{type:"button",onClick:()=>r(s=>!s),style:{background:"none",border:"1px solid var(--ss-border)",borderRadius:3,padding:"0 4px",fontSize:"10px",color:"var(--ss-dim)",cursor:"pointer",verticalAlign:"middle"},children:n?t(Ut,{}):t(Ft,{})})]})}function Er({current:e,max:n,prefix:r}){const s=n>0?Math.min(100,Math.round(e/n*100)):0,i=s>=100;return t("div",{className:`${r}-bar`,children:[t("div",{className:`${r}-bar-track`,children:t("div",{className:`${r}-bar-fill${i?` ${r}-bar-fill-warn`:""}`,style:{width:`${s}%`}})}),t("span",{className:`${r}-bar-pct${i?` ${r}-bar-pct-warn`:""}`,children:[s,"%"]})]})}function Ar({config:e,prefix:n}){const r=Object.entries(e);return r.length===0?t("span",{className:`${n}-c-dim`,children:"-"}):t("span",{className:`${n}-c-muted`,children:r.map(([s,i],l)=>t("span",{children:[l>0&&", ",t("span",{className:`${n}-c-dim`,children:s}),"=",jt(s)&&typeof i=="string"?t(Lr,{value:i}):t("span",{children:Rt(i)})]},s))})}function te({label:e,value:n,prefix:r}){return t("div",{className:`${r}-info-card`,children:[t("span",{className:`${r}-info-card-label`,children:e}),t("span",{className:`${r}-info-card-value`,children:n})]})}function Pr({data:e,tableClassName:n,classPrefix:r}){const s=r||"ss-dash",[i,l]=x(new Set),o=S(c=>{l(a=>{const p=new Set(a);return p.has(c)?p.delete(c):p.add(c),p})},[]),h=S((c,a)=>{if(a==null)return t("span",{className:`${s}-c-dim`,children:"null"});if(typeof a=="boolean")return t("span",{className:a?`${s}-c-green`:`${s}-c-red`,children:String(a)});if(Array.isArray(a))return t("span",{children:a.join(", ")||"-"});const p=Rt(a);if(jt(c)){const d=i.has(c);return t("span",{children:[d?p:"••••••••"," ",t("button",{type:"button",onClick:()=>o(c),style:{background:"none",border:"1px solid var(--ss-border)",borderRadius:3,padding:"0 4px",fontSize:"10px",color:"var(--ss-dim)",cursor:"pointer",verticalAlign:"middle"},children:d?t(Ut,{}):t(Ft,{})})]})}return t("span",{children:p})},[i,o,s]);return t("div",{children:[t("h3",{className:`${s}-internals-title`,children:"Package Info"}),t("div",{className:`${s}-info-cards`,children:[t(te,{label:"Version",value:e.package.version||"-",prefix:s}),t(te,{label:"Node.js",value:e.package.nodeVersion||"-",prefix:s}),t(te,{label:"AdonisJS",value:e.package.adonisVersion||"-",prefix:s}),t(te,{label:"Uptime",value:Fn(e.package.uptime),prefix:s}),t(te,{label:"Renderer",value:e.devToolbar?.renderer||"preact",prefix:s})]}),e.collectors.length>0&&t(I,{children:[t("h3",{className:`${s}-internals-title`,children:"Collectors"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Collector"}),t("th",{children:"Status"}),t("th",{children:"Last Error"}),t("th",{children:"Config"})]})}),t("tbody",{children:e.collectors.map(c=>t("tr",{children:[t("td",{children:[t("code",{children:c.name}),c.label&&c.label!==c.name&&t("span",{className:`${s}-c-dim`,children:[" ",c.label]})]}),t("td",{children:[t(ee,{status:c.status,prefix:s}),c.status]}),t("td",{className:c.lastError?`${s}-c-red`:`${s}-c-dim`,children:c.lastError?t(I,{children:[c.lastError,c.lastErrorAt&&t("span",{className:`${s}-c-dim`,style:{fontSize:"10px"},children:R(c.lastErrorAt)})]}):"-"}),t("td",{children:t(Ar,{config:c.config,prefix:s})})]},c.name))})]})]}),t("h3",{className:`${s}-internals-title`,children:"Buffers"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Buffer"}),t("th",{children:"Usage"}),t("th",{children:"Fill %"})]})}),t("tbody",{children:Object.entries(e.buffers).map(([c,a])=>t("tr",{children:[t("td",{style:{textTransform:"capitalize"},children:c}),t("td",{children:[a.current.toLocaleString()," / ",a.max.toLocaleString()]}),t("td",{children:t(Er,{current:a.current,max:a.max,prefix:s})})]},c))})]}),t("h3",{className:`${s}-internals-title`,children:"Timers"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Timer"}),t("th",{children:"Status"}),t("th",{children:"Interval"})]})}),t("tbody",{children:Object.entries(e.timers).map(([c,a])=>t("tr",{children:[t("td",{children:xr(c)}),t("td",{children:[t(ee,{status:a.active?"active":"inactive",prefix:s}),t("span",{className:a.active?`${s}-c-green`:`${s}-c-dim`,children:a.active?"active":"inactive"})]}),t("td",{children:a.active?a.intervalMs?j(a.intervalMs):a.debounceMs?`${j(a.debounceMs)} (debounce)`:"-":t("span",{className:`${s}-c-dim`,children:"—"})})]},c))})]}),t("h3",{className:`${s}-internals-title`,children:"Integrations"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{children:"Integration"}),t("th",{children:"Status"}),t("th",{children:"Details"})]})}),t("tbody",{children:[t("tr",{children:[t("td",{children:"Transmit (SSE)"}),t("td",{children:[t(ee,{status:e.transmit.available?"connected":"inactive",prefix:s}),e.transmit.available?"connected":"unavailable"]}),t("td",{style:{fontSize:"11px"},children:e.transmit.channels.length>0?`Channels: ${e.transmit.channels.join(", ")}`:"-"})]}),Object.entries(e.integrations).map(([c,a])=>{const p=a.active??a.available??!1,d=a.active?"active":a.available?"available":"unavailable";let u=a.mode?`Mode: ${a.mode}`:"-";return c==="edgePlugin"&&a.active?u="@serverStats() tag registered":c==="cacheInspector"&&a.available?u="Redis dependency detected":c==="queueInspector"&&a.available&&(u="Queue dependency detected"),t("tr",{children:[t("td",{children:kr(c)}),t("td",{children:[t(ee,{status:p?"active":"inactive",prefix:s}),d]}),t("td",{className:`${s}-c-dim`,style:{fontSize:"11px"},children:u})]},c)})]})]}),e.storage&&t(I,{children:[t("h3",{className:`${s}-internals-title`,children:"Storage (SQLite)"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{style:{width:"200px"},children:"Metric"}),t("th",{children:"Value"})]})}),t("tbody",{children:[t("tr",{children:[t("td",{children:"Status"}),t("td",{children:[t(ee,{status:e.storage.ready?"ready":"inactive",prefix:s}),e.storage.ready?"ready":"not ready"]})]}),t("tr",{children:[t("td",{children:"DB Path"}),t("td",{children:t("code",{children:e.storage.dbPath})})]}),t("tr",{children:[t("td",{children:"File Size"}),t("td",{children:[e.storage.fileSizeMb.toFixed(1)," MB"]})]}),t("tr",{children:[t("td",{children:"WAL Size"}),t("td",{children:[e.storage.walSizeMb.toFixed(1)," MB"]})]}),t("tr",{children:[t("td",{children:"Retention"}),t("td",{children:[e.storage.retentionDays," days"]})]}),t("tr",{children:[t("td",{children:"Last Cleanup"}),t("td",{children:e.storage.lastCleanupAt?R(e.storage.lastCleanupAt):"-"})]})]})]}),e.storage.tables.length>0&&t("table",{className:n,style:{marginTop:8},children:[t("thead",{children:t("tr",{children:[t("th",{children:"Table"}),t("th",{children:"Rows"})]})}),t("tbody",{children:e.storage.tables.map(c=>t("tr",{children:[t("td",{children:t("code",{children:c.name})}),t("td",{children:c.rowCount.toLocaleString()})]},c.name))})]})]}),t("h3",{className:`${s}-internals-title`,children:"Resolved Config"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{style:{width:"200px"},children:"Setting"}),t("th",{children:"Value"})]})}),t("tbody",{children:[t("tr",{children:[t("td",{children:"intervalMs"}),t("td",{children:e.config.intervalMs})]}),t("tr",{children:[t("td",{children:"transport"}),t("td",{children:e.config.transport})]}),t("tr",{children:[t("td",{children:"channelName"}),t("td",{children:e.config.channelName})]}),t("tr",{children:[t("td",{children:"endpoint"}),t("td",{children:e.config.endpoint===!1?"false":e.config.endpoint})]}),t("tr",{children:[t("td",{children:"skipInTest"}),t("td",{children:h("skipInTest",e.config.skipInTest)})]}),t("tr",{children:[t("td",{children:"onStats callback"}),t("td",{children:e.config.hasOnStatsCallback?"defined":"not defined"})]}),t("tr",{children:[t("td",{children:"shouldShow callback"}),t("td",{children:e.config.hasShouldShowCallback?"defined":"not defined"})]})]})]}),t("h4",{className:`${s}-internals-title`,children:"DevToolbar"}),t("table",{className:n,children:[t("thead",{children:t("tr",{children:[t("th",{style:{width:"200px"},children:"Setting"}),t("th",{children:"Value"})]})}),t("tbody",{children:Object.entries(e.devToolbar).map(([c,a])=>t("tr",{children:[t("td",{children:c==="customPaneCount"?"customPanes":c}),t("td",{children:c==="customPaneCount"?`${a} registered`:h(c,a)})]},c))})]})]})}const Mr=3e3;function Ht({options:e}){const{baseUrl:n="",debugEndpoint:r="/admin/api/debug",authToken:s}=e||{},[i,l]=x(null),[o,h]=x(!0),[c,a]=x(null),p=B(null),d=At(n,s),u=S(async()=>{try{const _=await d().get(`${r}/diagnostics`);l(_),a(null),h(!1)}catch(f){if(f instanceof ue){a(f),h(!1),p.current&&(clearInterval(p.current),p.current=null);return}a(f instanceof Error?f:new Error(String(f))),h(!1)}},[r,d]);return z(()=>(h(!0),a(null),u(),p.current=setInterval(u,Mr),()=>{p.current&&(clearInterval(p.current),p.current=null)}),[u]),o&&!i?t("div",{className:"ss-dbg-empty",children:"Loading diagnostics..."}):c?t("div",{className:"ss-dbg-empty",children:["Error: ",c.message]}):i?t(Pr,{data:i,tableClassName:"ss-dbg-table",classPrefix:"ss-dbg"}):t("div",{className:"ss-dbg-empty",children:"Diagnostics not available"})}const Or=Object.freeze(Object.defineProperty({__proto__:null,InternalsTab:Ht,default:Ht},Symbol.toStringTag,{value:"Module"}));function zt({pane:e,options:n}){({...n});const{data:r,isLoading:s,error:i,clearData:l}=F(e.endpoint.replace(/^\//,""),{...n,debugEndpoint:""}),[o,h]=x(""),c=T(()=>{if(!r)return[];const u=e.dataKey||e.id;let f=r;for(const _ of u.split("."))f=f?.[_];return Array.isArray(f)?f:[]},[r,e.dataKey,e.id]),a=T(()=>{if(!o)return c;const u=o.toLowerCase(),f=e.columns.filter(_=>_.searchable).map(_=>_.key);return f.length===0?c:c.filter(_=>f.some(m=>{const y=_[m];return y!==null&&String(y).toLowerCase().includes(u)}))},[c,o,e.columns]),p=(u,f)=>{if(u==null)return t("span",{className:"ss-dbg-c-dim",children:"-"});switch(f.format||"text"){case"time":return t("span",{className:"ss-dbg-event-time",title:V(u),children:typeof u=="number"?R(u):String(u)});case"timeAgo":return t("span",{className:"ss-dbg-event-time",title:V(u),children:R(u)});case"duration":{const m=typeof u=="number"?u:parseFloat(String(u));return isNaN(m)?String(u):t("span",{className:`ss-dbg-duration ${Q(m)==="very-slow"?"ss-dbg-very-slow":Q(m)==="slow"?"ss-dbg-slow":""}`,children:j(m)})}case"method":return t("span",{className:`ss-dbg-method ss-dbg-method-${String(u).toLowerCase()}`,children:String(u)});case"json":{let m=u;if(typeof u=="string")try{m=JSON.parse(u)}catch{}return pe(m,80)}case"badge":{const m=String(u).toLowerCase(),b=(f.badgeColorMap||{})[m]||"muted";return t("span",{className:`ss-dbg-badge ss-dbg-badge-${b}`,children:String(u)})}default:return String(u)}},d=W([a]);return s&&!r?t("div",{className:"ss-dbg-empty",children:["Loading ",e.label,"..."]}):i?t("div",{className:"ss-dbg-empty",children:["Error: ",i.message]}):t("div",{children:[e.search&&t("div",{className:"ss-dbg-search-bar",children:[t("input",{type:"text",className:"ss-dbg-search",placeholder:e.search.placeholder,value:o,onChange:u=>h(u.target.value)}),t("span",{className:"ss-dbg-summary",children:[a.length," items"]}),e.clearable&&t("button",{type:"button",className:"ss-dbg-btn-clear",onClick:l,children:"Clear"})]}),a.length===0?t("div",{className:"ss-dbg-empty",children:"No data"}):t("table",{ref:d,className:"ss-dbg-table",children:[t("thead",{children:t("tr",{children:e.columns.map(u=>t("th",{children:u.label},u.key))})}),t("tbody",{children:a.map((u,f)=>t("tr",{children:e.columns.map(_=>t("td",{children:p(u[_.key],_)},_.key))},u.id??f))})]})]})}const Ir=Object.freeze(Object.defineProperty({__proto__:null,CustomPaneTab:zt,default:zt},Symbol.toStringTag,{value:"Module"}))})();
|