@rvoh/psychic-workers 2.4.0 → 2.6.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,4 +1,5 @@
1
1
  import { Dream } from '@rvoh/dream';
2
+ import { mergeBackgroundWithOptsIntoJobConfig } from './BaseBackgroundedService.js';
2
3
  import background from './index.js';
3
4
  import durationToSeconds from '../helpers/durationToSeconds.js';
4
5
  export default class BaseBackgroundedModel extends Dream {
@@ -71,6 +72,8 @@ export default class BaseBackgroundedModel extends Dream {
71
72
  * method, preventing you from needing to explicitly wait for queues to flush
72
73
  * before making assertions.
73
74
  *
75
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
76
+ *
74
77
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
75
78
  * @param methodName - the name of the static method you wish to run in the background
76
79
  * @param args - a variadic list of arguments to be sent to your method
@@ -85,6 +88,38 @@ export default class BaseBackgroundedModel extends Dream {
85
88
  jobConfig: safeThis.backgroundJobConfig,
86
89
  });
87
90
  }
91
+ /**
92
+ * runs the specified method in a background queue, driven by BullMQ,
93
+ * sending in the provided args, along with an options object which can
94
+ * be used to delay the job and/or override the priority provided by
95
+ * the `backgroundJobConfig` getter on this model.
96
+ *
97
+ * ```ts
98
+ * await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
99
+ * ```
100
+ * though calling backgroundWith must be awaited, the resolution of the promise
101
+ * is an indication that the job was put in the queue, not that it has
102
+ * completed.
103
+ *
104
+ * NOTE: in test environments, psychic will immediately invoke the underlying
105
+ * method, preventing you from needing to explicitly wait for queues to flush
106
+ * before making assertions.
107
+ *
108
+ * @param opts - options for backgrounding this job
109
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
110
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
111
+ * @param methodName - the name of the static method you wish to run in the background
112
+ * @param args - a variadic list of arguments to be sent to your method
113
+ */
114
+ static async backgroundWith(opts, methodName, ...args) {
115
+ const safeThis = this;
116
+ return await background.staticMethod(safeThis, methodName, {
117
+ globalName: safeThis.globalName,
118
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
119
+ args,
120
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
121
+ });
122
+ }
88
123
  /**
89
124
  * types composed by psychic must be provided, since psychic-workers leverages
90
125
  * the sync command in psychic to read your backgroundable services and extract
@@ -139,6 +174,8 @@ export default class BaseBackgroundedModel extends Dream {
139
174
  * method, preventing you from needing to explicitly wait for queues to flush
140
175
  * before making assertions.
141
176
  *
177
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
178
+ *
142
179
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
143
180
  * @param methodName - the name of the static method you wish to run in the background
144
181
  * @param args - a variadic list of arguments to be sent to your method
@@ -152,4 +189,36 @@ export default class BaseBackgroundedModel extends Dream {
152
189
  jobConfig: safeThis.backgroundJobConfig,
153
190
  });
154
191
  }
192
+ /**
193
+ * runs the specified method in a background queue, driven by BullMQ,
194
+ * sending in the provided args, along with an options object which can
195
+ * be used to delay the job and/or override the priority provided by
196
+ * the `backgroundJobConfig` getter on this model.
197
+ *
198
+ * ```ts
199
+ * const user = await User.lastOrFail()
200
+ * await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
201
+ * ```
202
+ * though calling backgroundWith must be awaited, the resolution of the promise
203
+ * is an indication that the job was put in the queue, not that it has
204
+ * completed.
205
+ *
206
+ * NOTE: in test environments, psychic will immediately invoke the underlying
207
+ * method, preventing you from needing to explicitly wait for queues to flush
208
+ * before making assertions.
209
+ *
210
+ * @param opts - options for backgrounding this job
211
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
212
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
213
+ * @param methodName - the name of the instance method you wish to run in the background
214
+ * @param args - a variadic list of arguments to be sent to your method
215
+ */
216
+ async backgroundWith(opts, methodName, ...args) {
217
+ const safeThis = this;
218
+ return await background.modelInstanceMethod(safeThis, methodName, {
219
+ args,
220
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
221
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
222
+ });
223
+ }
155
224
  }
@@ -83,6 +83,8 @@ export default class BaseBackgroundedService {
83
83
  * method, preventing you from needing to explicitly wait for queues to flush
84
84
  * before making assertions.
85
85
  *
86
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
87
+ *
86
88
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
87
89
  * @param methodName - the name of the static method you wish to run in the background
88
90
  * @param args - a variadic list of arguments to be sent to your method
@@ -97,6 +99,43 @@ export default class BaseBackgroundedService {
97
99
  jobConfig: safeThis.backgroundJobConfig,
98
100
  });
99
101
  }
