node-schedule 1.3.0 → 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/lib/Job.js ADDED
@@ -0,0 +1,284 @@
1
+ 'use strict';
2
+
3
+ const events = require('events')
4
+ const cronParser = require('cron-parser')
5
+ const CronDate = require('cron-parser/lib/date')
6
+ const sorted = require('sorted-array-functions')
7
+
8
+ const { scheduleNextRecurrence, scheduleInvocation, cancelInvocation, RecurrenceRule, sorter, Invocation } = require('./Invocation')
9
+ const { isValidDate } = require('./utils/dateUtils')
10
+
11
+ const scheduledJobs = {};
12
+
13
+ let anonJobCounter = 0;
14
+ function resolveAnonJobName() {
15
+ const now = new Date()
16
+ if (anonJobCounter === Number.MAX_SAFE_INTEGER) {
17
+ anonJobCounter = 0
18
+ }
19
+ anonJobCounter++
20
+
21
+ return `<Anonymous Job ${anonJobCounter} ${now.toISOString()}>`
22
+ }
23
+
24
+ function Job(name, job, callback) {
25
+ // setup a private pendingInvocations variable
26
+ this.pendingInvocations = [];
27
+
28
+ //setup a private number of invocations variable
29
+ let triggeredJobs = 0;
30
+
31
+ // Set scope vars
32
+ const jobName = name && typeof name === 'string' ? name : resolveAnonJobName();
33
+ this.job = name && typeof name === 'function' ? name : job;
34
+
35
+ // Make sure callback is actually a callback
36
+ if (this.job === name) {
37
+ // Name wasn't provided and maybe a callback is there
38
+ this.callback = typeof job === 'function' ? job : false;
39
+ } else {
40
+ // Name was provided, and maybe a callback is there
41
+ this.callback = typeof callback === 'function' ? callback : false;
42
+ }
43
+
44
+ // Check for generator
45
+ if (typeof this.job === 'function' &&
46
+ this.job.prototype &&
47
+ this.job.prototype.next) {
48
+ this.job = function() {
49
+ return this.next().value;
50
+ }.bind(this.job.call(this));
51
+ }
52
+
53
+ // define properties
54
+ Object.defineProperty(this, 'name', {
55
+ value: jobName,
56
+ writable: false,
57
+ enumerable: true
58
+ });
59
+
60
+ // method that require private access
61
+ this.trackInvocation = function(invocation) {
62
+ // add to our invocation list
63
+ sorted.add(this.pendingInvocations, invocation, sorter);
64
+ return true;
65
+ };
66
+ this.stopTrackingInvocation = function(invocation) {
67
+ const invIdx = this.pendingInvocations.indexOf(invocation);
68
+ if (invIdx > -1) {
69
+ this.pendingInvocations.splice(invIdx, 1);
70
+ return true;
71
+ }
72
+
73
+ return false;
74
+ };
75
+ this.triggeredJobs = function() {
76
+ return triggeredJobs;
77
+ };
78
+ this.setTriggeredJobs = function(triggeredJob) {
79
+ triggeredJobs = triggeredJob;
80
+ };
81
+ this.deleteFromSchedule = function() {
82
+ deleteScheduledJob(this.name)
83
+ };
84
+ this.cancel = function(reschedule) {
85
+ reschedule = (typeof reschedule == 'boolean') ? reschedule : false;
86
+
87
+ let inv, newInv;
88
+ const newInvs = [];
89
+ for (let j = 0; j < this.pendingInvocations.length; j++) {
90
+ inv = this.pendingInvocations[j];
91
+
92
+ cancelInvocation(inv);
93
+
94
+ if (reschedule && (inv.recurrenceRule.recurs || inv.recurrenceRule.next)) {
95
+ newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
96
+ if (newInv !== null) {
97
+ newInvs.push(newInv);
98
+ }
99
+ }
100
+ }
101
+
102
+ this.pendingInvocations = [];
103
+
104
+ for (let k = 0; k < newInvs.length; k++) {
105
+ this.trackInvocation(newInvs[k]);
106
+ }
107
+
108
+ // remove from scheduledJobs if reschedule === false
109
+ if (!reschedule) {
110
+ this.deleteFromSchedule()
111
+ }
112
+
113
+ return true;
114
+ };
115
+ this.cancelNext = function(reschedule) {
116
+ reschedule = (typeof reschedule == 'boolean') ? reschedule : true;
117
+
118
+ if (!this.pendingInvocations.length) {
119
+ return false;
120
+ }
121
+
122
+ let newInv;
123
+ const nextInv = this.pendingInvocations.shift();
124
+
125
+ cancelInvocation(nextInv);
126
+
127
+ if (reschedule && (nextInv.recurrenceRule.recurs || nextInv.recurrenceRule.next)) {
128
+ newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
129
+ if (newInv !== null) {
130
+ this.trackInvocation(newInv);
131
+ }
132
+ }
133
+
134
+ return true;
135
+ };
136
+ this.reschedule = function(spec) {
137
+ let inv;
138
+ const invocationsToCancel = this.pendingInvocations.slice();
139
+
140
+ for (let j = 0; j < invocationsToCancel.length; j++) {
141
+ inv = invocationsToCancel[j];
142
+
143
+ cancelInvocation(inv);
144
+ }
145
+
146
+ this.pendingInvocations = [];
147
+
148
+ if (this.schedule(spec)) {
149
+ this.setTriggeredJobs(0);
150
+ return true;
151
+ } else {
152
+ this.pendingInvocations = invocationsToCancel;
153
+ return false;
154
+ }
155
+ };
156
+ this.nextInvocation = function() {
157
+ if (!this.pendingInvocations.length) {
158
+ return null;
159
+ }
160
+ return this.pendingInvocations[0].fireDate;
161
+ };
162
+ }
163
+
164
+ Object.setPrototypeOf(Job.prototype, events.EventEmitter.prototype);
165
+
166
+ Job.prototype.invoke = function(fireDate) {
167
+ this.setTriggeredJobs(this.triggeredJobs() + 1);
168
+ return this.job(fireDate);
169
+ };
170
+
171
+ Job.prototype.runOnDate = function(date) {
172
+ return this.schedule(date);
173
+ };
174
+
175
+ Job.prototype.schedule = function(spec) {
176
+ const self = this;
177
+ let success = false;
178
+ let inv;
179
+ let start;
180
+ let end;
181
+ let tz;
182
+
183
+ // save passed-in value before 'spec' is replaced
184
+ if (typeof spec === 'object' && 'tz' in spec) {
185
+ tz = spec.tz;
186
+ }
187
+
188
+ if (typeof spec === 'object' && spec.rule) {
189
+ start = spec.start || undefined;
190
+ end = spec.end || undefined;
191
+ spec = spec.rule;
192
+
193
+ if (start) {
194
+ if (!(start instanceof Date)) {
195
+ start = new Date(start);
196
+ }
197
+
198
+ start = new CronDate(start, tz);
199
+ if (!isValidDate(start) || start.getTime() < Date.now()) {
200
+ start = undefined;
201
+ }
202
+ }
203
+
204
+ if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
205
+ end = undefined;
206
+ }
207
+
208
+ if (end) {
209
+ end = new CronDate(end, tz);
210
+ }
211
+ }
212
+
213
+ try {
214
+ const res = cronParser.parseExpression(spec, {currentDate: start, tz: tz});
215
+ inv = scheduleNextRecurrence(res, self, start, end);
216
+ if (inv !== null) {
217
+ success = self.trackInvocation(inv);
218
+ }
219
+ } catch (err) {
220
+ const type = typeof spec;
221
+ if ((type === 'string') || (type === 'number')) {
222
+ spec = new Date(spec);
223
+ }
224
+
225
+ if ((spec instanceof Date) && (isValidDate(spec))) {
226
+ spec = new CronDate(spec);
227
+ self.isOneTimeJob = true;
228
+ if (spec.getTime() >= Date.now()) {
229
+ inv = new Invocation(self, spec);
230
+ scheduleInvocation(inv);
231
+ success = self.trackInvocation(inv);
232
+ }
233
+ } else if (type === 'object') {
234
+ self.isOneTimeJob = false;
235
+ if (!(spec instanceof RecurrenceRule)) {
236
+ const r = new RecurrenceRule();
237
+ if ('year' in spec) {
238
+ r.year = spec.year;
239
+ }
240
+ if ('month' in spec) {
241
+ r.month = spec.month;
242
+ }
243
+ if ('date' in spec) {
244
+ r.date = spec.date;
245
+ }
246
+ if ('dayOfWeek' in spec) {
247
+ r.dayOfWeek = spec.dayOfWeek;
248
+ }
249
+ if ('hour' in spec) {
250
+ r.hour = spec.hour;
251
+ }
252
+ if ('minute' in spec) {
253
+ r.minute = spec.minute;
254
+ }
255
+ if ('second' in spec) {
256
+ r.second = spec.second;
257
+ }
258
+
259
+ spec = r;
260
+ }
261
+
262
+ spec.tz = tz;
263
+ inv = scheduleNextRecurrence(spec, self, start, end);
264
+ if (inv !== null) {
265
+ success = self.trackInvocation(inv);
266
+ }
267
+ }
268
+ }
269
+
270
+ scheduledJobs[this.name] = this;
271
+ return success;
272
+ };
273
+
274
+ function deleteScheduledJob(name) {
275
+ if (name) {
276
+ delete scheduledJobs[name];
277
+ }
278
+ }
279
+
280
+ module.exports = {
281
+ Job,
282
+ deleteScheduledJob,
283
+ scheduledJobs
284
+ }