%PDF- %PDF-
Direktori : /lib/node_modules/pm2/node_modules/croner/src/ |
Current File : //lib/node_modules/pm2/node_modules/croner/src/pattern.js |
import { CronDate } from "./date.js"; /** * Name for each part of the cron pattern * @typedef {("seconds" | "minutes" | "hours" | "days" | "months" | "daysOfWeek")} CronPatternPart */ /** * Offset, 0 or -1. * * 0 for seconds,minutes and hours as they start on 1. * -1 on days and months, as the start on 0 * * @typedef {Number} CronIndexOffset */ /** * Create a CronPattern instance from pattern string ('* * * * * *') * @constructor * @param {string} pattern - Input pattern * @param {string} timezone - Input timezone, used for '?'-substitution */ function CronPattern (pattern, timezone) { this.pattern = pattern; this.timezone = timezone; this.seconds = Array(60).fill(0); // 0-59 this.minutes = Array(60).fill(0); // 0-59 this.hours = Array(24).fill(0); // 0-23 this.days = Array(31).fill(0); // 0-30 in array, 1-31 in config this.months = Array(12).fill(0); // 0-11 in array, 1-12 in config this.daysOfWeek = Array(8).fill(0); // 0-7 Where 0 = Sunday and 7=Sunday; this.lastDayOfMonth = false; this.parse(); } /** * Parse current pattern, will throw on any type of failure * @private */ CronPattern.prototype.parse = function () { // Sanity check if( !(typeof this.pattern === "string" || this.pattern.constructor === String) ) { throw new TypeError("CronPattern: Pattern has to be of type string."); } // Split configuration on whitespace const parts = this.pattern.trim().replace(/\s+/g, " ").split(" "); // Validite number of configuration entries 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 seconds is omitted, insert 0 for seconds if( parts.length === 5) { parts.unshift("0"); } // Convert 'L' to '*' and add lastDayOfMonth flag, // and set days to 28,29,30,31 as those are the only days that can be the last day of month if(parts[3].toUpperCase() == "L") { parts[3] = "28,29,30,31"; this.lastDayOfMonth = true; } // Replace alpha representations parts[4] = this.replaceAlphaMonths(parts[4]); parts[5] = this.replaceAlphaDays(parts[5]); // Implement '?' in the simplest possible way - replace ? with current value, before further processing 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); // getMonth is zero indexed while pattern starts from 1 parts[5] = parts[5].replace("?", initDate.getDay()); // Check part content this.throwAtIllegalCharacters(parts); // Parse parts into arrays, validates as we go 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); // 0 = Sunday, 7 = Sunday if( this.daysOfWeek[7] ) { this.daysOfWeek[0] = 1; } }; /** * Convert current part (seconds/minutes etc) to an array of 1 or 0 depending on if the part is about to trigger a run or not. * @private * * @param {CronPatternPart} type - Seconds/minutes etc * @param {string} conf - Current pattern part - *, 0-1 etc * @param {CronIndexOffset} valueIndexOffset * @param {boolean} [recursed] - Is this a recursed call */ CronPattern.prototype.partToArray = function (type, conf, valueIndexOffset, recursed) { const arr = this[type]; // First off, handle wildcard if( conf === "*" ) { for( let i = 0; i < arr.length; i++ ) { arr[i] = 1; } return; } // Handle separated entries (,) by recursion const split = conf.split(","); if( split.length > 1 ) { for( let i = 0; i < split.length; i++ ) { this.partToArray(type, split[i], valueIndexOffset, true); } // Handle range with stepping (x-y/z) } 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); // Handle range } else if( conf.indexOf("-") !== -1 ) { if (recursed) throw new Error("CronPattern: Range with stepping cannot coexist with ,"); this.handleRange(conf, type, valueIndexOffset); // Handle stepping } 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); } }; /** * After converting JAN-DEC, SUN-SAT only 0-9 * , / - are allowed, throw if anything else pops up * @private * * @param {string[]} parts - Each part split as strings */ 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."); } } }; /** * Nothing but a number left, handle that * @private * * @param {string} conf - Current part, expected to be a number, as a string * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ 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; }; /** * Take care of ranges with stepping (e.g. 3-23/5) * @private * * @param {string} conf - Current part, expected to be a string like 3-23/5 * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ 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; } }; /** * Take care of ranges (e.g. 1-20) * @private * * @param {string} conf - Current part, expected to be a string like 1-20 * @param {string} type - One of "seconds", "minutes" etc * @param {number} valueIndexOffset - -1 for day of month, and month, as they start at 1. 0 for seconds, hours, minutes */ 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)"); } // Check that value is within range 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; } }; /** * Handle stepping (e.g. * / 14) * @private * * @param {string} conf - Current part, expected to be a string like * /20 (without the space) * @param {string} type - One of "seconds", "minutes" etc */ 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; } }; /** * Replace day name with day numbers * @private * * @param {string} conf - Current part, expected to be a string that might contain sun,mon etc. * * @returns {string} - conf with 0 instead of sun etc. */ 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"); }; /** * Replace month name with month numbers * @private * * @param {string} conf - Current part, expected to be a string that might contain jan,feb etc. * * @returns {string} - conf with 0 instead of sun etc. */ 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"); }; export { CronPattern };