@getpipher/armory-fleet 0.4.0 → 0.5.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/package.json +1 -1
- package/src/engine/run-registry.ts +13 -1
- package/src/index.ts +67 -0
- package/src/lifecycle/run-lifecycle.ts +16 -0
- package/src/panel/bg-runs-store.ts +36 -0
- package/src/panel/fleet-items.ts +36 -0
- package/src/panel/fleet-panel.ts +164 -12
- package/src/panel/rows.ts +56 -0
- package/src/runtime/async-runner.ts +123 -0
- package/src/runtime/concurrency-pool.ts +27 -0
- package/src/runtime/results-inbox.ts +45 -0
- package/src/runtime/resume.ts +48 -0
- package/src/runtime/run-journal.ts +61 -0
- package/src/scheduling/expressions.ts +60 -0
- package/src/scheduling/pid-lock.ts +43 -0
- package/src/scheduling/scheduler.ts +147 -0
- package/src/tools/fleet-results.ts +35 -0
- package/src/tools/subagent.ts +26 -0
- package/src/vendor/cron-parser/NOTICE.md +23 -0
- package/src/vendor/cron-parser/lib/date.js +79 -0
- package/src/vendor/cron-parser/lib/expression.js +614 -0
- package/src/vendor/cron-parser/lib/number.js +8 -0
- package/src/vendor/cron-parser/lib/parser.js +103 -0
- package/src/vendor/cron-parser/types.d.ts +12 -0
- package/src/worktree/diff-service.ts +40 -0
- package/src/worktree/worktree-service.ts +92 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Date class extension methods
|
|
5
|
+
*/
|
|
6
|
+
var extensions = {
|
|
7
|
+
addYear: function addYear() {
|
|
8
|
+
this.setFullYear(this.getFullYear() + 1);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
addMonth: function addMonth() {
|
|
12
|
+
this.setDate(1);
|
|
13
|
+
this.setHours(0);
|
|
14
|
+
this.setMinutes(0);
|
|
15
|
+
this.setSeconds(0);
|
|
16
|
+
this.setMonth(this.getMonth() + 1);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
addDay: function addDay() {
|
|
20
|
+
var day = this.getDate();
|
|
21
|
+
this.setDate(day + 1);
|
|
22
|
+
|
|
23
|
+
this.setHours(0);
|
|
24
|
+
this.setMinutes(0);
|
|
25
|
+
this.setSeconds(0);
|
|
26
|
+
|
|
27
|
+
if (this.getDate() === day) {
|
|
28
|
+
this.setDate(day + 2);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
addHour: function addHour() {
|
|
33
|
+
var hours = this.getHours();
|
|
34
|
+
this.setHours(hours + 1);
|
|
35
|
+
|
|
36
|
+
if (this.getHours() === hours) {
|
|
37
|
+
this.setHours(hours + 2);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.setMinutes(0);
|
|
41
|
+
this.setSeconds(0);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
addMinute: function addMinute() {
|
|
45
|
+
this.setMinutes(this.getMinutes() + 1);
|
|
46
|
+
this.setSeconds(0);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
addSecond: function addSecond() {
|
|
50
|
+
this.setSeconds(this.getSeconds() + 1);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
toUTC: function toUTC() {
|
|
54
|
+
var to = new CronDate(this);
|
|
55
|
+
var ms = to.getTime() + (to.getTimezoneOffset() * 60000);
|
|
56
|
+
to.setTime(ms);
|
|
57
|
+
return to;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Extends Javascript Date class by adding
|
|
63
|
+
* utility methods for basic date incrementation
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
function CronDate (timestamp) {
|
|
67
|
+
var date = timestamp ? new Date(timestamp) : new Date();
|
|
68
|
+
|
|
69
|
+
// Attach extensions
|
|
70
|
+
var methods = Object.keys(extensions);
|
|
71
|
+
for (var i = 0, c = methods.length; i < c; i++) {
|
|
72
|
+
var method = methods[i];
|
|
73
|
+
date[method] = extensions[method].bind(date);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return date;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = CronDate;
|
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Load Date class extensions
|
|
4
|
+
var CronDate = require('./date');
|
|
5
|
+
|
|
6
|
+
// Load fix for isNaN (IE)
|
|
7
|
+
require('./number');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Construct a new expression parser
|
|
11
|
+
*
|
|
12
|
+
* Options:
|
|
13
|
+
* currentDate: iterator start date
|
|
14
|
+
* endDate: iterator end date
|
|
15
|
+
*
|
|
16
|
+
* @constructor
|
|
17
|
+
* @private
|
|
18
|
+
* @param {Object} fields Expression fields parsed values
|
|
19
|
+
* @param {Object} options Parser options
|
|
20
|
+
*/
|
|
21
|
+
function CronExpression (fields, options) {
|
|
22
|
+
this._options = options;
|
|
23
|
+
this._currentDate = new CronDate(options.currentDate);
|
|
24
|
+
this._endDate = options.endDate ? new CronDate(options.endDate) : null;
|
|
25
|
+
this._fields = {};
|
|
26
|
+
this._isIterator = options.iterator || false;
|
|
27
|
+
this._hasIterated = false;
|
|
28
|
+
this._utc = options.utc || false;
|
|
29
|
+
|
|
30
|
+
// Map fields
|
|
31
|
+
for (var i = 0, c = CronExpression.map.length; i < c; i++) {
|
|
32
|
+
var key = CronExpression.map[i];
|
|
33
|
+
this._fields[key] = fields[i];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Field mappings
|
|
39
|
+
* @type {Array}
|
|
40
|
+
*/
|
|
41
|
+
CronExpression.map = [ 'second', 'minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek' ];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Prefined intervals
|
|
45
|
+
* @type {Object}
|
|
46
|
+
*/
|
|
47
|
+
CronExpression.predefined = {
|
|
48
|
+
'@yearly': '0 0 1 1 *',
|
|
49
|
+
'@monthly': '0 0 1 * *',
|
|
50
|
+
'@weekly': '0 0 * * 0',
|
|
51
|
+
'@daily': '0 0 * * *',
|
|
52
|
+
'@hourly': '0 * * * *'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Fields constraints
|
|
57
|
+
* @type {Array}
|
|
58
|
+
*/
|
|
59
|
+
CronExpression.constraints = [
|
|
60
|
+
[ 0, 59 ], // Second
|
|
61
|
+
[ 0, 59 ], // Minute
|
|
62
|
+
[ 0, 23 ], // Hour
|
|
63
|
+
[ 1, 31 ], // Day of month
|
|
64
|
+
[ 1, 12 ], // Month
|
|
65
|
+
[ 0, 7 ] // Day of week
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Days in month
|
|
70
|
+
* @type {number[]}
|
|
71
|
+
*/
|
|
72
|
+
CronExpression.daysInMonth = [
|
|
73
|
+
31,
|
|
74
|
+
28,
|
|
75
|
+
31,
|
|
76
|
+
30,
|
|
77
|
+
31,
|
|
78
|
+
30,
|
|
79
|
+
31,
|
|
80
|
+
31,
|
|
81
|
+
30,
|
|
82
|
+
31,
|
|
83
|
+
30,
|
|
84
|
+
31
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Field aliases
|
|
89
|
+
* @type {Object}
|
|
90
|
+
*/
|
|
91
|
+
CronExpression.aliases = {
|
|
92
|
+
month: {
|
|
93
|
+
jan: 1,
|
|
94
|
+
feb: 2,
|
|
95
|
+
mar: 3,
|
|
96
|
+
apr: 4,
|
|
97
|
+
may: 5,
|
|
98
|
+
jun: 6,
|
|
99
|
+
jul: 7,
|
|
100
|
+
aug: 8,
|
|
101
|
+
sep: 9,
|
|
102
|
+
oct: 10,
|
|
103
|
+
nov: 11,
|
|
104
|
+
dec: 12
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
dayOfWeek: {
|
|
108
|
+
sun: 0,
|
|
109
|
+
mon: 1,
|
|
110
|
+
tue: 2,
|
|
111
|
+
wed: 3,
|
|
112
|
+
thu: 4,
|
|
113
|
+
fri: 5,
|
|
114
|
+
sat: 6
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Field defaults
|
|
120
|
+
* @type {Array}
|
|
121
|
+
*/
|
|
122
|
+
CronExpression.parseDefaults = [ '0', '*', '*', '*', '*', '*' ];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Parse input interval
|
|
126
|
+
*
|
|
127
|
+
* @param {String} field Field symbolic name
|
|
128
|
+
* @param {String} value Field value
|
|
129
|
+
* @param {Array} constraints Range upper and lower constraints
|
|
130
|
+
* @return {Array} Sequence of sorted values
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
CronExpression._parseField = function _parseField (field, value, constraints) {
|
|
134
|
+
// Replace aliases
|
|
135
|
+
switch (field) {
|
|
136
|
+
case 'month':
|
|
137
|
+
case 'dayOfWeek':
|
|
138
|
+
var aliases = CronExpression.aliases[field];
|
|
139
|
+
|
|
140
|
+
value = value.replace(/[a-z]{1,3}/gi, function(match) {
|
|
141
|
+
match = match.toLowerCase();
|
|
142
|
+
|
|
143
|
+
if (typeof aliases[match] !== undefined) {
|
|
144
|
+
return aliases[match];
|
|
145
|
+
} else {
|
|
146
|
+
throw new Error('Cannot resolve alias "' + match + '"')
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check for valid characters.
|
|
153
|
+
if (!(/^[\d|/|*|\-|,]+$/.test(value))) {
|
|
154
|
+
throw new Error('Invalid characters, got value: ' + value)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Replace '*'
|
|
158
|
+
if (value.indexOf('*') !== -1) {
|
|
159
|
+
value = value.replace(/\*/g, constraints.join('-'));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
// Inline parsing functions
|
|
164
|
+
//
|
|
165
|
+
// Parser path:
|
|
166
|
+
// - parseSequence
|
|
167
|
+
// - parseRepeat
|
|
168
|
+
// - parseRange
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parse sequence
|
|
172
|
+
*
|
|
173
|
+
* @param {String} val
|
|
174
|
+
* @return {Array}
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
function parseSequence (val) {
|
|
178
|
+
var stack = [];
|
|
179
|
+
|
|
180
|
+
function handleResult (result) {
|
|
181
|
+
var max = stack.length > 0 ? Math.max.apply(Math, stack) : -1;
|
|
182
|
+
|
|
183
|
+
if (result instanceof Array) { // Make sequence linear
|
|
184
|
+
for (var i = 0, c = result.length; i < c; i++) {
|
|
185
|
+
var value = result[i];
|
|
186
|
+
|
|
187
|
+
// Check constraints
|
|
188
|
+
if (value < constraints[0] || value > constraints[1]) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
'Constraint error, got value ' + value + ' expected range ' +
|
|
191
|
+
constraints[0] + '-' + constraints[1]
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (value > max) {
|
|
196
|
+
stack.push(value);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
max = Math.max.apply(Math, stack);
|
|
200
|
+
}
|
|
201
|
+
} else { // Scalar value
|
|
202
|
+
result = +result;
|
|
203
|
+
|
|
204
|
+
// Check constraints
|
|
205
|
+
if (result < constraints[0] || result > constraints[1]) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
'Constraint error, got value ' + result + ' expected range ' +
|
|
208
|
+
constraints[0] + '-' + constraints[1]
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (field == 'dayOfWeek') {
|
|
213
|
+
result = result % 7;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (result > max) {
|
|
217
|
+
stack.push(result);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
var atoms = val.split(',');
|
|
223
|
+
if (atoms.length > 1) {
|
|
224
|
+
for (var i = 0, c = atoms.length; i < c; i++) {
|
|
225
|
+
handleResult(parseRepeat(atoms[i]));
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
handleResult(parseRepeat(val));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return stack;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Parse repetition interval
|
|
236
|
+
*
|
|
237
|
+
* @param {String} val
|
|
238
|
+
* @return {Array}
|
|
239
|
+
*/
|
|
240
|
+
function parseRepeat (val) {
|
|
241
|
+
var repeatInterval = 1;
|
|
242
|
+
var atoms = val.split('/');
|
|
243
|
+
|
|
244
|
+
if (atoms.length > 1) {
|
|
245
|
+
return parseRange(atoms[0], atoms[atoms.length - 1]);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return parseRange(val, repeatInterval);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Parse range
|
|
253
|
+
*
|
|
254
|
+
* @param {String} val
|
|
255
|
+
* @param {Number} repeatInterval Repetition interval
|
|
256
|
+
* @return {Array}
|
|
257
|
+
* @private
|
|
258
|
+
*/
|
|
259
|
+
function parseRange (val, repeatInterval) {
|
|
260
|
+
var stack = [];
|
|
261
|
+
var atoms = val.split('-');
|
|
262
|
+
|
|
263
|
+
if (atoms.length > 1 ) {
|
|
264
|
+
// Invalid range, return value
|
|
265
|
+
if (atoms.length < 2 || !atoms[0].length) {
|
|
266
|
+
return +val;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Validate range
|
|
270
|
+
var min = +atoms[0];
|
|
271
|
+
var max = +atoms[1];
|
|
272
|
+
|
|
273
|
+
if (Number.isNaN(min) || Number.isNaN(max) ||
|
|
274
|
+
min < constraints[0] || max > constraints[1]) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
'Constraint error, got range ' +
|
|
277
|
+
min + '-' + max +
|
|
278
|
+
' expected range ' +
|
|
279
|
+
constraints[0] + '-' + constraints[1]
|
|
280
|
+
);
|
|
281
|
+
} else if (min >= max) {
|
|
282
|
+
throw new Error('Invalid range: ' + val);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Create range
|
|
286
|
+
var repeatIndex = +repeatInterval;
|
|
287
|
+
|
|
288
|
+
if (Number.isNaN(repeatIndex) || repeatIndex <= 0) {
|
|
289
|
+
throw new Error('Constraint error, cannot repeat at every ' + repeatIndex + ' time.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (var index = min, count = max; index <= count; index++) {
|
|
293
|
+
if (repeatIndex > 0 && (repeatIndex % repeatInterval) === 0) {
|
|
294
|
+
repeatIndex = 1;
|
|
295
|
+
stack.push(index);
|
|
296
|
+
} else {
|
|
297
|
+
repeatIndex++;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return stack;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return +val;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return parseSequence(value);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Find next matching schedule date
|
|
312
|
+
*
|
|
313
|
+
* @return {CronDate}
|
|
314
|
+
* @private
|
|
315
|
+
*/
|
|
316
|
+
CronExpression.prototype._findSchedule = function _findSchedule () {
|
|
317
|
+
/**
|
|
318
|
+
* Match field value
|
|
319
|
+
*
|
|
320
|
+
* @param {String} value
|
|
321
|
+
* @param {Array} sequence
|
|
322
|
+
* @return {Boolean}
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
function matchSchedule (value, sequence) {
|
|
326
|
+
for (var i = 0, c = sequence.length; i < c; i++) {
|
|
327
|
+
if (sequence[i] >= value) {
|
|
328
|
+
return sequence[i] === value;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return sequence[0] === value;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Detect if input range fully matches constraint bounds
|
|
337
|
+
* @param {Array} range Input range
|
|
338
|
+
* @param {Array} constraints Input constraints
|
|
339
|
+
* @returns {Boolean}
|
|
340
|
+
* @private
|
|
341
|
+
*/
|
|
342
|
+
function isWildcardRange (range, constraints) {
|
|
343
|
+
if (range instanceof Array && !range.length) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (constraints.length !== 2) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return range.length === (constraints[1] - (constraints[0] < 1 ? - 1 : 0));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
var method = function(name) {
|
|
355
|
+
return !this._utc ? name : ('getUTC' + name.slice(3));
|
|
356
|
+
}.bind(this);
|
|
357
|
+
|
|
358
|
+
var currentDate = new CronDate(this._currentDate);
|
|
359
|
+
var endDate = this._endDate;
|
|
360
|
+
|
|
361
|
+
// TODO: Improve this part
|
|
362
|
+
// Always increment second value when second part is present
|
|
363
|
+
if (this._fields.second.length > 1 && !this._hasIterated) {
|
|
364
|
+
currentDate.addSecond();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Find matching schedule
|
|
368
|
+
while (true) {
|
|
369
|
+
// Validate timespan
|
|
370
|
+
if (endDate && (endDate.getTime() - currentDate.getTime()) < 0) {
|
|
371
|
+
throw new Error('Out of the timespan range');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Day of month and week matching:
|
|
375
|
+
//
|
|
376
|
+
// "The day of a command's execution can be specified by two fields --
|
|
377
|
+
// day of month, and day of week. If both fields are restricted (ie,
|
|
378
|
+
// aren't *), the command will be run when either field matches the cur-
|
|
379
|
+
// rent time. For example, "30 4 1,15 * 5" would cause a command to be
|
|
380
|
+
// run at 4:30 am on the 1st and 15th of each month, plus every Friday."
|
|
381
|
+
//
|
|
382
|
+
// http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
|
|
383
|
+
//
|
|
384
|
+
|
|
385
|
+
var dayOfMonthMatch = matchSchedule(currentDate[method('getDate')](), this._fields.dayOfMonth);
|
|
386
|
+
var dayOfWeekMatch = matchSchedule(currentDate[method('getDay')](), this._fields.dayOfWeek);
|
|
387
|
+
|
|
388
|
+
var isDayOfMonthWildcardMatch = isWildcardRange(this._fields.dayOfMonth, CronExpression.constraints[3]);
|
|
389
|
+
var isMonthWildcardMatch = isWildcardRange(this._fields.month, CronExpression.constraints[4]);
|
|
390
|
+
var isDayOfWeekWildcardMatch = isWildcardRange(this._fields.dayOfWeek, CronExpression.constraints[5]);
|
|
391
|
+
|
|
392
|
+
// Validate days in month if explicit value is given
|
|
393
|
+
if (!isMonthWildcardMatch) {
|
|
394
|
+
var currentYear = currentDate[method('getFullYear')]();
|
|
395
|
+
var currentMonth = currentDate[method('getMonth')]() + 1;
|
|
396
|
+
var previousMonth = currentMonth === 1 ? 11 : currentMonth - 1;
|
|
397
|
+
var daysInPreviousMonth = CronExpression.daysInMonth[previousMonth - 1];
|
|
398
|
+
var daysOfMontRangeMax = this._fields.dayOfMonth[this._fields.dayOfMonth.length - 1];
|
|
399
|
+
|
|
400
|
+
var _daysInPreviousMonth = daysInPreviousMonth;
|
|
401
|
+
var _daysOfMontRangeMax = daysOfMontRangeMax;
|
|
402
|
+
|
|
403
|
+
// Handle leap year
|
|
404
|
+
var isLeap = !((currentYear % 4) || (!(currentYear % 100) && (currentYear % 400)));
|
|
405
|
+
if (isLeap) {
|
|
406
|
+
_daysInPreviousMonth = 29;
|
|
407
|
+
_daysOfMontRangeMax = 29;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (this._fields.month[0] === previousMonth && _daysInPreviousMonth < _daysOfMontRangeMax) {
|
|
411
|
+
throw new Error('Invalid explicit day of month definition');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Add day if select day not match with month (according to calendar)
|
|
416
|
+
if (!dayOfMonthMatch || !dayOfWeekMatch) {
|
|
417
|
+
currentDate.addDay();
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Add day if not day of month is set (and no match) and day of week is wildcard
|
|
422
|
+
if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !dayOfMonthMatch) {
|
|
423
|
+
currentDate.addDay();
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Add day if not day of week is set (and no match) and day of month is wildcard
|
|
428
|
+
if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !dayOfWeekMatch) {
|
|
429
|
+
currentDate.addDay();
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Add day if day of mont and week are non-wildcard values and both doesn't match
|
|
434
|
+
if (!(isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch) &&
|
|
435
|
+
!dayOfMonthMatch && !dayOfWeekMatch) {
|
|
436
|
+
currentDate.addDay();
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Match month
|
|
441
|
+
if (!matchSchedule(currentDate[method('getMonth')]() + 1, this._fields.month)) {
|
|
442
|
+
currentDate.addMonth();
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Match hour
|
|
447
|
+
if (!matchSchedule(currentDate[method('getHours')](), this._fields.hour)) {
|
|
448
|
+
currentDate.addHour();
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Match minute
|
|
453
|
+
if (!matchSchedule(currentDate[method('getMinutes')](), this._fields.minute)) {
|
|
454
|
+
currentDate.addMinute();
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Match second
|
|
459
|
+
if (!matchSchedule(currentDate[method('getSeconds')](), this._fields.second)) {
|
|
460
|
+
currentDate.addSecond();
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// When internal date is not mutated, append one second as a padding
|
|
468
|
+
var nextDate = new CronDate(currentDate);
|
|
469
|
+
if (this._currentDate !== currentDate) {
|
|
470
|
+
nextDate.addSecond();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
this._currentDate = nextDate;
|
|
474
|
+
this._hasIterated = true;
|
|
475
|
+
|
|
476
|
+
return currentDate;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Find next suitable date
|
|
481
|
+
*
|
|
482
|
+
* @public
|
|
483
|
+
* @return {CronDate|Object}
|
|
484
|
+
*/
|
|
485
|
+
CronExpression.prototype.next = function next () {
|
|
486
|
+
var schedule = this._findSchedule();
|
|
487
|
+
|
|
488
|
+
// Try to return ES6 compatible iterator
|
|
489
|
+
if (this._isIterator) {
|
|
490
|
+
return {
|
|
491
|
+
value: schedule,
|
|
492
|
+
done: !this.hasNext()
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return schedule;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Check if next suitable date exists
|
|
501
|
+
*
|
|
502
|
+
* @public
|
|
503
|
+
* @return {Boolean}
|
|
504
|
+
*/
|
|
505
|
+
CronExpression.prototype.hasNext = function() {
|
|
506
|
+
var current = this._currentDate;
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
this.next();
|
|
510
|
+
return true;
|
|
511
|
+
} catch (err) {
|
|
512
|
+
return false;
|
|
513
|
+
} finally {
|
|
514
|
+
this._currentDate = current;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Iterate over expression iterator
|
|
520
|
+
*
|
|
521
|
+
* @public
|
|
522
|
+
* @param {Number} steps Numbers of steps to iterate
|
|
523
|
+
* @param {Function} callback Optional callback
|
|
524
|
+
* @return {Array} Array of the iterated results
|
|
525
|
+
*/
|
|
526
|
+
CronExpression.prototype.iterate = function iterate (steps, callback) {
|
|
527
|
+
var dates = [];
|
|
528
|
+
|
|
529
|
+
for (var i = 0, c = steps; i < c; i++) {
|
|
530
|
+
try {
|
|
531
|
+
var item = this.next();
|
|
532
|
+
dates.push(item);
|
|
533
|
+
|
|
534
|
+
// Fire the callback
|
|
535
|
+
if (callback) {
|
|
536
|
+
callback(item, i);
|
|
537
|
+
}
|
|
538
|
+
} catch (err) {
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return dates;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Reset expression iterator state
|
|
548
|
+
*
|
|
549
|
+
* @public
|
|
550
|
+
*/
|
|
551
|
+
CronExpression.prototype.reset = function reset () {
|
|
552
|
+
this._currentDate = new CronDate(this._options.currentDate);
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Parse input expression (async)
|
|
557
|
+
*
|
|
558
|
+
* @public
|
|
559
|
+
* @param {String} expression Input expression
|
|
560
|
+
* @param {Object} [options] Parsing options
|
|
561
|
+
* @param {Function} [callback]
|
|
562
|
+
*/
|
|
563
|
+
CronExpression.parse = function parse (expression, options, callback) {
|
|
564
|
+
if (typeof options === 'function') {
|
|
565
|
+
callback = options;
|
|
566
|
+
options = {};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function parse (expression, options) {
|
|
570
|
+
if (!options) {
|
|
571
|
+
options = {};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (!options.currentDate) {
|
|
575
|
+
options.currentDate = new CronDate();
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Is input expression predefined?
|
|
579
|
+
if (CronExpression.predefined[expression]) {
|
|
580
|
+
expression = CronExpression.predefined[expression];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Split fields
|
|
584
|
+
var fields = [];
|
|
585
|
+
var atoms = expression.split(' ');
|
|
586
|
+
|
|
587
|
+
// Resolve fields
|
|
588
|
+
var start = (CronExpression.map.length - atoms.length);
|
|
589
|
+
for (var i = 0, c = CronExpression.map.length; i < c; ++i) {
|
|
590
|
+
var field = CronExpression.map[i]; // Field name
|
|
591
|
+
var value = atoms[atoms.length > c ? i : i - start]; // Field value
|
|
592
|
+
|
|
593
|
+
if (i < start || !value) {
|
|
594
|
+
fields.push(CronExpression._parseField(
|
|
595
|
+
field,
|
|
596
|
+
CronExpression.parseDefaults[i],
|
|
597
|
+
CronExpression.constraints[i])
|
|
598
|
+
);
|
|
599
|
+
} else { // Use default value
|
|
600
|
+
fields.push(CronExpression._parseField(
|
|
601
|
+
field,
|
|
602
|
+
value,
|
|
603
|
+
CronExpression.constraints[i])
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return new CronExpression(fields, options);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return parse(expression, options);
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
module.exports = CronExpression;
|