102
+ /**
103
+ * runs the specified method in a background queue, driven by BullMQ,
104
+ * sending in the provided args, along with an options object which can
105
+ * be used to delay the job and/or override the priority provided by
106
+ * the `backgroundJobConfig` getter on this service.
107
+ *
108
+ * ```ts
109
+ * await MyBackgroundableClass.backgroundWith(
110
+ * { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
111
+ * 'myMethod',
112
+ * 'abc',
113
+ * 123,
114
+ * )
115
+ * ```
116
+ * though calling backgroundWith must be awaited, the resolution of the promise
117
+ * is an indication that the job was put in the queue, not that it has
118
+ * completed.
119
+ *
120
+ * NOTE: in test environments, psychic will immediately invoke the underlying
121
+ * method, preventing you from needing to explicitly wait for queues to flush
122
+ * before making assertions.
123
+ *
124
+ * @param opts - options for backgrounding this job
125
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
126
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
127
+ * @param methodName - the name of the static method you wish to run in the background
128
+ * @param args - a variadic list of arguments to be sent to your method
129
+ */
130
+ static async backgroundWith(opts, methodName, ...args) {
131
+ const safeThis = this;
132
+ return await background.staticMethod(safeThis, methodName, {
133
+ globalName: safeThis.globalName,
134
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
135
+ args,
136
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
137
+ });
138
+ }
100
139
  /**
101
140
  * types composed by psychic must be provided, since psychic-workers leverages
102
141
  * the sync command in psychic to read your backgroundable services and extract
@@ -108,3 +147,16 @@ export default class BaseBackgroundedService {
108
147
  throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
109
148
  }
110
149
  }
150
+ /**
151
+ * @internal
152
+ *
153
+ * returns a copy of the provided job config, with the priority
154
+ * replaced by the priority found in the `backgroundWith` opts,
155
+ * if one was provided.
156
+ */
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ export function mergeBackgroundWithOptsIntoJobConfig(jobConfig, opts) {
159
+ if (!opts.priority)
160
+ return jobConfig;
161
+ return { ...jobConfig, priority: opts.priority };
162
+ }
@@ -66,6 +66,64 @@ export default class BaseScheduledService {
66
66
  jobConfig: safeThis.backgroundJobConfig,
67
67
  });
68
68
  }
69
+ /**
70
+ * Returns the id that `schedule` registers a job under, which can be passed
71
+ * to {@link BaseScheduledService.unschedule} to remove that job.
72
+ *
73
+ * The id is derived from the service's global name and the method name. It
74
+ * performs no queue construction and no redis I/O, so it is safe to call
75
+ * anywhere, including from a console in development.
76
+ *
77
+ * The intended workflow for deleting a scheduled service is to read the id
78
+ * off the class before removing it:
79
+ *
80
+ * ```ts
81
+ * MyScheduledService.unscheduleId('myHourlyMethod')
82
+ * // => 'services/MyScheduledService:myHourlyMethod'
83
+ * ```
84
+ *
85
+ * then check that string into a seed or migration and call `unschedule` with
86
+ * it, which allows the class itself to be deleted in the same deploy.
87
+ *
88
+ * ```ts
89
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
90
+ * ```
91
+ *
92
+ * @param methodName - the name of the static method that was scheduled
93
+ * @returns the id `schedule` registered this method under
94
+ */
95
+ static unscheduleId(methodName) {
96
+ const safeThis = this;
97
+ return background.jobSchedulerId(safeThis.globalName, methodName);
98
+ }
99
+ /**
100
+ * Removes a scheduled job, preventing it from being run again.
101
+ *
102
+ * ```ts
103
+ * await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
104
+ * ```
105
+ *
106
+ * Since the id is just a string, it can also be checked into a seed or
107
+ * migration, which allows the scheduled service class to be deleted in the
108
+ * same deploy that stops its job:
109
+ *
110
+ * ```ts
111
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
112
+ * ```
113
+ *
114
+ * Every queue in your application is checked, so this finds the job whether
115
+ * or not the service's workstream has changed since it was scheduled, and
116
+ * whether it landed in a current or a transitional queue.
117
+ *
118
+ * NOTE: unscheduling stops future runs. It does not cancel a run that has
119
+ * already been placed on a queue, so a final invocation may still happen.
120
+ *
121
+ * @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
122
+ * @returns true if a scheduled job was removed, false if none was found
123
+ */
124
+ static async unschedule(id) {
125
+ return await background.unschedule(id);
126
+ }
69
127
  /**
70
128
  * types composed by psychic must be provided, since psychic-workers leverages
71
129
  * the sync command in psychic to read your backgroundable services and extract
@@ -500,13 +500,7 @@ export class Background {
500
500
  */
501
501
  async scheduledMethod(ObjectClass, pattern, method, { globalName, args = [], jobConfig = {}, scheduleOpts = {}, }) {
502
502
  this.connect();
503
- // `jobId` is used to determine uniqueness along with name and repeat pattern.
504
- // Since the name is really a job type and never changes, the `jobId` is the only
505
- // way to allow multiple jobs with the same cron repeat pattern. Uniqueness will
506
- // now be enforced by combining class name, method name, and cron repeat pattern.
507
- //
508
- // See: https://docs.bullmq.io/guide/jobs/repeatable
509
- const schedulerId = `${globalName}:${method}`;
503
+ const schedulerId = this.jobSchedulerId(globalName, method);
510
504
  const queueInstance = this.queueInstance(jobConfig);
511
505
  if (!queueInstance)
512
506
  throw new Error(`Missing queue for: ${jobConfig.queue?.toString()}`);
@@ -520,6 +514,40 @@ export class Background {
520
514
  },
521
515
  });
522
516
  }
