adonisjs-server-stats 1.2.1 → 1.3.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 (95) hide show
  1. package/README.md +144 -9
  2. package/dist/src/dashboard/chart_aggregator.d.ts +21 -0
  3. package/dist/src/dashboard/chart_aggregator.d.ts.map +1 -0
  4. package/dist/src/dashboard/chart_aggregator.js +89 -0
  5. package/dist/src/dashboard/dashboard_controller.d.ts +147 -0
  6. package/dist/src/dashboard/dashboard_controller.d.ts.map +1 -0
  7. package/dist/src/dashboard/dashboard_controller.js +1008 -0
  8. package/dist/src/dashboard/dashboard_routes.d.ts +16 -0
  9. package/dist/src/dashboard/dashboard_routes.d.ts.map +1 -0
  10. package/dist/src/dashboard/dashboard_routes.js +88 -0
  11. package/dist/src/dashboard/dashboard_store.d.ts +158 -0
  12. package/dist/src/dashboard/dashboard_store.d.ts.map +1 -0
  13. package/dist/src/dashboard/dashboard_store.js +723 -0
  14. package/dist/src/dashboard/integrations/cache_inspector.d.ts +88 -0
  15. package/dist/src/dashboard/integrations/cache_inspector.d.ts.map +1 -0
  16. package/dist/src/dashboard/integrations/cache_inspector.js +215 -0
  17. package/dist/src/dashboard/integrations/config_inspector.d.ts +33 -0
  18. package/dist/src/dashboard/integrations/config_inspector.d.ts.map +1 -0
  19. package/dist/src/dashboard/integrations/config_inspector.js +155 -0
  20. package/dist/src/dashboard/integrations/index.d.ts +7 -0
  21. package/dist/src/dashboard/integrations/index.d.ts.map +1 -0
  22. package/dist/src/dashboard/integrations/index.js +3 -0
  23. package/dist/src/dashboard/integrations/queue_inspector.d.ts +106 -0
  24. package/dist/src/dashboard/integrations/queue_inspector.d.ts.map +1 -0
  25. package/dist/src/dashboard/integrations/queue_inspector.js +182 -0
  26. package/dist/src/dashboard/migrator.d.ts +18 -0
  27. package/dist/src/dashboard/migrator.d.ts.map +1 -0
  28. package/dist/src/dashboard/migrator.js +144 -0
  29. package/dist/src/dashboard/models/stats_email.d.ts +19 -0
  30. package/dist/src/dashboard/models/stats_email.d.ts.map +1 -0
  31. package/dist/src/dashboard/models/stats_email.js +66 -0
  32. package/dist/src/dashboard/models/stats_event.d.ts +14 -0
  33. package/dist/src/dashboard/models/stats_event.d.ts.map +1 -0
  34. package/dist/src/dashboard/models/stats_event.js +43 -0
  35. package/dist/src/dashboard/models/stats_log.d.ts +12 -0
  36. package/dist/src/dashboard/models/stats_log.d.ts.map +1 -0
  37. package/dist/src/dashboard/models/stats_log.js +42 -0
  38. package/dist/src/dashboard/models/stats_metric.d.ts +15 -0
  39. package/dist/src/dashboard/models/stats_metric.d.ts.map +1 -0
  40. package/dist/src/dashboard/models/stats_metric.js +50 -0
  41. package/dist/src/dashboard/models/stats_query.d.ts +20 -0
  42. package/dist/src/dashboard/models/stats_query.d.ts.map +1 -0
  43. package/dist/src/dashboard/models/stats_query.js +67 -0
  44. package/dist/src/dashboard/models/stats_request.d.ts +21 -0
  45. package/dist/src/dashboard/models/stats_request.d.ts.map +1 -0
  46. package/dist/src/dashboard/models/stats_request.js +61 -0
  47. package/dist/src/dashboard/models/stats_saved_filter.d.ts +11 -0
  48. package/dist/src/dashboard/models/stats_saved_filter.d.ts.map +1 -0
  49. package/dist/src/dashboard/models/stats_saved_filter.js +38 -0
  50. package/dist/src/dashboard/models/stats_trace.d.ts +19 -0
  51. package/dist/src/dashboard/models/stats_trace.d.ts.map +1 -0
  52. package/dist/src/dashboard/models/stats_trace.js +67 -0
  53. package/dist/src/debug/debug_store.d.ts +5 -0
  54. package/dist/src/debug/debug_store.d.ts.map +1 -1
  55. package/dist/src/debug/debug_store.js +10 -0
  56. package/dist/src/debug/email_collector.d.ts +2 -0
  57. package/dist/src/debug/email_collector.d.ts.map +1 -1
  58. package/dist/src/debug/email_collector.js +4 -0
  59. package/dist/src/debug/event_collector.d.ts +2 -0
  60. package/dist/src/debug/event_collector.d.ts.map +1 -1
  61. package/dist/src/debug/event_collector.js +11 -2
  62. package/dist/src/debug/query_collector.d.ts +2 -0
  63. package/dist/src/debug/query_collector.d.ts.map +1 -1
  64. package/dist/src/debug/query_collector.js +11 -0
  65. package/dist/src/debug/ring_buffer.d.ts +3 -0
  66. package/dist/src/debug/ring_buffer.d.ts.map +1 -1
  67. package/dist/src/debug/ring_buffer.js +6 -0
  68. package/dist/src/debug/trace_collector.d.ts +4 -2
  69. package/dist/src/debug/trace_collector.d.ts.map +1 -1
  70. package/dist/src/debug/trace_collector.js +7 -2
  71. package/dist/src/debug/types.d.ts +8 -0
  72. package/dist/src/debug/types.d.ts.map +1 -1
  73. package/dist/src/edge/client/dashboard.css +1504 -0
  74. package/dist/src/edge/client/dashboard.js +2378 -0
  75. package/dist/src/edge/client/debug-panel.css +530 -110
  76. package/dist/src/edge/client/debug-panel.js +663 -22
  77. package/dist/src/edge/client/stats-bar.css +115 -41
  78. package/dist/src/edge/client/stats-bar.js +37 -3
  79. package/dist/src/edge/plugin.d.ts.map +1 -1
  80. package/dist/src/edge/plugin.js +21 -0
  81. package/dist/src/edge/views/dashboard.edge +382 -0
  82. package/dist/src/edge/views/debug-panel.edge +60 -14
  83. package/dist/src/edge/views/stats-bar.edge +9 -0
  84. package/dist/src/index.d.ts +2 -0
  85. package/dist/src/index.d.ts.map +1 -1
  86. package/dist/src/index.js +1 -0
  87. package/dist/src/middleware/request_tracking_middleware.d.ts +20 -0
  88. package/dist/src/middleware/request_tracking_middleware.d.ts.map +1 -1
  89. package/dist/src/middleware/request_tracking_middleware.js +66 -2
  90. package/dist/src/provider/server_stats_provider.d.ts +13 -0
  91. package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
  92. package/dist/src/provider/server_stats_provider.js +175 -1
  93. package/dist/src/types.d.ts +42 -0
  94. package/dist/src/types.d.ts.map +1 -1
  95. package/package.json +14 -1
