@maydotinc/q-studio 0.1.0 → 0.8.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.
@@ -1,864 +0,0 @@
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
- * Group/folder of related BullMQ queues.
10
- */
11
- interface QueueGroup {
12
- name: string;
13
- queues: Queue[];
14
- }
15
- /**
16
- * Configuration options for Workbench
17
- */
18
- interface WorkbenchOptions {
19
- /** BullMQ Queue instances to display */
20
- queues?: Queue[];
21
- /** Named groups/folders of BullMQ Queue instances to display together */
22
- queueGroups?: QueueGroup[];
23
- /** Alias for queueGroups */
24
- groups?: QueueGroup[];
25
- /** Redis connection for auto-discovery of queues */
26
- redis?: string | RedisOptions;
27
- /** Basic auth credentials */
28
- auth?: {
29
- username: string;
30
- password: string;
31
- };
32
- /** Dashboard title */
33
- title?: string;
34
- /** Logo URL */
35
- logo?: string;
36
- /** Override base path detection */
37
- basePath?: string;
38
- /** Disable actions (retry, remove, promote) */
39
- readonly?: boolean;
40
- /** Fields from job.data to extract as filterable tags (e.g., ['teamId', 'userId']) */
41
- tags?: string[];
42
- }
43
- /**
44
- * Queue information for API responses
45
- */
46
- interface QueueInfo {
47
- name: string;
48
- group?: string;
49
- counts: {
50
- waiting: number;
51
- active: number;
52
- completed: number;
53
- failed: number;
54
- delayed: number;
55
- paused: number;
56
- };
57
- isPaused: boolean;
58
- }
59
- /**
60
- * Queue group information for API responses
61
- */
62
- interface QueueGroupInfo {
63
- name: string;
64
- queues: string[];
65
- }
66
- /**
67
- * Worker information from BullMQ
68
- */
69
- interface WorkerInfo {
70
- id: string;
71
- name: string;
72
- addr: string;
73
- age: number;
74
- idle: number;
75
- started: number;
76
- queueName: string;
77
- }
78
- /**
79
- * Extracted tag key-value pairs from job data
80
- */
81
- type JobTags = Record<string, string | number | boolean | null>;
82
- /**
83
- * Job information for API responses
84
- */
85
- interface JobInfo {
86
- id: string;
87
- name: string;
88
- data: unknown;
89
- opts: {
90
- attempts?: number;
91
- delay?: number;
92
- priority?: number;
93
- };
94
- progress: number | object;
95
- attemptsMade: number;
96
- processedOn?: number;
97
- finishedOn?: number;
98
- timestamp: number;
99
- failedReason?: string;
100
- stacktrace?: string[];
101
- returnvalue?: unknown;
102
- status: JobStatus;
103
- duration?: number;
104
- /** Extracted tag values from job.data based on configured tag fields */
105
- tags?: JobTags;
106
- /** Parent job info if this job is part of a flow */
107
- parent?: {
108
- id: string;
109
- queueName: string;
110
- };
111
- }
112
- /**
113
- * BullMQ log lines for a job.
114
- */
115
- interface JobLogsResponse {
116
- logs: string[];
117
- count: number;
118
- start: number;
119
- end: number;
120
- }
121
- /**
122
- * Overview stats for dashboard
123
- */
124
- interface OverviewStats {
125
- totalJobs: number;
126
- activeJobs: number;
127
- failedJobs: number;
128
- completedToday: number;
129
- avgDuration: number;
130
- queues: QueueInfo[];
131
- }
132
- /**
133
- * Paginated response wrapper
134
- */
135
- interface PaginatedResponse<T> {
136
- data: T[];
137
- total: number;
138
- cursor?: string;
139
- hasMore: boolean;
140
- }
141
- /**
142
- * Search result item
143
- */
144
- interface SearchResult {
145
- queue: string;
146
- job: JobInfo;
147
- }
148
- /**
149
- * Run item - job execution with queue context
150
- */
151
- interface RunInfo extends JobInfo {
152
- queueName: string;
153
- }
154
- /**
155
- * Lightweight run info for list view - only fields needed for table display
156
- * Excludes large fields like full job.data, opts, progress, etc.
157
- */
158
- interface RunInfoList {
159
- id: string;
160
- name: string;
161
- status: JobStatus;
162
- queueName: string;
163
- tags?: JobTags;
164
- processedOn?: number;
165
- timestamp: number;
166
- duration?: number;
167
- }
168
- /**
169
- * Scheduler info for repeatable jobs
170
- */
171
- interface SchedulerInfo {
172
- key: string;
173
- name: string;
174
- queueName: string;
175
- pattern?: string;
176
- every?: number;
177
- next?: number;
178
- endDate?: number;
179
- tz?: string;
180
- }
181
- /**
182
- * Delayed job info
183
- */
184
- interface DelayedJobInfo {
185
- id: string;
186
- name: string;
187
- queueName: string;
188
- delay: number;
189
- processAt: number;
190
- data: unknown;
191
- }
192
- /**
193
- * Test job request
194
- */
195
- interface TestJobRequest {
196
- queueName: string;
197
- jobName: string;
198
- data: unknown;
199
- opts?: {
200
- delay?: number;
201
- priority?: number;
202
- attempts?: number;
203
- };
204
- }
205
- /**
206
- * Sort direction
207
- */
208
- type SortDirection = "asc" | "desc";
209
- /**
210
- * Sort options for API requests
211
- */
212
- interface SortOptions {
213
- field: string;
214
- direction: SortDirection;
215
- }
216
- /**
217
- * Valid sort fields for runs/jobs
218
- */
219
- type RunSortField = "timestamp" | "name" | "status" | "duration" | "queueName";
220
- /**
221
- * Valid sort fields for repeatable schedulers
222
- */
223
- type RepeatableSortField = "name" | "queueName" | "pattern" | "next" | "tz";
224
- /**
225
- * Valid sort fields for delayed schedulers
226
- */
227
- type DelayedSortField = "name" | "queueName" | "processAt" | "delay";
228
- /**
229
- * Hourly bucket for metrics aggregation
230
- */
231
- interface HourlyBucket {
232
- /** Unix timestamp (start of hour) */
233
- hour: number;
234
- /** Number of completed jobs */
235
- completed: number;
236
- /** Number of failed jobs */
237
- failed: number;
238
- /** Average processing duration in ms */
239
- avgDuration: number;
240
- /** Average queue wait time in ms */
241
- avgWaitTime: number;
242
- }
243
- /**
244
- * Metrics for a single queue
245
- */
246
- interface QueueMetrics {
247
- queueName: string;
248
- buckets: HourlyBucket[];
249
- summary: {
250
- totalCompleted: number;
251
- totalFailed: number;
252
- /** Error rate as 0-1 */
253
- errorRate: number;
254
- /** Average processing duration in ms */
255
- avgDuration: number;
256
- /** Average queue wait time in ms */
257
- avgWaitTime: number;
258
- /** Average throughput per hour */
259
- throughputPerHour: number;
260
- };
261
- }
262
- /**
263
- * Slowest job entry
264
- */
265
- interface SlowestJob {
266
- name: string;
267
- queueName: string;
268
- duration: number;
269
- jobId: string;
270
- }
271
- /**
272
- * Most failing job type entry
273
- */
274
- interface FailingJobType {
275
- name: string;
276
- queueName: string;
277
- failCount: number;
278
- totalCount: number;
279
- errorRate: number;
280
- }
281
- /**
282
- * Complete metrics response
283
- */
284
- interface MetricsResponse {
285
- /** Metrics per queue */
286
- queues: QueueMetrics[];
287
- /** Aggregated metrics across all queues */
288
- aggregate: Omit<QueueMetrics, "queueName"> & {
289
- queueName: "all";
290
- };
291
- /** Top 10 slowest jobs */
292
- slowestJobs: SlowestJob[];
293
- /** Top 10 most failing job types */
294
- mostFailingTypes: FailingJobType[];
295
- /** Timestamp when metrics were computed */
296
- computedAt: number;
297
- }
298
- /**
299
- * A node in a flow tree representing a job and its children
300
- */
301
- interface FlowNode {
302
- job: JobInfo;
303
- queueName: string;
304
- children?: FlowNode[];
305
- }
306
- /**
307
- * Flow summary for list view
308
- */
309
- interface FlowSummary {
310
- /** Root job ID */
311
- id: string;
312
- /** Root job name */
313
- name: string;
314
- /** Queue containing root job */
315
- queueName: string;
316
- /** Root job status */
317
- status: JobStatus;
318
- /** Total number of jobs in flow */
319
- totalJobs: number;
320
- /** Number of completed jobs */
321
- completedJobs: number;
322
- /** Number of failed jobs */
323
- failedJobs: number;
324
- /** When flow was created */
325
- timestamp: number;
326
- /** Duration if completed */
327
- duration?: number;
328
- }
329
- /**
330
- * Request to create a test flow
331
- */
332
- interface CreateFlowRequest {
333
- name: string;
334
- queueName: string;
335
- data?: unknown;
336
- children: CreateFlowChildRequest[];
337
- }
338
- /**
339
- * Child job in a flow creation request
340
- */
341
- interface CreateFlowChildRequest {
342
- name: string;
343
- queueName: string;
344
- data?: unknown;
345
- children?: CreateFlowChildRequest[];
346
- }
347
- /**
348
- * Activity bucket for timeline
349
- */
350
- interface ActivityBucket {
351
- /** Unix timestamp (start of bucket) */
352
- time: number;
353
- /** Number of completed jobs */
354
- completed: number;
355
- /** Number of failed jobs */
356
- failed: number;
357
- }
358
- /**
359
- * Activity stats response for the 7-day timeline
360
- */
361
- interface ActivityStatsResponse {
362
- /** Activity buckets (4-hour intervals over 7 days) */
363
- buckets: ActivityBucket[];
364
- /** Start time of the first bucket */
365
- startTime: number;
366
- /** End time (now) */
367
- endTime: number;
368
- /** Size of each bucket in ms */
369
- bucketSize: number;
370
- /** Total completed in period */
371
- totalCompleted: number;
372
- /** Total failed in period */
373
- totalFailed: number;
374
- /** Timestamp when stats were computed */
375
- computedAt: number;
376
- }
377
-
378
- /**
379
- * Manages queue operations for the Workbench dashboard
380
- */
381
- declare class QueueManager {
382
- private queues;
383
- private queueGroupByName;
384
- private queueGroups;
385
- private tagFields;
386
- private flowProducer;
387
- private cache;
388
- private readonly CACHE_TTL;
389
- constructor(queues: Queue[], tagFields?: string[], queueGroups?: QueueGroup[]);
390
- /**
391
- * Get cached value or compute and cache
392
- */
393
- private cached;
394
- /**
395
- * Execute a promise with a timeout
396
- */
397
- private withTimeout;
398
- /**
399
- * Get jobs by time range using Redis sorted sets (ZRANGEBYSCORE)
400
- * This is more efficient than fetching all jobs and filtering in memory
401
- */
402
- private getJobsByTimeRange;
403
- /**
404
- * Cache for job state lookups to avoid repeated Redis calls
405
- */
406
- private jobStateCache;
407
- /**
408
- * Cache for job counts to avoid repeated Redis calls
409
- * Short TTL since counts change frequently but are expensive to fetch
410
- */
411
- private countCache;
412
- /**
413
- * Get job counts with caching
414
- */
415
- private getCachedJobCounts;
416
- /**
417
- * Invalidate caches related to a job or queue
418
- */
419
- private invalidateJobCache;
420
- /**
421
- * Clear cache (useful after mutations)
422
- */
423
- clearCache(prefix?: string): void;
424
- /**
425
- * Get quick job counts across all queues (lightweight, for smart polling)
426
- * Returns total counts per status - cached and very fast
427
- */
428
- getQuickCounts(): Promise<{
429
- waiting: number;
430
- active: number;
431
- completed: number;
432
- failed: number;
433
- delayed: number;
434
- total: number;
435
- timestamp: number;
436
- }>;
437
- /**
438
- * Get configured tag field names
439
- */
440
- getTagFields(): string[];
441
- /**
442
- * Get just queue names (very fast, no Redis calls)
443
- * Used for sidebar initial render
444
- */
445
- getQueueNames(): string[];
446
- /**
447
- * Get configured queue groups (very fast, no Redis calls)
448
- */
449
- getQueueGroups(): QueueGroupInfo[];
450
- /**
451
- * Get a queue by name
452
- */
453
- getQueue(name: string): Queue | undefined;
454
- /**
455
- * Get information for all queues (cached)
456
- */
457
- getQueues(): Promise<QueueInfo[]>;
458
- /**
459
- * Get overview statistics (cached)
460
- */
461
- getOverview(): Promise<OverviewStats>;
462
- /**
463
- * Pause a queue - stops processing new jobs
464
- */
465
- pauseQueue(queueName: string): Promise<void>;
466
- /**
467
- * Resume a paused queue
468
- */
469
- resumeQueue(queueName: string): Promise<void>;
470
- /**
471
- * Check if a queue is paused
472
- */
473
- isQueuePaused(queueName: string): Promise<boolean>;
474
- /**
475
- * Get metrics for the last 24 hours (cached - expensive operation)
476
- */
477
- getMetrics(): Promise<MetricsResponse>;
478
- /**
479
- * Get activity stats for the last 7 days (cached)
480
- * Returns 4-hour buckets for the activity timeline
481
- */
482
- getActivityStats(): Promise<ActivityStatsResponse>;
483
- /**
484
- * Get jobs for a specific queue with pagination and sorting
485
- */
486
- getJobs(queueName: string, status?: JobStatus, limit?: number, start?: number, sort?: SortOptions): Promise<PaginatedResponse<JobInfo>>;
487
- /**
488
- * Get a single job by ID
489
- */
490
- getJob(queueName: string, jobId: string): Promise<JobInfo | null>;
491
- /**
492
- * Get BullMQ log lines for a job.
493
- */
494
- getJobLogs(queueName: string, jobId: string, start?: number, end?: number, asc?: boolean): Promise<JobLogsResponse | null>;
495
- /**
496
- * Retry a failed job
497
- */
498
- retryJob(queueName: string, jobId: string): Promise<boolean>;
499
- /**
500
- * Remove a job
501
- */
502
- removeJob(queueName: string, jobId: string): Promise<boolean>;
503
- /**
504
- * Promote a delayed job to waiting
505
- */
506
- promoteJob(queueName: string, jobId: string): Promise<boolean>;
507
- /**
508
- * Parse search query for field:value filters
509
- * Returns { filters: { field: value }, text: remainingText }
510
- */
511
- private parseSearchQuery;
512
- /**
513
- * Check if a raw job matches all provided filters (before conversion)
514
- * This is more efficient than converting to JobInfo first
515
- */
516
- private jobMatchesAllFilters;
517
- /**
518
- * Check if a job matches the given tag filters
519
- */
520
- private jobMatchesFilters;
521
- /**
522
- * Search jobs across all queues
523
- * Supports field:value syntax (e.g., "teamId:abc-123 invoice")
524
- * Optimized with parallel processing, early exits, and count checks
525
- */
526
- search(query: string, limit?: number): Promise<SearchResult[]>;
527
- /**
528
- * Clean jobs from a queue
529
- */
530
- cleanJobs(queueName: string, status: "completed" | "failed", grace?: number): Promise<number>;
531
- /**
532
- * FAST PATH: Get latest runs without filters
533
- * Optimized for the common case of viewing newest jobs (timestamp desc, no filters)
534
- * - Single getJobs call per queue (not per status type)
535
- * - No count checks needed
536
- * - Minimal Redis round-trips
537
- */
538
- private getLatestRuns;
539
- /**
540
- * Get all runs (jobs) across all queues with sorting and filtering
541
- * Uses fast path for common case (no filters, timestamp desc)
542
- */
543
- getAllRuns(limit?: number, start?: number, sort?: SortOptions, filters?: {
544
- status?: JobStatus;
545
- tags?: Record<string, string>;
546
- text?: string;
547
- timeRange?: {
548
- start: number;
549
- end: number;
550
- };
551
- }): Promise<PaginatedResponse<RunInfoList>>;
552
- /**
553
- * Get all schedulers (repeatable and delayed jobs) with sorting
554
- */
555
- getSchedulers(repeatableSort?: SortOptions, delayedSort?: SortOptions): Promise<{
556
- repeatable: SchedulerInfo[];
557
- delayed: DelayedJobInfo[];
558
- }>;
559
- /**
560
- * Enqueue a new job (for testing)
561
- */
562
- enqueueJob(request: TestJobRequest): Promise<{
563
- id: string;
564
- }>;
565
- /**
566
- * Extract tag values from job data based on configured tag fields
567
- */
568
- private extractTags;
569
- /**
570
- * Get unique values for a specific tag field across all jobs
571
- */
572
- getTagValues(field: string, limit?: number): Promise<{
573
- value: string;
574
- count: number;
575
- }[]>;
576
- /**
577
- * Get sortable value from JobInfo/RunInfo
578
- */
579
- private getSortValue;
580
- /**
581
- * Get sortable value from RunInfoList (lightweight version)
582
- */
583
- private getSortValueForList;
584
- /**
585
- * Get sortable value from SchedulerInfo
586
- */
587
- private getSchedulerSortValue;
588
- /**
589
- * Get sortable value from DelayedJobInfo
590
- */
591
- private getDelayedSortValue;
592
- /**
593
- * Convert a BullMQ Job to JobInfo or RunInfoList
594
- * @param job - The BullMQ job to convert
595
- * @param fields - "list" for lightweight list view, "full" for complete job details
596
- * @param knownState - Optional: skip getState() call if state is already known from fetch
597
- */
598
- private jobToInfo;
599
- /**
600
- * Retry multiple jobs across queues
601
- * Processed in parallel for better performance
602
- */
603
- bulkRetry(jobs: {
604
- queueName: string;
605
- jobId: string;
606
- }[]): Promise<{
607
- success: number;
608
- failed: number;
609
- }>;
610
- /**
611
- * Delete multiple jobs across queues
612
- * Processed in parallel for better performance
613
- */
614
- bulkDelete(jobs: {
615
- queueName: string;
616
- jobId: string;
617
- }[]): Promise<{
618
- success: number;
619
- failed: number;
620
- }>;
621
- /**
622
- * Promote multiple delayed jobs across queues (move to waiting)
623
- * Processed in parallel for better performance
624
- */
625
- bulkPromote(jobs: {
626
- queueName: string;
627
- jobId: string;
628
- }[]): Promise<{
629
- success: number;
630
- failed: number;
631
- }>;
632
- /**
633
- * Get all flows (jobs that have children or are part of a flow) - cached
634
- * Optimized to focus on waiting-children type first and early exit
635
- */
636
- getFlows(limit?: number): Promise<FlowSummary[]>;
637
- /**
638
- * Get a single flow tree by root job ID
639
- */
640
- getFlow(queueName: string, jobId: string): Promise<FlowNode | null>;
641
- /**
642
- * Create a new flow
643
- */
644
- createFlow(request: CreateFlowRequest): Promise<{
645
- id: string;
646
- }>;
647
- /**
648
- * Build a FlowJob from CreateFlowRequest or CreateFlowChildRequest
649
- */
650
- private buildFlowJob;
651
- /**
652
- * Convert BullMQ flow tree to our FlowNode structure
653
- */
654
- private convertFlowTree;
655
- /**
656
- * Count statistics for a flow tree
657
- */
658
- private countFlowStats;
659
- }
660
-
661
- /**
662
- * Core Workbench class that manages the dashboard
663
- */
664
- declare class WorkbenchCore {
665
- readonly options: Required<Pick<WorkbenchOptions, "title" | "readonly">> & WorkbenchOptions;
666
- readonly queueManager: QueueManager;
667
- constructor(options: WorkbenchOptions | Queue[]);
668
- /**
669
- * Get the queue manager instance
670
- */
671
- getQueueManager(): QueueManager;
672
- /**
673
- * Check if authentication is required
674
- */
675
- requiresAuth(): boolean;
676
- /**
677
- * Validate authentication credentials
678
- */
679
- validateAuth(username: string, password: string): boolean;
680
- /**
681
- * Get dashboard configuration for the UI
682
- */
683
- getConfig(): {
684
- title: string;
685
- logo: string | undefined;
686
- readonly: boolean;
687
- queues: string[];
688
- queueGroups: QueueGroupInfo[];
689
- tags: string[];
690
- };
691
- }
692
-
693
- interface FetchHandlerResult {
694
- /**
695
- * Web-standard fetch handler. Accepts a `Request` and returns a `Response`.
696
- * Suitable for Elysia's `.mount(path, handler)`, Next.js route handlers,
697
- * Bun.serve, and any other web-standards-friendly runtime.
698
- */
699
- fetch: (req: Request) => Promise<Response>;
700
- /**
701
- * The underlying `WorkbenchCore` instance. Exposed so adapters can read
702
- * config, query state, or wire up custom auth strategies.
703
- */
704
- core: WorkbenchCore;
705
- }
706
- /**
707
- * Build a self-contained web-fetch handler for Workbench: API routes,
708
- * `/config`, static `/assets/:file`, an `index.html` catch-all with a
709
- * correct `<base href>`, CORS on `/api/*`, and optional Basic Auth on
710
- * everything.
711
- *
712
- * This is the engine shared by every fetch-native adapter (Elysia, Next.js).
713
- * Express and Fastify adapters use {@link buildRouteTable} directly instead.
714
- *
715
- * When `options.basePath` is set, the handler rewrites the incoming Request
716
- * URL to strip that prefix before routing. This makes the bridge work
717
- * uniformly for both fetch hosts:
718
- *
719
- * - `Elysia.mount()` already strips the prefix before calling us — the
720
- * strip below is a no-op in that case.
721
- * - Next.js App Router preserves the full path — the strip is what lets
722
- * our internal routes (`/api/*`, `/config`, …) match.
723
- */
724
- declare function createFetchHandler(options: WorkbenchOptions | Queue[]): FetchHandlerResult;
725
-
726
- /**
727
- * Framework-agnostic HTTP method.
728
- */
729
- type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
730
- /**
731
- * Normalized input passed to every handler. Adapters are responsible for
732
- * mapping their framework-specific request shape to this.
733
- */
734
- interface HandlerInput {
735
- params: Record<string, string>;
736
- query: Record<string, string | undefined>;
737
- body?: unknown;
738
- }
739
- /**
740
- * Normalized output returned by every handler. Adapters serialize this
741
- * onto their framework-specific response object.
742
- */
743
- interface HandlerResult {
744
- status: number;
745
- body: unknown;
746
- }
747
- /**
748
- * A framework-agnostic route handler. Closes over a `WorkbenchCore` and
749
- * takes a normalized request envelope.
750
- */
751
- type Handler = (input: HandlerInput) => Promise<HandlerResult>;
752
- /**
753
- * A framework-agnostic route definition.
754
- *
755
- * `path` uses `:param` syntax compatible with Hono, Express, and Fastify.
756
- * Paths are relative to `/api` — adapters mount them under that prefix.
757
- */
758
- interface RouteDef {
759
- method: HttpMethod;
760
- path: string;
761
- handler: Handler;
762
- }
763
- /**
764
- * Build the framework-agnostic route table for the Workbench API.
765
- *
766
- * Adapters iterate this list and register each route on their host framework.
767
- * Paths are relative to `/api`.
768
- */
769
- declare function buildRouteTable(core: WorkbenchCore): RouteDef[];
770
-
771
- /**
772
- * Create API routes for Workbench as a Hono app.
773
- *
774
- * Iterates the framework-agnostic `buildRouteTable(core)` and registers
775
- * each route on a fresh Hono instance. Adapters that don't speak Hono can
776
- * use `buildRouteTable` directly — see `@maydotinc/q-studio/express` and
777
- * `@maydotinc/q-studio-fastify`.
778
- */
779
- declare function createApiRoutes(core: WorkbenchCore): Hono;
780
-
781
- declare function computeBasePath(pathname: string): string;
782
- /**
783
- * Resolve the dashboard's base path, preferring an explicit override.
784
- *
785
- * Adapters where the host framework preserves the mount prefix on the
786
- * incoming URL (Hono `.route()`, Express `req.originalUrl`, Next.js route
787
- * files) can rely on auto-detection. Adapters where the prefix is stripped
788
- * before the handler runs (Elysia `.mount()`) require the user to pass
789
- * `basePath` so the dashboard's HTML still references assets under the
790
- * correct prefix.
791
- */
792
- declare function resolveBasePath(override: string | undefined, pathname: string): string;
793
-
794
- /**
795
- * Parse a `Basic` Authorization header and check it against the configured
796
- * credentials. Uses constant-time comparison to avoid leaking timing info
797
- * about which character mismatched.
798
- *
799
- * Returns `true` when credentials are valid, `false` otherwise. Both inputs
800
- * being undefined or empty count as a failed check — adapters should only
801
- * call this when `core.requiresAuth()` is true.
802
- */
803
- declare function checkBasicAuth(authHeader: string | undefined, username: string, password: string): boolean;
804
- /**
805
- * Standard 401 response body + header for an unauthenticated Basic auth
806
- * request. Adapters use this when `checkBasicAuth` returns false.
807
- */
808
- declare const BASIC_AUTH_CHALLENGE: {
809
- status: 401;
810
- headers: {
811
- "WWW-Authenticate": string;
812
- };
813
- body: string;
814
- };
815
-
816
- /**
817
- * Build a fully-wired Hono app for Workbench:
818
- *
819
- * - `POST /api/*`, `GET /api/*` etc. — JSON API
820
- * - `GET /config` — UI bootstrap config
821
- * - `GET /assets/:file` — static asset reader
822
- * - `GET *` — `index.html` with `<base href>`
823
- * - CORS on `/api/*`
824
- * - Basic auth on everything when `core.requiresAuth()` is true
825
- *
826
- * Used directly by `@maydotinc/q-studio-hono` (returned as-is for `.route()`
827
- * mounting) and indirectly by `createFetchHandler` for non-Hono adapters.
828
- */
829
- declare function buildWorkbenchApp(core: WorkbenchCore): Hono;
830
-
831
- interface StaticAssetResult {
832
- status: 200 | 404;
833
- body: Buffer | null;
834
- contentType: string;
835
- }
836
- /**
837
- * Read a bundled UI asset from `UI_DIST_PATH/assets/<filename>`.
838
- *
839
- * Returns a uniform `{ status, body, contentType }` shape so each adapter
840
- * can serialize it onto its framework-native response without re-implementing
841
- * the file lookup or content-type sniffing.
842
- */
843
- declare function serveStaticAsset(filename: string): StaticAssetResult;
844
- interface IndexHtmlResult {
845
- body: string;
846
- contentType: "text/html; charset=utf-8";
847
- }
848
- /**
849
- * Read the bundled `index.html`, inject a `<base href>` matching the request's
850
- * mount path so client-side asset URLs resolve correctly, and return it.
851
- *
852
- * Falls back to a tiny "UI assets not found" stub when the core package has
853
- * not been built yet — useful for `pnpm dev` against a fresh checkout.
854
- */
855
- declare function renderIndexHtml(basePath: string, title: string): IndexHtmlResult;
856
-
857
- /**
858
- * Absolute filesystem path to the bundled UI assets (index.html + /assets).
859
- * Adapters that don't go through {@link createFetchHandler} serve static
860
- * files from this directory directly.
861
- */
862
- declare const UI_DIST_PATH: string;
863
-
864
- 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 QueueGroup, type QueueGroupInfo, 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 };