bunqueue-client 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/connection-types.d.ts +24 -0
  4. package/dist/connection-types.js +2 -0
  5. package/dist/connection.d.ts +44 -0
  6. package/dist/connection.js +229 -0
  7. package/dist/errors.d.ts +22 -0
  8. package/dist/errors.js +25 -0
  9. package/dist/flow-types.d.ts +34 -0
  10. package/dist/flow-types.js +2 -0
  11. package/dist/flow.d.ts +31 -0
  12. package/dist/flow.js +186 -0
  13. package/dist/frame.d.ts +16 -0
  14. package/dist/frame.js +57 -0
  15. package/dist/index.d.ts +19 -0
  16. package/dist/index.js +12 -0
  17. package/dist/job.d.ts +56 -0
  18. package/dist/job.js +151 -0
  19. package/dist/queue-admin.d.ts +63 -0
  20. package/dist/queue-admin.js +143 -0
  21. package/dist/queue-control.d.ts +44 -0
  22. package/dist/queue-control.js +85 -0
  23. package/dist/queue-query.d.ts +58 -0
  24. package/dist/queue-query.js +165 -0
  25. package/dist/queue.d.ts +50 -0
  26. package/dist/queue.js +77 -0
  27. package/dist/types.d.ts +74 -0
  28. package/dist/types.js +50 -0
  29. package/dist/worker-base.d.ts +51 -0
  30. package/dist/worker-base.js +122 -0
  31. package/dist/worker-types.d.ts +27 -0
  32. package/dist/worker-types.js +5 -0
  33. package/dist/worker.d.ts +20 -0
  34. package/dist/worker.js +151 -0
  35. package/package.json +64 -0
  36. package/src/connection-types.ts +21 -0
  37. package/src/connection.ts +242 -0
  38. package/src/errors.ts +26 -0
  39. package/src/flow-types.ts +40 -0
  40. package/src/flow.ts +236 -0
  41. package/src/frame.ts +62 -0
  42. package/src/index.ts +41 -0
  43. package/src/job.ts +194 -0
  44. package/src/queue-admin.ts +213 -0
  45. package/src/queue-control.ts +145 -0
  46. package/src/queue-query.ts +220 -0
  47. package/src/queue.ts +112 -0
  48. package/src/types.ts +133 -0
  49. package/src/worker-base.ts +132 -0
  50. package/src/worker-types.ts +32 -0
  51. package/src/worker.ts +174 -0
