croner 4.1.97 → 4.2.2

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
@@ -6,7 +6,7 @@ Trigger functions and/or evaluate cron expressions in JavaScript. No dependencie
6
6
  # Croner
7
7
 
8
8
  ![Node.js CI](https://github.com/Hexagon/croner/workflows/Node.js%20CI/badge.svg?branch=master) [![npm version](https://badge.fury.io/js/croner.svg)](https://badge.fury.io/js/croner) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/4978bdbf495941c087ecb32b120f28ff)](https://www.codacy.com/gh/Hexagon/croner/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Hexagon/croner&utm_campaign=Badge_Grade)
9
- [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Hexagon/croner/blob/master/LICENSE) [![NPM Downloads](https://img.shields.io/npm/dm/croner.svg)](https://www.npmjs.org/package/croner)
9
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Hexagon/croner/blob/master/LICENSE) [![NPM Downloads](https://img.shields.io/npm/dw/croner.svg)](https://www.npmjs.org/package/croner)
10
10
  ![No dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)
11
11
 
12
12
  * Trigger functions in JavaScript using [Cron](https://en.wikipedia.org/wiki/Cron#CRON_expression) syntax.
@@ -46,17 +46,19 @@ More [examples](#examples)...
46
46
 
47
47
  Because the existing ones aren't good enough. They have serious bugs, use bloated dependencies, do not work in all environments and/or simply don't work as expected.
48
48
 
49
- Benchmark at 2022-02-01:
49
+ Benchmark at 2022-02-20:
50
50
 
51
51
  ```
52
52
  > node cron-implementation-test.js
53
53
 
54
- Test: When is next monday in october, pattern '0 0 0 * 10 1'
54
+ Test: When is 23:00 next 31st march, pattern '0 0 23 31 3 *'
55
55
 
56
- node-schedule: 2022-10-03 00:00:00 in 15.26ms
57
- node-cron: ??? in 1.076ms
58
- cron: 2022-11-07 00:00:00 in 2.923ms
59
- croner: 2022-10-03 00:00:00 in 1.774ms
56
+ node-schedule: 2022-03-31 23:00:00 in 15.379ms
57
+ node-cron: ??? in 0.339ms
58
+ Month '3' is limited to '30' days.
59
+ cron: 2022-04-01 23:00:00 in 7.785ms
60
+ croner (legacy): 2022-03-31 23:00:00 in 2.142ms
61
+ croner (default): 2022-03-31 23:00:00 in 0.672ms
60
62
  ```
61
63
 
62
64
  <details>
@@ -65,18 +67,19 @@ croner: 2022-10-03 00:00:00 in 1.774ms
65
67
  ```
66
68
  Test: When is next 15th of february, pattern '0 0 0 15 2 *'
67
69
 
68
- node-schedule: 2022-02-15 00:00:00 in 13.306ms
69
- node-cron: ??? in 1.676ms
70
- cron: 2022-03-15 00:00:00 in 6.066ms
71
- croner: 2022-02-15 00:00:00 in 0.575ms
70
+ node-schedule: 2023-02-15 00:00:00 in 43.875ms
71
+ node-cron: ??? in 2.217ms
72
+ cron: 2022-03-15 00:00:00 in 4.147ms
73
+ croner (legacy): 2023-02-15 00:00:00 in 1.72ms
74
+ croner (default): 2023-02-15 00:00:00 in 0.946ms
72
75
 
73
- Test: When is 23:00 next 31st march, pattern '0 0 23 31 3 *'
76
+ Test: When is next monday in october, pattern '0 0 0 * 10 1'
74
77
 
75
- node-schedule: 2022-03-31 23:00:00 in 18.894ms
76
- node-cron: ??? in 3.017ms
77
- Month '3' is limited to '30' days.
78
- cron: 2022-04-01 23:00:00 in 4.508ms
79
- croner: 2022-03-31 23:00:00 in 1.381ms
78
+ node-schedule: 2022-10-03 00:00:00 in 5.594ms
79
+ node-cron: ??? in 3.62ms
80
+ cron: 2022-11-07 00:00:00 in 1.658ms
81
+ croner (legacy): 2022-10-03 00:00:00 in 0.697ms
82
+ croner (default): 2022-10-03 00:00:00 in 0.546ms
80
83
  ```
81
84
 
82
85
  </details>
@@ -203,12 +206,13 @@ job.stop();
203
206
  | stopAt | undefined | String | ISO 8601 formatted datetime (2021-10-17T23:43:00)<br>in local or specified timezone |
204
207
  | paused | false | Boolean | If the job should be paused from start. |
205
208
  | context | undefined | Any | Passed as the second parameter to triggered function |
209
+ | legacyMode | false | boolean | Combine day-of-month and day-of-week using OR, default is AND |
206
210
 
207
211
  #### Pattern
208
212
 
209
213
  The expressions of Croner are very similar to the ones of Vixie Cron, with a few additions and changes listed below.
210
214
 
211
- * In croner, a combination of day-of-week and day-of-month will only trigger when both conditions match. An example: ```0 20 1 * MON``` will only trigger when monday occur the first day of any month. In Vixie Cron, it would trigger every monday AND the first day of every month. See issue [#53](https://github.com/Hexagon/croner/issues/53).
215
+ * In croner, a combination of day-of-week and day-of-month will only trigger when both conditions match. An example: ```0 20 1 * MON``` will only trigger when monday occur the first day of any month. In Vixie Cron, it would trigger every monday AND the first day of every month. Vixie style can be enabled with `legacyMode: true` from version `4.2.0`. See issue [#53](https://github.com/Hexagon/croner/issues/53) for more information.
212
216
 
213
217
  * Croner expressions support the following additional modifiers
214
218
  - *?* A question mark is substituted with croner initialization time, as an example - `? ? * * * *` would be substituted with `25 8 * * * *` if time is `<any hour>:08:25` at the time of `new Cron('? ? * * * *', <...>)`. The question mark can be used in any field.
package/dist/croner.cjs CHANGED
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Example:
14
14
  * let normalDate = new Date(); // d is a normal Date instance, with local timezone and correct utc representation
15
- * tzDate = convertTZ(d, 'America/New_York') // d is a tainted Date instance, where getHours()
15
+ * tzDate = CronTZ(d, 'America/New_York') // d is a tainted Date instance, where getHours()
16
16
  * (for example) will return local time in new york, but getUTCHours()
17
17
  * will return something irrelevant.
18
18
  *
@@ -20,10 +20,56 @@
20
20
  * @param {string} tzString - Timezone string in Europe/Stockholm format
21
21
  * @returns {Date}
22
22
  */
23
- function convertTZ(date, tzString) {
23
+ function CronTZ(date, tzString) {
24
24
  return new Date(date.toLocaleString("en-US", {timeZone: tzString}));
25
25
  }
26
26
 
27
+ /**
28
+ * @typedef {Object} CronOptions - Cron scheduler options
29
+ * @property {boolean} [paused] - Job is paused
30
+ * @property {boolean} [kill] - Job is about to be killed or killed
31
+ * @property {boolean} [catch] - Continue exection even if a unhandled error is thrown by triggered function
32
+ * @property {number} [maxRuns] - Maximum nuber of executions
33
+ * @property {string | Date} [startAt] - When to start running
34
+ * @property {string | Date} [stopAt] - When to stop running
35
+ * @property {string} [timezone] - Time zone in Europe/Stockholm format
36
+ * @property {boolean} [legacyMode] - Combine day-of-month and day-of-week using OR. Default is AND.
37
+ * @property {?} [context] - Used to pass any object to scheduled function
38
+ */
39
+
40
+ /**
41
+ * Internal function that validates options, and sets defaults
42
+ * @private
43
+ *
44
+ * @param {CronOptions} options
45
+ * @returns {CronOptions}
46
+ */
47
+ function CronOptions(options) {
48
+
49
+ // If no options are passed, create empty object
50
+ if (options === void 0) {
51
+ options = {};
52
+ }
53
+
54
+ // Keep options, or set defaults
55
+ options.legacyMode = (options.legacyMode === void 0) ? false : options.legacyMode;
56
+ options.paused = (options.paused === void 0) ? false : options.paused;
57
+ options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns;
58
+ options.catch = (options.catch === void 0) ? false : options.catch;
59
+ options.kill = false;
60
+
61
+ // startAt is set, validate it
62
+ if( options.startAt ) {
63
+ options.startAt = new CronDate(options.startAt, options.timezone);
64
+ }
65
+ if( options.stopAt ) {
66
+ options.stopAt = new CronDate(options.stopAt, options.timezone);
67
+ }
68
+
69
+ return options;
70
+
71
+ }
72
+
27
73
  /**
28
74
  * Converts date to CronDate
29
75
  * @constructor
@@ -57,7 +103,7 @@
57
103
  CronDate.prototype.fromDate = function (date) {
58
104
 
59
105
  if (this.timezone) {
60
- date = convertTZ(date, this.timezone);
106
+ date = CronTZ(date, this.timezone);
61
107
  }
62
108
 
63
109
  this.milliseconds = date.getMilliseconds();
@@ -128,10 +174,11 @@
128
174
  * @public
129
175
  *
130
176
  * @param {string} pattern - The pattern used to increment current state
177
+ * @param {CronOptions} options - Cron options used for incrementing
131
178
  * @param {boolean} [rerun=false] - If this is an internal incremental run
132
179
  * @return {CronDate|null} - Returns itself for chaining, or null if increment wasnt possible
133
180
  */
134
- CronDate.prototype.increment = function (pattern, rerun) {
181
+ CronDate.prototype.increment = function (pattern, options, rerun) {
135
182
 
136
183
  if (!rerun) {
137
184
  this.seconds += 1;
@@ -140,8 +187,7 @@
140
187
  this.milliseconds = 0;
141
188
 
142
189
  const
143
- origTime = this.getTime(),
144
-
190
+
145
191
  /**
146
192
  * Find next
147
193
  *
@@ -159,27 +205,53 @@
159
205
 
160
206
  for( let i = startPos; i < pattern[target].length; i++ ) {
161
207
 
162
- // If pattern matches and, in case of days, weekday matches, go on
163
- if( pattern[target][i] ) {
164
-
208
+ // This applies to all "levels"
209
+ let match = pattern[target][i];
210
+
211
+ // Days has a couple of special cases
212
+ if (target === "days") {
213
+
214
+ // Create a date object for the target date
215
+ let targetDate = this.getDate(true);
216
+ targetDate.setDate(i-offset);
217
+
165
218
  // Special handling for L (last day of month), when we are searching for days
166
- if (target === "days" && pattern.lastDayOfMonth) {
167
- let baseDate = this.getDate(true);
219
+ if (pattern.lastDayOfMonth) {
168
220
 
221
+ // Create a copy of targetDate
169
222
  // Set days to one day after today, if month changes, then we are at the last day of the month
170
- baseDate.setDate(i-offset+1);
171
- if (baseDate.getMonth() !== this["months"]) {
172
- this[target] = i-offset;
173
- return true;
223
+ let targetDateCopy = new Date(targetDate);
224
+ targetDateCopy.setDate(i-offset+1);
225
+
226
+ // Overwrite match if last day of month is matching
227
+ if (targetDateCopy.getMonth() !== this.months) {
228
+ match = true;
229
+ }
230
+
231
+ }
232
+
233
+ // Weekdays must also match when incrementing days
234
+ // If running in legacy mode, it is sufficient that only weekday match.
235
+ const dowMatch = pattern.daysOfWeek[targetDate.getDay()];
236
+ if (options.legacyMode) {
237
+ if (!pattern.starDayOfWeek && pattern.starDayOfMonth) {
238
+ match = dowMatch;
239
+ } else if (!pattern.starDayOfWeek && !pattern.starDayOfMonth) {
240
+ match = match || dowMatch;
174
241
  }
175
-
176
- // Normal handling
177
242
  } else {
178
- this[target] = i-offset;
179
- return true;
243
+ // dom AND dow
244
+ match = match && dowMatch;
180
245
  }
181
246
 
182
247
  }
248
+
249
+ if (match) {
250
+ this[target] = i-offset;
251
+ this.apply();
252
+ return true;
253
+ }
254
+
183
255
  }
184
256
  return false;
185
257
 
@@ -233,6 +305,7 @@
233
305
 
234
306
  // If pattern didn't provide a match, increment next value (e.g. minues)
235
307
  if(!findNext(toDo[doing][0], pattern, toDo[doing][2])) {
308
+
236
309
  this[toDo[doing][1]]++;
237
310
 
238
311
  // Reset current level and previous levels
@@ -240,6 +313,7 @@
240
313
 
241
314
  // If pattern provided a match, but changed current value ...
242
315
  } else if (currentValue !== this[toDo[doing][0]]) {
316
+
243
317
  // Reset previous levels
244
318
  resetPrevious(-1);
245
319
 
@@ -253,25 +327,8 @@
253
327
  // Gp down, seconds -> minutes -> hours -> days -> months -> year
254
328
  doing++;
255
329
  }
256
-
257
- // This is a special case for weekday, as the user isn't able to combine date/month patterns
258
- // with weekday patterns, it's just to increment days until we get a match.
259
- while (!pattern.daysOfWeek[this.getDate(true).getDay()]) {
260
- this.days += 1;
261
-
262
- // Reset everything before days
263
- doing = 2;
264
- resetPrevious();
265
- }
266
-
267
- // If anything changed, recreate this CronDate and run again without incrementing
268
- if (origTime != this.getTime()) {
269
- this.apply();
270
- return this.increment(pattern, true);
271
- } else {
272
- return this;
273
- }
274
330
 
331
+ return this;
275
332
  };
276
333
 
277
334
  /**
@@ -286,7 +343,7 @@
286
343
  if (internal || !this.timezone) {
287
344
  return targetDate;
288
345
  } else {
289
- const offset = convertTZ(targetDate, this.timezone).getTime()-targetDate.getTime();
346
+ const offset = CronTZ(targetDate, this.timezone).getTime()-targetDate.getTime();
290
347
  return new Date(targetDate.getTime()-offset);
291
348
  }
292
349
  };
@@ -402,6 +459,8 @@
402
459
  this.daysOfWeek = Array(8).fill(0); // 0-7 Where 0 = Sunday and 7=Sunday;
403
460
 
404
461
  this.lastDayOfMonth = false;
462
+ this.starDayOfMonth = false;
463
+ this.starDayOfWeek = false;
405
464
 
406
465
  this.parse();
407
466
 
@@ -431,17 +490,27 @@
431
490
  parts.unshift("0");
432
491
  }
433
492
 
434
- // Convert 'L' to '*' and add lastDayOfMonth flag,
493
+ // Convert 'L' to lastDayOfMonth flag,
435
494
  // and set days to 28,29,30,31 as those are the only days that can be the last day of month
436
- if(parts[3].toUpperCase() == "L") {
437
- parts[3] = "28,29,30,31";
495
+ if(parts[3].indexOf("L") >= 0) {
496
+ parts[3] = parts[3].replace("L","");
438
497
  this.lastDayOfMonth = true;
439
498
  }
440
499
 
500
+ // Check for starDayOfMonth
501
+ if(parts[3].toUpperCase() == "*") {
502
+ this.starDayOfMonth = true;
503
+ }
504
+
441
505
  // Replace alpha representations
442
506
  parts[4] = this.replaceAlphaMonths(parts[4]);
443
507
  parts[5] = this.replaceAlphaDays(parts[5]);
444
508
 
509
+ // Check for starDayOfWeek
510
+ if(parts[5].toUpperCase() == "*") {
511
+ this.starDayOfWeek = true;
512
+ }
513
+
445
514
  // Implement '?' in the simplest possible way - replace ? with current value, before further processing
446
515
  let initDate = new CronDate(new Date(),this.timezone).getDate(true);
447
516
 
@@ -516,7 +585,8 @@
516
585
 
517
586
  this.handleStepping(conf, type, valueIndexOffset);
518
587
 
519
- } else {
588
+ // Anything left should be a number
589
+ } else if( conf !== "" ) {
520
590
  this.handleNumber(conf, type, valueIndexOffset);
521
591
  }
522
592
 
@@ -548,6 +618,10 @@
548
618
  CronPattern.prototype.handleNumber = function (conf, type, valueIndexOffset) {
549
619
  const i = (parseInt(conf, 10) + valueIndexOffset);
550
620
 
621
+ if( isNaN(i) ) {
622
+ throw new TypeError("CronPattern: " + type + " is not a number: '" + conf + "'");
623
+ }
624
+
551
625
  if( i < 0 || i >= this[type].length ) {
552
626
  throw new TypeError("CronPattern: " + type + " value out of range: '" + conf + "'");
553
627
  }
@@ -731,18 +805,6 @@
731
805
  THE SOFTWARE.
732
806
 
733
807
  ------------------------------------------------------------------------------------ */
734
-
735
- /**
736
- * @typedef {Object} CronOptions - Cron scheduler options
737
- * @property {boolean} [paused] - Job is paused
738
- * @property {boolean} [kill] - Job is about to be killed or killed
739
- * @property {boolean} [catch] - Continue exection even if a unhandled error is thrown by triggered function
740
- * @property {number} [maxRuns] - Maximum nuber of executions
741
- * @property {string | Date} [startAt] - When to start running
742
- * @property {string | Date} [stopAt] - When to stop running
743
- * @property {string} [timezone] - Time zone in Europe/Stockholm format
744
- * @property {?} [context] - Used to pass any object to scheduled function
745
- */
746
808
 
747
809
  /**
748
810
  * Many JS engines stores the delay as a 32-bit signed integer internally.
@@ -779,22 +841,23 @@
779
841
  }
780
842
 
781
843
  /** @type {CronOptions} */
782
- this.options = this.processOptions(options);
844
+ this.options = CronOptions(options);
845
+
846
+ /** @type {CronDate|undefined} */
847
+ this.once = void 0;
848
+
849
+ /** @type {CronPattern|undefined} */
850
+ this.pattern = void 0;
783
851
 
784
852
  // Check if we got a date, or a pattern supplied as first argument
785
- if (pattern && (pattern instanceof Date)) {
786
- this.once = new CronDate(pattern, this.options.timezone);
787
- } else if (pattern && (typeof pattern === "string") && pattern.indexOf(":") > 0) {
788
- /** @type {CronDate} */
853
+ // Then set either this.once or this.pattern
854
+ if (pattern && (pattern instanceof Date || ((typeof pattern === "string") && pattern.indexOf(":") > 0))) {
789
855
  this.once = new CronDate(pattern, this.options.timezone);
790
856
  } else {
791
- /** @type {CronPattern} */
792
857
  this.pattern = new CronPattern(pattern, this.options.timezone);
793
858
  }
794
859
 
795
- /**
796
- * Allow shorthand scheduling
797
- */
860
+ // Allow shorthand scheduling
798
861
  if( func !== void 0 ) {
799
862
  this.fn = func;
800
863
  this.schedule();
@@ -804,37 +867,6 @@
804
867
 
805
868
  }
806
869
 
807
- /**
808
- * Internal function that validates options, and sets defaults
809
- * @private
810
- *
811
- * @param {CronOptions} options
812
- * @returns {CronOptions}
813
- */
814
- Cron.prototype.processOptions = function (options) {
815
-
816
- // If no options are passed, create empty object
817
- if (options === void 0) {
818
- options = {};
819
- }
820
-
821
- // Keep options, or set defaults
822
- options.paused = (options.paused === void 0) ? false : options.paused;
823
- options.maxRuns = (options.maxRuns === void 0) ? Infinity : options.maxRuns;
824
- options.catch = (options.catch === void 0) ? false : options.catch;
825
- options.kill = false;
826
-
827
- // startAt is set, validate it
828
- if( options.startAt ) {
829
- options.startAt = new CronDate(options.startAt, options.timezone);
830
- }
831
- if( options.stopAt ) {
832
- options.stopAt = new CronDate(options.stopAt, options.timezone);
833
- }
834
-
835
- return options;
836
- };
837
-
838
870
  /**
839
871
  * Find next runtime, based on supplied date. Strips milliseconds.
840
872
  *
@@ -886,40 +918,6 @@
886
918
  return this.previousrun ? this.previousrun.getDate() : null;
887
919
  };
888
920
 
889
- /**
890
- * Internal version of next. Cron needs millseconds internally, hence _next.
891
- * @private
892
- *
893
- * @param {CronDate} prev - Input pattern
894
- * @returns {CronDate | null} - Next run time
895
- */
896
- Cron.prototype._next = function (prev) {
897
-
898
- // Previous run should never be before startAt
899
- if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) {
900
- prev = this.options.startAt;
901
- }
902
-
903
- // Calculate next run according to pattern or one-off timestamp
904
- const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern);
905
-
906
- if (this.once && this.once.getTime(true) <= prev.getTime(true)) {
907
- return null;
908
-
909
- } else if ((nextRun === null) ||
910
- (this.options.maxRuns <= 0) ||
911
- (this.options.kill) ||
912
- (this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) {
913
- return null;
914
-
915
- } else {
916
- // All seem good, return next run
917
- return nextRun;
918
-
919
- }
920
-
921
- };
922
-
923
921
  /**
924
922
  * Returns number of milliseconds to next run
925
923
  * @public
@@ -1027,6 +1025,41 @@
1027
1025
 
1028
1026
  };
1029
1027
 
1028
+
1029
+ /**
1030
+ * Internal version of next. Cron needs millseconds internally, hence _next.
1031
+ * @private
1032
+ *
1033
+ * @param {CronDate} prev - Input pattern
1034
+ * @returns {CronDate | null} - Next run time
1035
+ */
1036
+ Cron.prototype._next = function (prev) {
1037
+
1038
+ // Previous run should never be before startAt
1039
+ if( this.options.startAt && prev && prev.getTime(true) < this.options.startAt.getTime(true) ) {
1040
+ prev = this.options.startAt;
1041
+ }
1042
+
1043
+ // Calculate next run according to pattern or one-off timestamp
1044
+ const nextRun = this.once || new CronDate(prev, this.options.timezone).increment(this.pattern, this.options);
1045
+
1046
+ if (this.once && this.once.getTime(true) <= prev.getTime(true)) {
1047
+ return null;
1048
+
1049
+ } else if ((nextRun === null) ||
1050
+ (this.options.maxRuns <= 0) ||
1051
+ (this.options.kill) ||
1052
+ (this.options.stopAt && nextRun.getTime(true) >= this.options.stopAt.getTime(true) )) {
1053
+ return null;
1054
+
1055
+ } else {
1056
+ // All seem good, return next run
1057
+ return nextRun;
1058
+
1059
+ }
1060
+
1061
+ };
1062
+
1030
1063
  return Cron;
1031
1064
 
1032
1065
  }));
@@ -1 +1 @@
1
- (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.Cron=factory())})(this,function(){"use strict";function convertTZ(date,tzString){return new Date(date.toLocaleString("en-US",{timeZone:tzString}))}function CronDate(date,timezone){this.timezone=timezone;if(date&&date instanceof Date){this.fromDate(date)}else if(date===void 0){this.fromDate(new Date)}else if(date&&typeof date==="string"){this.fromString(date)}else if(date instanceof CronDate){this.fromCronDate(date)}else{throw new TypeError("CronDate: Invalid type ("+typeof date+") passed as parameter to CronDate constructor")}}CronDate.prototype.fromDate=function(date){if(this.timezone){date=convertTZ(date,this.timezone)}this.milliseconds=date.getMilliseconds();this.seconds=date.getSeconds();this.minutes=date.getMinutes();this.hours=date.getHours();this.days=date.getDate();this.months=date.getMonth();this.years=date.getFullYear()};CronDate.prototype.fromCronDate=function(date){this.timezone=date.timezone;this.milliseconds=date.milliseconds;this.seconds=date.seconds;this.minutes=date.minutes;this.hours=date.hours;this.days=date.days;this.months=date.months;this.years=date.years};CronDate.prototype.apply=function(){const newDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);this.milliseconds=newDate.getMilliseconds();this.seconds=newDate.getSeconds();this.minutes=newDate.getMinutes();this.hours=newDate.getHours();this.days=newDate.getDate();this.months=newDate.getMonth();this.years=newDate.getFullYear()};CronDate.prototype.fromString=function(str){const parsedDate=this.parseISOLocal(str);if(isNaN(parsedDate)){throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date.")}this.fromDate(parsedDate)};CronDate.prototype.increment=function(pattern,rerun){if(!rerun){this.seconds+=1}this.milliseconds=0;const origTime=this.getTime(),findNext=(target,pattern,offset,override)=>{const startPos=override===void 0?this[target]+offset:0+offset;for(let i=startPos;i<pattern[target].length;i++){if(pattern[target][i]){if(target==="days"&&pattern.lastDayOfMonth){let baseDate=this.getDate(true);baseDate.setDate(i-offset+1);if(baseDate.getMonth()!==this["months"]){this[target]=i-offset;return true}}else{this[target]=i-offset;return true}}}return false},resetPrevious=offset=>{while(doing+offset>=0){findNext(toDo[doing+offset][0],pattern,toDo[doing+offset][2],0);doing--}};const toDo=[["seconds","minutes",0],["minutes","hours",0],["hours","days",0],["days","months",-1],["months","years",0]];let doing=0;while(doing<5){let currentValue=this[toDo[doing][0]];if(!findNext(toDo[doing][0],pattern,toDo[doing][2])){this[toDo[doing][1]]++;resetPrevious(0)}else if(currentValue!==this[toDo[doing][0]]){resetPrevious(-1)}if(this.years>=4e3){return null}doing++}while(!pattern.daysOfWeek[this.getDate(true).getDay()]){this.days+=1;doing=2;resetPrevious()}if(origTime!=this.getTime()){this.apply();return this.increment(pattern,true)}else{return this}};CronDate.prototype.getDate=function(internal){const targetDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);if(internal||!this.timezone){return targetDate}else{const offset=convertTZ(targetDate,this.timezone).getTime()-targetDate.getTime();return new Date(targetDate.getTime()-offset)}};CronDate.prototype.getTime=function(internal){return this.getDate(internal).getTime()};CronDate.prototype.parseISOLocal=function(dateTimeString){const dateTimeStringSplit=dateTimeString.split(/\D/);if(dateTimeStringSplit.length<6){return NaN}const year=parseInt(dateTimeStringSplit[0],10),month=parseInt(dateTimeStringSplit[1],10),day=parseInt(dateTimeStringSplit[2],10),hour=parseInt(dateTimeStringSplit[3],10),minute=parseInt(dateTimeStringSplit[4],10),second=parseInt(dateTimeStringSplit[5],10);if(isNaN(year)||isNaN(month)||isNaN(day)||isNaN(hour)||isNaN(minute)||isNaN(second)){return NaN}else{let generatedDate;if(dateTimeString.indexOf("Z")>0){generatedDate=new Date(Date.UTC(year,month-1,day,hour,minute,second));if(year==generatedDate.getUTCFullYear()&&month==generatedDate.getUTCMonth()+1&&day==generatedDate.getUTCDate()&&hour==generatedDate.getUTCHours()&&minute==generatedDate.getUTCMinutes()&&second==generatedDate.getUTCSeconds()){return generatedDate}else{return NaN}}else{generatedDate=new Date(year,month-1,day,hour,minute,second);if(year==generatedDate.getFullYear()&&month==generatedDate.getMonth()+1&&day==generatedDate.getDate()&&hour==generatedDate.getHours()&&minute==generatedDate.getMinutes()&&second==generatedDate.getSeconds()){return generatedDate}else{return NaN}}}};function CronPattern(pattern,timezone){this.pattern=pattern;this.timezone=timezone;this.seconds=Array(60).fill(0);this.minutes=Array(60).fill(0);this.hours=Array(24).fill(0);this.days=Array(31).fill(0);this.months=Array(12).fill(0);this.daysOfWeek=Array(8).fill(0);this.lastDayOfMonth=false;this.parse()}CronPattern.prototype.parse=function(){if(!(typeof this.pattern==="string"||this.pattern.constructor===String)){throw new TypeError("CronPattern: Pattern has to be of type string.")}const parts=this.pattern.trim().replace(/\s+/g," ").split(" ");if(parts.length<5||parts.length>6){throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exacly five or six space separated parts required.")}if(parts.length===5){parts.unshift("0")}if(parts[3].toUpperCase()=="L"){parts[3]="28,29,30,31";this.lastDayOfMonth=true}parts[4]=this.replaceAlphaMonths(parts[4]);parts[5]=this.replaceAlphaDays(parts[5]);let initDate=new CronDate(new Date,this.timezone).getDate(true);parts[0]=parts[0].replace("?",initDate.getSeconds());parts[1]=parts[1].replace("?",initDate.getMinutes());parts[2]=parts[2].replace("?",initDate.getHours());parts[3]=parts[3].replace("?",initDate.getDate());parts[4]=parts[4].replace("?",initDate.getMonth()+1);parts[5]=parts[5].replace("?",initDate.getDay());this.throwAtIllegalCharacters(parts);this.partToArray("seconds",parts[0],0);this.partToArray("minutes",parts[1],0);this.partToArray("hours",parts[2],0);this.partToArray("days",parts[3],-1);this.partToArray("months",parts[4],-1);this.partToArray("daysOfWeek",parts[5],0);if(this.daysOfWeek[7]){this.daysOfWeek[0]=1}};CronPattern.prototype.partToArray=function(type,conf,valueIndexOffset,recursed){const arr=this[type];if(conf==="*"){for(let i=0;i<arr.length;i++){arr[i]=1}return}const split=conf.split(",");if(split.length>1){for(let i=0;i<split.length;i++){this.partToArray(type,split[i],valueIndexOffset,true)}}else if(conf.indexOf("-")!==-1&&conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRangeWithStepping(conf,type,valueIndexOffset)}else if(conf.indexOf("-")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRange(conf,type,valueIndexOffset)}else if(conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleStepping(conf,type,valueIndexOffset)}else{this.handleNumber(conf,type,valueIndexOffset)}};CronPattern.prototype.throwAtIllegalCharacters=function(parts){const reValidCron=/[^/*0-9,-]+/;for(let i=0;i<parts.length;i++){if(reValidCron.test(parts[i])){throw new TypeError("CronPattern: configuration entry "+i+" ("+parts[i]+") contains illegal characters.")}}};CronPattern.prototype.handleNumber=function(conf,type,valueIndexOffset){const i=parseInt(conf,10)+valueIndexOffset;if(i<0||i>=this[type].length){throw new TypeError("CronPattern: "+type+" value out of range: '"+conf+"'")}this[type][i]=1};CronPattern.prototype.handleRangeWithStepping=function(conf,type,valueIndexOffset){const matches=conf.match(/^(\d+)-(\d+)\/(\d+)$/);if(matches===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+conf+"'");let[,lower,upper,steps]=matches;lower=parseInt(lower,10)+valueIndexOffset;upper=parseInt(upper,10)+valueIndexOffset;steps=parseInt(steps,10);if(isNaN(lower))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(upper))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");if(lower<0||upper>=this[type].length)throw new TypeError("CronPattern: Value out of range: '"+conf+"'");if(lower>upper)throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'");for(let i=lower;i<=upper;i+=steps){this[type][i]=1}};CronPattern.prototype.handleRange=function(conf,type,valueIndexOffset){const split=conf.split("-");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal range: '"+conf+"'")}const lower=parseInt(split[0],10)+valueIndexOffset,upper=parseInt(split[1],10)+valueIndexOffset;if(isNaN(lower)){throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)")}else if(isNaN(upper)){throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)")}if(lower<0||upper>=this[type].length){throw new TypeError("CronPattern: Value out of range: '"+conf+"'")}if(lower>upper){throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'")}for(let i=lower;i<=upper;i++){this[type][i]=1}};CronPattern.prototype.handleStepping=function(conf,type){const split=conf.split("/");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+conf+"'")}let start=0;if(split[0]!=="*"){start=parseInt(split[0],10)}const steps=parseInt(split[1],10);if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");for(let i=start;i<this[type].length;i+=steps){this[type][i]=1}};CronPattern.prototype.replaceAlphaDays=function(conf){return conf.replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};CronPattern.prototype.replaceAlphaMonths=function(conf){return conf.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};const maxDelay=Math.pow(2,32-1)-1;function Cron(pattern,options,func){if(!(this instanceof Cron)){return new Cron(pattern,options,func)}if(typeof options==="function"){func=options;options=void 0}this.options=this.processOptions(options);if(pattern&&pattern instanceof Date){this.once=new CronDate(pattern,this.options.timezone)}else if(pattern&&typeof pattern==="string"&&pattern.indexOf(":")>0){this.once=new CronDate(pattern,this.options.timezone)}else{this.pattern=new CronPattern(pattern,this.options.timezone)}if(func!==void 0){this.fn=func;this.schedule()}return this}Cron.prototype.processOptions=function(options){if(options===void 0){options={}}options.paused=options.paused===void 0?false:options.paused;options.maxRuns=options.maxRuns===void 0?Infinity:options.maxRuns;options.catch=options.catch===void 0?false:options.catch;options.kill=false;if(options.startAt){options.startAt=new CronDate(options.startAt,options.timezone)}if(options.stopAt){options.stopAt=new CronDate(options.stopAt,options.timezone)}return options};Cron.prototype.next=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);return next?next.getDate():null};Cron.prototype.enumerate=function(n,previous){let enumeration=[];while(n--&&(previous=this.next(previous))){enumeration.push(previous)}return enumeration};Cron.prototype.running=function(){const msLeft=this.msToNext(this.previousrun);const running=!this.options.paused&&this.fn!==void 0;return msLeft!==null&&running};Cron.prototype.previous=function(){return this.previousrun?this.previousrun.getDate():null};Cron.prototype._next=function(prev){if(this.options.startAt&&prev&&prev.getTime(true)<this.options.startAt.getTime(true)){prev=this.options.startAt}const nextRun=this.once||new CronDate(prev,this.options.timezone).increment(this.pattern);if(this.once&&this.once.getTime(true)<=prev.getTime(true)){return null}else if(nextRun===null||this.options.maxRuns<=0||this.options.kill||this.options.stopAt&&nextRun.getTime(true)>=this.options.stopAt.getTime(true)){return null}else{return nextRun}};Cron.prototype.msToNext=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);if(next){return next.getTime(true)-prev.getTime(true)}else{return null}};Cron.prototype.stop=function(){this.options.kill=true;if(this.currentTimeout){clearTimeout(this.currentTimeout)}};Cron.prototype.pause=function(){return(this.options.paused=true)&&!this.options.kill};Cron.prototype.resume=function(){return!(this.options.paused=false)&&!this.options.kill};Cron.prototype.schedule=function(func){if(func&&this.fn){throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.")}else if(func){this.fn=func}let waitMs=this.msToNext(this.previousrun);if(waitMs===null)return this;if(waitMs>maxDelay){waitMs=maxDelay}this.currentTimeout=setTimeout(()=>{if(waitMs!==maxDelay&&!this.options.paused){this.options.maxRuns--;if(this.options.catch){try{this.fn(this,this.options.context)}catch(_e){}}else{this.fn(this,this.options.context)}this.previousrun=new CronDate(void 0,this.options.timezone)}this.schedule()},waitMs);return this};return Cron});
1
+ (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,global.Cron=factory())})(this,function(){"use strict";function CronTZ(date,tzString){return new Date(date.toLocaleString("en-US",{timeZone:tzString}))}function CronOptions(options){if(options===void 0){options={}}options.legacyMode=options.legacyMode===void 0?false:options.legacyMode;options.paused=options.paused===void 0?false:options.paused;options.maxRuns=options.maxRuns===void 0?Infinity:options.maxRuns;options.catch=options.catch===void 0?false:options.catch;options.kill=false;if(options.startAt){options.startAt=new CronDate(options.startAt,options.timezone)}if(options.stopAt){options.stopAt=new CronDate(options.stopAt,options.timezone)}return options}function CronDate(date,timezone){this.timezone=timezone;if(date&&date instanceof Date){this.fromDate(date)}else if(date===void 0){this.fromDate(new Date)}else if(date&&typeof date==="string"){this.fromString(date)}else if(date instanceof CronDate){this.fromCronDate(date)}else{throw new TypeError("CronDate: Invalid type ("+typeof date+") passed as parameter to CronDate constructor")}}CronDate.prototype.fromDate=function(date){if(this.timezone){date=CronTZ(date,this.timezone)}this.milliseconds=date.getMilliseconds();this.seconds=date.getSeconds();this.minutes=date.getMinutes();this.hours=date.getHours();this.days=date.getDate();this.months=date.getMonth();this.years=date.getFullYear()};CronDate.prototype.fromCronDate=function(date){this.timezone=date.timezone;this.milliseconds=date.milliseconds;this.seconds=date.seconds;this.minutes=date.minutes;this.hours=date.hours;this.days=date.days;this.months=date.months;this.years=date.years};CronDate.prototype.apply=function(){const newDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);this.milliseconds=newDate.getMilliseconds();this.seconds=newDate.getSeconds();this.minutes=newDate.getMinutes();this.hours=newDate.getHours();this.days=newDate.getDate();this.months=newDate.getMonth();this.years=newDate.getFullYear()};CronDate.prototype.fromString=function(str){const parsedDate=this.parseISOLocal(str);if(isNaN(parsedDate)){throw new TypeError("CronDate: Provided string value for CronDate could not be parsed as date.")}this.fromDate(parsedDate)};CronDate.prototype.increment=function(pattern,options,rerun){if(!rerun){this.seconds+=1}this.milliseconds=0;const findNext=(target,pattern,offset,override)=>{const startPos=override===void 0?this[target]+offset:0+offset;for(let i=startPos;i<pattern[target].length;i++){let match=pattern[target][i];if(target==="days"){let targetDate=this.getDate(true);targetDate.setDate(i-offset);if(pattern.lastDayOfMonth){let targetDateCopy=new Date(targetDate);targetDateCopy.setDate(i-offset+1);if(targetDateCopy.getMonth()!==this.months){match=true}}const dowMatch=pattern.daysOfWeek[targetDate.getDay()];if(options.legacyMode){if(!pattern.starDayOfWeek&&pattern.starDayOfMonth){match=dowMatch}else if(!pattern.starDayOfWeek&&!pattern.starDayOfMonth){match=match||dowMatch}}else{match=match&&dowMatch}}if(match){this[target]=i-offset;this.apply();return true}}return false},resetPrevious=offset=>{while(doing+offset>=0){findNext(toDo[doing+offset][0],pattern,toDo[doing+offset][2],0);doing--}};const toDo=[["seconds","minutes",0],["minutes","hours",0],["hours","days",0],["days","months",-1],["months","years",0]];let doing=0;while(doing<5){let currentValue=this[toDo[doing][0]];if(!findNext(toDo[doing][0],pattern,toDo[doing][2])){this[toDo[doing][1]]++;resetPrevious(0)}else if(currentValue!==this[toDo[doing][0]]){resetPrevious(-1)}if(this.years>=4e3){return null}doing++}return this};CronDate.prototype.getDate=function(internal){const targetDate=new Date(this.years,this.months,this.days,this.hours,this.minutes,this.seconds,this.milliseconds);if(internal||!this.timezone){return targetDate}else{const offset=CronTZ(targetDate,this.timezone).getTime()-targetDate.getTime();return new Date(targetDate.getTime()-offset)}};CronDate.prototype.getTime=function(internal){return this.getDate(internal).getTime()};CronDate.prototype.parseISOLocal=function(dateTimeString){const dateTimeStringSplit=dateTimeString.split(/\D/);if(dateTimeStringSplit.length<6){return NaN}const year=parseInt(dateTimeStringSplit[0],10),month=parseInt(dateTimeStringSplit[1],10),day=parseInt(dateTimeStringSplit[2],10),hour=parseInt(dateTimeStringSplit[3],10),minute=parseInt(dateTimeStringSplit[4],10),second=parseInt(dateTimeStringSplit[5],10);if(isNaN(year)||isNaN(month)||isNaN(day)||isNaN(hour)||isNaN(minute)||isNaN(second)){return NaN}else{let generatedDate;if(dateTimeString.indexOf("Z")>0){generatedDate=new Date(Date.UTC(year,month-1,day,hour,minute,second));if(year==generatedDate.getUTCFullYear()&&month==generatedDate.getUTCMonth()+1&&day==generatedDate.getUTCDate()&&hour==generatedDate.getUTCHours()&&minute==generatedDate.getUTCMinutes()&&second==generatedDate.getUTCSeconds()){return generatedDate}else{return NaN}}else{generatedDate=new Date(year,month-1,day,hour,minute,second);if(year==generatedDate.getFullYear()&&month==generatedDate.getMonth()+1&&day==generatedDate.getDate()&&hour==generatedDate.getHours()&&minute==generatedDate.getMinutes()&&second==generatedDate.getSeconds()){return generatedDate}else{return NaN}}}};function CronPattern(pattern,timezone){this.pattern=pattern;this.timezone=timezone;this.seconds=Array(60).fill(0);this.minutes=Array(60).fill(0);this.hours=Array(24).fill(0);this.days=Array(31).fill(0);this.months=Array(12).fill(0);this.daysOfWeek=Array(8).fill(0);this.lastDayOfMonth=false;this.starDayOfMonth=false;this.starDayOfWeek=false;this.parse()}CronPattern.prototype.parse=function(){if(!(typeof this.pattern==="string"||this.pattern.constructor===String)){throw new TypeError("CronPattern: Pattern has to be of type string.")}const parts=this.pattern.trim().replace(/\s+/g," ").split(" ");if(parts.length<5||parts.length>6){throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exacly five or six space separated parts required.")}if(parts.length===5){parts.unshift("0")}if(parts[3].indexOf("L")>=0){parts[3]=parts[3].replace("L","");this.lastDayOfMonth=true}if(parts[3].toUpperCase()=="*"){this.starDayOfMonth=true}parts[4]=this.replaceAlphaMonths(parts[4]);parts[5]=this.replaceAlphaDays(parts[5]);if(parts[5].toUpperCase()=="*"){this.starDayOfWeek=true}let initDate=new CronDate(new Date,this.timezone).getDate(true);parts[0]=parts[0].replace("?",initDate.getSeconds());parts[1]=parts[1].replace("?",initDate.getMinutes());parts[2]=parts[2].replace("?",initDate.getHours());parts[3]=parts[3].replace("?",initDate.getDate());parts[4]=parts[4].replace("?",initDate.getMonth()+1);parts[5]=parts[5].replace("?",initDate.getDay());this.throwAtIllegalCharacters(parts);this.partToArray("seconds",parts[0],0);this.partToArray("minutes",parts[1],0);this.partToArray("hours",parts[2],0);this.partToArray("days",parts[3],-1);this.partToArray("months",parts[4],-1);this.partToArray("daysOfWeek",parts[5],0);if(this.daysOfWeek[7]){this.daysOfWeek[0]=1}};CronPattern.prototype.partToArray=function(type,conf,valueIndexOffset,recursed){const arr=this[type];if(conf==="*"){for(let i=0;i<arr.length;i++){arr[i]=1}return}const split=conf.split(",");if(split.length>1){for(let i=0;i<split.length;i++){this.partToArray(type,split[i],valueIndexOffset,true)}}else if(conf.indexOf("-")!==-1&&conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRangeWithStepping(conf,type,valueIndexOffset)}else if(conf.indexOf("-")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleRange(conf,type,valueIndexOffset)}else if(conf.indexOf("/")!==-1){if(recursed)throw new Error("CronPattern: Range with stepping cannot coexist with ,");this.handleStepping(conf,type,valueIndexOffset)}else if(conf!==""){this.handleNumber(conf,type,valueIndexOffset)}};CronPattern.prototype.throwAtIllegalCharacters=function(parts){const reValidCron=/[^/*0-9,-]+/;for(let i=0;i<parts.length;i++){if(reValidCron.test(parts[i])){throw new TypeError("CronPattern: configuration entry "+i+" ("+parts[i]+") contains illegal characters.")}}};CronPattern.prototype.handleNumber=function(conf,type,valueIndexOffset){const i=parseInt(conf,10)+valueIndexOffset;if(isNaN(i)){throw new TypeError("CronPattern: "+type+" is not a number: '"+conf+"'")}if(i<0||i>=this[type].length){throw new TypeError("CronPattern: "+type+" value out of range: '"+conf+"'")}this[type][i]=1};CronPattern.prototype.handleRangeWithStepping=function(conf,type,valueIndexOffset){const matches=conf.match(/^(\d+)-(\d+)\/(\d+)$/);if(matches===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+conf+"'");let[,lower,upper,steps]=matches;lower=parseInt(lower,10)+valueIndexOffset;upper=parseInt(upper,10)+valueIndexOffset;steps=parseInt(steps,10);if(isNaN(lower))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(upper))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");if(lower<0||upper>=this[type].length)throw new TypeError("CronPattern: Value out of range: '"+conf+"'");if(lower>upper)throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'");for(let i=lower;i<=upper;i+=steps){this[type][i]=1}};CronPattern.prototype.handleRange=function(conf,type,valueIndexOffset){const split=conf.split("-");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal range: '"+conf+"'")}const lower=parseInt(split[0],10)+valueIndexOffset,upper=parseInt(split[1],10)+valueIndexOffset;if(isNaN(lower)){throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)")}else if(isNaN(upper)){throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)")}if(lower<0||upper>=this[type].length){throw new TypeError("CronPattern: Value out of range: '"+conf+"'")}if(lower>upper){throw new TypeError("CronPattern: From value is larger than to value: '"+conf+"'")}for(let i=lower;i<=upper;i++){this[type][i]=1}};CronPattern.prototype.handleStepping=function(conf,type){const split=conf.split("/");if(split.length!==2){throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+conf+"'")}let start=0;if(split[0]!=="*"){start=parseInt(split[0],10)}const steps=parseInt(split[1],10);if(isNaN(steps))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(steps===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(steps>this[type].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[type].length+")");for(let i=start;i<this[type].length;i+=steps){this[type][i]=1}};CronPattern.prototype.replaceAlphaDays=function(conf){return conf.replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};CronPattern.prototype.replaceAlphaMonths=function(conf){return conf.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};const maxDelay=Math.pow(2,32-1)-1;function Cron(pattern,options,func){if(!(this instanceof Cron)){return new Cron(pattern,options,func)}if(typeof options==="function"){func=options;options=void 0}this.options=CronOptions(options);this.once=void 0;this.pattern=void 0;if(pattern&&(pattern instanceof Date||typeof pattern==="string"&&pattern.indexOf(":")>0)){this.once=new CronDate(pattern,this.options.timezone)}else{this.pattern=new CronPattern(pattern,this.options.timezone)}if(func!==void 0){this.fn=func;this.schedule()}return this}Cron.prototype.next=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);return next?next.getDate():null};Cron.prototype.enumerate=function(n,previous){let enumeration=[];while(n--&&(previous=this.next(previous))){enumeration.push(previous)}return enumeration};Cron.prototype.running=function(){const msLeft=this.msToNext(this.previousrun);const running=!this.options.paused&&this.fn!==void 0;return msLeft!==null&&running};Cron.prototype.previous=function(){return this.previousrun?this.previousrun.getDate():null};Cron.prototype.msToNext=function(prev){prev=new CronDate(prev,this.options.timezone);const next=this._next(prev);if(next){return next.getTime(true)-prev.getTime(true)}else{return null}};Cron.prototype.stop=function(){this.options.kill=true;if(this.currentTimeout){clearTimeout(this.currentTimeout)}};Cron.prototype.pause=function(){return(this.options.paused=true)&&!this.options.kill};Cron.prototype.resume=function(){return!(this.options.paused=false)&&!this.options.kill};Cron.prototype.schedule=function(func){if(func&&this.fn){throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.")}else if(func){this.fn=func}let waitMs=this.msToNext(this.previousrun);if(waitMs===null)return this;if(waitMs>maxDelay){waitMs=maxDelay}this.currentTimeout=setTimeout(()=>{if(waitMs!==maxDelay&&!this.options.paused){this.options.maxRuns--;if(this.options.catch){try{this.fn(this,this.options.context)}catch(_e){}}else{this.fn(this,this.options.context)}this.previousrun=new CronDate(void 0,this.options.timezone)}this.schedule()},waitMs);return this};Cron.prototype._next=function(prev){if(this.options.startAt&&prev&&prev.getTime(true)<this.options.startAt.getTime(true)){prev=this.options.startAt}const nextRun=this.once||new CronDate(prev,this.options.timezone).increment(this.pattern,this.options);if(this.once&&this.once.getTime(true)<=prev.getTime(true)){return null}else if(nextRun===null||this.options.maxRuns<=0||this.options.kill||this.options.stopAt&&nextRun.getTime(true)>=this.options.stopAt.getTime(true)){return null}else{return nextRun}};return Cron});