@cleverbrush/scheduler 1.1.10 → 2.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/README.md CHANGED
@@ -1,66 +1,174 @@
1
- # Task scheduler for NodeJS
1
+ # @cleverbrush/scheduler
2
+ <!-- coverage-badge-start -->
3
+ ![Coverage](https://img.shields.io/badge/coverage-96.7%25-brightgreen)
4
+ <!-- coverage-badge-end -->
2
5
 
3
- To install the scheduler, run:
6
+ A job scheduler for Node.js that runs tasks in worker threads on configurable schedules.
7
+
8
+ ## Installation
4
9
 
5
10
  ```bash
6
- npm install @cleverbrush/scheduler
11
+ npm install @cleverbrush/scheduler
7
12
  ```
8
13
 
9
- This library makes use of Node.js `worker_threads` module to run tasks in parallel. It's tested
10
- on Node.js v16, but should work on older versions as well.
14
+ This library uses the Node.js `worker_threads` module. It is tested on Node.js v16+.
11
15
 
12
- ### An example:
16
+ ## Quick Start
13
17
 
14
18
  ```typescript
15
- import { JobScheduler } from '@cleverbrush/scheduler';
16
-
17
- const scheduler = new JobScheduler({
18
- rootFolder: '/some/path/to/your/tasks/root/folder'
19
- });
20
-
21
- scheduler.addJob({
22
- id: 'my-job-1',
23
- path: 'path/to/file/under/rootFolder/job1.js',
24
- schedule: {
25
- every: 'minute',
26
- // every 5 minutes
27
- interval: 5,
28
- timeout: 1000 * 60 * 4,
29
- maxRetries: 3
30
- }
31
- });
32
-
33
- scheduler.addJob({
34
- id: 'my-job-2',
35
- path: 'another/path/to/file/under/rootFolder/job2.js',
36
- schedule: {
37
- every: 'week',
38
- // every second week
39
- interval: 2,
40
- // at Monday, Wednesday and Friday
41
- dayOfWeek: [1, 3, 5],
42
- // at 9:30 AM (UTC)
43
- hour: 9,
44
- minute 30,
45
- timeout: 1000 * 60 * 4,
46
- maxRetries: 3
47
- }
48
- });
49
-
50
- scheduler.start();
19
+ import { JobScheduler } from '@cleverbrush/scheduler';
20
+
21
+ const scheduler = new JobScheduler({
22
+ rootFolder: '/path/to/your/jobs'
23
+ });
24
+
25
+ scheduler.addJob({
26
+ id: 'my-job-1',
27
+ path: 'job1.js',
28
+ schedule: {
29
+ every: 'minute',
30
+ interval: 5
31
+ }
32
+ });
33
+
34
+ scheduler.addJob({
35
+ id: 'my-job-2',
36
+ path: 'job2.js',
37
+ schedule: {
38
+ every: 'week',
39
+ interval: 2,
40
+ dayOfWeek: [1, 3, 5],
41
+ hour: 9,
42
+ minute: 30
43
+ },
44
+ timeout: 1000 * 60 * 4,
45
+ maxRetries: 3
46
+ });
47
+
48
+ scheduler.start();
51
49
  ```
52
50
 
53
- This will run `job1.js` and `job2.js` according to the schedules defined in the code above.
54
- You can subscribe to several events to get notified about the job status:
51
+ ## API
52
+
53
+ ### `JobScheduler`
54
+
55
+ The main class for scheduling and running jobs. Extends `EventEmitter`.
56
+
57
+ #### Constructor
55
58
 
56
59
  ```typescript
57
- scheduler.on(
58
- 'job:start',
59
- ({ jobId, instanceId, stdout, stderr, startDate, endDate }) => {
60
- console.log(`Job ${jobId} started`);
61
- }
62
- );
60
+ const scheduler = new JobScheduler(props: JobSchedulerProps);
61
+ ```
62
+
63
+ | Property | Type | Default | Description |
64
+ | --- | --- | --- | --- |
65
+ | `rootFolder` | `string` | — | Path to the folder containing job files |
66
+ | `defaultTimeZone` | `string` | `'UTC'` | Timezone used for scheduling |
67
+
68
+ #### Methods
69
+
70
+ | Method | Description |
71
+ | --- | --- |
72
+ | `start()` | Starts the scheduler |
73
+ | `stop()` | Stops the scheduler |
74
+ | `addJob(job)` | Adds a job to the scheduler |
75
+ | `removeJob(jobId)` | Removes a job by ID |
76
+ | `jobExists(jobId)` | Returns `true` if a job with the given ID exists |
77
+ | `status` | Current scheduler status: `'started'` or `'stopped'` |
78
+
79
+ #### Events
80
+
81
+ ```typescript
82
+ scheduler.on('job:start', ({ jobId, instanceId, startDate }) => {
83
+ console.log(`Job ${jobId} started`);
84
+ });
85
+
86
+ scheduler.on('job:end', ({ jobId, instanceId, startDate, endDate, stdout, stderr }) => {
87
+ console.log(`Job ${jobId} finished`);
88
+ });
89
+
90
+ scheduler.on('job:error', ({ jobId, instanceId, startDate, endDate, stdout, stderr }) => {
91
+ console.error(`Job ${jobId} failed`);
92
+ });
93
+
94
+ scheduler.on('job:timeout', ({ jobId, instanceId, startDate, endDate }) => {
95
+ console.warn(`Job ${jobId} timed out`);
96
+ });
97
+
98
+ scheduler.on('job:message', ({ jobId, instanceId, message }) => {
99
+ console.log(`Message from ${jobId}:`, message);
100
+ });
101
+ ```
102
+
103
+ ### Job Configuration
104
+
105
+ ```typescript
106
+ scheduler.addJob({
107
+ id: 'unique-job-id',
108
+ path: 'relative/path/to/job.js',
109
+ schedule: { /* see Schedule Types */ },
110
+ timeout: 60000, // timeout in milliseconds
111
+ maxRetries: 3, // retry on failure
112
+ maxConsequentFails: 10, // disable after N consecutive failures
113
+ noConcurrentRuns: true, // prevent overlapping executions
114
+ props: { key: 'value' } // data passed to the worker
115
+ });
116
+ ```
117
+
118
+ ### Schedule Types
63
119
 
64
- // other possible events are 'job:end', 'job:error', 'job:timeout', 'job:message'
65
- // passing the same parameters to callback as above
120
+ All schedules share these optional properties:
121
+
122
+ | Property | Type | Description |
123
+ | --- | --- | --- |
124
+ | `interval` | `number` | Number of periods between repeats (1–356) |
125
+ | `startsOn` | `Date` | Do not start before this date |
126
+ | `endsOn` | `Date` | Do not repeat after this date |
127
+ | `maxOccurences` | `number` | Maximum number of executions |
128
+ | `skipFirst` | `number` | Skip this many initial executions |
129
+
130
+ #### Minute
131
+
132
+ ```typescript
133
+ { every: 'minute', interval: 5 }
134
+ ```
135
+
136
+ Runs every N minutes.
137
+
138
+ #### Day
139
+
140
+ ```typescript
141
+ { every: 'day', interval: 1, hour: 9, minute: 30 }
142
+ ```
143
+
144
+ Runs every N days at the specified time.
145
+
146
+ #### Week
147
+
148
+ ```typescript
149
+ { every: 'week', interval: 2, dayOfWeek: [1, 3, 5], hour: 9, minute: 30 }
66
150
  ```
151
+
152
+ Runs every N weeks on the specified days (1 = Monday, 7 = Sunday).
153
+
154
+ #### Month
155
+
156
+ ```typescript
157
+ { every: 'month', interval: 1, day: 15, hour: 0, minute: 0 }
158
+ // or on the last day of the month:
159
+ { every: 'month', interval: 1, day: 'last', hour: 0, minute: 0 }
160
+ ```
161
+
162
+ Runs every N months on the specified day (1–28 or `'last'`).
163
+
164
+ #### Year
165
+
166
+ ```typescript
167
+ { every: 'year', interval: 1, month: 6, day: 1, hour: 12, minute: 0 }
168
+ ```
169
+
170
+ Runs every N years on the specified month (1–12) and day (1–28 or `'last'`).
171
+
172
+ ## License
173
+
174
+ BSD-3-Clause
@@ -1,8 +1,50 @@
1
- import { Schedule } from './types.js';
1
+ import type { Schedule } from './types.js';
2
+ /**
3
+ * Iterates over the dates defined by a {@link Schedule}.
4
+ *
5
+ * Given a schedule configuration (e.g. every 2 days, every week on Monday/Friday,
6
+ * every month on the 15th) the calculator produces the sequence of `Date` objects
7
+ * that match that schedule. Use {@link hasNext} / {@link next} to walk through
8
+ * the sequence.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const calc = new ScheduleCalculator({
13
+ * every: 'day',
14
+ * interval: 1,
15
+ * hour: 9,
16
+ * minute: 0,
17
+ * startsOn: new Date('2025-01-01T00:00:00Z'),
18
+ * maxOccurences: 5
19
+ * });
20
+ *
21
+ * while (calc.hasNext()) {
22
+ * const { date, index } = calc.next();
23
+ * console.log(`Run #${index} at ${date.toISOString()}`);
24
+ * }
25
+ * ```
26
+ */
2
27
  export declare class ScheduleCalculator {
3
28
  #private;
29
+ /**
30
+ * @param schedule - The schedule definition to iterate over.
31
+ * @throws If `schedule` is falsy.
32
+ */
4
33
  constructor(schedule: Schedule);
34
+ /**
35
+ * Returns `true` when the schedule has at least one more date.
36
+ *
37
+ * @param span - Optional millisecond window. When provided the method
38
+ * returns `true` only if the next date falls within `span` ms from now.
39
+ */
5
40
  hasNext(span?: number): boolean;
41
+ /**
42
+ * Advances to the next scheduled date and returns it together with
43
+ * its 1-based index in the sequence.
44
+ *
45
+ * @returns An object with the scheduled `date` and its `index`.
46
+ * @throws If the schedule has no more dates ({@link hasNext} is `false`).
47
+ */
6
48
  next(): {
7
49
  date: Date;
8
50
  index: number;
package/dist/index.d.ts CHANGED
@@ -1,13 +1,10 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import { EventEmitter } from 'events';
5
- import { Readable } from 'stream';
2
+ import { type Readable } from 'stream';
6
3
  import { Worker } from 'worker_threads';
7
- import { JobSchedulerProps, CreateJobRequest, SchedulerStatus, Schedule, Job, JobInstance, JobInstanceStatus, Schemas } from './types.js';
4
+ import { type IJobRepository } from './jobRepository.js';
8
5
  import { ScheduleCalculator } from './ScheduleCalculator.js';
9
- import { IJobRepository } from './jobRepository.js';
10
- export { ScheduleCalculator, Schedule as TaskSchedule, Schemas };
6
+ import { type CreateJobRequest, type Job, type JobInstance, type JobInstanceStatus, type JobSchedulerProps, Schedule, type SchedulerStatus, Schemas } from './types.js';
7
+ export { Schedule as TaskSchedule, ScheduleCalculator, Schemas };
11
8
  type WorkerResult = {
12
9
  status: JobInstanceStatus;
13
10
  exitCode: number;
@@ -49,7 +46,7 @@ export declare class JobScheduler extends EventEmitter implements IJobScheduler
49
46
  protected _rootFolder: string;
50
47
  protected _status: SchedulerStatus;
51
48
  protected _defaultTimezone: string;
52
- protected _checkTimer: any;
49
+ protected _checkTimer: ReturnType<typeof setTimeout> | undefined;
53
50
  protected _jobsRepository: IJobRepository;
54
51
  protected _jobProps: Map<string, any>;
55
52
  /**
@@ -63,7 +60,7 @@ export declare class JobScheduler extends EventEmitter implements IJobScheduler
63
60
  * @param {Job} job
64
61
  * @returns {Promise<ScheduleCalculator>}
65
62
  */
66
- protected getJobSchedule(job: Job): Promise<ScheduleCalculator>;
63
+ protected getJobSchedule(job: Job): Promise<ScheduleCalculator | undefined>;
67
64
  protected runWorkerWithTimeout(file: string, props: any, timeout: number): {
68
65
  promise: Promise<WorkerResult>;
69
66
  worker: Worker;