@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
package/LICENSE.md ADDED
@@ -0,0 +1,25 @@
1
+ ## License
2
+ (The MIT License)
3
+
4
+ Copyright (c) 2013 Ryan Schmukler <ryan@slingingcode.com>
5
+
6
+ Copyright (c) 2022-2026 Simon Tretter <s.tretter@gmail.com>
7
+
8
+ Contributors: Mikael Korpela, Alexis Tyler, Vasyl Boroviak, Haris Sulaiman, Loris Guignard
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
11
+ this software and associated documentation files (the 'Software'), to deal in
12
+ the Software without restriction, including without limitation the rights to
13
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14
+ the Software, and to permit persons to whom the Software is furnished to do so,
15
+ subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,380 @@
1
+ # @quave/agenda
2
+
3
+ <p align="center">
4
+ <img src="https://cdn.jsdelivr.net/gh/agenda/agenda@main/agenda.svg" alt="Agenda" width="100" height="100">
5
+ </p>
6
+
7
+ <p align="center">
8
+ A light-weight job scheduling library for Node.js
9
+ </p>
10
+
11
+ > Quave-managed fork of [agenda](https://github.com/agenda/agenda) 6.x (forked at `agenda@6.2.5`, commit `b9c47688`), kept on current dependencies and the newest MongoDB driver. Published as `@quave/agenda` (core) and [`@quave/agenda-mongodb`](../agenda-mongodb) (MongoDB backend).
12
+
13
+ > **Migrating from agenda v5?** See the upstream [Migration Guide](https://github.com/agenda/agenda/blob/main/docs/migration-guide-v6.md) for all breaking changes.
14
+
15
+ ## Runtime compatibility
16
+
17
+ Unlike upstream agenda 6.x (ESM-only, Node 18+), this fork ships **dual ESM + CommonJS builds** with an ES2020 emit target, so it also works on older runtimes:
18
+
19
+ - **Node.js**: 14.13.1+ (CI smoke-tests the built package on Node 14.21).
20
+ - **Meteor 2.x**: supported. Meteor 2.16 runs apps on Node 14.21 with CommonJS module loading — `require('@quave/agenda')` resolves the CJS build. CI runs a full Meteor-style simulation (Node 14 + CJS + mongodb driver 4.17) on every push.
21
+ - Modern ESM projects get the ESM build via the `exports` map automatically.
22
+
23
+ ## Agenda 6.x
24
+
25
+ Agenda 6.x is a complete TypeScript rewrite with a focus on **modularity** and **flexibility**:
26
+
27
+ - **Pluggable storage backends** - Choose from MongoDB, PostgreSQL, Redis, or implement your own. Each backend is a separate package - install only what you need.
28
+
29
+ - **Pluggable notification channels** - Move beyond polling with real-time job notifications via Redis, PostgreSQL LISTEN/NOTIFY, or other pub/sub systems. Jobs get processed immediately when saved, not on the next poll cycle.
30
+
31
+ - **Modern stack** - ESM-only, Node.js 18+, full TypeScript with strict typing.
32
+
33
+ See the [6.x Roadmap](https://github.com/agenda/agenda/issues/1610) for details and progress.
34
+
35
+ ## Features
36
+
37
+ - Minimal overhead job scheduling
38
+ - Pluggable storage backends (MongoDB, PostgreSQL, Redis)
39
+ - TypeScript support with full typing
40
+ - Scheduling via cron or human-readable syntax
41
+ - Configurable concurrency and locking
42
+ - Real-time job notifications (optional)
43
+ - Sandboxed worker execution via fork mode
44
+ - TypeScript decorators for class-based job definitions
45
+
46
+ ## Installation
47
+
48
+ Install the core package and your preferred backend:
49
+
50
+ ```bash
51
+ # For MongoDB
52
+ npm install @quave/agenda @quave/agenda-mongodb
53
+
54
+ # For PostgreSQL
55
+ npm install agenda @agendajs/postgres-backend
56
+
57
+ # For Redis
58
+ npm install agenda @agendajs/redis-backend
59
+ ```
60
+
61
+ **Requirements:**
62
+ - Node.js 18+
63
+ - Database of your choice (MongoDB 4+, PostgreSQL, or Redis)
64
+
65
+ ## Quick Start
66
+
67
+ ```javascript
68
+ import { Agenda } from '@quave/agenda';
69
+ import { MongoBackend } from '@quave/agenda-mongodb';
70
+
71
+ const agenda = new Agenda({
72
+ backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
73
+ });
74
+
75
+ // Define a job
76
+ agenda.define('send email', async (job) => {
77
+ const { to, subject } = job.attrs.data;
78
+ await sendEmail(to, subject);
79
+ });
80
+
81
+ // Start processing
82
+ await agenda.start();
83
+
84
+ // Schedule jobs
85
+ await agenda.every('1 hour', 'send email', { to: 'user@example.com', subject: 'Hello' });
86
+ await agenda.schedule('in 5 minutes', 'send email', { to: 'admin@example.com', subject: 'Report' });
87
+ await agenda.now('send email', { to: 'support@example.com', subject: 'Urgent' });
88
+ ```
89
+
90
+ ## Official Backend Packages
91
+
92
+ | Package | Backend | Notifications | Install |
93
+ |---------|---------|---------------|---------|
94
+ | [`@quave/agenda-mongodb`](https://www.npmjs.com/package/@quave/agenda-mongodb) | MongoDB | Polling only | `npm install @quave/agenda-mongodb` |
95
+ | [`@agendajs/postgres-backend`](https://www.npmjs.com/package/@agendajs/postgres-backend) | PostgreSQL | LISTEN/NOTIFY | `npm install @agendajs/postgres-backend` |
96
+ | [`@agendajs/redis-backend`](https://www.npmjs.com/package/@agendajs/redis-backend) | Redis | Pub/Sub | `npm install @agendajs/redis-backend` |
97
+
98
+ ### Backend Capabilities
99
+
100
+ | Backend | Storage | Notifications | Notes |
101
+ |---------|:-------:|:-------------:|-------|
102
+ | **MongoDB** (`MongoBackend`) | ✅ | ❌ | Storage only. Combine with external notification channel for real-time. |
103
+ | **PostgreSQL** (`PostgresBackend`) | ✅ | ✅ | Full backend. Uses LISTEN/NOTIFY for notifications. |
104
+ | **Redis** (`RedisBackend`) | ✅ | ✅ | Full backend. Uses Pub/Sub for notifications. |
105
+ | **InMemoryNotificationChannel** | ❌ | ✅ | Notifications only. For single-process/testing. |
106
+
107
+ ## Backend Configuration
108
+
109
+ ### MongoDB
110
+
111
+ ```javascript
112
+ import { Agenda } from '@quave/agenda';
113
+ import { MongoBackend } from '@quave/agenda-mongodb';
114
+
115
+ // Via connection string
116
+ const agenda = new Agenda({
117
+ backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
118
+ });
119
+
120
+ // Via existing MongoDB connection
121
+ const agenda = new Agenda({
122
+ backend: new MongoBackend({ mongo: existingDb })
123
+ });
124
+
125
+ // With options
126
+ const agenda = new Agenda({
127
+ backend: new MongoBackend({
128
+ mongo: db,
129
+ collection: 'jobs' // Collection name (default: 'agendaJobs')
130
+ }),
131
+ processEvery: '30 seconds', // Job polling interval
132
+ maxConcurrency: 20, // Max concurrent jobs
133
+ defaultConcurrency: 5 // Default per job type
134
+ });
135
+ ```
136
+
137
+ ### PostgreSQL
138
+
139
+ ```javascript
140
+ import { Agenda } from '@quave/agenda';
141
+ import { PostgresBackend } from '@agendajs/postgres-backend';
142
+
143
+ const agenda = new Agenda({
144
+ backend: new PostgresBackend({
145
+ connectionString: 'postgresql://user:pass@localhost:5432/mydb'
146
+ })
147
+ });
148
+ ```
149
+
150
+ ### Redis
151
+
152
+ ```javascript
153
+ import { Agenda } from '@quave/agenda';
154
+ import { RedisBackend } from '@agendajs/redis-backend';
155
+
156
+ const agenda = new Agenda({
157
+ backend: new RedisBackend({
158
+ connectionString: 'redis://localhost:6379'
159
+ })
160
+ });
161
+ ```
162
+
163
+ ## Real-Time Notifications
164
+
165
+ For faster job processing across distributed systems:
166
+
167
+ ```javascript
168
+ import { Agenda, InMemoryNotificationChannel } from '@quave/agenda';
169
+ import { MongoBackend } from '@quave/agenda-mongodb';
170
+
171
+ const agenda = new Agenda({
172
+ backend: new MongoBackend({ mongo: db }),
173
+ notificationChannel: new InMemoryNotificationChannel()
174
+ });
175
+ ```
176
+
177
+ ### Mixing Storage and Notification Backends
178
+
179
+ You can use MongoDB for storage while using a different system for real-time notifications:
180
+
181
+ ```javascript
182
+ import { Agenda } from '@quave/agenda';
183
+ import { MongoBackend } from '@quave/agenda-mongodb';
184
+ import { RedisBackend } from '@agendajs/redis-backend';
185
+
186
+ // MongoDB for storage + Redis for real-time notifications
187
+ const redisBackend = new RedisBackend({ connectionString: 'redis://localhost:6379' });
188
+ const agenda = new Agenda({
189
+ backend: new MongoBackend({ mongo: db }),
190
+ notificationChannel: redisBackend.notificationChannel
191
+ });
192
+ ```
193
+
194
+ This is useful when you want MongoDB's proven durability and flexible queries for job storage, but need faster real-time notifications across multiple processes.
195
+
196
+ ## API Overview
197
+
198
+ ### Defining Jobs
199
+
200
+ ```javascript
201
+ // Simple async handler
202
+ agenda.define('my-job', async (job) => {
203
+ console.log('Processing:', job.attrs.data);
204
+ });
205
+
206
+ // With options
207
+ agenda.define('my-job', async (job) => { /* ... */ }, {
208
+ concurrency: 10,
209
+ lockLimit: 5,
210
+ lockLifetime: 10 * 60 * 1000, // 10 minutes
211
+ priority: 'high'
212
+ });
213
+ ```
214
+
215
+ ### Defining Jobs with Decorators
216
+
217
+ For a class-based approach, use TypeScript decorators:
218
+
219
+ ```typescript
220
+ import { JobsController, Define, Every, registerJobs, Job } from '@quave/agenda';
221
+
222
+ @JobsController({ namespace: 'email' })
223
+ class EmailJobs {
224
+ @Define({ concurrency: 5 })
225
+ async sendWelcome(job: Job<{ userId: string }>) {
226
+ console.log('Sending welcome to:', job.attrs.data.userId);
227
+ }
228
+
229
+ @Every('1 hour')
230
+ async cleanupBounced(job: Job) {
231
+ console.log('Cleaning up bounced emails');
232
+ }
233
+ }
234
+
235
+ registerJobs(agenda, [new EmailJobs()]);
236
+ await agenda.start();
237
+
238
+ // Schedule using namespaced name
239
+ await agenda.now('email.sendWelcome', { userId: '123' });
240
+ ```
241
+
242
+ See [Decorators Documentation](./docs/decorators.md) for full details.
243
+
244
+ ### Scheduling Jobs
245
+
246
+ ```javascript
247
+ // Run immediately
248
+ await agenda.now('my-job', { userId: '123' });
249
+
250
+ // Run at specific time
251
+ await agenda.schedule('tomorrow at noon', 'my-job', data);
252
+ await agenda.schedule(new Date('2024-12-25'), 'my-job', data);
253
+
254
+ // Run repeatedly
255
+ await agenda.every('5 minutes', 'my-job');
256
+ await agenda.every('0 * * * *', 'my-job'); // Cron syntax
257
+ ```
258
+
259
+ ### Job Control
260
+
261
+ ```javascript
262
+ // Cancel jobs matching a filter (removes from database)
263
+ await agenda.cancel({ name: 'my-job' });
264
+ await agenda.cancel({ name: 'my-job', data: { userId: 123 } });
265
+
266
+ // Cancel ALL jobs unconditionally
267
+ await agenda.cancelAll();
268
+
269
+ // Disable/enable jobs globally (by query)
270
+ await agenda.disable({ name: 'my-job' }); // Disable all jobs matching query
271
+ await agenda.enable({ name: 'my-job' }); // Enable all jobs matching query
272
+
273
+ // Disable/enable individual jobs
274
+ const job = await agenda.create('my-job', data);
275
+ job.disable();
276
+ await job.save();
277
+
278
+ // Progress tracking
279
+ agenda.define('long-job', async (job) => {
280
+ for (let i = 0; i <= 100; i += 10) {
281
+ await doWork();
282
+ await job.touch(i); // Report progress 0-100
283
+ }
284
+ });
285
+ ```
286
+
287
+ ### Stopping / Draining
288
+
289
+ ```javascript
290
+ // Stop immediately - unlocks running jobs so other workers can pick them up
291
+ await agenda.stop();
292
+
293
+ // Drain - waits for running jobs to complete before stopping
294
+ await agenda.drain();
295
+
296
+ // Drain with timeout (30 seconds) - for cloud platforms with shutdown deadlines
297
+ const result = await agenda.drain(30000);
298
+ if (result.timedOut) {
299
+ console.log(`${result.running} jobs still running after timeout`);
300
+ }
301
+
302
+ // Drain with AbortSignal - for external control
303
+ const controller = new AbortController();
304
+ setTimeout(() => controller.abort(), 30000);
305
+ await agenda.drain({ signal: controller.signal });
306
+ ```
307
+
308
+ Use `drain()` for graceful shutdowns where you want in-progress jobs to finish their work.
309
+
310
+ ### Events
311
+
312
+ ```javascript
313
+ agenda.on('start', (job) => console.log('Job started:', job.attrs.name));
314
+ agenda.on('complete', (job) => console.log('Job completed:', job.attrs.name));
315
+ agenda.on('success', (job) => console.log('Job succeeded:', job.attrs.name));
316
+ agenda.on('fail', (err, job) => console.log('Job failed:', job.attrs.name, err));
317
+
318
+ // Job-specific events
319
+ agenda.on('start:send email', (job) => { /* ... */ });
320
+ agenda.on('fail:send email', (err, job) => { /* ... */ });
321
+ ```
322
+
323
+ Use `fail` listeners to capture richer error context, such as stack traces,
324
+ without storing large payloads in `job.attrs.failReason`:
325
+
326
+ ```javascript
327
+ agenda.on('fail', async (err, job) => {
328
+ await saveJobError({
329
+ jobId: job.attrs._id,
330
+ jobName: job.attrs.name,
331
+ message: err.message,
332
+ stack: err.stack
333
+ });
334
+ });
335
+ ```
336
+
337
+ ## Custom Backend
338
+
339
+ For databases other than MongoDB, PostgreSQL, or Redis, implement `AgendaBackend`:
340
+
341
+ ```javascript
342
+ import { AgendaBackend, JobRepository } from '@quave/agenda';
343
+
344
+ class SQLiteBackend implements AgendaBackend {
345
+ readonly repository: JobRepository;
346
+ readonly notificationChannel = undefined; // Or implement NotificationChannel
347
+
348
+ async connect() { /* ... */ }
349
+ async disconnect() { /* ... */ }
350
+ }
351
+
352
+ const agenda = new Agenda({
353
+ backend: new SQLiteBackend({ path: './jobs.db' })
354
+ });
355
+ ```
356
+
357
+ See [Custom Backend Driver](https://github.com/agenda/agenda/blob/main/docs/custom-database-driver.md) for details.
358
+
359
+ ## Documentation
360
+
361
+ - [Full Documentation](https://github.com/agenda/agenda#readme)
362
+ - [Migration Guide (v5 to v6)](https://github.com/agenda/agenda/blob/main/docs/migration-guide-v6.md)
363
+ - [Custom Backend Driver](https://github.com/agenda/agenda/blob/main/docs/custom-database-driver.md)
364
+ - [TypeScript Decorators](./docs/decorators.md)
365
+ - [API Reference](https://agenda.github.io/agenda/)
366
+
367
+ ## Related Packages
368
+
369
+ **Official Backend Packages:**
370
+ - [@quave/agenda-mongodb](https://www.npmjs.com/package/@quave/agenda-mongodb) - MongoDB backend
371
+ - [@agendajs/postgres-backend](https://www.npmjs.com/package/@agendajs/postgres-backend) - PostgreSQL backend with LISTEN/NOTIFY
372
+ - [@agendajs/redis-backend](https://www.npmjs.com/package/@agendajs/redis-backend) - Redis backend with Pub/Sub
373
+
374
+ **Tools:**
375
+ - [agendash](https://www.npmjs.com/package/agendash) - Web dashboard for Agenda
376
+ - [agenda-rest](https://www.npmjs.com/package/agenda-rest) - REST API for Agenda
377
+
378
+ ## License
379
+
380
+ MIT
package/dist/Job.d.ts ADDED
@@ -0,0 +1,190 @@
1
+ import type { Agenda } from './index.js';
2
+ import { JobParameters, JobId } from './types/JobParameters.js';
3
+ import { JobPriority } from './utils/priority.js';
4
+ /**
5
+ * @class
6
+ */
7
+ export declare class Job<DATA = unknown | void> {
8
+ readonly agenda: Agenda;
9
+ private readonly byJobProcessor;
10
+ readonly attrs: JobParameters<DATA>;
11
+ /** this flag is set to true, if a job got canceled (e.g. due to a timeout or other exception),
12
+ * you can use it for long running tasks to periodically check if canceled is true,
13
+ * also touch will check if and throws that the job got canceled
14
+ */
15
+ private canceled?;
16
+ getCanceledMessage(): string | true | Error | undefined;
17
+ private forkedChild?;
18
+ cancel(error?: Error): void;
19
+ /** internal variable to ensure a job does not set unlimited numbers of setTimeouts if the job is not processed
20
+ * immediately */
21
+ gotTimerToExecute: boolean;
22
+ /**
23
+ * creates a new job object
24
+ * @param agenda
25
+ * @param args
26
+ * @param byJobProcessor
27
+ */
28
+ constructor(agenda: Agenda, args: Partial<JobParameters<void>> & {
29
+ name: string;
30
+ type: 'normal' | 'single';
31
+ }, byJobProcessor?: boolean);
32
+ constructor(agenda: Agenda, args: Partial<JobParameters<DATA>> & {
33
+ name: string;
34
+ type: 'normal' | 'single';
35
+ data: DATA;
36
+ }, byJobProcessor?: boolean);
37
+ /**
38
+ * Fields managed by the job processor that should not be saved by user code.
39
+ * These are excluded in save() to prevent race conditions between user code and the processor.
40
+ */
41
+ private static readonly PROCESSOR_MANAGED_FIELDS;
42
+ /**
43
+ * Given a job, turn it into an JobParameters object
44
+ * @param excludeProcessorFields - If true, excludes fields managed by the job processor
45
+ */
46
+ toJson(excludeProcessorFields?: boolean): JobParameters;
47
+ /**
48
+ * Sets a job to repeat every X amount of time
49
+ * @param interval
50
+ * @param options
51
+ */
52
+ repeatEvery(interval: string | number, options?: {
53
+ timezone?: string;
54
+ skipImmediate?: boolean;
55
+ }): this;
56
+ /**
57
+ * Sets a job to repeat at a specific time
58
+ * @param time
59
+ */
60
+ repeatAt(time: string): this;
61
+ /**
62
+ * Sets the start date for the job.
63
+ * The job will not run before this date.
64
+ * @param date - The start date (Date object or string parseable by Date constructor)
65
+ */
66
+ startDate(dateInput: Date | string): this;
67
+ /**
68
+ * Sets the end date for the job.
69
+ * The job will not run after this date (nextRunAt will be set to null).
70
+ * @param date - The end date (Date object or string parseable by Date constructor)
71
+ */
72
+ endDate(dateInput: Date | string): this;
73
+ /**
74
+ * Sets the days of the week to skip.
75
+ * The job will not run on these days.
76
+ * @param days - Array of days to skip (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
77
+ */
78
+ skipDays(days: number[]): this;
79
+ /**
80
+ * if set, a job is forked via node child process and runs in a seperate/own
81
+ * thread
82
+ * @param enableForkMode
83
+ */
84
+ forkMode(enableForkMode: boolean): this;
85
+ /**
86
+ * Prevents the job from running
87
+ */
88
+ disable(): this;
89
+ /**
90
+ * Allows job to run
91
+ */
92
+ enable(): this;
93
+ /**
94
+ * Data to ensure is unique for job to be created
95
+ * @param unique
96
+ * @param opts
97
+ */
98
+ unique(unique: Required<JobParameters<DATA>>['unique'], opts?: JobParameters['uniqueOpts']): this;
99
+ /**
100
+ * Configure debounce behavior for this job.
101
+ * Debouncing delays job execution and resets the timer on subsequent saves,
102
+ * ensuring the job only runs once after a quiet period.
103
+ *
104
+ * IMPORTANT: Requires a unique constraint to be set via `.unique()`.
105
+ * The unique constraint identifies which jobs should be debounced together.
106
+ *
107
+ * @param delay - Debounce window in milliseconds
108
+ * @param options - Optional configuration (maxWait, strategy)
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // Basic trailing debounce - execute 2s after last save
113
+ * await agenda.create('updateIndex', { entityId: 123 })
114
+ * .unique({ 'data.entityId': 123 })
115
+ * .debounce(2000)
116
+ * .save();
117
+ *
118
+ * // With maxWait - guarantee execution within 30s
119
+ * await agenda.create('syncUser', { userId: 456 })
120
+ * .unique({ 'data.userId': 456 })
121
+ * .debounce(5000, { maxWait: 30000 })
122
+ * .save();
123
+ *
124
+ * // Leading strategy - execute immediately, ignore subsequent calls
125
+ * await agenda.create('notify', { channel: '#alerts' })
126
+ * .unique({ 'data.channel': '#alerts' })
127
+ * .debounce(60000, { strategy: 'leading' })
128
+ * .save();
129
+ * ```
130
+ */
131
+ debounce(delay: number, options?: {
132
+ maxWait?: number;
133
+ strategy?: 'trailing' | 'leading';
134
+ }): this;
135
+ /**
136
+ * Schedules a job to run at specified time.
137
+ * Date constraints (startDate, endDate, skipDays) are applied if set.
138
+ * @param time
139
+ */
140
+ schedule(time: string | Date): this;
141
+ /**
142
+ * Sets priority of the job
143
+ * @param priority priority of when job should be queued
144
+ */
145
+ priority(priority: JobPriority): this;
146
+ /**
147
+ * Fails the job with a reason (error) specified
148
+ *
149
+ * @param reason
150
+ */
151
+ fail(reason: Error | string): this;
152
+ private fetchStatus;
153
+ /**
154
+ * A job is running if:
155
+ * (lastRunAt exists AND lastFinishedAt does not exist)
156
+ * OR
157
+ * (lastRunAt exists AND lastFinishedAt exists but the lastRunAt is newer [in time] than lastFinishedAt)
158
+ * @returns Whether or not job is running at the moment (true for running)
159
+ */
160
+ isRunning(): Promise<boolean>;
161
+ /**
162
+ * Saves a job to database
163
+ */
164
+ save(): Promise<Job>;
165
+ /**
166
+ * Remove the job from database
167
+ */
168
+ remove(): Promise<number>;
169
+ isDead(): Promise<boolean>;
170
+ isExpired(): Promise<boolean>;
171
+ /**
172
+ * Updates "lockedAt" time so the job does not get picked up again
173
+ * @param progress 0 to 100
174
+ */
175
+ touch(progress?: number): Promise<void>;
176
+ private computeNextRunAt;
177
+ /**
178
+ * Handle automatic retry with backoff strategy
179
+ * @param error - The error that caused the failure
180
+ */
181
+ private handleRetry;
182
+ run(): Promise<void>;
183
+ runJob(): Promise<void>;
184
+ private isPromise;
185
+ }
186
+ export type JobWithId = Job & {
187
+ attrs: JobParameters & {
188
+ _id: JobId;
189
+ };
190
+ };