@@ -1,5 +1,20 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
1
2
  import { performance } from "node:perf_hooks";
2
3
  import { getRequestMetrics } from "../collectors/http_collector.js";
4
+ /**
5
+ * AsyncLocalStorage that marks the current request as "excluded" from
6
+ * debug collection. Checked by QueryCollector and EventCollector to
7
+ * skip queries/events triggered by the debug panel's own polling.
8
+ */
9
+ const excludedRequestAls = new AsyncLocalStorage();
10
+ /**
11
+ * Returns true if the current async context is inside an excluded request
12
+ * (e.g. a debug panel polling request). Used by collectors to skip
13
+ * self-generated data.
14
+ */
15
+ export function isExcludedRequest() {
16
+ return excludedRequestAls.getStore() === true;
17
+ }
3
18
  /**
4
19
  * Module-level `shouldShow` callback, set by the provider at boot.
5
20
  */
@@ -14,8 +29,39 @@ let traceCollector = null;
14
29
  export function setTraceCollector(collector) {
15
30
  traceCollector = collector;
16
31
  }
32
+ /**
33
+ * Module-level dashboard path, set by the provider when dashboard is enabled.
34
+ * Requests to this path (and sub-paths) are excluded from metrics and tracing.
35
+ */
36
+ let dashboardPath = null;
37
+ export function setDashboardPath(path) {
38
+ dashboardPath = path;
39
+ }
40
+ /**
41
+ * Module-level list of URL prefixes to exclude from tracing and onRequestComplete.
42
+ * Used to filter out the debug panel's own polling requests (e.g. /admin/api/debug/,
43
+ * /admin/api/server-stats) so they don't flood the timeline.
44
+ */
45
+ let excludedPrefixes = [];
46
+ export function setExcludedPrefixes(prefixes) {
47
+ excludedPrefixes = prefixes;
48
+ }
49
+ /**
50
+ * Module-level callback fired after each request completes.
51
+ * Used by the provider to pipe request data to the DashboardStore.
52
+ */
53
+ let onRequestCompleteFn = null;
54
+ export function setOnRequestComplete(fn) {
55
+ onRequestCompleteFn = fn;
56
+ }
17
57
  export default class RequestTrackingMiddleware {
18
58
  async handle(ctx, next) {
59
+ // Self-exclude: skip metrics and tracing for dashboard routes
60
+ const requestUrl = ctx.request.url(true);
61
+ if (dashboardPath && requestUrl.startsWith(dashboardPath)) {
62
+ await next();
63
+ return;
64
+ }
19
65
  const metrics = getRequestMetrics();
20
66
  const start = performance.now();
21
67
  metrics.incrementActiveConnections();
@@ -36,6 +82,11 @@ export default class RequestTrackingMiddleware {
36
82
  },
37
83
  });