package/src/job.ts ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Job wrapper: read view over the server job record + per-job operations.
3
+ * Mirrors the official TS client's Job surface (TCP mode).
4
+ */
5
+
6
+ import type { Connection, Response } from './connection.js';
7
+
8
+ export type JobRaw = Record<string, unknown>;
9
+
10
+ export type ProgressHook = (job: Job, progress: number) => void;
11
+
12
+ export class Job<T = unknown> {
13
+ readonly raw: JobRaw;
14
+ readonly token: string | undefined;
15
+ private readonly conn: Connection | undefined;
16
+ private readonly onProgress: ProgressHook | undefined;
17
+
18
+ constructor(raw: JobRaw, connection?: Connection, token?: string, onProgress?: ProgressHook) {
19
+ this.raw = raw;
20
+ this.conn = connection;
21
+ this.token = token;
22
+ this.onProgress = onProgress;
23
+ }
24
+
25
+ // ------------------------------------------------------------- properties
26
+
27
+ get id(): string {
28
+ return String(this.raw.id);
29
+ }
30
+
31
+ get queue(): string {
32
+ return String(this.raw.queue);
33
+ }
34
+
35
+ get data(): T {
36
+ return this.raw.data as T;
37
+ }
38
+
39
+ /** Job name (stored inside `data.name`, mirroring the official client). */
40
+ get name(): string | undefined {
41
+ const data = this.raw.data;
42
+ if (data && typeof data === 'object' && 'name' in data) {
43
+ const name = (data as Record<string, unknown>).name;
44
+ return name === undefined || name === null ? undefined : String(name);
45
+ }
46
+ return undefined;
47
+ }
48
+
49
+ get priority(): number {
50
+ return Number(this.raw.priority ?? 0);
51
+ }
52
+
53
+ get attempts(): number {
54
+ return Number(this.raw.attempts ?? 0);
55
+ }
56
+
57
+ get maxAttempts(): number {
58
+ return Number(this.raw.maxAttempts ?? 0);
59
+ }
60
+
61
+ get createdAt(): number | undefined {
62
+ return this.raw.createdAt === undefined ? undefined : Number(this.raw.createdAt);
63
+ }
64
+
65
+ get startedAt(): number | null {
66
+ return this.raw.startedAt === undefined || this.raw.startedAt === null
67
+ ? null
68
+ : Number(this.raw.startedAt);
69
+ }
70
+
71
+ get completedAt(): number | null {
72
+ return this.raw.completedAt === undefined || this.raw.completedAt === null
73
+ ? null
74
+ : Number(this.raw.completedAt);
75
+ }
76
+
77
+ get customId(): string | null {
78
+ return this.raw.customId === undefined || this.raw.customId === null
79
+ ? null
80
+ : String(this.raw.customId);
81
+ }
82
+
83
+ get tags(): string[] {
84
+ return Array.isArray(this.raw.tags) ? this.raw.tags.map(String) : [];
85
+ }
86
+
87
+ get groupId(): string | null {
88
+ return this.raw.groupId === undefined || this.raw.groupId === null
89
+ ? null
90
+ : String(this.raw.groupId);
91
+ }
92
+
93
+ get parentId(): string | null {
94
+ return this.raw.parentId === undefined || this.raw.parentId === null
95
+ ? null
96
+ : String(this.raw.parentId);
97
+ }
98
+
99
+ get childrenIds(): string[] {
100
+ return Array.isArray(this.raw.childrenIds) ? this.raw.childrenIds.map(String) : [];
101
+ }
102
+
103
+ get dependsOn(): string[] {
104
+ return Array.isArray(this.raw.dependsOn) ? this.raw.dependsOn.map(String) : [];
105
+ }
106
+
107
+ get progress(): number {
108
+ return Number(this.raw.progress ?? 0);
109
+ }
110
+
111
+ get stacktrace(): string[] | null {
112
+ return Array.isArray(this.raw.stacktrace) ? this.raw.stacktrace.map(String) : null;
113
+ }
114
+
115
+ get state(): string | undefined {
116
+ return this.raw.state === undefined ? undefined : String(this.raw.state);
117
+ }
118
+
119
+ // ------------------------------------------------------------- operations
120
+
121
+ private call(command: Record<string, unknown> & { cmd: string }): Promise<Response> {
122
+ if (!this.conn) return Promise.reject(new Error('Job is not bound to a connection'));
123
+ return this.conn.call(command);
124
+ }
125
+
126
+ /** Report processing progress (0-100) to the server. */
127
+ async updateProgress(progress: number, message?: string): Promise<void> {
128
+ await this.call({ cmd: 'Progress', id: this.id, progress, message });
129
+ this.onProgress?.(this, progress);
130
+ }
131
+
132
+ /** Append a log row to the job. */
133
+ async log(message: string, level?: 'info' | 'warn' | 'error'): Promise<void> {
134
+ await this.call({ cmd: 'AddLog', id: this.id, message, level });
135
+ }
136
+
137
+ /** Renew this job's lock (stall detection). */
138
+ async heartbeat(): Promise<void> {
139
+ await this.call({ cmd: 'JobHeartbeat', id: this.id, token: this.token });
140
+ }
141
+
142
+ async extendLock(durationMs: number): Promise<void> {
143
+ await this.call({ cmd: 'ExtendLock', id: this.id, token: this.token, duration: durationMs });
144
+ }
145
+
146
+ async getState(): Promise<string> {
147
+ const response = await this.call({ cmd: 'GetState', id: this.id });
148
+ return String(response.state);
149
+ }
150
+
151
+ async getChildrenValues<R = unknown>(): Promise<Record<string, R>> {
152
+ const response = await this.call({ cmd: 'GetChildrenValues', id: this.id });
153
+ const data = (response.data ?? {}) as Record<string, unknown>;
154
+ return (data.values ?? response.values ?? {}) as Record<string, R>;
155
+ }
156
+
157
+ async remove(): Promise<void> {
158
+ await this.call({ cmd: 'Cancel', id: this.id });
159
+ }
160
+
161
+ async discard(): Promise<void> {
162
+ await this.call({ cmd: 'Discard', id: this.id });
163
+ }
164
+
165
+ async promote(): Promise<void> {
166
+ await this.call({ cmd: 'Promote', id: this.id });
167
+ }
168
+
169
+ /** failed → waiting (BullMQ contract; MoveToWait over TCP). */
170
+ async retry(): Promise<void> {
171
+ await this.call({ cmd: 'MoveToWait', id: this.id });
172
+ }
173
+
174
+ async updateData(data: unknown): Promise<void> {
175
+ await this.call({ cmd: 'Update', id: this.id, data });
176
+ }
177
+
178
+ async changePriority(opts: { priority: number; lifo?: boolean }): Promise<void> {
179
+ await this.call({
180
+ cmd: 'ChangePriority',
181
+ id: this.id,
182
+ priority: opts.priority,
183
+ lifo: opts.lifo,
184
+ });
185
+ }
186
+
187
+ async changeDelay(delayMs: number): Promise<void> {
188
+ await this.call({ cmd: 'ChangeDelay', id: this.id, delay: delayMs });
189
+ }
190
+
191
+ async moveToDelayed(delayMs: number): Promise<void> {
192
+ await this.call({ cmd: 'MoveToDelayed', id: this.id, delay: delayMs });
193
+ }
194
+ }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Queue admin surface: DLQ, stall/DLQ configs, rate limits, schedulers,
3
+ * monitoring and webhooks. Merged onto Queue.prototype by queue.ts.
4
+ */
5
+
6
+ import { compact } from './frame.js';
7
+ import type { Queue } from './queue.js';
8
+ import { type JobOptions, jobPayload, wireJobOptions } from './types.js';
9
+
10
+ type Ctx = Queue<unknown>;
11
+ type Raw = Record<string, unknown>;
12
+
13
+ export interface SchedulerOptions {
14
+ pattern?: string;
15
+ every?: number;
16
+ tz?: string;
17
+ immediately?: boolean;
18
+ limit?: number;
19
+ skipMissedOnRestart?: boolean;
20
+ skipIfNoWorker?: boolean;
21
+ preventOverlap?: boolean;
22
+ }
23
+
24
+ export const adminMethods = {
25
+ // ---------------------------------------------------------------------- dlq
26
+
27
+ async getDlq(this: Ctx, count?: number): Promise<Raw[]> {
28
+ const response = await this.call(
29
+ compact({ cmd: 'Dlq', queue: this.name, count }) as {
30
+ cmd: string;
31
+ }
32
+ );
33
+ return (response.jobs ?? response.data ?? []) as Raw[];
34
+ },
35
+
36
+ async retryDlq(this: Ctx, jobId?: string): Promise<number> {
37
+ const response = await this.call(
38
+ compact({ cmd: 'RetryDlq', queue: this.name, jobId }) as {
39
+ cmd: string;
40
+ }
41
+ );
42
+ return Number(response.count ?? 0);
43
+ },
44
+
45
+ async purgeDlq(this: Ctx): Promise<number> {
46
+ const response = await this.call({ cmd: 'PurgeDlq', queue: this.name });
47
+ return Number(response.count ?? 0);
48
+ },
49
+
50
+ async setDlqConfig(this: Ctx, config: Raw): Promise<void> {
51
+ await this.call({ cmd: 'SetDlqConfig', queue: this.name, config });
52
+ },
53
+
54
+ async getDlqConfig(this: Ctx): Promise<Raw> {
55
+ const response = await this.call({ cmd: 'GetDlqConfig', queue: this.name });
56
+ return (response.config ?? response.data ?? {}) as Raw;
57
+ },
58
+
59
+ // -------------------------------------------------------------------- stall
60
+
61
+ async setStallConfig(this: Ctx, config: Raw): Promise<void> {
62
+ await this.call({ cmd: 'SetStallConfig', queue: this.name, config });
63
+ },
64
+
65
+ async getStallConfig(this: Ctx): Promise<Raw> {
66
+ const response = await this.call({ cmd: 'GetStallConfig', queue: this.name });
67
+ return (response.config ?? response.data ?? {}) as Raw;
68
+ },
69
+
70
+ // -------------------------------------------------- rate limit / concurrency
71
+
72
+ /** `duration` accepted for signature parity; the TCP wire carries only `limit`. */
73
+ async setGlobalRateLimit(this: Ctx, max: number, _duration?: number): Promise<void> {
74
+ await this.call({ cmd: 'RateLimit', queue: this.name, limit: max });
75
+ },
76
+
77
+ async removeGlobalRateLimit(this: Ctx): Promise<void> {
78
+ await this.call({ cmd: 'RateLimitClear', queue: this.name });
79
+ },
80
+
81
+ async setGlobalConcurrency(this: Ctx, concurrency: number): Promise<void> {
82
+ await this.call({ cmd: 'SetConcurrency', queue: this.name, limit: concurrency });
83
+ },
84
+
85
+ async removeGlobalConcurrency(this: Ctx): Promise<void> {
86
+ await this.call({ cmd: 'ClearConcurrency', queue: this.name });
87
+ },
88
+
89
+ // ---------------------------------------------------------------- scheduler
90
+
91
+ /** Create/update a recurring job scheduler (cron pattern or fixed interval). */
92
+ async upsertJobScheduler(
93
+ this: Ctx,
94
+ schedulerId: string,
95
+ repeat: SchedulerOptions,
96
+ template: { name?: string; data?: unknown; opts?: JobOptions } = {}
97
+ ): Promise<void> {
98
+ await this.call(
99
+ compact({
100
+ cmd: 'Cron',
101
+ name: schedulerId,
102
+ queue: this.name,
103
+ data: jobPayload(template.name ?? schedulerId, template.data ?? {}),
104
+ schedule: repeat.pattern,
105
+ repeatEvery: repeat.every,
106
+ timezone: repeat.tz,
107
+ immediately: repeat.immediately,
108
+ maxLimit: repeat.limit,
109
+ skipMissedOnRestart: repeat.skipMissedOnRestart,
110
+ skipIfNoWorker: repeat.skipIfNoWorker,
111
+ preventOverlap: repeat.preventOverlap,
112
+ jobOptions: template.opts ? wireJobOptions(template.opts) : undefined,
113
+ }) as { cmd: string }
114
+ );
115
+ },
116
+
117
+ async removeJobScheduler(this: Ctx, schedulerId: string): Promise<void> {
118
+ await this.call({ cmd: 'CronDelete', name: schedulerId });
119
+ },
120
+
121
+ async getJobScheduler(this: Ctx, schedulerId: string): Promise<Raw | null> {
122
+ try {
123
+ const response = await this.call({ cmd: 'CronGet', name: schedulerId });
124
+ return (response.cron ?? response.data ?? null) as Raw | null;
125
+ } catch {
126
+ return null; // not-found surfaces as a CommandError
127
+ }
128
+ },
129
+
130
+ async getJobSchedulers(this: Ctx): Promise<Raw[]> {
131
+ const response = await this.call({ cmd: 'CronList' });
132
+ const crons = (response.crons ?? []) as Raw[];
133
+ return crons.filter((cron) => cron.queue === this.name);
134
+ },
135
+
136
+ async getJobSchedulersCount(this: Ctx): Promise<number> {
137
+ return (await this.getJobSchedulers()).length;
138
+ },
139
+
140
+ /** Helper: run `name` on a cron `schedule`. */
141
+ addCron(this: Ctx, name: string, schedule: string, data?: unknown, opts?: JobOptions) {
142
+ return this.upsertJobScheduler(name, { pattern: schedule }, { name, data, opts });
143
+ },
144
+
145
+ /** Helper: run `name` every `intervalMs` milliseconds. */
146
+ every(this: Ctx, name: string, intervalMs: number, data?: unknown, opts?: JobOptions) {
147
+ return this.upsertJobScheduler(name, { every: intervalMs }, { name, data, opts });
148
+ },
149
+
150
+ // --------------------------------------------------------------- monitoring
151
+
152
+ async getWorkers(this: Ctx): Promise<Raw[]> {
153
+ const response = await this.call({ cmd: 'ListWorkers' });
154
+ const data = (response.data ?? {}) as Raw;
155
+ return (data.workers ?? response.workers ?? []) as Raw[];
156
+ },
157
+
158
+ async getWorkersCount(this: Ctx): Promise<number> {
159
+ return (await this.getWorkers()).length;
160
+ },
161
+
162
+ async getStats(this: Ctx): Promise<Raw> {
163
+ const response = await this.call({ cmd: 'Stats' });
164
+ return (response.stats ?? {}) as Raw;
165
+ },
166
+
167
+ async getMetrics(this: Ctx): Promise<Raw> {
168
+ const response = await this.call({ cmd: 'Metrics' });
169
+ return (response.metrics ?? {}) as Raw;
170
+ },
171
+
172
+ /** Server returns queue NAMES (strings). */
173
+ async listQueues(this: Ctx): Promise<string[]> {
174
+ const response = await this.call({ cmd: 'ListQueues' });
175
+ const queues = (response.queues ?? []) as unknown[];
176
+ return queues.map((q) => (typeof q === 'string' ? q : String((q as Raw).name)));
177
+ },
178
+
179
+ // ----------------------------------------------------------------- webhooks
180
+
181
+ async addWebhook(
182
+ this: Ctx,
183
+ opts: { url: string; events: string[]; queue?: string; secret?: string }
184
+ ): Promise<Raw> {
185
+ const response = await this.call(
186
+ compact({
187
+ cmd: 'AddWebhook',
188
+ url: opts.url,
189
+ events: opts.events,
190
+ queue: opts.queue ?? this.name,
191
+ secret: opts.secret,
192
+ }) as { cmd: string }
193
+ );
194
+ const data = (response.data ?? {}) as Raw;
195
+ return (data.webhook ?? data) as Raw;
196
+ },
197
+
198
+ async removeWebhook(this: Ctx, webhookId: string): Promise<void> {
199
+ await this.call({ cmd: 'RemoveWebhook', webhookId });
200
+ },
201
+
202
+ async listWebhooks(this: Ctx): Promise<Raw[]> {
203
+ const response = await this.call({ cmd: 'ListWebhooks' });
204
+ const data = (response.data ?? {}) as Raw;
205
+ return (data.webhooks ?? response.webhooks ?? []) as Raw[];
206
+ },
207
+
208
+ async setWebhookEnabled(this: Ctx, webhookId: string, enabled: boolean): Promise<void> {
209
+ await this.call({ cmd: 'SetWebhookEnabled', webhookId, enabled });
210
+ },
211
+ };
212
+
213
+ export type QueueAdminApi = typeof adminMethods;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Queue control surface: pause/drain/clean, promotion, retry and per-job
3
+ * mutations. Methods are merged onto Queue.prototype by queue.ts.
4
+ */
5
+
6
+ import { compact } from './frame.js';
7
+ import type { Queue } from './queue.js';
8
+ import type { JobStateName } from './types.js';
9
+
10
+ type Ctx = Queue<unknown>;
11
+
12
+ export const controlMethods = {
13
+ async pause(this: Ctx): Promise<void> {
14
+ await this.call({ cmd: 'Pause', queue: this.name });
15
+ },
16
+
17
+ async resume(this: Ctx): Promise<void> {
18
+ await this.call({ cmd: 'Resume', queue: this.name });
19
+ },
20
+
21
+ async isPaused(this: Ctx): Promise<boolean> {
22
+ const response = await this.call({ cmd: 'IsPaused', queue: this.name });
23
+ return response.paused === true;
24
+ },
25
+
26
+ async drain(this: Ctx): Promise<void> {
27
+ await this.call({ cmd: 'Drain', queue: this.name });
28
+ },
29
+
30
+ async obliterate(this: Ctx): Promise<void> {
31
+ await this.call({ cmd: 'Obliterate', queue: this.name });
32
+ },
33
+
34
+ /** Remove old jobs; returns how many were removed. */
35
+ async clean(this: Ctx, graceMs: number, limit?: number, state?: JobStateName): Promise<number> {
36
+ const response = await this.call(
37
+ compact({ cmd: 'Clean', queue: this.name, grace: graceMs, limit, state }) as { cmd: string }
38
+ );
39
+ return Number(response.count ?? 0);
40
+ },
41
+
42
+ async remove(this: Ctx, id: string): Promise<void> {
43
+ await this.call({ cmd: 'Cancel', id });
44
+ },
45
+
46
+ async discard(this: Ctx, id: string): Promise<void> {
47
+ await this.call({ cmd: 'Discard', id });
48
+ },
49
+
50
+ async promoteJob(this: Ctx, id: string): Promise<void> {
51
+ await this.call({ cmd: 'Promote', id });
52
+ },
53
+
54
+ /** Promote delayed jobs to waiting; returns how many were promoted. */
55
+ async promoteJobs(this: Ctx, opts: { count?: number } = {}): Promise<number> {
56
+ const response = await this.call(
57
+ compact({ cmd: 'PromoteJobs', queue: this.name, count: opts.count }) as { cmd: string }
58
+ );
59
+ return Number(response.count ?? 0);
60
+ },
61
+
62
+ /** BullMQ contract: failed → waiting. */
63
+ async retryJob(this: Ctx, id: string): Promise<void> {
64
+ await this.call({ cmd: 'MoveToWait', id });
65
+ },
66
+
67
+ async retryJobs(
68
+ this: Ctx,
69
+ opts: { state?: 'failed' | 'completed'; count?: number } = {}
70
+ ): Promise<void> {
71
+ if (opts.state === 'completed') {
72
+ await this.call({ cmd: 'RetryCompleted', queue: this.name });
73
+ return;
74
+ }
75
+ await this.call(
76
+ compact({ cmd: 'RetryDlq', queue: this.name, count: opts.count }) as {
77
+ cmd: string;
78
+ }
79
+ );
80
+ },
81
+
82
+ async retryCompleted(this: Ctx, id?: string): Promise<void> {
83
+ await this.call(compact({ cmd: 'RetryCompleted', queue: this.name, id }) as { cmd: string });
84
+ },
85
+
86
+ // ----------------------------------------------------------- job mutations
87
+
88
+ async updateJobProgress(
89
+ this: Ctx,
90
+ id: string,
91
+ progress: number,
92
+ message?: string
93
+ ): Promise<void> {
94
+ await this.call(compact({ cmd: 'Progress', id, progress, message }) as { cmd: string });
95
+ },
96
+
97
+ async updateJobData(this: Ctx, id: string, data: unknown): Promise<void> {
98
+ await this.call({ cmd: 'Update', id, data });
99
+ },
100
+
101
+ async changeJobPriority(
102
+ this: Ctx,
103
+ id: string,
104
+ opts: { priority: number; lifo?: boolean }
105
+ ): Promise<void> {
106
+ await this.call({ cmd: 'ChangePriority', id, priority: opts.priority, lifo: opts.lifo });
107
+ },
108
+
109
+ async changeJobDelay(this: Ctx, id: string, delayMs: number): Promise<void> {
110
+ await this.call({ cmd: 'ChangeDelay', id, delay: delayMs });
111
+ },
112
+
113
+ async moveJobToWait(this: Ctx, id: string): Promise<void> {
114
+ await this.call({ cmd: 'MoveToWait', id });
115
+ },
116
+
117
+ async moveJobToDelayed(this: Ctx, id: string, delayMs: number): Promise<void> {
118
+ await this.call({ cmd: 'MoveToDelayed', id, delay: delayMs });
119
+ },
120
+
121
+ async extendJobLock(this: Ctx, id: string, token: string, durationMs: number): Promise<void> {
122
+ await this.call({ cmd: 'ExtendLock', id, token, duration: durationMs });
123
+ },
124
+
125
+ async moveJobToCompleted(
126
+ this: Ctx,
127
+ id: string,
128
+ returnValue: unknown,
129
+ token?: string
130
+ ): Promise<void> {
131
+ await this.call(compact({ cmd: 'ACK', id, result: returnValue, token }) as { cmd: string });
132
+ },
133
+
134
+ async moveJobToFailed(
135
+ this: Ctx,
136
+ id: string,
137
+ error: Error | string,
138
+ token?: string
139
+ ): Promise<void> {
140
+ const message = typeof error === 'string' ? error : error.message;
141
+ await this.call(compact({ cmd: 'FAIL', id, error: message, token }) as { cmd: string });
142
+ },
143
+ };
144
+
145
+ export type QueueControlApi = typeof controlMethods;