@lavoro/core 0.3.0 → 0.4.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/build/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Logger as Logger$1, LogObject, Levels } from '@julr/utils/logger';
2
+ import { EventEmitter } from 'stream';
2
3
  import { LockFactory } from '@verrou/core';
3
4
  import { SerializedLock, Duration } from '@verrou/core/types';
4
5
  import { Cron } from 'croner';
@@ -16,6 +17,31 @@ declare class Logger {
16
17
  }
17
18
  declare function createDefaultLogger(name: string, level?: Levels): Logger;
18
19
 
20
+ /**
21
+ * Type-safe queue driver events.
22
+ */
23
+ interface QueueDriverEvents {
24
+ error: [error: Error];
25
+ 'job:start': [job: Job, payload: unknown];
26
+ 'job:progress': [job: Job, payload: unknown, elapsed: number];
27
+ 'job:complete': [job: Job, payload: unknown, elapsed: number];
28
+ 'job:error': [error: Error, job: Job, payload: unknown];
29
+ 'job:finish': [job: Job, payload: unknown, elapsed: number];
30
+ }
31
+ /**
32
+ * Type-safe event emitter for the queue driver.
33
+ */
34
+ declare abstract class QueueDriverEventEmitter extends EventEmitter {
35
+ on<K extends keyof QueueDriverEvents>(event: K, listener: (...args: QueueDriverEvents[K]) => void): this;
36
+ off<K extends keyof QueueDriverEvents>(event: K, listener: (...args: QueueDriverEvents[K]) => void): this;
37
+ emit<K extends keyof QueueDriverEvents>(event: K, ...args: QueueDriverEvents[K]): boolean;
38
+ }
39
+
40
+ type ProcessJobParams = {
41
+ id: string;
42
+ fullyQualifiedName: string;
43
+ payload: unknown;
44
+ };
19
45
  /**
20
46
  * Interface to be augmented by users to define their queue names.
21
47
  * This enables type-safe queue names throughout the application.
@@ -52,13 +78,14 @@ type QueueDriverStopOptions = {
52
78
  timeout?: number;
53
79
  };
54
80
  type QueueDriverConfig = {};
55
- declare abstract class QueueDriver<Config extends QueueDriverConfig = QueueDriverConfig> {
81
+ declare abstract class QueueDriver<Config extends QueueDriverConfig = QueueDriverConfig> extends QueueDriverEventEmitter {
56
82
  protected config: QueueConfig;
57
83
  protected options: Record<string, WorkerOptions>;
58
84
  protected driverConfig: Config;
59
85
  protected logger: Logger;
60
86
  protected registeredQueues: Set<string>;
61
87
  protected registeredJobs: Map<string, new () => Job>;
88
+ protected lockFactory?: LockFactory;
62
89
  connection: QueueConnectionName | undefined;
63
90
  constructor(config: QueueConfig, options: Record<string, WorkerOptions>, driverConfig?: Config);
64
91
  setLogger(logger: Logger): void;
@@ -93,6 +120,11 @@ declare abstract class QueueDriver<Config extends QueueDriverConfig = QueueDrive
93
120
  * @param lockFactory - The lock factory instance to clean up
94
121
  */
95
122
  destroyLockProvider(_lockFactory: LockFactory): Promise<void>;
123
+ /**
124
+ * Execute a job with the given parameters.
125
+ * This method contains the common job processing logic shared by all drivers.
126
+ */
127
+ protected process(params: ProcessJobParams): Promise<void>;
96
128
  }
97
129
 
98
130
  declare class PendingDispatch<T extends Job, P extends Payload<T>, C extends QueueConnectionName = DefaultConnection extends {
@@ -111,7 +143,21 @@ declare class PendingDispatch<T extends Job, P extends Payload<T>, C extends Que
111
143
  then<TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
112
144
  }
113
145
 
114
- declare class Queue {
146
+ /**
147
+ * Queue service can emit any of the queue driver
148
+ * events alongside its own events (currently none).
149
+ */
150
+ interface QueueEvents extends QueueDriverEvents {
151
+ }
152
+ /**
153
+ * Type-safe event emitter for the queue service.
154
+ */
155
+ declare abstract class QueueEventEmitter extends QueueDriverEventEmitter {
156
+ on<K extends keyof QueueEvents>(event: K, listener: (...args: QueueEvents[K]) => void): this;
157
+ emit<K extends keyof QueueEvents>(event: K, ...args: QueueEvents[K]): boolean;
158
+ }
159
+
160
+ declare class Queue extends QueueEventEmitter {
115
161
  private config;
116
162
  private drivers;
117
163
  private started;
@@ -121,6 +167,11 @@ declare class Queue {
121
167
  constructor(config: QueueConfig & {
122
168
  logger?: Logger;
123
169
  });
170
+ /**
171
+ * Bind all queue driver events and
172
+ * proxy them through the queue service.
173
+ */
174
+ private bindEvents;
124
175
  private createDriver;
125
176
  start(): Promise<void>;
126
177
  stop(options?: QueueDriverStopOptions): Promise<void>;
@@ -398,7 +449,7 @@ declare function intervalToCron(interval: ScheduleInterval, options?: IntervalCr
398
449
  declare const getDistributedLockKey: (name: string) => string;
399
450
  declare class PendingSchedule {
400
451
  protected name: string;
401
- protected cb: (serializedLock?: SerializedLock) => MaybePromise<void>;
452
+ protected cb: (lock?: SerializedLock) => MaybePromise<void>;
402
453
  protected lockProviderResolver: () => LockFactory;
403
454
  protected cronPattern?: string;
404
455
  protected interval: ScheduleInterval;
@@ -424,7 +475,7 @@ declare class PendingSchedule {
424
475
  */
425
476
  handOff: boolean;
426
477
  };
427
- constructor(name: string, cb: (serializedLock?: SerializedLock) => MaybePromise<void>, lockProviderResolver: () => LockFactory);
478
+ constructor(name: string, cb: (lock?: SerializedLock) => MaybePromise<void>, lockProviderResolver: () => LockFactory);
428
479
  /**
429
480
  * Schedule using a cron pattern.
430
481
  * You can use https://crontab.guru to generate a cron pattern.
@@ -592,4 +643,4 @@ declare class ScheduleRegistry {
592
643
  static clear(name?: string): void;
593
644
  }
594
645
 
595
- export { type ConfiguredDriver, type ConnectionQueues, type DefaultConnection, type InferConnectionQueues, type InferConnections, type InferDefaultConnection, type InferQueueNames, type InferQueueNamesForConnection, type IntervalCronOptions, Job, Logger, type MaybePromise, type Payload, PendingDispatch, PendingJobSchedule, PendingSchedule, Queue, type QueueConfig, type QueueConnectionConfig, type QueueConnectionName, type QueueConnections, type QueueConnectionsList, QueueDriver, type QueueDriverConfig, type QueueDriverStopOptions, type QueueList, type QueueName, type QueueNameForConnection, Schedule, type ScheduleInterval, type ScheduleIntervalTime, ScheduleRegistry, type WorkerOptions, createDefaultLogger, defaultScheduleLockProvider, defineConfig, getDistributedLockKey, intervalToCron, parseTime };
646
+ export { type ConfiguredDriver, type ConnectionQueues, type DefaultConnection, type InferConnectionQueues, type InferConnections, type InferDefaultConnection, type InferQueueNames, type InferQueueNamesForConnection, type IntervalCronOptions, Job, Logger, type MaybePromise, type Payload, type PayloadWithLock, PendingDispatch, PendingJobSchedule, PendingSchedule, type ProcessJobParams, Queue, type QueueConfig, type QueueConnectionConfig, type QueueConnectionName, type QueueConnections, type QueueConnectionsList, QueueDriver, type QueueDriverConfig, type QueueDriverEvents, type QueueDriverStopOptions, type QueueEvents, type QueueList, type QueueName, type QueueNameForConnection, Schedule, type ScheduleInterval, type ScheduleIntervalTime, ScheduleRegistry, type WorkerOptions, createDefaultLogger, defaultScheduleLockProvider, defineConfig, getDistributedLockKey, intervalToCron, parseTime };
package/build/index.js CHANGED
@@ -19,6 +19,12 @@ var __spreadValues = (a, b) => {
19
19
  return a;
20
20
  };
21
21
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
23
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
24
+ }) : x)(function(x) {
25
+ if (typeof require !== "undefined") return require.apply(this, arguments);
26
+ throw Error('Dynamic require of "' + x + '" is not supported');
27
+ });
22
28
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
23
29
  var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
24
30
  var __async = (__this, __arguments, generator) => {
@@ -43,7 +49,26 @@ var __async = (__this, __arguments, generator) => {
43
49
  };
44
50
 
45
51
  // src/logger.ts
46
- import pino from "pino";
52
+ var NoOpLogger = class {
53
+ constructor() {
54
+ __publicField(this, "level", "trace");
55
+ }
56
+ child(_obj) {
57
+ return this;
58
+ }
59
+ trace(_msg, _obj) {
60
+ }
61
+ debug(_msg, _obj) {
62
+ }
63
+ info(_msg, _obj) {
64
+ }
65
+ warn(_msg, _obj) {
66
+ }
67
+ error(_msg, _obj) {
68
+ }
69
+ fatal(_msg, _obj) {
70
+ }
71
+ };
47
72
  var Logger = class _Logger {
48
73
  constructor(internalLogger) {
49
74
  __publicField(this, "internalLogger");
@@ -72,7 +97,12 @@ var Logger = class _Logger {
72
97
  }
73
98
  };
74
99
  function createDefaultLogger(name, level = "info") {
75
- return new Logger(pino({ name, level }));
100
+ try {
101
+ const pino = __require("pino");
102
+ return new Logger(pino({ name, level }));
103
+ } catch (e) {
104
+ return new Logger(new NoOpLogger());
105
+ }
76
106
  }
77
107
 
78
108
  // src/queue/define_config.ts
@@ -211,15 +241,34 @@ var _Job = class _Job {
211
241
  __publicField(_Job, "defaultQueueServiceResolver");
212
242
  var Job = _Job;
213
243
 
244
+ // src/queue/contracts/queue_driver_event_emitter.ts
245
+ import { EventEmitter } from "stream";
246
+ var QueueDriverEventEmitter = class extends EventEmitter {
247
+ on(event, listener) {
248
+ return super.on(event, listener);
249
+ }
250
+ off(event, listener) {
251
+ return super.off(event, listener);
252
+ }
253
+ emit(event, ...args) {
254
+ if (this.listenerCount(event) === 0) {
255
+ return false;
256
+ }
257
+ return super.emit(event, ...args);
258
+ }
259
+ };
260
+
214
261
  // src/queue/contracts/queue_driver.ts
215
- var QueueDriver = class {
262
+ var QueueDriver = class extends QueueDriverEventEmitter {
216
263
  constructor(config, options, driverConfig = {}) {
264
+ super();
217
265
  this.config = config;
218
266
  this.options = options;
219
267
  this.driverConfig = driverConfig;
220
268
  __publicField(this, "logger");
221
269
  __publicField(this, "registeredQueues", /* @__PURE__ */ new Set());
222
270
  __publicField(this, "registeredJobs", /* @__PURE__ */ new Map());
271
+ __publicField(this, "lockFactory");
223
272
  __publicField(this, "connection");
224
273
  this.logger = createDefaultLogger("queue");
225
274
  }
@@ -322,6 +371,124 @@ var QueueDriver = class {
322
371
  return __async(this, null, function* () {
323
372
  });
324
373
  }
374
+ /**
375
+ * Execute a job with the given parameters.
376
+ * This method contains the common job processing logic shared by all drivers.
377
+ */
378
+ process(params) {
379
+ return __async(this, null, function* () {
380
+ const { id, fullyQualifiedName, payload } = params;
381
+ const { queue, name } = Job.parseName(fullyQualifiedName);
382
+ if (!queue || !name) {
383
+ const error = new Error(`Invalid job class name: ${fullyQualifiedName}`);
384
+ this.logger.warn(error);
385
+ throw error;
386
+ }
387
+ this.logger.trace({ job: name }, "Processing job");
388
+ try {
389
+ this.checkIfJobIsRegistered(name);
390
+ } catch (error) {
391
+ this.logger.warn(error);
392
+ throw error;
393
+ }
394
+ const jobClass = this.registeredJobs.get(name);
395
+ const jobInstance = new jobClass();
396
+ jobInstance.connection = this.connection;
397
+ jobInstance.queue = queue;
398
+ jobInstance.id = id;
399
+ this.logger.debug(
400
+ {
401
+ connection: this.connection,
402
+ queue,
403
+ job: name,
404
+ id
405
+ },
406
+ "Processing job"
407
+ );
408
+ const serializedLock = payload == null ? void 0 : payload._lock;
409
+ const ttl = serializedLock == null ? void 0 : serializedLock.ttl;
410
+ let lock;
411
+ if (serializedLock !== void 0) {
412
+ if (this.lockFactory === void 0) {
413
+ this.logger.warn(
414
+ { job: name, id },
415
+ "Scheduled job has lock but lock factory is not available"
416
+ );
417
+ } else {
418
+ try {
419
+ lock = this.lockFactory.restoreLock(serializedLock);
420
+ yield lock.acquireImmediately();
421
+ if (ttl) {
422
+ yield lock.extend(ttl);
423
+ }
424
+ this.logger.trace(
425
+ {
426
+ job: name,
427
+ id,
428
+ serializedLock
429
+ },
430
+ "Restored lock from scheduler"
431
+ );
432
+ } catch (error) {
433
+ this.logger.warn({ job: name, id, error }, "Failed to restore lock");
434
+ }
435
+ }
436
+ }
437
+ const startedAt = Date.now();
438
+ this.emit("job:start", jobInstance, payload);
439
+ let isExtending = false;
440
+ const onProgress = () => __async(this, null, function* () {
441
+ if (lock && ttl && !isExtending) {
442
+ try {
443
+ const remainingTime = lock.getRemainingTime();
444
+ if (remainingTime !== null && remainingTime <= ttl / 2) {
445
+ isExtending = true;
446
+ this.logger.trace(
447
+ { job: name, id, remainingTime, ttl },
448
+ "Extending lock"
449
+ );
450
+ yield lock.extend(ttl);
451
+ }
452
+ } catch (error) {
453
+ this.logger.warn({ job: name, id, error }, "Failed to extend lock");
454
+ } finally {
455
+ isExtending = false;
456
+ }
457
+ }
458
+ this.emit("job:progress", jobInstance, payload, Date.now() - startedAt);
459
+ });
460
+ const intervalId = setInterval(onProgress, 1e3);
461
+ try {
462
+ yield jobInstance.handle(payload);
463
+ this.emit("job:complete", jobInstance, payload, Date.now() - startedAt);
464
+ this.logger.trace(
465
+ {
466
+ connection: this.connection,
467
+ queue,
468
+ job: name,
469
+ id
470
+ },
471
+ "Job completed"
472
+ );
473
+ } catch (error) {
474
+ this.emit("job:error", error, jobInstance, payload);
475
+ } finally {
476
+ this.emit("job:finish", jobInstance, payload, Date.now() - startedAt);
477
+ clearInterval(intervalId);
478
+ if (lock) {
479
+ try {
480
+ yield lock.forceRelease();
481
+ this.logger.trace(
482
+ { job: name, id, lock: lock.serialize() },
483
+ "Released lock for scheduled job"
484
+ );
485
+ } catch (error) {
486
+ this.logger.warn({ job: name, id, error }, "Failed to release lock");
487
+ }
488
+ }
489
+ }
490
+ });
491
+ }
325
492
  };
326
493
 
327
494
  // src/schedule/schedule_interval.ts
@@ -445,8 +612,11 @@ var PendingSchedule = class {
445
612
  __publicField(this, "intervalOptions");
446
613
  __publicField(this, "distributedLockOptions", {
447
614
  key: getDistributedLockKey,
448
- ttl: "1m",
449
- // TODO: Decide on a default TTL
615
+ /**
616
+ * Interval during which the lock is held for the job
617
+ * and no other instances of it will not be scheduled
618
+ */
619
+ ttl: "15s",
450
620
  overlap: false,
451
621
  handOff: false
452
622
  });
@@ -567,9 +737,11 @@ var PendingSchedule = class {
567
737
  return;
568
738
  }
569
739
  const key = this.distributedLockOptions.key(this.name);
570
- const ttl = this.distributedLockOptions.ttl;
571
740
  const lockProvider = this.lockProviderResolver();
572
- const lock = lockProvider.createLock(key, ttl);
741
+ const lock = lockProvider.createLock(
742
+ key,
743
+ this.distributedLockOptions.ttl
744
+ );
573
745
  const acquired = yield lock.acquireImmediately();
574
746
  if (!acquired) {
575
747
  return;
@@ -584,7 +756,6 @@ var PendingSchedule = class {
584
756
  } catch (error) {
585
757
  yield lock.forceRelease();
586
758
  throw error;
587
- } finally {
588
759
  }
589
760
  }))
590
761
  );
