@getworkbench/core 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,655 +1,6 @@
1
1
  import { Queue, RedisOptions } from 'bullmq';
2
- import { Hono } from 'hono';
3
-
4
- /**
5
- * Job status types matching BullMQ states
6
- */
7
- type JobStatus = "waiting" | "active" | "completed" | "failed" | "delayed" | "paused" | "unknown";
8
- /**
9
- * Configuration options for Workbench
10
- */
11
- interface WorkbenchOptions {
12
- /** BullMQ Queue instances to display */
13
- queues?: Queue[];
14
- /** Redis connection for auto-discovery of queues */
15
- redis?: string | RedisOptions;
16
- /** Basic auth credentials */
17
- auth?: {
18
- username: string;
19
- password: string;
20
- };
21
- /** Dashboard title */
22
- title?: string;
23
- /** Logo URL */
24
- logo?: string;
25
- /** Override base path detection */
26
- basePath?: string;
27
- /** Disable actions (retry, remove, promote) */
28
- readonly?: boolean;
29
- /** Fields from job.data to extract as filterable tags (e.g., ['teamId', 'userId']) */
30
- tags?: string[];
31
- }
32
- /**
33
- * Queue information for API responses
34
- */
35
- interface QueueInfo {
36
- name: string;
37
- counts: {
38
- waiting: number;
39
- active: number;
40
- completed: number;
41
- failed: number;
42
- delayed: number;
43
- paused: number;
44
- };
45
- isPaused: boolean;
46
- }
47
- /**
48
- * Worker information from BullMQ
49
- */
50
- interface WorkerInfo {
51
- id: string;
52
- name: string;
53
- addr: string;
54
- age: number;
55
- idle: number;
56
- started: number;
57
- queueName: string;
58
- }
59
- /**
60
- * Extracted tag key-value pairs from job data
61
- */
62
- type JobTags = Record<string, string | number | boolean | null>;
63
- /**
64
- * Job information for API responses
65
- */
66
- interface JobInfo {
67
- id: string;
68
- name: string;
69
- data: unknown;
70
- opts: {
71
- attempts?: number;
72
- delay?: number;
73
- priority?: number;
74
- };
75
- progress: number | object;
76
- attemptsMade: number;
77
- processedOn?: number;
78
- finishedOn?: number;
79
- timestamp: number;
80
- failedReason?: string;
81
- stacktrace?: string[];
82
- returnvalue?: unknown;
83
- status: JobStatus;
84
- duration?: number;
85
- /** Extracted tag values from job.data based on configured tag fields */
86
- tags?: JobTags;
87
- /** Parent job info if this job is part of a flow */
88
- parent?: {
89
- id: string;
90
- queueName: string;
91
- };
92
- }
93
- /**
94
- * Overview stats for dashboard
95
- */
96
- interface OverviewStats {
97
- totalJobs: number;
98
- activeJobs: number;
99
- failedJobs: number;
100
- completedToday: number;
101
- avgDuration: number;
102
- queues: QueueInfo[];
103
- }
104
- /**
105
- * Paginated response wrapper
106
- */
107
- interface PaginatedResponse<T> {
108
- data: T[];
109
- total: number;
110
- cursor?: string;
111
- hasMore: boolean;
112
- }
113
- /**
114
- * Search result item
115
- */
116
- interface SearchResult {
117
- queue: string;
118
- job: JobInfo;
119
- }
120
- /**
121
- * Run item - job execution with queue context
122
- */
123
- interface RunInfo extends JobInfo {
124
- queueName: string;
125
- }
126
- /**
127
- * Lightweight run info for list view - only fields needed for table display
128
- * Excludes large fields like full job.data, opts, progress, etc.
129
- */
130
- interface RunInfoList {
131
- id: string;
132
- name: string;
133
- status: JobStatus;
134
- queueName: string;
135
- tags?: JobTags;
136
- processedOn?: number;
137
- timestamp: number;
138
- duration?: number;
139
- }
140
- /**
141
- * Scheduler info for repeatable jobs
142
- */
143
- interface SchedulerInfo {
144
- key: string;
145
- name: string;
146
- queueName: string;
147
- pattern?: string;
148
- every?: number;
149
- next?: number;
150
- endDate?: number;
151
- tz?: string;
152
- }
153
- /**
154
- * Delayed job info
155
- */
156
- interface DelayedJobInfo {
157
- id: string;
158
- name: string;
159
- queueName: string;
160
- delay: number;
161
- processAt: number;
162
- data: unknown;
163
- }
164
- /**
165
- * Test job request
166
- */
167
- interface TestJobRequest {
168
- queueName: string;
169
- jobName: string;
170
- data: unknown;
171
- opts?: {
172
- delay?: number;
173
- priority?: number;
174
- attempts?: number;
175
- };
176
- }
177
- /**
178
- * Sort direction
179
- */
180
- type SortDirection = "asc" | "desc";
181
- /**
182
- * Sort options for API requests
183
- */
184
- interface SortOptions {
185
- field: string;
186
- direction: SortDirection;
187
- }
188
- /**
189
- * Valid sort fields for runs/jobs
190
- */
191
- type RunSortField = "timestamp" | "name" | "status" | "duration" | "queueName";
192
- /**
193
- * Valid sort fields for repeatable schedulers
194
- */
195
- type RepeatableSortField = "name" | "queueName" | "pattern" | "next" | "tz";
196
- /**
197
- * Valid sort fields for delayed schedulers
198
- */
199
- type DelayedSortField = "name" | "queueName" | "processAt" | "delay";
200
- /**
201
- * Hourly bucket for metrics aggregation
202
- */
203
- interface HourlyBucket {
204
- /** Unix timestamp (start of hour) */
205
- hour: number;
206
- /** Number of completed jobs */
207
- completed: number;
208
- /** Number of failed jobs */
209
- failed: number;
210
- /** Average processing duration in ms */
211
- avgDuration: number;
212
- /** Average queue wait time in ms */
213
- avgWaitTime: number;
214
- }
215
- /**
216
- * Metrics for a single queue
217
- */
218
- interface QueueMetrics {
219
- queueName: string;
220
- buckets: HourlyBucket[];
221
- summary: {
222
- totalCompleted: number;
223
- totalFailed: number;
224
- /** Error rate as 0-1 */
225
- errorRate: number;
226
- /** Average processing duration in ms */
227
- avgDuration: number;
228
- /** Average queue wait time in ms */
229
- avgWaitTime: number;
230
- /** Average throughput per hour */
231
- throughputPerHour: number;
232
- };
233
- }
234
- /**
235
- * Slowest job entry
236
- */
237
- interface SlowestJob {
238
- name: string;
239
- queueName: string;
240
- duration: number;
241
- jobId: string;
242
- }
243
- /**
244
- * Most failing job type entry
245
- */
246
- interface FailingJobType {
247
- name: string;
248
- queueName: string;
249
- failCount: number;
250
- totalCount: number;
251
- errorRate: number;
252
- }
253
- /**
254
- * Complete metrics response
255
- */
256
- interface MetricsResponse {
257
- /** Metrics per queue */
258
- queues: QueueMetrics[];
259
- /** Aggregated metrics across all queues */
260
- aggregate: Omit<QueueMetrics, "queueName"> & {
261
- queueName: "all";
262
- };
263
- /** Top 10 slowest jobs */
264
- slowestJobs: SlowestJob[];
265
- /** Top 10 most failing job types */
266
- mostFailingTypes: FailingJobType[];
267
- /** Timestamp when metrics were computed */
268
- computedAt: number;
269
- }
270
- /**
271
- * A node in a flow tree representing a job and its children
272
- */
273
- interface FlowNode {
274
- job: JobInfo;
275
- queueName: string;
276
- children?: FlowNode[];
277
- }
278
- /**
279
- * Flow summary for list view
280
- */
281
- interface FlowSummary {
282
- /** Root job ID */
283
- id: string;
284
- /** Root job name */
285
- name: string;
286
- /** Queue containing root job */
287
- queueName: string;
288
- /** Root job status */
289
- status: JobStatus;
290
- /** Total number of jobs in flow */
291
- totalJobs: number;
292
- /** Number of completed jobs */
293
- completedJobs: number;
294
- /** Number of failed jobs */
295
- failedJobs: number;
296
- /** When flow was created */
297
- timestamp: number;
298
- /** Duration if completed */
299
- duration?: number;
300
- }
301
- /**
302
- * Request to create a test flow
303
- */
304
- interface CreateFlowRequest {
305
- name: string;
306
- queueName: string;
307
- data?: unknown;
308
- children: CreateFlowChildRequest[];
309
- }
310
- /**
311
- * Child job in a flow creation request
312
- */
313
- interface CreateFlowChildRequest {
314
- name: string;
315
- queueName: string;
316
- data?: unknown;
317
- children?: CreateFlowChildRequest[];
318
- }
319
- /**
320
- * Activity bucket for timeline
321
- */
322
- interface ActivityBucket {
323
- /** Unix timestamp (start of bucket) */
324
- time: number;
325
- /** Number of completed jobs */
326
- completed: number;
327
- /** Number of failed jobs */
328
- failed: number;
329
- }
330
- /**
331
- * Activity stats response for the 7-day timeline
332
- */
333
- interface ActivityStatsResponse {
334
- /** Activity buckets (4-hour intervals over 7 days) */
335
- buckets: ActivityBucket[];
336
- /** Start time of the first bucket */
337
- startTime: number;
338
- /** End time (now) */
339
- endTime: number;
340
- /** Size of each bucket in ms */
341
- bucketSize: number;
342
- /** Total completed in period */
343
- totalCompleted: number;
344
- /** Total failed in period */
345
- totalFailed: number;
346
- /** Timestamp when stats were computed */
347
- computedAt: number;
348
- }
349
-
350
- /**
351
- * Manages queue operations for the Workbench dashboard
352
- */
353
- declare class QueueManager {
354
- private queues;
355
- private tagFields;
356
- private flowProducer;
357
- private cache;
358
- private readonly CACHE_TTL;
359
- constructor(queues: Queue[], tagFields?: string[]);
360
- /**
361
- * Get cached value or compute and cache
362
- */
363
- private cached;
364
- /**
365
- * Execute a promise with a timeout
366
- */
367
- private withTimeout;
368
- /**
369
- * Get jobs by time range using Redis sorted sets (ZRANGEBYSCORE)
370
- * This is more efficient than fetching all jobs and filtering in memory
371
- */
372
- private getJobsByTimeRange;
373
- /**
374
- * Cache for job state lookups to avoid repeated Redis calls
375
- */
376
- private jobStateCache;
377
- /**
378
- * Cache for job counts to avoid repeated Redis calls
379
- * Short TTL since counts change frequently but are expensive to fetch
380
- */
381
- private countCache;
382
- /**
383
- * Get job counts with caching
384
- */
385
- private getCachedJobCounts;
386
- /**
387
- * Invalidate caches related to a job or queue
388
- */
389
- private invalidateJobCache;
390
- /**
391
- * Clear cache (useful after mutations)
392
- */
393
- clearCache(prefix?: string): void;
394
- /**
395
- * Get quick job counts across all queues (lightweight, for smart polling)
396
- * Returns total counts per status - cached and very fast
397
- */
398
- getQuickCounts(): Promise<{
399
- waiting: number;
400
- active: number;
401
- completed: number;
402
- failed: number;
403
- delayed: number;
404
- total: number;
405
- timestamp: number;
406
- }>;
407
- /**
408
- * Get configured tag field names
409
- */
410
- getTagFields(): string[];
411
- /**
412
- * Get just queue names (very fast, no Redis calls)
413
- * Used for sidebar initial render
414
- */
415
- getQueueNames(): string[];
416
- /**
417
- * Get a queue by name
418
- */
419
- getQueue(name: string): Queue | undefined;
420
- /**
421
- * Get information for all queues (cached)
422
- */
423
- getQueues(): Promise<QueueInfo[]>;
424
- /**
425
- * Get overview statistics (cached)
426
- */
427
- getOverview(): Promise<OverviewStats>;
428
- /**
429
- * Pause a queue - stops processing new jobs
430
- */
431
- pauseQueue(queueName: string): Promise<void>;
432
- /**
433
- * Resume a paused queue
434
- */
435
- resumeQueue(queueName: string): Promise<void>;
436
- /**
437
- * Check if a queue is paused
438
- */
439
- isQueuePaused(queueName: string): Promise<boolean>;
440
- /**
441
- * Get metrics for the last 24 hours (cached - expensive operation)
442
- */
443
- getMetrics(): Promise<MetricsResponse>;
444
- /**
445
- * Get activity stats for the last 7 days (cached)
446
- * Returns 4-hour buckets for the activity timeline
447
- */
448
- getActivityStats(): Promise<ActivityStatsResponse>;
449
- /**
450
- * Get jobs for a specific queue with pagination and sorting
451
- */
452
- getJobs(queueName: string, status?: JobStatus, limit?: number, start?: number, sort?: SortOptions): Promise<PaginatedResponse<JobInfo>>;
453
- /**
454
- * Get a single job by ID
455
- */
456
- getJob(queueName: string, jobId: string): Promise<JobInfo | null>;
457
- /**
458
- * Retry a failed job
459
- */
460
- retryJob(queueName: string, jobId: string): Promise<boolean>;
461
- /**
462
- * Remove a job
463
- */
464
- removeJob(queueName: string, jobId: string): Promise<boolean>;
465
- /**
466
- * Promote a delayed job to waiting
467
- */
468
- promoteJob(queueName: string, jobId: string): Promise<boolean>;
469
- /**
470
- * Parse search query for field:value filters
471
- * Returns { filters: { field: value }, text: remainingText }
472
- */
473
- private parseSearchQuery;
474
- /**
475
- * Check if a raw job matches all provided filters (before conversion)
476
- * This is more efficient than converting to JobInfo first
477
- */
478
- private jobMatchesAllFilters;
479
- /**
480
- * Check if a job matches the given tag filters
481
- */
482
- private jobMatchesFilters;
483
- /**
484
- * Search jobs across all queues
485
- * Supports field:value syntax (e.g., "teamId:abc-123 invoice")
486
- * Optimized with parallel processing, early exits, and count checks
487
- */
488
- search(query: string, limit?: number): Promise<SearchResult[]>;
489
- /**
490
- * Clean jobs from a queue
491
- */
492
- cleanJobs(queueName: string, status: "completed" | "failed", grace?: number): Promise<number>;
493
- /**
494
- * FAST PATH: Get latest runs without filters
495
- * Optimized for the common case of viewing newest jobs (timestamp desc, no filters)
496
- * - Single getJobs call per queue (not per status type)
497
- * - No count checks needed
498
- * - Minimal Redis round-trips
499
- */
500
- private getLatestRuns;
501
- /**
502
- * Get all runs (jobs) across all queues with sorting and filtering
503
- * Uses fast path for common case (no filters, timestamp desc)
504
- */
505
- getAllRuns(limit?: number, start?: number, sort?: SortOptions, filters?: {
506
- status?: JobStatus;
507
- tags?: Record<string, string>;
508
- text?: string;
509
- timeRange?: {
510
- start: number;
511
- end: number;
512
- };
513
- }): Promise<PaginatedResponse<RunInfoList>>;
514
- /**
515
- * Get all schedulers (repeatable and delayed jobs) with sorting
516
- */
517
- getSchedulers(repeatableSort?: SortOptions, delayedSort?: SortOptions): Promise<{
518
- repeatable: SchedulerInfo[];
519
- delayed: DelayedJobInfo[];
520
- }>;
521
- /**
522
- * Enqueue a new job (for testing)
523
- */
524
- enqueueJob(request: TestJobRequest): Promise<{
525
- id: string;
526
- }>;
527
- /**
528
- * Extract tag values from job data based on configured tag fields
529
- */
530
- private extractTags;
531
- /**
532
- * Get unique values for a specific tag field across all jobs
533
- */
534
- getTagValues(field: string, limit?: number): Promise<{
535
- value: string;
536
- count: number;
537
- }[]>;
538
- /**
539
- * Get sortable value from JobInfo/RunInfo
540
- */
541
- private getSortValue;
542
- /**
543
- * Get sortable value from RunInfoList (lightweight version)
544
- */
545
- private getSortValueForList;
546
- /**
547
- * Get sortable value from SchedulerInfo
548
- */
549
- private getSchedulerSortValue;
550
- /**
551
- * Get sortable value from DelayedJobInfo
552
- */
553
- private getDelayedSortValue;
554
- /**
555
- * Convert a BullMQ Job to JobInfo or RunInfoList
556
- * @param job - The BullMQ job to convert
557
- * @param fields - "list" for lightweight list view, "full" for complete job details
558
- * @param knownState - Optional: skip getState() call if state is already known from fetch
559
- */
560
- private jobToInfo;
561
- /**
562
- * Retry multiple jobs across queues
563
- * Processed in parallel for better performance
564
- */
565
- bulkRetry(jobs: {
566
- queueName: string;
567
- jobId: string;
568
- }[]): Promise<{
569
- success: number;
570
- failed: number;
571
- }>;
572
- /**
573
- * Delete multiple jobs across queues
574
- * Processed in parallel for better performance
575
- */
576
- bulkDelete(jobs: {
577
- queueName: string;
578
- jobId: string;
579
- }[]): Promise<{
580
- success: number;
581
- failed: number;
582
- }>;
583
- /**
584
- * Promote multiple delayed jobs across queues (move to waiting)
585
- * Processed in parallel for better performance
586
- */
587
- bulkPromote(jobs: {
588
- queueName: string;
589
- jobId: string;
590
- }[]): Promise<{
591
- success: number;
592
- failed: number;
593
- }>;
594
- /**
595
- * Get all flows (jobs that have children or are part of a flow) - cached
596
- * Optimized to focus on waiting-children type first and early exit
597
- */
598
- getFlows(limit?: number): Promise<FlowSummary[]>;
599
- /**
600
- * Get a single flow tree by root job ID
601
- */
602
- getFlow(queueName: string, jobId: string): Promise<FlowNode | null>;
603
- /**
604
- * Create a new flow
605
- */
606
- createFlow(request: CreateFlowRequest): Promise<{
607
- id: string;
608
- }>;
609
- /**
610
- * Build a FlowJob from CreateFlowRequest or CreateFlowChildRequest
611
- */
612
- private buildFlowJob;
613
- /**
614
- * Convert BullMQ flow tree to our FlowNode structure
615
- */
616
- private convertFlowTree;
617
- /**
618
- * Count statistics for a flow tree
619
- */
620
- private countFlowStats;
621
- }
622
-
623
- /**
624
- * Core Workbench class that manages the dashboard
625
- */
626
- declare class WorkbenchCore {
627
- readonly options: Required<Pick<WorkbenchOptions, "title" | "readonly">> & WorkbenchOptions;
628
- readonly queueManager: QueueManager;
629
- constructor(options: WorkbenchOptions | Queue[]);
630
- /**
631
- * Get the queue manager instance
632
- */
633
- getQueueManager(): QueueManager;
634
- /**
635
- * Check if authentication is required
636
- */
637
- requiresAuth(): boolean;
638
- /**
639
- * Validate authentication credentials
640
- */
641
- validateAuth(username: string, password: string): boolean;
642
- /**
643
- * Get dashboard configuration for the UI
644
- */
645
- getConfig(): {
646
- title: string;
647
- logo: string | undefined;
648
- readonly: boolean;
649
- queues: string[];
650
- tags: string[];
651
- };
652
- }
2
+ import { W as WorkbenchCore, r as WorkbenchOptions } from './workbench-CRdU4cB7.js';
3
+ export { A as ActivityBucket, a as ActivityStatsResponse, C as CreateFlowChildRequest, b as CreateFlowRequest, D as DelayedJobInfo, c as DelayedSortField, d as DiscoveryMeta, F as FailingJobType, e as FlowNode, f as FlowSummary, H as HourlyBucket, J as JobInfo, g as JobStatus, h as JobTags, M as MetricsResponse, O as OverviewStats, P as PaginatedResponse, Q as QueueInfo, i as QueueManager, j as QueueMetrics, R as RepeatableSortField, k as RunInfo, l as RunInfoList, m as RunSortField, S as SchedulerInfo, n as SearchResult, o as SlowestJob, p as SortDirection, q as SortOptions, T as TestJobRequest, s as WorkerInfo } from './workbench-CRdU4cB7.js';
653
4
 