517
+ /**
518
+ * @internal
519
+ *
520
+ * Returns the id a scheduled job is registered with BullMQ under.
521
+ *
522
+ * `jobId` is used to determine uniqueness along with name and repeat pattern.
523
+ * Since the name is really a job type and never changes, the `jobId` is the only
524
+ * way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
525
+ * enforced by combining the global name and the method name.
526
+ *
527
+ * See: https://docs.bullmq.io/guide/jobs/repeatable
528
+ *
529
+ * @param globalName - the globalName of the class the method belongs to
530
+ * @param method - the name of the scheduled method
531
+ */
532
+ jobSchedulerId(globalName, method) {
533
+ return `${globalName}:${method}`;
534
+ }
535
+ /**
536
+ * removes a scheduled job from BullMQ, preventing it from being run again
537
+ *
538
+ * Every queue is checked, rather than the one the job's current
539
+ * `backgroundJobConfig` routes to, so that a job can still be unscheduled
540
+ * after its workstream has changed, or after the class that scheduled it has
541
+ * been deleted.
542
+ *
543
+ * @param jobSchedulerId - the id the job was scheduled under
544
+ * @returns true if a scheduled job was removed, false if none was found
545
+ */
546
+ async unschedule(jobSchedulerId) {
547
+ this.connect();
548
+ const removed = await Promise.all(this.queues.map(queue => queue.removeJobScheduler(jobSchedulerId)));
549
+ return removed.some(Boolean);
550
+ }
523
551
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
524
552
  queueInstance(values) {
525
553
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -28,7 +28,7 @@ export default class WorkerTestUtils {
28
28
  static async work(opts = {}) {
29
29
  background.connect();
30
30
  const queues = background.queues;
31
- let workWasDone = true;
31
+ let workWasDone;
32
32
  do {
33
33
  workWasDone = false;
34
34
  for (const queue of queues) {
@@ -62,9 +62,9 @@ export default class WorkerTestUtils {
62
62
  }
63
63
  /*
64
64
  * iterates through each registered queue, and cleans out all
65
- * jobs, including completed, failed, and scheduled jobs. This
66
- * is especially useful before a test where you plan to exercise
67
- * background jobs manually.
65
+ * jobs, including waiting, paused, prioritized, delayed, completed,
66
+ * failed, and scheduled jobs. This is especially useful before a test
67
+ * where you plan to exercise background jobs manually.
68
68
  *
69
69
  * If your entire app is continuously exercising background jobs
70
70
  * manually, you may want to do this in your spec/setup/hooks.ts file,
@@ -79,8 +79,11 @@ export default class WorkerTestUtils {
79
79
  static async clean() {
80
80
  background.connect();
81
81
  for (const queue of background.queues) {
82
- // clears all non-scheduled, non-completed, and non-failed jobs
83
- await queue.drain();
82
+ // clears waiting, paused, and prioritized jobs, as well as delayed jobs
83
+ // (i.e. jobs which have failed and are awaiting a retry). Delayed jobs which
84
+ // belong to a job scheduler are intentionally left alone by BullMQ here, and
85
+ // are cleaned up below when their scheduler is removed.
86
+ await queue.drain(true);
84
87
  // clear out completed and failed jobs
85
88
  await queue.clean(0, 10000, 'completed');
86
89
  await queue.clean(0, 10000, 'failed');
@@ -1,4 +1,5 @@
1
1
  import { Dream } from '@rvoh/dream';
2
+ import { mergeBackgroundWithOptsIntoJobConfig } from './BaseBackgroundedService.js';
2
3
  import background from './index.js';
3
4
  import durationToSeconds from '../helpers/durationToSeconds.js';
4
5
  export default class BaseBackgroundedModel extends Dream {
@@ -71,6 +72,8 @@ export default class BaseBackgroundedModel extends Dream {
71
72
  * method, preventing you from needing to explicitly wait for queues to flush
72
73
  * before making assertions.
73
74
  *
75
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
76
+ *
74
77
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
75
78
  * @param methodName - the name of the static method you wish to run in the background
76
79
  * @param args - a variadic list of arguments to be sent to your method
@@ -85,6 +88,38 @@ export default class BaseBackgroundedModel extends Dream {
85
88
  jobConfig: safeThis.backgroundJobConfig,
86
89
  });
87
90
  }
91
+ /**
92
+ * runs the specified method in a background queue, driven by BullMQ,
93
+ * sending in the provided args, along with an options object which can
94
+ * be used to delay the job and/or override the priority provided by
95
+ * the `backgroundJobConfig` getter on this model.
96
+ *
97
+ * ```ts
98
+ * await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
99
+ * ```
100
+ * though calling backgroundWith must be awaited, the resolution of the promise
101
+ * is an indication that the job was put in the queue, not that it has
102
+ * completed.
103
+ *
104
+ * NOTE: in test environments, psychic will immediately invoke the underlying
105
+ * method, preventing you from needing to explicitly wait for queues to flush
106
+ * before making assertions.
107
+ *
108
+ * @param opts - options for backgrounding this job
109
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
110
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
111
+ * @param methodName - the name of the static method you wish to run in the background
112
+ * @param args - a variadic list of arguments to be sent to your method
113
+ */
114
+ static async backgroundWith(opts, methodName, ...args) {
115
+ const safeThis = this;
116
+ return await background.staticMethod(safeThis, methodName, {
117
+ globalName: safeThis.globalName,
118
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
119
+ args,
120
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
121
+ });
122
+ }
88
123
  /**
89
124
  * types composed by psychic must be provided, since psychic-workers leverages
90
125
  * the sync command in psychic to read your backgroundable services and extract
@@ -139,6 +174,8 @@ export default class BaseBackgroundedModel extends Dream {
139
174
  * method, preventing you from needing to explicitly wait for queues to flush
140
175
  * before making assertions.
141
176
  *
177
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
178
+ *
142
179
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
143
180
  * @param methodName - the name of the static method you wish to run in the background
144
181
  * @param args - a variadic list of arguments to be sent to your method
@@ -152,4 +189,36 @@ export default class BaseBackgroundedModel extends Dream {
152
189
  jobConfig: safeThis.backgroundJobConfig,
153
190
  });
154
191
  }
192
+ /**
193
+ * runs the specified method in a background queue, driven by BullMQ,
194
+ * sending in the provided args, along with an options object which can
195
+ * be used to delay the job and/or override the priority provided by
196
+ * the `backgroundJobConfig` getter on this model.
197
+ *
198
+ * ```ts
199
+ * const user = await User.lastOrFail()
200
+ * await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
201
+ * ```
202
+ * though calling backgroundWith must be awaited, the resolution of the promise
203
+ * is an indication that the job was put in the queue, not that it has
204
+ * completed.
205
+ *
206
+ * NOTE: in test environments, psychic will immediately invoke the underlying
207
+ * method, preventing you from needing to explicitly wait for queues to flush
208
+ * before making assertions.
209
+ *
210
+ * @param opts - options for backgrounding this job
211
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
212
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
213
+ * @param methodName - the name of the instance method you wish to run in the background
214
+ * @param args - a variadic list of arguments to be sent to your method
215
+ */
216
+ async backgroundWith(opts, methodName, ...args) {
217
+ const safeThis = this;
218
+ return await background.modelInstanceMethod(safeThis, methodName, {
219
+ args,
220
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
221
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
222
+ });
223
+ }
155
224
  }
@@ -83,6 +83,8 @@ export default class BaseBackgroundedService {
83
83
  * method, preventing you from needing to explicitly wait for queues to flush
84
84
  * before making assertions.
85
85
  *
86
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
87
+ *
86
88
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
87
89
  * @param methodName - the name of the static method you wish to run in the background
88
90
  * @param args - a variadic list of arguments to be sent to your method
@@ -97,6 +99,43 @@ export default class BaseBackgroundedService {
97
99
  jobConfig: safeThis.backgroundJobConfig,
98
100
  });
99
101
  }
102
+ /**
103
+ * runs the specified method in a background queue, driven by BullMQ,
104
+ * sending in the provided args, along with an options object which can
105
+ * be used to delay the job and/or override the priority provided by
106
+ * the `backgroundJobConfig` getter on this service.
107
+ *
108
+ * ```ts
109
+ * await MyBackgroundableClass.backgroundWith(
110
+ * { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
111
+ * 'myMethod',
112
+ * 'abc',
113
+ * 123,
114
+ * )
115
+ * ```
116
+ * though calling backgroundWith must be awaited, the resolution of the promise
117
+ * is an indication that the job was put in the queue, not that it has
118
+ * completed.
119
+ *
120
+ * NOTE: in test environments, psychic will immediately invoke the underlying
121
+ * method, preventing you from needing to explicitly wait for queues to flush
122
+ * before making assertions.
123
+ *
124
+ * @param opts - options for backgrounding this job
125
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
126
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
127
+ * @param methodName - the name of the static method you wish to run in the background
128
+ * @param args - a variadic list of arguments to be sent to your method
129
+ */
130
+ static async backgroundWith(opts, methodName, ...args) {
131
+ const safeThis = this;
132
+ return await background.staticMethod(safeThis, methodName, {
133
+ globalName: safeThis.globalName,
134
+ ...(opts.delay ? { delaySeconds: durationToSeconds(opts.delay), jobId: opts.delay.jobId } : {}),
135
+ args,
136
+ jobConfig: mergeBackgroundWithOptsIntoJobConfig(safeThis.backgroundJobConfig, opts),
137
+ });
138
+ }
100
139
  /**
101
140
  * types composed by psychic must be provided, since psychic-workers leverages
102
141
  * the sync command in psychic to read your backgroundable services and extract
@@ -108,3 +147,16 @@ export default class BaseBackgroundedService {
108
147
  throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
109
148
  }
110
149
  }
150
+ /**
151
+ * @internal
152
+ *
153
+ * returns a copy of the provided job config, with the priority
154
+ * replaced by the priority found in the `backgroundWith` opts,
155
+ * if one was provided.
156
+ */
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ export function mergeBackgroundWithOptsIntoJobConfig(jobConfig, opts) {
159
+ if (!opts.priority)
160
+ return jobConfig;
161
+ return { ...jobConfig, priority: opts.priority };
162
+ }
@@ -66,6 +66,64 @@ export default class BaseScheduledService {
66
66
  jobConfig: safeThis.backgroundJobConfig,
67
67
  });
68
68
  }
69
+ /**
70
+ * Returns the id that `schedule` registers a job under, which can be passed
71
+ * to {@link BaseScheduledService.unschedule} to remove that job.
72
+ *
73
+ * The id is derived from the service's global name and the method name. It
74
+ * performs no queue construction and no redis I/O, so it is safe to call
75
+ * anywhere, including from a console in development.
76
+ *
77
+ * The intended workflow for deleting a scheduled service is to read the id
78
+ * off the class before removing it:
79
+ *
80
+ * ```ts
81
+ * MyScheduledService.unscheduleId('myHourlyMethod')
82
+ * // => 'services/MyScheduledService:myHourlyMethod'
83
+ * ```
84
+ *
85
+ * then check that string into a seed or migration and call `unschedule` with
86
+ * it, which allows the class itself to be deleted in the same deploy.
87
+ *
88
+ * ```ts
89
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
90
+ * ```
91
+ *
92
+ * @param methodName - the name of the static method that was scheduled
93
+ * @returns the id `schedule` registered this method under
94
+ */
95
+ static unscheduleId(methodName) {
96
+ const safeThis = this;
97
+ return background.jobSchedulerId(safeThis.globalName, methodName);
98
+ }
99
+ /**
100
+ * Removes a scheduled job, preventing it from being run again.
101
+ *
102
+ * ```ts
103
+ * await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
104
+ * ```
105
+ *
106
+ * Since the id is just a string, it can also be checked into a seed or
107
+ * migration, which allows the scheduled service class to be deleted in the
108
+ * same deploy that stops its job:
109
+ *
110
+ * ```ts
111
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
112
+ * ```
113
+ *
114
+ * Every queue in your application is checked, so this finds the job whether
115
+ * or not the service's workstream has changed since it was scheduled, and
116
+ * whether it landed in a current or a transitional queue.
117
+ *
118
+ * NOTE: unscheduling stops future runs. It does not cancel a run that has
119
+ * already been placed on a queue, so a final invocation may still happen.
120
+ *
121
+ * @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
122
+ * @returns true if a scheduled job was removed, false if none was found
123
+ */
124
+ static async unschedule(id) {
125
+ return await background.unschedule(id);
126
+ }
69
127
  /**
70
128
  * types composed by psychic must be provided, since psychic-workers leverages
71
129
  * the sync command in psychic to read your backgroundable services and extract
@@ -500,13 +500,7 @@ export class Background {
500
500
  */
501
501
  async scheduledMethod(ObjectClass, pattern, method, { globalName, args = [], jobConfig = {}, scheduleOpts = {}, }) {
502
502
  this.connect();
503
- // `jobId` is used to determine uniqueness along with name and repeat pattern.
504
- // Since the name is really a job type and never changes, the `jobId` is the only
505
- // way to allow multiple jobs with the same cron repeat pattern. Uniqueness will
506
- // now be enforced by combining class name, method name, and cron repeat pattern.
507
- //
508
- // See: https://docs.bullmq.io/guide/jobs/repeatable
509
- const schedulerId = `${globalName}:${method}`;
503
+ const schedulerId = this.jobSchedulerId(globalName, method);
510
504
  const queueInstance = this.queueInstance(jobConfig);
511
505
  if (!queueInstance)
512
506
  throw new Error(`Missing queue for: ${jobConfig.queue?.toString()}`);
@@ -520,6 +514,40 @@ export class Background {
520
514
  },
521
515
  });
