@boringnode/queue 0.5.2 → 0.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.
- package/README.md +94 -14
- package/build/chunk-6IO4P6RB.js +145 -0
- package/build/chunk-6IO4P6RB.js.map +1 -0
- package/build/{chunk-VRXHCWNK.js → chunk-AHUVTAI7.js} +220 -15
- package/build/chunk-AHUVTAI7.js.map +1 -0
- package/build/chunk-S37X3CBO.js +500 -0
- package/build/chunk-S37X3CBO.js.map +1 -0
- package/build/index.d.ts +10 -2
- package/build/index.js +187 -31
- package/build/index.js.map +1 -1
- package/build/{job-Z5fBSzRX.d.ts → job-C4oyCVxR.d.ts} +131 -10
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +7 -6
- package/build/src/drivers/fake_adapter.js +1 -1
- package/build/src/drivers/knex_adapter.d.ts +6 -5
- package/build/src/drivers/knex_adapter.js +112 -0
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +6 -5
- package/build/src/drivers/redis_adapter.js +134 -368
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/redis_job_storage.d.ts +17 -0
- package/build/src/drivers/redis_job_storage.js +14 -0
- package/build/src/drivers/redis_job_storage.js.map +1 -0
- package/build/src/drivers/redis_scripts.d.ts +87 -0
- package/build/src/drivers/redis_scripts.js +29 -0
- package/build/src/drivers/redis_scripts.js.map +1 -0
- package/build/src/drivers/sync_adapter.d.ts +2 -1
- package/build/src/drivers/sync_adapter.js +7 -1
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/otel.d.ts +2 -2
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +7 -1
- package/package.json +17 -17
- package/build/chunk-VRXHCWNK.js.map +0 -1
|
@@ -228,9 +228,19 @@ type JobStatus = 'pending' | 'active' | 'delayed' | 'completed' | 'failed';
|
|
|
228
228
|
* console.log(`Dispatched job: ${jobId}`)
|
|
229
229
|
* ```
|
|
230
230
|
*/
|
|
231
|
+
/**
|
|
232
|
+
* Outcome of a dedup-enabled dispatch.
|
|
233
|
+
* - `added`: new job was inserted
|
|
234
|
+
* - `skipped`: duplicate found within TTL, skipped silently
|
|
235
|
+
* - `replaced`: duplicate found within TTL, existing job's payload was replaced
|
|
236
|
+
* - `extended`: duplicate found within TTL, TTL window was reset
|
|
237
|
+
*/
|
|
238
|
+
type DedupOutcome = 'added' | 'skipped' | 'replaced' | 'extended';
|
|
231
239
|
interface DispatchResult {
|
|
232
240
|
/** Unique identifier for this specific job instance */
|
|
233
241
|
jobId: string;
|
|
242
|
+
/** Dedup outcome (only present when `.dedup()` was used). */
|
|
243
|
+
deduped?: DedupOutcome;
|
|
234
244
|
}
|
|
235
245
|
/**
|
|
236
246
|
* Result returned when dispatching multiple jobs at once.
|
|
@@ -307,6 +317,40 @@ interface JobData {
|
|
|
307
317
|
* Injected by OTel plugin at dispatch time.
|
|
308
318
|
*/
|
|
309
319
|
traceContext?: Record<string, string>;
|
|
320
|
+
/**
|
|
321
|
+
* Deduplication configuration for this job.
|
|
322
|
+
* When set, adapters apply dedup semantics keyed on `dedup.id`.
|
|
323
|
+
* Set automatically when `.dedup()` is called on the dispatcher.
|
|
324
|
+
*/
|
|
325
|
+
dedup?: {
|
|
326
|
+
/**
|
|
327
|
+
* Dedup key, prefixed with the job name (e.g. `SendInvoiceJob::order-123`).
|
|
328
|
+
* The combined `<jobName>::<id>` length is capped at 510 characters by the
|
|
329
|
+
* Knex storage column. Dispatcher validates this at `.dedup()` time.
|
|
330
|
+
*/
|
|
331
|
+
id: string;
|
|
332
|
+
/**
|
|
333
|
+
* TTL in milliseconds (must be positive). When set, dedup lock auto-expires
|
|
334
|
+
* after TTL. After expiry, the same dedup id produces a brand-new job
|
|
335
|
+
* (coexists with prior). Omit `ttl` entirely for a no-expiry lock that
|
|
336
|
+
* persists until the job is removed.
|
|
337
|
+
*/
|
|
338
|
+
ttl?: number;
|
|
339
|
+
/**
|
|
340
|
+
* Reset the TTL clock when a duplicate arrives within the window.
|
|
341
|
+
* The window length stays at the original ttl — passing a different
|
|
342
|
+
* `ttl` on the duplicating dispatch does not resize the window.
|
|
343
|
+
*/
|
|
344
|
+
extend?: boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Swap the payload of the existing pending or delayed job when a
|
|
347
|
+
* duplicate arrives within the TTL window. Active jobs and retained
|
|
348
|
+
* completed/failed jobs return `'skipped'` without mutation. Only
|
|
349
|
+
* `payload` is swapped — priority, queue, delay, and groupId of the
|
|
350
|
+
* existing job are preserved.
|
|
351
|
+
*/
|
|
352
|
+
replace?: boolean;
|
|
353
|
+
};
|
|
310
354
|
}
|
|
311
355
|
/**
|
|
312
356
|
* Record of a job's current state, including history for completed/failed jobs.
|
|
@@ -781,6 +825,16 @@ interface QueueManagerConfig {
|
|
|
781
825
|
executionWrapper?: <T>(fn: () => Promise<T>, job: AcquiredJob, queue: string) => Promise<T>;
|
|
782
826
|
}
|
|
783
827
|
|
|
828
|
+
/**
|
|
829
|
+
* Result of a push operation when dedup was involved.
|
|
830
|
+
* `outcome` tells the dispatcher what happened; `jobId` is the ID of the
|
|
831
|
+
* existing job when deduped (skipped/replaced/extended).
|
|
832
|
+
*/
|
|
833
|
+
interface PushResult {
|
|
834
|
+
outcome: DedupOutcome;
|
|
835
|
+
/** ID of the existing job when a duplicate was detected, otherwise the newly added job's id. */
|
|
836
|
+
jobId: string;
|
|
837
|
+
}
|
|
784
838
|
/**
|
|
785
839
|
* A job that has been acquired by a worker for processing.
|
|
786
840
|
* Extends JobData with the timestamp when the job was acquired.
|
|
@@ -843,6 +897,23 @@ interface Adapter {
|
|
|
843
897
|
* @returns Number of jobs that were recovered (not including permanently failed ones)
|
|
844
898
|
*/
|
|
845
899
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
900
|
+
/**
|
|
901
|
+
* Renew the acquired timestamp of in-flight jobs (heartbeat).
|
|
902
|
+
*
|
|
903
|
+
* A worker calls this periodically for the jobs it is actively processing
|
|
904
|
+
* so that long-running handlers are not mistaken for stalled jobs and
|
|
905
|
+
* re-delivered while they are still running. Only jobs that are still active
|
|
906
|
+
* AND still owned by the calling worker (the one set via setWorkerId) are
|
|
907
|
+
* renewed; jobs that have already been recovered/completed, or have since
|
|
908
|
+
* been re-acquired by another worker, are skipped. This prevents a slow
|
|
909
|
+
* worker from resurrecting a job or sabotaging the recovery of the worker
|
|
910
|
+
* that legitimately owns it now with a late heartbeat.
|
|
911
|
+
*
|
|
912
|
+
* @param queue - The queue the jobs belong to
|
|
913
|
+
* @param jobIds - The ids of the jobs currently being processed
|
|
914
|
+
* @returns Number of jobs whose timestamp was renewed
|
|
915
|
+
*/
|
|
916
|
+
renewJobs(queue: string, jobIds: string[]): Promise<number>;
|
|
846
917
|
/**
|
|
847
918
|
* Mark a job as completed and remove it from the queue.
|
|
848
919
|
*
|
|
@@ -880,30 +951,34 @@ interface Adapter {
|
|
|
880
951
|
* Push a job to the default queue for immediate processing.
|
|
881
952
|
*
|
|
882
953
|
* @param jobData - The job data to push
|
|
954
|
+
* @returns PushResult if jobData.dedup is set, otherwise void
|
|
883
955
|
*/
|
|
884
|
-
push(jobData: JobData): Promise<void>;
|
|
956
|
+
push(jobData: JobData): Promise<PushResult | void>;
|
|
885
957
|
/**
|
|
886
958
|
* Push a job to a specific queue for immediate processing.
|
|
887
959
|
*
|
|
888
960
|
* @param queue - The queue name to push to
|
|
889
961
|
* @param jobData - The job data to push
|
|
962
|
+
* @returns PushResult if jobData.dedup is set, otherwise void
|
|
890
963
|
*/
|
|
891
|
-
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
964
|
+
pushOn(queue: string, jobData: JobData): Promise<PushResult | void>;
|
|
892
965
|
/**
|
|
893
966
|
* Push a job to the default queue with a delay.
|
|
894
967
|
*
|
|
895
968
|
* @param jobData - The job data to push
|
|
896
969
|
* @param delay - Delay in milliseconds before the job becomes available
|
|
970
|
+
* @returns PushResult if jobData.dedup is set, otherwise void
|
|
897
971
|
*/
|
|
898
|
-
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
972
|
+
pushLater(jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
899
973
|
/**
|
|
900
974
|
* Push a job to a specific queue with a delay.
|
|
901
975
|
*
|
|
902
976
|
* @param queue - The queue name to push to
|
|
903
977
|
* @param jobData - The job data to push
|
|
904
978
|
* @param delay - Delay in milliseconds before the job becomes available
|
|
979
|
+
* @returns PushResult if jobData.dedup is set, otherwise void
|
|
905
980
|
*/
|
|
906
|
-
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
981
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
907
982
|
/**
|
|
908
983
|
* Push multiple jobs to the default queue for immediate processing.
|
|
909
984
|
*
|
|
@@ -1010,11 +1085,12 @@ interface Adapter {
|
|
|
1010
1085
|
*
|
|
1011
1086
|
* ```
|
|
1012
1087
|
* Job.dispatch(payload)
|
|
1013
|
-
* .toQueue('emails')
|
|
1014
|
-
* .priority(1)
|
|
1015
|
-
* .in('5m')
|
|
1016
|
-
* .
|
|
1017
|
-
* .
|
|
1088
|
+
* .toQueue('emails') // optional: target queue
|
|
1089
|
+
* .priority(1) // optional: 1-10, lower = higher priority
|
|
1090
|
+
* .in('5m') // optional: delay before processing
|
|
1091
|
+
* .dedup({ id: 'order-123' }) // optional: deduplication
|
|
1092
|
+
* .with('redis') // optional: specific adapter
|
|
1093
|
+
* .run() // dispatch the job
|
|
1018
1094
|
* ```
|
|
1019
1095
|
*
|
|
1020
1096
|
* @typeParam T - The payload type for this job
|
|
@@ -1112,6 +1188,51 @@ declare class JobDispatcher<T> {
|
|
|
1112
1188
|
* ```
|
|
1113
1189
|
*/
|
|
1114
1190
|
group(groupId: string): this;
|
|
1191
|
+
/**
|
|
1192
|
+
* Configure deduplication for this job.
|
|
1193
|
+
*
|
|
1194
|
+
* Modes:
|
|
1195
|
+
* - **Simple** (`{ id }`): skip duplicates while the job exists.
|
|
1196
|
+
* - **Throttle** (`{ id, ttl }`): skip duplicates within a TTL window.
|
|
1197
|
+
* - **Extend** (`{ id, ttl, extend: true }`): reset the TTL clock on each duplicate.
|
|
1198
|
+
* The window length stays at the original ttl from the first dispatch.
|
|
1199
|
+
* - **Replace** (`{ id, ttl, replace: true }`): swap the payload of the existing
|
|
1200
|
+
* pending/delayed job on duplicate within TTL. Active jobs and retained
|
|
1201
|
+
* completed/failed jobs return `'skipped'`. Only `payload` changes —
|
|
1202
|
+
* priority/queue/delay/groupId are preserved.
|
|
1203
|
+
* - **Debounce** (`{ id, ttl, replace: true, extend: true }`): replace + reset TTL.
|
|
1204
|
+
*
|
|
1205
|
+
* The id is automatically prefixed with the job name to prevent collisions
|
|
1206
|
+
* between different job types.
|
|
1207
|
+
*
|
|
1208
|
+
* @param options.id - Unique deduplication key
|
|
1209
|
+
* @param options.ttl - TTL as Duration ('5s', 5000). Required for extend/replace.
|
|
1210
|
+
* @param options.extend - Reset the TTL clock on duplicate within window. Window
|
|
1211
|
+
* length stays at the original ttl; this option's `ttl` arg is ignored on extend.
|
|
1212
|
+
* @param options.replace - Swap payload of existing pending/delayed job within
|
|
1213
|
+
* window. Active and retained jobs are not modified.
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```typescript
|
|
1217
|
+
* // Simple dedup
|
|
1218
|
+
* await SendInvoiceJob.dispatch({ orderId: 123 })
|
|
1219
|
+
* .dedup({ id: 'order-123' })
|
|
1220
|
+
*
|
|
1221
|
+
* // Throttle: 5 second window
|
|
1222
|
+
* await SendEmailJob.dispatch({ to: 'x' })
|
|
1223
|
+
* .dedup({ id: 'welcome', ttl: '5s' })
|
|
1224
|
+
*
|
|
1225
|
+
* // Debounce: replace payload within window
|
|
1226
|
+
* await SaveDraftJob.dispatch({ content: 'latest' })
|
|
1227
|
+
* .dedup({ id: 'draft-42', ttl: '2s', replace: true, extend: true })
|
|
1228
|
+
* ```
|
|
1229
|
+
*/
|
|
1230
|
+
dedup(options: {
|
|
1231
|
+
id: string;
|
|
1232
|
+
ttl?: Duration;
|
|
1233
|
+
extend?: boolean;
|
|
1234
|
+
replace?: boolean;
|
|
1235
|
+
}): this;
|
|
1115
1236
|
/**
|
|
1116
1237
|
* Use a specific adapter for this job.
|
|
1117
1238
|
*
|
|
@@ -1590,4 +1711,4 @@ declare abstract class Job<Payload = any> {
|
|
|
1590
1711
|
failed?(error: Error): Promise<void>;
|
|
1591
1712
|
}
|
|
1592
1713
|
|
|
1593
|
-
export { type Adapter as A, type BackoffConfig as B, type
|
|
1714
|
+
export { type Adapter as A, type BackoffConfig as B, type DedupOutcome as D, type JobData as J, type Logger as L, type PushResult as P, type QueueManagerConfig as Q, type RetryConfig as R, type ScheduleConfig as S, type WorkerCycle as W, type JobClass as a, type AcquiredJob as b, type JobRetention as c, type JobRecord as d, type ScheduleData as e, type ScheduleListOptions as f, type JobOptions as g, type QueueConfig as h, type Duration as i, type JobFactory as j, Job as k, type ScheduleStatus as l, JobBatchDispatcher as m, ScheduleBuilder as n, customBackoff as o, exponentialBackoff as p, fixedBackoff as q, linearBackoff as r, type AdapterFactory as s, type BackoffStrategy as t, type DispatchManyResult as u, type DispatchResult as v, type JobContext as w, type JobStatus as x, type ScheduleResult as y, type WorkerConfig as z };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AcquiredJob, A as Adapter } from '../../job-
|
|
1
|
+
export { b as AcquiredJob, A as Adapter, P as PushResult } from '../../job-C4oyCVxR.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
1
|
+
import { A as Adapter, J as JobData, a as JobClass, P as PushResult, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-C4oyCVxR.js';
|
|
2
2
|
|
|
3
3
|
interface FakeJobRecord {
|
|
4
4
|
queue: string;
|
|
@@ -28,7 +28,7 @@ declare class FakeAdapter implements Adapter {
|
|
|
28
28
|
*/
|
|
29
29
|
onDispose(fn: () => void): this;
|
|
30
30
|
[Symbol.dispose](): void;
|
|
31
|
-
setWorkerId(
|
|
31
|
+
setWorkerId(workerId: string): void;
|
|
32
32
|
getPushedJobs(): FakeJobRecord[];
|
|
33
33
|
getPushedJobsOn(queue: string): FakeJobRecord[];
|
|
34
34
|
findPushed(matcher: FakeJobMatcher, query?: FakeJobQuery): FakeJobRecord | undefined;
|
|
@@ -42,10 +42,10 @@ declare class FakeAdapter implements Adapter {
|
|
|
42
42
|
assertNothingPushed(): void;
|
|
43
43
|
size(): Promise<number>;
|
|
44
44
|
sizeOf(queue: string): Promise<number>;
|
|
45
|
-
push(jobData: JobData): Promise<void>;
|
|
46
|
-
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
47
|
-
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
48
|
-
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
45
|
+
push(jobData: JobData): Promise<PushResult | void>;
|
|
46
|
+
pushOn(queue: string, jobData: JobData): Promise<PushResult | void>;
|
|
47
|
+
pushLater(jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
48
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
49
49
|
pushMany(jobs: JobData[]): Promise<void>;
|
|
50
50
|
pushManyOn(queue: string, jobs: JobData[]): Promise<void>;
|
|
51
51
|
pop(): Promise<AcquiredJob | null>;
|
|
@@ -54,6 +54,7 @@ declare class FakeAdapter implements Adapter {
|
|
|
54
54
|
failJob(jobId: string, queue: string, error?: Error, removeOnFail?: JobRetention): Promise<void>;
|
|
55
55
|
retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
|
|
56
56
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
57
|
+
renewJobs(queue: string, jobIds: string[]): Promise<number>;
|
|
57
58
|
getJob(jobId: string, queue: string): Promise<JobRecord | null>;
|
|
58
59
|
destroy(): Promise<void>;
|
|
59
60
|
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, P as PushResult, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-C4oyCVxR.js';
|
|
3
3
|
|
|
4
4
|
interface KnexAdapterOptions {
|
|
5
5
|
connection: Knex;
|
|
@@ -34,15 +34,16 @@ declare class KnexAdapter implements Adapter {
|
|
|
34
34
|
failJob(jobId: string, queue: string, error?: Error, removeOnFail?: JobRetention): Promise<void>;
|
|
35
35
|
getJob(jobId: string, queue: string): Promise<JobRecord | null>;
|
|
36
36
|
retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
|
|
37
|
-
push(jobData: JobData): Promise<void>;
|
|
38
|
-
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
39
|
-
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
40
|
-
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
37
|
+
push(jobData: JobData): Promise<PushResult | void>;
|
|
38
|
+
pushOn(queue: string, jobData: JobData): Promise<PushResult | void>;
|
|
39
|
+
pushLater(jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
40
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
41
41
|
pushMany(jobs: JobData[]): Promise<void>;
|
|
42
42
|
pushManyOn(queue: string, jobs: JobData[]): Promise<void>;
|
|
43
43
|
size(): Promise<number>;
|
|
44
44
|
sizeOf(queue: string): Promise<number>;
|
|
45
45
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
46
|
+
renewJobs(queue: string, jobIds: string[]): Promise<number>;
|
|
46
47
|
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
47
48
|
/**
|
|
48
49
|
* @deprecated Use `upsertSchedule` instead.
|
|
@@ -194,6 +194,15 @@ var KnexAdapter = class {
|
|
|
194
194
|
const priority = jobData.priority ?? DEFAULT_PRIORITY;
|
|
195
195
|
const timestamp = Date.now();
|
|
196
196
|
const score = calculateScore(priority, timestamp);
|
|
197
|
+
if (jobData.dedup) {
|
|
198
|
+
return this.#pushWithDedup(queue, jobData, {
|
|
199
|
+
id: jobData.id,
|
|
200
|
+
queue,
|
|
201
|
+
status: "pending",
|
|
202
|
+
data: JSON.stringify(jobData),
|
|
203
|
+
score
|
|
204
|
+
});
|
|
205
|
+
}
|
|
197
206
|
await this.#connection(this.#jobsTable).insert({
|
|
198
207
|
id: jobData.id,
|
|
199
208
|
queue,
|
|
@@ -207,6 +216,15 @@ var KnexAdapter = class {
|
|
|
207
216
|
}
|
|
208
217
|
async pushLaterOn(queue, jobData, delay) {
|
|
209
218
|
const executeAt = Date.now() + delay;
|
|
219
|
+
if (jobData.dedup) {
|
|
220
|
+
return this.#pushWithDedup(queue, jobData, {
|
|
221
|
+
id: jobData.id,
|
|
222
|
+
queue,
|
|
223
|
+
status: "delayed",
|
|
224
|
+
data: JSON.stringify(jobData),
|
|
225
|
+
execute_at: executeAt
|
|
226
|
+
});
|
|
227
|
+
}
|
|
210
228
|
await this.#connection(this.#jobsTable).insert({
|
|
211
229
|
id: jobData.id,
|
|
212
230
|
queue,
|
|
@@ -215,11 +233,97 @@ var KnexAdapter = class {
|
|
|
215
233
|
execute_at: executeAt
|
|
216
234
|
});
|
|
217
235
|
}
|
|
236
|
+
async #pushWithDedup(queue, jobData, insertRow) {
|
|
237
|
+
const dedup = jobData.dedup;
|
|
238
|
+
try {
|
|
239
|
+
return await this.#pushWithDedupTxn(queue, jobData, insertRow, dedup);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
if (this.#isMissingDedupColumn(err)) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Dedup columns missing on "${this.#jobsTable}". Run QueueSchemaService.addDedupColumns() on your jobs table before dispatching jobs with .dedup().`,
|
|
244
|
+
{ cause: err }
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
#isMissingDedupColumn(err) {
|
|
251
|
+
if (!err || typeof err !== "object") return false;
|
|
252
|
+
const message = err.message;
|
|
253
|
+
if (!message) return false;
|
|
254
|
+
return /dedup_id/.test(message) && /(does not exist|no such column|Unknown column)/i.test(message);
|
|
255
|
+
}
|
|
256
|
+
#pushWithDedupTxn(queue, jobData, insertRow, dedup) {
|
|
257
|
+
return this.#connection.transaction(async (trx) => {
|
|
258
|
+
const existing = await trx(this.#jobsTable).where("queue", queue).where("dedup_id", dedup.id).orderBy("dedup_at", "desc").forUpdate().first();
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
if (existing) {
|
|
261
|
+
const dedupAt = existing.dedup_at != null ? Number(existing.dedup_at) : null;
|
|
262
|
+
const dedupTtl = existing.dedup_ttl != null ? Number(existing.dedup_ttl) : null;
|
|
263
|
+
const withinTtl = dedupTtl === null || dedupAt !== null && now - dedupAt < dedupTtl;
|
|
264
|
+
if (withinTtl) {
|
|
265
|
+
const status2 = existing.status;
|
|
266
|
+
const replaceable = status2 === "pending" || status2 === "delayed";
|
|
267
|
+
if (dedup.replace && replaceable) {
|
|
268
|
+
const storedData = typeof existing.data === "string" ? JSON.parse(existing.data) : existing.data;
|
|
269
|
+
const newData = { ...storedData, payload: jobData.payload };
|
|
270
|
+
const updates = { data: JSON.stringify(newData) };
|
|
271
|
+
if (dedup.extend && dedupTtl) {
|
|
272
|
+
updates.dedup_at = now;
|
|
273
|
+
}
|
|
274
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update(updates);
|
|
275
|
+
return { outcome: "replaced", jobId: existing.id };
|
|
276
|
+
}
|
|
277
|
+
if (dedup.extend && dedupTtl) {
|
|
278
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_at: now });
|
|
279
|
+
return { outcome: "extended", jobId: existing.id };
|
|
280
|
+
}
|
|
281
|
+
return { outcome: "skipped", jobId: existing.id };
|
|
282
|
+
}
|
|
283
|
+
const status = existing.status;
|
|
284
|
+
if (status === "pending" || status === "delayed" || status === "active") {
|
|
285
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_id: null, dedup_at: null, dedup_ttl: null });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let raceLost = false;
|
|
289
|
+
try {
|
|
290
|
+
await trx.transaction(async (sp) => {
|
|
291
|
+
await sp(this.#jobsTable).insert({
|
|
292
|
+
...insertRow,
|
|
293
|
+
dedup_id: dedup.id,
|
|
294
|
+
dedup_at: now,
|
|
295
|
+
dedup_ttl: dedup.ttl ?? null
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
} catch (err) {
|
|
299
|
+
if (this.#isUniqueViolation(err)) {
|
|
300
|
+
raceLost = true;
|
|
301
|
+
} else {
|
|
302
|
+
throw err;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (raceLost) {
|
|
306
|
+
const winner = await trx(this.#jobsTable).where("queue", queue).where("dedup_id", dedup.id).whereIn("status", ["pending", "delayed"]).orderBy("dedup_at", "desc").first();
|
|
307
|
+
if (winner) {
|
|
308
|
+
return { outcome: "skipped", jobId: winner.id };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { outcome: "added", jobId: jobData.id };
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
#isUniqueViolation(err) {
|
|
315
|
+
if (!err || typeof err !== "object") return false;
|
|
316
|
+
const e = err;
|
|
317
|
+
return e.code === "23505" || e.code === "SQLITE_CONSTRAINT_UNIQUE" || /UNIQUE constraint/i.test(e.message ?? "");
|
|
318
|
+
}
|
|
218
319
|
async pushMany(jobs) {
|
|
219
320
|
return this.pushManyOn("default", jobs);
|
|
220
321
|
}
|
|
221
322
|
async pushManyOn(queue, jobs) {
|
|
222
323
|
if (jobs.length === 0) return;
|
|
324
|
+
if (jobs.some((j) => j.dedup)) {
|
|
325
|
+
throw new Error("dedup is not supported in batch dispatch; use single dispatch");
|
|
326
|
+
}
|
|
223
327
|
const now = Date.now();
|
|
224
328
|
const rows = jobs.map((job) => ({
|
|
225
329
|
id: job.id,
|
|
@@ -269,6 +373,14 @@ var KnexAdapter = class {
|
|
|
269
373
|
return recovered;
|
|
270
374
|
});
|
|
271
375
|
}
|
|
376
|
+
async renewJobs(queue, jobIds) {
|
|
377
|
+
if (jobIds.length === 0) {
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
const now = Date.now();
|
|
381
|
+
const renewed = await this.#connection(this.#jobsTable).where("queue", queue).where("status", "active").where("worker_id", this.#workerId).whereIn("id", jobIds).update({ acquired_at: now });
|
|
382
|
+
return renewed;
|
|
383
|
+
}
|
|
272
384
|
async upsertSchedule(config) {
|
|
273
385
|
const id = config.id ?? randomUUID();
|
|
274
386
|
const data = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/drivers/knex_adapter.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport KnexPkg from 'knex'\nimport type { Knex } from 'knex'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobData,\n JobRecord,\n JobRetention,\n JobStatus,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { calculateScore, resolveRetention } from '../utils.js'\n\nexport interface KnexAdapterOptions {\n connection: Knex\n tableName?: string\n schedulesTableName?: string\n ownsConnection?: boolean\n}\n\ntype KnexConfig = Knex | Knex.Config\ntype DbRow = Record<string, unknown>\n\ninterface ScheduleRow extends DbRow {\n id: string\n name: string\n payload: unknown\n cron_expression: string | null\n every_ms: number | string | null\n timezone: string | null\n from_date: Date | string | number | null\n to_date: Date | string | number | null\n run_limit: number | string | null\n run_count: number | string | null\n next_run_at: Date | string | number | null\n last_run_at: Date | string | number | null\n status: string\n created_at: Date | string | number | null\n}\n\n/**\n * Create a new Knex adapter factory.\n * Accepts either a Knex instance or a Knex configuration object.\n *\n * When passing a config object, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Knex instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function knex(config: KnexConfig, tableName?: string) {\n return () => {\n const isKnexInstance = typeof config === 'function'\n const connection = isKnexInstance ? config : KnexPkg(config)\n return new KnexAdapter({ connection, tableName, ownsConnection: !isKnexInstance })\n }\n}\n\n/**\n * Knex adapter for the queue system.\n * Stores jobs in a SQL database using Knex.\n */\nexport class KnexAdapter implements Adapter {\n readonly #connection: Knex\n readonly #jobsTable: string\n readonly #schedulesTable: string\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(config: KnexAdapterOptions) {\n this.#connection = config.connection\n this.#jobsTable = config.tableName ?? 'queue_jobs'\n this.#schedulesTable = config.schedulesTableName ?? 'queue_schedules'\n this.#ownsConnection = config.ownsConnection ?? false\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.destroy()\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n\n // First, move ready delayed jobs to pending\n await this.#processDelayedJobs(queue, now)\n\n // Use a transaction to atomically pop a job\n return this.#connection.transaction(async (trx) => {\n // Build the query for highest priority job (lowest score)\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .orderBy('score', 'asc')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const job = await query.first()\n\n if (!job) {\n return null\n }\n\n // Update job to active status\n // For SQLite (no SKIP LOCKED), add status='pending' guard to prevent double-claim\n const updateQuery = trx(this.#jobsTable)\n .where('id', job.id)\n .where('queue', queue)\n\n if (!this.#supportsSkipLocked()) {\n updateQuery.where('status', 'pending')\n }\n\n const updated = await updateQuery.update({\n status: 'active',\n worker_id: this.#workerId,\n acquired_at: now,\n })\n\n // Another worker already claimed this job\n if (updated === 0) {\n return null\n }\n\n const jobData: JobData = JSON.parse(job.data)\n\n return {\n ...jobData,\n acquiredAt: now,\n }\n })\n }\n\n /**\n * Check if the database supports FOR UPDATE SKIP LOCKED.\n * PostgreSQL 9.5+, MySQL 8.0+, and MariaDB 10.6+ support it.\n * SQLite does not, but it's single-writer so it doesn't need it.\n */\n #supportsSkipLocked(): boolean {\n const client = this.#connection.client.config.client\n return client === 'pg' || client === 'mysql' || client === 'mysql2' || client === 'mariadb'\n }\n\n async #processDelayedJobs(queue: string, now: number): Promise<void> {\n // Use a transaction with row locking to prevent race conditions\n await this.#connection.transaction(async (trx) => {\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'delayed')\n .where('execute_at', '<=', now)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const delayedJobs = await query\n\n if (delayedJobs.length === 0) return\n\n // Move them to pending\n for (const job of delayedJobs) {\n const jobData: JobData = JSON.parse(job.data)\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', job.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n score,\n execute_at: null,\n })\n }\n })\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnComplete)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'completed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'completed', maxAge, maxCount, now)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnFail)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'failed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n error: error?.message || null,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'failed', maxAge, maxCount, now)\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const row = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .first()\n\n if (!row) {\n return null\n }\n\n const jobData: JobData = JSON.parse(row.data)\n\n return {\n status: row.status as JobStatus,\n data: jobData,\n finishedAt: row.finished_at ? Number(row.finished_at) : undefined,\n error: row.error || undefined,\n }\n }\n\n async #pruneHistory(\n queue: string,\n status: 'completed' | 'failed',\n maxAge: number,\n maxCount: number,\n now: number\n ): Promise<void> {\n if (maxAge > 0) {\n const cutoff = now - maxAge\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .where('finished_at', '<', cutoff)\n .delete()\n }\n\n if (maxCount > 0) {\n const toKeep = this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .orderBy('finished_at', 'desc')\n .limit(maxCount)\n .select('id')\n\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .whereNotIn('id', toKeep)\n .delete()\n }\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n\n // Get the active job\n const activeJob = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .first()\n\n if (!activeJob) return\n\n const jobData: JobData = JSON.parse(activeJob.data)\n jobData.attempts = (jobData.attempts || 0) + 1\n\n const updatedData = JSON.stringify(jobData)\n\n if (retryAt && retryAt.getTime() > now) {\n // Move to delayed\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .update({\n status: 'delayed',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score: null,\n execute_at: retryAt.getTime(),\n })\n } else {\n // Move back to pending\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score,\n execute_at: null,\n })\n }\n }\n\n async push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const timestamp = Date.now()\n const score = calculateScore(priority, timestamp)\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n const executeAt = Date.now() + delay\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n if (jobs.length === 0) return\n\n const now = Date.now()\n const rows = jobs.map((job) => ({\n id: job.id,\n queue,\n status: 'pending' as const,\n data: JSON.stringify(job),\n score: calculateScore(job.priority ?? DEFAULT_PRIORITY, now),\n }))\n\n await this.#connection(this.#jobsTable).insert(rows)\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const result = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .count('* as count')\n .first()\n\n return Number(result?.count ?? 0)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n const stalledCutoff = now - stalledThreshold\n\n // Use a transaction with row locking to prevent race conditions\n return this.#connection.transaction(async (trx) => {\n let recovered = 0\n\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('acquired_at', '<', stalledCutoff)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const stalledJobs = await query\n\n for (const row of stalledJobs) {\n const jobData: JobData = JSON.parse(row.data)\n const currentStalledCount = jobData.stalledCount ?? 0\n\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - remove the job\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .delete()\n } else {\n // Recover: increment stalledCount and put back in pending\n jobData.stalledCount = currentStalledCount + 1\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: JSON.stringify(jobData),\n worker_id: null,\n acquired_at: null,\n score,\n })\n\n recovered++\n }\n }\n\n return recovered\n })\n }\n\n async upsertSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n\n const data = {\n id,\n name: config.name,\n payload: JSON.stringify(config.payload),\n cron_expression: config.cronExpression ?? null,\n every_ms: config.everyMs ?? null,\n timezone: config.timezone,\n from_date: config.from ?? null,\n to_date: config.to ?? null,\n run_limit: config.limit ?? null,\n status: 'active',\n }\n\n // Atomic upsert\n await this.#connection(this.#schedulesTable)\n .insert({\n ...data,\n run_count: 0,\n created_at: this.#connection.fn.now(),\n })\n .onConflict('id')\n .merge({\n name: data.name,\n payload: data.payload,\n cron_expression: data.cron_expression,\n every_ms: data.every_ms,\n timezone: data.timezone,\n from_date: data.from_date,\n to_date: data.to_date,\n run_limit: data.run_limit,\n status: 'active',\n })\n\n return id\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n const row = (await this.#connection(this.#schedulesTable)\n .where('id', id)\n .first()) as ScheduleRow | undefined\n if (!row) return null\n\n return this.#rowToScheduleData(row)\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n let query = this.#connection(this.#schedulesTable).whereNot('status', 'cancelled')\n\n if (options?.status) {\n query = query.where('status', options.status)\n }\n\n const rows = (await query) as ScheduleRow[]\n return rows.map((row) => this.#rowToScheduleData(row))\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const data: Record<string, unknown> = {}\n\n if (updates.status !== undefined) data.status = updates.status\n if (updates.nextRunAt !== undefined) data.next_run_at = updates.nextRunAt\n if (updates.lastRunAt !== undefined) data.last_run_at = updates.lastRunAt\n if (updates.runCount !== undefined) data.run_count = updates.runCount\n\n if (Object.keys(data).length > 0) {\n await this.#connection(this.#schedulesTable)\n .where('id', id)\n .update(data)\n }\n }\n\n async deleteSchedule(id: string): Promise<void> {\n await this.#connection(this.#schedulesTable)\n .where('id', id)\n .delete()\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n return this.#connection.transaction(async (trx) => {\n // Find one due schedule with row locking\n let query = trx(this.#schedulesTable)\n .where('status', 'active')\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', now)\n .where((builder) => {\n builder.whereNull('run_limit').orWhereRaw('run_count < run_limit')\n })\n .where((builder) => {\n builder.whereNull('to_date').orWhere('to_date', '>=', now)\n })\n .orderBy('next_run_at', 'asc')\n .limit(1)\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const row = (await query.first()) as ScheduleRow | undefined\n if (!row) return null\n\n // Calculate next run time\n let nextRunAt: Date | null = null\n const newRunCount = Number(row.run_count ?? 0) + 1\n\n if (row.every_ms) {\n nextRunAt = new Date(now.getTime() + Number(row.every_ms))\n } else if (row.cron_expression) {\n // Import cron-parser dynamically to calculate next run\n const { CronExpressionParser } = await import('cron-parser')\n const cron = CronExpressionParser.parse(row.cron_expression, {\n currentDate: now,\n tz: row.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached\n if (row.run_limit !== null && newRunCount >= Number(row.run_limit)) {\n nextRunAt = null\n }\n\n // Check if past end date\n if (nextRunAt && row.to_date && nextRunAt > new Date(row.to_date)) {\n nextRunAt = null\n }\n\n // Update atomically\n await trx(this.#schedulesTable)\n .where('id', row.id)\n .update({\n next_run_at: nextRunAt,\n last_run_at: now,\n run_count: newRunCount,\n })\n\n // Return schedule data (before update state for payload)\n return this.#rowToScheduleData(row)\n })\n }\n\n #rowToScheduleData(row: ScheduleRow): ScheduleData {\n return {\n id: row.id,\n name: row.name,\n payload: typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload,\n cronExpression: row.cron_expression ?? null,\n everyMs: row.every_ms ? Number(row.every_ms) : null,\n timezone: row.timezone ?? 'UTC',\n from: row.from_date ? new Date(row.from_date) : null,\n to: row.to_date ? new Date(row.to_date) : null,\n limit: row.run_limit ? Number(row.run_limit) : null,\n runCount: Number(row.run_count ?? 0),\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : null,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : null,\n status: row.status === 'paused' || row.status === 'cancelled' ? 'paused' : 'active',\n createdAt: row.created_at ? new Date(row.created_at) : new Date(),\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/drivers/knex_adapter.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport KnexPkg from 'knex'\nimport type { Knex } from 'knex'\nimport type { Adapter, AcquiredJob, PushResult } from '../contracts/adapter.js'\nimport type {\n DedupOutcome,\n JobData,\n JobRecord,\n JobRetention,\n JobStatus,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { calculateScore, resolveRetention } from '../utils.js'\n\nexport interface KnexAdapterOptions {\n connection: Knex\n tableName?: string\n schedulesTableName?: string\n ownsConnection?: boolean\n}\n\ntype KnexConfig = Knex | Knex.Config\ntype DbRow = Record<string, unknown>\n\ninterface ScheduleRow extends DbRow {\n id: string\n name: string\n payload: unknown\n cron_expression: string | null\n every_ms: number | string | null\n timezone: string | null\n from_date: Date | string | number | null\n to_date: Date | string | number | null\n run_limit: number | string | null\n run_count: number | string | null\n next_run_at: Date | string | number | null\n last_run_at: Date | string | number | null\n status: string\n created_at: Date | string | number | null\n}\n\n/**\n * Create a new Knex adapter factory.\n * Accepts either a Knex instance or a Knex configuration object.\n *\n * When passing a config object, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Knex instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function knex(config: KnexConfig, tableName?: string) {\n return () => {\n const isKnexInstance = typeof config === 'function'\n const connection = isKnexInstance ? config : KnexPkg(config)\n return new KnexAdapter({ connection, tableName, ownsConnection: !isKnexInstance })\n }\n}\n\n/**\n * Knex adapter for the queue system.\n * Stores jobs in a SQL database using Knex.\n */\nexport class KnexAdapter implements Adapter {\n readonly #connection: Knex\n readonly #jobsTable: string\n readonly #schedulesTable: string\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(config: KnexAdapterOptions) {\n this.#connection = config.connection\n this.#jobsTable = config.tableName ?? 'queue_jobs'\n this.#schedulesTable = config.schedulesTableName ?? 'queue_schedules'\n this.#ownsConnection = config.ownsConnection ?? false\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.destroy()\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n\n // First, move ready delayed jobs to pending\n await this.#processDelayedJobs(queue, now)\n\n // Use a transaction to atomically pop a job\n return this.#connection.transaction(async (trx) => {\n // Build the query for highest priority job (lowest score)\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .orderBy('score', 'asc')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const job = await query.first()\n\n if (!job) {\n return null\n }\n\n // Update job to active status\n // For SQLite (no SKIP LOCKED), add status='pending' guard to prevent double-claim\n const updateQuery = trx(this.#jobsTable).where('id', job.id).where('queue', queue)\n\n if (!this.#supportsSkipLocked()) {\n updateQuery.where('status', 'pending')\n }\n\n const updated = await updateQuery.update({\n status: 'active',\n worker_id: this.#workerId,\n acquired_at: now,\n })\n\n // Another worker already claimed this job\n if (updated === 0) {\n return null\n }\n\n const jobData: JobData = JSON.parse(job.data)\n\n return {\n ...jobData,\n acquiredAt: now,\n }\n })\n }\n\n /**\n * Check if the database supports FOR UPDATE SKIP LOCKED.\n * PostgreSQL 9.5+, MySQL 8.0+, and MariaDB 10.6+ support it.\n * SQLite does not, but it's single-writer so it doesn't need it.\n */\n #supportsSkipLocked(): boolean {\n const client = this.#connection.client.config.client\n return client === 'pg' || client === 'mysql' || client === 'mysql2' || client === 'mariadb'\n }\n\n async #processDelayedJobs(queue: string, now: number): Promise<void> {\n // Use a transaction with row locking to prevent race conditions\n await this.#connection.transaction(async (trx) => {\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'delayed')\n .where('execute_at', '<=', now)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const delayedJobs = await query\n\n if (delayedJobs.length === 0) return\n\n // Move them to pending\n for (const job of delayedJobs) {\n const jobData: JobData = JSON.parse(job.data)\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable).where('id', job.id).where('queue', queue).update({\n status: 'pending',\n score,\n execute_at: null,\n })\n }\n })\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnComplete)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'completed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'completed', maxAge, maxCount, now)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnFail)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'failed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n error: error?.message || null,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'failed', maxAge, maxCount, now)\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const row = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .first()\n\n if (!row) {\n return null\n }\n\n const jobData: JobData = JSON.parse(row.data)\n\n return {\n status: row.status as JobStatus,\n data: jobData,\n finishedAt: row.finished_at ? Number(row.finished_at) : undefined,\n error: row.error || undefined,\n }\n }\n\n async #pruneHistory(\n queue: string,\n status: 'completed' | 'failed',\n maxAge: number,\n maxCount: number,\n now: number\n ): Promise<void> {\n if (maxAge > 0) {\n const cutoff = now - maxAge\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .where('finished_at', '<', cutoff)\n .delete()\n }\n\n if (maxCount > 0) {\n const toKeep = this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .orderBy('finished_at', 'desc')\n .limit(maxCount)\n .select('id')\n\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .whereNotIn('id', toKeep)\n .delete()\n }\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n\n // Get the active job\n const activeJob = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .first()\n\n if (!activeJob) return\n\n const jobData: JobData = JSON.parse(activeJob.data)\n jobData.attempts = (jobData.attempts || 0) + 1\n\n const updatedData = JSON.stringify(jobData)\n\n if (retryAt && retryAt.getTime() > now) {\n // Move to delayed\n await this.#connection(this.#jobsTable).where('id', jobId).where('queue', queue).update({\n status: 'delayed',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score: null,\n execute_at: retryAt.getTime(),\n })\n } else {\n // Move back to pending\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await this.#connection(this.#jobsTable).where('id', jobId).where('queue', queue).update({\n status: 'pending',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score,\n execute_at: null,\n })\n }\n }\n\n async push(jobData: JobData): Promise<PushResult | void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<PushResult | void> {\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const timestamp = Date.now()\n const score = calculateScore(priority, timestamp)\n\n if (jobData.dedup) {\n return this.#pushWithDedup(queue, jobData, {\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<PushResult | void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void> {\n const executeAt = Date.now() + delay\n\n if (jobData.dedup) {\n return this.#pushWithDedup(queue, jobData, {\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n async #pushWithDedup(\n queue: string,\n jobData: JobData,\n insertRow: Record<string, unknown>\n ): Promise<PushResult> {\n const dedup = jobData.dedup!\n\n try {\n return await this.#pushWithDedupTxn(queue, jobData, insertRow, dedup)\n } catch (err) {\n if (this.#isMissingDedupColumn(err)) {\n throw new Error(\n `Dedup columns missing on \"${this.#jobsTable}\". Run QueueSchemaService.addDedupColumns() on your jobs table before dispatching jobs with .dedup().`,\n { cause: err }\n )\n }\n throw err\n }\n }\n\n #isMissingDedupColumn(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const message = (err as { message?: string }).message\n if (!message) return false\n // Postgres: 'column \"dedup_id\" does not exist'\n // SQLite: 'no such column: dedup_id'\n // MySQL: \"Unknown column 'dedup_id' in 'where clause'\"\n return (\n /dedup_id/.test(message) && /(does not exist|no such column|Unknown column)/i.test(message)\n )\n }\n\n #pushWithDedupTxn(\n queue: string,\n jobData: JobData,\n insertRow: Record<string, unknown>,\n dedup: NonNullable<JobData['dedup']>\n ): Promise<PushResult> {\n return this.#connection.transaction(async (trx) => {\n const existing = await trx(this.#jobsTable)\n .where('queue', queue)\n .where('dedup_id', dedup.id)\n .orderBy('dedup_at', 'desc')\n .forUpdate()\n .first()\n\n const now = Date.now()\n\n if (existing) {\n const dedupAt = existing.dedup_at != null ? Number(existing.dedup_at) : null\n const dedupTtl = existing.dedup_ttl != null ? Number(existing.dedup_ttl) : null\n const withinTtl = dedupTtl === null || (dedupAt !== null && now - dedupAt < dedupTtl)\n\n if (withinTtl) {\n const status = existing.status as JobStatus\n const replaceable = status === 'pending' || status === 'delayed'\n\n if (dedup.replace && replaceable) {\n const storedData =\n typeof existing.data === 'string' ? JSON.parse(existing.data) : existing.data\n const newData = { ...storedData, payload: jobData.payload }\n const updates: Record<string, unknown> = { data: JSON.stringify(newData) }\n if (dedup.extend && dedupTtl) {\n updates.dedup_at = now\n }\n await trx(this.#jobsTable).where({ id: existing.id, queue }).update(updates)\n return { outcome: 'replaced' as DedupOutcome, jobId: existing.id as string }\n }\n\n if (dedup.extend && dedupTtl) {\n await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_at: now })\n return { outcome: 'extended' as DedupOutcome, jobId: existing.id as string }\n }\n\n return { outcome: 'skipped' as DedupOutcome, jobId: existing.id as string }\n }\n // TTL expired — release the dedup slot from the old row so the new\n // insert can claim it. The old job keeps running to completion; only\n // its dedup identity is cleared. Retained history rows are excluded\n // from the partial unique index predicate, so no update needed there.\n const status = existing.status as JobStatus\n if (status === 'pending' || status === 'delayed' || status === 'active') {\n await trx(this.#jobsTable)\n .where({ id: existing.id, queue })\n .update({ dedup_id: null, dedup_at: null, dedup_ttl: null })\n }\n }\n\n let raceLost = false\n try {\n await trx.transaction(async (sp) => {\n await sp(this.#jobsTable).insert({\n ...insertRow,\n dedup_id: dedup.id,\n dedup_at: now,\n dedup_ttl: dedup.ttl ?? null,\n })\n })\n } catch (err) {\n if (this.#isUniqueViolation(err)) {\n raceLost = true\n } else {\n throw err\n }\n }\n\n if (raceLost) {\n const winner = await trx(this.#jobsTable)\n .where('queue', queue)\n .where('dedup_id', dedup.id)\n .whereIn('status', ['pending', 'delayed'])\n .orderBy('dedup_at', 'desc')\n .first()\n if (winner) {\n return { outcome: 'skipped' as DedupOutcome, jobId: winner.id as string }\n }\n }\n\n return { outcome: 'added' as DedupOutcome, jobId: jobData.id }\n })\n }\n\n #isUniqueViolation(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const e = err as { code?: string; message?: string }\n return (\n e.code === '23505' ||\n e.code === 'SQLITE_CONSTRAINT_UNIQUE' ||\n /UNIQUE constraint/i.test(e.message ?? '')\n )\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n if (jobs.length === 0) return\n\n if (jobs.some((j) => j.dedup)) {\n throw new Error('dedup is not supported in batch dispatch; use single dispatch')\n }\n\n const now = Date.now()\n const rows = jobs.map((job) => ({\n id: job.id,\n queue,\n status: 'pending' as const,\n data: JSON.stringify(job),\n score: calculateScore(job.priority ?? DEFAULT_PRIORITY, now),\n }))\n\n await this.#connection(this.#jobsTable).insert(rows)\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const result = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .count('* as count')\n .first()\n\n return Number(result?.count ?? 0)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n const stalledCutoff = now - stalledThreshold\n\n // Use a transaction with row locking to prevent race conditions\n return this.#connection.transaction(async (trx) => {\n let recovered = 0\n\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('acquired_at', '<', stalledCutoff)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const stalledJobs = await query\n\n for (const row of stalledJobs) {\n const jobData: JobData = JSON.parse(row.data)\n const currentStalledCount = jobData.stalledCount ?? 0\n\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - remove the job\n await trx(this.#jobsTable).where('id', row.id).where('queue', queue).delete()\n } else {\n // Recover: increment stalledCount and put back in pending\n jobData.stalledCount = currentStalledCount + 1\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: JSON.stringify(jobData),\n worker_id: null,\n acquired_at: null,\n score,\n })\n\n recovered++\n }\n }\n\n return recovered\n })\n }\n\n async renewJobs(queue: string, jobIds: string[]): Promise<number> {\n if (jobIds.length === 0) {\n return 0\n }\n\n const now = Date.now()\n\n // Only renew jobs that are still active AND still owned by this worker; a\n // job that was already recovered, finalized, or re-acquired by another\n // worker will not match and is therefore never resurrected.\n const renewed = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('worker_id', this.#workerId)\n .whereIn('id', jobIds)\n .update({ acquired_at: now })\n\n return renewed\n }\n\n async upsertSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n\n const data = {\n id,\n name: config.name,\n payload: JSON.stringify(config.payload),\n cron_expression: config.cronExpression ?? null,\n every_ms: config.everyMs ?? null,\n timezone: config.timezone,\n from_date: config.from ?? null,\n to_date: config.to ?? null,\n run_limit: config.limit ?? null,\n status: 'active',\n }\n\n // Atomic upsert\n await this.#connection(this.#schedulesTable)\n .insert({\n ...data,\n run_count: 0,\n created_at: this.#connection.fn.now(),\n })\n .onConflict('id')\n .merge({\n name: data.name,\n payload: data.payload,\n cron_expression: data.cron_expression,\n every_ms: data.every_ms,\n timezone: data.timezone,\n from_date: data.from_date,\n to_date: data.to_date,\n run_limit: data.run_limit,\n status: 'active',\n })\n\n return id\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n const row = (await this.#connection(this.#schedulesTable).where('id', id).first()) as\n | ScheduleRow\n | undefined\n if (!row) return null\n\n return this.#rowToScheduleData(row)\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n let query = this.#connection(this.#schedulesTable).whereNot('status', 'cancelled')\n\n if (options?.status) {\n query = query.where('status', options.status)\n }\n\n const rows = (await query) as ScheduleRow[]\n return rows.map((row) => this.#rowToScheduleData(row))\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const data: Record<string, unknown> = {}\n\n if (updates.status !== undefined) data.status = updates.status\n if (updates.nextRunAt !== undefined) data.next_run_at = updates.nextRunAt\n if (updates.lastRunAt !== undefined) data.last_run_at = updates.lastRunAt\n if (updates.runCount !== undefined) data.run_count = updates.runCount\n\n if (Object.keys(data).length > 0) {\n await this.#connection(this.#schedulesTable).where('id', id).update(data)\n }\n }\n\n async deleteSchedule(id: string): Promise<void> {\n await this.#connection(this.#schedulesTable).where('id', id).delete()\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n return this.#connection.transaction(async (trx) => {\n // Find one due schedule with row locking\n let query = trx(this.#schedulesTable)\n .where('status', 'active')\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', now)\n .where((builder) => {\n builder.whereNull('run_limit').orWhereRaw('run_count < run_limit')\n })\n .where((builder) => {\n builder.whereNull('to_date').orWhere('to_date', '>=', now)\n })\n .orderBy('next_run_at', 'asc')\n .limit(1)\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const row = (await query.first()) as ScheduleRow | undefined\n if (!row) return null\n\n // Calculate next run time\n let nextRunAt: Date | null = null\n const newRunCount = Number(row.run_count ?? 0) + 1\n\n if (row.every_ms) {\n nextRunAt = new Date(now.getTime() + Number(row.every_ms))\n } else if (row.cron_expression) {\n // Import cron-parser dynamically to calculate next run\n const { CronExpressionParser } = await import('cron-parser')\n const cron = CronExpressionParser.parse(row.cron_expression, {\n currentDate: now,\n tz: row.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached\n if (row.run_limit !== null && newRunCount >= Number(row.run_limit)) {\n nextRunAt = null\n }\n\n // Check if past end date\n if (nextRunAt && row.to_date && nextRunAt > new Date(row.to_date)) {\n nextRunAt = null\n }\n\n // Update atomically\n await trx(this.#schedulesTable).where('id', row.id).update({\n next_run_at: nextRunAt,\n last_run_at: now,\n run_count: newRunCount,\n })\n\n // Return schedule data (before update state for payload)\n return this.#rowToScheduleData(row)\n })\n }\n\n #rowToScheduleData(row: ScheduleRow): ScheduleData {\n return {\n id: row.id,\n name: row.name,\n payload: typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload,\n cronExpression: row.cron_expression ?? null,\n everyMs: row.every_ms ? Number(row.every_ms) : null,\n timezone: row.timezone ?? 'UTC',\n from: row.from_date ? new Date(row.from_date) : null,\n to: row.to_date ? new Date(row.to_date) : null,\n limit: row.run_limit ? Number(row.run_limit) : null,\n runCount: Number(row.run_count ?? 0),\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : null,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : null,\n status: row.status === 'paused' || row.status === 'cancelled' ? 'paused' : 'active',\n createdAt: row.created_at ? new Date(row.created_at) : new Date(),\n }\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,OAAO,aAAa;AAqDb,SAAS,KAAK,QAAoB,WAAoB;AAC3D,SAAO,MAAM;AACX,UAAM,iBAAiB,OAAO,WAAW;AACzC,UAAM,aAAa,iBAAiB,SAAS,QAAQ,MAAM;AAC3D,WAAO,IAAI,YAAY,EAAE,YAAY,WAAW,gBAAgB,CAAC,eAAe,CAAC;AAAA,EACnF;AACF;AAMO,IAAM,cAAN,MAAqC;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAoB;AAAA,EAEpB,YAAY,QAA4B;AACtC,SAAK,cAAc,OAAO;AAC1B,SAAK,aAAa,OAAO,aAAa;AACtC,SAAK,kBAAkB,OAAO,sBAAsB;AACpD,SAAK,kBAAkB,OAAO,kBAAkB;AAAA,EAClD;AAAA,EAEA,YAAY,UAAwB;AAClC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,iBAAiB;AACxB,YAAM,KAAK,YAAY,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAM,MAAmC;AACvC,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,MAAM,QAAQ,OAA4C;AACxD,UAAM,MAAM,KAAK,IAAI;AAGrB,UAAM,KAAK,oBAAoB,OAAO,GAAG;AAGzC,WAAO,KAAK,YAAY,YAAY,OAAO,QAAQ;AAEjD,UAAI,QAAQ,IAAI,KAAK,UAAU,EAC5B,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,SAAS,EACzB,QAAQ,SAAS,KAAK;AAEzB,UAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAQ,MAAM,UAAU,EAAE,WAAW;AAAA,MACvC;AAEA,YAAM,MAAM,MAAM,MAAM,MAAM;AAE9B,UAAI,CAAC,KAAK;AACR,eAAO;AAAA,MACT;AAIA,YAAM,cAAc,IAAI,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,MAAM,SAAS,KAAK;AAEjF,UAAI,CAAC,KAAK,oBAAoB,GAAG;AAC/B,oBAAY,MAAM,UAAU,SAAS;AAAA,MACvC;AAEA,YAAM,UAAU,MAAM,YAAY,OAAO;AAAA,QACvC,QAAQ;AAAA,QACR,WAAW,KAAK;AAAA,QAChB,aAAa;AAAA,MACf,CAAC;AAGD,UAAI,YAAY,GAAG;AACjB,eAAO;AAAA,MACT;AAEA,YAAM,UAAmB,KAAK,MAAM,IAAI,IAAI;AAE5C,aAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAA+B;AAC7B,UAAM,SAAS,KAAK,YAAY,OAAO,OAAO;AAC9C,WAAO,WAAW,QAAQ,WAAW,WAAW,WAAW,YAAY,WAAW;AAAA,EACpF;AAAA,EAEA,MAAM,oBAAoB,OAAe,KAA4B;AAEnE,UAAM,KAAK,YAAY,YAAY,OAAO,QAAQ;AAChD,UAAI,QAAQ,IAAI,KAAK,UAAU,EAC5B,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,SAAS,EACzB,MAAM,cAAc,MAAM,GAAG,EAC7B,OAAO,MAAM,MAAM;AAEtB,UAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAQ,MAAM,UAAU,EAAE,WAAW;AAAA,MACvC;AAEA,YAAM,cAAc,MAAM;AAE1B,UAAI,YAAY,WAAW,EAAG;AAG9B,iBAAW,OAAO,aAAa;AAC7B,cAAM,UAAmB,KAAK,MAAM,IAAI,IAAI;AAC5C,cAAM,WAAW,QAAQ,YAAY;AACrC,cAAM,QAAQ,eAAe,UAAU,GAAG;AAE1C,cAAM,IAAI,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,MAAM,SAAS,KAAK,EAAE,OAAO;AAAA,UAC1E,QAAQ;AAAA,UACR;AAAA,UACA,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,OAAe,OAAe,kBAAgD;AAC9F,UAAM,EAAE,MAAM,QAAQ,SAAS,IAAI,iBAAiB,gBAAgB;AAEpE,QAAI,CAAC,MAAM;AACT,YAAM,KAAK,YAAY,KAAK,UAAU,EACnC,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,OAAO;AACV;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAI;AAErB,UAAM,UAAU,MAAM,KAAK,YAAY,KAAK,UAAU,EACnD,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,OAAO;AAAA,MACN,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAEH,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,UAAM,KAAK,cAAc,OAAO,aAAa,QAAQ,UAAU,GAAG;AAAA,EACpE;AAAA,EAEA,MAAM,QACJ,OACA,OACA,OACA,cACe;AACf,UAAM,EAAE,MAAM,QAAQ,SAAS,IAAI,iBAAiB,YAAY;AAEhE,QAAI,CAAC,MAAM;AACT,YAAM,KAAK,YAAY,KAAK,UAAU,EACnC,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,OAAO;AACV;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAI;AAErB,UAAM,UAAU,MAAM,KAAK,YAAY,KAAK,UAAU,EACnD,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,OAAO;AAAA,MACN,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,aAAa;AAAA,MACb,aAAa;AAAA,MACb,OAAO,OAAO,WAAW;AAAA,IAC3B,CAAC;AAEH,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AAEA,UAAM,KAAK,cAAc,OAAO,UAAU,QAAQ,UAAU,GAAG;AAAA,EACjE;AAAA,EAEA,MAAM,OAAO,OAAe,OAA0C;AACpE,UAAM,MAAM,MAAM,KAAK,YAAY,KAAK,UAAU,EAC/C,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM;AAET,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,IACT;AAEA,UAAM,UAAmB,KAAK,MAAM,IAAI,IAAI;AAE5C,WAAO;AAAA,MACL,QAAQ,IAAI;AAAA,MACZ,MAAM;AAAA,MACN,YAAY,IAAI,cAAc,OAAO,IAAI,WAAW,IAAI;AAAA,MACxD,OAAO,IAAI,SAAS;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,cACJ,OACA,QACA,QACA,UACA,KACe;AACf,QAAI,SAAS,GAAG;AACd,YAAM,SAAS,MAAM;AACrB,YAAM,KAAK,YAAY,KAAK,UAAU,EACnC,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,MAAM,EACtB,MAAM,eAAe,KAAK,MAAM,EAChC,OAAO;AAAA,IACZ;AAEA,QAAI,WAAW,GAAG;AAChB,YAAM,SAAS,KAAK,YAAY,KAAK,UAAU,EAC5C,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,MAAM,EACtB,QAAQ,eAAe,MAAM,EAC7B,MAAM,QAAQ,EACd,OAAO,IAAI;AAEd,YAAM,KAAK,YAAY,KAAK,UAAU,EACnC,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,MAAM,EACtB,WAAW,MAAM,MAAM,EACvB,OAAO;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAAe,OAAe,SAA+B;AAC1E,UAAM,MAAM,KAAK,IAAI;AAGrB,UAAM,YAAY,MAAM,KAAK,YAAY,KAAK,UAAU,EACrD,MAAM,MAAM,KAAK,EACjB,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,MAAM;AAET,QAAI,CAAC,UAAW;AAEhB,UAAM,UAAmB,KAAK,MAAM,UAAU,IAAI;AAClD,YAAQ,YAAY,QAAQ,YAAY,KAAK;AAE7C,UAAM,cAAc,KAAK,UAAU,OAAO;AAE1C,QAAI,WAAW,QAAQ,QAAQ,IAAI,KAAK;AAEtC,YAAM,KAAK,YAAY,KAAK,UAAU,EAAE,MAAM,MAAM,KAAK,EAAE,MAAM,SAAS,KAAK,EAAE,OAAO;AAAA,QACtF,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,WAAW;AAAA,QACX,aAAa;AAAA,QACb,OAAO;AAAA,QACP,YAAY,QAAQ,QAAQ;AAAA,MAC9B,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,WAAW,QAAQ,YAAY;AACrC,YAAM,QAAQ,eAAe,UAAU,GAAG;AAE1C,YAAM,KAAK,YAAY,KAAK,UAAU,EAAE,MAAM,MAAM,KAAK,EAAE,MAAM,SAAS,KAAK,EAAE,OAAO;AAAA,QACtF,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,WAAW;AAAA,QACX,aAAa;AAAA,QACb;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,SAA8C;AACvD,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,MAAM,OAAO,OAAe,SAA8C;AACxE,UAAM,WAAW,QAAQ,YAAY;AACrC,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,QAAQ,eAAe,UAAU,SAAS;AAEhD,QAAI,QAAQ,OAAO;AACjB,aAAO,KAAK,eAAe,OAAO,SAAS;AAAA,QACzC,IAAI,QAAQ;AAAA,QACZ;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO;AAAA,MAC7C,IAAI,QAAQ;AAAA,MACZ;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,SAAkB,OAA2C;AAC3E,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,MAAM,YAAY,OAAe,SAAkB,OAA2C;AAC5F,UAAM,YAAY,KAAK,IAAI,IAAI;AAE/B,QAAI,QAAQ,OAAO;AACjB,aAAO,KAAK,eAAe,OAAO,SAAS;AAAA,QACzC,IAAI,QAAQ;AAAA,QACZ;AAAA,QACA,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAEA,UAAM,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO;AAAA,MAC7C,IAAI,QAAQ;AAAA,MACZ;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,eACJ,OACA,SACA,WACqB;AACrB,UAAM,QAAQ,QAAQ;AAEtB,QAAI;AACF,aAAO,MAAM,KAAK,kBAAkB,OAAO,SAAS,WAAW,KAAK;AAAA,IACtE,SAAS,KAAK;AACZ,UAAI,KAAK,sBAAsB,GAAG,GAAG;AACnC,cAAM,IAAI;AAAA,UACR,6BAA6B,KAAK,UAAU;AAAA,UAC5C,EAAE,OAAO,IAAI;AAAA,QACf;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,sBAAsB,KAAuB;AAC3C,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,UAAM,UAAW,IAA6B;AAC9C,QAAI,CAAC,QAAS,QAAO;AAIrB,WACE,WAAW,KAAK,OAAO,KAAK,kDAAkD,KAAK,OAAO;AAAA,EAE9F;AAAA,EAEA,kBACE,OACA,SACA,WACA,OACqB;AACrB,WAAO,KAAK,YAAY,YAAY,OAAO,QAAQ;AACjD,YAAM,WAAW,MAAM,IAAI,KAAK,UAAU,EACvC,MAAM,SAAS,KAAK,EACpB,MAAM,YAAY,MAAM,EAAE,EAC1B,QAAQ,YAAY,MAAM,EAC1B,UAAU,EACV,MAAM;AAET,YAAM,MAAM,KAAK,IAAI;AAErB,UAAI,UAAU;AACZ,cAAM,UAAU,SAAS,YAAY,OAAO,OAAO,SAAS,QAAQ,IAAI;AACxE,cAAM,WAAW,SAAS,aAAa,OAAO,OAAO,SAAS,SAAS,IAAI;AAC3E,cAAM,YAAY,aAAa,QAAS,YAAY,QAAQ,MAAM,UAAU;AAE5E,YAAI,WAAW;AACb,gBAAMA,UAAS,SAAS;AACxB,gBAAM,cAAcA,YAAW,aAAaA,YAAW;AAEvD,cAAI,MAAM,WAAW,aAAa;AAChC,kBAAM,aACJ,OAAO,SAAS,SAAS,WAAW,KAAK,MAAM,SAAS,IAAI,IAAI,SAAS;AAC3E,kBAAM,UAAU,EAAE,GAAG,YAAY,SAAS,QAAQ,QAAQ;AAC1D,kBAAM,UAAmC,EAAE,MAAM,KAAK,UAAU,OAAO,EAAE;AACzE,gBAAI,MAAM,UAAU,UAAU;AAC5B,sBAAQ,WAAW;AAAA,YACrB;AACA,kBAAM,IAAI,KAAK,UAAU,EAAE,MAAM,EAAE,IAAI,SAAS,IAAI,MAAM,CAAC,EAAE,OAAO,OAAO;AAC3E,mBAAO,EAAE,SAAS,YAA4B,OAAO,SAAS,GAAa;AAAA,UAC7E;AAEA,cAAI,MAAM,UAAU,UAAU;AAC5B,kBAAM,IAAI,KAAK,UAAU,EAAE,MAAM,EAAE,IAAI,SAAS,IAAI,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,IAAI,CAAC;AACrF,mBAAO,EAAE,SAAS,YAA4B,OAAO,SAAS,GAAa;AAAA,UAC7E;AAEA,iBAAO,EAAE,SAAS,WAA2B,OAAO,SAAS,GAAa;AAAA,QAC5E;AAKA,cAAM,SAAS,SAAS;AACxB,YAAI,WAAW,aAAa,WAAW,aAAa,WAAW,UAAU;AACvE,gBAAM,IAAI,KAAK,UAAU,EACtB,MAAM,EAAE,IAAI,SAAS,IAAI,MAAM,CAAC,EAChC,OAAO,EAAE,UAAU,MAAM,UAAU,MAAM,WAAW,KAAK,CAAC;AAAA,QAC/D;AAAA,MACF;AAEA,UAAI,WAAW;AACf,UAAI;AACF,cAAM,IAAI,YAAY,OAAO,OAAO;AAClC,gBAAM,GAAG,KAAK,UAAU,EAAE,OAAO;AAAA,YAC/B,GAAG;AAAA,YACH,UAAU,MAAM;AAAA,YAChB,UAAU;AAAA,YACV,WAAW,MAAM,OAAO;AAAA,UAC1B,CAAC;AAAA,QACH,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,YAAI,KAAK,mBAAmB,GAAG,GAAG;AAChC,qBAAW;AAAA,QACb,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,UAAU;AACZ,cAAM,SAAS,MAAM,IAAI,KAAK,UAAU,EACrC,MAAM,SAAS,KAAK,EACpB,MAAM,YAAY,MAAM,EAAE,EAC1B,QAAQ,UAAU,CAAC,WAAW,SAAS,CAAC,EACxC,QAAQ,YAAY,MAAM,EAC1B,MAAM;AACT,YAAI,QAAQ;AACV,iBAAO,EAAE,SAAS,WAA2B,OAAO,OAAO,GAAa;AAAA,QAC1E;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,SAAyB,OAAO,QAAQ,GAAG;AAAA,IAC/D,CAAC;AAAA,EACH;AAAA,EAEA,mBAAmB,KAAuB;AACxC,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,UAAM,IAAI;AACV,WACE,EAAE,SAAS,WACX,EAAE,SAAS,8BACX,qBAAqB,KAAK,EAAE,WAAW,EAAE;AAAA,EAE7C;AAAA,EAEA,MAAM,SAAS,MAAgC;AAC7C,WAAO,KAAK,WAAW,WAAW,IAAI;AAAA,EACxC;AAAA,EAEA,MAAM,WAAW,OAAe,MAAgC;AAC9D,QAAI,KAAK,WAAW,EAAG;AAEvB,QAAI,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,GAAG;AAC7B,YAAM,IAAI,MAAM,+DAA+D;AAAA,IACjF;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,OAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MAC9B,IAAI,IAAI;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,GAAG;AAAA,MACxB,OAAO,eAAe,IAAI,YAAY,kBAAkB,GAAG;AAAA,IAC7D,EAAE;AAEF,UAAM,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO,IAAI;AAAA,EACrD;AAAA,EAEA,MAAM,OAAwB;AAC5B,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,MAAM,OAAO,OAAgC;AAC3C,UAAM,SAAS,MAAM,KAAK,YAAY,KAAK,UAAU,EAClD,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,SAAS,EACzB,MAAM,YAAY,EAClB,MAAM;AAET,WAAO,OAAO,QAAQ,SAAS,CAAC;AAAA,EAClC;AAAA,EAEA,MAAM,mBACJ,OACA,kBACA,iBACiB;AACjB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,MAAM;AAG5B,WAAO,KAAK,YAAY,YAAY,OAAO,QAAQ;AACjD,UAAI,YAAY;AAEhB,UAAI,QAAQ,IAAI,KAAK,UAAU,EAC5B,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,MAAM,eAAe,KAAK,aAAa,EACvC,OAAO,MAAM,MAAM;AAEtB,UAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAQ,MAAM,UAAU,EAAE,WAAW;AAAA,MACvC;AAEA,YAAM,cAAc,MAAM;AAE1B,iBAAW,OAAO,aAAa;AAC7B,cAAM,UAAmB,KAAK,MAAM,IAAI,IAAI;AAC5C,cAAM,sBAAsB,QAAQ,gBAAgB;AAEpD,YAAI,uBAAuB,iBAAiB;AAE1C,gBAAM,IAAI,KAAK,UAAU,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,MAAM,SAAS,KAAK,EAAE,OAAO;AAAA,QAC9E,OAAO;AAEL,kBAAQ,eAAe,sBAAsB;AAC7C,gBAAM,WAAW,QAAQ,YAAY;AACrC,gBAAM,QAAQ,eAAe,UAAU,GAAG;AAE1C,gBAAM,IAAI,KAAK,UAAU,EACtB,MAAM,MAAM,IAAI,EAAE,EAClB,MAAM,SAAS,KAAK,EACpB,OAAO;AAAA,YACN,QAAQ;AAAA,YACR,MAAM,KAAK,UAAU,OAAO;AAAA,YAC5B,WAAW;AAAA,YACX,aAAa;AAAA,YACb;AAAA,UACF,CAAC;AAEH;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,OAAe,QAAmC;AAChE,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,IAAI;AAKrB,UAAM,UAAU,MAAM,KAAK,YAAY,KAAK,UAAU,EACnD,MAAM,SAAS,KAAK,EACpB,MAAM,UAAU,QAAQ,EACxB,MAAM,aAAa,KAAK,SAAS,EACjC,QAAQ,MAAM,MAAM,EACpB,OAAO,EAAE,aAAa,IAAI,CAAC;AAE9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,eAAe,QAAyC;AAC5D,UAAM,KAAK,OAAO,MAAM,WAAW;AAEnC,UAAM,OAAO;AAAA,MACX;AAAA,MACA,MAAM,OAAO;AAAA,MACb,SAAS,KAAK,UAAU,OAAO,OAAO;AAAA,MACtC,iBAAiB,OAAO,kBAAkB;AAAA,MAC1C,UAAU,OAAO,WAAW;AAAA,MAC5B,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO,QAAQ;AAAA,MAC1B,SAAS,OAAO,MAAM;AAAA,MACtB,WAAW,OAAO,SAAS;AAAA,MAC3B,QAAQ;AAAA,IACV;AAGA,UAAM,KAAK,YAAY,KAAK,eAAe,EACxC,OAAO;AAAA,MACN,GAAG;AAAA,MACH,WAAW;AAAA,MACX,YAAY,KAAK,YAAY,GAAG,IAAI;AAAA,IACtC,CAAC,EACA,WAAW,IAAI,EACf,MAAM;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,iBAAiB,KAAK;AAAA,MACtB,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,WAAW,KAAK;AAAA,MAChB,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,QAAQ;AAAA,IACV,CAAC;AAEH,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,QAAyC;AACtD,WAAO,KAAK,eAAe,MAAM;AAAA,EACnC;AAAA,EAEA,MAAM,YAAY,IAA0C;AAC1D,UAAM,MAAO,MAAM,KAAK,YAAY,KAAK,eAAe,EAAE,MAAM,MAAM,EAAE,EAAE,MAAM;AAGhF,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO,KAAK,mBAAmB,GAAG;AAAA,EACpC;AAAA,EAEA,MAAM,cAAc,SAAwD;AAC1E,QAAI,QAAQ,KAAK,YAAY,KAAK,eAAe,EAAE,SAAS,UAAU,WAAW;AAEjF,QAAI,SAAS,QAAQ;AACnB,cAAQ,MAAM,MAAM,UAAU,QAAQ,MAAM;AAAA,IAC9C;AAEA,UAAM,OAAQ,MAAM;AACpB,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,mBAAmB,GAAG,CAAC;AAAA,EACvD;AAAA,EAEA,MAAM,eACJ,IACA,SACe;AACf,UAAM,OAAgC,CAAC;AAEvC,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ;AACxD,QAAI,QAAQ,cAAc,OAAW,MAAK,cAAc,QAAQ;AAChE,QAAI,QAAQ,cAAc,OAAW,MAAK,cAAc,QAAQ;AAChE,QAAI,QAAQ,aAAa,OAAW,MAAK,YAAY,QAAQ;AAE7D,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AAChC,YAAM,KAAK,YAAY,KAAK,eAAe,EAAE,MAAM,MAAM,EAAE,EAAE,OAAO,IAAI;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,IAA2B;AAC9C,UAAM,KAAK,YAAY,KAAK,eAAe,EAAE,MAAM,MAAM,EAAE,EAAE,OAAO;AAAA,EACtE;AAAA,EAEA,MAAM,mBAAiD;AACrD,UAAM,MAAM,oBAAI,KAAK;AAErB,WAAO,KAAK,YAAY,YAAY,OAAO,QAAQ;AAEjD,UAAI,QAAQ,IAAI,KAAK,eAAe,EACjC,MAAM,UAAU,QAAQ,EACxB,aAAa,aAAa,EAC1B,MAAM,eAAe,MAAM,GAAG,EAC9B,MAAM,CAAC,YAAY;AAClB,gBAAQ,UAAU,WAAW,EAAE,WAAW,uBAAuB;AAAA,MACnE,CAAC,EACA,MAAM,CAAC,YAAY;AAClB,gBAAQ,UAAU,SAAS,EAAE,QAAQ,WAAW,MAAM,GAAG;AAAA,MAC3D,CAAC,EACA,QAAQ,eAAe,KAAK,EAC5B,MAAM,CAAC;AAEV,UAAI,KAAK,oBAAoB,GAAG;AAC9B,gBAAQ,MAAM,UAAU,EAAE,WAAW;AAAA,MACvC;AAEA,YAAM,MAAO,MAAM,MAAM,MAAM;AAC/B,UAAI,CAAC,IAAK,QAAO;AAGjB,UAAI,YAAyB;AAC7B,YAAM,cAAc,OAAO,IAAI,aAAa,CAAC,IAAI;AAEjD,UAAI,IAAI,UAAU;AAChB,oBAAY,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,CAAC;AAAA,MAC3D,WAAW,IAAI,iBAAiB;AAE9B,cAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,aAAa;AAC3D,cAAM,OAAO,qBAAqB,MAAM,IAAI,iBAAiB;AAAA,UAC3D,aAAa;AAAA,UACb,IAAI,IAAI,YAAY;AAAA,QACtB,CAAC;AACD,oBAAY,KAAK,KAAK,EAAE,OAAO;AAAA,MACjC;AAGA,UAAI,IAAI,cAAc,QAAQ,eAAe,OAAO,IAAI,SAAS,GAAG;AAClE,oBAAY;AAAA,MACd;AAGA,UAAI,aAAa,IAAI,WAAW,YAAY,IAAI,KAAK,IAAI,OAAO,GAAG;AACjE,oBAAY;AAAA,MACd;AAGA,YAAM,IAAI,KAAK,eAAe,EAAE,MAAM,MAAM,IAAI,EAAE,EAAE,OAAO;AAAA,QACzD,aAAa;AAAA,QACb,aAAa;AAAA,QACb,WAAW;AAAA,MACb,CAAC;AAGD,aAAO,KAAK,mBAAmB,GAAG;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,mBAAmB,KAAgC;AACjD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,SAAS,OAAO,IAAI,YAAY,WAAW,KAAK,MAAM,IAAI,OAAO,IAAI,IAAI;AAAA,MACzE,gBAAgB,IAAI,mBAAmB;AAAA,MACvC,SAAS,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,MAC/C,UAAU,IAAI,YAAY;AAAA,MAC1B,MAAM,IAAI,YAAY,IAAI,KAAK,IAAI,SAAS,IAAI;AAAA,MAChD,IAAI,IAAI,UAAU,IAAI,KAAK,IAAI,OAAO,IAAI;AAAA,MAC1C,OAAO,IAAI,YAAY,OAAO,IAAI,SAAS,IAAI;AAAA,MAC/C,UAAU,OAAO,IAAI,aAAa,CAAC;AAAA,MACnC,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,MACzD,WAAW,IAAI,cAAc,IAAI,KAAK,IAAI,WAAW,IAAI;AAAA,MACzD,QAAQ,IAAI,WAAW,YAAY,IAAI,WAAW,cAAc,WAAW;AAAA,MAC3E,WAAW,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,IAAI,oBAAI,KAAK;AAAA,IAClE;AAAA,EACF;AACF;","names":["status"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Redis, RedisOptions } from 'ioredis';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, P as PushResult, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-C4oyCVxR.js';
|
|
3
3
|
|
|
4
4
|
type RedisConfig = Redis | RedisOptions;
|
|
5
5
|
/**
|
|
@@ -24,15 +24,16 @@ declare class RedisAdapter implements Adapter {
|
|
|
24
24
|
failJob(jobId: string, queue: string, error?: Error, removeOnFail?: JobRetention): Promise<void>;
|
|
25
25
|
retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
|
|
26
26
|
getJob(jobId: string, queue: string): Promise<JobRecord | null>;
|
|
27
|
-
push(jobData: JobData): Promise<void>;
|
|
28
|
-
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
29
|
-
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
30
|
-
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
27
|
+
push(jobData: JobData): Promise<PushResult | void>;
|
|
28
|
+
pushLater(jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
29
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
30
|
+
pushOn(queue: string, jobData: JobData): Promise<PushResult | void>;
|
|
31
31
|
pushMany(jobs: JobData[]): Promise<void>;
|
|
32
32
|
pushManyOn(queue: string, jobs: JobData[]): Promise<void>;
|
|
33
33
|
size(): Promise<number>;
|
|
34
34
|
sizeOf(queue: string): Promise<number>;
|
|
35
35
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
36
|
+
renewJobs(queue: string, jobIds: string[]): Promise<number>;
|
|
36
37
|
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
37
38
|
/**
|
|
38
39
|
* @deprecated Use `upsertSchedule` instead.
|