38
84
  }
85
+ // Skip tracing and dashboard persistence for the debug panel's own requests
86
+ // (e.g. /admin/api/debug/*, /admin/api/server-stats) so they don't flood
87
+ // the timeline. HTTP metrics (req/s, avg latency) are still recorded.
88
+ const skipTracing = excludedPrefixes.length > 0
89
+ && excludedPrefixes.some((prefix) => requestUrl.startsWith(prefix));
39
90
  const runRequest = async () => {
40
91
  try {
41
92
  await next();
@@ -44,12 +95,25 @@ export default class RequestTrackingMiddleware {
44
95
  const duration = performance.now() - start;
45
96
  metrics.decrementActiveConnections();
46
97
  metrics.recordRequest(duration, ctx.response.getStatus());
47
- traceCollector?.finishTrace(ctx.request.method(), ctx.request.url(true), ctx.response.getStatus());
98
+ if (!skipTracing) {
99
+ const traceRecord = traceCollector?.finishTrace(ctx.request.method(), ctx.request.url(true), ctx.response.getStatus());
100
+ onRequestCompleteFn?.({
101
+ method: ctx.request.method(),
102
+ url: ctx.request.url(true),
103
+ statusCode: ctx.response.getStatus(),
104
+ duration,
105
+ trace: traceRecord ?? undefined,
106
+ });
107
+ }
48
108
  }
49
109
  };
50
- if (traceCollector) {
110
+ if (traceCollector && !skipTracing) {
51
111
  await traceCollector.startTrace(runRequest);
52
112
  }
113
+ else if (skipTracing) {
114
+ // Run inside ALS so collectors can check isExcludedRequest()
115
+ await excludedRequestAls.run(true, runRequest);
116
+ }
53
117
  else {
54
118
  await runRequest();
55
119
  }
@@ -4,12 +4,25 @@ export default class ServerStatsProvider {
4
4
  private intervalId;
5
5
  private engine;
6
6
  private debugStore;
7
+ private dashboardStore;
8
+ private dashboardController;
9
+ private dashboardLogStream;
10
+ private dashboardBroadcastTimer;
11
+ private debugBroadcastTimer;
7
12
  private persistPath;
8
13
  private flushTimer;
9
14
  constructor(app: ApplicationService);
10
15
  boot(): Promise<void>;
11
16
  ready(): Promise<void>;
12
17
  private setupDevToolbar;
18
+ /**
19
+ * Initialize the full-page dashboard: SQLite store, controller,
20
+ * log piping, and per-request data persistence.
21
+ *
22
+ * Routes are already registered in boot() with a lazy controller getter.
23
+ * This method creates the controller so those routes become functional.
24
+ */
25
+ private setupDashboard;
13
26
  shutdown(): Promise<void>;
14
27
  }
15
28
  //# sourceMappingURL=server_stats_provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI/D,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAO1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAN7C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,UAAU,CAA+C;gBAE3C,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAoBJ,KAAK;YAsEG,eAAe;IAoDvB,QAAQ;CAuBf"}
1
+ {"version":3,"file":"server_stats_provider.d.ts","sourceRoot":"","sources":["../../../src/provider/server_stats_provider.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,CAAC,OAAO,OAAO,mBAAmB;IAY1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAX7C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,uBAAuB,CAA+C;IAC9E,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,UAAU,CAA+C;gBAE3C,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAuCJ,KAAK;YAoFG,eAAe;IAqF7B;;;;;;OAMG;YACW,cAAc;IA4GtB,QAAQ;CAwCf"}
@@ -1,11 +1,19 @@
1
1
  import { StatsEngine } from "../engine/stats_engine.js";
2
2
  import { DebugStore } from "../debug/debug_store.js";
3
- import { setShouldShow, setTraceCollector } from "../middleware/request_tracking_middleware.js";
3
+ import { DashboardStore } from "../dashboard/dashboard_store.js";
4
+ import { LogStreamService } from "../log_stream/log_stream_service.js";
5
+ import { setShouldShow, setTraceCollector, setDashboardPath, setExcludedPrefixes, setOnRequestComplete, } from "../middleware/request_tracking_middleware.js";
6
+ import { registerDashboardRoutes } from "../dashboard/dashboard_routes.js";
4
7
  export default class ServerStatsProvider {
5
8
  app;
6
9
  intervalId = null;
7
10
  engine = null;
8
11
  debugStore = null;
12
+ dashboardStore = null;
13
+ dashboardController = null;
14
+ dashboardLogStream = null;
15
+ dashboardBroadcastTimer = null;
16
+ debugBroadcastTimer = null;
9
17
  persistPath = null;
10
18
  flushTimer = null;
11
19
  constructor(app) {
@@ -19,6 +27,19 @@ export default class ServerStatsProvider {
19
27
  if (config.shouldShow) {
20
28
  setShouldShow(config.shouldShow);
21
29
  }
30
+ // Register dashboard routes early (before router commits).
31
+ // The controller is created later in ready() — routes use a lazy getter.
32
+ const toolbarConfig = config.devToolbar;
33
+ if (toolbarConfig?.enabled && toolbarConfig.dashboard && !this.app.inProduction) {
34
+ try {
35
+ const router = await this.app.container.make("router");
36
+ const dashPath = toolbarConfig.dashboardPath ?? '/__stats';
37
+ registerDashboardRoutes(router, dashPath, () => this.dashboardController, config.shouldShow);
38
+ }
39
+ catch {
40
+ // Router not available — skip route registration
41
+ }
42
+ }
22
43
  if (!this.app.usingEdgeJS)
23
44
  return;
24
45
  try {
@@ -52,7 +73,20 @@ export default class ServerStatsProvider {
52
73
  persistDebugData: toolbarConfig.persistDebugData ?? false,
53
74
  tracing: toolbarConfig.tracing ?? false,
54
75
  maxTraces: toolbarConfig.maxTraces ?? 200,
76
+ dashboard: toolbarConfig.dashboard ?? false,
77
+ dashboardPath: toolbarConfig.dashboardPath ?? '/__stats',
78
+ retentionDays: toolbarConfig.retentionDays ?? 7,
79
+ dbPath: toolbarConfig.dbPath ?? '.adonisjs/server-stats/dashboard.sqlite3',
55
80
  });
81
+ // Exclude the stats endpoint and user-specified prefixes from tracing
82
+ // so the debug panel's own polling doesn't flood the timeline
83
+ const prefixes = [...(toolbarConfig.excludeFromTracing ?? [])];
84
+ if (typeof config.endpoint === 'string') {
85
+ prefixes.push(config.endpoint);
86
+ }
87
+ if (prefixes.length > 0) {
88
+ setExcludedPrefixes(prefixes);
89
+ }
56
90
  }
57
91
  let transmit = null;
58
92
  if (config.transport === "transmit") {
@@ -130,6 +164,132 @@ export default class ServerStatsProvider {
130
164
  }
131
165
  }, 30_000);
132
166
  }
167
+ // ── Transmit broadcasting for debug panel live updates ────────
168
+ let debugTransmit = null;
169
+ try {
170
+ debugTransmit = await this.app.container.make("transmit");
171
+ }
172
+ catch {
173
+ // Transmit not installed — debug panel will use polling
174
+ }
175
+ if (debugTransmit) {
176
+ const debugChannel = "server-stats/debug";
177
+ const pendingTypes = new Set();
178
+ this.debugStore.onNewItem((type) => {
179
+ // Debounce: coalesce rapid events into a single broadcast
180
+ pendingTypes.add(type);
181
+ if (this.debugBroadcastTimer)
182
+ return;
183
+ this.debugBroadcastTimer = setTimeout(() => {
184
+ this.debugBroadcastTimer = null;
185
+ const types = Array.from(pendingTypes);
186
+ pendingTypes.clear();
187
+ try {
188
+ debugTransmit.broadcast(debugChannel, { types });
189
+ }
190
+ catch {
191
+ // Silently ignore broadcast errors
192
+ }
193
+ }, 200);
194
+ });
195
+ }
196
+ // Full-page dashboard setup (routes already registered in boot)
197
+ if (toolbarConfig.dashboard) {
198
+ await this.setupDashboard(toolbarConfig, emitter);
199
+ }
200
+ }
201
+ /**
202
+ * Initialize the full-page dashboard: SQLite store, controller,
203
+ * log piping, and per-request data persistence.
204
+ *
205
+ * Routes are already registered in boot() with a lazy controller getter.
206
+ * This method creates the controller so those routes become functional.
207
+ */
208
+ async setupDashboard(toolbarConfig, emitter) {
209
+ // Create and start the DashboardStore
210
+ this.dashboardStore = new DashboardStore(toolbarConfig);
211
+ const appRoot = this.app.makePath('');
212
+ try {
213
+ await this.dashboardStore.start(null, emitter, appRoot);
214
+ }
215
+ catch (err) {
216
+ const msg = err?.message || '';
217
+ if (msg.includes('better-sqlite3') || msg.includes('Cannot find module')) {
218
+ console.warn('[server-stats] Dashboard requires better-sqlite3. Install it with:\n' +
219
+ ' npm install better-sqlite3\n' +
220
+ 'Dashboard has been disabled for this session.');
221
+ this.dashboardStore = null;
222
+ return;
223
+ }
224
+ throw err;
225
+ }
226
+ // Bind to container
227
+ this.app.container.singleton("dashboard.store", () => this.dashboardStore);
228
+ // Set dashboard path in middleware for self-exclusion
229
+ setDashboardPath(toolbarConfig.dashboardPath);
230
+ // Create the controller — this makes the routes registered in boot() functional
231
+ const DashboardControllerClass = (await import("../dashboard/dashboard_controller.js")).default;
232
+ this.dashboardController = new DashboardControllerClass(this.dashboardStore, this.debugStore, this.app);
233
+ // ── Log piping ────────────────────────────────────────────────
234
+ const logPath = this.app.makePath("logs", "adonisjs.log");
235
+ this.dashboardLogStream = new LogStreamService(logPath, (entry) => {
236
+ this.dashboardStore?.recordLog(entry);
237
+ });
238
+ await this.dashboardLogStream.start();
239
+ // ── Per-request data piping ────────────────────────────────────
240
+ const debugStore = this.debugStore;
241
+ const dashStore = this.dashboardStore;
242
+ let lastQueryId = 0;
243
+ let lastEventId = 0;
244
+ setOnRequestComplete(({ method, url, statusCode, duration, trace }) => {
245
+ if (!dashStore.isReady())
246
+ return;
247
+ // Gather new queries since last request
248
+ const allQueries = debugStore.queries.getQueries();
249
+ const newQueries = allQueries.filter((q) => q.id > lastQueryId);
250
+ if (allQueries.length > 0) {
251
+ lastQueryId = allQueries[allQueries.length - 1].id;
252
+ }
253
+ // Gather new events since last request
254
+ const allEvents = debugStore.events.getEvents();
255
+ const newEvents = allEvents.filter((e) => e.id > lastEventId);
256
+ if (allEvents.length > 0) {
257
+ lastEventId = allEvents[allEvents.length - 1].id;
258
+ }
259
+ // Persist asynchronously (fire-and-forget)
260
+ dashStore
261
+ .persistRequest(method, url, statusCode, duration, newQueries, trace ?? null)
262
+ .then((requestId) => {
263
+ if (requestId !== null && newEvents.length > 0) {
264
+ return dashStore.recordEvents(requestId, newEvents);
265
+ }
266
+ })
267
+ .catch(() => {
268
+ // Silently ignore persistence errors
269
+ });
270
+ });
271
+ // ── Transmit streaming for real-time dashboard updates ────────
272
+ let transmit = null;
273
+ try {
274
+ transmit = await this.app.container.make("transmit");
275
+ }
276
+ catch {
277
+ // Transmit not installed — skip real-time updates
278
+ }
279
+ if (transmit) {
280
+ const dashChannel = "server-stats/dashboard";
281
+ this.dashboardBroadcastTimer = setInterval(async () => {
282
+ try {
283
+ if (!dashStore.isReady())
284
+ return;
285
+ const overview = await dashStore.getOverviewMetrics("1h");
286
+ transmit.broadcast(dashChannel, overview);
287
+ }
288
+ catch {
289
+ // Silently ignore
290
+ }
291
+ }, 5_000);
292
+ }
133
293
  }
134
294
  async shutdown() {
135
295
  if (this.intervalId) {
@@ -140,6 +300,14 @@ export default class ServerStatsProvider {
140
300
  clearInterval(this.flushTimer);
141
301
  this.flushTimer = null;
142
302
  }
303
+ if (this.dashboardBroadcastTimer) {
304
+ clearInterval(this.dashboardBroadcastTimer);
305
+ this.dashboardBroadcastTimer = null;
306
+ }
307
+ if (this.debugBroadcastTimer) {
308
+ clearTimeout(this.debugBroadcastTimer);
309
+ this.debugBroadcastTimer = null;
310
+ }
143
311
  // Save debug data before stopping collectors
144
312
  if (this.persistPath && this.debugStore) {
145
313
  try {
@@ -149,6 +317,12 @@ export default class ServerStatsProvider {
149
317
  // Silently ignore save errors during shutdown
150
318
  }
151
319
  }
320
+ // Clean up dashboard resources
321
+ this.dashboardLogStream?.stop();
322
+ setOnRequestComplete(null);
323
+ setDashboardPath(null);
324
+ setExcludedPrefixes([]);
325
+ await this.dashboardStore?.stop();
152
326
  this.debugStore?.stop();
153
327
  await this.engine?.stop();
154
328
  }
@@ -245,6 +245,48 @@ export interface DevToolbarOptions {
245
245
  * @default 200
246
246
  */
247
247
  maxTraces?: number;
248
+ /**
249
+ * Enable the full-page dashboard.
250
+ * Serves a standalone HTML page at `dashboardPath`.
251
+ * @default false
252
+ */
253
+ dashboard?: boolean;
254
+ /**
255
+ * URL path for the full-page dashboard.
256
+ * Must start with `/`.
257
+ * @default '/__stats'
258
+ */
259
+ dashboardPath?: string;
260
+ /**
261
+ * Data retention period in days for historical persistence.
262
+ * Records older than this are auto-pruned on startup and periodically.
263
+ * @default 7
264
+ */
265
+ retentionDays?: number;
266
+ /**
267
+ * Path to the SQLite database file for historical persistence.
268
+ * Relative to app root.
269
+ * @default '.adonisjs/server-stats/dashboard.sqlite3'
270
+ */
271
+ dbPath?: string;
272
+ /**
273
+ * URL prefixes to exclude from tracing and dashboard persistence.
274
+ *
275
+ * Requests matching these prefixes will still count toward HTTP
276
+ * metrics (req/s, latency) but won't appear in the timeline or
277
+ * be persisted to the dashboard.
278
+ *
279
+ * The stats endpoint (`config.endpoint`) is always excluded
280
+ * automatically when tracing is enabled.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * excludeFromTracing: ['/admin/api/debug']
285
+ * ```
286
+ *
287
+ * @default []
288
+ */
289
+ excludeFromTracing?: string[];
248
290
  }
249
291
  /**
250
292
  * Top-level configuration for `adonisjs-server-stats`.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adonisjs-server-stats",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Real-time server monitoring for AdonisJS v6 applications",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -58,6 +58,14 @@
58
58
  "types": "./dist/src/edge/plugin.d.ts",
59
59
  "import": "./dist/src/edge/plugin.js"
60
60
  },
61
+ "./dashboard": {
62
+ "types": "./dist/src/dashboard/dashboard_store.d.ts",
63
+ "import": "./dist/src/dashboard/dashboard_store.js"
64
+ },
65
+ "./dashboard/controller": {
66
+ "types": "./dist/src/dashboard/dashboard_controller.d.ts",
67
+ "import": "./dist/src/dashboard/dashboard_controller.js"
68
+ },
61
69
  "./configure": {
62
70
  "types": "./dist/configure.d.ts",
63
71
  "import": "./dist/configure.js"
@@ -106,6 +114,9 @@
106
114
  },
107
115
  "edge.js": {
108
116
  "optional": true
117
+ },
118
+ "better-sqlite3": {
119
+ "optional": true
109
120
  }
110
121
  },
111
122
  "devDependencies": {
@@ -114,7 +125,9 @@
114
125
  "@adonisjs/redis": "^9.0.0",
115
126
  "@adonisjs/transmit": "^1.0.0",
116
127
  "@julr/adonisjs-prometheus": "^1.4.0",
128
+ "@types/better-sqlite3": "^7.0.0",
117
129
  "@types/node": "^22.0.0",
130
+ "better-sqlite3": "^11.0.0",
118
131
  "bullmq": "^5.0.0",
119
132
  "edge.js": "^6.0.0",
120
133
  "typescript": "^5.9.0"