@@ -606,9 +777,9 @@ var PendingJobSchedule = class _PendingJobSchedule extends PendingSchedule {
606
777
  constructor(job, payload, lockProviderResolver) {
607
778
  super(
608
779
  job.name,
609
- (serializedLock) => __async(this, null, function* () {
610
- if (serializedLock) {
611
- this.payload._lock = serializedLock;
780
+ (lock) => __async(this, null, function* () {
781
+ if (lock) {
782
+ this.payload._lock = lock;
612
783
  }
613
784
  yield this.dispatch.then(void 0, (error) => {
614
785
  throw error;
@@ -772,9 +943,20 @@ var Schedule = class {
772
943
  */
773
944
  __publicField(Schedule, "defaultLockProviderResolver", () => defaultScheduleLockProvider);
774
945
 
946
+ // src/queue/queue_event_emitter.ts
947
+ var QueueEventEmitter = class extends QueueDriverEventEmitter {
948
+ on(event, listener) {
949
+ return super.on(event, listener);
950
+ }
951
+ emit(event, ...args) {
952
+ return super.emit(event, ...args);
953
+ }
954
+ };
955
+
775
956
  // src/queue/queue.ts
776
- var Queue = class {
957
+ var Queue = class extends QueueEventEmitter {
777
958
  constructor(config) {
959
+ super();
778
960
  this.config = config;
779
961
  __publicField(this, "drivers", /* @__PURE__ */ new Map());
780
962
  __publicField(this, "started", false);
@@ -791,9 +973,29 @@ var Queue = class {
791
973
  );
792
974
  const driver = this.createDriver(driverConfig);
793
975
  driver.connection = connection;
976
+ this.bindEvents(driver);
794
977
  this.drivers.set(driver.connection, driver);
795
978
  }
796
979
  }
980
+ /**
981
+ * Bind all queue driver events and
982
+ * proxy them through the queue service.
983
+ */
984
+ bindEvents(driver) {
985
+ const events = [
986
+ "error",
987
+ "job:start",
988
+ "job:progress",
989
+ "job:complete",
990
+ "job:error",
991
+ "job:finish"
992
+ ];
993
+ for (const event of events) {
994
+ driver.on(event, (...args) => {
995
+ this.emit(event, ...args);
996
+ });
997
+ }
998
+ }
797
999
  createDriver(config) {
798
1000
  const { constructor: driverConstructor, config: driverConfig } = config.driver;
799
1001
  const driver = new driverConstructor(
@@ -868,13 +1070,13 @@ var Queue = class {
868
1070
  Schedule.clear(id);
869
1071
  }
870
1072
  this.scheduledJobs.clear();
1073
+ for (const job of this.config.jobs) {
1074
+ yield this.unregister(job);
1075
+ }
871
1076
  for (const [connection, driver] of this.drivers) {
872
1077
  this.logger.trace({ connection }, "Stopping queue connection");
873
1078
  yield driver.stop(options);
874
1079
  }
875
- for (const job of this.config.jobs) {
876
- yield this.unregister(job);
877
- }
878
1080
  for (const [connection, lockFactory] of this.lockFactories) {
879
1081
  const driver = this.drivers.get(connection);
880
1082
  if (!driver) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/logger.ts","../src/queue/define_config.ts","../src/queue/pending_dispatch.ts","../src/queue/contracts/job.ts","../src/queue/contracts/queue_driver.ts","../src/schedule/schedule_interval.ts","../src/schedule/schedule_registry.ts","../src/schedule/pending_schedule.ts","../src/schedule/pending_job_schedule.ts","../src/schedule/schedule.ts","../src/queue/queue.ts"],"sourcesContent":["import type {\n Logger as InternalLogger,\n Levels,\n LogObject,\n} from '@julr/utils/logger'\nimport pino from 'pino'\n\nexport class Logger {\n private internalLogger: InternalLogger\n\n constructor(internalLogger: InternalLogger) {\n this.internalLogger = internalLogger\n }\n\n child(obj: LogObject) {\n return new Logger(this.internalLogger.child(obj))\n }\n\n trace(msg: any, obj?: any) {\n this.internalLogger.trace(msg, obj)\n }\n\n debug(msg: any, obj?: any) {\n this.internalLogger.debug(msg, obj)\n }\n\n warn(msg: any, obj?: any) {\n this.internalLogger.warn(msg, obj)\n }\n\n error(msg: any, obj?: any) {\n this.internalLogger.error(msg, obj)\n }\n\n fatal(msg: any, obj?: any) {\n this.internalLogger.fatal(msg, obj)\n }\n\n info(msg: any, obj?: any) {\n this.internalLogger.info(msg, obj)\n }\n}\n\nexport function createDefaultLogger(\n name: string,\n level: Levels = 'info',\n): Logger {\n return new Logger(pino({ name, level }))\n}\n","import type { QueueConfig, QueueConnectionsList } from './types.js'\n\nimport { RuntimeException } from '@poppinss/utils'\n\ntype QueueConfigWithConnections<Connections, Connection> = QueueConfig & {\n connection: Connection\n connections: Connections\n}\n\n/**\n * Define config for queue service.\n *\n * @example\n * ```ts\n * // config/queue.ts\n * import { memory } from '@lavoro/memory'\n * import { postgres } from '@lavoro/postgres'\n *\n * const config = {\n * jobs: [...],\n * connection: 'main',\n * connections: {\n * main: {\n * driver: memory(),\n * queues: {\n * default: { concurrency: 1 },\n * emails: { concurrency: 3 },\n * },\n * },\n * background: {\n * driver: postgres({ ... }),\n * queues: {\n * 'heavy-tasks': { concurrency: 2 },\n * reports: { concurrency: 1 },\n * },\n * },\n * },\n * }\n *\n * const queueConfig = defineConfig(config)\n *\n * // Type augmentation to enable type-safe queue names\n * declare module '@lavoro/core' {\n * interface QueueList extends InferQueueNames<typeof config> {}\n * interface DefaultConnection {\n * name: InferDefaultConnection<typeof config>\n * }\n * interface QueueConnections extends InferConnections<typeof config> {}\n * interface ConnectionQueues extends InferConnectionQueues<typeof config> {}\n * }\n * ```\n *\n * After defining your queue names, you'll get autocomplete and type checking:\n * ```ts\n * await queue.listen('emails') // ✓ Valid\n * await queue.listen('heavy-tasks') // ✓ Valid\n * await queue.listen('invalid') // ✗ Type error\n *\n * await job.dispatch(payload).onQueue('emails') // ✓ Valid\n * await job.dispatch(payload).onQueue('typo') // ✗ Type error\n * ```\n */\nexport function defineConfig<\n const Connections extends QueueConnectionsList,\n const Connection extends keyof Connections = keyof Connections,\n>(\n config: QueueConfigWithConnections<Connections, Connection>,\n): QueueConfigWithConnections<Connections, Connection> {\n // Validate required fields\n if (!config.connection) {\n throw new RuntimeException(\n 'Missing \"connection\" property in queue config file',\n )\n }\n\n if (!config.connections) {\n throw new RuntimeException(\n 'Missing \"connections\" property in queue config file',\n )\n }\n\n // Validate default connection exists\n if (!config.connections[config.connection as string]) {\n throw new RuntimeException(\n `Missing \"connections.${String(config.connection)}\". It is referenced by the \"connection\" property`,\n )\n }\n\n // Validate each connection has queues\n Object.keys(config.connections).forEach((connectionName) => {\n const connection =\n config.connections[connectionName as keyof typeof config.connections]\n if (!connection.queues || Object.keys(connection.queues).length === 0) {\n throw new RuntimeException(\n `Connection \"${connectionName}\" must have at least one queue defined`,\n )\n }\n })\n\n return config\n}\n","import { Job, Payload } from './contracts/job.js'\nimport { QueueName, QueueNameForConnection } from './contracts/queue_driver.js'\nimport { DefaultConnection, QueueConnectionName } from './types.js'\n\nexport class PendingDispatch<\n T extends Job,\n P extends Payload<T>,\n C extends QueueConnectionName = DefaultConnection extends { name: infer N }\n ? N extends QueueConnectionName\n ? N\n : QueueConnectionName\n : QueueConnectionName,\n> {\n constructor(\n protected job: T,\n protected payload: P,\n ) {}\n\n public onConnection<NewC extends QueueConnectionName>(\n connection: NewC,\n ): PendingDispatch<T, P, NewC> {\n this.job.options.connection = connection\n\n return this as unknown as PendingDispatch<T, P, NewC>\n }\n\n public onQueue(queue: QueueNameForConnection<C>) {\n this.job.options.queue = queue as QueueName\n\n return this\n }\n\n // public withQueueServiceResolver(\n // queueServiceResolver: () => Promise<Queue>,\n // ): this {\n // this.job.setQueueServiceResolver(queueServiceResolver)\n\n // return this\n // }\n\n protected async execute(): Promise<void> {\n if (!this.job.getQueueServiceResolver) {\n throw new Error(\n 'Queue service resolver is not set.\\nDid you forget to call Job.setDefaultQueueServiceResolver()?',\n )\n }\n\n const queue = await this.job.getQueueServiceResolver()\n await queue.enqueue(this.job, this.payload)\n }\n\n /**\n * By defining the \"then\" method, PendingDispatch becomes \"thenable\",\n * allowing it to trigger automatically when await is called.\n */\n public async then<TResult1 = void, TResult2 = never>(\n onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n // When await is called, actually execte pending chain.\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import { PendingDispatch } from '../pending_dispatch.js'\nimport { Queue } from '../queue.js'\nimport { QueueConnectionName } from '../types.js'\n\nimport { SerializedLock } from '@verrou/core/types'\nimport { randomUUID } from 'node:crypto'\n\nexport type Payload<T extends Job> = T extends Job<infer P> ? P : unknown\n\nexport type PayloadWithLock<T extends Job, P extends Payload<T>> = P & {\n _lock?: SerializedLock\n}\n\nexport type Options = {\n connection?: QueueConnectionName\n queue?: string\n retries: number\n delay: number\n}\n\nexport abstract class Job<P = unknown> {\n private _id: string = randomUUID()\n\n public options: Options = {\n retries: 3,\n delay: 0,\n }\n\n public static compileName(queue: string, name: string): string {\n return `${queue}_${name}`\n }\n\n public static parseName(name: string): { queue: string; name: string } {\n const [q, n] = name.split(/_(.+)/) // split on the first underscore only\n return { queue: q, name: n }\n }\n\n public get connection(): QueueConnectionName | undefined {\n return this.options.connection\n }\n\n public set connection(connection: QueueConnectionName | undefined) {\n this.options.connection = connection\n }\n\n public get queue(): string | undefined {\n return this.options.queue\n }\n\n public set queue(queue: string | undefined) {\n this.options.queue = queue\n }\n\n public get name(): string {\n return this.constructor.name\n }\n\n public get id(): string {\n return this._id\n }\n\n public set id(id: string) {\n this._id = id\n }\n\n public get fullyQualifiedName(): string {\n if (!this.options.queue) {\n throw new Error('Queue is not set.')\n }\n\n return Job.compileName(this.options.queue, this.name)\n }\n\n private static defaultQueueServiceResolver: () => Promise<Queue>\n\n public static setDefaultQueueServiceResolver(\n queueServiceResolver: () => Promise<Queue>,\n ): void {\n this.defaultQueueServiceResolver = queueServiceResolver\n }\n\n public setQueueServiceResolver(\n queueServiceResolver: () => Promise<Queue>,\n ): void {\n this.queueServiceResolver = queueServiceResolver\n }\n\n private queueServiceResolver?: () => Promise<Queue> = undefined\n\n public get getQueueServiceResolver(): () => Promise<Queue> {\n return this.queueServiceResolver || Job.defaultQueueServiceResolver\n }\n\n /**\n * Handle the job with the typed payload.\n */\n public abstract handle(payload: P): Promise<void>\n\n /**\n * Dispatch a job of type with a typed payload.\n */\n public static dispatch<T extends Job, P extends Payload<T>>(\n this: new () => T,\n payload: P,\n ): PendingDispatch<T, P> {\n const job = new this() as T\n return new PendingDispatch<T, P>(job, payload)\n }\n}\n","import { Logger, createDefaultLogger } from '../../logger.js'\nimport { QueueConfig, QueueConnectionName, WorkerOptions } from '../types.js'\nimport { Job, Payload } from './job.js'\n\nimport type { LockFactory } from '@verrou/core'\n\n/**\n * Interface to be augmented by users to define their queue names.\n * This enables type-safe queue names throughout the application.\n */\nexport interface QueueList {}\n\n/**\n * Interface to be augmented by users to map connections to their queue names.\n * This enables connection-specific type-safe queue names.\n */\nexport interface ConnectionQueues {}\n\n/**\n * Extract queue names from QueueList.\n * Defaults to string if no queues are defined.\n */\nexport type QueueName = keyof QueueList extends never ? string : keyof QueueList\n\n/**\n * Extract queue names for a specific connection from ConnectionQueues.\n */\nexport type QueueNameForConnection<C extends QueueConnectionName> =\n C extends keyof ConnectionQueues ? ConnectionQueues[C] : QueueName\n\nexport type QueueDriverStopOptions = {\n /**\n * Whether to wait for the jobs to finish processing before stopping.\n *\n * Default: true\n */\n graceful?: boolean\n\n /**\n * The timeout in milliseconds to wait for the jobs to finish processing.\n *\n * Default: 30000 (30 seconds)\n */\n timeout?: number\n}\n\nexport type QueueDriverConfig = {}\n\nexport abstract class QueueDriver<\n Config extends QueueDriverConfig = QueueDriverConfig,\n> {\n protected logger: Logger\n\n protected registeredQueues: Set<string> = new Set()\n\n protected registeredJobs: Map<string, new () => Job> = new Map()\n\n public connection: QueueConnectionName | undefined\n\n constructor(\n protected config: QueueConfig,\n protected options: Record<string, WorkerOptions>,\n protected driverConfig: Config = {} as Config,\n ) {\n this.logger = createDefaultLogger('queue')\n }\n\n public setLogger(logger: Logger): void {\n this.logger = logger\n }\n\n protected getMergedWorkerOptions(\n queue: QueueName,\n options?: WorkerOptions,\n ): WorkerOptions {\n const base = this.options[queue] || {}\n return { ...base, ...(options || {}) }\n }\n\n public async listen(\n queue: QueueName,\n options?: WorkerOptions,\n ): Promise<void> {\n if (this.registeredQueues.has(queue as string)) {\n throw new Error(`Queue '${queue as string}' already registered`)\n }\n\n this.registeredQueues.add(queue as string)\n\n const workerOptions = this.getMergedWorkerOptions(queue, options)\n\n this.logger.trace(\n { connection: this.connection, queue, options: workerOptions },\n 'Listening queue',\n )\n }\n\n public async register(job: new () => Job): Promise<void> {\n if (this.registeredJobs.has(job.name)) {\n return\n }\n\n this.registeredJobs.set(job.name, job)\n\n this.logger.trace(\n { connection: this.connection, job: job.name },\n 'Registered job',\n )\n }\n\n public async unregister(job: new () => Job): Promise<void> {\n if (!this.registeredJobs.has(job.name)) {\n return\n }\n\n this.registeredJobs.delete(job.name)\n\n this.logger.trace(\n { connection: this.connection, job: job.name },\n 'Unregistered job',\n )\n }\n\n public async start(): Promise<void> {\n for (const [queue, options] of Object.entries(this.options)) {\n await this.listen(queue as QueueName, options)\n }\n }\n\n public async stop(_options?: QueueDriverStopOptions): Promise<void> {\n this.registeredQueues.clear()\n this.registeredJobs.clear()\n }\n\n protected checkIfQueueIsRegistered(queue: string): void {\n if (!this.registeredQueues.has(queue)) {\n throw new Error(`Queue '${queue}' is not registered.`)\n }\n }\n\n protected checkIfJobIsRegistered(job: string): void {\n if (!this.registeredJobs.has(job)) {\n throw new Error(`Job '${job}' is not registered.`)\n }\n }\n\n public getDefaultQueue(): QueueName {\n if (this.registeredQueues.size === 0) {\n throw new Error(\n `No queues registered for connection: ${this.connection}.`,\n )\n }\n\n return this.registeredQueues.values().next().value as QueueName\n }\n\n /**\n * Parent method to be extended by the driver.\n * It implements checks for the job and queue being registered.\n */\n public async enqueue<T extends Job, P extends Payload<T>>(\n job: T,\n // @ts-ignore\n payload: P,\n ): Promise<void> {\n if (!job.options.queue) {\n job.options.queue = this.getDefaultQueue()\n }\n\n this.checkIfQueueIsRegistered(job.options.queue)\n this.checkIfJobIsRegistered(job.name)\n\n return Promise.resolve()\n }\n\n /**\n * Create a lock factory instance for this driver.\n *\n * Each driver implementation should return a\n * [Verrou LockFactory instance](https://verrou.dev/docs/quick-setup#lockfactory-api)\n * that matches the driver's backing store.\n *\n * @returns A LockFactory instance\n */\n public abstract createLockProvider(): LockFactory\n\n /**\n * Clean up resources associated with a lock factory created by this driver.\n * This is called when the queue is stopped to ensure proper resource cleanup.\n *\n * @param lockFactory - The lock factory instance to clean up\n */\n public async destroyLockProvider(_lockFactory: LockFactory): Promise<void> {\n // Default implementation does nothing\n // Drivers that need cleanup (e.g., Postgres with Knex) should override this\n }\n}\n","export type ScheduleInterval =\n | 'second'\n | 'two seconds'\n | 'three seconds'\n | 'four seconds'\n | 'five seconds'\n | 'ten seconds'\n | 'fifteen seconds'\n | 'twenty seconds'\n | 'thirty seconds'\n | 'minute'\n | 'two minutes'\n | 'three minutes'\n | 'four minutes'\n | 'five minutes'\n | 'ten minutes'\n | 'fifteen minutes'\n | 'twenty minutes'\n | 'thirty minutes'\n | 'hour'\n | 'two hours'\n | 'three hours'\n | 'four hours'\n | 'five hours'\n | 'six hours'\n | 'seven hours'\n | 'eight hours'\n | 'nine hours'\n | 'ten hours'\n | 'eleven hours'\n | 'twelve hours'\n | 'day'\n | 'week'\n | 'sunday'\n | 'monday'\n | 'tuesday'\n | 'wednesday'\n | 'thursday'\n | 'friday'\n | 'saturday'\n | 'month'\n | 'last day of month'\n// | 'quarter'\n// | 'year'\n\nexport type ScheduleIntervalDayOfWeek =\n | 'sunday'\n | 'monday'\n | 'tuesday'\n | 'wednesday'\n | 'thursday'\n | 'friday'\n | 'saturday'\n\ntype Hour = `${0 | 1 | 2}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`\ntype Minute = `${0 | 1 | 2 | 3 | 4 | 5}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`\n\nexport type ScheduleIntervalTime = `${Hour}:${Minute}`\n\nexport function parseTime(time: ScheduleIntervalTime): [number, number] {\n const [hour, minute] = time.split(':')\n\n if (!hour || !minute || hour.length !== 2 || minute.length !== 2) {\n throw new Error('Invalid time format')\n }\n\n return [Number(hour), Number(minute)]\n}\n\nexport interface IntervalCronOptions {\n /** Minute to run. Default: 0 */\n minute?: number\n /** Hour to run. Default: 0 */\n hour?: number\n /** Day of month to run. Default: 1 */\n dayOfMonth?: number\n /** Day of week to run. Default: 0 */\n dayOfWeek?: ScheduleIntervalDayOfWeek\n}\n\nexport function dayOfWeekToNumber(\n dayOfWeek: ScheduleIntervalDayOfWeek,\n): number {\n switch (dayOfWeek) {\n case 'sunday':\n return 0\n case 'monday':\n return 1\n case 'tuesday':\n return 2\n case 'wednesday':\n return 3\n case 'thursday':\n return 4\n case 'friday':\n return 5\n case 'saturday':\n return 6\n }\n}\n\n/**\n * Converts a ScheduleInterval to a cron pattern string.\n * For longer intervals (hour, day, week, etc.), you can customize when they run.\n *\n * @param interval - The schedule interval\n * @param options - Options to customize the cron pattern\n * @returns A cron pattern string\n *\n * @example\n * intervalToCron('minute') // '* * * * *' - every minute\n * intervalToCron('hour', { minute: 30 }) // '30 * * * *' - every hour at :30\n * intervalToCron('day', { hour: 14, minute: 30 }) // '30 14 * * *' - daily at 2:30 PM\n * intervalToCron('week', { dayOfWeek: 1, hour: 9 }) // '0 9 * * 1' - Mondays at 9 AM\n */\nexport function intervalToCron(\n interval: ScheduleInterval,\n options: IntervalCronOptions = {},\n): string {\n const { minute = 0, hour = 0, dayOfMonth = 1, dayOfWeek } = options\n\n const dayOfWeekNumber = dayOfWeekToNumber(dayOfWeek ?? 'sunday')\n\n const patterns: Record<ScheduleInterval, string> = {\n second: '* * * * * *',\n 'two seconds': '*/2 * * * * *',\n 'three seconds': '*/3 * * * * *',\n 'four seconds': '*/4 * * * * *',\n 'five seconds': '*/5 * * * * *',\n 'ten seconds': '*/10 * * * * *',\n 'fifteen seconds': '*/15 * * * * *',\n 'twenty seconds': '*/20 * * * * *',\n 'thirty seconds': '*/30 * * * * *',\n\n minute: '* * * * *',\n 'two minutes': '*/2 * * * *',\n 'three minutes': '*/3 * * * *',\n 'four minutes': '*/4 * * * *',\n 'five minutes': '*/5 * * * *',\n 'ten minutes': '*/10 * * * *',\n 'fifteen minutes': '*/15 * * * *',\n 'twenty minutes': '*/20 * * * *',\n 'thirty minutes': '*/30 * * * *',\n\n hour: `${minute} * * * *`,\n 'two hours': `${minute} */2 * * *`,\n 'three hours': `${minute} */3 * * *`,\n 'four hours': `${minute} */4 * * *`,\n 'five hours': `${minute} */5 * * *`,\n 'six hours': `${minute} */6 * * *`,\n 'seven hours': `${minute} */7 * * *`,\n 'eight hours': `${minute} */8 * * *`,\n 'nine hours': `${minute} */9 * * *`,\n 'ten hours': `${minute} */10 * * *`,\n 'eleven hours': `${minute} */11 * * *`,\n 'twelve hours': `${minute} */12 * * *`,\n\n day: `${minute} ${hour} * * *`,\n\n week: `${minute} ${hour} * * ${dayOfWeekNumber}`,\n sunday: `${minute} ${hour} * * 0`,\n monday: `${minute} ${hour} * * 1`,\n tuesday: `${minute} ${hour} * * 2`,\n wednesday: `${minute} ${hour} * * 3`,\n thursday: `${minute} ${hour} * * 4`,\n friday: `${minute} ${hour} * * 5`,\n saturday: `${minute} ${hour} * * 6`,\n\n month: `${minute} ${hour} ${dayOfWeek ? '*' : dayOfMonth} * ${dayOfWeek ? dayOfWeekNumber + '#1' : '*'}`,\n 'last day of month': `${minute} ${hour} L * *`,\n\n // quarter: `${minute} ${hour} ${dayOfWeek ? '*' : dayOfMonth} */3 ${dayOfWeek ? dayOfWeekNumber : '*'}`,\n // year: `${minute} ${hour} ${dayOfMonth} 1 ${dayOfWeek ? dayOfWeekNumber : '*'}`,\n }\n\n return patterns[interval]\n}\n","import { Cron } from 'croner'\n\n/**\n * Cron instance registry for internal use\n */\nexport class ScheduleRegistry {\n private static instances: Record<string, Cron> = {}\n\n public static add(name: string, cron: Cron): void {\n if (this.instances[name]) {\n throw new Error(`Cron instance with name '${name}' already exists`)\n }\n\n this.instances[name] = cron\n }\n\n public static all(): Record<string, Cron> {\n return this.instances\n }\n\n public static get(name: string): Cron | undefined {\n return this.instances[name]\n }\n\n public static clear(name?: string): void {\n if (name) {\n this.instances[name]?.stop()\n delete this.instances[name]\n } else {\n Object.values(this.instances).forEach((cron) => cron.stop())\n this.instances = {}\n }\n }\n}\n","import { MaybePromise } from '../types.js'\nimport {\n IntervalCronOptions,\n ScheduleInterval,\n ScheduleIntervalDayOfWeek,\n ScheduleIntervalTime,\n intervalToCron,\n parseTime,\n} from './schedule_interval.js'\nimport { ScheduleRegistry } from './schedule_registry.js'\n\nimport type { LockFactory } from '@verrou/core'\nimport type { SerializedLock } from '@verrou/core/types'\nimport { Duration } from '@verrou/core/types'\nimport { Cron } from 'croner'\nimport { createHash } from 'node:crypto'\n\nexport const getDistributedLockKey = (name: string) => {\n const hash = createHash('sha1').update(name).digest('hex').slice(0, 8)\n return `lavoro:schedule:${hash}`\n}\n\nexport class PendingSchedule {\n protected cronPattern?: string\n\n protected interval: ScheduleInterval = 'day'\n protected intervalOptions?: IntervalCronOptions\n\n protected distributedLockOptions: {\n /**\n * The lock key is based on the task name to ensure only one instance runs\n *\n * @param name - The task name\n * @returns The lock key\n */\n key: (name: string) => string\n /**\n * The lock TTL is the duration for which the lock is held\n */\n ttl: Duration\n /**\n * Whether to allow overlapping executions of the same task\n */\n overlap: boolean\n /**\n * Whether to hand off the lock to the queue worker\n */\n handOff: boolean\n } = {\n key: getDistributedLockKey,\n ttl: '1m', // TODO: Decide on a default TTL\n overlap: false,\n handOff: false,\n }\n\n constructor(\n protected name: string,\n protected cb: (serializedLock?: SerializedLock) => MaybePromise<void>,\n protected lockProviderResolver: () => LockFactory,\n ) {}\n\n /**\n * Schedule using a cron pattern.\n * You can use https://crontab.guru to generate a cron pattern.\n *\n * @param pattern - Cron pattern string\n *\n * @example\n * Schedule.call('my-task', () => {}).cron('0 0 * * *') // Daily at midnight\n * Schedule.call('my-task', () => {}).cron('*\\/5 * * * *') // Every 5 minutes\n * Schedule.call('my-task', () => {}).cron('0 *\\/2 * * *') // Every 2 hours\n */\n public cron(pattern: string): this {\n this.cronPattern = pattern\n return this\n }\n\n /**\n * Schedule to run at a specific interval.\n * For longer intervals (hour, day, week, etc.), you can customize when they run.\n *\n * @param interval - The schedule interval\n * @param options - Options to customize the cron pattern\n *\n * @example\n * Schedule.call('my-task', () => {}).every('minute')\n * Schedule.call('my-task', () => {}).every('hour', { minute: 30 })\n * Schedule.call('my-task', () => {}).every('day', { hour: 14, minute: 30 })\n * Schedule.call('my-task', () => {}).every('week', { dayOfWeek: 1, hour: 9 })\n */\n public every(\n interval: ScheduleInterval,\n options?: IntervalCronOptions,\n ): this {\n this.interval = interval\n this.intervalOptions = options\n this.cronPattern = intervalToCron(interval, options)\n return this\n }\n\n public on(dayOfWeek: ScheduleIntervalDayOfWeek): this {\n // .on() only makes sense for intervals larger than 'day'\n const valid: ScheduleInterval[] = [\n //\n 'week',\n 'month',\n ]\n\n if (!valid.includes(this.interval)) {\n throw new Error(\n `.on() can only be used for weekly intervals or larger. Current interval: '${this.interval}'`,\n )\n }\n\n this.intervalOptions = { ...(this.intervalOptions ?? {}), dayOfWeek }\n this.cronPattern = intervalToCron(this.interval, this.intervalOptions)\n return this\n }\n\n public at(time: ScheduleIntervalTime): this {\n // .at() only makes sense for intervals larger than 'hour'\n const valid: ScheduleInterval[] = [\n 'day',\n 'week',\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n 'month',\n 'last day of month',\n ]\n\n if (!valid.includes(this.interval)) {\n throw new Error(\n `.at() can only be used for daily intervals or larger. Current interval: '${this.interval}'`,\n )\n }\n\n const [hour, minute] = parseTime(time)\n this.intervalOptions = { ...(this.intervalOptions ?? {}), hour, minute }\n this.cronPattern = intervalToCron(this.interval, this.intervalOptions)\n return this\n }\n\n /**\n * Set the duration during which other instances\n * of the same task will be prevented from running.\n *\n * This should be roughly the duration of the task execution or longer\n * since the lock will be released automatically after the task execution.\n *\n * @param ttl - The lock duration\n *\n * @example\n * Schedule.call('my-task', () => {}).lockFor('10s')\n */\n public lockFor(ttl: Duration): this {\n this.distributedLockOptions.ttl = ttl\n return this\n }\n\n /**\n * Allow overlapping executions of the same task.\n *\n * @example\n * Schedule.call('my-task', () => {}).overlapping()\n */\n public overlapping(): this {\n this.distributedLockOptions.overlap = true\n return this\n }\n\n protected async execute(): Promise<void> {\n if (!this.cronPattern) {\n throw new Error(\n 'No schedule pattern defined. To schedule a task, set interval explicitly.',\n )\n }\n\n ScheduleRegistry.add(\n this.name,\n new Cron(this.cronPattern, async () => {\n // If overlapping is allowed, we can call the callback right away.\n if (this.distributedLockOptions.overlap) {\n await this.cb()\n return\n }\n\n // First, we create a distributed lock based on the task name,\n // which ensures that only one instance of the task runs at a time.\n const key = this.distributedLockOptions.key(this.name)\n const ttl = this.distributedLockOptions.ttl\n\n // Resolve lock provider instance and create the lock.\n const lockProvider = this.lockProviderResolver()\n const lock = lockProvider.createLock(key, ttl)\n\n // If the lock is already locked, we skip the execution.\n // if (await lock.isLocked()) {\n // return\n // }\n\n const acquired = await lock.acquireImmediately()\n\n if (!acquired) {\n return\n }\n\n // If the lock was acquired, we run the task,\n // otherwise just skip it and do nothing.\n try {\n // If the lock is marked to be handed off to the queue\n // worker, we serialize and pass it to the callback.\n // Otherwise we just run the task and then release\n // the lock immediately.\n if (this.distributedLockOptions.handOff) {\n await this.cb(lock.serialize())\n // await lock.release()\n } else {\n await this.cb()\n await lock.forceRelease()\n }\n } catch (error) {\n // If the task callback failed, we release\n // the lock and re-throw the error.\n await lock.forceRelease()\n throw error\n } finally {\n // await lock.release()\n }\n }),\n )\n }\n\n /**\n * By defining the \"then\" method, PendingDispatch becomes \"thenable\",\n * allowing it to trigger automatically when await is called.\n */\n public async then<TResult1 = void, TResult2 = never>(\n onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n // When await is called, actually execte pending chain.\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import { Job, Payload, PayloadWithLock } from '../queue/contracts/job.js'\nimport { QueueNameForConnection } from '../queue/contracts/queue_driver.js'\nimport { PendingDispatch } from '../queue/pending_dispatch.js'\nimport { DefaultConnection, QueueConnectionName } from '../queue/types.js'\nimport { PendingSchedule } from './pending_schedule.js'\n\nimport type { LockFactory } from '@verrou/core'\nimport type { SerializedLock } from '@verrou/core/types'\n\nexport class PendingJobSchedule<\n T extends Job,\n P extends Payload<T>,\n C extends QueueConnectionName = DefaultConnection extends { name: infer N }\n ? N extends QueueConnectionName\n ? N\n : QueueConnectionName\n : QueueConnectionName,\n> extends PendingSchedule {\n /**\n * Use composition pattern and store pending\n * job dispatch inside a pending schedule.\n */\n protected dispatch: PendingDispatch<T, PayloadWithLock<T, P>, C>\n\n private _connection?: string\n private _queue?: string\n\n private get jobName(): string {\n return `${this._connection}_${this._queue}_${this.job.name}`\n }\n\n constructor(\n protected job: T,\n protected payload: PayloadWithLock<T, P>,\n protected lockProviderResolver: () => LockFactory,\n ) {\n super(\n job.name,\n async (serializedLock?: SerializedLock) => {\n // Pass the serialized lock to the payload.\n if (serializedLock) {\n this.payload._lock = serializedLock\n }\n\n await this.dispatch.then(undefined, (error) => {\n throw error\n })\n },\n lockProviderResolver,\n )\n\n this.job = job\n this.payload = payload\n this.dispatch = new PendingDispatch(job, payload)\n\n this.distributedLockOptions.handOff = true\n }\n\n public onConnection<NewC extends QueueConnectionName>(\n connection: NewC,\n ): PendingJobSchedule<T, P, NewC> {\n this._connection = connection\n this.dispatch.onConnection(connection)\n // TypeScript can't track type parameter changes through `this`.\n // The instance is the same, but the type parameter C changes to NewC,\n // so we need to assert the type to the new type.\n return this as unknown as PendingJobSchedule<T, P, NewC>\n }\n\n public onQueue(queue: QueueNameForConnection<C>): this {\n this._queue = queue\n this.dispatch.onQueue(queue)\n\n return this\n }\n\n // public withQueueServiceResolver(queueServiceResolver: () => Promise<Queue>) {\n // this.dispatch.withQueueServiceResolver(queueServiceResolver)\n // return this\n // }\n\n protected async execute(): Promise<void> {\n // Before executing, update the lock provider resolver to use the one from the queue connection\n const queueServiceResolver = this.job.getQueueServiceResolver\n if (queueServiceResolver) {\n const queue = await queueServiceResolver()\n\n // Get the connection the job will use\n const connection =\n this.job.options.connection ?? queue.getDefaultConnection()\n\n // Override the lock service resolver to use the connection's lock provider\n const connectionLockService = queue.getLockProvider(connection)\n\n if (connectionLockService) {\n this.lockProviderResolver = () => connectionLockService\n }\n\n // Register this scheduled job with the queue service\n if (this.job.id) {\n queue.registerScheduledJob(this.job.id)\n }\n\n this._connection = this._connection ?? queue.getDefaultConnection()\n this._queue = this._queue ?? queue.getDefaultQueue()\n }\n\n this.name = this.jobName\n\n await super.execute()\n }\n}\n","import { Job, Payload, PayloadWithLock } from '../queue/contracts/job.js'\nimport { MaybePromise } from '../types.js'\nimport { PendingJobSchedule } from './pending_job_schedule.js'\nimport { PendingSchedule } from './pending_schedule.js'\nimport { ScheduleRegistry } from './schedule_registry.js'\n\nimport { LockFactory } from '@verrou/core'\nimport { memoryStore } from '@verrou/core/drivers/memory'\n\n/**\n * Default lock provider instance for distributed locking.\n * Uses memory store by default.\n */\nexport const defaultScheduleLockProvider = new LockFactory(\n memoryStore().factory(),\n)\n\nexport class Schedule {\n /**\n * Default lock provider resolver for distributed locking.\n * Returns the default memory-based instance if not overridden.\n */\n private static defaultLockProviderResolver: () => LockFactory = () =>\n defaultScheduleLockProvider\n\n /**\n * Set a custom lock provider resolver for distributed locking.\n *\n * This allows dynamic resolution of lock provider instances, useful when\n * integrating with Queue or other services that manage lock instances.\n *\n * @param resolver - Function that returns a LockFactory instance\n *\n * @example\n * import { LockFactory } from '@verrou/core'\n * import { redisStore } from '@verrou/core/drivers/redis'\n *\n * const customLockProvider = new LockFactory(\n * redisStore({ connection: redisClient })\n * )\n *\n * Schedule.setLockProviderResolver(() => customLockProvider)\n */\n public static setLockProviderResolver(resolver: () => LockFactory): void {\n this.defaultLockProviderResolver = resolver\n }\n\n /**\n * Get the current lock provider instance from the resolver.\n * @internal\n */\n public static getLockProvider(): LockFactory {\n return this.defaultLockProviderResolver()\n }\n\n /**\n * Clear all scheduled tasks (or specific one if name is specified).\n *\n * @param name - The name of the task to clear\n *\n * @example\n * Schedule.clear() // Clear all tasks\n * Schedule.clear('my-task') // Clear specific task\n */\n public static clear(name?: string): void {\n if (name) {\n ScheduleRegistry.clear(name)\n } else {\n Object.keys(ScheduleRegistry.all()).forEach((name) =>\n ScheduleRegistry.clear(name),\n )\n }\n }\n\n /**\n * Schedule a callback to run at specified intervals.\n *\n * @param name - Unique identifier for the task (required for distributed systems)\n * @param cb - The callback function to execute\n *\n * @example\n * // Using cron pattern\n * const cleanupUsers = async () => { ... }\n * Schedule.call('cleanup-users', cleanupUsers).cron('0 0 * * *')\n *\n * // Using convenience methods\n * Schedule.call('hourly-task', () => { ... }).hourly()\n * Schedule.call('daily-task', () => { ... }).daily()\n */\n public static call(name: string, cb: () => MaybePromise<void>) {\n return new PendingSchedule(name, cb, this.defaultLockProviderResolver)\n }\n\n /**\n * Schedule a job to run at specified intervals.\n *\n * You can specify the connection and queue to use for the job\n * the same way you would dispatch a job.\n *\n * @param job - The job class to schedule\n * @param payload - The payload to pass to the job\n *\n * @example\n * Schedule.job(TestJob, { arg1: 'hello', arg2: 1 }).every('minute')\n * Schedule.job(TestJob, { arg1: 'hello', arg2: 1 })\n * .onConnection('main')\n * .onQueue('default')\n * .every('minute')\n */\n public static job<T extends Job, P extends Payload<T>>(\n job: new () => T,\n payload: P,\n ): PendingJobSchedule<T, P> {\n return new PendingJobSchedule<T, P>(\n new job(),\n payload as PayloadWithLock<T, P>,\n this.defaultLockProviderResolver,\n )\n }\n}\n","import { Logger, createDefaultLogger } from '../logger.js'\nimport { Schedule } from '../schedule/schedule.js'\nimport { Job, Payload } from './contracts/job.js'\nimport {\n QueueDriver,\n QueueDriverStopOptions,\n QueueName,\n} from './contracts/queue_driver.js'\nimport type {\n QueueConfig,\n QueueConnectionConfig,\n QueueConnectionName,\n} from './types.js'\n\nimport type { LockFactory } from '@verrou/core'\n\nexport class Queue {\n private drivers: Map<QueueConnectionName, QueueDriver> = new Map()\n private started: boolean = false\n private logger: Logger\n private scheduledJobs: Set<string> = new Set()\n private lockFactories: Map<QueueConnectionName, LockFactory> = new Map()\n\n constructor(private config: QueueConfig & { logger?: Logger }) {\n this.logger = config?.logger || createDefaultLogger('queue')\n\n for (const [connection, driverConfig] of Object.entries(\n this.config.connections,\n )) {\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Creating queue driver',\n )\n\n const driver = this.createDriver(driverConfig)\n driver.connection = connection as QueueConnectionName\n\n this.drivers.set(driver.connection, driver)\n }\n }\n\n private createDriver(\n config: QueueConnectionConfig<QueueDriver>,\n ): QueueDriver {\n const { constructor: driverConstructor, config: driverConfig } =\n config.driver\n\n const driver = new driverConstructor(\n this.config,\n config.queues,\n driverConfig,\n )\n\n driver.setLogger(this.logger)\n\n return driver\n }\n\n async start() {\n if (this.started) {\n this.logger.warn('Queue service already started')\n return\n }\n\n /**\n * Create lock factories for each connection.\n * If no lock provider was explicitly configured,\n * create a driver-specific lock factory.\n */\n for (const [connection, driverConfig] of Object.entries(\n this.config.connections,\n )) {\n const driver = this.drivers.get(connection as QueueConnectionName)\n if (!driver) {\n throw new Error(`Driver not found for connection: ${connection}.`)\n }\n\n /**\n * Create driver-specific lock factory if\n * no lock provider was explicitly configured.\n */\n if (!driverConfig.lockProvider) {\n const lockFactory = driver.createLockProvider()\n if (lockFactory) {\n this.lockFactories.set(connection as QueueConnectionName, lockFactory)\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Created lock factory for driver',\n )\n }\n } else {\n this.lockFactories.set(\n connection as QueueConnectionName,\n driverConfig.lockProvider,\n )\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Using explicitly configured lock provider for driver',\n )\n }\n }\n\n for (const job of this.config.jobs) {\n await this.register(job)\n }\n\n this.started = true\n\n for (const [connection, driver] of this.drivers) {\n this.logger.trace({ connection }, 'Starting queue connection')\n await driver.start()\n }\n\n this.logger.trace(\n { connections: Array.from(this.drivers.keys()) },\n 'Queue service started',\n )\n }\n\n async stop(\n options: QueueDriverStopOptions = {\n graceful: true,\n timeout: 30000,\n },\n ) {\n if (!this.started) {\n this.logger.warn('Queue service not started')\n return\n }\n\n this.logger.trace(\n { count: this.scheduledJobs.size },\n 'Cleared scheduled jobs for the queue',\n )\n\n // Clear all scheduled jobs registered with this Queue instance\n for (const id of this.scheduledJobs) {\n Schedule.clear(id)\n }\n\n this.scheduledJobs.clear()\n\n for (const [connection, driver] of this.drivers) {\n this.logger.trace({ connection }, 'Stopping queue connection')\n await driver.stop(options)\n }\n\n for (const job of this.config.jobs) {\n await this.unregister(job)\n }\n\n /**\n * Destroy lock factories for each connection.\n */\n for (const [connection, lockFactory] of this.lockFactories) {\n const driver = this.drivers.get(connection as QueueConnectionName)\n if (!driver) {\n throw new Error(`Driver not found for connection: ${connection}.`)\n }\n\n await driver.destroyLockProvider(lockFactory)\n this.lockFactories.delete(connection as QueueConnectionName)\n }\n\n this.started = false\n\n this.logger.trace('Queue service stopped')\n }\n\n protected async register(job: new () => Job): Promise<void> {\n for (const [_, driver] of this.drivers) {\n await driver.register(job)\n }\n }\n\n protected async unregister(job: new () => Job): Promise<void> {\n for (const [_, driver] of this.drivers) {\n await driver.unregister(job)\n }\n }\n\n public async enqueue<T extends Job, P extends Payload<T>>(\n job: T,\n payload: P,\n ): Promise<void> {\n if (this.drivers.size === 0) {\n throw new Error('No queue drivers available.')\n }\n\n /**\n * Try to get driver from connection if specified\n * otherwise use the first available driver.\n */\n const driver: QueueDriver | undefined = job.options.connection\n ? this.drivers.get(job.options.connection)\n : this.drivers.values().next().value\n\n if (!driver) {\n throw new Error(\n `No driver found for connection: ${job.options.connection}.`,\n )\n }\n\n if (!job.options.queue) {\n job.options.queue = driver.getDefaultQueue()\n }\n\n await driver.enqueue(job, payload)\n }\n\n /**\n * Get the default connection name.\n *\n * Returns the name of the first available connection.\n * Throws an error if no connections are available.\n */\n public getDefaultConnection(): QueueConnectionName {\n if (this.drivers.size === 0) {\n throw new Error(`No queue drivers available.`)\n }\n\n return this.drivers.keys().next().value as QueueConnectionName\n }\n\n public getDefaultQueue(): QueueName {\n if (this.drivers.size === 0) {\n throw new Error(`No queue drivers available.`)\n }\n\n return this.drivers.values().next().value?.getDefaultQueue() as QueueName\n }\n\n /**\n * Get the lock factory instance for a specific connection.\n * Returns the explicitly configured lock provider or the auto-created lock factory.\n * Throws an error if no lock factory is available.\n *\n * @param connection - The connection name\n * @returns The lock factory instance\n */\n public getLockProvider(connection: QueueConnectionName): LockFactory {\n const connectionConfig = this.config.connections[connection]\n\n // Return explicitly configured lock provider if available\n if (connectionConfig?.lockProvider) {\n return connectionConfig.lockProvider\n }\n\n // Return auto-created lock factory\n const lockFactory = this.lockFactories.get(connection)\n\n if (!lockFactory) {\n throw new Error(`No lock provider found for connection: ${connection}.`)\n }\n\n return lockFactory\n }\n\n /**\n * Register a scheduled job ID with this Queue instance.\n * This allows the Queue to clean up scheduled jobs when it stops.\n */\n public registerScheduledJob(id: string): void {\n this.scheduledJobs.add(id)\n }\n}\n"],"mappings":"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","names":["name"]}
1
+ {"version":3,"sources":["../src/logger.ts","../src/queue/define_config.ts","../src/queue/pending_dispatch.ts","../src/queue/contracts/job.ts","../src/queue/contracts/queue_driver_event_emitter.ts","../src/queue/contracts/queue_driver.ts","../src/schedule/schedule_interval.ts","../src/schedule/schedule_registry.ts","../src/schedule/pending_schedule.ts","../src/schedule/pending_job_schedule.ts","../src/schedule/schedule.ts","../src/queue/queue_event_emitter.ts","../src/queue/queue.ts"],"sourcesContent":["import type {\n Logger as InternalLogger,\n Levels,\n LogObject,\n} from '@julr/utils/logger'\n\n/**\n * No-op logger implementation used as fallback when pino is not installed\n */\nclass NoOpLogger implements InternalLogger {\n level: Levels = 'trace'\n\n child(_obj: LogObject): InternalLogger {\n return this\n }\n\n trace(_msg: any, _obj?: any): void {}\n debug(_msg: any, _obj?: any): void {}\n info(_msg: any, _obj?: any): void {}\n warn(_msg: any, _obj?: any): void {}\n error(_msg: any, _obj?: any): void {}\n fatal(_msg: any, _obj?: any): void {}\n}\n\nexport class Logger {\n private internalLogger: InternalLogger\n\n constructor(internalLogger: InternalLogger) {\n this.internalLogger = internalLogger\n }\n\n child(obj: LogObject) {\n return new Logger(this.internalLogger.child(obj))\n }\n\n trace(msg: any, obj?: any) {\n this.internalLogger.trace(msg, obj)\n }\n\n debug(msg: any, obj?: any) {\n this.internalLogger.debug(msg, obj)\n }\n\n warn(msg: any, obj?: any) {\n this.internalLogger.warn(msg, obj)\n }\n\n error(msg: any, obj?: any) {\n this.internalLogger.error(msg, obj)\n }\n\n fatal(msg: any, obj?: any) {\n this.internalLogger.fatal(msg, obj)\n }\n\n info(msg: any, obj?: any) {\n this.internalLogger.info(msg, obj)\n }\n}\n\nexport function createDefaultLogger(\n name: string,\n level: Levels = 'info',\n): Logger {\n try {\n // Try to require pino synchronously\n const pino = require('pino')\n return new Logger(pino({ name, level }))\n } catch {\n // Fallback to no-op logger if pino is not installed\n return new Logger(new NoOpLogger())\n }\n}\n","import type { QueueConfig, QueueConnectionsList } from './types.js'\n\nimport { RuntimeException } from '@poppinss/utils'\n\ntype QueueConfigWithConnections<Connections, Connection> = QueueConfig & {\n connection: Connection\n connections: Connections\n}\n\n/**\n * Define config for queue service.\n *\n * @example\n * ```ts\n * // config/queue.ts\n * import { memory } from '@lavoro/memory'\n * import { postgres } from '@lavoro/postgres'\n *\n * const config = {\n * jobs: [...],\n * connection: 'main',\n * connections: {\n * main: {\n * driver: memory(),\n * queues: {\n * default: { concurrency: 1 },\n * emails: { concurrency: 3 },\n * },\n * },\n * background: {\n * driver: postgres({ ... }),\n * queues: {\n * 'heavy-tasks': { concurrency: 2 },\n * reports: { concurrency: 1 },\n * },\n * },\n * },\n * }\n *\n * const queueConfig = defineConfig(config)\n *\n * // Type augmentation to enable type-safe queue names\n * declare module '@lavoro/core' {\n * interface QueueList extends InferQueueNames<typeof config> {}\n * interface DefaultConnection {\n * name: InferDefaultConnection<typeof config>\n * }\n * interface QueueConnections extends InferConnections<typeof config> {}\n * interface ConnectionQueues extends InferConnectionQueues<typeof config> {}\n * }\n * ```\n *\n * After defining your queue names, you'll get autocomplete and type checking:\n * ```ts\n * await queue.listen('emails') // ✓ Valid\n * await queue.listen('heavy-tasks') // ✓ Valid\n * await queue.listen('invalid') // ✗ Type error\n *\n * await job.dispatch(payload).onQueue('emails') // ✓ Valid\n * await job.dispatch(payload).onQueue('typo') // ✗ Type error\n * ```\n */\nexport function defineConfig<\n const Connections extends QueueConnectionsList,\n const Connection extends keyof Connections = keyof Connections,\n>(\n config: QueueConfigWithConnections<Connections, Connection>,\n): QueueConfigWithConnections<Connections, Connection> {\n // Validate required fields\n if (!config.connection) {\n throw new RuntimeException(\n 'Missing \"connection\" property in queue config file',\n )\n }\n\n if (!config.connections) {\n throw new RuntimeException(\n 'Missing \"connections\" property in queue config file',\n )\n }\n\n // Validate default connection exists\n if (!config.connections[config.connection as string]) {\n throw new RuntimeException(\n `Missing \"connections.${String(config.connection)}\". It is referenced by the \"connection\" property`,\n )\n }\n\n // Validate each connection has queues\n Object.keys(config.connections).forEach((connectionName) => {\n const connection =\n config.connections[connectionName as keyof typeof config.connections]\n if (!connection.queues || Object.keys(connection.queues).length === 0) {\n throw new RuntimeException(\n `Connection \"${connectionName}\" must have at least one queue defined`,\n )\n }\n })\n\n return config\n}\n","import { Job, Payload } from './contracts/job.js'\nimport { QueueName, QueueNameForConnection } from './contracts/queue_driver.js'\nimport { DefaultConnection, QueueConnectionName } from './types.js'\n\nexport class PendingDispatch<\n T extends Job,\n P extends Payload<T>,\n C extends QueueConnectionName = DefaultConnection extends { name: infer N }\n ? N extends QueueConnectionName\n ? N\n : QueueConnectionName\n : QueueConnectionName,\n> {\n constructor(\n protected job: T,\n protected payload: P,\n ) {}\n\n public onConnection<NewC extends QueueConnectionName>(\n connection: NewC,\n ): PendingDispatch<T, P, NewC> {\n this.job.options.connection = connection\n\n return this as unknown as PendingDispatch<T, P, NewC>\n }\n\n public onQueue(queue: QueueNameForConnection<C>) {\n this.job.options.queue = queue as QueueName\n\n return this\n }\n\n // public withQueueServiceResolver(\n // queueServiceResolver: () => Promise<Queue>,\n // ): this {\n // this.job.setQueueServiceResolver(queueServiceResolver)\n\n // return this\n // }\n\n protected async execute(): Promise<void> {\n if (!this.job.getQueueServiceResolver) {\n throw new Error(\n 'Queue service resolver is not set.\\nDid you forget to call Job.setDefaultQueueServiceResolver()?',\n )\n }\n\n const queue = await this.job.getQueueServiceResolver()\n await queue.enqueue(this.job, this.payload)\n }\n\n /**\n * By defining the \"then\" method, PendingDispatch becomes \"thenable\",\n * allowing it to trigger automatically when await is called.\n */\n public async then<TResult1 = void, TResult2 = never>(\n onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n // When await is called, actually execte pending chain.\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import { PendingDispatch } from '../pending_dispatch.js'\nimport { Queue } from '../queue.js'\nimport { QueueConnectionName } from '../types.js'\n\nimport { SerializedLock } from '@verrou/core/types'\nimport { randomUUID } from 'node:crypto'\n\nexport type Payload<T extends Job> = T extends Job<infer P> ? P : unknown\n\nexport type PayloadWithLock<T extends Job, P extends Payload<T>> = P & {\n _lock?: SerializedLock\n}\n\nexport type Options = {\n connection?: QueueConnectionName\n queue?: string\n retries: number\n delay: number\n}\n\nexport abstract class Job<P = unknown> {\n private _id: string = randomUUID()\n\n public options: Options = {\n retries: 3,\n delay: 0,\n }\n\n public static compileName(queue: string, name: string): string {\n return `${queue}_${name}`\n }\n\n public static parseName(name: string): { queue: string; name: string } {\n const [q, n] = name.split(/_(.+)/) // split on the first underscore only\n return { queue: q, name: n }\n }\n\n public get connection(): QueueConnectionName | undefined {\n return this.options.connection\n }\n\n public set connection(connection: QueueConnectionName | undefined) {\n this.options.connection = connection\n }\n\n public get queue(): string | undefined {\n return this.options.queue\n }\n\n public set queue(queue: string | undefined) {\n this.options.queue = queue\n }\n\n public get name(): string {\n return this.constructor.name\n }\n\n public get id(): string {\n return this._id\n }\n\n public set id(id: string) {\n this._id = id\n }\n\n public get fullyQualifiedName(): string {\n if (!this.options.queue) {\n throw new Error('Queue is not set.')\n }\n\n return Job.compileName(this.options.queue, this.name)\n }\n\n private static defaultQueueServiceResolver: () => Promise<Queue>\n\n public static setDefaultQueueServiceResolver(\n queueServiceResolver: () => Promise<Queue>,\n ): void {\n this.defaultQueueServiceResolver = queueServiceResolver\n }\n\n public setQueueServiceResolver(\n queueServiceResolver: () => Promise<Queue>,\n ): void {\n this.queueServiceResolver = queueServiceResolver\n }\n\n private queueServiceResolver?: () => Promise<Queue> = undefined\n\n public get getQueueServiceResolver(): () => Promise<Queue> {\n return this.queueServiceResolver || Job.defaultQueueServiceResolver\n }\n\n /**\n * Handle the job with the typed payload.\n */\n public abstract handle(payload: P): Promise<void>\n\n /**\n * Dispatch a job of type with a typed payload.\n */\n public static dispatch<T extends Job, P extends Payload<T>>(\n this: new () => T,\n payload: P,\n ): PendingDispatch<T, P> {\n const job = new this() as T\n return new PendingDispatch<T, P>(job, payload)\n }\n}\n","import { Job } from './job.js'\n\nimport { EventEmitter } from 'stream'\n\n/**\n * Type-safe queue driver events.\n */\nexport interface QueueDriverEvents {\n error: [error: Error]\n 'job:start': [job: Job, payload: unknown]\n 'job:progress': [job: Job, payload: unknown, elapsed: number]\n 'job:complete': [job: Job, payload: unknown, elapsed: number]\n 'job:error': [error: Error, job: Job, payload: unknown]\n 'job:finish': [job: Job, payload: unknown, elapsed: number]\n}\n\n/**\n * Type-safe event emitter for the queue driver.\n */\nexport abstract class QueueDriverEventEmitter extends EventEmitter {\n public override on<K extends keyof QueueDriverEvents>(\n event: K,\n listener: (...args: QueueDriverEvents[K]) => void,\n ): this {\n return super.on(event, listener)\n }\n\n public override off<K extends keyof QueueDriverEvents>(\n event: K,\n listener: (...args: QueueDriverEvents[K]) => void,\n ): this {\n return super.off(event, listener)\n }\n\n public override emit<K extends keyof QueueDriverEvents>(\n event: K,\n ...args: QueueDriverEvents[K]\n ): boolean {\n /**\n * Avoid throwing error even if there are no listeners\n * for the `error` event (special case, see:\n * https://nodejs.org/api/events.html#error-events).\n */\n if (this.listenerCount(event) === 0) {\n return false\n }\n\n return super.emit(event, ...args)\n }\n}\n","import { Logger, createDefaultLogger } from '../../logger.js'\nimport { QueueConfig, QueueConnectionName, WorkerOptions } from '../types.js'\nimport { Job, Payload } from './job.js'\nimport { QueueDriverEventEmitter } from './queue_driver_event_emitter.js'\n\nimport type { Lock, LockFactory } from '@verrou/core'\nimport type { SerializedLock } from '@verrou/core/types'\n\nexport type ProcessJobParams = {\n id: string\n fullyQualifiedName: string\n payload: unknown\n}\n\n/**\n * Interface to be augmented by users to define their queue names.\n * This enables type-safe queue names throughout the application.\n */\nexport interface QueueList {}\n\n/**\n * Interface to be augmented by users to map connections to their queue names.\n * This enables connection-specific type-safe queue names.\n */\nexport interface ConnectionQueues {}\n\n/**\n * Extract queue names from QueueList.\n * Defaults to string if no queues are defined.\n */\nexport type QueueName = keyof QueueList extends never ? string : keyof QueueList\n\n/**\n * Extract queue names for a specific connection from ConnectionQueues.\n */\nexport type QueueNameForConnection<C extends QueueConnectionName> =\n C extends keyof ConnectionQueues ? ConnectionQueues[C] : QueueName\n\nexport type QueueDriverStopOptions = {\n /**\n * Whether to wait for the jobs to finish processing before stopping.\n *\n * Default: true\n */\n graceful?: boolean\n\n /**\n * The timeout in milliseconds to wait for the jobs to finish processing.\n *\n * Default: 30000 (30 seconds)\n */\n timeout?: number\n}\n\nexport type QueueDriverConfig = {}\n\nexport abstract class QueueDriver<\n Config extends QueueDriverConfig = QueueDriverConfig,\n> extends QueueDriverEventEmitter {\n protected logger: Logger\n\n protected registeredQueues: Set<string> = new Set()\n\n protected registeredJobs: Map<string, new () => Job> = new Map()\n\n protected lockFactory?: LockFactory\n\n public connection: QueueConnectionName | undefined\n\n constructor(\n protected config: QueueConfig,\n protected options: Record<string, WorkerOptions>,\n protected driverConfig: Config = {} as Config,\n ) {\n super()\n this.logger = createDefaultLogger('queue')\n }\n\n public setLogger(logger: Logger): void {\n this.logger = logger\n }\n\n protected getMergedWorkerOptions(\n queue: QueueName,\n options?: WorkerOptions,\n ): WorkerOptions {\n const base = this.options[queue] || {}\n return { ...base, ...(options || {}) }\n }\n\n public async listen(\n queue: QueueName,\n options?: WorkerOptions,\n ): Promise<void> {\n if (this.registeredQueues.has(queue as string)) {\n throw new Error(`Queue '${queue as string}' already registered`)\n }\n\n this.registeredQueues.add(queue as string)\n\n const workerOptions = this.getMergedWorkerOptions(queue, options)\n\n this.logger.trace(\n { connection: this.connection, queue, options: workerOptions },\n 'Listening queue',\n )\n }\n\n public async register(job: new () => Job): Promise<void> {\n if (this.registeredJobs.has(job.name)) {\n return\n }\n\n this.registeredJobs.set(job.name, job)\n\n this.logger.trace(\n { connection: this.connection, job: job.name },\n 'Registered job',\n )\n }\n\n public async unregister(job: new () => Job): Promise<void> {\n if (!this.registeredJobs.has(job.name)) {\n return\n }\n\n this.registeredJobs.delete(job.name)\n\n this.logger.trace(\n { connection: this.connection, job: job.name },\n 'Unregistered job',\n )\n }\n\n public async start(): Promise<void> {\n for (const [queue, options] of Object.entries(this.options)) {\n await this.listen(queue as QueueName, options)\n }\n }\n\n public async stop(_options?: QueueDriverStopOptions): Promise<void> {\n this.registeredQueues.clear()\n this.registeredJobs.clear()\n }\n\n protected checkIfQueueIsRegistered(queue: string): void {\n if (!this.registeredQueues.has(queue)) {\n throw new Error(`Queue '${queue}' is not registered.`)\n }\n }\n\n protected checkIfJobIsRegistered(job: string): void {\n if (!this.registeredJobs.has(job)) {\n throw new Error(`Job '${job}' is not registered.`)\n }\n }\n\n public getDefaultQueue(): QueueName {\n if (this.registeredQueues.size === 0) {\n throw new Error(\n `No queues registered for connection: ${this.connection}.`,\n )\n }\n\n return this.registeredQueues.values().next().value as QueueName\n }\n\n /**\n * Parent method to be extended by the driver.\n * It implements checks for the job and queue being registered.\n */\n public async enqueue<T extends Job, P extends Payload<T>>(\n job: T,\n // @ts-ignore\n payload: P,\n ): Promise<void> {\n if (!job.options.queue) {\n job.options.queue = this.getDefaultQueue()\n }\n\n this.checkIfQueueIsRegistered(job.options.queue)\n this.checkIfJobIsRegistered(job.name)\n\n return Promise.resolve()\n }\n\n /**\n * Create a lock factory instance for this driver.\n *\n * Each driver implementation should return a\n * [Verrou LockFactory instance](https://verrou.dev/docs/quick-setup#lockfactory-api)\n * that matches the driver's backing store.\n *\n * @returns A LockFactory instance\n */\n public abstract createLockProvider(): LockFactory\n\n /**\n * Clean up resources associated with a lock factory created by this driver.\n * This is called when the queue is stopped to ensure proper resource cleanup.\n *\n * @param lockFactory - The lock factory instance to clean up\n */\n public async destroyLockProvider(_lockFactory: LockFactory): Promise<void> {\n // Default implementation does nothing\n // Drivers that need cleanup (e.g., Postgres with Knex) should override this\n }\n\n /**\n * Execute a job with the given parameters.\n * This method contains the common job processing logic shared by all drivers.\n */\n protected async process(params: ProcessJobParams): Promise<void> {\n const { id, fullyQualifiedName, payload } = params\n const { queue, name } = Job.parseName(fullyQualifiedName)\n\n if (!queue || !name) {\n const error = new Error(`Invalid job class name: ${fullyQualifiedName}`)\n this.logger.warn(error)\n throw error\n }\n\n this.logger.trace({ job: name }, 'Processing job')\n\n try {\n this.checkIfJobIsRegistered(name)\n } catch (error) {\n this.logger.warn(error)\n throw error\n }\n\n const jobClass = this.registeredJobs.get(name)!\n\n const jobInstance = new jobClass()\n\n jobInstance.connection = this.connection\n jobInstance.queue = queue\n jobInstance.id = id\n\n this.logger.debug(\n {\n connection: this.connection,\n queue,\n job: name,\n id,\n },\n 'Processing job',\n )\n\n /**\n * A job might have been scheduled by the scheduler,\n * in which case we need to restore the lock from the payload.\n *\n * This will prevent the job from being scheduled\n * while it is being processed.\n */\n const serializedLock = (payload as any)?._lock as SerializedLock | undefined\n const ttl = serializedLock?.ttl\n\n let lock: Lock | undefined\n\n if (serializedLock !== undefined) {\n if (this.lockFactory === undefined) {\n this.logger.warn(\n { job: name, id },\n 'Scheduled job has lock but lock factory is not available',\n )\n } else {\n try {\n lock = this.lockFactory.restoreLock(serializedLock)\n await lock.acquireImmediately()\n\n if (ttl) {\n await lock.extend(ttl)\n }\n\n this.logger.trace(\n {\n job: name,\n id,\n serializedLock,\n },\n 'Restored lock from scheduler',\n )\n } catch (error) {\n this.logger.warn({ job: name, id, error }, 'Failed to restore lock')\n }\n }\n }\n\n const startedAt = Date.now()\n\n this.emit('job:start', jobInstance, payload)\n\n let isExtending = false\n\n const onProgress = async () => {\n if (lock && ttl && !isExtending) {\n try {\n const remainingTime = lock.getRemainingTime()\n\n /**\n * Extend lock if remaining time drops below half the TTL.\n * This acts as a heartbeat to prevent the lock from expiring\n * while the job is still running, without unnecessary extensions\n * for short jobs.\n */\n if (remainingTime !== null && remainingTime <= ttl / 2) {\n isExtending = true\n this.logger.trace(\n { job: name, id, remainingTime, ttl },\n 'Extending lock',\n )\n await lock.extend(ttl)\n }\n } catch (error) {\n this.logger.warn({ job: name, id, error }, 'Failed to extend lock')\n } finally {\n isExtending = false\n }\n }\n\n this.emit('job:progress', jobInstance, payload, Date.now() - startedAt)\n }\n\n const intervalId = setInterval(onProgress, 1000)\n\n /**\n * Next, we process the actual job.\n */\n try {\n await jobInstance.handle(payload)\n\n this.emit('job:complete', jobInstance, payload, Date.now() - startedAt)\n\n this.logger.trace(\n {\n connection: this.connection,\n queue,\n job: name,\n id,\n },\n 'Job completed',\n )\n } catch (error) {\n this.emit('job:error', error, jobInstance, payload)\n } finally {\n this.emit('job:finish', jobInstance, payload, Date.now() - startedAt)\n /**\n * Once the job is done with any status,\n * we must clear the interval.\n */\n clearInterval(intervalId)\n\n /**\n * If we previously acquired a lock for\n * this job, we need to release it here.\n */\n if (lock) {\n try {\n await lock.forceRelease()\n\n this.logger.trace(\n { job: name, id, lock: lock.serialize() },\n 'Released lock for scheduled job',\n )\n } catch (error) {\n this.logger.warn({ job: name, id, error }, 'Failed to release lock')\n }\n }\n }\n }\n}\n","export type ScheduleInterval =\n | 'second'\n | 'two seconds'\n | 'three seconds'\n | 'four seconds'\n | 'five seconds'\n | 'ten seconds'\n | 'fifteen seconds'\n | 'twenty seconds'\n | 'thirty seconds'\n | 'minute'\n | 'two minutes'\n | 'three minutes'\n | 'four minutes'\n | 'five minutes'\n | 'ten minutes'\n | 'fifteen minutes'\n | 'twenty minutes'\n | 'thirty minutes'\n | 'hour'\n | 'two hours'\n | 'three hours'\n | 'four hours'\n | 'five hours'\n | 'six hours'\n | 'seven hours'\n | 'eight hours'\n | 'nine hours'\n | 'ten hours'\n | 'eleven hours'\n | 'twelve hours'\n | 'day'\n | 'week'\n | 'sunday'\n | 'monday'\n | 'tuesday'\n | 'wednesday'\n | 'thursday'\n | 'friday'\n | 'saturday'\n | 'month'\n | 'last day of month'\n// | 'quarter'\n// | 'year'\n\nexport type ScheduleIntervalDayOfWeek =\n | 'sunday'\n | 'monday'\n | 'tuesday'\n | 'wednesday'\n | 'thursday'\n | 'friday'\n | 'saturday'\n\ntype Hour = `${0 | 1 | 2}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`\ntype Minute = `${0 | 1 | 2 | 3 | 4 | 5}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`\n\nexport type ScheduleIntervalTime = `${Hour}:${Minute}`\n\nexport function parseTime(time: ScheduleIntervalTime): [number, number] {\n const [hour, minute] = time.split(':')\n\n if (!hour || !minute || hour.length !== 2 || minute.length !== 2) {\n throw new Error('Invalid time format')\n }\n\n return [Number(hour), Number(minute)]\n}\n\nexport interface IntervalCronOptions {\n /** Minute to run. Default: 0 */\n minute?: number\n /** Hour to run. Default: 0 */\n hour?: number\n /** Day of month to run. Default: 1 */\n dayOfMonth?: number\n /** Day of week to run. Default: 0 */\n dayOfWeek?: ScheduleIntervalDayOfWeek\n}\n\nexport function dayOfWeekToNumber(\n dayOfWeek: ScheduleIntervalDayOfWeek,\n): number {\n switch (dayOfWeek) {\n case 'sunday':\n return 0\n case 'monday':\n return 1\n case 'tuesday':\n return 2\n case 'wednesday':\n return 3\n case 'thursday':\n return 4\n case 'friday':\n return 5\n case 'saturday':\n return 6\n }\n}\n\n/**\n * Converts a ScheduleInterval to a cron pattern string.\n * For longer intervals (hour, day, week, etc.), you can customize when they run.\n *\n * @param interval - The schedule interval\n * @param options - Options to customize the cron pattern\n * @returns A cron pattern string\n *\n * @example\n * intervalToCron('minute') // '* * * * *' - every minute\n * intervalToCron('hour', { minute: 30 }) // '30 * * * *' - every hour at :30\n * intervalToCron('day', { hour: 14, minute: 30 }) // '30 14 * * *' - daily at 2:30 PM\n * intervalToCron('week', { dayOfWeek: 1, hour: 9 }) // '0 9 * * 1' - Mondays at 9 AM\n */\nexport function intervalToCron(\n interval: ScheduleInterval,\n options: IntervalCronOptions = {},\n): string {\n const { minute = 0, hour = 0, dayOfMonth = 1, dayOfWeek } = options\n\n const dayOfWeekNumber = dayOfWeekToNumber(dayOfWeek ?? 'sunday')\n\n const patterns: Record<ScheduleInterval, string> = {\n second: '* * * * * *',\n 'two seconds': '*/2 * * * * *',\n 'three seconds': '*/3 * * * * *',\n 'four seconds': '*/4 * * * * *',\n 'five seconds': '*/5 * * * * *',\n 'ten seconds': '*/10 * * * * *',\n 'fifteen seconds': '*/15 * * * * *',\n 'twenty seconds': '*/20 * * * * *',\n 'thirty seconds': '*/30 * * * * *',\n\n minute: '* * * * *',\n 'two minutes': '*/2 * * * *',\n 'three minutes': '*/3 * * * *',\n 'four minutes': '*/4 * * * *',\n 'five minutes': '*/5 * * * *',\n 'ten minutes': '*/10 * * * *',\n 'fifteen minutes': '*/15 * * * *',\n 'twenty minutes': '*/20 * * * *',\n 'thirty minutes': '*/30 * * * *',\n\n hour: `${minute} * * * *`,\n 'two hours': `${minute} */2 * * *`,\n 'three hours': `${minute} */3 * * *`,\n 'four hours': `${minute} */4 * * *`,\n 'five hours': `${minute} */5 * * *`,\n 'six hours': `${minute} */6 * * *`,\n 'seven hours': `${minute} */7 * * *`,\n 'eight hours': `${minute} */8 * * *`,\n 'nine hours': `${minute} */9 * * *`,\n 'ten hours': `${minute} */10 * * *`,\n 'eleven hours': `${minute} */11 * * *`,\n 'twelve hours': `${minute} */12 * * *`,\n\n day: `${minute} ${hour} * * *`,\n\n week: `${minute} ${hour} * * ${dayOfWeekNumber}`,\n sunday: `${minute} ${hour} * * 0`,\n monday: `${minute} ${hour} * * 1`,\n tuesday: `${minute} ${hour} * * 2`,\n wednesday: `${minute} ${hour} * * 3`,\n thursday: `${minute} ${hour} * * 4`,\n friday: `${minute} ${hour} * * 5`,\n saturday: `${minute} ${hour} * * 6`,\n\n month: `${minute} ${hour} ${dayOfWeek ? '*' : dayOfMonth} * ${dayOfWeek ? dayOfWeekNumber + '#1' : '*'}`,\n 'last day of month': `${minute} ${hour} L * *`,\n\n // quarter: `${minute} ${hour} ${dayOfWeek ? '*' : dayOfMonth} */3 ${dayOfWeek ? dayOfWeekNumber : '*'}`,\n // year: `${minute} ${hour} ${dayOfMonth} 1 ${dayOfWeek ? dayOfWeekNumber : '*'}`,\n }\n\n return patterns[interval]\n}\n","import { Cron } from 'croner'\n\n/**\n * Cron instance registry for internal use\n */\nexport class ScheduleRegistry {\n private static instances: Record<string, Cron> = {}\n\n public static add(name: string, cron: Cron): void {\n if (this.instances[name]) {\n throw new Error(`Cron instance with name '${name}' already exists`)\n }\n\n this.instances[name] = cron\n }\n\n public static all(): Record<string, Cron> {\n return this.instances\n }\n\n public static get(name: string): Cron | undefined {\n return this.instances[name]\n }\n\n public static clear(name?: string): void {\n if (name) {\n this.instances[name]?.stop()\n delete this.instances[name]\n } else {\n Object.values(this.instances).forEach((cron) => cron.stop())\n this.instances = {}\n }\n }\n}\n","import { MaybePromise } from '../types.js'\nimport {\n IntervalCronOptions,\n ScheduleInterval,\n ScheduleIntervalDayOfWeek,\n ScheduleIntervalTime,\n intervalToCron,\n parseTime,\n} from './schedule_interval.js'\nimport { ScheduleRegistry } from './schedule_registry.js'\n\nimport type { LockFactory } from '@verrou/core'\nimport type { SerializedLock } from '@verrou/core/types'\nimport { Duration } from '@verrou/core/types'\nimport { Cron } from 'croner'\nimport { createHash } from 'node:crypto'\n\nexport const getDistributedLockKey = (name: string) => {\n const hash = createHash('sha1').update(name).digest('hex').slice(0, 8)\n return `lavoro:schedule:${hash}`\n}\n\nexport class PendingSchedule {\n protected cronPattern?: string\n\n protected interval: ScheduleInterval = 'day'\n protected intervalOptions?: IntervalCronOptions\n\n protected distributedLockOptions: {\n /**\n * The lock key is based on the task name to ensure only one instance runs\n *\n * @param name - The task name\n * @returns The lock key\n */\n key: (name: string) => string\n /**\n * The lock TTL is the duration for which the lock is held\n */\n ttl: Duration\n /**\n * Whether to allow overlapping executions of the same task\n */\n overlap: boolean\n /**\n * Whether to hand off the lock to the queue worker\n */\n handOff: boolean\n } = {\n key: getDistributedLockKey,\n /**\n * Interval during which the lock is held for the job\n * and no other instances of it will not be scheduled\n */\n ttl: '15s',\n overlap: false,\n handOff: false,\n }\n\n constructor(\n protected name: string,\n protected cb: (lock?: SerializedLock) => MaybePromise<void>,\n protected lockProviderResolver: () => LockFactory,\n ) {}\n\n /**\n * Schedule using a cron pattern.\n * You can use https://crontab.guru to generate a cron pattern.\n *\n * @param pattern - Cron pattern string\n *\n * @example\n * Schedule.call('my-task', () => {}).cron('0 0 * * *') // Daily at midnight\n * Schedule.call('my-task', () => {}).cron('*\\/5 * * * *') // Every 5 minutes\n * Schedule.call('my-task', () => {}).cron('0 *\\/2 * * *') // Every 2 hours\n */\n public cron(pattern: string): this {\n this.cronPattern = pattern\n return this\n }\n\n /**\n * Schedule to run at a specific interval.\n * For longer intervals (hour, day, week, etc.), you can customize when they run.\n *\n * @param interval - The schedule interval\n * @param options - Options to customize the cron pattern\n *\n * @example\n * Schedule.call('my-task', () => {}).every('minute')\n * Schedule.call('my-task', () => {}).every('hour', { minute: 30 })\n * Schedule.call('my-task', () => {}).every('day', { hour: 14, minute: 30 })\n * Schedule.call('my-task', () => {}).every('week', { dayOfWeek: 1, hour: 9 })\n */\n public every(\n interval: ScheduleInterval,\n options?: IntervalCronOptions,\n ): this {\n this.interval = interval\n this.intervalOptions = options\n this.cronPattern = intervalToCron(interval, options)\n return this\n }\n\n public on(dayOfWeek: ScheduleIntervalDayOfWeek): this {\n // .on() only makes sense for intervals larger than 'day'\n const valid: ScheduleInterval[] = [\n //\n 'week',\n 'month',\n ]\n\n if (!valid.includes(this.interval)) {\n throw new Error(\n `.on() can only be used for weekly intervals or larger. Current interval: '${this.interval}'`,\n )\n }\n\n this.intervalOptions = { ...(this.intervalOptions ?? {}), dayOfWeek }\n this.cronPattern = intervalToCron(this.interval, this.intervalOptions)\n return this\n }\n\n public at(time: ScheduleIntervalTime): this {\n // .at() only makes sense for intervals larger than 'hour'\n const valid: ScheduleInterval[] = [\n 'day',\n 'week',\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n 'month',\n 'last day of month',\n ]\n\n if (!valid.includes(this.interval)) {\n throw new Error(\n `.at() can only be used for daily intervals or larger. Current interval: '${this.interval}'`,\n )\n }\n\n const [hour, minute] = parseTime(time)\n this.intervalOptions = { ...(this.intervalOptions ?? {}), hour, minute }\n this.cronPattern = intervalToCron(this.interval, this.intervalOptions)\n return this\n }\n\n /**\n * Set the duration during which other instances\n * of the same task will be prevented from running.\n *\n * This should be roughly the duration of the task execution or longer\n * since the lock will be released automatically after the task execution.\n *\n * @param ttl - The lock duration\n *\n * @example\n * Schedule.call('my-task', () => {}).lockFor('10s')\n */\n public lockFor(ttl: Duration): this {\n this.distributedLockOptions.ttl = ttl\n return this\n }\n\n /**\n * Allow overlapping executions of the same task.\n *\n * @example\n * Schedule.call('my-task', () => {}).overlapping()\n */\n public overlapping(): this {\n this.distributedLockOptions.overlap = true\n return this\n }\n\n protected async execute(): Promise<void> {\n if (!this.cronPattern) {\n throw new Error(\n 'No schedule pattern defined. To schedule a task, set interval explicitly.',\n )\n }\n\n ScheduleRegistry.add(\n this.name,\n new Cron(this.cronPattern, async () => {\n // If overlapping is allowed, we can call the callback right away.\n if (this.distributedLockOptions.overlap) {\n await this.cb()\n return\n }\n\n // First, we create a distributed lock based on the task name,\n // which ensures that only one instance of the task runs at a time.\n const key = this.distributedLockOptions.key(this.name)\n\n // Resolve lock provider instance and create the lock.\n const lockProvider = this.lockProviderResolver()\n const lock = lockProvider.createLock(\n key,\n this.distributedLockOptions.ttl,\n )\n\n // If the lock is already locked, we skip the execution.\n // if (await lock.isLocked()) {\n // return\n // }\n\n const acquired = await lock.acquireImmediately()\n\n if (!acquired) {\n return\n }\n\n // If the lock was acquired, we run the task,\n // otherwise just skip it and do nothing.\n try {\n // If the lock is marked to be handed off to the queue\n // worker, we serialize and pass it to the callback.\n // Otherwise we just run the task and then release\n // the lock immediately.\n if (this.distributedLockOptions.handOff) {\n await this.cb(lock.serialize())\n } else {\n await this.cb()\n await lock.forceRelease()\n }\n } catch (error) {\n // If the task callback failed, we release\n // the lock and re-throw the error.\n await lock.forceRelease()\n throw error\n }\n }),\n )\n }\n\n /**\n * By defining the \"then\" method, PendingDispatch becomes \"thenable\",\n * allowing it to trigger automatically when await is called.\n */\n public async then<TResult1 = void, TResult2 = never>(\n onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n // When await is called, actually execte pending chain.\n return this.execute().then(onfulfilled, onrejected)\n }\n}\n","import { Job, Payload, PayloadWithLock } from '../queue/contracts/job.js'\nimport { QueueNameForConnection } from '../queue/contracts/queue_driver.js'\nimport { PendingDispatch } from '../queue/pending_dispatch.js'\nimport { DefaultConnection, QueueConnectionName } from '../queue/types.js'\nimport { PendingSchedule } from './pending_schedule.js'\n\nimport type { LockFactory } from '@verrou/core'\nimport type { SerializedLock } from '@verrou/core/types'\n\nexport class PendingJobSchedule<\n T extends Job,\n P extends Payload<T>,\n C extends QueueConnectionName = DefaultConnection extends { name: infer N }\n ? N extends QueueConnectionName\n ? N\n : QueueConnectionName\n : QueueConnectionName,\n> extends PendingSchedule {\n /**\n * Use composition pattern and store pending\n * job dispatch inside a pending schedule.\n */\n protected dispatch: PendingDispatch<T, PayloadWithLock<T, P>, C>\n\n private _connection?: string\n private _queue?: string\n\n private get jobName(): string {\n return `${this._connection}_${this._queue}_${this.job.name}`\n }\n\n constructor(\n protected job: T,\n protected payload: PayloadWithLock<T, P>,\n protected lockProviderResolver: () => LockFactory,\n ) {\n super(\n job.name,\n async (lock?: SerializedLock) => {\n if (lock) {\n this.payload._lock = lock\n }\n\n await this.dispatch.then(undefined, (error) => {\n throw error\n })\n },\n lockProviderResolver,\n )\n\n this.job = job\n this.payload = payload\n this.dispatch = new PendingDispatch(job, payload)\n\n this.distributedLockOptions.handOff = true\n }\n\n public onConnection<NewC extends QueueConnectionName>(\n connection: NewC,\n ): PendingJobSchedule<T, P, NewC> {\n this._connection = connection\n this.dispatch.onConnection(connection)\n // TypeScript can't track type parameter changes through `this`.\n // The instance is the same, but the type parameter C changes to NewC,\n // so we need to assert the type to the new type.\n return this as unknown as PendingJobSchedule<T, P, NewC>\n }\n\n public onQueue(queue: QueueNameForConnection<C>): this {\n this._queue = queue\n this.dispatch.onQueue(queue)\n\n return this\n }\n\n // public withQueueServiceResolver(queueServiceResolver: () => Promise<Queue>) {\n // this.dispatch.withQueueServiceResolver(queueServiceResolver)\n // return this\n // }\n\n protected async execute(): Promise<void> {\n // Before executing, update the lock provider resolver to use the one from the queue connection\n const queueServiceResolver = this.job.getQueueServiceResolver\n if (queueServiceResolver) {\n const queue = await queueServiceResolver()\n\n // Get the connection the job will use\n const connection =\n this.job.options.connection ?? queue.getDefaultConnection()\n\n // Override the lock service resolver to use the connection's lock provider\n const connectionLockService = queue.getLockProvider(connection)\n\n if (connectionLockService) {\n this.lockProviderResolver = () => connectionLockService\n }\n\n // Register this scheduled job with the queue service\n if (this.job.id) {\n queue.registerScheduledJob(this.job.id)\n }\n\n this._connection = this._connection ?? queue.getDefaultConnection()\n this._queue = this._queue ?? queue.getDefaultQueue()\n }\n\n this.name = this.jobName\n\n await super.execute()\n }\n}\n","import { Job, Payload, PayloadWithLock } from '../queue/contracts/job.js'\nimport { MaybePromise } from '../types.js'\nimport { PendingJobSchedule } from './pending_job_schedule.js'\nimport { PendingSchedule } from './pending_schedule.js'\nimport { ScheduleRegistry } from './schedule_registry.js'\n\nimport { LockFactory } from '@verrou/core'\nimport { memoryStore } from '@verrou/core/drivers/memory'\n\n/**\n * Default lock provider instance for distributed locking.\n * Uses memory store by default.\n */\nexport const defaultScheduleLockProvider = new LockFactory(\n memoryStore().factory(),\n)\n\nexport class Schedule {\n /**\n * Default lock provider resolver for distributed locking.\n * Returns the default memory-based instance if not overridden.\n */\n private static defaultLockProviderResolver: () => LockFactory = () =>\n defaultScheduleLockProvider\n\n /**\n * Set a custom lock provider resolver for distributed locking.\n *\n * This allows dynamic resolution of lock provider instances, useful when\n * integrating with Queue or other services that manage lock instances.\n *\n * @param resolver - Function that returns a LockFactory instance\n *\n * @example\n * import { LockFactory } from '@verrou/core'\n * import { redisStore } from '@verrou/core/drivers/redis'\n *\n * const customLockProvider = new LockFactory(\n * redisStore({ connection: redisClient })\n * )\n *\n * Schedule.setLockProviderResolver(() => customLockProvider)\n */\n public static setLockProviderResolver(resolver: () => LockFactory): void {\n this.defaultLockProviderResolver = resolver\n }\n\n /**\n * Get the current lock provider instance from the resolver.\n * @internal\n */\n public static getLockProvider(): LockFactory {\n return this.defaultLockProviderResolver()\n }\n\n /**\n * Clear all scheduled tasks (or specific one if name is specified).\n *\n * @param name - The name of the task to clear\n *\n * @example\n * Schedule.clear() // Clear all tasks\n * Schedule.clear('my-task') // Clear specific task\n */\n public static clear(name?: string): void {\n if (name) {\n ScheduleRegistry.clear(name)\n } else {\n Object.keys(ScheduleRegistry.all()).forEach((name) =>\n ScheduleRegistry.clear(name),\n )\n }\n }\n\n /**\n * Schedule a callback to run at specified intervals.\n *\n * @param name - Unique identifier for the task (required for distributed systems)\n * @param cb - The callback function to execute\n *\n * @example\n * // Using cron pattern\n * const cleanupUsers = async () => { ... }\n * Schedule.call('cleanup-users', cleanupUsers).cron('0 0 * * *')\n *\n * // Using convenience methods\n * Schedule.call('hourly-task', () => { ... }).hourly()\n * Schedule.call('daily-task', () => { ... }).daily()\n */\n public static call(name: string, cb: () => MaybePromise<void>) {\n return new PendingSchedule(name, cb, this.defaultLockProviderResolver)\n }\n\n /**\n * Schedule a job to run at specified intervals.\n *\n * You can specify the connection and queue to use for the job\n * the same way you would dispatch a job.\n *\n * @param job - The job class to schedule\n * @param payload - The payload to pass to the job\n *\n * @example\n * Schedule.job(TestJob, { arg1: 'hello', arg2: 1 }).every('minute')\n * Schedule.job(TestJob, { arg1: 'hello', arg2: 1 })\n * .onConnection('main')\n * .onQueue('default')\n * .every('minute')\n */\n public static job<T extends Job, P extends Payload<T>>(\n job: new () => T,\n payload: P,\n ): PendingJobSchedule<T, P> {\n return new PendingJobSchedule<T, P>(\n new job(),\n payload as PayloadWithLock<T, P>,\n this.defaultLockProviderResolver,\n )\n }\n}\n","import {\n QueueDriverEventEmitter,\n QueueDriverEvents,\n} from './contracts/queue_driver_event_emitter.js'\n\n/**\n * Queue service can emit any of the queue driver\n * events alongside its own events (currently none).\n */\nexport interface QueueEvents extends QueueDriverEvents {}\n\n/**\n * Type-safe event emitter for the queue service.\n */\nexport abstract class QueueEventEmitter extends QueueDriverEventEmitter {\n public override on<K extends keyof QueueEvents>(\n event: K,\n listener: (...args: QueueEvents[K]) => void,\n ): this {\n return super.on(event, listener)\n }\n\n public override emit<K extends keyof QueueEvents>(\n event: K,\n ...args: QueueEvents[K]\n ): boolean {\n return super.emit(event, ...args)\n }\n}\n","import { Logger, createDefaultLogger } from '../logger.js'\nimport { Schedule } from '../schedule/schedule.js'\nimport { Job, Payload } from './contracts/job.js'\nimport {\n QueueDriver,\n QueueDriverStopOptions,\n QueueName,\n} from './contracts/queue_driver.js'\nimport { QueueDriverEvents } from './contracts/queue_driver_event_emitter.js'\nimport { QueueEventEmitter } from './queue_event_emitter.js'\nimport type {\n QueueConfig,\n QueueConnectionConfig,\n QueueConnectionName,\n} from './types.js'\n\nimport type { LockFactory } from '@verrou/core'\n\nexport class Queue extends QueueEventEmitter {\n private drivers: Map<QueueConnectionName, QueueDriver> = new Map()\n private started: boolean = false\n private logger: Logger\n private scheduledJobs: Set<string> = new Set()\n private lockFactories: Map<QueueConnectionName, LockFactory> = new Map()\n\n constructor(private config: QueueConfig & { logger?: Logger }) {\n super()\n\n this.logger = config?.logger || createDefaultLogger('queue')\n\n for (const [connection, driverConfig] of Object.entries(\n this.config.connections,\n )) {\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Creating queue driver',\n )\n\n const driver = this.createDriver(driverConfig)\n driver.connection = connection as QueueConnectionName\n\n this.bindEvents(driver)\n\n this.drivers.set(driver.connection, driver)\n }\n }\n\n /**\n * Bind all queue driver events and\n * proxy them through the queue service.\n */\n private bindEvents(driver: QueueDriver) {\n const events = [\n 'error',\n 'job:start',\n 'job:progress',\n 'job:complete',\n 'job:error',\n 'job:finish',\n ] as const\n\n for (const event of events) {\n driver.on(event, (...args) => {\n this.emit(event, ...(args as QueueDriverEvents[typeof event]))\n })\n }\n }\n\n private createDriver(\n config: QueueConnectionConfig<QueueDriver>,\n ): QueueDriver {\n const { constructor: driverConstructor, config: driverConfig } =\n config.driver\n\n const driver = new driverConstructor(\n this.config,\n config.queues,\n driverConfig,\n )\n\n driver.setLogger(this.logger)\n\n return driver\n }\n\n async start() {\n if (this.started) {\n this.logger.warn('Queue service already started')\n return\n }\n\n /**\n * Create lock factories for each connection.\n * If no lock provider was explicitly configured,\n * create a driver-specific lock factory.\n */\n for (const [connection, driverConfig] of Object.entries(\n this.config.connections,\n )) {\n const driver = this.drivers.get(connection as QueueConnectionName)\n if (!driver) {\n throw new Error(`Driver not found for connection: ${connection}.`)\n }\n\n /**\n * Create driver-specific lock factory if\n * no lock provider was explicitly configured.\n */\n if (!driverConfig.lockProvider) {\n const lockFactory = driver.createLockProvider()\n if (lockFactory) {\n this.lockFactories.set(connection as QueueConnectionName, lockFactory)\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Created lock factory for driver',\n )\n }\n } else {\n this.lockFactories.set(\n connection as QueueConnectionName,\n driverConfig.lockProvider,\n )\n this.logger.trace(\n { connection, driver: driverConfig.driver },\n 'Using explicitly configured lock provider for driver',\n )\n }\n }\n\n for (const job of this.config.jobs) {\n await this.register(job)\n }\n\n this.started = true\n\n for (const [connection, driver] of this.drivers) {\n this.logger.trace({ connection }, 'Starting queue connection')\n await driver.start()\n }\n\n this.logger.trace(\n { connections: Array.from(this.drivers.keys()) },\n 'Queue service started',\n )\n }\n\n async stop(\n options: QueueDriverStopOptions = {\n graceful: true,\n timeout: 30000,\n },\n ) {\n if (!this.started) {\n this.logger.warn('Queue service not started')\n return\n }\n\n this.logger.trace(\n { count: this.scheduledJobs.size },\n 'Cleared scheduled jobs for the queue',\n )\n\n // Clear all scheduled jobs registered with this Queue instance\n for (const id of this.scheduledJobs) {\n Schedule.clear(id)\n }\n\n this.scheduledJobs.clear()\n\n for (const job of this.config.jobs) {\n await this.unregister(job)\n }\n\n for (const [connection, driver] of this.drivers) {\n this.logger.trace({ connection }, 'Stopping queue connection')\n await driver.stop(options)\n }\n\n /**\n * Destroy lock factories for each connection.\n */\n for (const [connection, lockFactory] of this.lockFactories) {\n const driver = this.drivers.get(connection as QueueConnectionName)\n if (!driver) {\n throw new Error(`Driver not found for connection: ${connection}.`)\n }\n\n await driver.destroyLockProvider(lockFactory)\n this.lockFactories.delete(connection as QueueConnectionName)\n }\n\n this.started = false\n\n this.logger.trace('Queue service stopped')\n }\n\n protected async register(job: new () => Job): Promise<void> {\n for (const [_, driver] of this.drivers) {\n await driver.register(job)\n }\n }\n\n protected async unregister(job: new () => Job): Promise<void> {\n for (const [_, driver] of this.drivers) {\n await driver.unregister(job)\n }\n }\n\n public async enqueue<T extends Job, P extends Payload<T>>(\n job: T,\n payload: P,\n ): Promise<void> {\n if (this.drivers.size === 0) {\n throw new Error('No queue drivers available.')\n }\n\n /**\n * Try to get driver from connection if specified\n * otherwise use the first available driver.\n */\n const driver: QueueDriver | undefined = job.options.connection\n ? this.drivers.get(job.options.connection)\n : this.drivers.values().next().value\n\n if (!driver) {\n throw new Error(\n `No driver found for connection: ${job.options.connection}.`,\n )\n }\n\n if (!job.options.queue) {\n job.options.queue = driver.getDefaultQueue()\n }\n\n await driver.enqueue(job, payload)\n }\n\n /**\n * Get the default connection name.\n *\n * Returns the name of the first available connection.\n * Throws an error if no connections are available.\n */\n public getDefaultConnection(): QueueConnectionName {\n if (this.drivers.size === 0) {\n throw new Error(`No queue drivers available.`)\n }\n\n return this.drivers.keys().next().value as QueueConnectionName\n }\n\n public getDefaultQueue(): QueueName {\n if (this.drivers.size === 0) {\n throw new Error(`No queue drivers available.`)\n }\n\n return this.drivers.values().next().value?.getDefaultQueue() as QueueName\n }\n\n /**\n * Get the lock factory instance for a specific connection.\n * Returns the explicitly configured lock provider or the auto-created lock factory.\n * Throws an error if no lock factory is available.\n *\n * @param connection - The connection name\n * @returns The lock factory instance\n */\n public getLockProvider(connection: QueueConnectionName): LockFactory {\n const connectionConfig = this.config.connections[connection]\n\n // Return explicitly configured lock provider if available\n if (connectionConfig?.lockProvider) {\n return connectionConfig.lockProvider\n }\n\n // Return auto-created lock factory\n const lockFactory = this.lockFactories.get(connection)\n\n if (!lockFactory) {\n throw new Error(`No lock provider found for connection: ${connection}.`)\n }\n\n return lockFactory\n }\n\n /**\n * Register a scheduled job ID with this Queue instance.\n * This allows the Queue to clean up scheduled jobs when it stops.\n */\n public registerScheduledJob(id: string): void {\n this.scheduledJobs.add(id)\n }\n}\n"],"mappings":"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","names":["name"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lavoro/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "A type-safe, flexible queue library for Node.js with support for multiple drivers",
6
6
  "author": "Aleksei Ivanov <contact@aleksei.dev>",
@@ -18,11 +18,15 @@
18
18
  "url": "https://github.com/lexuzieel/lavoro/issues"
19
19
  },
20
20
  "keywords": [
21
- "queue",
22
- "background",
23
21
  "worker",
22
+ "queue",
23
+ "distributed",
24
24
  "job",
25
- "scheduler"
25
+ "task",
26
+ "scheduler",
27
+ "postgres",
28
+ "postgresql",
29
+ "pg-boss"
26
30
  ],
27
31
  "exports": {
28
32
  ".": {
@@ -40,6 +44,7 @@
40
44
  "watch": "tsup-node --watch"
41
45
  },
42
46
  "dependencies": {
47
+ "@lukeed/ms": "^2.0.2",
43
48
  "@verrou/core": "^0.5.2",
44
49
  "croner": "^9.1.0"
45
50
  },