adonisjs-server-stats 1.0.10 → 1.1.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.
Files changed (32) hide show
  1. package/README.md +27 -9
  2. package/dist/src/controller/debug_controller.d.ts +2 -0
  3. package/dist/src/controller/debug_controller.d.ts.map +1 -1
  4. package/dist/src/controller/debug_controller.js +14 -0
  5. package/dist/src/debug/debug_store.d.ts +6 -0
  6. package/dist/src/debug/debug_store.d.ts.map +1 -1
  7. package/dist/src/debug/debug_store.js +40 -0
  8. package/dist/src/debug/email_collector.d.ts +36 -0
  9. package/dist/src/debug/email_collector.d.ts.map +1 -0
  10. package/dist/src/debug/email_collector.js +138 -0
  11. package/dist/src/debug/event_collector.d.ts +2 -0
  12. package/dist/src/debug/event_collector.d.ts.map +1 -1
  13. package/dist/src/debug/event_collector.js +11 -2
  14. package/dist/src/debug/query_collector.d.ts +2 -0
  15. package/dist/src/debug/query_collector.d.ts.map +1 -1
  16. package/dist/src/debug/query_collector.js +6 -0
  17. package/dist/src/debug/ring_buffer.d.ts +4 -0
  18. package/dist/src/debug/ring_buffer.d.ts.map +1 -1
  19. package/dist/src/debug/ring_buffer.js +10 -0
  20. package/dist/src/debug/types.d.ts +38 -0
  21. package/dist/src/debug/types.d.ts.map +1 -1
  22. package/dist/src/edge/client/debug-panel.css +50 -0
  23. package/dist/src/edge/client/debug-panel.js +117 -0
  24. package/dist/src/edge/views/debug-panel.edge +16 -0
  25. package/dist/src/index.d.ts +1 -1
  26. package/dist/src/index.d.ts.map +1 -1
  27. package/dist/src/provider/server_stats_provider.d.ts +2 -0
  28. package/dist/src/provider/server_stats_provider.d.ts.map +1 -1
  29. package/dist/src/provider/server_stats_provider.js +33 -0
  30. package/dist/src/types.d.ts +12 -0
  31. package/dist/src/types.d.ts.map +1 -1
  32. package/package.json +2 -2
package/README.md CHANGED
@@ -34,7 +34,7 @@ Zero frontend dependencies. Zero build step. Just `@serverStats()` and go.
34
34
  ## Features
35
35
 
36
36
  - **Live stats bar** -- CPU, memory, event loop lag, HTTP throughput, DB pool, Redis, queues, logs
37
- - **Debug toolbar** -- SQL queries, events, routes, logs with search and filtering
37
+ - **Debug toolbar** -- SQL queries, events, emails, routes, logs with search and filtering
38
38
  - **Custom panes** -- add your own tabs (webhooks, emails, cache, anything) with a simple config
39
39
  - **Pluggable collectors** -- use built-in collectors or write your own
40
40
  - **Visibility control** -- show only to admins, specific roles, or in dev mode
@@ -223,13 +223,15 @@ export default class ServerStatsController {
223
223
 
224
224
  ### `DevToolbarOptions`
225
225
 
226
- | Option | Type | Default | Description |
227
- |------------------------|-----------------|---------|------------------------------------|
228
- | `enabled` | `boolean` | `false` | Enable the dev toolbar |
229
- | `maxQueries` | `number` | `500` | Max SQL queries to buffer |
230
- | `maxEvents` | `number` | `200` | Max events to buffer |
231
- | `slowQueryThresholdMs` | `number` | `100` | Slow query threshold (ms) |
232
- | `panes` | `DebugPane[]` | -- | Custom debug panel tabs |
226
+ | Option | Type | Default | Description |
227
+ |------------------------|-----------------|---------|------------------------------------------------|
228
+ | `enabled` | `boolean` | `false` | Enable the dev toolbar |
229
+ | `maxQueries` | `number` | `500` | Max SQL queries to buffer |
230
+ | `maxEvents` | `number` | `200` | Max events to buffer |
231
+ | `maxEmails` | `number` | `100` | Max emails to buffer |
232
+ | `slowQueryThresholdMs` | `number` | `100` | Slow query threshold (ms) |
233
+ | `persistDebugData` | `boolean` | `false` | Persist debug data to disk across restarts |
234
+ | `panes` | `DebugPane[]` | -- | Custom debug panel tabs |
233
235
 
234
236
  ---
235
237
 
@@ -378,7 +380,7 @@ Features:
378
380
 
379
381
  ## Dev Toolbar
380
382
 
381
- Adds a debug panel with SQL query inspection, event tracking, route table, and live logs. Only active in non-production environments.
383
+ Adds a debug panel with SQL query inspection, event tracking, email capture with HTML preview, route table, and live logs. Only active in non-production environments.
382
384
 
383
385
  ```ts
384
386
  export default defineConfig({
@@ -386,7 +388,9 @@ export default defineConfig({
386
388
  enabled: true,
387
389
  maxQueries: 500,
388
390
  maxEvents: 200,
391
+ maxEmails: 100,
389
392
  slowQueryThresholdMs: 100,
393
+ persistDebugData: true, // survive server restarts
390
394
  },
391
395
  })
392
396
  ```
@@ -400,11 +404,24 @@ router
400
404
  router.get('queries', '#controllers/admin/debug_controller.queries')
401
405
  router.get('events', '#controllers/admin/debug_controller.events')
402
406
  router.get('routes', '#controllers/admin/debug_controller.routes')
407
+ router.get('emails', '#controllers/admin/debug_controller.emails')
408
+ router.get('emails/:id/preview', '#controllers/admin/debug_controller.emailPreview')
403
409
  })
404
410
  .prefix('/admin/api/debug')
405
411
  .use(middleware.admin())
