adonisjs-server-stats 1.0.9 → 1.1.1

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 +55 -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 +1 -1
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
@@ -118,16 +118,39 @@ import {
118
118
  } from 'adonisjs-server-stats/collectors'
119
119
 
120
120
  export default defineConfig({
121
+ // How often to collect and broadcast stats (in milliseconds)
121
122
  intervalMs: 3000,
123
+
124
+ // Real-time transport: 'transmit' for SSE via @adonisjs/transmit, 'none' for polling only
122
125
  transport: 'transmit',
126
+
127
+ // Transmit channel name clients subscribe to
123
128
  channelName: 'admin/server-stats',
129
+
130
+ // HTTP endpoint that serves the latest stats snapshot (set to false to disable)
124
131
  endpoint: '/admin/api/server-stats',
132
+
125
133
  collectors: [
134
+ // CPU usage, event loop lag, heap/RSS memory, uptime, Node.js version
126
135
  processCollector(),
136
+
137
+ // OS load averages, total/free system memory, system uptime
127
138
  systemCollector(),
139
+
140
+ // Requests/sec, avg response time, error rate, active connections
141
+ // maxRecords: size of the circular buffer for request tracking
128
142
  httpCollector({ maxRecords: 10_000 }),
143
+
144
+ // Lucid connection pool: used/free/pending/max connections
145
+ // Requires @adonisjs/lucid
129
146
  dbPoolCollector({ connectionName: 'postgres' }),
147
+
148
+ // Redis server stats: memory, connected clients, keys, hit rate
149
+ // Requires @adonisjs/redis
130
150
  redisCollector(),
151
+
152
+ // BullMQ queue stats: active/waiting/delayed/failed jobs
153
+ // Requires bullmq -- connects directly to Redis (not via @adonisjs/redis)
131
154
  queueCollector({
132
155
  queueName: 'default',
133
156
  connection: {
@@ -136,7 +159,12 @@ export default defineConfig({
136
159
  password: env.get('QUEUE_REDIS_PASSWORD'),
137
160
  },
138
161
  }),
162
+
163
+ // Log file stats: errors/warnings in a 5-minute window, entries/minute
139
164
  logCollector({ logPath: 'logs/adonisjs.log' }),
165
+
166
+ // App-level metrics: online users, pending webhooks, pending emails
167
+ // Requires @adonisjs/lucid
140
168
  appCollector(),
141
169
  ],
142
170
  })
@@ -195,13 +223,15 @@ export default class ServerStatsController {
195
223
 
196
224
  ### `DevToolbarOptions`
197
225
 
198
- | Option | Type | Default | Description |
199
- |------------------------|-----------------|---------|------------------------------------|
200
- | `enabled` | `boolean` | `false` | Enable the dev toolbar |
201
- | `maxQueries` | `number` | `500` | Max SQL queries to buffer |
202
- | `maxEvents` | `number` | `200` | Max events to buffer |
203
- | `slowQueryThresholdMs` | `number` | `100` | Slow query threshold (ms) |
204
- | `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 |
205
235
 
206
236
  ---
207
237
 
@@ -350,7 +380,7 @@ Features:
350
380
 
351
381
  ## Dev Toolbar
352
382
 
353
- 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.
354
384
 
355
385
  ```ts
356
386
  export default defineConfig({
@@ -358,7 +388,9 @@ export default defineConfig({
358
388
  enabled: true,
359
389
  maxQueries: 500,
360
390
  maxEvents: 200,
391
+ maxEmails: 100,
361
392
  slowQueryThresholdMs: 100,
393
+ persistDebugData: true, // survive server restarts
362
394
  },
363
395
  })
364
396
  ```
@@ -372,11 +404,24 @@ router
372
404
  router.get('queries', '#controllers/admin/debug_controller.queries')
373
405
  router.get('events', '#controllers/admin/debug_controller.events')
374
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')
375
409
  })
376
410
  .prefix('/admin/api/debug')
377
411
  .use(middleware.admin())
378
412
  ```
379
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
+
380
425
  ### Custom Debug Panes
381
426
 
382
427
  Add custom tabs to the debug panel:
@@ -534,6 +579,7 @@ import type {
534
579
  BadgeColor,
535
580
  QueryRecord,
536
581
  EventRecord,
582
+ EmailRecord,
537
583
  RouteRecord,
538
584
  } from 'adonisjs-server-stats'
539
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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adonisjs-server-stats",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "Real-time server monitoring for AdonisJS v6 applications",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",