@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.
- package/LICENSE.md +25 -0
- package/README.md +380 -0
- package/dist/Job.d.ts +190 -0
- package/dist/Job.js +689 -0
- package/dist/Job.js.map +1 -0
- package/dist/JobProcessingQueue.d.ts +47 -0
- package/dist/JobProcessingQueue.js +107 -0
- package/dist/JobProcessingQueue.js.map +1 -0
- package/dist/JobProcessor.d.ts +72 -0
- package/dist/JobProcessor.js +512 -0
- package/dist/JobProcessor.js.map +1 -0
- package/dist/backends/index.d.ts +18 -0
- package/dist/backends/index.js +20 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/cjs/Job.d.ts +190 -0
- package/dist/cjs/Job.js +696 -0
- package/dist/cjs/Job.js.map +1 -0
- package/dist/cjs/JobProcessingQueue.d.ts +47 -0
- package/dist/cjs/JobProcessingQueue.js +111 -0
- package/dist/cjs/JobProcessingQueue.js.map +1 -0
- package/dist/cjs/JobProcessor.d.ts +72 -0
- package/dist/cjs/JobProcessor.js +519 -0
- package/dist/cjs/JobProcessor.js.map +1 -0
- package/dist/cjs/backends/index.d.ts +17 -0
- package/dist/cjs/backends/index.js +20 -0
- package/dist/cjs/backends/index.js.map +1 -0
- package/dist/cjs/decorators/Define.d.ts +33 -0
- package/dist/cjs/decorators/Define.js +47 -0
- package/dist/cjs/decorators/Define.js.map +1 -0
- package/dist/cjs/decorators/Every.d.ts +43 -0
- package/dist/cjs/decorators/Every.js +58 -0
- package/dist/cjs/decorators/Every.js.map +1 -0
- package/dist/cjs/decorators/JobsController.d.ts +43 -0
- package/dist/cjs/decorators/JobsController.js +40 -0
- package/dist/cjs/decorators/JobsController.js.map +1 -0
- package/dist/cjs/decorators/index.d.ts +41 -0
- package/dist/cjs/decorators/index.js +57 -0
- package/dist/cjs/decorators/index.js.map +1 -0
- package/dist/cjs/decorators/metadata.d.ts +50 -0
- package/dist/cjs/decorators/metadata.js +41 -0
- package/dist/cjs/decorators/metadata.js.map +1 -0
- package/dist/cjs/decorators/register.d.ts +52 -0
- package/dist/cjs/decorators/register.js +145 -0
- package/dist/cjs/decorators/register.js.map +1 -0
- package/dist/cjs/index.d.ts +498 -0
- package/dist/cjs/index.js +776 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/notifications/BaseNotificationChannel.d.ts +47 -0
- package/dist/cjs/notifications/BaseNotificationChannel.js +112 -0
- package/dist/cjs/notifications/BaseNotificationChannel.js.map +1 -0
- package/dist/cjs/notifications/InMemoryNotificationChannel.d.ts +13 -0
- package/dist/cjs/notifications/InMemoryNotificationChannel.js +37 -0
- package/dist/cjs/notifications/InMemoryNotificationChannel.js.map +1 -0
- package/dist/cjs/notifications/index.d.ts +2 -0
- package/dist/cjs/notifications/index.js +8 -0
- package/dist/cjs/notifications/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/AgendaBackend.d.ts +61 -0
- package/dist/cjs/types/AgendaBackend.js +3 -0
- package/dist/cjs/types/AgendaBackend.js.map +1 -0
- package/dist/cjs/types/AgendaConfig.d.ts +10 -0
- package/dist/cjs/types/AgendaConfig.js +3 -0
- package/dist/cjs/types/AgendaConfig.js.map +1 -0
- package/dist/cjs/types/AgendaStatus.d.ts +32 -0
- package/dist/cjs/types/AgendaStatus.js +3 -0
- package/dist/cjs/types/AgendaStatus.js.map +1 -0
- package/dist/cjs/types/DbOptions.d.ts +4 -0
- package/dist/cjs/types/DbOptions.js +3 -0
- package/dist/cjs/types/DbOptions.js.map +1 -0
- package/dist/cjs/types/DrainOptions.d.ts +37 -0
- package/dist/cjs/types/DrainOptions.js +3 -0
- package/dist/cjs/types/DrainOptions.js.map +1 -0
- package/dist/cjs/types/JobDefinition.d.ts +49 -0
- package/dist/cjs/types/JobDefinition.js +3 -0
- package/dist/cjs/types/JobDefinition.js.map +1 -0
- package/dist/cjs/types/JobLogger.d.ts +111 -0
- package/dist/cjs/types/JobLogger.js +3 -0
- package/dist/cjs/types/JobLogger.js.map +1 -0
- package/dist/cjs/types/JobParameters.d.ts +106 -0
- package/dist/cjs/types/JobParameters.js +21 -0
- package/dist/cjs/types/JobParameters.js.map +1 -0
- package/dist/cjs/types/JobQuery.d.ts +79 -0
- package/dist/cjs/types/JobQuery.js +36 -0
- package/dist/cjs/types/JobQuery.js.map +1 -0
- package/dist/cjs/types/JobRepository.d.ts +101 -0
- package/dist/cjs/types/JobRepository.js +3 -0
- package/dist/cjs/types/JobRepository.js.map +1 -0
- package/dist/cjs/types/NotificationChannel.d.ts +120 -0
- package/dist/cjs/types/NotificationChannel.js +3 -0
- package/dist/cjs/types/NotificationChannel.js.map +1 -0
- package/dist/cjs/utils/backoff.d.ts +152 -0
- package/dist/cjs/utils/backoff.js +175 -0
- package/dist/cjs/utils/backoff.js.map +1 -0
- package/dist/cjs/utils/dateConstraints.d.ts +48 -0
- package/dist/cjs/utils/dateConstraints.js +127 -0
- package/dist/cjs/utils/dateConstraints.js.map +1 -0
- package/dist/cjs/utils/isValidDate.d.ts +1 -0
- package/dist/cjs/utils/isValidDate.js +8 -0
- package/dist/cjs/utils/isValidDate.js.map +1 -0
- package/dist/cjs/utils/nextRunAt.d.ts +10 -0
- package/dist/cjs/utils/nextRunAt.js +119 -0
- package/dist/cjs/utils/nextRunAt.js.map +1 -0
- package/dist/cjs/utils/priority.d.ts +13 -0
- package/dist/cjs/utils/priority.js +23 -0
- package/dist/cjs/utils/priority.js.map +1 -0
- package/dist/cjs/utils/processEvery.d.ts +1 -0
- package/dist/cjs/utils/processEvery.js +13 -0
- package/dist/cjs/utils/processEvery.js.map +1 -0
- package/dist/cjs/utils/stack.d.ts +1 -0
- package/dist/cjs/utils/stack.js +21 -0
- package/dist/cjs/utils/stack.js.map +1 -0
- package/dist/cjs/version.d.ts +9 -0
- package/dist/cjs/version.js +13 -0
- package/dist/cjs/version.js.map +1 -0
- package/dist/decorators/Define.d.ts +33 -0
- package/dist/decorators/Define.js +44 -0
- package/dist/decorators/Define.js.map +1 -0
- package/dist/decorators/Every.d.ts +43 -0
- package/dist/decorators/Every.js +55 -0
- package/dist/decorators/Every.js.map +1 -0
- package/dist/decorators/JobsController.d.ts +43 -0
- package/dist/decorators/JobsController.js +37 -0
- package/dist/decorators/JobsController.js.map +1 -0
- package/dist/decorators/index.d.ts +41 -0
- package/dist/decorators/index.js +46 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/metadata.d.ts +50 -0
- package/dist/decorators/metadata.js +35 -0
- package/dist/decorators/metadata.js.map +1 -0
- package/dist/decorators/register.d.ts +52 -0
- package/dist/decorators/register.js +139 -0
- package/dist/decorators/register.js.map +1 -0
- package/dist/index.d.ts +498 -0
- package/dist/index.js +744 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/BaseNotificationChannel.d.ts +47 -0
- package/dist/notifications/BaseNotificationChannel.js +108 -0
- package/dist/notifications/BaseNotificationChannel.js.map +1 -0
- package/dist/notifications/InMemoryNotificationChannel.d.ts +13 -0
- package/dist/notifications/InMemoryNotificationChannel.js +33 -0
- package/dist/notifications/InMemoryNotificationChannel.js.map +1 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +3 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/types/AgendaBackend.d.ts +61 -0
- package/dist/types/AgendaBackend.js +2 -0
- package/dist/types/AgendaBackend.js.map +1 -0
- package/dist/types/AgendaConfig.d.ts +10 -0
- package/dist/types/AgendaConfig.js +2 -0
- package/dist/types/AgendaConfig.js.map +1 -0
- package/dist/types/AgendaStatus.d.ts +32 -0
- package/dist/types/AgendaStatus.js +2 -0
- package/dist/types/AgendaStatus.js.map +1 -0
- package/dist/types/DbOptions.d.ts +4 -0
- package/dist/types/DbOptions.js +2 -0
- package/dist/types/DbOptions.js.map +1 -0
- package/dist/types/DrainOptions.d.ts +37 -0
- package/dist/types/DrainOptions.js +2 -0
- package/dist/types/DrainOptions.js.map +1 -0
- package/dist/types/JobDefinition.d.ts +49 -0
- package/dist/types/JobDefinition.js +2 -0
- package/dist/types/JobDefinition.js.map +1 -0
- package/dist/types/JobLogger.d.ts +111 -0
- package/dist/types/JobLogger.js +2 -0
- package/dist/types/JobLogger.js.map +1 -0
- package/dist/types/JobParameters.d.ts +106 -0
- package/dist/types/JobParameters.js +17 -0
- package/dist/types/JobParameters.js.map +1 -0
- package/dist/types/JobQuery.d.ts +79 -0
- package/dist/types/JobQuery.js +33 -0
- package/dist/types/JobQuery.js.map +1 -0
- package/dist/types/JobRepository.d.ts +101 -0
- package/dist/types/JobRepository.js +2 -0
- package/dist/types/JobRepository.js.map +1 -0
- package/dist/types/NotificationChannel.d.ts +120 -0
- package/dist/types/NotificationChannel.js +2 -0
- package/dist/types/NotificationChannel.js.map +1 -0
- package/dist/utils/backoff.d.ts +152 -0
- package/dist/utils/backoff.js +167 -0
- package/dist/utils/backoff.js.map +1 -0
- package/dist/utils/dateConstraints.d.ts +48 -0
- package/dist/utils/dateConstraints.js +117 -0
- package/dist/utils/dateConstraints.js.map +1 -0
- package/dist/utils/isValidDate.d.ts +1 -0
- package/dist/utils/isValidDate.js +5 -0
- package/dist/utils/isValidDate.js.map +1 -0
- package/dist/utils/nextRunAt.d.ts +10 -0
- package/dist/utils/nextRunAt.js +110 -0
- package/dist/utils/nextRunAt.js.map +1 -0
- package/dist/utils/priority.d.ts +13 -0
- package/dist/utils/priority.js +20 -0
- package/dist/utils/priority.js.map +1 -0
- package/dist/utils/processEvery.d.ts +1 -0
- package/dist/utils/processEvery.js +7 -0
- package/dist/utils/processEvery.js.map +1 -0
- package/dist/utils/stack.d.ts +1 -0
- package/dist/utils/stack.js +18 -0
- package/dist/utils/stack.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +79 -0
- package/test/fixtures/forkHelper.js +40 -0
- package/test/fixtures/someJobDefinition.ts +12 -0
- package/test/shared/agenda-test-suite.ts +1876 -0
- package/test/shared/backoff-test-suite.ts +408 -0
- package/test/shared/debounce-test-suite.ts +377 -0
- package/test/shared/full-test-suite.ts +161 -0
- package/test/shared/index.ts +50 -0
- package/test/shared/joblogger-test-suite.ts +305 -0
- package/test/shared/jobprocessor-test-suite.ts +266 -0
- package/test/shared/notification-channel-test-suite.ts +471 -0
- package/test/shared/removeoncomplete-test-suite.ts +228 -0
- package/test/shared/repository-test-suite.ts +708 -0
- package/test/shared/retry-test-suite.ts +174 -0
- 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
|
+
};
|