406
412
  ```
407
413
 
414
+ ### Built-in Emails Tab
415
+
416
+ The debug toolbar captures all emails sent via AdonisJS mail (`mail:sending`, `mail:sent`, `mail:queued`, `queued:mail:error` events). Click any email row to preview its HTML in an iframe.
417
+
418
+ ### Persistent Debug Data
419
+
420
+ Enable `persistDebugData: true` to save queries, events, and emails to `tmp/debug-data.json`. Data is:
421
+ - **Loaded** on server startup (before collectors start)
422
+ - **Flushed** every 30 seconds (handles crashes)
423
+ - **Saved** on graceful shutdown
424
+
408
425
  ### Custom Debug Panes
409
426
 
410
427
  Add custom tabs to the debug panel:
@@ -562,6 +579,7 @@ import type {
562
579
  BadgeColor,
563
580
  QueryRecord,
564
581
  EventRecord,
582
+ EmailRecord,
565
583
  RouteRecord,
566
584
  } from 'adonisjs-server-stats'
567
585
 
@@ -6,5 +6,7 @@ export default class DebugController {
6
6
  queries({ response }: HttpContext): Promise<void>;
7
7
  events({ response }: HttpContext): Promise<void>;
8
8
  routes({ response }: HttpContext): Promise<void>;
9
+ emails({ response }: HttpContext): Promise<void>;
10
+ emailPreview({ params, response }: HttpContext): Promise<void>;
9
11
  }
10
12
  //# sourceMappingURL=debug_controller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"debug_controller.d.ts","sourceRoot":"","sources":["../../../src/controller/debug_controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,CAAC,OAAO,OAAO,eAAe;IACtB,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,UAAU;IAE/B,OAAO,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAMjC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAKhC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;CAIvC"}
1
+ {"version":3,"file":"debug_controller.d.ts","sourceRoot":"","sources":["../../../src/controller/debug_controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,CAAC,OAAO,OAAO,eAAe;IACtB,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,UAAU;IAE/B,OAAO,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAMjC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAKhC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAKhC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW;IAOhC,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,WAAW;CAQrD"}
@@ -16,4 +16,18 @@ export default class DebugController {
16
16
  const routes = this.store.routes.getRoutes();
17
17
  return response.json({ routes, total: this.store.routes.getRouteCount() });
18
18
  }
19
+ async emails({ response }) {
20
+ const emails = this.store.emails.getLatest(100);
21
+ // Strip html/text from list response to keep it lightweight
22
+ const stripped = emails.map(({ html, text, ...rest }) => rest);
23
+ return response.json({ emails: stripped, total: this.store.emails.getTotalCount() });
24
+ }
25
+ async emailPreview({ params, response }) {
26
+ const id = Number(params.id);
27
+ const html = this.store.emails.getEmailHtml(id);
28
+ if (!html) {
29
+ return response.notFound({ error: 'Email not found' });
30
+ }
31
+ return response.header('Content-Type', 'text/html; charset=utf-8').send(html);
32
+ }
19
33
  }
@@ -1,5 +1,6 @@
1
1
  import { QueryCollector } from "./query_collector.js";
2
2
  import { EventCollector } from "./event_collector.js";
3
+ import { EmailCollector } from "./email_collector.js";
3
4
  import { RouteInspector } from "./route_inspector.js";
4
5
  import type { DevToolbarConfig } from "./types.js";
5
6
  /**
@@ -9,9 +10,14 @@ import type { DevToolbarConfig } from "./types.js";
9
10
  export declare class DebugStore {
10
11
  readonly queries: QueryCollector;
11
12
  readonly events: EventCollector;
13
+ readonly emails: EmailCollector;
12
14
  readonly routes: RouteInspector;
13
15
  constructor(config: DevToolbarConfig);
14
16
  start(emitter: any, router: any): Promise<void>;
15
17
  stop(): void;
18
+ /** Serialize all collector data to a JSON file (atomic write). */
19
+ saveToDisk(filePath: string): Promise<void>;
20
+ /** Restore collector data from a JSON file on disk. */
21
+ loadFromDisk(filePath: string): Promise<void>;
16
22
  }
17
23
  //# sourceMappingURL=debug_store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"debug_store.d.ts","sourceRoot":"","sources":["../../../src/debug/debug_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;GAGG;AACH,qBAAa,UAAU;IACrB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;gBAEpB,MAAM,EAAE,gBAAgB;IAS9B,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,IAAI,IAAI,IAAI;CAIb"}
1
+ {"version":3,"file":"debug_store.d.ts","sourceRoot":"","sources":["../../../src/debug/debug_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;GAGG;AACH,qBAAa,UAAU;IACrB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;gBAEpB,MAAM,EAAE,gBAAgB;IAU9B,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,IAAI,IAAI,IAAI;IAMZ,kEAAkE;IAC5D,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajD,uDAAuD;IACjD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBpD"}
@@ -1,5 +1,8 @@
1
+ import { writeFile, readFile, rename, mkdir } from "node:fs/promises";
2
+ import { dirname } from "node:path";
1
3
  import { QueryCollector } from "./query_collector.js";
2
4
  import { EventCollector } from "./event_collector.js";
5
+ import { EmailCollector } from "./email_collector.js";
3
6
  import { RouteInspector } from "./route_inspector.js";
4
7
  /**
5
8
  * Singleton store holding all debug data collectors.
@@ -8,19 +11,56 @@ import { RouteInspector } from "./route_inspector.js";
8
11
  export class DebugStore {
9
12
  queries;
10
13
  events;
14
+ emails;
11
15
  routes;
12
16
  constructor(config) {
13
17
  this.queries = new QueryCollector(config.maxQueries, config.slowQueryThresholdMs);
14
18
  this.events = new EventCollector(config.maxEvents);
19
+ this.emails = new EmailCollector(config.maxEmails);
15
20
  this.routes = new RouteInspector();
16
21
  }
17
22
  async start(emitter, router) {
18
23
  await this.queries.start(emitter);
19
24
  this.events.start(emitter);
25
+ await this.emails.start(emitter);
20
26
  this.routes.inspect(router);
21
27
  }
22
28
  stop() {
23
29
  this.queries.stop();
24
30
  this.events.stop();
31
+ this.emails.stop();
32
+ }
33
+ /** Serialize all collector data to a JSON file (atomic write). */
34
+ async saveToDisk(filePath) {
35
+ const data = {
36
+ queries: this.queries.getQueries(),
37
+ events: this.events.getEvents(),
38
+ emails: this.emails.getEmails(),
39
+ };
40
+ const json = JSON.stringify(data);
41
+ const tmpPath = filePath + ".tmp";
42
+ await mkdir(dirname(filePath), { recursive: true });
43
+ await writeFile(tmpPath, json, "utf-8");
44
+ await rename(tmpPath, filePath);
45
+ }
46
+ /** Restore collector data from a JSON file on disk. */
47
+ async loadFromDisk(filePath) {
48
+ let raw;
49
+ try {
50
+ raw = await readFile(filePath, "utf-8");
51
+ }
52
+ catch {
53
+ return; // file doesn't exist yet
54
+ }
55
+ const data = JSON.parse(raw);
56
+ if (Array.isArray(data.queries) && data.queries.length > 0) {
57
+ this.queries.loadRecords(data.queries);
58
+ }
59
+ if (Array.isArray(data.events) && data.events.length > 0) {
60
+ this.events.loadRecords(data.events);
61
+ }
62
+ if (Array.isArray(data.emails) && data.emails.length > 0) {
63
+ this.emails.loadRecords(data.emails);
64
+ }
25
65
  }
26
66
  }
@@ -0,0 +1,36 @@
1
+ import type { EmailRecord } from './types.js';
2
+ /**
3
+ * Listens to AdonisJS mail events and stores captured emails in a ring buffer.
4
+ *
5
+ * Events:
6
+ * - `mail:sending` — email about to be sent
7
+ * - `mail:sent` — email successfully sent (updates matching 'sending' record)
8
+ * - `mail:queued` — email queued for later delivery
9
+ * - `queued:mail:error` — queued email failed
10
+ */
11
+ export declare class EmailCollector {
12
+ private buffer;
13
+ private emitter;
14
+ private handlers;
15
+ constructor(maxEmails?: number);
16
+ start(emitter: any): Promise<void>;
17
+ stop(): void;
18
+ getEmails(): EmailRecord[];
19
+ getLatest(n?: number): EmailRecord[];
20
+ getEmailHtml(id: number): string | null;
21
+ getTotalCount(): number;
22
+ clear(): void;
23
+ private buildRecord;
24
+ /**
25
+ * Normalize various address formats to a comma-separated string.
26
+ *
27
+ * AdonisJS mail addresses can be:
28
+ * - A string: `"user@example.com"`
29
+ * - An object: `{ address: "user@example.com", name: "User" }`
30
+ * - An array of strings or objects
31
+ */
32
+ /** Restore persisted records into the buffer and reset the ID counter. */
33
+ loadRecords(records: EmailRecord[]): void;
34
+ private extractAddresses;
35
+ }
36
+ //# sourceMappingURL=email_collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email_collector.d.ts","sourceRoot":"","sources":["../../../src/debug/email_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C;;;;;;;;GAQG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,QAAQ,CAAmD;gBAEvD,SAAS,GAAE,MAAY;IAI7B,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDxC,IAAI,IAAI,IAAI;IAUZ,SAAS,IAAI,WAAW,EAAE;IAI1B,SAAS,CAAC,CAAC,GAAE,MAAY,GAAG,WAAW,EAAE;IAIzC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMvC,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,WAAW;IAsBnB;;;;;;;OAOG;IACH,0EAA0E;IAC1E,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAMzC,OAAO,CAAC,gBAAgB;CAYzB"}
@@ -0,0 +1,138 @@
1
+ import { RingBuffer } from './ring_buffer.js';
2
+ /**
3
+ * Listens to AdonisJS mail events and stores captured emails in a ring buffer.
4
+ *
5
+ * Events:
6
+ * - `mail:sending` — email about to be sent
7
+ * - `mail:sent` — email successfully sent (updates matching 'sending' record)
8
+ * - `mail:queued` — email queued for later delivery
9
+ * - `queued:mail:error` — queued email failed
10
+ */
11
+ export class EmailCollector {
12
+ buffer;
13
+ emitter = null;
14
+ handlers = [];
15
+ constructor(maxEmails = 100) {
16
+ this.buffer = new RingBuffer(maxEmails);
17
+ }
18
+ async start(emitter) {
19
+ if (!emitter || typeof emitter.on !== 'function')
20
+ return;
21
+ this.emitter = emitter;
22
+ const onSending = (data) => {
23
+ const msg = data?.message || data;
24
+ const record = this.buildRecord(msg, 'sending', data);
25
+ this.buffer.push(record);
26
+ };
27
+ const onSent = (data) => {
28
+ const msg = data?.message || data;
29
+ const to = this.extractAddresses(msg?.to);
30
+ const subject = msg?.subject || '';
31
+ // Try to find the matching 'sending' record and update it
32
+ const all = this.buffer.toArray();
33
+ for (let i = all.length - 1; i >= 0; i--) {
34
+ const rec = all[i];
35
+ if (rec.status === 'sending' && rec.to === to && rec.subject === subject) {
36
+ rec.status = 'sent';
37
+ rec.messageId = data?.response?.messageId || data?.messageId || null;
38
+ return;
39
+ }
40
+ }
41
+ // No matching 'sending' record — insert a new 'sent' record
42
+ const record = this.buildRecord(msg, 'sent', data);
43
+ record.messageId = data?.response?.messageId || data?.messageId || null;
44
+ this.buffer.push(record);
45
+ };
46
+ const onQueued = (data) => {
47
+ const msg = data?.message || data;
48
+ const record = this.buildRecord(msg, 'queued', data);
49
+ this.buffer.push(record);
50
+ };
51
+ const onQueuedError = (data) => {
52
+ const msg = data?.message || data;
53
+ const record = this.buildRecord(msg, 'failed', data);
54
+ this.buffer.push(record);
55
+ };
56
+ this.handlers = [
57
+ { event: 'mail:sending', fn: onSending },
58
+ { event: 'mail:sent', fn: onSent },
59
+ { event: 'mail:queued', fn: onQueued },
60
+ { event: 'queued:mail:error', fn: onQueuedError },
61
+ ];
62
+ for (const h of this.handlers) {
63
+ emitter.on(h.event, h.fn);
64
+ }
65
+ }
66
+ stop() {
67
+ if (this.emitter && typeof this.emitter.off === 'function') {
68
+ for (const h of this.handlers) {
69
+ this.emitter.off(h.event, h.fn);
70
+ }
71
+ }
72
+ this.handlers = [];
73
+ this.emitter = null;
74
+ }
75
+ getEmails() {
76
+ return this.buffer.toArray();
77
+ }
78
+ getLatest(n = 100) {
79
+ return this.buffer.latest(n);
80
+ }
81
+ getEmailHtml(id) {
82
+ const all = this.buffer.toArray();
83
+ const record = all.find((r) => r.id === id);
84
+ return record?.html ?? null;
85
+ }
86
+ getTotalCount() {
87
+ return this.buffer.size();
88
+ }
89
+ clear() {
90
+ this.buffer.clear();
91
+ }
92
+ buildRecord(msg, status, data) {
93
+ return {
94
+ id: this.buffer.getNextId(),
95
+ from: this.extractAddresses(msg?.from) || 'unknown',
96
+ to: this.extractAddresses(msg?.to) || 'unknown',
97
+ cc: this.extractAddresses(msg?.cc) || null,
98
+ bcc: this.extractAddresses(msg?.bcc) || null,
99
+ subject: msg?.subject || '(no subject)',
100
+ html: msg?.html || null,
101
+ text: msg?.text || null,
102
+ mailer: data?.mailerName || data?.mailer || 'unknown',
103
+ status,
104
+ messageId: null,
105
+ attachmentCount: Array.isArray(msg?.attachments) ? msg.attachments.length : 0,
106
+ timestamp: Date.now(),
107
+ };
108
+ }
109
+ /**
110
+ * Normalize various address formats to a comma-separated string.
111
+ *
112
+ * AdonisJS mail addresses can be:
113
+ * - A string: `"user@example.com"`
114
+ * - An object: `{ address: "user@example.com", name: "User" }`
115
+ * - An array of strings or objects
116
+ */
117
+ /** Restore persisted records into the buffer and reset the ID counter. */
118
+ loadRecords(records) {
119
+ this.buffer.load(records);
120
+ const maxId = records.reduce((m, r) => Math.max(m, r.id), 0);
121
+ this.buffer.setNextId(maxId + 1);
122
+ }
123
+ extractAddresses(value) {
124
+ if (!value)
125
+ return '';
126
+ if (typeof value === 'string')
127
+ return value;
128
+ if (Array.isArray(value)) {
129
+ return value
130
+ .map((v) => (typeof v === 'string' ? v : v?.address || ''))
131
+ .filter(Boolean)
132
+ .join(', ');
133
+ }
134
+ if (typeof value === 'object' && value.address)
135
+ return value.address;
136
+ return '';
137
+ }
138
+ }
@@ -16,5 +16,7 @@ export declare class EventCollector {
16
16
  getLatest(n?: number): EventRecord[];
17
17
  getTotalCount(): number;
18
18
  clear(): void;
19
+ /** Restore persisted records into the buffer and reset the ID counter. */
20
+ loadRecords(records: EventRecord[]): void;
19
21
  }
20
22
  //# sourceMappingURL=event_collector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event_collector.d.ts","sourceRoot":"","sources":["../../../src/debug/event_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,OAAO,CAAa;gBAEhB,SAAS,GAAE,MAAY;IAInC,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IA0BzB,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,YAAY;IAapB,SAAS,IAAI,WAAW,EAAE;IAI1B,SAAS,CAAC,CAAC,GAAE,MAAY,GAAG,WAAW,EAAE;IAIzC,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"event_collector.d.ts","sourceRoot":"","sources":["../../../src/debug/event_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,OAAO,CAAa;gBAEhB,SAAS,GAAE,MAAY;IAInC,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IA+BzB,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,YAAY;IAapB,SAAS,IAAI,WAAW,EAAE;IAI1B,SAAS,CAAC,CAAC,GAAE,MAAY,GAAG,WAAW,EAAE;IAIzC,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;IAIb,0EAA0E;IAC1E,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;CAK1C"}
@@ -19,8 +19,11 @@ export class EventCollector {
19
19
  emitter.emit = function (event, data) {
20
20
  // Resolve event name: class-based events use the class name, string events are used as-is
21
21
  const eventName = typeof event === "string" ? event : event?.name || "unknown";
22
- // Skip internal/noisy events
23
- if (!eventName.startsWith("__") && eventName !== "db:query") {
22
+ // Skip internal/noisy events and mail events (handled by EmailCollector)
23
+ if (!eventName.startsWith("__") &&
24
+ eventName !== "db:query" &&
25
+ !eventName.startsWith("mail:") &&
26
+ eventName !== "queued:mail:error") {
24
27
  const record = {
25
28
  id: self.buffer.getNextId(),
26
29
  event: eventName,
@@ -83,4 +86,10 @@ export class EventCollector {
83
86
  clear() {
84
87
  this.buffer.clear();
85
88
  }
89
+ /** Restore persisted records into the buffer and reset the ID counter. */
90
+ loadRecords(records) {
91
+ this.buffer.load(records);
92
+ const maxId = records.reduce((m, r) => Math.max(m, r.id), 0);
93
+ this.buffer.setNextId(maxId + 1);
94
+ }
86
95
  }
@@ -23,5 +23,7 @@ export declare class QueryCollector {
23
23
  };
24
24
  getTotalCount(): number;
25
25
  clear(): void;
26
+ /** Restore persisted records into the buffer and reset the ID counter. */
27
+ loadRecords(records: QueryRecord[]): void;
26
28
  }
27
29
  //# sourceMappingURL=query_collector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query_collector.d.ts","sourceRoot":"","sources":["../../../src/debug/query_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAsC;gBAEzC,UAAU,GAAE,MAAY,EAAE,eAAe,GAAE,MAAY;IAK7D,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxC,IAAI,IAAI,IAAI;IAYZ,UAAU,IAAI,WAAW,EAAE;IAI3B,SAAS,CAAC,CAAC,GAAE,MAAY,GAAG,WAAW,EAAE;IAIzC,UAAU;;;;;;IA0BV,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"query_collector.d.ts","sourceRoot":"","sources":["../../../src/debug/query_collector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAsC;gBAEzC,UAAU,GAAE,MAAY,EAAE,eAAe,GAAE,MAAY;IAK7D,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BxC,IAAI,IAAI,IAAI;IAYZ,UAAU,IAAI,WAAW,EAAE;IAI3B,SAAS,CAAC,CAAC,GAAE,MAAY,GAAG,WAAW,EAAE;IAIzC,UAAU;;;;;;IA0BV,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;IAIb,0EAA0E;IAC1E,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;CAK1C"}
@@ -77,4 +77,10 @@ export class QueryCollector {
77
77
  clear() {
78
78
  this.buffer.clear();
79
79
  }
80
+ /** Restore persisted records into the buffer and reset the ID counter. */
81
+ loadRecords(records) {
82
+ this.buffer.load(records);
83
+ const maxId = records.reduce((m, r) => Math.max(m, r.id), 0);
84
+ this.buffer.setNextId(maxId + 1);
85
+ }
80
86
  }
@@ -17,5 +17,9 @@ export declare class RingBuffer<T> {
17
17
  getNextId(): number;
18
18
  size(): number;
19
19
  clear(): void;
20
+ /** Bulk-load items (e.g. from disk). Pushes each in order, respecting capacity. */
21
+ load(items: T[]): void;
22
+ /** Restore the auto-increment counter (e.g. after loading persisted data). */
23
+ setNextId(id: number): void;
20
24
  }
21
25
  //# sourceMappingURL=ring_buffer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ring_buffer.d.ts","sourceRoot":"","sources":["../../../src/debug/ring_buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAU,CAAC,CAAC;IAMX,OAAO,CAAC,QAAQ;IAL5B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,MAAM,CAAa;gBAEP,QAAQ,EAAE,MAAM;IAIpC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAQnB,2DAA2D;IAC3D,OAAO,IAAI,CAAC,EAAE;IAcd,sDAAsD;IACtD,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE;IAKtB,SAAS,IAAI,MAAM;IAInB,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;CAKd"}
1
+ {"version":3,"file":"ring_buffer.d.ts","sourceRoot":"","sources":["../../../src/debug/ring_buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAU,CAAC,CAAC;IAMX,OAAO,CAAC,QAAQ;IAL5B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,MAAM,CAAa;gBAEP,QAAQ,EAAE,MAAM;IAIpC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAQnB,2DAA2D;IAC3D,OAAO,IAAI,CAAC,EAAE;IAcd,sDAAsD;IACtD,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE;IAKtB,SAAS,IAAI,MAAM;IAInB,IAAI,IAAI,MAAM;IAId,KAAK,IAAI,IAAI;IAMb,mFAAmF;IACnF,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAMtB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAG5B"}
@@ -47,4 +47,14 @@ export class RingBuffer {
47
47
  this.head = 0;
48
48
  this.count = 0;
49
49
  }
50
+ /** Bulk-load items (e.g. from disk). Pushes each in order, respecting capacity. */
51
+ load(items) {
52
+ for (const item of items) {
53
+ this.push(item);
54
+ }
55
+ }
56
+ /** Restore the auto-increment counter (e.g. after loading persisted data). */
57
+ setNextId(id) {
58
+ this.nextId = id;
59
+ }
50
60
  }
@@ -40,6 +40,40 @@ export interface EventRecord {
40
40
  /** Unix timestamp in **milliseconds** when the event was emitted. */
41
41
  timestamp: number;
42
42
  }
43
+ /**
44
+ * A captured email sent via AdonisJS mail.
45
+ *
46
+ * Stored in a {@link RingBuffer} by the {@link EmailCollector} and
47
+ * served via the debug API endpoint.
48
+ */
49
+ export interface EmailRecord {
50
+ /** Auto-incrementing sequence number. */
51
+ id: number;
52
+ /** Sender address (e.g. `"noreply@example.com"`). */
53
+ from: string;
54
+ /** Comma-separated recipient addresses. */
55
+ to: string;
56
+ /** CC recipients, or `null` if none. */
57
+ cc: string | null;
58
+ /** BCC recipients, or `null` if none. */
59
+ bcc: string | null;
60
+ /** Email subject line. */
61
+ subject: string;
62
+ /** Full HTML body for iframe preview, or `null`. */
63
+ html: string | null;
64
+ /** Plain-text body, or `null`. */
65
+ text: string | null;
66
+ /** Mailer name (e.g. `"smtp"`, `"ses"`). */
67
+ mailer: string;
68
+ /** Current delivery status. */
69
+ status: 'sending' | 'sent' | 'queued' | 'failed';
70
+ /** Message ID from the mail transport response, or `null`. */
71
+ messageId: string | null;
72
+ /** Number of file attachments. */
73
+ attachmentCount: number;
74
+ /** Unix timestamp in **milliseconds** when the email was captured. */
75
+ timestamp: number;
76
+ }
43
77
  /**
44
78
  * A registered route extracted from the AdonisJS router.
45
79
  *
@@ -75,8 +109,12 @@ export interface DevToolbarConfig {
75
109
  maxQueries: number;
76
110
  /** Maximum events to buffer. */
77
111
  maxEvents: number;
112
+ /** Maximum emails to buffer. */
113
+ maxEmails: number;
78
114
  /** Slow query highlight threshold in **milliseconds**. */
79
115
  slowQueryThresholdMs: number;
116
+ /** Whether to persist debug data to disk across restarts. */
117
+ persistDebugData: boolean;
80
118
  }
81
119
  /**
82
120
  * Color names available for the `badge` column format.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/debug/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/debug/types.ts"],"names":[],"mappings":"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"}
@@ -310,6 +310,56 @@
310
310
  .ss-dbg-badge-purple { background: #2e1065; color: #c084fc; }
311
311
  .ss-dbg-badge-muted { background: #262626; color: #737373; }
312
312
 
313
+ /* Email preview overlay */
314
+ .ss-dbg-email-preview {
315
+ position: absolute;
316
+ inset: 0;
317
+ display: flex;
318
+ flex-direction: column;
319
+ background: #0f0f0f;
320
+ z-index: 10;
321
+ }
322
+ .ss-dbg-email-preview-header {
323
+ display: flex;
324
+ align-items: flex-start;
325
+ justify-content: space-between;
326
+ gap: 12px;
327
+ padding: 10px 12px;
328
+ border-bottom: 1px solid #262626;
329
+ background: #141414;
330
+ flex-shrink: 0;
331
+ }
332
+ .ss-dbg-email-preview-meta {
333
+ font-size: 11px;
334
+ color: #a3a3a3;
335
+ line-height: 1.6;
336
+ overflow: hidden;
337
+ }
338
+ .ss-dbg-email-preview-meta strong { color: #d4d4d4; font-weight: 600; }
339
+ .ss-dbg-email-iframe {
340
+ flex: 1;
341
+ border: none;
342
+ background: #fff;
343
+ }
344
+
345
+ /* Email row */
346
+ .ss-dbg-email-row { cursor: pointer; }
347
+ .ss-dbg-email-row:hover td { background: rgba(52, 211, 153, 0.06) !important; }
348
+
349
+ /* Email status badges */
350
+ .ss-dbg-email-status {
351
+ display: inline-block;
352
+ padding: 1px 6px;
353
+ border-radius: 3px;
354
+ font-size: 10px;
355
+ font-weight: 600;
356
+ text-transform: uppercase;
357
+ }
358
+ .ss-dbg-email-status-sent { background: #064e3b; color: #34d399; }
359
+ .ss-dbg-email-status-sending { background: #422006; color: #fbbf24; }
360
+ .ss-dbg-email-status-queued { background: #1e3a5f; color: #60a5fa; }
361
+ .ss-dbg-email-status-failed { background: #450a0a; color: #f87171; }
362
+
313
363
  /* Filterable cell */
314
364
  .ss-dbg-filterable:hover { background: rgba(52, 211, 153, 0.08); }
315
365
 
@@ -189,6 +189,7 @@
189
189
  else if (name === 'events') fetchEvents();
190
190
  else if (name === 'routes' && !fetched.routes) fetchRoutes();
191
191
  else if (name === 'logs') fetchLogs();
192
+ else if (name === 'emails') fetchEmails();
192
193
  else {
193
194
  const cp = customPanes.find((p) => p.id === name);
194
195
  if (cp) {
@@ -580,6 +581,122 @@
580
581
  });
581
582
  });
582
583
 
584
+ // ── Emails Tab ─────────────────────────────────────────────────
585
+ const emailSearchInput = document.getElementById('ss-dbg-search-emails');
586
+ const emailSummaryEl = document.getElementById('ss-dbg-emails-summary');
587
+ const emailBodyEl = document.getElementById('ss-dbg-emails-body');
588
+ const emailClearBtn = document.getElementById('ss-dbg-emails-clear');
589
+ const emailPreviewEl = document.getElementById('ss-dbg-email-preview');
590
+ const emailPreviewMeta = document.getElementById('ss-dbg-email-preview-meta');
591
+ const emailPreviewClose = document.getElementById('ss-dbg-email-preview-close');
592
+ const emailIframe = document.getElementById('ss-dbg-email-iframe');
593
+ let cachedEmails = { emails: [], total: 0 };
594
+
595
+ const fetchEmails = () => {
596
+ fetchJSON(BASE + '/emails')
597
+ .then((data) => {
598
+ cachedEmails = data;
599
+ renderEmails();
600
+ })
601
+ .catch(() => {
602
+ if (emailBodyEl) emailBodyEl.innerHTML = '<div class="ss-dbg-empty">Failed to load emails</div>';
603
+ });
604
+ };
605
+
606
+ const renderEmails = () => {
607
+ if (!emailBodyEl) return;
608
+ const filter = (emailSearchInput ? emailSearchInput.value : '').toLowerCase();
609
+ const emails = cachedEmails.emails || [];
610
+
611
+ if (emailSummaryEl) {
612
+ emailSummaryEl.textContent = cachedEmails.total + ' emails';
613
+ }
614
+
615
+ let filtered = emails;
616
+ if (filter) {
617
+ filtered = emails.filter((e) =>
618
+ (e.from || '').toLowerCase().indexOf(filter) !== -1
619
+ || (e.to || '').toLowerCase().indexOf(filter) !== -1
620
+ || (e.subject || '').toLowerCase().indexOf(filter) !== -1
621
+ || (e.mailer || '').toLowerCase().indexOf(filter) !== -1
622
+ );
623
+ }
624
+
625
+ if (filtered.length === 0) {
626
+ emailBodyEl.innerHTML = '<div class="ss-dbg-empty">' + (filter ? 'No matching emails' : 'No emails captured yet') + '</div>';
627
+ return;
628
+ }
629
+
630
+ let html = '<table class="ss-dbg-table"><thead><tr>'
631
+ + '<th style="width:40px">#</th>'
632
+ + '<th style="width:160px">From</th>'
633
+ + '<th style="width:160px">To</th>'
634
+ + '<th>Subject</th>'
635
+ + '<th style="width:60px">Status</th>'
636
+ + '<th style="width:60px">Mailer</th>'
637
+ + '<th style="width:30px" title="Attachments">&#x1F4CE;</th>'
638
+ + '<th style="width:70px">Time</th>'
639
+ + '</tr></thead><tbody>';
640
+
641
+ for (let i = 0; i < filtered.length; i++) {
642
+ const e = filtered[i];
643
+ html += '<tr class="ss-dbg-email-row" data-email-id="' + e.id + '">'
644
+ + '<td style="color:#525252">' + e.id + '</td>'
645
+ + '<td style="color:#a3a3a3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px" title="' + esc(e.from) + '">' + esc(e.from) + '</td>'
646
+ + '<td style="color:#a3a3a3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px" title="' + esc(e.to) + '">' + esc(e.to) + '</td>'
647
+ + '<td style="color:#93c5fd;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(e.subject) + '</td>'
648
+ + '<td><span class="ss-dbg-email-status ss-dbg-email-status-' + esc(e.status) + '">' + esc(e.status) + '</span></td>'
649
+ + '<td style="color:#737373">' + esc(e.mailer) + '</td>'
650
+ + '<td style="color:#525252;text-align:center">' + (e.attachmentCount > 0 ? e.attachmentCount : '-') + '</td>'
651
+ + '<td class="ss-dbg-event-time">' + timeAgo(e.timestamp) + '</td>'
652
+ + '</tr>';
653
+ }
654
+
655
+ html += '</tbody></table>';
656
+ emailBodyEl.innerHTML = html;
657
+
658
+ // Click row to open preview
659
+ emailBodyEl.querySelectorAll('.ss-dbg-email-row').forEach((row) => {
660
+ row.addEventListener('click', () => {
661
+ const id = row.getAttribute('data-email-id');
662
+ showEmailPreview(id, filtered);
663
+ });
664
+ });
665
+ };
666
+
667
+ const showEmailPreview = (id, emails) => {
668
+ if (!emailPreviewEl || !emailIframe || !emailPreviewMeta) return;
669
+ const email = emails.find((e) => String(e.id) === String(id));
670
+
671
+ if (emailPreviewMeta && email) {
672
+ emailPreviewMeta.innerHTML =
673
+ '<strong>Subject:</strong> ' + esc(email.subject)
674
+ + '&nbsp;&nbsp;|&nbsp;&nbsp;<strong>From:</strong> ' + esc(email.from)
675
+ + '&nbsp;&nbsp;|&nbsp;&nbsp;<strong>To:</strong> ' + esc(email.to)
676
+ + (email.cc ? '&nbsp;&nbsp;|&nbsp;&nbsp;<strong>CC:</strong> ' + esc(email.cc) : '')
677
+ + '&nbsp;&nbsp;|&nbsp;&nbsp;<strong>Status:</strong> <span class="ss-dbg-email-status ss-dbg-email-status-' + esc(email.status) + '">' + esc(email.status) + '</span>'
678
+ + '&nbsp;&nbsp;|&nbsp;&nbsp;<strong>Mailer:</strong> ' + esc(email.mailer);
679
+ }
680
+
681
+ emailIframe.src = BASE + '/emails/' + id + '/preview';
682
+ emailPreviewEl.style.display = 'flex';
683
+ };
684
+
685
+ if (emailPreviewClose) {
686
+ emailPreviewClose.addEventListener('click', () => {
687
+ if (emailPreviewEl) emailPreviewEl.style.display = 'none';
688
+ if (emailIframe) emailIframe.src = 'about:blank';
689
+ });
690
+ }
691
+
692
+ if (emailSearchInput) emailSearchInput.addEventListener('input', renderEmails);
693
+ if (emailClearBtn) {
694
+ emailClearBtn.addEventListener('click', () => {
695
+ cachedEmails = { emails: [], total: 0 };
696
+ renderEmails();
697
+ });
698
+ }
699
+
583
700
  // ── Custom panes: fetch, render, bind ───────────────────────────
584
701
  const getNestedValue = (obj, path) => {
585
702
  const parts = path.split('.');
@@ -4,6 +4,7 @@
4
4
  <button type="button" class="ss-dbg-tab" data-ss-dbg-tab="events">Events</button>
5
5
  <button type="button" class="ss-dbg-tab" data-ss-dbg-tab="routes">Routes</button>
6
6
  <button type="button" class="ss-dbg-tab" data-ss-dbg-tab="logs">Logs</button>
7
+ <button type="button" class="ss-dbg-tab" data-ss-dbg-tab="emails">Emails</button>
7
8
  @each(pane in customPanes)
8
9
  <button type="button" class="ss-dbg-tab" data-ss-dbg-tab="{{ pane.id }}">{{ pane.label }}</button>
9
10
  @end
@@ -48,6 +49,21 @@
48
49
  </div>
49
50
  <div id="ss-dbg-logs-body"><div class="ss-dbg-empty">Loading logs...</div></div>
50
51
  </div>
52
+ <div id="ss-dbg-pane-emails" class="ss-dbg-pane" style="position:relative">
53
+ <div class="ss-dbg-search-bar">
54
+ <input type="text" class="ss-dbg-search" id="ss-dbg-search-emails" placeholder="Filter emails by from, to, subject, or mailer..." />
55
+ <span id="ss-dbg-emails-summary" class="ss-dbg-summary"></span>
56
+ <button type="button" class="ss-dbg-btn-clear" id="ss-dbg-emails-clear">Clear</button>
57
+ </div>
58
+ <div id="ss-dbg-emails-body"><div class="ss-dbg-empty">Loading emails...</div></div>
59
+ <div id="ss-dbg-email-preview" class="ss-dbg-email-preview" style="display:none">
60
+ <div class="ss-dbg-email-preview-header">
61
+ <div id="ss-dbg-email-preview-meta" class="ss-dbg-email-preview-meta"></div>
62
+ <button type="button" class="ss-dbg-btn-clear" id="ss-dbg-email-preview-close">Close</button>
63
+ </div>
64
+ <iframe id="ss-dbg-email-iframe" class="ss-dbg-email-iframe" sandbox="allow-same-origin" src="about:blank"></iframe>
65
+ </div>
66
+ </div>
51
67
  @each(pane in customPanes)
52
68
  <div id="ss-dbg-pane-{{ pane.id }}" class="ss-dbg-pane">
53
69
  @if(pane.search || pane.clearable)
@@ -3,5 +3,5 @@ export { StatsEngine } from './engine/stats_engine.js';
3
3
  export { RequestMetrics } from './engine/request_metrics.js';
4
4
  export type { MetricCollector } from './collectors/collector.js';
5
5
  export type { MetricValue, ServerStats, ServerStatsConfig, LogStats, DevToolbarOptions } from './types.js';
6
- export type { DebugPane, DebugPaneColumn, DebugPaneFormatType, DebugPaneSearch, BadgeColor, QueryRecord, EventRecord, RouteRecord, DevToolbarConfig, } from './debug/types.js';
6
+ export type { DebugPane, DebugPaneColumn, DebugPaneFormatType, DebugPaneSearch, BadgeColor, QueryRecord, EventRecord, EmailRecord, RouteRecord, DevToolbarConfig, } from './debug/types.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC1G,YAAY,EACV,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC1G,YAAY,EACV,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,kBAAkB,CAAA"}
@@ -4,6 +4,8 @@ export default class ServerStatsProvider {
4
4
  private intervalId;
5
5
  private engine;
6
6
  private debugStore;
7
+ private persistPath;
8
+ private flushTimer;
7
9
  constructor(app: ApplicationService);
8
10
  boot(): Promise<void>;
9
11
  ready(): Promise<void>;
@@ -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;IAK1B,SAAS,CAAC,GAAG,EAAE,kBAAkB;IAJ7C,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,UAAU,CAA2B;gBAEvB,GAAG,EAAE,kBAAkB;IAEvC,IAAI;IAoBJ,KAAK;YAkEG,eAAe;IA4BvB,QAAQ;CASf"}
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;YAoEG,eAAe;IA6CvB,QAAQ;CAuBf"}
@@ -6,6 +6,8 @@ export default class ServerStatsProvider {
6
6
  intervalId = null;
7
7
  engine = null;
8
8
  debugStore = null;
9
+ persistPath = null;
10
+ flushTimer = null;
9
11
  constructor(app) {
10
12
  this.app = app;
11
13
  }
@@ -45,7 +47,9 @@ export default class ServerStatsProvider {
45
47
  enabled: true,
46
48
  maxQueries: toolbarConfig.maxQueries ?? 500,
47
49
  maxEvents: toolbarConfig.maxEvents ?? 200,
50
+ maxEmails: toolbarConfig.maxEmails ?? 100,
48
51
  slowQueryThresholdMs: toolbarConfig.slowQueryThresholdMs ?? 100,
52
+ persistDebugData: toolbarConfig.persistDebugData ?? false,
49
53
  });
50
54
  }
51
55
  let transmit = null;
@@ -85,6 +89,11 @@ export default class ServerStatsProvider {
85
89
  this.debugStore = new DebugStore(toolbarConfig);
86
90
  // Bind debug store to container
87
91
  this.app.container.singleton("debug.store", () => this.debugStore);
92
+ // Load persisted data before starting collectors
93
+ if (toolbarConfig.persistDebugData) {
94
+ this.persistPath = this.app.makePath("tmp", "debug-data.json");
95
+ await this.debugStore.loadFromDisk(this.persistPath);
96
+ }
88
97
  // Get the emitter
89
98
  let emitter = null;
90
99
  try {
@@ -102,12 +111,36 @@ export default class ServerStatsProvider {
102
111
  // Router not available
103
112
  }
104
113
  await this.debugStore.start(emitter, router);
114
+ // Periodic flush every 30 seconds (handles crashes)
115
+ if (this.persistPath) {
116
+ this.flushTimer = setInterval(async () => {
117
+ try {
118
+ await this.debugStore?.saveToDisk(this.persistPath);
119
+ }
120
+ catch {
121
+ // Silently ignore flush errors
122
+ }
123
+ }, 30_000);
124
+ }
105
125
  }
106
126
  async shutdown() {
107
127
  if (this.intervalId) {
108
128
  clearInterval(this.intervalId);
109
129
  this.intervalId = null;
110
130
  }
131
+ if (this.flushTimer) {
132
+ clearInterval(this.flushTimer);
133
+ this.flushTimer = null;
134
+ }
135
+ // Save debug data before stopping collectors
136
+ if (this.persistPath && this.debugStore) {
137
+ try {
138
+ await this.debugStore.saveToDisk(this.persistPath);
139
+ }
140
+ catch {
141
+ // Silently ignore save errors during shutdown
142
+ }
143
+ }
111
144
  this.debugStore?.stop();
112
145
  await this.engine?.stop();
113
146
  }
@@ -199,6 +199,11 @@ export interface DevToolbarOptions {
199
199
  * @default 200
200
200
  */
201
201
  maxEvents?: number;
202
+ /**
203
+ * Maximum number of captured emails to keep in the ring buffer.
204
+ * @default 100
205
+ */
206
+ maxEmails?: number;
202
207
  /**
203
208
  * Queries slower than this threshold (in **milliseconds**) are
204
209
  * highlighted in the toolbar.
@@ -214,6 +219,13 @@ export interface DevToolbarOptions {
214
219
  * @see {@link DebugPane}
215
220
  */
216
221
  panes?: DebugPane[];
222
+ /**
223
+ * Persist debug data (queries, events, emails) to disk so it
224
+ * survives server restarts. Data is saved to `tmp/debug-data.json`.
225
+ *
226
+ * @default false
227
+ */
228
+ persistDebugData?: boolean;
217
229
  }
218
230
  /**
219
231
  * 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":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAMnD;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAG1B,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAA;IAEnB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;IAEd,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAA;IAEnB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAA;IAEpB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAA;IAEd;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IAIjB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAA;IAEzB;;;;;;;;OAQG;IACH,iBAAiB,EAAE,MAAM,CAAA;IAEzB;;;;;;;;;;OAUG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB,iDAAiD;IACjD,qBAAqB,EAAE,MAAM,CAAA;IAI7B,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAA;IAElB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAA;IAElB,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAA;IAErB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;IAIjB,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAA;IAEhB,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAA;IAEzB,sDAAsD;IACtD,qBAAqB,EAAE,MAAM,CAAA;IAE7B,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAA;IAEtB;;;;;;;;;;OAUG;IACH,YAAY,EAAE,MAAM,CAAA;IAIpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAA;IAEnB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAA;IAEpB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAA;IAEpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAA;IAEnB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAA;IAIxB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAA;IAEvB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,CAAA;IAEvB,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,CAAA;IAExB,iDAAiD;IACjD,mBAAmB,EAAE,MAAM,CAAA;IAE3B,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,CAAA;IAE1B,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAA;IAIpB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IAEnB,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAA;IAEvB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAA;IAIrB,uEAAuE;IACvE,eAAe,EAAE,MAAM,CAAA;IAEvB,0DAA0D;IAC1D,iBAAiB,EAAE,MAAM,CAAA;IAEzB,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAA;IAExB,gEAAgE;IAChE,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAMD;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAA;IAEpB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAA;IAEtB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAA;IAErB,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAA;IAEhB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAE7B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,EAAE,CAAA;IAEnB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;;;;;;;;OASG;IACH,SAAS,EAAE,UAAU,GAAG,MAAM,CAAA;IAE9B;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAA;IAEnB;;;;;;;OAOG;IACH,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAA;IAExB;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,EAAE,eAAe,EAAE,CAAA;IAE7B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAA;IAE/C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAE9B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAA;CACnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adonisjs-server-stats",
3
- "version": "1.0.10",
3
+ "version": "1.1.2",
4
4
  "description": "Real-time server monitoring for AdonisJS v6 applications",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -86,7 +86,7 @@
86
86
  "author": "",
87
87
  "license": "MIT",
88
88
  "peerDependencies": {
89
- "@adonisjs/core": "^6.0.0"
89
+ "@adonisjs/core": "^6.0.0 || ^7.0.0-next || ^7.0.0"
90
90
  },
91
91
  "peerDependenciesMeta": {
92
92
  "@adonisjs/lucid": {