@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
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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
|
+
};
|