@quave/agenda 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE.md +25 -0
  2. package/README.md +380 -0
  3. package/dist/Job.d.ts +190 -0
  4. package/dist/Job.js +689 -0
  5. package/dist/Job.js.map +1 -0
  6. package/dist/JobProcessingQueue.d.ts +47 -0
  7. package/dist/JobProcessingQueue.js +107 -0
  8. package/dist/JobProcessingQueue.js.map +1 -0
  9. package/dist/JobProcessor.d.ts +72 -0
  10. package/dist/JobProcessor.js +512 -0
  11. package/dist/JobProcessor.js.map +1 -0
  12. package/dist/backends/index.d.ts +18 -0
  13. package/dist/backends/index.js +20 -0
  14. package/dist/backends/index.js.map +1 -0
  15. package/dist/cjs/Job.d.ts +190 -0
  16. package/dist/cjs/Job.js +696 -0
  17. package/dist/cjs/Job.js.map +1 -0
  18. package/dist/cjs/JobProcessingQueue.d.ts +47 -0
  19. package/dist/cjs/JobProcessingQueue.js +111 -0
  20. package/dist/cjs/JobProcessingQueue.js.map +1 -0
  21. package/dist/cjs/JobProcessor.d.ts +72 -0
  22. package/dist/cjs/JobProcessor.js +519 -0
  23. package/dist/cjs/JobProcessor.js.map +1 -0
  24. package/dist/cjs/backends/index.d.ts +17 -0
  25. package/dist/cjs/backends/index.js +20 -0
  26. package/dist/cjs/backends/index.js.map +1 -0
  27. package/dist/cjs/decorators/Define.d.ts +33 -0
  28. package/dist/cjs/decorators/Define.js +47 -0
  29. package/dist/cjs/decorators/Define.js.map +1 -0
  30. package/dist/cjs/decorators/Every.d.ts +43 -0
  31. package/dist/cjs/decorators/Every.js +58 -0
  32. package/dist/cjs/decorators/Every.js.map +1 -0
  33. package/dist/cjs/decorators/JobsController.d.ts +43 -0
  34. package/dist/cjs/decorators/JobsController.js +40 -0
  35. package/dist/cjs/decorators/JobsController.js.map +1 -0
  36. package/dist/cjs/decorators/index.d.ts +41 -0
  37. package/dist/cjs/decorators/index.js +57 -0
  38. package/dist/cjs/decorators/index.js.map +1 -0
  39. package/dist/cjs/decorators/metadata.d.ts +50 -0
  40. package/dist/cjs/decorators/metadata.js +41 -0
  41. package/dist/cjs/decorators/metadata.js.map +1 -0
  42. package/dist/cjs/decorators/register.d.ts +52 -0
  43. package/dist/cjs/decorators/register.js +145 -0
  44. package/dist/cjs/decorators/register.js.map +1 -0
  45. package/dist/cjs/index.d.ts +498 -0
  46. package/dist/cjs/index.js +776 -0
  47. package/dist/cjs/index.js.map +1 -0
  48. package/dist/cjs/notifications/BaseNotificationChannel.d.ts +47 -0
  49. package/dist/cjs/notifications/BaseNotificationChannel.js +112 -0
  50. package/dist/cjs/notifications/BaseNotificationChannel.js.map +1 -0
  51. package/dist/cjs/notifications/InMemoryNotificationChannel.d.ts +13 -0
  52. package/dist/cjs/notifications/InMemoryNotificationChannel.js +37 -0
  53. package/dist/cjs/notifications/InMemoryNotificationChannel.js.map +1 -0
  54. package/dist/cjs/notifications/index.d.ts +2 -0
  55. package/dist/cjs/notifications/index.js +8 -0
  56. package/dist/cjs/notifications/index.js.map +1 -0
  57. package/dist/cjs/package.json +1 -0
  58. package/dist/cjs/types/AgendaBackend.d.ts +61 -0
  59. package/dist/cjs/types/AgendaBackend.js +3 -0
  60. package/dist/cjs/types/AgendaBackend.js.map +1 -0
  61. package/dist/cjs/types/AgendaConfig.d.ts +10 -0
  62. package/dist/cjs/types/AgendaConfig.js +3 -0
  63. package/dist/cjs/types/AgendaConfig.js.map +1 -0
  64. package/dist/cjs/types/AgendaStatus.d.ts +32 -0
  65. package/dist/cjs/types/AgendaStatus.js +3 -0
  66. package/dist/cjs/types/AgendaStatus.js.map +1 -0
  67. package/dist/cjs/types/DbOptions.d.ts +4 -0
  68. package/dist/cjs/types/DbOptions.js +3 -0
  69. package/dist/cjs/types/DbOptions.js.map +1 -0
  70. package/dist/cjs/types/DrainOptions.d.ts +37 -0
  71. package/dist/cjs/types/DrainOptions.js +3 -0
  72. package/dist/cjs/types/DrainOptions.js.map +1 -0
  73. package/dist/cjs/types/JobDefinition.d.ts +49 -0
  74. package/dist/cjs/types/JobDefinition.js +3 -0
  75. package/dist/cjs/types/JobDefinition.js.map +1 -0
  76. package/dist/cjs/types/JobLogger.d.ts +111 -0
  77. package/dist/cjs/types/JobLogger.js +3 -0
  78. package/dist/cjs/types/JobLogger.js.map +1 -0
  79. package/dist/cjs/types/JobParameters.d.ts +106 -0
  80. package/dist/cjs/types/JobParameters.js +21 -0
  81. package/dist/cjs/types/JobParameters.js.map +1 -0
  82. package/dist/cjs/types/JobQuery.d.ts +79 -0
  83. package/dist/cjs/types/JobQuery.js +36 -0
  84. package/dist/cjs/types/JobQuery.js.map +1 -0
  85. package/dist/cjs/types/JobRepository.d.ts +101 -0
  86. package/dist/cjs/types/JobRepository.js +3 -0
  87. package/dist/cjs/types/JobRepository.js.map +1 -0
  88. package/dist/cjs/types/NotificationChannel.d.ts +120 -0
  89. package/dist/cjs/types/NotificationChannel.js +3 -0
  90. package/dist/cjs/types/NotificationChannel.js.map +1 -0
  91. package/dist/cjs/utils/backoff.d.ts +152 -0
  92. package/dist/cjs/utils/backoff.js +175 -0
  93. package/dist/cjs/utils/backoff.js.map +1 -0
  94. package/dist/cjs/utils/dateConstraints.d.ts +48 -0
  95. package/dist/cjs/utils/dateConstraints.js +127 -0
  96. package/dist/cjs/utils/dateConstraints.js.map +1 -0
  97. package/dist/cjs/utils/isValidDate.d.ts +1 -0
  98. package/dist/cjs/utils/isValidDate.js +8 -0
  99. package/dist/cjs/utils/isValidDate.js.map +1 -0
  100. package/dist/cjs/utils/nextRunAt.d.ts +10 -0
  101. package/dist/cjs/utils/nextRunAt.js +119 -0
  102. package/dist/cjs/utils/nextRunAt.js.map +1 -0
  103. package/dist/cjs/utils/priority.d.ts +13 -0
  104. package/dist/cjs/utils/priority.js +23 -0
  105. package/dist/cjs/utils/priority.js.map +1 -0
  106. package/dist/cjs/utils/processEvery.d.ts +1 -0
  107. package/dist/cjs/utils/processEvery.js +13 -0
  108. package/dist/cjs/utils/processEvery.js.map +1 -0
  109. package/dist/cjs/utils/stack.d.ts +1 -0
  110. package/dist/cjs/utils/stack.js +21 -0
  111. package/dist/cjs/utils/stack.js.map +1 -0
  112. package/dist/cjs/version.d.ts +9 -0
  113. package/dist/cjs/version.js +13 -0
  114. package/dist/cjs/version.js.map +1 -0
  115. package/dist/decorators/Define.d.ts +33 -0
  116. package/dist/decorators/Define.js +44 -0
  117. package/dist/decorators/Define.js.map +1 -0
  118. package/dist/decorators/Every.d.ts +43 -0
  119. package/dist/decorators/Every.js +55 -0
  120. package/dist/decorators/Every.js.map +1 -0
  121. package/dist/decorators/JobsController.d.ts +43 -0
  122. package/dist/decorators/JobsController.js +37 -0
  123. package/dist/decorators/JobsController.js.map +1 -0
  124. package/dist/decorators/index.d.ts +41 -0
  125. package/dist/decorators/index.js +46 -0
  126. package/dist/decorators/index.js.map +1 -0
  127. package/dist/decorators/metadata.d.ts +50 -0
  128. package/dist/decorators/metadata.js +35 -0
  129. package/dist/decorators/metadata.js.map +1 -0
  130. package/dist/decorators/register.d.ts +52 -0
  131. package/dist/decorators/register.js +139 -0
  132. package/dist/decorators/register.js.map +1 -0
  133. package/dist/index.d.ts +498 -0
  134. package/dist/index.js +744 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/notifications/BaseNotificationChannel.d.ts +47 -0
  137. package/dist/notifications/BaseNotificationChannel.js +108 -0
  138. package/dist/notifications/BaseNotificationChannel.js.map +1 -0
  139. package/dist/notifications/InMemoryNotificationChannel.d.ts +13 -0
  140. package/dist/notifications/InMemoryNotificationChannel.js +33 -0
  141. package/dist/notifications/InMemoryNotificationChannel.js.map +1 -0
  142. package/dist/notifications/index.d.ts +2 -0
  143. package/dist/notifications/index.js +3 -0
  144. package/dist/notifications/index.js.map +1 -0
  145. package/dist/types/AgendaBackend.d.ts +61 -0
  146. package/dist/types/AgendaBackend.js +2 -0
  147. package/dist/types/AgendaBackend.js.map +1 -0
  148. package/dist/types/AgendaConfig.d.ts +10 -0
  149. package/dist/types/AgendaConfig.js +2 -0
  150. package/dist/types/AgendaConfig.js.map +1 -0
  151. package/dist/types/AgendaStatus.d.ts +32 -0
  152. package/dist/types/AgendaStatus.js +2 -0
  153. package/dist/types/AgendaStatus.js.map +1 -0
  154. package/dist/types/DbOptions.d.ts +4 -0
  155. package/dist/types/DbOptions.js +2 -0
  156. package/dist/types/DbOptions.js.map +1 -0
  157. package/dist/types/DrainOptions.d.ts +37 -0
  158. package/dist/types/DrainOptions.js +2 -0
  159. package/dist/types/DrainOptions.js.map +1 -0
  160. package/dist/types/JobDefinition.d.ts +49 -0
  161. package/dist/types/JobDefinition.js +2 -0
  162. package/dist/types/JobDefinition.js.map +1 -0
  163. package/dist/types/JobLogger.d.ts +111 -0
  164. package/dist/types/JobLogger.js +2 -0
  165. package/dist/types/JobLogger.js.map +1 -0
  166. package/dist/types/JobParameters.d.ts +106 -0
  167. package/dist/types/JobParameters.js +17 -0
  168. package/dist/types/JobParameters.js.map +1 -0
  169. package/dist/types/JobQuery.d.ts +79 -0
  170. package/dist/types/JobQuery.js +33 -0
  171. package/dist/types/JobQuery.js.map +1 -0
  172. package/dist/types/JobRepository.d.ts +101 -0
  173. package/dist/types/JobRepository.js +2 -0
  174. package/dist/types/JobRepository.js.map +1 -0
  175. package/dist/types/NotificationChannel.d.ts +120 -0
  176. package/dist/types/NotificationChannel.js +2 -0
  177. package/dist/types/NotificationChannel.js.map +1 -0
  178. package/dist/utils/backoff.d.ts +152 -0
  179. package/dist/utils/backoff.js +167 -0
  180. package/dist/utils/backoff.js.map +1 -0
  181. package/dist/utils/dateConstraints.d.ts +48 -0
  182. package/dist/utils/dateConstraints.js +117 -0
  183. package/dist/utils/dateConstraints.js.map +1 -0
  184. package/dist/utils/isValidDate.d.ts +1 -0
  185. package/dist/utils/isValidDate.js +5 -0
  186. package/dist/utils/isValidDate.js.map +1 -0
  187. package/dist/utils/nextRunAt.d.ts +10 -0
  188. package/dist/utils/nextRunAt.js +110 -0
  189. package/dist/utils/nextRunAt.js.map +1 -0
  190. package/dist/utils/priority.d.ts +13 -0
  191. package/dist/utils/priority.js +20 -0
  192. package/dist/utils/priority.js.map +1 -0
  193. package/dist/utils/processEvery.d.ts +1 -0
  194. package/dist/utils/processEvery.js +7 -0
  195. package/dist/utils/processEvery.js.map +1 -0
  196. package/dist/utils/stack.d.ts +1 -0
  197. package/dist/utils/stack.js +18 -0
  198. package/dist/utils/stack.js.map +1 -0
  199. package/dist/version.d.ts +9 -0
  200. package/dist/version.js +10 -0
  201. package/dist/version.js.map +1 -0
  202. package/package.json +79 -0
  203. package/test/fixtures/forkHelper.js +40 -0
  204. package/test/fixtures/someJobDefinition.ts +12 -0
  205. package/test/shared/agenda-test-suite.ts +1876 -0
  206. package/test/shared/backoff-test-suite.ts +408 -0
  207. package/test/shared/debounce-test-suite.ts +377 -0
  208. package/test/shared/full-test-suite.ts +161 -0
  209. package/test/shared/index.ts +50 -0
  210. package/test/shared/joblogger-test-suite.ts +305 -0
  211. package/test/shared/jobprocessor-test-suite.ts +266 -0
  212. package/test/shared/notification-channel-test-suite.ts +471 -0
  213. package/test/shared/removeoncomplete-test-suite.ts +228 -0
  214. package/test/shared/repository-test-suite.ts +708 -0
  215. package/test/shared/retry-test-suite.ts +174 -0
  216. package/test/shared/test-utils.ts +100 -0
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=JobLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobLogger.js","sourceRoot":"","sources":["../../../src/types/JobLogger.ts"],"names":[],"mappings":""}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Branded type for job IDs.
3
+ * At runtime it's a string, but TypeScript treats it as a distinct type.
4
+ */
5
+ export type JobId = string & {
6
+ readonly __brand: 'AgendaJobId';
7
+ };
8
+ /**
9
+ * Create a JobId from a string
10
+ */
11
+ export declare function toJobId(id: string): JobId;
12
+ /**
13
+ * Options for debouncing job execution.
14
+ * Debouncing delays job execution and resets the timer on subsequent saves,
15
+ * ensuring the job only runs once after a quiet period.
16
+ */
17
+ export interface DebounceOptions {
18
+ /**
19
+ * Debounce window in milliseconds.
20
+ * The job will be scheduled to run this many ms after the last save.
21
+ */
22
+ delay: number;
23
+ /**
24
+ * Maximum time to wait before forcing execution (in milliseconds).
25
+ * If set, the job will execute within maxWait even if new saves keep coming.
26
+ * Without maxWait, continuous saves could delay execution indefinitely.
27
+ */
28
+ maxWait?: number;
29
+ /**
30
+ * Debounce strategy:
31
+ * - 'trailing' (default): Execute after quiet period ends (last call wins)
32
+ * - 'leading': Execute immediately on first call, ignore subsequent calls during window
33
+ */
34
+ strategy?: 'trailing' | 'leading';
35
+ }
36
+ /**
37
+ * Options for unique constraint behavior during job save.
38
+ */
39
+ export interface UniqueOpts {
40
+ /**
41
+ * If true, only insert if no matching job exists.
42
+ * Existing jobs are returned unchanged.
43
+ */
44
+ insertOnly?: boolean;
45
+ /**
46
+ * Debounce options for combining rapid job saves.
47
+ * Requires a unique constraint to be set.
48
+ */
49
+ debounce?: DebounceOptions;
50
+ }
51
+ export interface JobParameters<DATA = unknown | void> {
52
+ /** Job ID */
53
+ _id?: JobId;
54
+ name: string;
55
+ priority: number;
56
+ nextRunAt: Date | null;
57
+ /**
58
+ * normal: job is queued and will be processed (regular case when the user adds a new job)
59
+ * single: job with this name is only queued once, if there is an existing entry in the database, the job is just updated, but not newly inserted (this is used for .every())
60
+ */
61
+ type: 'normal' | 'single';
62
+ lockedAt?: Date;
63
+ lastFinishedAt?: Date;
64
+ failedAt?: Date;
65
+ failCount?: number;
66
+ failReason?: string;
67
+ repeatTimezone?: string;
68
+ lastRunAt?: Date;
69
+ repeatInterval?: string | number;
70
+ data: DATA;
71
+ repeatAt?: string;
72
+ disabled?: boolean;
73
+ progress?: number;
74
+ /**
75
+ * Unique constraint query object.
76
+ * Keys are field paths (e.g., 'data.userId'), values are the expected values.
77
+ * The implementation interprets this for upsert operations.
78
+ */
79
+ unique?: Record<string, unknown>;
80
+ uniqueOpts?: UniqueOpts;
81
+ /**
82
+ * Tracks when debounce window started.
83
+ * Used internally for maxWait calculations.
84
+ */
85
+ debounceStartedAt?: Date;
86
+ lastModifiedBy?: string;
87
+ /** Forks a new node sub process for executing this job */
88
+ fork?: boolean;
89
+ /**
90
+ * The date when the job should start running.
91
+ * The job will not run before this date.
92
+ */
93
+ startDate?: Date;
94
+ /**
95
+ * The date when the job should stop running.
96
+ * The job will not run after this date (nextRunAt will be set to null).
97
+ */
98
+ endDate?: Date;
99
+ /**
100
+ * Days of the week to skip (0 = Sunday, 1 = Monday, ..., 6 = Saturday).
101
+ * The job will not run on these days.
102
+ */
103
+ skipDays?: number[];
104
+ }
105
+ export type TJobDatefield = keyof Pick<JobParameters, 'lastRunAt' | 'lastFinishedAt' | 'nextRunAt' | 'failedAt' | 'lockedAt' | 'startDate' | 'endDate' | 'debounceStartedAt'>;
106
+ export declare const datefields: Array<TJobDatefield>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.datefields = void 0;
4
+ exports.toJobId = toJobId;
5
+ /**
6
+ * Create a JobId from a string
7
+ */
8
+ function toJobId(id) {
9
+ return id;
10
+ }
11
+ exports.datefields = [
12
+ 'lastRunAt',
13
+ 'lastFinishedAt',
14
+ 'nextRunAt',
15
+ 'failedAt',
16
+ 'lockedAt',
17
+ 'startDate',
18
+ 'endDate',
19
+ 'debounceStartedAt'
20
+ ];
21
+ //# sourceMappingURL=JobParameters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobParameters.js","sourceRoot":"","sources":["../../../src/types/JobParameters.ts"],"names":[],"mappings":";;;AASA,0BAEC;AALD;;GAEG;AACH,SAAgB,OAAO,CAAC,EAAU;IACjC,OAAO,EAAW,CAAC;AACpB,CAAC;AAmHY,QAAA,UAAU,GAAyB;IAC/C,WAAW;IACX,gBAAgB;IAChB,WAAW;IACX,UAAU;IACV,UAAU;IACV,WAAW;IACX,SAAS;IACT,mBAAmB;CACnB,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { JobParameters, JobId } from './JobParameters.js';
2
+ import type { SortDirection } from './DbOptions.js';
3
+ /**
4
+ * Computed job states based on job timestamps.
5
+ * These are derived from job data, not stored in the database.
6
+ * 'paused' is based on the disabled field rather than timestamps.
7
+ */
8
+ export type JobState = 'running' | 'scheduled' | 'queued' | 'completed' | 'failed' | 'repeating' | 'paused';
9
+ /**
10
+ * Overview statistics for a single job name
11
+ */
12
+ export interface JobsOverview {
13
+ name: string;
14
+ total: number;
15
+ running: number;
16
+ scheduled: number;
17
+ queued: number;
18
+ completed: number;
19
+ failed: number;
20
+ repeating: number;
21
+ paused: number;
22
+ }
23
+ /**
24
+ * Job with computed state
25
+ */
26
+ export interface JobWithState<DATA = unknown> extends JobParameters<DATA> {
27
+ _id: JobId;
28
+ state: JobState;
29
+ }
30
+ /**
31
+ * Sort options for job queries (database-agnostic)
32
+ */
33
+ export interface JobsSort {
34
+ nextRunAt?: SortDirection;
35
+ lastRunAt?: SortDirection;
36
+ lastFinishedAt?: SortDirection;
37
+ priority?: SortDirection;
38
+ name?: SortDirection;
39
+ data?: SortDirection;
40
+ }
41
+ /**
42
+ * Database-agnostic query options for jobs
43
+ */
44
+ export interface JobsQueryOptions {
45
+ /** Filter by job name */
46
+ name?: string;
47
+ /** Filter by job names (multiple) */
48
+ names?: string[];
49
+ /** Filter by computed state */
50
+ state?: JobState;
51
+ /** Filter by job ID */
52
+ id?: string;
53
+ /** Filter by job IDs (multiple) */
54
+ ids?: string[];
55
+ /** Text to search in job name */
56
+ search?: string;
57
+ /** Filter by job data (exact match or partial object match) */
58
+ data?: unknown;
59
+ /** Include disabled jobs (default: true) */
60
+ includeDisabled?: boolean;
61
+ /** Sort order */
62
+ sort?: JobsSort;
63
+ /** Number of jobs to skip (pagination) */
64
+ skip?: number;
65
+ /** Maximum number of jobs to return */
66
+ limit?: number;
67
+ }
68
+ /**
69
+ * Result of jobs query with state computation
70
+ */
71
+ export interface JobsResult<DATA = unknown> {
72
+ jobs: JobWithState<DATA>[];
73
+ /** Total count (before pagination, after state filter) */
74
+ total: number;
75
+ }
76
+ /**
77
+ * Compute the state for a job based on its timestamps
78
+ */
79
+ export declare function computeJobState(job: JobParameters, now?: Date): JobState;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeJobState = computeJobState;
4
+ /**
5
+ * Compute the state for a job based on its timestamps
6
+ */
7
+ function computeJobState(job, now = new Date()) {
8
+ const { lockedAt, lastFinishedAt, failedAt, nextRunAt, repeatInterval } = job;
9
+ // Running: currently locked and processing
10
+ if (lockedAt) {
11
+ return 'running';
12
+ }
13
+ // Failed: has failed and failure is at least as recent as completion
14
+ if (failedAt && (!lastFinishedAt || failedAt >= lastFinishedAt)) {
15
+ return 'failed';
16
+ }
17
+ // Repeating: has repeat interval and hasn't failed
18
+ if (repeatInterval) {
19
+ return 'repeating';
20
+ }
21
+ // Scheduled: next run is in the future
22
+ if (nextRunAt && nextRunAt > now) {
23
+ return 'scheduled';
24
+ }
25
+ // Queued: next run is now or past (ready to run)
26
+ if (nextRunAt && nextRunAt <= now) {
27
+ return 'queued';
28
+ }
29
+ // Completed: has finished and either no failure or completion is strictly more recent
30
+ if (lastFinishedAt && (!failedAt || lastFinishedAt > failedAt)) {
31
+ return 'completed';
32
+ }
33
+ // Default to completed if nothing else matches
34
+ return 'completed';
35
+ }
36
+ //# sourceMappingURL=JobQuery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobQuery.js","sourceRoot":"","sources":["../../../src/types/JobQuery.ts"],"names":[],"mappings":";;AAqFA,0CAmCC;AAtCD;;GAEG;AACH,SAAgB,eAAe,CAAC,GAAkB,EAAE,MAAY,IAAI,IAAI,EAAE;IACzE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC;IAE9E,2CAA2C;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,qEAAqE;IACrE,IAAI,QAAQ,IAAI,CAAC,CAAC,cAAc,IAAI,QAAQ,IAAI,cAAc,CAAC,EAAE,CAAC;QACjE,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,mDAAmD;IACnD,IAAI,cAAc,EAAE,CAAC;QACpB,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,uCAAuC;IACvC,IAAI,SAAS,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QAClC,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,iDAAiD;IACjD,IAAI,SAAS,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;QACnC,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,sFAAsF;IACtF,IAAI,cAAc,IAAI,CAAC,CAAC,QAAQ,IAAI,cAAc,GAAG,QAAQ,CAAC,EAAE,CAAC;QAChE,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,+CAA+C;IAC/C,OAAO,WAAW,CAAC;AACpB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { JobParameters, JobId } from './JobParameters.js';
2
+ import type { JobsQueryOptions, JobsResult, JobsOverview } from './JobQuery.js';
3
+ /**
4
+ * Options passed to repository methods that modify jobs
5
+ */
6
+ export interface JobRepositoryOptions {
7
+ /** Name to set as lastModifiedBy on the job */
8
+ lastModifiedBy?: string;
9
+ }
10
+ /**
11
+ * Options for removing jobs (database-agnostic)
12
+ */
13
+ export interface RemoveJobsOptions {
14
+ /** Remove job by ID */
15
+ id?: JobId | string;
16
+ /** Remove jobs by IDs */
17
+ ids?: (JobId | string)[];
18
+ /** Remove jobs by name */
19
+ name?: string;
20
+ /** Remove jobs by names (include) */
21
+ names?: string[];
22
+ /** Remove jobs NOT matching these names (exclude) */
23
+ notNames?: string[];
24
+ /** Remove jobs matching data */
25
+ data?: unknown;
26
+ }
27
+ /**
28
+ * Database-agnostic job repository interface.
29
+ * Implementations can be created for MongoDB, PostgreSQL, etc.
30
+ */
31
+ export interface JobRepository {
32
+ /**
33
+ * Connect to the database
34
+ */
35
+ connect(): Promise<void>;
36
+ /**
37
+ * Query jobs with filtering, pagination, and state computation
38
+ */
39
+ queryJobs(options?: JobsQueryOptions): Promise<JobsResult>;
40
+ /**
41
+ * Get overview statistics for all job types
42
+ */
43
+ getJobsOverview(): Promise<JobsOverview[]>;
44
+ /**
45
+ * Get all distinct job names
46
+ */
47
+ getDistinctJobNames(): Promise<string[]>;
48
+ /**
49
+ * Get a single job by ID
50
+ */
51
+ getJobById(id: string): Promise<JobParameters | null>;
52
+ /**
53
+ * Get count of jobs ready to run (nextRunAt < now)
54
+ */
55
+ getQueueSize(): Promise<number>;
56
+ /**
57
+ * Remove jobs matching the given options
58
+ * @returns Number of jobs removed
59
+ */
60
+ removeJobs(options: RemoveJobsOptions): Promise<number>;
61
+ /**
62
+ * Save a job (insert or update)
63
+ */
64
+ saveJob<DATA = unknown>(job: JobParameters<DATA>, options: JobRepositoryOptions | undefined): Promise<JobParameters<DATA>>;
65
+ /**
66
+ * Update job state fields (lockedAt, lastRunAt, progress, etc.)
67
+ */
68
+ saveJobState(job: JobParameters, options: JobRepositoryOptions | undefined): Promise<void>;
69
+ /**
70
+ * Attempt to lock a job for processing
71
+ * @returns The locked job data, or undefined if lock failed
72
+ */
73
+ lockJob(job: JobParameters, options: JobRepositoryOptions | undefined): Promise<JobParameters | undefined>;
74
+ /**
75
+ * Unlock a single job
76
+ */
77
+ unlockJob(job: JobParameters): Promise<void>;
78
+ /**
79
+ * Unlock multiple jobs by ID
80
+ */
81
+ unlockJobs(jobIds: (JobId | string)[]): Promise<void>;
82
+ /**
83
+ * Find and lock the next job to run for a given job type
84
+ */
85
+ getNextJobToRun(jobName: string, nextScanAt: Date, lockDeadline: Date, now: Date | undefined, options: JobRepositoryOptions | undefined): Promise<JobParameters | undefined>;
86
+ /**
87
+ * Disable jobs matching the given options
88
+ * @returns Number of jobs disabled
89
+ */
90
+ disableJobs(options: RemoveJobsOptions): Promise<number>;
91
+ /**
92
+ * Enable jobs matching the given options
93
+ * @returns Number of jobs enabled
94
+ */
95
+ enableJobs(options: RemoveJobsOptions): Promise<number>;
96
+ /**
97
+ * Remove ALL jobs from the database unconditionally.
98
+ * @returns Number of jobs removed
99
+ */
100
+ purgeAllJobs(): Promise<number>;
101
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=JobRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobRepository.js","sourceRoot":"","sources":["../../../src/types/JobRepository.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import type { JobId } from './JobParameters.js';
2
+ /**
3
+ * Notification payload sent when a job is saved/updated
4
+ */
5
+ export interface JobNotification {
6
+ jobId: JobId;
7
+ jobName: string;
8
+ nextRunAt: Date | null;
9
+ priority: number;
10
+ timestamp: Date;
11
+ source?: string;
12
+ }
13
+ /**
14
+ * Types of job state events that can be published
15
+ */
16
+ export type JobStateType = 'start' | 'progress' | 'success' | 'fail' | 'complete' | 'retry';
17
+ /**
18
+ * Notification payload sent when a job's state changes during execution.
19
+ * This enables bi-directional communication so that all subscribers
20
+ * can receive job lifecycle events (start, success, fail, complete, etc.)
21
+ */
22
+ export interface JobStateNotification {
23
+ type: JobStateType;
24
+ jobId: JobId;
25
+ jobName: string;
26
+ timestamp: Date;
27
+ source?: string;
28
+ /** Progress percentage (0-100) for 'progress' events */
29
+ progress?: number;
30
+ /** Error message for 'fail' events */
31
+ error?: string;
32
+ /** Failure count for 'fail' events */
33
+ failCount?: number;
34
+ /** Scheduled retry time for 'retry' events */
35
+ retryAt?: Date;
36
+ /** Retry attempt number for 'retry' events */
37
+ retryAttempt?: number;
38
+ /** Job execution duration in ms for 'complete' events */
39
+ duration?: number;
40
+ /** When the job started for 'complete' events */
41
+ lastRunAt?: Date;
42
+ /** When the job finished for 'complete' events */
43
+ lastFinishedAt?: Date;
44
+ }
45
+ /**
46
+ * Handler function for processing job state notifications
47
+ */
48
+ export type StateNotificationHandler = (notification: JobStateNotification) => void | Promise<void>;
49
+ /**
50
+ * Handler function for processing job notifications
51
+ */
52
+ export type NotificationHandler = (notification: JobNotification) => void | Promise<void>;
53
+ /**
54
+ * Possible states of a notification channel
55
+ */
56
+ export type NotificationChannelState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
57
+ /**
58
+ * Configuration options for notification channels
59
+ */
60
+ export interface NotificationChannelConfig {
61
+ channelName?: string;
62
+ reconnect?: {
63
+ enabled: boolean;
64
+ maxAttempts?: number;
65
+ initialDelayMs?: number;
66
+ maxDelayMs?: number;
67
+ };
68
+ }
69
+ /**
70
+ * Interface for notification channels that enable cross-process job notifications.
71
+ * Implementations can use Redis, PostgreSQL LISTEN/NOTIFY, or other pub/sub systems.
72
+ */
73
+ export interface NotificationChannel {
74
+ /**
75
+ * Current state of the channel
76
+ */
77
+ readonly state: NotificationChannelState;
78
+ /**
79
+ * Connect to the notification backend
80
+ */
81
+ connect(): Promise<void>;
82
+ /**
83
+ * Disconnect from the notification backend
84
+ */
85
+ disconnect(): Promise<void>;
86
+ /**
87
+ * Subscribe to job notifications
88
+ * @param handler - Function to call when a notification is received
89
+ * @returns Unsubscribe function
90
+ */
91
+ subscribe(handler: NotificationHandler): () => void;
92
+ /**
93
+ * Publish a job notification
94
+ * @param notification - The notification to publish
95
+ */
96
+ publish(notification: JobNotification): Promise<void>;
97
+ /**
98
+ * Register an event listener
99
+ */
100
+ on(event: 'stateChange', listener: (state: NotificationChannelState) => void): this;
101
+ on(event: 'error', listener: (error: Error) => void): this;
102
+ /**
103
+ * Remove an event listener
104
+ */
105
+ off(event: 'stateChange', listener: (state: NotificationChannelState) => void): this;
106
+ off(event: 'error', listener: (error: Error) => void): this;
107
+ /**
108
+ * Publish a job state notification (bi-directional communication).
109
+ * Optional - only implement if the channel supports state notifications.
110
+ * @param notification - The state notification to publish
111
+ */
112
+ publishState?(notification: JobStateNotification): Promise<void>;
113
+ /**
114
+ * Subscribe to job state notifications (bi-directional communication).
115
+ * Optional - only implement if the channel supports state notifications.
116
+ * @param handler - Function to call when a state notification is received
117
+ * @returns Unsubscribe function
118
+ */
119
+ subscribeState?(handler: StateNotificationHandler): () => void;
120
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=NotificationChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationChannel.js","sourceRoot":"","sources":["../../../src/types/NotificationChannel.ts"],"names":[],"mappings":""}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Backoff strategy utilities for job retry logic
3
+ */
4
+ /**
5
+ * Context passed to backoff strategy functions
6
+ */
7
+ export interface BackoffContext {
8
+ /** Current attempt number (1-based, so first retry is attempt 1) */
9
+ attempt: number;
10
+ /** The error that caused the failure */
11
+ error: Error;
12
+ /** Job name */
13
+ jobName: string;
14
+ /** Job data */
15
+ jobData: unknown;
16
+ }
17
+ /**
18
+ * A backoff strategy is a function that returns the delay in milliseconds
19
+ * before the next retry attempt, or `null` to stop retrying.
20
+ */
21
+ export type BackoffStrategy = (context: BackoffContext) => number | null;
22
+ /**
23
+ * Options for built-in backoff strategies
24
+ */
25
+ export interface BackoffOptions {
26
+ /** Initial delay in milliseconds (default: 1000) */
27
+ delay?: number;
28
+ /** Maximum delay in milliseconds (default: Infinity) */
29
+ maxDelay?: number;
30
+ /** Maximum number of retry attempts (default: 3) */
31
+ maxRetries?: number;
32
+ /** Add randomness to prevent thundering herd (default: 0, range: 0-1) */
33
+ jitter?: number;
34
+ }
35
+ /**
36
+ * Options specific to exponential backoff
37
+ */
38
+ export interface ExponentialBackoffOptions extends BackoffOptions {
39
+ /** Multiplier for each attempt (default: 2) */
40
+ factor?: number;
41
+ }
42
+ /**
43
+ * Options specific to linear backoff
44
+ */
45
+ export interface LinearBackoffOptions extends BackoffOptions {
46
+ /** Amount to increase delay by each attempt (default: same as delay) */
47
+ increment?: number;
48
+ }
49
+ /**
50
+ * Constant backoff - same delay between each retry
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * agenda.define('myJob', handler, {
55
+ * backoff: constant({ delay: 5000, maxRetries: 5 })
56
+ * });
57
+ * // Retries at: 5s, 5s, 5s, 5s, 5s
58
+ * ```
59
+ */
60
+ export declare function constant(options?: BackoffOptions): BackoffStrategy;
61
+ /**
62
+ * Linear backoff - delay increases by a fixed amount each retry
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * agenda.define('myJob', handler, {
67
+ * backoff: linear({ delay: 1000, increment: 2000, maxRetries: 4 })
68
+ * });
69
+ * // Retries at: 1s, 3s, 5s, 7s
70
+ * ```
71
+ */
72
+ export declare function linear(options?: LinearBackoffOptions): BackoffStrategy;
73
+ /**
74
+ * Exponential backoff - delay multiplies by a factor each retry
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * agenda.define('myJob', handler, {
79
+ * backoff: exponential({ delay: 1000, factor: 2, maxRetries: 5 })
80
+ * });
81
+ * // Retries at: 1s, 2s, 4s, 8s, 16s
82
+ *
83
+ * // With jitter to prevent thundering herd:
84
+ * agenda.define('myJob', handler, {
85
+ * backoff: exponential({ delay: 1000, factor: 2, maxRetries: 5, jitter: 0.2 })
86
+ * });
87
+ * ```
88
+ */
89
+ export declare function exponential(options?: ExponentialBackoffOptions): BackoffStrategy;
90
+ /**
91
+ * Combine multiple strategies - tries each in sequence until one returns non-null
92
+ * Useful for implementing complex retry logic
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * // Retry quickly at first, then back off
97
+ * agenda.define('myJob', handler, {
98
+ * backoff: combine(
99
+ * // First 2 retries: immediate (100ms)
100
+ * (ctx) => ctx.attempt <= 2 ? 100 : null,
101
+ * // Then exponential backoff for 3 more attempts
102
+ * exponential({ delay: 1000, maxRetries: 3 })
103
+ * )
104
+ * });
105
+ * ```
106
+ */
107
+ export declare function combine(...strategies: BackoffStrategy[]): BackoffStrategy;
108
+ /**
109
+ * Conditional backoff - only retry if condition is met
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * // Only retry on specific errors
114
+ * agenda.define('myJob', handler, {
115
+ * backoff: when(
116
+ * (ctx) => ctx.error.message.includes('timeout'),
117
+ * exponential({ delay: 1000, maxRetries: 3 })
118
+ * )
119
+ * });
120
+ * ```
121
+ */
122
+ export declare function when(condition: (context: BackoffContext) => boolean, strategy: BackoffStrategy): BackoffStrategy;
123
+ /**
124
+ * Pre-configured backoff strategies for common use cases
125
+ */
126
+ export declare const backoffStrategies: {
127
+ /** Constant 1s delay, 3 retries */
128
+ constant: typeof constant;
129
+ /** Linear increase starting at 1s, 3 retries */
130
+ linear: typeof linear;
131
+ /** Exponential with factor 2, starting at 1s, 3 retries */
132
+ exponential: typeof exponential;
133
+ /** Combine multiple strategies */
134
+ combine: typeof combine;
135
+ /** Conditional retry */
136
+ when: typeof when;
137
+ /**
138
+ * Aggressive retry - quick retries for transient failures
139
+ * 100ms, 200ms, 400ms (3 retries in ~700ms total)
140
+ */
141
+ aggressive: () => BackoffStrategy;
142
+ /**
143
+ * Relaxed retry - gentle backoff for rate-limited APIs
144
+ * 5s, 15s, 45s, 135s (4 retries over ~3 minutes)
145
+ */
146
+ relaxed: () => BackoffStrategy;
147
+ /**
148
+ * Standard retry - balanced approach
149
+ * 1s, 2s, 4s, 8s, 16s (5 retries over ~31 seconds)
150
+ */
151
+ standard: () => BackoffStrategy;
152
+ };