adonisjs-server-stats 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/README.md +23 -14
  2. package/dist/core/config-utils.d.ts +8 -0
  3. package/dist/core/constants.d.ts +4 -0
  4. package/dist/core/dashboard-data-controller.d.ts +16 -0
  5. package/dist/core/dashboard-data-helpers.d.ts +12 -0
  6. package/dist/core/debug-data-controller.d.ts +4 -0
  7. package/dist/core/define-config-helpers.d.ts +25 -0
  8. package/dist/core/feature-detect-helpers.d.ts +36 -0
  9. package/dist/core/field-resolvers.d.ts +64 -0
  10. package/dist/core/formatters-helpers.d.ts +23 -0
  11. package/dist/core/formatters.d.ts +15 -0
  12. package/dist/core/index.d.ts +1 -1
  13. package/dist/core/index.js +599 -509
  14. package/dist/core/log-utils-helpers.d.ts +13 -0
  15. package/dist/core/metrics.d.ts +3 -28
  16. package/dist/core/pagination.d.ts +0 -9
  17. package/dist/core/server-stats-controller.d.ts +6 -0
  18. package/dist/core/transmit-helpers.d.ts +7 -0
  19. package/dist/core/types-dashboard.d.ts +178 -0
  20. package/dist/core/types-diagnostics.d.ts +85 -0
  21. package/dist/core/types.d.ts +10 -442
  22. package/dist/react/CacheSection-BYN53kYO.js +135 -0
  23. package/dist/react/CacheStatsBar-CRodCOeP.js +27 -0
  24. package/dist/react/CacheTab-DOhuK05d.js +106 -0
  25. package/dist/react/{ConfigSection-DfFd-WRq.js → ConfigSection-B9EHh4Rp.js} +1 -1
  26. package/dist/react/{ConfigTab-Bdg8YMer.js → ConfigTab-C8kriE2b.js} +1 -1
  27. package/dist/react/CustomPaneTab-CvzQS_Wh.js +99 -0
  28. package/dist/react/EmailPreviewOverlay-BmXOAvqG.js +58 -0
  29. package/dist/react/EmailsSection-BJyFJf7A.js +226 -0
  30. package/dist/react/EmailsTab-Ch8jp10B.js +110 -0
  31. package/dist/react/{EventsSection-ByQ-9blq.js → EventsSection-DJPwHeT8.js} +28 -27
  32. package/dist/react/EventsTab-B-FoehXC.js +58 -0
  33. package/dist/react/{FilterBar-DQRXpWrb.js → FilterBar-CQ7bD669.js} +15 -15
  34. package/dist/react/{InternalsContent-DBzsI0CG.js → InternalsContent-O8ino9oM.js} +133 -109
  35. package/dist/react/InternalsSection-B6VlVx5f.js +22 -0
  36. package/dist/react/InternalsTab-CkEKpRMU.js +17 -0
  37. package/dist/react/JobStatsBar-C7RslAFE.js +30 -0
  38. package/dist/react/JobsSection-DWF4i1t_.js +167 -0
  39. package/dist/react/JobsTab-DqnifQXV.js +129 -0
  40. package/dist/react/LogEntryRow-CMMkqA9M.js +43 -0
  41. package/dist/react/LogsSection-C1xC5aP4.js +198 -0
  42. package/dist/react/LogsTab-CS4sLfLw.js +79 -0
  43. package/dist/react/{OverviewSection-C4T1ur51.js → OverviewSection-CxvfOR0v.js} +70 -80
  44. package/dist/react/QueriesSection-CrMdU5Ax.js +458 -0
  45. package/dist/react/{QueriesTab-osLUWd4L.js → QueriesTab-x85PjkyS.js} +38 -40
  46. package/dist/react/RequestsSection-DETN9oZb.js +321 -0
  47. package/dist/react/{RoutesSection-BUSkM6PY.js → RoutesSection-CmorkJeC.js} +2 -2
  48. package/dist/react/RoutesTab-CbzBOzpc.js +68 -0
  49. package/dist/react/SplitPaneWrapper-BiIgT4ND.js +49 -0
  50. package/dist/react/TimeAgoCell-o3KigGfM.js +8 -0
  51. package/dist/react/{TimelineTab-Covg5weo.js → TimelineTab-Ue9tUD_n.js} +76 -102
  52. package/dist/react/index-DwDK-4oX.js +1121 -0
  53. package/dist/react/index.js +6 -6
  54. package/dist/react/react/components/shared/CacheStatsBar.d.ts +13 -0
  55. package/dist/react/react/components/shared/EmailPreviewOverlay.d.ts +29 -0
  56. package/dist/react/react/components/{Dashboard/shared → shared}/FilterBar.d.ts +4 -3
  57. package/dist/react/react/components/shared/JobStatsBar.d.ts +12 -0
  58. package/dist/react/react/components/shared/LogEntryRow.d.ts +9 -0
  59. package/dist/react/react/components/shared/RelatedLogs.d.ts +2 -2
  60. package/dist/react/react/components/shared/SplitPaneWrapper.d.ts +7 -0
  61. package/dist/react/react/components/shared/TimeAgoCell.d.ts +17 -0
  62. package/dist/react/react/hooks/useDashboardData.d.ts +4 -8
  63. package/dist/react/react/hooks/useDiagnosticsData.d.ts +14 -0
  64. package/dist/react/style.css +1 -1
  65. package/dist/src/collectors/app_collector.d.ts +0 -8
  66. package/dist/src/collectors/app_collector.js +45 -52
  67. package/dist/src/collectors/auto_detect.d.ts +0 -23
  68. package/dist/src/collectors/auto_detect.js +33 -55
  69. package/dist/src/collectors/db_pool_collector.d.ts +14 -16
  70. package/dist/src/collectors/db_pool_collector.js +72 -57
  71. package/dist/src/collectors/log_collector.d.ts +0 -47
  72. package/dist/src/collectors/log_collector.js +36 -65
  73. package/dist/src/collectors/queue_collector.d.ts +0 -20
  74. package/dist/src/collectors/queue_collector.js +60 -76
  75. package/dist/src/collectors/redis_collector.d.ts +10 -10
  76. package/dist/src/collectors/redis_collector.js +69 -66
  77. package/dist/src/config/deprecation_migration.d.ts +7 -0
  78. package/dist/src/config/deprecation_migration.js +201 -0
  79. package/dist/src/controller/debug_controller.d.ts +1 -1
  80. package/dist/src/controller/debug_controller.js +87 -81
  81. package/dist/src/dashboard/cache_handlers.d.ts +14 -0
  82. package/dist/src/dashboard/cache_handlers.js +52 -0
  83. package/dist/src/dashboard/chart_aggregator.d.ts +0 -7
  84. package/dist/src/dashboard/chart_aggregator.js +68 -50
  85. package/dist/src/dashboard/coalesce_cache.d.ts +25 -0
  86. package/dist/src/dashboard/coalesce_cache.js +47 -0
  87. package/dist/src/dashboard/dashboard_controller.d.ts +11 -37
  88. package/dist/src/dashboard/dashboard_controller.js +51 -544
  89. package/dist/src/dashboard/dashboard_page_assets.d.ts +17 -0
  90. package/dist/src/dashboard/dashboard_page_assets.js +51 -0
  91. package/dist/src/dashboard/dashboard_store.d.ts +19 -218
  92. package/dist/src/dashboard/dashboard_store.js +115 -1116
  93. package/dist/src/dashboard/dashboard_types.d.ts +83 -0
  94. package/dist/src/dashboard/dashboard_types.js +4 -0
  95. package/dist/src/dashboard/detail_queries.d.ts +19 -0
  96. package/dist/src/dashboard/detail_queries.js +98 -0
  97. package/dist/src/dashboard/email_event_builder.d.ts +8 -0
  98. package/dist/src/dashboard/email_event_builder.js +65 -0
  99. package/dist/src/dashboard/explain_query.d.ts +8 -0
  100. package/dist/src/dashboard/explain_query.js +22 -0
  101. package/dist/src/dashboard/filter_handlers.d.ts +23 -0
  102. package/dist/src/dashboard/filter_handlers.js +56 -0
  103. package/dist/src/dashboard/filtered_queries.d.ts +15 -0
  104. package/dist/src/dashboard/filtered_queries.js +155 -0
  105. package/dist/src/dashboard/flush_manager.d.ts +25 -0
  106. package/dist/src/dashboard/flush_manager.js +107 -0
  107. package/dist/src/dashboard/format_helpers.d.ts +126 -0
  108. package/dist/src/dashboard/format_helpers.js +140 -0
  109. package/dist/src/dashboard/inspector_manager.d.ts +36 -0
  110. package/dist/src/dashboard/inspector_manager.js +102 -0
  111. package/dist/src/dashboard/integrations/config_inspector.js +11 -13
  112. package/dist/src/dashboard/integrations/queue_inspector.d.ts +3 -3
  113. package/dist/src/dashboard/integrations/queue_inspector.js +13 -10
  114. package/dist/src/dashboard/jobs_handlers.d.ts +14 -0
  115. package/dist/src/dashboard/jobs_handlers.js +61 -0
  116. package/dist/src/dashboard/knex_factory.d.ts +18 -0
  117. package/dist/src/dashboard/knex_factory.js +91 -0
  118. package/dist/src/dashboard/migrator.js +30 -159
  119. package/dist/src/dashboard/migrator_tables.d.ts +19 -0
  120. package/dist/src/dashboard/migrator_tables.js +153 -0
  121. package/dist/src/dashboard/overview_queries.d.ts +66 -0
  122. package/dist/src/dashboard/overview_queries.js +155 -0
  123. package/dist/src/dashboard/overview_query_runners.d.ts +25 -0
  124. package/dist/src/dashboard/overview_query_runners.js +84 -0
  125. package/dist/src/dashboard/overview_store_queries.d.ts +40 -0
  126. package/dist/src/dashboard/overview_store_queries.js +69 -0
  127. package/dist/src/dashboard/paginate_helper.d.ts +12 -0
  128. package/dist/src/dashboard/paginate_helper.js +33 -0
  129. package/dist/src/dashboard/query_explain_handler.d.ts +10 -0
  130. package/dist/src/dashboard/query_explain_handler.js +80 -0
  131. package/dist/src/dashboard/read_queries.d.ts +32 -0
  132. package/dist/src/dashboard/read_queries.js +107 -0
  133. package/dist/src/dashboard/saved_filter_queries.d.ts +10 -0
  134. package/dist/src/dashboard/saved_filter_queries.js +24 -0
  135. package/dist/src/dashboard/storage_stats.d.ts +41 -0
  136. package/dist/src/dashboard/storage_stats.js +81 -0
  137. package/dist/src/dashboard/write_queue.d.ts +106 -0
  138. package/dist/src/dashboard/write_queue.js +225 -0
  139. package/dist/src/data/data_access.d.ts +2 -39
  140. package/dist/src/data/data_access.js +17 -193
  141. package/dist/src/data/data_access_helpers.d.ts +130 -0
  142. package/dist/src/data/data_access_helpers.js +212 -0
  143. package/dist/src/debug/debug_store.js +37 -32
  144. package/dist/src/debug/email_collector.d.ts +1 -10
  145. package/dist/src/debug/email_collector.js +78 -81
  146. package/dist/src/debug/event_collector.d.ts +0 -9
  147. package/dist/src/debug/event_collector.js +79 -62
  148. package/dist/src/debug/query_collector.js +23 -19
  149. package/dist/src/debug/route_inspector.d.ts +1 -5
  150. package/dist/src/debug/route_inspector.js +50 -51
  151. package/dist/src/debug/trace_collector.d.ts +9 -1
  152. package/dist/src/debug/trace_collector.js +21 -15
  153. package/dist/src/debug/types.d.ts +1 -1
  154. package/dist/src/define_config.d.ts +0 -65
  155. package/dist/src/define_config.js +93 -333
  156. package/dist/src/edge/client/dashboard.js +2 -2
  157. package/dist/src/edge/client/debug-panel-deferred.js +1 -1
  158. package/dist/src/edge/client/stats-bar.js +1 -1
  159. package/dist/src/edge/client-vue/dashboard.js +5 -5
  160. package/dist/src/edge/client-vue/debug-panel-deferred.js +4 -4
  161. package/dist/src/edge/client-vue/stats-bar.js +3 -3
  162. package/dist/src/edge/plugin.d.ts +0 -16
  163. package/dist/src/edge/plugin.js +57 -64
  164. package/dist/src/engine/request_metrics.d.ts +1 -0
  165. package/dist/src/engine/request_metrics.js +32 -42
  166. package/dist/src/middleware/request_tracking_middleware.d.ts +2 -8
  167. package/dist/src/middleware/request_tracking_middleware.js +65 -93
  168. package/dist/src/provider/auth_middleware_detector.d.ts +16 -0
  169. package/dist/src/provider/auth_middleware_detector.js +97 -0
  170. package/dist/src/provider/boot_helpers.d.ts +20 -0
  171. package/dist/src/provider/boot_helpers.js +91 -0
  172. package/dist/src/provider/boot_initializer.d.ts +28 -0
  173. package/dist/src/provider/boot_initializer.js +35 -0
  174. package/dist/src/provider/dashboard_init.d.ts +30 -0
  175. package/dist/src/provider/dashboard_init.js +138 -0
  176. package/dist/src/provider/dashboard_setup.d.ts +25 -0
  177. package/dist/src/provider/dashboard_setup.js +78 -0
  178. package/dist/src/provider/diagnostics.d.ts +134 -0
  179. package/dist/src/provider/diagnostics.js +127 -0
  180. package/dist/src/provider/email_bridge.d.ts +43 -0
  181. package/dist/src/provider/email_bridge.js +80 -0
  182. package/dist/src/provider/email_helpers.d.ts +13 -0
  183. package/dist/src/provider/email_helpers.js +68 -0
  184. package/dist/src/provider/pino_hook.d.ts +17 -0
  185. package/dist/src/provider/pino_hook.js +35 -0
  186. package/dist/src/provider/provider_helpers_extra.d.ts +47 -0
  187. package/dist/src/provider/provider_helpers_extra.js +177 -0
  188. package/dist/src/provider/server_stats_provider.d.ts +39 -85
  189. package/dist/src/provider/server_stats_provider.js +132 -951
  190. package/dist/src/provider/shutdown_helpers.d.ts +43 -0
  191. package/dist/src/provider/shutdown_helpers.js +70 -0
  192. package/dist/src/provider/toolbar_setup.d.ts +57 -0
  193. package/dist/src/provider/toolbar_setup.js +141 -0
  194. package/dist/src/routes/dashboard_routes.d.ts +14 -0
  195. package/dist/src/routes/dashboard_routes.js +197 -0
  196. package/dist/src/routes/debug_routes.d.ts +14 -0
  197. package/dist/src/routes/debug_routes.js +101 -0
  198. package/dist/src/routes/register_routes.d.ts +0 -78
  199. package/dist/src/routes/register_routes.js +22 -352
  200. package/dist/src/routes/stats_routes.d.ts +5 -0
  201. package/dist/src/routes/stats_routes.js +14 -0
  202. package/dist/src/styles/components.css +163 -0
  203. package/dist/src/styles/dashboard.css +13 -105
  204. package/dist/src/styles/debug-panel.css +2 -53
  205. package/dist/src/styles/utilities.css +3 -1
  206. package/dist/src/types.d.ts +305 -14
  207. package/dist/vue/{CacheSection-oFAJL3mo.js → CacheSection-DT2Mwf_s.js} +1 -1
  208. package/dist/vue/{ConfigSection-BhfJ4KqL.js → ConfigSection-BwKwS9lh.js} +1 -1
  209. package/dist/vue/CustomPaneTab-Hr1IBHfz.js +172 -0
  210. package/dist/vue/{EmailsSection-BcNyhyHs.js → EmailsSection-B65g0FVS.js} +1 -1
  211. package/dist/vue/{EventsSection-r60Q5Lmu.js → EventsSection-CxqtVF-o.js} +1 -1
  212. package/dist/vue/{JobsSection-BHL-hkQw.js → JobsSection-rMIyMb-g.js} +1 -1
  213. package/dist/vue/{LogsSection-DRMGzJmg.js → LogsSection-DmmZVJ7D.js} +9 -3
  214. package/dist/vue/{LogsTab-Bg3o0Mm6.js → LogsTab-47zEK7jL.js} +4 -1
  215. package/dist/vue/{OverviewSection-CXh6Ja1B.js → OverviewSection-BMabyqw-.js} +49 -50
  216. package/dist/vue/{QueriesSection-IodIsCJ-.js → QueriesSection-BfDFwGqH.js} +44 -45
  217. package/dist/vue/{QueriesTab-C8_7oprC.js → QueriesTab-DuTG7cpC.js} +30 -31
  218. package/dist/vue/RelatedLogs.vue_vue_type_script_setup_true_lang-Py1iu9GU.js +77 -0
  219. package/dist/vue/{RequestsSection-BPuMdmMc.js → RequestsSection-CTu4jPZ_.js} +143 -147
  220. package/dist/vue/{RoutesSection-NKo3Rbq3.js → RoutesSection-zQZDedL7.js} +1 -1
  221. package/dist/vue/TimelineTab-DHfXsX7t.js +334 -0
  222. package/dist/vue/components/shared/RelatedLogs.vue.d.ts +1 -4
  223. package/dist/vue/composables/useDashboardData.d.ts +12 -23
  224. package/dist/vue/index-CM3yNVUR.js +1232 -0
  225. package/dist/vue/index.js +1 -1
  226. package/dist/vue/style.css +1 -1
  227. package/package.json +1 -1
  228. package/dist/react/CacheSection-UCMptWyn.js +0 -146
  229. package/dist/react/CacheTab-CA8LB1J5.js +0 -123
  230. package/dist/react/CustomPaneTab-Bxtv_8Rw.js +0 -104
  231. package/dist/react/EmailsSection-CM7stSyh.js +0 -262
  232. package/dist/react/EmailsTab-BDhEiomM.js +0 -153
  233. package/dist/react/EventsTab-CMfY98Rl.js +0 -63
  234. package/dist/react/InternalsSection-t7ihcWO-.js +0 -32
  235. package/dist/react/InternalsTab-Oij0A2fN.js +0 -30
  236. package/dist/react/JobsSection-DF3qEv9O.js +0 -187
  237. package/dist/react/JobsTab-BbrBWIOb.js +0 -141
  238. package/dist/react/LogsSection-DcFTZY7b.js +0 -227
  239. package/dist/react/LogsTab-CicucmVk.js +0 -103
  240. package/dist/react/QueriesSection-PswteoF9.js +0 -461
  241. package/dist/react/RelatedLogs-DFDOyUMr.js +0 -40
  242. package/dist/react/RequestsSection-Nag30rEA.js +0 -341
  243. package/dist/react/RoutesTab-DgVzd2PZ.js +0 -74
  244. package/dist/react/index-Cflz9Ebj.js +0 -1069
  245. package/dist/vue/CustomPaneTab-BJxT5Dp7.js +0 -172
  246. package/dist/vue/RelatedLogs.vue_vue_type_script_setup_true_lang-CB2_TzYW.js +0 -84
  247. package/dist/vue/TimelineTab-zj5Z5OdT.js +0 -338
  248. package/dist/vue/index-Dtgysd26.js +0 -1229
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Shared types for the dashboard store and its extracted modules.
3
+ */
4
+ import type { Knex } from 'knex';
5
+ export interface EventEmitter {
6
+ on(event: string, handler: (...args: unknown[]) => void): void;
7
+ off(event: string, handler: (...args: unknown[]) => void): void;
8
+ }
9
+ export interface RequestInput {
10
+ method: string;
11
+ url: string;
12
+ statusCode: number;
13
+ duration: number;
14
+ spanCount?: number;
15
+ warningCount?: number;
16
+ }
17
+ export interface PersistRequestInput extends RequestInput {
18
+ queries: import('../debug/types.js').QueryRecord[];
19
+ trace: import('../debug/types.js').TraceRecord | null;
20
+ httpRequestId?: string | null;
21
+ }
22
+ export interface RequestFilters {
23
+ method?: string;
24
+ url?: string;
25
+ status?: number;
26
+ statusMin?: number;
27
+ statusMax?: number;
28
+ durationMin?: number;
29
+ durationMax?: number;
30
+ search?: string;
31
+ }
32
+ export interface QueryFilters {
33
+ method?: string;
34
+ model?: string;
35
+ connection?: string;
36
+ durationMin?: number;
37
+ durationMax?: number;
38
+ requestId?: number;
39
+ search?: string;
40
+ }
41
+ export interface EventFilters {
42
+ eventName?: string;
43
+ search?: string;
44
+ }
45
+ export interface EmailFilters {
46
+ search?: string;
47
+ from?: string;
48
+ to?: string;
49
+ subject?: string;
50
+ mailer?: string;
51
+ status?: string;
52
+ }
53
+ export interface LogFilters {
54
+ level?: string;
55
+ requestId?: string;
56
+ search?: string;
57
+ structured?: {
58
+ field: string;
59
+ operator: 'equals' | 'contains' | 'startsWith';
60
+ value: string;
61
+ }[];
62
+ }
63
+ export interface TraceFilters {
64
+ method?: string;
65
+ url?: string;
66
+ statusMin?: number;
67
+ statusMax?: number;
68
+ search?: string;
69
+ }
70
+ export interface PaginatedResult<T> {
71
+ data: T[];
72
+ total: number;
73
+ page: number;
74
+ perPage: number;
75
+ lastPage: number;
76
+ }
77
+ export interface PaginateOptions {
78
+ table: string;
79
+ page: number;
80
+ perPage: number;
81
+ applyFilters?: (query: Knex.QueryBuilder) => void;
82
+ filterKey?: string;
83
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Shared types for the dashboard store and its extracted modules.
3
+ */
4
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Detail queries for individual request and trace records.
3
+ *
4
+ * Extracted from DashboardStore to reduce file length.
5
+ */
6
+ import type { Knex } from 'knex';
7
+ /**
8
+ * Query logs within a time window around a given timestamp.
9
+ * Used as a fallback when no precise request_id correlation exists.
10
+ */
11
+ export declare function queryLogsByTimeWindow(db: Knex, createdAt: string, duration: number): Promise<Record<string, unknown>[]>;
12
+ /**
13
+ * Fetch a single trace with full span data and correlated logs.
14
+ */
15
+ export declare function fetchTraceDetail(db: Knex, id: number): Promise<Record<string, unknown> | null>;
16
+ /**
17
+ * Fetch a single request with associated queries, events, trace, and logs.
18
+ */
19
+ export declare function fetchRequestDetail(db: Knex, id: number): Promise<Record<string, unknown> | null>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Detail queries for individual request and trace records.
3
+ *
4
+ * Extracted from DashboardStore to reduce file length.
5
+ */
6
+ import { safeParseJson, safeParseJsonArray } from '../utils/json_helpers.js';
7
+ // ---------------------------------------------------------------------------
8
+ // Log time-window fallback
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Query logs within a time window around a given timestamp.
12
+ * Used as a fallback when no precise request_id correlation exists.
13
+ */
14
+ export async function queryLogsByTimeWindow(db, createdAt, duration) {
15
+ const windowSec = Math.ceil(duration / 1000) + 2;
16
+ return db('server_stats_logs')
17
+ .where('created_at', '>=', db.raw(`datetime(?, '-${windowSec} seconds')`, [createdAt]))
18
+ .where('created_at', '<=', db.raw(`datetime(?, '+${windowSec} seconds')`, [createdAt]))
19
+ .orderBy('created_at', 'asc')
20
+ .limit(100);
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // Trace detail
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Fetch a single trace with full span data and correlated logs.
27
+ */
28
+ export async function fetchTraceDetail(db, id) {
29
+ const row = await db('server_stats_traces').where('id', id).first();
30
+ if (!row)
31
+ return null;
32
+ let logs = [];
33
+ let httpRequestId = null;
34
+ if (row.request_id) {
35
+ const linkedRequest = await db('server_stats_requests')
36
+ .where('id', row.request_id)
37
+ .select('http_request_id', 'created_at')
38
+ .first();
39
+ if (linkedRequest?.http_request_id) {
40
+ httpRequestId = linkedRequest.http_request_id;
41
+ logs = await db('server_stats_logs')
42
+ .where('request_id', linkedRequest.http_request_id)
43
+ .orderBy('created_at', 'asc');
44
+ }
45
+ }
46
+ if (logs.length === 0 && row.created_at) {
47
+ logs = await queryLogsByTimeWindow(db, row.created_at, row.total_duration || 0);
48
+ }
49
+ return {
50
+ ...row,
51
+ spans: safeParseJson(row.spans) ?? [],
52
+ warnings: safeParseJsonArray(row.warnings),
53
+ logs,
54
+ http_request_id: httpRequestId,
55
+ };
56
+ }
57
+ // ---------------------------------------------------------------------------
58
+ // Request detail
59
+ // ---------------------------------------------------------------------------
60
+ /**
61
+ * Fetch a single request with associated queries, events, trace, and logs.
62
+ */
63
+ export async function fetchRequestDetail(db, id) {
64
+ return db.transaction(async (trx) => {
65
+ const request = await trx('server_stats_requests').where('id', id).first();
66
+ if (!request)
67
+ return null;
68
+ const queries = await trx('server_stats_queries')
69
+ .where('request_id', id)
70
+ .orderBy('created_at', 'asc');
71
+ const events = await trx('server_stats_events')
72
+ .where('request_id', id)
73
+ .orderBy('created_at', 'asc');
74
+ const trace = await trx('server_stats_traces').where('request_id', id).first();
75
+ let logs = [];
76
+ if (request.http_request_id) {
77
+ logs = await trx('server_stats_logs')
78
+ .where('request_id', request.http_request_id)
79
+ .orderBy('created_at', 'asc');
80
+ }
81
+ if (logs.length === 0 && request.created_at) {
82
+ logs = await queryLogsByTimeWindow(trx, request.created_at, request.duration || 0);
83
+ }
84
+ return {
85
+ ...request,
86
+ queries,
87
+ events,
88
+ logs,
89
+ trace: trace
90
+ ? {
91
+ ...trace,
92
+ spans: safeParseJson(trace.spans) ?? [],
93
+ warnings: safeParseJsonArray(trace.warnings),
94
+ }
95
+ : null,
96
+ };
97
+ });
98
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pure function to build an EmailRecord from an AdonisJS mail event.
3
+ *
4
+ * Extracted from DashboardStore.wireEventListeners to reduce
5
+ * cyclomatic complexity (the original had 28 due to many || chains).
6
+ */
7
+ import type { EmailRecord } from '../debug/types.js';
8
+ export declare function buildEmailRecordFromEvent(data: unknown, status: EmailRecord['status']): EmailRecord;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Pure function to build an EmailRecord from an AdonisJS mail event.
3
+ *
4
+ * Extracted from DashboardStore.wireEventListeners to reduce
5
+ * cyclomatic complexity (the original had 28 due to many || chains).
6
+ */
7
+ import { extractAddresses } from '../utils/mail_helpers.js';
8
+ /**
9
+ * Build an EmailRecord from raw event data emitted by AdonisJS mail events.
10
+ *
11
+ * Handles two shapes:
12
+ * 1. Standard: `{ message: { from, to, ... }, mailerName, response }`
13
+ * 2. Flat: `{ from, to, subject, ... }` (data IS the message)
14
+ */
15
+ function extractAddressFields(msg) {
16
+ return {
17
+ from: extractAddresses(msg?.from) || 'unknown',
18
+ to: extractAddresses(msg?.to) || 'unknown',
19
+ cc: extractAddresses(msg?.cc) || null,
20
+ bcc: extractAddresses(msg?.bcc) || null,
21
+ };
22
+ }
23
+ function extractContentFields(msg) {
24
+ return {
25
+ subject: extractSubject(msg),
26
+ html: extractString(msg?.html),
27
+ text: extractString(msg?.text),
28
+ attachmentCount: countAttachments(msg),
29
+ };
30
+ }
31
+ export function buildEmailRecordFromEvent(data, status) {
32
+ const d = (data ?? {});
33
+ const msg = extractMessage(d);
34
+ return {
35
+ ...extractAddressFields(msg),
36
+ ...extractContentFields(msg),
37
+ mailer: extractMailer(d),
38
+ status,
39
+ messageId: extractMessageId(d),
40
+ timestamp: Date.now(),
41
+ };
42
+ }
43
+ function extractMessage(d) {
44
+ return (d.message || d);
45
+ }
46
+ function extractSubject(msg) {
47
+ return msg?.subject || '(no subject)';
48
+ }
49
+ function extractString(value) {
50
+ return value || null;
51
+ }
52
+ function extractMailer(d) {
53
+ return d.mailerName || d.mailer || 'unknown';
54
+ }
55
+ function extractMessageId(d) {
56
+ const fromResponse = d.response?.messageId;
57
+ if (fromResponse)
58
+ return fromResponse;
59
+ return d.messageId || null;
60
+ }
61
+ function countAttachments(msg) {
62
+ if (!Array.isArray(msg?.attachments))
63
+ return 0;
64
+ return msg.attachments.length;
65
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * EXPLAIN query execution helper for the DashboardStore.
3
+ *
4
+ * Extracted from DashboardStore to reduce file length.
5
+ */
6
+ import { CoalesceCache } from './coalesce_cache.js';
7
+ import type { Knex } from 'knex';
8
+ export declare function executeExplain(db: Knex, cache: CoalesceCache, queryId: number, appDb: unknown): Promise<Record<string, unknown> | null>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * EXPLAIN query execution helper for the DashboardStore.
3
+ *
4
+ * Extracted from DashboardStore to reduce file length.
5
+ */
6
+ export function executeExplain(db, cache, queryId, appDb) {
7
+ return cache.coalesce('explain:' + queryId, async () => {
8
+ const row = await db('server_stats_queries').where('id', queryId).first();
9
+ if (!row)
10
+ return { error: 'Query not found' };
11
+ const sql = row.sql_text.trim();
12
+ if (!sql.toLowerCase().startsWith('select'))
13
+ return { error: 'EXPLAIN is only supported for SELECT queries' };
14
+ try {
15
+ const r = await appDb.rawQuery(`EXPLAIN ${sql}`);
16
+ return { plan: r.rows || r };
17
+ }
18
+ catch (err) {
19
+ return { error: err.message || 'EXPLAIN failed' };
20
+ }
21
+ });
22
+ }
@@ -0,0 +1,23 @@
1
+ import type { DashboardStore } from './dashboard_store.js';
2
+ import type { HttpContext } from '@adonisjs/core/http';
3
+ /**
4
+ * Handle GET /saved-filters — list all saved filters.
5
+ */
6
+ export declare function handleSavedFilters(dashboardStore: DashboardStore): Promise<{
7
+ filters: {
8
+ id: unknown;
9
+ name: unknown;
10
+ section: unknown;
11
+ filterConfig: unknown;
12
+ createdAt: unknown;
13
+ }[];
14
+ }>;
15
+ /**
16
+ * Handle POST /saved-filters — create a new saved filter.
17
+ * Returns the response payload or null if creation fails.
18
+ */
19
+ export declare function handleCreateSavedFilter(dashboardStore: DashboardStore, { request, response }: HttpContext): Promise<void>;
20
+ /**
21
+ * Handle DELETE /saved-filters/:id — delete a saved filter.
22
+ */
23
+ export declare function handleDeleteSavedFilter(dashboardStore: DashboardStore, { params, response }: HttpContext): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { safeParseJson } from '../utils/json_helpers.js';
2
+ /**
3
+ * Handle GET /saved-filters — list all saved filters.
4
+ */
5
+ export async function handleSavedFilters(dashboardStore) {
6
+ const filters = await dashboardStore.getSavedFilters();
7
+ return {
8
+ filters: filters.map((f) => ({
9
+ id: f.id,
10
+ name: f.name,
11
+ section: f.section,
12
+ filterConfig: safeParseJson(f.filter_config),
13
+ createdAt: f.created_at,
14
+ })),
15
+ };
16
+ }
17
+ /**
18
+ * Handle POST /saved-filters — create a new saved filter.
19
+ * Returns the response payload or null if creation fails.
20
+ */
21
+ export async function handleCreateSavedFilter(dashboardStore, { request, response }) {
22
+ if (!dashboardStore.isReady()) {
23
+ return response.serviceUnavailable({ error: 'Database not available' });
24
+ }
25
+ try {
26
+ const { name, section, filterConfig } = request.body();
27
+ if (!name || !section || !filterConfig) {
28
+ return response.badRequest({
29
+ error: 'Missing required fields: name, section, filterConfig',
30
+ });
31
+ }
32
+ const result = await dashboardStore.createSavedFilter(name, section, typeof filterConfig === 'string' ? safeParseJson(filterConfig) : filterConfig);
33
+ return result
34
+ ? response.json(result)
35
+ : response.serviceUnavailable({ error: 'Database not available' });
36
+ }
37
+ catch {
38
+ return response.internalServerError({ error: 'Failed to create filter' });
39
+ }
40
+ }
41
+ /**
42
+ * Handle DELETE /saved-filters/:id — delete a saved filter.
43
+ */
44
+ export async function handleDeleteSavedFilter(dashboardStore, { params, response }) {
45
+ if (!dashboardStore.isReady()) {
46
+ return response.serviceUnavailable({ error: 'Database not available' });
47
+ }
48
+ try {
49
+ return (await dashboardStore.deleteSavedFilter(Number(params.id)))
50
+ ? response.json({ success: true })
51
+ : response.notFound({ error: 'Filter not found' });
52
+ }
53
+ catch {
54
+ return response.internalServerError({ error: 'Failed to delete filter' });
55
+ }
56
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Filter application functions for paginated dashboard queries.
3
+ *
4
+ * Each `apply*Filters` function takes a Knex QueryBuilder and an
5
+ * optional filter object, adding WHERE clauses as needed.
6
+ * Extracting these from DashboardStore reduces per-function complexity.
7
+ */
8
+ import type { RequestFilters, QueryFilters, EventFilters, EmailFilters, LogFilters, TraceFilters } from './dashboard_types.js';
9
+ import type { Knex } from 'knex';
10
+ export declare function applyRequestFilters(query: Knex.QueryBuilder, filters: RequestFilters | undefined): void;
11
+ export declare function applyQueryFilters(query: Knex.QueryBuilder, filters: QueryFilters | undefined): void;
12
+ export declare function applyEventFilters(query: Knex.QueryBuilder, filters: EventFilters | undefined): void;
13
+ export declare function applyEmailFilters(query: Knex.QueryBuilder, filters: EmailFilters | undefined, excludeBody: boolean): void;
14
+ export declare function applyLogFilters(query: Knex.QueryBuilder, filters: LogFilters | undefined): void;
15
+ export declare function applyTraceFilters(query: Knex.QueryBuilder, filters: TraceFilters | undefined): void;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Filter application functions for paginated dashboard queries.
3
+ *
4
+ * Each `apply*Filters` function takes a Knex QueryBuilder and an
5
+ * optional filter object, adding WHERE clauses as needed.
6
+ * Extracting these from DashboardStore reduces per-function complexity.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Request filters
10
+ // ---------------------------------------------------------------------------
11
+ export function applyRequestFilters(query, filters) {
12
+ if (!filters)
13
+ return;
14
+ if (filters.method)
15
+ query.where('method', filters.method);
16
+ if (filters.url)
17
+ query.where('url', 'like', `%${filters.url}%`);
18
+ if (filters.status)
19
+ query.where('status_code', filters.status);
20
+ if (filters.statusMin)
21
+ query.where('status_code', '>=', filters.statusMin);
22
+ if (filters.statusMax)
23
+ query.where('status_code', '<=', filters.statusMax);
24
+ if (filters.durationMin)
25
+ query.where('duration', '>=', filters.durationMin);
26
+ if (filters.durationMax)
27
+ query.where('duration', '<=', filters.durationMax);
28
+ if (filters.search) {
29
+ const term = `%${filters.search}%`;
30
+ query.where((qb) => {
31
+ qb.where('url', 'like', term).orWhere('method', 'like', term);
32
+ });
33
+ }
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Query filters
37
+ // ---------------------------------------------------------------------------
38
+ export function applyQueryFilters(query, filters) {
39
+ if (!filters)
40
+ return;
41
+ if (filters.method)
42
+ query.where('method', filters.method);
43
+ if (filters.model)
44
+ query.where('model', filters.model);
45
+ if (filters.connection)
46
+ query.where('connection', filters.connection);
47
+ if (filters.durationMin)
48
+ query.where('duration', '>=', filters.durationMin);
49
+ if (filters.durationMax)
50
+ query.where('duration', '<=', filters.durationMax);
51
+ if (filters.requestId)
52
+ query.where('request_id', filters.requestId);
53
+ if (filters.search) {
54
+ const term = `%${filters.search}%`;
55
+ query.where((qb) => {
56
+ qb.where('sql_text', 'like', term)
57
+ .orWhere('model', 'like', term)
58
+ .orWhere('connection', 'like', term);
59
+ });
60
+ }
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // Event filters
64
+ // ---------------------------------------------------------------------------
65
+ export function applyEventFilters(query, filters) {
66
+ if (!filters)
67
+ return;
68
+ if (filters.eventName)
69
+ query.where('event_name', 'like', `%${filters.eventName}%`);
70
+ if (filters.search)
71
+ query.where('event_name', 'like', `%${filters.search}%`);
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Email filters
75
+ // ---------------------------------------------------------------------------
76
+ export function applyEmailFilters(query, filters, excludeBody) {
77
+ if (filters) {
78
+ if (filters.search) {
79
+ const term = `%${filters.search}%`;
80
+ query.where((sub) => {
81
+ sub
82
+ .where('from_addr', 'like', term)
83
+ .orWhere('to_addr', 'like', term)
84
+ .orWhere('subject', 'like', term);
85
+ });
86
+ }
87
+ if (filters.from)
88
+ query.where('from_addr', 'like', `%${filters.from}%`);
89
+ if (filters.to)
90
+ query.where('to_addr', 'like', `%${filters.to}%`);
91
+ if (filters.subject)
92
+ query.where('subject', 'like', `%${filters.subject}%`);
93
+ if (filters.mailer)
94
+ query.where('mailer', filters.mailer);
95
+ if (filters.status)
96
+ query.where('status', filters.status);
97
+ }
98
+ if (excludeBody) {
99
+ query.select('id', 'from_addr', 'to_addr', 'cc', 'bcc', 'subject', 'mailer', 'status', 'message_id', 'attachment_count', 'created_at');
100
+ }
101
+ }
102
+ // ---------------------------------------------------------------------------
103
+ // Log filters
104
+ // ---------------------------------------------------------------------------
105
+ /** Operator-to-SQL pattern lookup for structured log filters. */
106
+ const STRUCTURED_OPERATORS = {
107
+ equals: (value) => value,
108
+ contains: (value) => `%${value}%`,
109
+ startsWith: (value) => `${value}%`,
110
+ };
111
+ /** Apply a single structured filter to a query. */
112
+ function applyStructuredFilter(query, sf) {
113
+ const patternFn = STRUCTURED_OPERATORS[sf.operator];
114
+ if (!patternFn)
115
+ return;
116
+ const jsonPath = `$.${sf.field}`;
117
+ const op = sf.operator === 'equals' ? '=' : 'LIKE';
118
+ query.whereRaw(`json_extract(data, ?) ${op} ?`, [jsonPath, patternFn(sf.value)]);
119
+ }
120
+ export function applyLogFilters(query, filters) {
121
+ if (!filters)
122
+ return;
123
+ if (filters.level)
124
+ query.where('level', filters.level);
125
+ if (filters.requestId)
126
+ query.where('request_id', filters.requestId);
127
+ if (filters.search)
128
+ query.where('message', 'like', `%${filters.search}%`);
129
+ if (filters.structured && filters.structured.length > 0) {
130
+ for (const sf of filters.structured) {
131
+ applyStructuredFilter(query, sf);
132
+ }
133
+ }
134
+ }
135
+ // ---------------------------------------------------------------------------
136
+ // Trace filters
137
+ // ---------------------------------------------------------------------------
138
+ export function applyTraceFilters(query, filters) {
139
+ if (!filters)
140
+ return;
141
+ if (filters.method)
142
+ query.where('method', filters.method);
143
+ if (filters.url)
144
+ query.where('url', 'like', `%${filters.url}%`);
145
+ if (filters.statusMin)
146
+ query.where('status_code', '>=', filters.statusMin);
147
+ if (filters.statusMax)
148
+ query.where('status_code', '<=', filters.statusMax);
149
+ if (filters.search) {
150
+ const term = `%${filters.search}%`;
151
+ query.where((qb) => {
152
+ qb.where('url', 'like', term).orWhere('method', 'like', term);
153
+ });
154
+ }
155
+ }
@@ -0,0 +1,25 @@
1
+ import type { EventRecord, EmailRecord } from '../debug/types.js';
2
+ import type { PersistRequestInput } from './dashboard_types.js';
3
+ import type { Knex } from 'knex';
4
+ export declare class FlushManager {
5
+ writeQueue: PersistRequestInput[];
6
+ pendingEvents: {
7
+ requestIndex: number;
8
+ events: EventRecord[];
9
+ }[];
10
+ pendingLogs: Record<string, unknown>[];
11
+ pendingEmails: EmailRecord[];
12
+ private flushTimer;
13
+ private flushing;
14
+ private db;
15
+ constructor(getDb: () => Knex | null);
16
+ persistRequest(input: PersistRequestInput, dashboardPath: string): void;
17
+ queueEvents(requestIndex: number, events: EventRecord[]): void;
18
+ recordLog(entry: Record<string, unknown>): void;
19
+ recordEmail(record: EmailRecord): void;
20
+ stop(): Promise<void>;
21
+ private backpressure;
22
+ private scheduleFlush;
23
+ flush(): Promise<void>;
24
+ private takeSnapshot;
25
+ }