654
5
  interface FetchHandlerResult {
655
6
  /**
@@ -730,14 +81,14 @@ interface RouteDef {
730
81
  declare function buildRouteTable(core: WorkbenchCore): RouteDef[];
731
82
 
732
83
  /**
733
- * Create API routes for Workbench as a Hono app.
84
+ * Discover BullMQ queues on a Redis connection by scanning for `<prefix>:*:meta`
85
+ * keys. Returns one `Queue` instance per discovered queue, each constructed
86
+ * with a fresh clone of the connection options.
734
87
  *
735
- * Iterates the framework-agnostic `buildRouteTable(core)` and registers
736
- * each route on a fresh Hono instance. Adapters that don't speak Hono can
737
- * use `buildRouteTable` directly — see `@getworkbench/express` and
738
- * `@getworkbench/fastify`.
88
+ * Used by `WorkbenchCore.fromOptions` for the desktop client where the user
89
+ * supplies a Redis URL but no explicit queue list.
739
90
  */
740
- declare function createApiRoutes(core: WorkbenchCore): Hono;
91
+ declare function discoverQueues(connection: string | RedisOptions, prefix?: string): Promise<Queue[]>;
741
92
 
742
93
  declare function computeBasePath(pathname: string): string;
743
94
  /**
@@ -774,21 +125,6 @@ declare const BASIC_AUTH_CHALLENGE: {
774
125
  body: string;
775
126
  };
776
127
 
777
- /**
778
- * Build a fully-wired Hono app for Workbench:
779
- *
780
- * - `POST /api/*`, `GET /api/*` etc. — JSON API
781
- * - `GET /config` — UI bootstrap config
782
- * - `GET /assets/:file` — static asset reader
783
- * - `GET *` — `index.html` with `<base href>`
784
- * - CORS on `/api/*`
785
- * - Basic auth on everything when `core.requiresAuth()` is true
786
- *
787
- * Used directly by `@getworkbench/hono` (returned as-is for `.route()`
788
- * mounting) and indirectly by `createFetchHandler` for non-Hono adapters.
789
- */
790
- declare function buildWorkbenchApp(core: WorkbenchCore): Hono;
791
-
792
128
  interface StaticAssetResult {
793
129
  status: 200 | 404;
794
130
  body: Buffer | null;
@@ -822,4 +158,4 @@ declare function renderIndexHtml(basePath: string, title: string): IndexHtmlResu
822
158
  */
823
159
  declare const UI_DIST_PATH: string;
824
160
 
825
- export { type ActivityBucket, type ActivityStatsResponse, BASIC_AUTH_CHALLENGE, type CreateFlowChildRequest, type CreateFlowRequest, type DelayedJobInfo, type DelayedSortField, type FailingJobType, type FetchHandlerResult, type FlowNode, type FlowSummary, type Handler, type HandlerInput, type HandlerResult, type HourlyBucket, type HttpMethod, type IndexHtmlResult, type JobInfo, type JobStatus, type JobTags, type MetricsResponse, type OverviewStats, type PaginatedResponse, type QueueInfo, QueueManager, type QueueMetrics, type RepeatableSortField, type RouteDef, type RunInfo, type RunInfoList, type RunSortField, type SchedulerInfo, type SearchResult, type SlowestJob, type SortDirection, type SortOptions, type StaticAssetResult, type TestJobRequest, UI_DIST_PATH, WorkbenchCore, type WorkbenchOptions, type WorkerInfo, buildRouteTable, buildWorkbenchApp, checkBasicAuth, computeBasePath, createApiRoutes, createFetchHandler, renderIndexHtml, resolveBasePath, serveStaticAsset };
161
+ export { BASIC_AUTH_CHALLENGE, type FetchHandlerResult, type Handler, type HandlerInput, type HandlerResult, type HttpMethod, type IndexHtmlResult, type RouteDef, type StaticAssetResult, UI_DIST_PATH, WorkbenchCore, WorkbenchOptions, buildRouteTable, checkBasicAuth, computeBasePath, createFetchHandler, discoverQueues, renderIndexHtml, resolveBasePath, serveStaticAsset };