522
516
  }
517
+ /**
518
+ * @internal
519
+ *
520
+ * Returns the id a scheduled job is registered with BullMQ under.
521
+ *
522
+ * `jobId` is used to determine uniqueness along with name and repeat pattern.
523
+ * Since the name is really a job type and never changes, the `jobId` is the only
524
+ * way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
525
+ * enforced by combining the global name and the method name.
526
+ *
527
+ * See: https://docs.bullmq.io/guide/jobs/repeatable
528
+ *
529
+ * @param globalName - the globalName of the class the method belongs to
530
+ * @param method - the name of the scheduled method
531
+ */
532
+ jobSchedulerId(globalName, method) {
533
+ return `${globalName}:${method}`;
534
+ }
535
+ /**
536
+ * removes a scheduled job from BullMQ, preventing it from being run again
537
+ *
538
+ * Every queue is checked, rather than the one the job's current
539
+ * `backgroundJobConfig` routes to, so that a job can still be unscheduled
540
+ * after its workstream has changed, or after the class that scheduled it has
541
+ * been deleted.
542
+ *
543
+ * @param jobSchedulerId - the id the job was scheduled under
544
+ * @returns true if a scheduled job was removed, false if none was found
545
+ */
546
+ async unschedule(jobSchedulerId) {
547
+ this.connect();
548
+ const removed = await Promise.all(this.queues.map(queue => queue.removeJobScheduler(jobSchedulerId)));
549
+ return removed.some(Boolean);
550
+ }
523
551
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
524
552
  queueInstance(values) {
525
553
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -28,7 +28,7 @@ export default class WorkerTestUtils {
28
28
  static async work(opts = {}) {
29
29
  background.connect();
30
30
  const queues = background.queues;
31
- let workWasDone = true;
31
+ let workWasDone;
32
32
  do {
33
33
  workWasDone = false;
34
34
  for (const queue of queues) {
@@ -62,9 +62,9 @@ export default class WorkerTestUtils {
62
62
  }
63
63
  /*
64
64
  * iterates through each registered queue, and cleans out all
65
- * jobs, including completed, failed, and scheduled jobs. This
66
- * is especially useful before a test where you plan to exercise
67
- * background jobs manually.
65
+ * jobs, including waiting, paused, prioritized, delayed, completed,
66
+ * failed, and scheduled jobs. This is especially useful before a test
67
+ * where you plan to exercise background jobs manually.
68
68
  *
69
69
  * If your entire app is continuously exercising background jobs
70
70
  * manually, you may want to do this in your spec/setup/hooks.ts file,
@@ -79,8 +79,11 @@ export default class WorkerTestUtils {
79
79
  static async clean() {
80
80
  background.connect();
81
81
  for (const queue of background.queues) {
82
- // clears all non-scheduled, non-completed, and non-failed jobs
83
- await queue.drain();
82
+ // clears waiting, paused, and prioritized jobs, as well as delayed jobs
83
+ // (i.e. jobs which have failed and are awaiting a retry). Delayed jobs which
84
+ // belong to a job scheduler are intentionally left alone by BullMQ here, and
85
+ // are cleaned up below when their scheduler is removed.
86
+ await queue.drain(true);
84
87
  // clear out completed and failed jobs
85
88
  await queue.clean(0, 10000, 'completed');
86
89
  await queue.clean(0, 10000, 'failed');
@@ -1,5 +1,5 @@
1
1
  import { Dream } from '@rvoh/dream';
2
- import { BackgroundJobConfig, DelayedJobOpts } from '../types/background.js';
2
+ import { BackgroundJobConfig, BackgroundWithOpts, DelayedJobOpts } from '../types/background.js';
3
3
  import { FunctionPropertyNames } from '../types/utils.js';
4
4
  import { BackgroundableMethodArgs } from './BaseBackgroundedService.js';
5
5
  export default class BaseBackgroundedModel extends Dream {
@@ -60,11 +60,37 @@ export default class BaseBackgroundedModel extends Dream {
60
60
  * method, preventing you from needing to explicitly wait for queues to flush
61
61
  * before making assertions.
62
62
  *
63
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
64
+ *
63
65
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
64
66
  * @param methodName - the name of the static method you wish to run in the background
65
67
  * @param args - a variadic list of arguments to be sent to your method
66
68
  */
67
69
  static backgroundWithDelay<T, MethodName extends PsychicBackgroundedModelStaticMethods<T & typeof BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
70
+ /**
71
+ * runs the specified method in a background queue, driven by BullMQ,
72
+ * sending in the provided args, along with an options object which can
73
+ * be used to delay the job and/or override the priority provided by
74
+ * the `backgroundJobConfig` getter on this model.
75
+ *
76
+ * ```ts
77
+ * await User.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
78
+ * ```
79
+ * though calling backgroundWith must be awaited, the resolution of the promise
80
+ * is an indication that the job was put in the queue, not that it has
81
+ * completed.
82
+ *
83
+ * NOTE: in test environments, psychic will immediately invoke the underlying
84
+ * method, preventing you from needing to explicitly wait for queues to flush
85
+ * before making assertions.
86
+ *
87
+ * @param opts - options for backgrounding this job
88
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
89
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
90
+ * @param methodName - the name of the static method you wish to run in the background
91
+ * @param args - a variadic list of arguments to be sent to your method
92
+ */
93
+ static backgroundWith<T, MethodName extends PsychicBackgroundedModelStaticMethods<T & typeof BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
68
94
  /**
69
95
  * types composed by psychic must be provided, since psychic-workers leverages
70
96
  * the sync command in psychic to read your backgroundable services and extract
@@ -110,11 +136,38 @@ export default class BaseBackgroundedModel extends Dream {
110
136
  * method, preventing you from needing to explicitly wait for queues to flush
111
137
  * before making assertions.
112
138
  *
139
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
140
+ *
113
141
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
114
142
  * @param methodName - the name of the static method you wish to run in the background
115
143
  * @param args - a variadic list of arguments to be sent to your method
116
144
  */
117
145
  backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceInstanceMethods<T & BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
146
+ /**
147
+ * runs the specified method in a background queue, driven by BullMQ,
148
+ * sending in the provided args, along with an options object which can
149
+ * be used to delay the job and/or override the priority provided by
150
+ * the `backgroundJobConfig` getter on this model.
151
+ *
152
+ * ```ts
153
+ * const user = await User.lastOrFail()
154
+ * await user.backgroundWith({ delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' }, 'myMethod', 'abc', 123)
155
+ * ```
156
+ * though calling backgroundWith must be awaited, the resolution of the promise
157
+ * is an indication that the job was put in the queue, not that it has
158
+ * completed.
159
+ *
160
+ * NOTE: in test environments, psychic will immediately invoke the underlying
161
+ * method, preventing you from needing to explicitly wait for queues to flush
162
+ * before making assertions.
163
+ *
164
+ * @param opts - options for backgrounding this job
165
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
166
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
167
+ * @param methodName - the name of the instance method you wish to run in the background
168
+ * @param args - a variadic list of arguments to be sent to your method
169
+ */
170
+ backgroundWith<T, MethodName extends PsychicBackgroundedServiceInstanceMethods<T & BaseBackgroundedModel>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
118
171
  }
119
172
  export type PsychicBackgroundedModelStaticMethods<T extends typeof BaseBackgroundedModel> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedModel>>;
120
173
  export type PsychicBackgroundedServiceInstanceMethods<T extends BaseBackgroundedModel> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<BaseBackgroundedModel>>;
@@ -1,5 +1,5 @@
1
1
  import { Job } from 'bullmq';
2
- import { BackgroundJobConfig, DelayedJobOpts } from '../types/background.js';
2
+ import { BackgroundJobConfig, BackgroundWithOpts, DelayedJobOpts } from '../types/background.js';
3
3
  import { FunctionPropertyNames } from '../types/utils.js';
4
4
  export default class BaseBackgroundedService {
5
5
  /**
@@ -68,11 +68,42 @@ export default class BaseBackgroundedService {
68
68
  * method, preventing you from needing to explicitly wait for queues to flush
69
69
  * before making assertions.
70
70
  *
71
+ * @deprecated use `backgroundWith({ delay }, methodName, ...args)` instead. This method will be removed in a future major version.
72
+ *
71
73
  * @param delaySeconds - the amount of time (in seconds) you want to hold off before allowing the job to run
72
74
  * @param methodName - the name of the static method you wish to run in the background
73
75
  * @param args - a variadic list of arguments to be sent to your method
74
76
  */
75
77
  static backgroundWithDelay<T, MethodName extends PsychicBackgroundedServiceStaticMethods<T & typeof BaseBackgroundedService>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, delay: DelayedJobOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
78
+ /**
79
+ * runs the specified method in a background queue, driven by BullMQ,
80
+ * sending in the provided args, along with an options object which can
81
+ * be used to delay the job and/or override the priority provided by
82
+ * the `backgroundJobConfig` getter on this service.
83
+ *
84
+ * ```ts
85
+ * await MyBackgroundableClass.backgroundWith(
86
+ * { delay: { seconds: 30, jobId: 'my-unique-job-id' }, priority: 'urgent' },
87
+ * 'myMethod',
88
+ * 'abc',
89
+ * 123,
90
+ * )
91
+ * ```
92
+ * though calling backgroundWith must be awaited, the resolution of the promise
93
+ * is an indication that the job was put in the queue, not that it has
94
+ * completed.
95
+ *
96
+ * NOTE: in test environments, psychic will immediately invoke the underlying
97
+ * method, preventing you from needing to explicitly wait for queues to flush
98
+ * before making assertions.
99
+ *
100
+ * @param opts - options for backgrounding this job
101
+ * @param opts.delay - (optional) the amount of time you want to hold off before allowing the job to run, and an optional `jobId` which debounces repeated calls within the delay window
102
+ * @param opts.priority - (optional) a priority which, when provided, overrides the priority provided by `backgroundJobConfig`
103
+ * @param methodName - the name of the static method you wish to run in the background
104
+ * @param args - a variadic list of arguments to be sent to your method
105
+ */
106
+ static backgroundWith<T, MethodName extends PsychicBackgroundedServiceStaticMethods<T & typeof BaseBackgroundedService>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends BackgroundableMethodArgs<MethodFunc>>(this: T, opts: BackgroundWithOpts, methodName: MethodName, ...args: MethodArgs): Promise<void>;
76
107
  /**
77
108
  * types composed by psychic must be provided, since psychic-workers leverages
78
109
  * the sync command in psychic to read your backgroundable services and extract
@@ -81,6 +112,14 @@ export default class BaseBackgroundedService {
81
112
  */
82
113
  get psychicWorkerTypes(): any;
83
114
  }
115
+ /**
116
+ * @internal
117
+ *
118
+ * returns a copy of the provided job config, with the priority
119
+ * replaced by the priority found in the `backgroundWith` opts,
120
+ * if one was provided.
121
+ */
122
+ export declare function mergeBackgroundWithOptsIntoJobConfig<T extends BackgroundJobConfig<any>>(jobConfig: T, opts: BackgroundWithOpts): T;
84
123
  export type PsychicBackgroundedServiceStaticMethods<T extends typeof BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedService>>;
85
124
  export type PsychicBackgroundedServiceInstanceMethods<T extends BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<BaseBackgroundedService>>;
86
125
  type OmitJobFromEndOfArguments<Original extends unknown[]> = Original extends [Job] ? Original extends [string] ? Original : [] : Original extends [...infer Rest, Job] ? Original extends [...unknown[], string] ? Original : Rest : Original;
@@ -51,6 +51,59 @@ export default class BaseScheduledService {
51
51
  * @param args - a variadic list of arguments to be sent to your method
52
52
  */
53
53
  static schedule<T, MethodName extends FunctionPropertyNames<Required<T>>, MethodFunc extends T[MethodName & keyof T], MethodArgs extends MethodFunc extends (...args: any) => any ? Parameters<MethodFunc> : never>(this: T, pattern: string, methodName: MethodName, ...args: MethodArgs): Promise<void>;
54
+ /**
55
+ * Returns the id that `schedule` registers a job under, which can be passed
56
+ * to {@link BaseScheduledService.unschedule} to remove that job.
57
+ *
58
+ * The id is derived from the service's global name and the method name. It
59
+ * performs no queue construction and no redis I/O, so it is safe to call
60
+ * anywhere, including from a console in development.
61
+ *
62
+ * The intended workflow for deleting a scheduled service is to read the id
63
+ * off the class before removing it:
64
+ *
65
+ * ```ts
66
+ * MyScheduledService.unscheduleId('myHourlyMethod')
67
+ * // => 'services/MyScheduledService:myHourlyMethod'
68
+ * ```
69
+ *
70
+ * then check that string into a seed or migration and call `unschedule` with
71
+ * it, which allows the class itself to be deleted in the same deploy.
72
+ *
73
+ * ```ts
74
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
75
+ * ```
76
+ *
77
+ * @param methodName - the name of the static method that was scheduled
78
+ * @returns the id `schedule` registered this method under
79
+ */
80
+ static unscheduleId<T, MethodName extends FunctionPropertyNames<Required<T>>>(this: T, methodName: MethodName): string;
81
+ /**
82
+ * Removes a scheduled job, preventing it from being run again.
83
+ *
84
+ * ```ts
85
+ * await MyScheduledService.unschedule(MyScheduledService.unscheduleId('myHourlyMethod'))
86
+ * ```
87
+ *
88
+ * Since the id is just a string, it can also be checked into a seed or
89
+ * migration, which allows the scheduled service class to be deleted in the
90
+ * same deploy that stops its job:
91
+ *
92
+ * ```ts
93
+ * await ApplicationScheduledService.unschedule('services/MyScheduledService:myHourlyMethod')
94
+ * ```
95
+ *
96
+ * Every queue in your application is checked, so this finds the job whether
97
+ * or not the service's workstream has changed since it was scheduled, and
98
+ * whether it landed in a current or a transitional queue.
99
+ *
100
+ * NOTE: unscheduling stops future runs. It does not cancel a run that has
101
+ * already been placed on a queue, so a final invocation may still happen.
102
+ *
103
+ * @param id - the id the job was scheduled under, from {@link BaseScheduledService.unscheduleId}
104
+ * @returns true if a scheduled job was removed, false if none was found
105
+ */
106
+ static unschedule(id: string): Promise<boolean>;
54
107
  /**
55
108
  * types composed by psychic must be provided, since psychic-workers leverages
56
109
  * the sync command in psychic to read your backgroundable services and extract
@@ -182,6 +182,34 @@ export declare class Background {
182
182
  jobConfig?: BackgroundJobConfig<any>;
183
183
  scheduleOpts?: JobSchedulerTemplateOptions;
184
184
  }): Promise<void>;
185
+ /**
186
+ * @internal
187
+ *
188
+ * Returns the id a scheduled job is registered with BullMQ under.
189
+ *
190
+ * `jobId` is used to determine uniqueness along with name and repeat pattern.
191
+ * Since the name is really a job type and never changes, the `jobId` is the only
192
+ * way to allow multiple jobs with the same cron repeat pattern. Uniqueness is
193
+ * enforced by combining the global name and the method name.
194
+ *
195
+ * See: https://docs.bullmq.io/guide/jobs/repeatable
196
+ *
197
+ * @param globalName - the globalName of the class the method belongs to
198
+ * @param method - the name of the scheduled method
199
+ */
200
+ jobSchedulerId(globalName: string, method: string): string;
201
+ /**
202
+ * removes a scheduled job from BullMQ, preventing it from being run again
203
+ *
204
+ * Every queue is checked, rather than the one the job's current
205
+ * `backgroundJobConfig` routes to, so that a job can still be unscheduled
206
+ * after its workstream has changed, or after the class that scheduled it has
207
+ * been deleted.
208
+ *
209
+ * @param jobSchedulerId - the id the job was scheduled under
210
+ * @returns true if a scheduled job was removed, false if none was found
211
+ */
212
+ unschedule(jobSchedulerId: string): Promise<boolean>;
185
213
  private queueInstance;
186
214
  /**
187
215
  * adds the instance method of a provided dream model to BullMQ
@@ -45,6 +45,20 @@ export interface DelayedJobDuration {
45
45
  }
46
46
  export type JobTypes = 'BackgroundJobQueueFunctionJob' | 'BackgroundJobQueueStaticJob' | 'BackgroundJobQueueModelInstanceJob';
47
47
  export type BackgroundQueuePriority = 'default' | 'urgent' | 'not_urgent' | 'last';
48
+ export interface BackgroundWithOpts {
49
+ /**
50
+ * an optional delay to hold off the job for a certain amount of
51
+ * time after it is entered into the queue. Accepts the same options
52
+ * as `backgroundWithDelay`, including an optional `jobId` which
53
+ * debounces repeated calls within the delay window.
54
+ */
55
+ delay?: DelayedJobOpts;
56
+ /**
57
+ * an optional priority. When provided, this overrides the priority
58
+ * set on the `backgroundJobConfig` of the service or model.
59
+ */
60
+ priority?: BackgroundQueuePriority;
61
+ }
48
62
  interface BaseBackgroundJobConfig {
49
63
  priority?: BackgroundQueuePriority;
50
64
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "name": "@rvoh/psychic-workers",
4
4
  "description": "Background job system for Psychic applications",
5
- "version": "2.4.0",
5
+ "version": "2.6.0",
6
6
  "author": "RVO Health",
7
7
  "publishConfig": {
8
8
  "access": "public"
@@ -48,7 +48,7 @@
48
48
  "build:test-app": "rm -rf dist && echo \"building test app to esm...\" && tsc -p ./tsconfig.esm.build.test-app.json && echo \"building test app to cjs...\" && tsc -p ./tsconfig.cjs.build.test-app.json",
49
49
  "uspec": "vitest --config ./spec/unit/vite.config.ts",
50
50
  "format": "pnpm prettier . --write",
51
- "lint": "pnpm eslint --no-warn-ignored \"src/**/*.ts\" && pnpm prettier . --check",
51
+ "lint": "pnpm eslint --no-warn-ignored \"src/**/*.ts\" \"spec/**/*.ts\" \"test-app/**/*.ts\" && pnpm prettier . --check",
52
52
  "console": "tsx ./src/conf/repl.ts",
53
53
  "console:js": "node ./dist/src/conf/repl.js",
54
54
  "prepack": "pnpm build",
@@ -61,7 +61,7 @@
61
61
  "ioredis": "^5.8.2"
62
62
  },
63
63
  "devDependencies": {
64
- "@eslint/js": "^9.0.0",
64
+ "@eslint/js": "^10.0.1",
65
65
  "@koa/cors": "^5.0.0",
66
66
  "@koa/etag": "^5.0.2",
67
67
  "@koa/router": "^15.4.0",
@@ -78,15 +78,15 @@
78
78
  "@types/node": "^22.12.0",
79
79
  "@types/pg": "^8",
80
80
  "@types/supertest": "^6.0.3",
81
- "@typescript-eslint/parser": "^8.48.1",
81
+ "@typescript-eslint/parser": "^8.67.0",
82
82
  "bullmq": "^5.64.0",
83
- "eslint": "^9.39.1",
83
+ "eslint": "^10.0.1",
84
84
  "ioredis": "^5.8.2",
85
85
  "koa": "^3.1.2",
86
86
  "koa-bodyparser": "^4.4.1",
87
87
  "koa-conditional-get": "^3.0.0",
88
- "kysely": "^0.28.13",
89
- "kysely-codegen": "^0.19.0",
88
+ "kysely": "^0.28.17",
89
+ "kysely-codegen": "~0.20.0",
90
90
  "luxon-jest-matchers": "^0.1.14",
91
91
  "openapi-typescript": "^7.9.1",
92
92
  "pg": "^8.13.1",
@@ -96,8 +96,9 @@
96
96
  "tsx": "^4.20.6",
97
97
  "typedoc": "^0.28.17",
98
98
  "typescript": "^5.8.2",
99
- "typescript-eslint": "^8.48.1",
100
- "vitest": "^4.1.0"
99
+ "typescript-eslint": "^8.67.0",
100
+ "vite": "^7.3.6",
101
+ "vitest": "^4.1.11"
101
102
  },
102
103
  "packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
103
104
  "dependencies": {