@onebun/core 0.2.12 → 0.2.14

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.
@@ -158,76 +158,12 @@ describe('RedisQueueAdapter', () => {
158
158
  // current RedisClient.raw() implementation. These features need a proper
159
159
  // implementation using Bun's Redis client's sendCommand API.
160
160
 
161
- describe('scheduled jobs', () => {
162
- beforeEach(async () => {
163
- await adapter.connect();
164
- });
165
-
166
- it('should add and get scheduled jobs', async () => {
167
- await adapter.addScheduledJob('test-job', {
168
- pattern: 'job:test',
169
- data: { action: 'process' },
170
- schedule: { every: 1000 },
171
- });
172
-
173
- const jobs = await adapter.getScheduledJobs();
174
-
175
- expect(jobs.find((j) => j.name === 'test-job')).toBeDefined();
176
- });
177
-
178
- it('should add cron scheduled job', async () => {
179
- await adapter.addScheduledJob('cron-job', {
180
- pattern: 'job:cron',
181
- data: { action: 'cron' },
182
- schedule: { cron: '0 * * * *' },
183
- });
184
-
185
- const jobs = await adapter.getScheduledJobs();
186
-
187
- expect(jobs.find((j) => j.name === 'cron-job')).toBeDefined();
188
- });
189
-
190
- it('should remove scheduled job', async () => {
191
- await adapter.addScheduledJob('removable-job', {
192
- pattern: 'job:remove',
193
- data: {},
194
- schedule: { every: 1000 },
195
- });
196
-
197
- const removed = await adapter.removeScheduledJob('removable-job');
198
-
199
- expect(removed).toBe(true);
200
-
201
- const jobs = await adapter.getScheduledJobs();
202
- expect(jobs.find((j) => j.name === 'removable-job')).toBeUndefined();
203
- });
204
-
205
- it('should return false when removing non-existent job', async () => {
206
- const removed = await adapter.removeScheduledJob('non-existent');
207
-
208
- expect(removed).toBe(false);
209
- });
210
-
211
- it('should throw when adding job while disconnected', async () => {
212
- await adapter.disconnect();
213
-
214
- await expect(
215
- adapter.addScheduledJob('fail-job', {
216
- pattern: 'job:fail',
217
- data: {},
218
- schedule: { every: 1000 },
219
- }),
220
- ).rejects.toThrow('not connected');
221
- });
222
- });
223
-
224
161
  describe('features', () => {
225
162
  it('should support all standard queue features', () => {
226
163
  expect(adapter.supports('delayed-messages')).toBe(true);
227
164
  expect(adapter.supports('priority')).toBe(true);
228
165
  expect(adapter.supports('dead-letter-queue')).toBe(true);
229
166
  expect(adapter.supports('retry')).toBe(true);
230
- expect(adapter.supports('scheduled-jobs')).toBe(true);
231
167
  expect(adapter.supports('consumer-groups')).toBe(true);
232
168
  expect(adapter.supports('pattern-subscriptions')).toBe(true);
233
169
  });
@@ -21,8 +21,6 @@ import type {
21
21
  PublishOptions,
22
22
  SubscribeOptions,
23
23
  Subscription,
24
- ScheduledJobOptions,
25
- ScheduledJobInfo,
26
24
  MessageHandler,
27
25
  } from '../types';
28
26
 
@@ -409,45 +407,6 @@ export class RedisQueueAdapter implements QueueAdapter {
409
407
  return subscription;
410
408
  }
411
409
 
412
- // ============================================================================
413
- // Scheduled Jobs
414
- // ============================================================================
415
-
416
- async addScheduledJob(name: string, options: ScheduledJobOptions): Promise<void> {
417
- this.ensureConnected();
418
-
419
- if (!this.scheduler) {
420
- throw new Error('Scheduler not initialized');
421
- }
422
-
423
- if (options.schedule.cron) {
424
- this.scheduler.addCronJob(name, options.schedule.cron, options.pattern, () => options.data, {
425
- metadata: options.metadata,
426
- overlapStrategy: options.overlapStrategy,
427
- });
428
- } else if (options.schedule.every) {
429
- this.scheduler.addIntervalJob(name, options.schedule.every, options.pattern, () => options.data, {
430
- metadata: options.metadata,
431
- });
432
- }
433
- }
434
-
435
- async removeScheduledJob(name: string): Promise<boolean> {
436
- if (!this.scheduler) {
437
- return false;
438
- }
439
-
440
- return this.scheduler.removeJob(name);
441
- }
442
-
443
- async getScheduledJobs(): Promise<ScheduledJobInfo[]> {
444
- if (!this.scheduler) {
445
- return [];
446
- }
447
-
448
- return this.scheduler.getJobs();
449
- }
450
-
451
410
  // ============================================================================
452
411
  // Features
453
412
  // ============================================================================
@@ -50,6 +50,7 @@ import {
50
50
  getTimeoutMetadata,
51
51
  hasQueueDecorators,
52
52
  QueueScheduler,
53
+ QueueService,
53
54
  } from './index';
54
55
 
55
56
  /**
@@ -457,13 +458,6 @@ describe('Custom adapter NATS JetStream (docs/api/queue.md)', () => {
457
458
  isActive: true,
458
459
  };
459
460
  }
460
- async addScheduledJob(): Promise<void> {}
461
- async removeScheduledJob(): Promise<boolean> {
462
- return false;
463
- }
464
- async getScheduledJobs(): Promise<import('./types').ScheduledJobInfo[]> {
465
- return [];
466
- }
467
461
  supports(): boolean {
468
462
  return false;
469
463
  }
@@ -586,8 +580,6 @@ describe('Feature Support Matrix (docs/api/queue.md)', () => {
586
580
  expect(adapter.supports('pattern-subscriptions')).toBe(true);
587
581
  expect(adapter.supports('delayed-messages')).toBe(true);
588
582
  expect(adapter.supports('priority')).toBe(true);
589
- expect(adapter.supports('scheduled-jobs')).toBe(true);
590
-
591
583
  // Not supported
592
584
  expect(adapter.supports('consumer-groups')).toBe(false);
593
585
  expect(adapter.supports('dead-letter-queue')).toBe(false);
@@ -683,3 +675,132 @@ describe('Scheduled Job Error Handling (docs/api/queue.md)', () => {
683
675
  scheduler.stop();
684
676
  });
685
677
  });
678
+
679
+ /**
680
+ * @source docs/api/queue.md#dynamic-job-management
681
+ */
682
+ describe('Dynamic Job Management (docs/api/queue.md)', () => {
683
+ let queueService: QueueService;
684
+
685
+ beforeEach(async () => {
686
+ queueService = new QueueService({ adapter: 'memory' });
687
+ const adapter = new InMemoryQueueAdapter();
688
+ await queueService.initialize(adapter);
689
+ await queueService.start();
690
+ });
691
+
692
+ afterEach(async () => {
693
+ await queueService.stop();
694
+ });
695
+
696
+ it('should add and get a cron job', () => {
697
+ // From docs/api/queue.md: Dynamic Job Management - addJob (cron)
698
+ queueService.addJob({
699
+ type: 'cron',
700
+ name: 'cleanup',
701
+ expression: '0 * * * *',
702
+ pattern: 'jobs.cleanup',
703
+ });
704
+
705
+ const job = queueService.getJob('cleanup');
706
+ expect(job).toBeDefined();
707
+ expect(job!.type).toBe('cron');
708
+ expect(job!.schedule.cron).toBe('0 * * * *');
709
+ expect(job!.paused).toBe(false);
710
+ });
711
+
712
+ it('should add and get an interval job', () => {
713
+ // From docs/api/queue.md: Dynamic Job Management - addJob (interval)
714
+ queueService.addJob({
715
+ type: 'interval',
716
+ name: 'heartbeat',
717
+ intervalMs: 5000,
718
+ pattern: 'jobs.heartbeat',
719
+ });
720
+
721
+ const job = queueService.getJob('heartbeat');
722
+ expect(job).toBeDefined();
723
+ expect(job!.type).toBe('interval');
724
+ expect(job!.schedule.every).toBe(5000);
725
+ });
726
+
727
+ it('should add and get a timeout job', () => {
728
+ // From docs/api/queue.md: Dynamic Job Management - addJob (timeout)
729
+ queueService.addJob({
730
+ type: 'timeout',
731
+ name: 'warmup',
732
+ timeoutMs: 3000,
733
+ pattern: 'jobs.warmup',
734
+ });
735
+
736
+ const job = queueService.getJob('warmup');
737
+ expect(job).toBeDefined();
738
+ expect(job!.type).toBe('timeout');
739
+ expect(job!.schedule.timeout).toBe(3000);
740
+ });
741
+
742
+ it('should pause and resume a job', () => {
743
+ // From docs/api/queue.md: Dynamic Job Management - pauseJob / resumeJob
744
+ queueService.addJob({
745
+ type: 'interval',
746
+ name: 'metrics',
747
+ intervalMs: 10000,
748
+ pattern: 'jobs.metrics',
749
+ });
750
+
751
+ expect(queueService.pauseJob('metrics')).toBe(true);
752
+ expect(queueService.getJob('metrics')!.paused).toBe(true);
753
+
754
+ expect(queueService.resumeJob('metrics')).toBe(true);
755
+ expect(queueService.getJob('metrics')!.paused).toBe(false);
756
+ });
757
+
758
+ it('should update a cron job expression', () => {
759
+ // From docs/api/queue.md: Dynamic Job Management - updateJob
760
+ queueService.addJob({
761
+ type: 'cron',
762
+ name: 'report',
763
+ expression: '0 0 * * *',
764
+ pattern: 'jobs.report',
765
+ });
766
+
767
+ expect(queueService.getJob('report')!.schedule.cron).toBe('0 0 * * *');
768
+
769
+ queueService.updateJob({
770
+ type: 'cron',
771
+ name: 'report',
772
+ expression: '0 */2 * * *',
773
+ });
774
+
775
+ expect(queueService.getJob('report')!.schedule.cron).toBe('0 */2 * * *');
776
+ });
777
+
778
+ it('should list and remove jobs', () => {
779
+ // From docs/api/queue.md: Dynamic Job Management - getJobs / removeJob
780
+ queueService.addJob({
781
+ type: 'cron',
782
+ name: 'job-a',
783
+ expression: '0 * * * *',
784
+ pattern: 'jobs.a',
785
+ });
786
+ queueService.addJob({
787
+ type: 'interval',
788
+ name: 'job-b',
789
+ intervalMs: 5000,
790
+ pattern: 'jobs.b',
791
+ });
792
+ queueService.addJob({
793
+ type: 'timeout',
794
+ name: 'job-c',
795
+ timeoutMs: 1000,
796
+ pattern: 'jobs.c',
797
+ });
798
+
799
+ const jobs = queueService.getJobs();
800
+ expect(jobs.length).toBe(3);
801
+
802
+ expect(queueService.removeJob('job-b')).toBe(true);
803
+ expect(queueService.hasJob('job-b')).toBe(false);
804
+ expect(queueService.getJobs().length).toBe(2);
805
+ });
806
+ });
@@ -17,7 +17,14 @@ export type {
17
17
  QueueEvents,
18
18
  Subscription,
19
19
  OverlapStrategy,
20
- ScheduledJobOptions,
20
+ AddCronJob,
21
+ AddIntervalJob,
22
+ AddTimeoutJob,
23
+ AddJobOptions,
24
+ UpdateCronJob,
25
+ UpdateIntervalJob,
26
+ UpdateTimeoutJob,
27
+ UpdateJobOptions,
21
28
  ScheduledJobInfo,
22
29
  QueueFeature,
23
30
  QueueAdapterType,
@@ -27,11 +27,20 @@ describe('QueueServiceProxy', () => {
27
27
  /* no-op for throw test */
28
28
  }),
29
29
  ).rejects.toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
30
- await expect(proxy.addScheduledJob('j', { pattern: 'e', schedule: { every: 1000 } })).rejects.toThrow(
30
+ expect(() => proxy.addJob({
31
+ type: 'cron', name: 'j', expression: '* * * * *', pattern: 'e',
32
+ })).toThrow(
33
+ QUEUE_NOT_ENABLED_ERROR_MESSAGE,
34
+ );
35
+ expect(() => proxy.removeJob('j')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
36
+ expect(() => proxy.getJob('j')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
37
+ expect(() => proxy.getJobs()).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
38
+ expect(() => proxy.hasJob('j')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
39
+ expect(() => proxy.pauseJob('j')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
40
+ expect(() => proxy.resumeJob('j')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
41
+ expect(() => proxy.updateJob({ type: 'cron', name: 'j', expression: '* * * * *' })).toThrow(
31
42
  QUEUE_NOT_ENABLED_ERROR_MESSAGE,
32
43
  );
33
- await expect(proxy.removeScheduledJob('j')).rejects.toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
34
- await expect(proxy.getScheduledJobs()).rejects.toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
35
44
  expect(() => proxy.supports('pattern-subscriptions')).toThrow(QUEUE_NOT_ENABLED_ERROR_MESSAGE);
36
45
  expect(() =>
37
46
  proxy.on('onReady', () => {
@@ -8,14 +8,15 @@ import type { QueueService } from './queue.service';
8
8
  import type { QueueScheduler } from './scheduler';
9
9
  import type { QueueAdapter } from './types';
10
10
  import type {
11
+ AddJobOptions,
11
12
  MessageHandler,
12
13
  PublishOptions,
13
14
  QueueEvents,
14
15
  ScheduledJobInfo,
15
- ScheduledJobOptions,
16
16
  SubscribeOptions,
17
17
  Subscription,
18
18
  QueueFeature,
19
+ UpdateJobOptions,
19
20
  } from './types';
20
21
 
21
22
  const QUEUE_NOT_ENABLED_MESSAGE =
@@ -74,22 +75,51 @@ export class QueueServiceProxy {
74
75
  return await this.delegate.subscribe(pattern, handler, options);
75
76
  }
76
77
 
77
- async addScheduledJob(name: string, options: ScheduledJobOptions): Promise<void> {
78
+ addJob(options: AddJobOptions): void {
79
+ throwIfNoDelegate(this.delegate);
80
+ this.delegate.addJob(options);
81
+ }
82
+
83
+ removeJob(name: string): boolean {
84
+ throwIfNoDelegate(this.delegate);
85
+
86
+ return this.delegate.removeJob(name);
87
+ }
88
+
89
+ getJob(name: string): ScheduledJobInfo | undefined {
90
+ throwIfNoDelegate(this.delegate);
91
+
92
+ return this.delegate.getJob(name);
93
+ }
94
+
95
+ getJobs(): ScheduledJobInfo[] {
96
+ throwIfNoDelegate(this.delegate);
97
+
98
+ return this.delegate.getJobs();
99
+ }
100
+
101
+ hasJob(name: string): boolean {
102
+ throwIfNoDelegate(this.delegate);
103
+
104
+ return this.delegate.hasJob(name);
105
+ }
106
+
107
+ pauseJob(name: string): boolean {
78
108
  throwIfNoDelegate(this.delegate);
79
109
 
80
- return await this.delegate.addScheduledJob(name, options);
110
+ return this.delegate.pauseJob(name);
81
111
  }
82
112
 
83
- async removeScheduledJob(name: string): Promise<boolean> {
113
+ resumeJob(name: string): boolean {
84
114
  throwIfNoDelegate(this.delegate);
85
115
 
86
- return await this.delegate.removeScheduledJob(name);
116
+ return this.delegate.resumeJob(name);
87
117
  }
88
118
 
89
- async getScheduledJobs(): Promise<ScheduledJobInfo[]> {
119
+ updateJob(options: UpdateJobOptions): boolean {
90
120
  throwIfNoDelegate(this.delegate);
91
121
 
92
- return await this.delegate.getScheduledJobs();
122
+ return this.delegate.updateJob(options);
93
123
  }
94
124
 
95
125
  supports(feature: QueueFeature): boolean {
@@ -192,30 +192,152 @@ describe('QueueService', () => {
192
192
  });
193
193
  });
194
194
 
195
- describe('scheduled jobs via adapter', () => {
196
- test('should add and remove scheduled jobs', async () => {
197
- await service.start();
198
-
199
- await service.addScheduledJob('test-scheduled', {
200
- pattern: 'scheduled.pattern',
201
- schedule: { every: 60000 },
195
+ describe('scheduled jobs', () => {
196
+ test('addJob should delegate to scheduler addJob', () => {
197
+ service.addJob({
198
+ type: 'interval',
199
+ name: 'test-interval',
200
+ intervalMs: 5000,
201
+ pattern: 'test.pattern',
202
202
  });
203
203
 
204
- const jobs = await service.getScheduledJobs();
205
- expect(jobs.some((j) => j.name === 'test-scheduled')).toBe(true);
204
+ expect(service.hasJob('test-interval')).toBe(true);
205
+ });
206
+
207
+ test('removeJob should delegate to scheduler removeJob', () => {
208
+ service.addJob({
209
+ type: 'interval',
210
+ name: 'remove-me',
211
+ intervalMs: 5000,
212
+ pattern: 'test.pattern',
213
+ });
206
214
 
207
- const removed = await service.removeScheduledJob('test-scheduled');
215
+ const removed = service.removeJob('remove-me');
208
216
  expect(removed).toBe(true);
217
+ expect(service.hasJob('remove-me')).toBe(false);
218
+ });
209
219
 
210
- const jobsAfter = await service.getScheduledJobs();
211
- expect(jobsAfter.some((j) => j.name === 'test-scheduled')).toBe(false);
220
+ test('removeJob should return false for non-existent job', () => {
221
+ expect(service.removeJob('nonexistent')).toBe(false);
212
222
  });
213
223
 
214
- test('should return false when removing non-existent job', async () => {
215
- await service.start();
224
+ test('getJob should delegate to scheduler getJob', () => {
225
+ service.addJob({
226
+ type: 'cron',
227
+ name: 'my-cron',
228
+ expression: '*/5 * * * *',
229
+ pattern: 'cron.pattern',
230
+ });
231
+
232
+ const job = service.getJob('my-cron');
233
+ expect(job).toBeDefined();
234
+ expect(job!.name).toBe('my-cron');
235
+ expect(job!.type).toBe('cron');
236
+ expect(job!.pattern).toBe('cron.pattern');
237
+ });
238
+
239
+ test('getJob should return undefined for non-existent job', () => {
240
+ expect(service.getJob('nonexistent')).toBeUndefined();
241
+ });
242
+
243
+ test('getJobs should delegate to scheduler getJobs', () => {
244
+ service.addJob({
245
+ type: 'interval',
246
+ name: 'job-a',
247
+ intervalMs: 1000,
248
+ pattern: 'a.pattern',
249
+ });
250
+ service.addJob({
251
+ type: 'timeout',
252
+ name: 'job-b',
253
+ timeoutMs: 2000,
254
+ pattern: 'b.pattern',
255
+ });
256
+
257
+ const jobs = service.getJobs();
258
+ expect(jobs.length).toBe(2);
259
+ expect(jobs.some((j) => j.name === 'job-a')).toBe(true);
260
+ expect(jobs.some((j) => j.name === 'job-b')).toBe(true);
261
+ });
262
+
263
+ test('hasJob should delegate to scheduler hasJob', () => {
264
+ expect(service.hasJob('nope')).toBe(false);
265
+
266
+ service.addJob({
267
+ type: 'interval',
268
+ name: 'exists',
269
+ intervalMs: 1000,
270
+ pattern: 'test.pattern',
271
+ });
272
+
273
+ expect(service.hasJob('exists')).toBe(true);
274
+ });
216
275
 
217
- const removed = await service.removeScheduledJob('nonexistent');
218
- expect(removed).toBe(false);
276
+ test('pauseJob should delegate to scheduler pauseJob', () => {
277
+ service.addJob({
278
+ type: 'interval',
279
+ name: 'pausable',
280
+ intervalMs: 1000,
281
+ pattern: 'test.pattern',
282
+ });
283
+
284
+ const paused = service.pauseJob('pausable');
285
+ expect(paused).toBe(true);
286
+
287
+ const job = service.getJob('pausable');
288
+ expect(job!.paused).toBe(true);
289
+ });
290
+
291
+ test('pauseJob should return false for non-existent job', () => {
292
+ expect(service.pauseJob('nonexistent')).toBe(false);
293
+ });
294
+
295
+ test('resumeJob should delegate to scheduler resumeJob', () => {
296
+ service.addJob({
297
+ type: 'interval',
298
+ name: 'resumable',
299
+ intervalMs: 1000,
300
+ pattern: 'test.pattern',
301
+ });
302
+
303
+ service.pauseJob('resumable');
304
+ const resumed = service.resumeJob('resumable');
305
+ expect(resumed).toBe(true);
306
+
307
+ const job = service.getJob('resumable');
308
+ expect(job!.paused).toBe(false);
309
+ });
310
+
311
+ test('resumeJob should return false for non-existent job', () => {
312
+ expect(service.resumeJob('nonexistent')).toBe(false);
313
+ });
314
+
315
+ test('updateJob should delegate to scheduler updateJob', () => {
316
+ service.addJob({
317
+ type: 'interval',
318
+ name: 'updatable',
319
+ intervalMs: 1000,
320
+ pattern: 'test.pattern',
321
+ });
322
+
323
+ const updated = service.updateJob({
324
+ type: 'interval',
325
+ name: 'updatable',
326
+ intervalMs: 5000,
327
+ });
328
+ expect(updated).toBe(true);
329
+
330
+ const job = service.getJob('updatable');
331
+ expect(job!.schedule.every).toBe(5000);
332
+ });
333
+
334
+ test('updateJob should return false for non-existent job', () => {
335
+ const updated = service.updateJob({
336
+ type: 'interval',
337
+ name: 'nonexistent',
338
+ intervalMs: 1000,
339
+ });
340
+ expect(updated).toBe(false);
219
341
  });
220
342
  });
221
343
 
@@ -18,7 +18,8 @@ import type {
18
18
  PublishOptions,
19
19
  SubscribeOptions,
20
20
  Subscription,
21
- ScheduledJobOptions,
21
+ AddJobOptions,
22
+ UpdateJobOptions,
22
23
  ScheduledJobInfo,
23
24
  QueueFeature,
24
25
  QueueEvents,
@@ -184,24 +185,59 @@ export class QueueService {
184
185
  // ============================================================================
185
186
 
186
187
  /**
187
- * Add a scheduled job
188
+ * Add a scheduled job dynamically
188
189
  */
189
- async addScheduledJob(name: string, options: ScheduledJobOptions): Promise<void> {
190
- return await this.getAdapter().addScheduledJob(name, options);
190
+ addJob(options: AddJobOptions): void {
191
+ this.getScheduler().addJob(options);
191
192
  }
192
193
 
193
194
  /**
194
- * Remove a scheduled job
195
+ * Remove a scheduled job by name
195
196
  */
196
- async removeScheduledJob(name: string): Promise<boolean> {
197
- return await this.getAdapter().removeScheduledJob(name);
197
+ removeJob(name: string): boolean {
198
+ return this.getScheduler().removeJob(name);
199
+ }
200
+
201
+ /**
202
+ * Get information about a specific scheduled job
203
+ */
204
+ getJob(name: string): ScheduledJobInfo | undefined {
205
+ return this.getScheduler().getJob(name);
198
206
  }
199
207
 
200
208
  /**
201
209
  * Get all scheduled jobs
202
210
  */
203
- async getScheduledJobs(): Promise<ScheduledJobInfo[]> {
204
- return await this.getAdapter().getScheduledJobs();
211
+ getJobs(): ScheduledJobInfo[] {
212
+ return this.getScheduler().getJobs();
213
+ }
214
+
215
+ /**
216
+ * Check if a scheduled job exists
217
+ */
218
+ hasJob(name: string): boolean {
219
+ return this.getScheduler().hasJob(name);
220
+ }
221
+
222
+ /**
223
+ * Pause a scheduled job
224
+ */
225
+ pauseJob(name: string): boolean {
226
+ return this.getScheduler().pauseJob(name);
227
+ }
228
+
229
+ /**
230
+ * Resume a paused scheduled job
231
+ */
232
+ resumeJob(name: string): boolean {
233
+ return this.getScheduler().resumeJob(name);
234
+ }
235
+
236
+ /**
237
+ * Update a scheduled job's timing configuration
238
+ */
239
+ updateJob(options: UpdateJobOptions): boolean {
240
+ return this.getScheduler().updateJob(options);
205
241
  }
206
242
 
207
243
  // ============================================================================
@@ -288,6 +324,7 @@ export class QueueService {
288
324
  {
289
325
  metadata: cron.options.metadata,
290
326
  overlapStrategy: cron.options.overlapStrategy,
327
+ declarative: true,
291
328
  },
292
329
  );
293
330
  }
@@ -301,7 +338,7 @@ export class QueueService {
301
338
  interval.milliseconds,
302
339
  interval.options.pattern,
303
340
  method,
304
- { metadata: interval.options.metadata },
341
+ { metadata: interval.options.metadata, declarative: true },
305
342
  );
306
343
  }
307
344
 
@@ -314,7 +351,7 @@ export class QueueService {
314
351
  timeout.milliseconds,
315
352
  timeout.options.pattern,
316
353
  method,
317
- { metadata: timeout.options.metadata },
354
+ { metadata: timeout.options.metadata, declarative: true },
318
355
  );
319
356
  }
320
357