adonisjs-server-stats 1.12.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -271,7 +271,7 @@ Each collector is a factory function that returns a `MetricCollector`. All colle
271
271
  | `httpCollector(opts?)` | Requests/sec, avg response time, error rate, active connections | optional | -- |
272
272
  | `dbPoolCollector(opts?)` | Pool used/free/pending/max connections | optional | `@adonisjs/lucid` |
273
273
  | `redisCollector()` | Status, memory, clients, keys, hit rate | none | `@adonisjs/redis` |
274
- | `queueCollector(opts)` | Active/waiting/delayed/failed jobs, worker count | **required** | `bullmq` |
274
+ | `queueCollector(opts)` | Active/waiting/delayed/failed jobs, worker count | **required** | `bullmq` or `@adonisjs/queue`/`@boringnode/queue` (auto-detected) |
275
275
  | `logCollector(opts?)` | Errors/warnings/entries (5m window), entries/minute | optional | -- |
276
276
  | `appCollector()` | Online users, pending webhooks, pending emails | none | `@adonisjs/lucid` |
277
277
 
@@ -673,7 +673,7 @@ The debug toolbar captures all emails sent via AdonisJS mail (`mail:sending`, `m
673
673
 
674
674
  ### Cross-Process Email Capture (Queue Workers)
675
675
 
676
- AdonisJS mail events (`mail:sending`, `mail:sent`, etc.) are process-local. If your app sends emails from **Bull queue workers** or other separate processes, the web server's email collector never sees them.
676
+ AdonisJS mail events (`mail:sending`, `mail:sent`, etc.) are process-local. If your app sends emails from **queue workers** (BullMQ via `@rlanz/bull-queue`, or `@adonisjs/queue`/`@boringnode/queue`) or other separate processes, the web server's email collector never sees them.
677
677
 
678
678
  The provider handles this automatically via a **Redis pub/sub bridge**:
679
679
 
@@ -681,6 +681,8 @@ The provider handles this automatically via a **Redis pub/sub bridge**:
681
681
  2. When an email is sent, the event is published to a Redis pub/sub channel
682
682
  3. In the web server (`web` environment), the provider subscribes to the same channel and ingests the email into both the debug panel and the dashboard
683
683
 
684
+ Worker-sent emails appear in the dashboard with HTML preview alongside emails sent from web requests — no additional configuration needed.
685
+
684
686
  **Requirements:**
685
687
  - `@adonisjs/redis` must be installed and configured (used for pub/sub between processes)
686
688
  - The provider must be registered **without** an `environment` restriction so it loads in all environments:
@@ -810,7 +812,7 @@ export default defineConfig({
810
812
  | **Emails** | Email history with sender, recipient, subject, status. Click for HTML preview in iframe |
811
813
  | **Timeline** | Per-request waterfall timeline (requires `tracing: true`) |
812
814
  | **Cache** | Redis key browser with SCAN-based listing, type-aware detail view, and server stats (requires `@adonisjs/redis`) |
813
- | **Jobs** | Queue overview with job listing, detail, and retry for failed jobs (requires `@rlanz/bull-queue`) |
815
+ | **Jobs** | Queue overview with job listing, detail, and retry for failed jobs (requires `bullmq` or `@adonisjs/queue`/`@boringnode/queue` — auto-detected) |
814
816
  | **Config** | Sanitized view of app configuration and environment variables. Secrets are auto-redacted |
815
817
 
816
818
  #### Access Control
@@ -1069,7 +1071,9 @@ All integrations use lazy `import()` -- missing peer deps won't crash the app. T
1069
1071
  | `@adonisjs/transmit` | Provider (SSE broadcast), dashboard real-time |
1070
1072
  | `@adonisjs/transmit-client` | React/Vue real-time updates (falls back to polling) |
1071
1073
  | `@julr/adonisjs-prometheus` | `serverStatsCollector` |
1072
- | `bullmq` | `queueCollector` |
1074
+ | `bullmq` | `queueCollector` (BullMQ backend) |
1075
+ | `@adonisjs/queue` | `queueCollector` (AdonisJS queue backend, auto-detected) |
1076
+ | `@boringnode/queue` | `queueCollector` (AdonisJS queue backend, auto-detected) |
1073
1077
  | `better-sqlite3` | Dashboard (`dashboard: true`) |
1074
1078
  | `edge.js` | Edge tag |
1075
1079
  | `react`, `react-dom` | React components (alpha) |
@@ -0,0 +1,21 @@
1
+ import type { MetricCollector } from './collector.js';
2
+ import type { QueueStoreReader } from '../dashboard/integrations/adonisjs_queue_store.js';
3
+ /**
4
+ * Options for {@link adonisQueueCollector}.
5
+ */
6
+ export interface AdonisQueueCollectorOptions {
7
+ }
8
+ /** Internal deps seam — NOT part of the public API; for unit tests only. */
9
+ interface AdonisQueueCollectorDeps {
10
+ resolveReader?: () => Promise<QueueStoreReader | null>;
11
+ }
12
+ /**
13
+ * Monitors an @adonisjs/queue job queue for active, waiting, delayed, and failed jobs.
14
+ *
15
+ * Auto-detects the driver (database or redis) from the host app's queue config.
16
+ * Returns zeros if @adonisjs/queue is unavailable or the store cannot be reached.
17
+ *
18
+ * **Peer dependencies:** `@adonisjs/queue`
19
+ */
20
+ export declare function adonisQueueCollector(_opts?: AdonisQueueCollectorOptions, deps?: AdonisQueueCollectorDeps): MetricCollector;
21
+ export {};
@@ -0,0 +1,61 @@
1
+ import { resolveFromAppImport } from '../dashboard/integrations/adonisjs_queue_store.js';
2
+ import { log, dim, bold } from '../utils/logger.js';
3
+ /** Default metrics returned when queue data is unavailable. */
4
+ const QUEUE_DEFAULTS = {
5
+ queueActive: 0,
6
+ queueWaiting: 0,
7
+ queueDelayed: 0,
8
+ queueFailed: 0,
9
+ queueWorkerCount: 0,
10
+ };
11
+ /**
12
+ * Monitors an @adonisjs/queue job queue for active, waiting, delayed, and failed jobs.
13
+ *
14
+ * Auto-detects the driver (database or redis) from the host app's queue config.
15
+ * Returns zeros if @adonisjs/queue is unavailable or the store cannot be reached.
16
+ *
17
+ * **Peer dependencies:** `@adonisjs/queue`
18
+ */
19
+ export function adonisQueueCollector(_opts, deps) {
20
+ let warnedNotInstalled = false;
21
+ return {
22
+ name: 'queue',
23
+ label: 'queue — @adonisjs/queue (auto-detected driver)',
24
+ getConfig() {
25
+ return {
26
+ driver: 'auto',
27
+ source: '@adonisjs/queue',
28
+ };
29
+ },
30
+ async collect() {
31
+ try {
32
+ const resolve = deps?.resolveReader ?? resolveFromAppImport;
33
+ const reader = await resolve();
34
+ if (!reader) {
35
+ if (!warnedNotInstalled) {
36
+ warnedNotInstalled = true;
37
+ log.block(`Queue collector ${bold('skipped')} — @adonisjs/queue is not installed or not reachable`, [
38
+ dim('Queue metrics will return zeros until the package is available.'),
39
+ `Run ${bold('node ace add @adonisjs/queue')} to install it.`,
40
+ ]);
41
+ }
42
+ return QUEUE_DEFAULTS;
43
+ }
44
+ const [counts, workerCount] = await Promise.all([
45
+ reader.getCounts(),
46
+ reader.getWorkerCount(),
47
+ ]);
48
+ return {
49
+ queueActive: counts.active,
50
+ queueWaiting: counts.waiting,
51
+ queueDelayed: counts.delayed,
52
+ queueFailed: counts.failed,
53
+ queueWorkerCount: workerCount,
54
+ };
55
+ }
56
+ catch {
57
+ return QUEUE_DEFAULTS;
58
+ }
59
+ },
60
+ };
61
+ }
@@ -64,15 +64,21 @@ async function registerOptionalCollectors(collectors, entries) {
64
64
  pkg: '@adonisjs/redis',
65
65
  });
66
66
  const hasBullMQ = await isInstalled('bullmq');
67
+ const hasAdonisQueue = hasBullMQ ? false : await isInstalled('@adonisjs/queue');
67
68
  if (hasBullMQ) {
68
69
  const { queueCollector } = await import('./queue_collector.js');
69
70
  collectors.push(queueCollector({ queueName: 'default', connection: { host: '127.0.0.1', port: 6379 } }));
70
71
  }
72
+ else if (hasAdonisQueue) {
73
+ const { adonisQueueCollector } = await import('./adonisjs_queue_collector.js');
74
+ collectors.push(adonisQueueCollector());
75
+ }
76
+ const queueEnabled = hasBullMQ || hasAdonisQueue;
71
77
  pushOptionalEntry(entries, {
72
78
  name: 'queue',
73
79
  description: 'jobs, wait time, throughput',
74
- enabled: hasBullMQ,
75
- pkg: 'bullmq',
80
+ enabled: queueEnabled,
81
+ pkg: hasBullMQ ? 'bullmq' : hasAdonisQueue ? '@adonisjs/queue' : 'bullmq',
76
82
  });
77
83
  }
78
84
  function printBootLog(entries) {
@@ -8,6 +8,8 @@ export type { DbPoolCollectorOptions } from './db_pool_collector.js';
8
8
  export { redisCollector } from './redis_collector.js';
9
9
  export { queueCollector } from './queue_collector.js';
10
10
  export type { QueueCollectorOptions, QueueRedisConnection } from './queue_collector.js';
11
+ export { adonisQueueCollector } from './adonisjs_queue_collector.js';
12
+ export type { AdonisQueueCollectorOptions } from './adonisjs_queue_collector.js';
11
13
  export { logCollector, getLogStreamService } from './log_collector.js';
12
14
  export type { LogCollectorOptions } from './log_collector.js';
13
15
  export { appCollector } from './app_collector.js';
@@ -4,6 +4,7 @@ export { httpCollector, getRequestMetrics } from './http_collector.js';
4
4
  export { dbPoolCollector } from './db_pool_collector.js';
5
5
  export { redisCollector } from './redis_collector.js';
6
6
  export { queueCollector } from './queue_collector.js';
7
+ export { adonisQueueCollector } from './adonisjs_queue_collector.js';
7
8
  export { logCollector, getLogStreamService } from './log_collector.js';
8
9
  export { appCollector } from './app_collector.js';
9
10
  export { autoDetectCollectors } from './auto_detect.js';
@@ -1,9 +1,9 @@
1
1
  import { CacheInspector } from './integrations/cache_inspector.js';
2
- import { QueueInspector } from './integrations/queue_inspector.js';
3
2
  import type { ApplicationService } from '@adonisjs/core/types';
3
+ import type { QueueInspectorContract } from './integrations/queue_inspector_contract.js';
4
4
  /**
5
5
  * Manages lazy initialization and availability detection for
6
- * cache (Redis) and queue (BullMQ) inspectors.
6
+ * cache (Redis) and queue (BullMQ / @adonisjs/queue) inspectors.
7
7
  *
8
8
  * Extracted from DashboardController to reduce file size and complexity.
9
9
  */
@@ -16,8 +16,8 @@ export declare class InspectorManager {
16
16
  constructor(app: ApplicationService);
17
17
  /** Lazy-init the cache inspector. Returns null if Redis is unavailable. */
18
18
  getCacheInspector(): Promise<CacheInspector | null>;
19
- /** Lazy-init the queue inspector. Returns null if BullMQ is unavailable. */
20
- getQueueInspector(): Promise<QueueInspector | null>;
19
+ /** Lazy-init the queue inspector. Returns null if neither BullMQ nor @adonisjs/queue is available. */
20
+ getQueueInspector(): Promise<QueueInspectorContract | null>;
21
21
  /** Fetch cache overview stats for the overview page. */
22
22
  fetchCacheOverview(): Promise<{
23
23
  available: boolean;
@@ -1,9 +1,10 @@
1
1
  import { log } from '../utils/logger.js';
2
2
  import { CacheInspector } from './integrations/cache_inspector.js';
3
3
  import { QueueInspector } from './integrations/queue_inspector.js';
4
+ import { AdonisQueueInspector } from './integrations/adonisjs_queue_inspector.js';
4
5
  /**
5
6
  * Manages lazy initialization and availability detection for
6
- * cache (Redis) and queue (BullMQ) inspectors.
7
+ * cache (Redis) and queue (BullMQ / @adonisjs/queue) inspectors.
7
8
  *
8
9
  * Extracted from DashboardController to reduce file size and complexity.
9
10
  */
@@ -39,22 +40,31 @@ export class InspectorManager {
39
40
  return null;
40
41
  }
41
42
  }
42
- /** Lazy-init the queue inspector. Returns null if BullMQ is unavailable. */
43
+ /** Lazy-init the queue inspector. Returns null if neither BullMQ nor @adonisjs/queue is available. */
43
44
  async getQueueInspector() {
44
45
  if (this.queueAvailable === false)
45
46
  return null;
46
47
  if (this.queueInspector)
47
48
  return this.queueInspector;
48
49
  try {
49
- const available = await QueueInspector.isAvailable(this.app);
50
- this.queueAvailable = available;
51
- if (!available) {
52
- log.info('dashboard: Queue not detected — Jobs panel disabled');
53
- return null;
50
+ // Try BullMQ (@rlanz/bull-queue) first.
51
+ if (await QueueInspector.isAvailable(this.app)) {
52
+ const queue = await this.app.container.make('rlanz/queue');
53
+ this.queueInspector = new QueueInspector(queue);
54
+ this.queueAvailable = true;
55
+ log.info('dashboard: BullMQ detected — Jobs panel enabled');
56
+ return this.queueInspector;
54
57
  }
55
- const queue = await this.app.container.make('rlanz/queue');
56
- this.queueInspector = new QueueInspector(queue);
57
- return this.queueInspector;
58
+ // Fall back to @adonisjs/queue.
59
+ if (await AdonisQueueInspector.isAvailable(this.app)) {
60
+ this.queueInspector = new AdonisQueueInspector(this.app);
61
+ this.queueAvailable = true;
62
+ log.info('dashboard: @adonisjs/queue detected — Jobs panel enabled');
63
+ return this.queueInspector;
64
+ }
65
+ this.queueAvailable = false;
66
+ log.info('dashboard: Queue not detected — Jobs panel disabled');
67
+ return null;
58
68
  }
59
69
  catch (err) {
60
70
  this.queueAvailable = false;
@@ -0,0 +1,42 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ import type { QueueInspectorContract } from './queue_inspector_contract.js';
3
+ import type { QueueOverview, QueueJobDetail, QueueJobListResult, JobStatus } from './queue_inspector_contract.js';
4
+ import type { QueueStoreReader } from './adonisjs_queue_store.js';
5
+ /**
6
+ * Inspects @adonisjs/queue jobs via the shared store reader.
7
+ *
8
+ * Supports both the database (Lucid/Knex) and Redis drivers.
9
+ * All methods catch errors and return safe defaults.
10
+ */
11
+ export declare class AdonisQueueInspector implements QueueInspectorContract {
12
+ #private;
13
+ _resolveReader: (app: ApplicationService) => Promise<QueueStoreReader | null>;
14
+ constructor(app: ApplicationService, ...args: unknown[]);
15
+ /**
16
+ * Detect whether `@adonisjs/queue` is registered in the application container.
17
+ */
18
+ static isAvailable(app: ApplicationService): Promise<boolean>;
19
+ /**
20
+ * Get an overview of job counts by status across all queues.
21
+ * The @adonisjs/queue driver has no concept of paused queues, so `paused` is always 0.
22
+ */
23
+ getOverview(): Promise<QueueOverview>;
24
+ /**
25
+ * List jobs filtered by status with pagination.
26
+ *
27
+ * @param status Job status to filter by, or `'all'` for every status.
28
+ * @param page Page number (1-based).
29
+ * @param perPage Jobs per page.
30
+ */
31
+ listJobs(status?: JobStatus | 'all', page?: number, perPage?: number): Promise<QueueJobListResult>;
32
+ /**
33
+ * Get full detail for a single job by ID.
34
+ */
35
+ getJob(id: string): Promise<QueueJobDetail | null>;
36
+ /**
37
+ * Retry a failed job.
38
+ *
39
+ * @returns `true` if the job was successfully requeued.
40
+ */
41
+ retryJob(id: string): Promise<boolean>;
42
+ }
@@ -0,0 +1,135 @@
1
+ import { resolveFromApplication } from './adonisjs_queue_store.js';
2
+ // ---------------------------------------------------------------------------
3
+ // AdonisQueueInspector
4
+ // ---------------------------------------------------------------------------
5
+ /**
6
+ * Inspects @adonisjs/queue jobs via the shared store reader.
7
+ *
8
+ * Supports both the database (Lucid/Knex) and Redis drivers.
9
+ * All methods catch errors and return safe defaults.
10
+ */
11
+ export class AdonisQueueInspector {
12
+ #app;
13
+ #readerPromise = null;
14
+ constructor(app, ...args) {
15
+ this.#app = app;
16
+ // Allow an optional second arg to inject a fake resolver in tests.
17
+ if (typeof args[0] === 'function') {
18
+ this._resolveReader = args[0];
19
+ }
20
+ else {
21
+ this._resolveReader = (a) => resolveFromApplication(a);
22
+ }
23
+ }
24
+ /**
25
+ * Detect whether `@adonisjs/queue` is registered in the application container.
26
+ */
27
+ static async isAvailable(app) {
28
+ try {
29
+ await app.container.make('queue.manager');
30
+ return true;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Private helpers
38
+ // ---------------------------------------------------------------------------
39
+ /**
40
+ * Lazily resolve and cache the store reader.
41
+ *
42
+ * Memoizes the *promise* (not a boolean flag) so concurrent callers —
43
+ * e.g. `getOverview()` + `listJobs()` fired together via `Promise.all` —
44
+ * all await the same in-flight resolution instead of racing on a half-set
45
+ * flag and getting a still-null reader.
46
+ */
47
+ async #getReader() {
48
+ if (!this.#readerPromise) {
49
+ this.#readerPromise = this._resolveReader(this.#app);
50
+ }
51
+ return this.#readerPromise;
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // QueueInspectorContract implementation
55
+ // ---------------------------------------------------------------------------
56
+ /**
57
+ * Get an overview of job counts by status across all queues.
58
+ * The @adonisjs/queue driver has no concept of paused queues, so `paused` is always 0.
59
+ */
60
+ async getOverview() {
61
+ const defaults = {
62
+ active: 0,
63
+ waiting: 0,
64
+ delayed: 0,
65
+ completed: 0,
66
+ failed: 0,
67
+ paused: 0,
68
+ };
69
+ try {
70
+ const reader = await this.#getReader();
71
+ if (!reader)
72
+ return defaults;
73
+ const counts = await reader.getCounts();
74
+ return {
75
+ active: counts.active,
76
+ waiting: counts.waiting,
77
+ delayed: counts.delayed,
78
+ completed: counts.completed,
79
+ failed: counts.failed,
80
+ paused: 0,
81
+ };
82
+ }
83
+ catch {
84
+ return defaults;
85
+ }
86
+ }
87
+ /**
88
+ * List jobs filtered by status with pagination.
89
+ *
90
+ * @param status Job status to filter by, or `'all'` for every status.
91
+ * @param page Page number (1-based).
92
+ * @param perPage Jobs per page.
93
+ */
94
+ async listJobs(status = 'all', page = 1, perPage = 25) {
95
+ try {
96
+ const reader = await this.#getReader();
97
+ if (!reader)
98
+ return { jobs: [], total: 0 };
99
+ return await reader.listJobs(status === 'paused' ? 'all' : status, page, perPage);
100
+ }
101
+ catch {
102
+ return { jobs: [], total: 0 };
103
+ }
104
+ }
105
+ /**
106
+ * Get full detail for a single job by ID.
107
+ */
108
+ async getJob(id) {
109
+ try {
110
+ const reader = await this.#getReader();
111
+ if (!reader)
112
+ return null;
113
+ return await reader.getJob(id);
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ /**
120
+ * Retry a failed job.
121
+ *
122
+ * @returns `true` if the job was successfully requeued.
123
+ */
124
+ async retryJob(id) {
125
+ try {
126
+ const reader = await this.#getReader();
127
+ if (!reader)
128
+ return false;
129
+ return await reader.retryJob(id);
130
+ }
131
+ catch {
132
+ return false;
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,166 @@
1
+ import type { QueueJobSummary, QueueJobDetail, QueueJobListResult } from './queue_inspector.js';
2
+ /** JobStatus as stored in the queue_jobs table / Redis hashes. */
3
+ type BoringJobStatus = 'pending' | 'active' | 'delayed' | 'completed' | 'failed';
4
+ /** JobData — the JSON blob stored in queue_jobs.data / Redis ::data hash. */
5
+ interface BoringJobData {
6
+ id: string;
7
+ name: string;
8
+ payload?: unknown;
9
+ attempts: number;
10
+ priority?: number;
11
+ nextRetryAt?: Date;
12
+ stalledCount?: number;
13
+ groupId?: string;
14
+ /** Unix ms timestamp set when the job was dispatched. */
15
+ createdAt?: number;
16
+ /** Not persisted per-row; best-effort from static job options. */
17
+ maxRetries?: number;
18
+ [key: string]: unknown;
19
+ }
20
+ /** JobRecord — returned by adapter.getJob / stored in completed|failed hashes. */
21
+ interface BoringJobRecord {
22
+ status: BoringJobStatus;
23
+ data: BoringJobData;
24
+ finishedAt?: number;
25
+ error?: string;
26
+ }
27
+ /** Minimal Lucid db service interface. */
28
+ interface LucidDb {
29
+ primaryConnectionName: string;
30
+ connection(name?: string): {
31
+ getWriteClient(): unknown;
32
+ };
33
+ }
34
+ /** Minimal @adonisjs/redis manager interface. */
35
+ interface AdonisRedisManager {
36
+ connection(name?: string): AdonisRedisConnection;
37
+ }
38
+ /**
39
+ * Minimal ioredis-compatible connection interface.
40
+ * All methods present on a real ioredis client that we call.
41
+ */
42
+ interface AdonisRedisConnection {
43
+ zcard(key: string): Promise<number>;
44
+ hlen(key: string): Promise<number>;
45
+ zrange(key: string, start: number, stop: number): Promise<string[]>;
46
+ hget(key: string, field: string): Promise<string | null>;
47
+ hscan(key: string, cursor: string, countKeyword: string, count: string): Promise<[string, string[]]>;
48
+ hset(key: string, field: string, value: string): Promise<number>;
49
+ zadd(key: string, score: number, member: string): Promise<number>;
50
+ hdel(key: string, field: string): Promise<number>;
51
+ zrem(key: string, member: string): Promise<number>;
52
+ zscore(key: string, member: string): Promise<string | null>;
53
+ hkeys(key: string): Promise<string[]>;
54
+ }
55
+ /** Minimal adapter interface (only what we need for driver detection). */
56
+ interface BoringAdapter {
57
+ constructor: {
58
+ name: string;
59
+ };
60
+ }
61
+ /** Minimal QueueManagerSingleton (only what we call). */
62
+ interface BoringQueueManager {
63
+ use(name?: string): BoringAdapter;
64
+ }
65
+ /** Config shape for @adonisjs/queue. */
66
+ interface QueueConfig {
67
+ default?: string;
68
+ adapters?: Record<string, {
69
+ connectionName?: string;
70
+ }>;
71
+ queues?: Record<string, unknown>;
72
+ }
73
+ /** Job counts aggregated across all configured queues. */
74
+ export interface QueueCounts {
75
+ active: number;
76
+ waiting: number;
77
+ delayed: number;
78
+ completed: number;
79
+ failed: number;
80
+ }
81
+ /** Thin interface implemented by both the database and redis readers. */
82
+ export interface QueueStoreReader {
83
+ getCounts(): Promise<QueueCounts>;
84
+ listJobs(status: QueueJobSummary['status'] | 'all', page: number, perPage: number): Promise<QueueJobListResult>;
85
+ getJob(id: string): Promise<QueueJobDetail | null>;
86
+ retryJob(id: string): Promise<boolean>;
87
+ getWorkerCount(): Promise<number>;
88
+ }
89
+ /**
90
+ * Map a @boringnode `JobRecord` (plus optional DB `acquired_at` timestamp) to
91
+ * the `QueueJobSummary` shape used by the dashboard.
92
+ *
93
+ * Known gaps (no per-row data; documented inline):
94
+ * - `progress`: always 0 — @boringnode/queue does not persist per-row progress.
95
+ * - `returnValue`: always null — not stored in the queue table/hash.
96
+ * - `maxAttempts`: best-effort from `data.maxRetries`; falls back to `attempts`.
97
+ * - `stackTrace`: collapsed to `[record.error]`; no real stack trace in store.
98
+ *
99
+ * @param record The raw job record from the store.
100
+ * @param acquiredAtMs Unix-ms timestamp from DB `acquired_at` column, or null.
101
+ */
102
+ export declare function mapJobRecordToSummary(record: BoringJobRecord, acquiredAtMs: number | null): QueueJobSummary;
103
+ /** Pre-resolved services, accepted by the factory for both call paths. */
104
+ export interface QueueStoreReaderServices {
105
+ queueManager: BoringQueueManager;
106
+ config: QueueConfig;
107
+ /** Lucid db instance — required for the database driver. */
108
+ db?: LucidDb;
109
+ /** AdonisJS redis manager — required for the redis driver. */
110
+ redis?: AdonisRedisManager;
111
+ /** Override the database/redis connection name derived from config. */
112
+ connectionName?: string;
113
+ }
114
+ /**
115
+ * Build a {@link QueueStoreReader} from already-resolved services.
116
+ *
117
+ * Both the inspector path (`app.container.make`) and the collector path
118
+ * (`appImport`) resolve their own services and pass them here.
119
+ * Returns a safe-defaults no-op reader if driver detection fails or the
120
+ * required service (db or redis) is absent.
121
+ */
122
+ export declare function buildQueueStoreReader(services: QueueStoreReaderServices): QueueStoreReader;
123
+ /** Minimal shape of the AdonisJS Application instance we depend on. */
124
+ interface ApplicationLike {
125
+ config: {
126
+ get(key: string): unknown;
127
+ };
128
+ container: {
129
+ make(binding: string): Promise<unknown>;
130
+ };
131
+ }
132
+ /**
133
+ * Resolve services from an AdonisJS Application instance and build a store reader.
134
+ *
135
+ * This is the canonical resolver: the queue config lives in the app's
136
+ * `config/queue.ts` (there is no package-level config export), so it can only
137
+ * be read from `app.config.get('queue')`. Both the inspector and collector
138
+ * paths funnel through here.
139
+ *
140
+ * Returns null if @adonisjs/queue is not registered (e.g. wrong environment).
141
+ */
142
+ export declare function resolveFromApplication(app: ApplicationLike): Promise<QueueStoreReader | null>;
143
+ /**
144
+ * Resolve services for the inspector path.
145
+ *
146
+ * Accepts either a full Application-like object (with `config` + `container`)
147
+ * or a bare container (legacy). When given a bare container it resolves the
148
+ * application via the `app` binding to read the queue config.
149
+ *
150
+ * Returns null if @adonisjs/queue is not registered (e.g. wrong environment).
151
+ */
152
+ export declare function resolveFromContainer(appOrContainer: ApplicationLike | {
153
+ make(binding: string): Promise<unknown>;
154
+ }): Promise<QueueStoreReader | null>;
155
+ /**
156
+ * Resolve services via `appImport` and build a store reader.
157
+ *
158
+ * Designed for the collector path where no IoC container reference is held.
159
+ * Resolves the running Application via `@adonisjs/core/services/app` (the same
160
+ * service the official `@adonisjs/queue` package uses), then reads the queue
161
+ * config and services from it.
162
+ *
163
+ * Returns null if @adonisjs/queue is not installed in the host application.
164
+ */
165
+ export declare function resolveFromAppImport(): Promise<QueueStoreReader | null>;
166
+ export {};