cron-parser
Advanced tools
Comparing version 3.2.0 to 3.3.0
@@ -10,3 +10,3 @@ { | ||
"main": "lib/parser.js", | ||
"scripts": ["lib/parser.js", "lib/expression.js", "lib/date.js"] | ||
"scripts": ["lib/parser.js", "lib/expression.js", "lib/date.js", "lib/field_compactor.js", "lib/field_stringify.js"] | ||
} |
@@ -9,2 +9,4 @@ 'use strict'; | ||
var stringifyField = require('./field_stringify'); | ||
/** | ||
@@ -23,11 +25,3 @@ * Cron iteration loop safety limit | ||
function isWildcardRange(range, constraints) { | ||
if (range instanceof Array && !range.length) { | ||
return false; | ||
} | ||
if (constraints.length !== 2) { | ||
return false; | ||
} | ||
return range.length === (constraints[1] - (constraints[0] < 1 ? - 1 : 0)); | ||
return range.length === (constraints.max - (constraints.min < 1 ? - 1 : 0)); | ||
} | ||
@@ -57,3 +51,3 @@ | ||
this._nthDayOfWeek = options.nthDayOfWeek || 0; | ||
this.fields = Object.freeze(fields); | ||
this.fields = CronExpression._freezeFields(fields); | ||
} | ||
@@ -84,8 +78,8 @@ | ||
CronExpression.constraints = [ | ||
[ 0, 59 ], // Second | ||
[ 0, 59 ], // Minute | ||
[ 0, 23 ], // Hour | ||
[ 1, 31 ], // Day of month | ||
[ 1, 12 ], // Month | ||
[ 0, 7 ] // Day of week | ||
{ min: 0, max: 59, chars: [] }, // Second | ||
{ min: 0, max: 59, chars: [] }, // Minute | ||
{ min: 0, max: 23, chars: [] }, // Hour | ||
{ min: 1, max: 31, chars: ['L'] }, // Day of month | ||
{ min: 1, max: 12, chars: [] }, // Month | ||
{ min: 0, max: 7, chars: [] }, // Day of week | ||
]; | ||
@@ -180,6 +174,6 @@ | ||
if (typeof aliases[match] !== undefined) { | ||
if (typeof aliases[match] !== 'undefined') { | ||
return aliases[match]; | ||
} else { | ||
throw new Error('Cannot resolve alias "' + match + '"'); | ||
throw new Error('Validation error, cannot resolve alias "' + match + '"'); | ||
} | ||
@@ -197,5 +191,5 @@ }); | ||
if (value.indexOf('*') !== -1) { | ||
value = value.replace(/\*/g, constraints.join('-')); | ||
value = value.replace(/\*/g, constraints.min + '-' + constraints.max); | ||
} else if (value.indexOf('?') !== -1) { | ||
value = value.replace(/\?/g, constraints.join('-')); | ||
value = value.replace(/\?/g, constraints.min + '-' + constraints.max); | ||
} | ||
@@ -226,7 +220,11 @@ | ||
if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) { | ||
stack.push(value); | ||
continue; | ||
} | ||
// Check constraints | ||
if (value < constraints[0] || value > constraints[1]) { | ||
if (typeof value !== 'number' || safeIsNaN(value) || value < constraints.min || value > constraints.max) { | ||
throw new Error( | ||
'Constraint error, got value ' + value + ' expected range ' + | ||
constraints[0] + '-' + constraints[1] | ||
constraints.min + '-' + constraints.max | ||
); | ||
@@ -238,4 +236,4 @@ } | ||
} else { // Scalar value | ||
//TODO: handle the cases when there is a range and L, or list of dates and L | ||
if (field === 'dayOfMonth' && result === 'L') { | ||
if (typeof result === 'string' && constraints.chars.indexOf(result) > -1) { | ||
stack.push(result); | ||
@@ -245,17 +243,17 @@ return; | ||
result = +result; | ||
var numResult = +result; | ||
// Check constraints | ||
if (result < constraints[0] || result > constraints[1]) { | ||
if (safeIsNaN(numResult) || numResult < constraints.min || numResult > constraints.max) { | ||
throw new Error( | ||
'Constraint error, got value ' + result + ' expected range ' + | ||
constraints[0] + '-' + constraints[1] | ||
constraints.min + '-' + constraints.max | ||
); | ||
} | ||
if (field == 'dayOfWeek') { | ||
result = result % 7; | ||
if (field === 'dayOfWeek') { | ||
numResult = numResult % 7; | ||
} | ||
stack.push(result); | ||
stack.push(numResult); | ||
} | ||
@@ -279,5 +277,3 @@ } | ||
stack.sort(function(a, b) { | ||
return a - b; | ||
}); | ||
stack.sort(CronExpression._sortCompareFn); | ||
@@ -299,3 +295,3 @@ return stack; | ||
if (atoms[0] == +atoms[0]) { | ||
atoms = [atoms[0] + '-' + constraints[1], atoms[1]]; | ||
atoms = [atoms[0] + '-' + constraints.max, atoms[1]]; | ||
} | ||
@@ -339,3 +335,3 @@ return parseRange(atoms[0], atoms[atoms.length - 1]); | ||
if (safeIsNaN(min) || safeIsNaN(max) || | ||
min < constraints[0] || max > constraints[1]) { | ||
min < constraints.min || max > constraints.max) { | ||
throw new Error( | ||
@@ -345,3 +341,3 @@ 'Constraint error, got range ' + | ||
' expected range ' + | ||
constraints[0] + '-' + constraints[1] | ||
constraints.min + '-' + constraints.max | ||
); | ||
@@ -371,3 +367,3 @@ } else if (min >= max) { | ||
return isNaN(+val) ? val : +val; | ||
return safeIsNaN(+val) ? val : +val; | ||
} | ||
@@ -378,2 +374,47 @@ | ||
CronExpression._sortCompareFn = function(a, b) { | ||
var aIsNumber = typeof a === 'number'; | ||
var bIsNumber = typeof b === 'number'; | ||
if (aIsNumber && bIsNumber) { | ||
return a - b; | ||
} | ||
if (!aIsNumber && bIsNumber) { | ||
return 1; | ||
} | ||
if (aIsNumber && !bIsNumber) { | ||
return -1; | ||
} | ||
return a.localeCompare(b); | ||
}; | ||
CronExpression._handleMaxDaysInMonth = function(mappedFields) { | ||
// Filter out any day of month value that is larger than given month expects | ||
if (mappedFields.month.length === 1) { | ||
var daysInMonth = CronExpression.daysInMonth[mappedFields.month[0] - 1]; | ||
if (mappedFields.dayOfMonth[0] > daysInMonth) { | ||
throw new Error('Invalid explicit day of month definition'); | ||
} | ||
return mappedFields.dayOfMonth | ||
.filter(function(dayOfMonth) { | ||
return dayOfMonth === 'L' ? true : dayOfMonth <= daysInMonth; | ||
}) | ||
.sort(CronExpression._sortCompareFn); | ||
} | ||
}; | ||
CronExpression._freezeFields = function(fields) { | ||
for (var i = 0, c = CronExpression.map.length; i < c; ++i) { | ||
var field = CronExpression.map[i]; // Field name | ||
var value = fields[field]; | ||
fields[field] = Object.freeze(value); | ||
} | ||
return Object.freeze(fields); | ||
}; | ||
CronExpression.prototype._applyTimezoneShift = function(currentDate, dateMathVerb, method) { | ||
@@ -760,2 +801,20 @@ if ((method === 'Month') || (method === 'Day')) { | ||
/** | ||
* Stringify the expression | ||
* | ||
* @public | ||
* @param {Boolean} [includeSeconds] Should stringify seconds | ||
* @return {String} | ||
*/ | ||
CronExpression.prototype.stringify = function stringify(includeSeconds) { | ||
var resultArr = []; | ||
for (var i = includeSeconds ? 0 : 1, c = CronExpression.map.length; i < c; ++i) { | ||
var field = CronExpression.map[i]; | ||
var value = this.fields[field]; | ||
var constraint = CronExpression.constraints[i]; | ||
resultArr.push(stringifyField(value, constraint.min, constraint.max)); | ||
} | ||
return resultArr.join(' '); | ||
}; | ||
/** | ||
* Parse input expression (async) | ||
@@ -766,8 +825,6 @@ * | ||
* @param {Object} [options] Parsing options | ||
* @param {Function} [callback] | ||
*/ | ||
CronExpression.parse = function parse(expression, options, callback) { | ||
CronExpression.parse = function parse(expression, options) { | ||
var self = this; | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
@@ -808,3 +865,4 @@ } | ||
CronExpression.parseDefaults[i], | ||
CronExpression.constraints[i]) | ||
CronExpression.constraints[i] | ||
) | ||
); | ||
@@ -817,3 +875,4 @@ } else { | ||
val, | ||
CronExpression.constraints[i]) | ||
CronExpression.constraints[i] | ||
) | ||
); | ||
@@ -826,34 +885,7 @@ } | ||
var key = CronExpression.map[i]; | ||
mappedFields[key] = Object.freeze(fields[i]); | ||
mappedFields[key] = fields[i]; | ||
} | ||
// Filter out any day of month value that is larger than given month expects | ||
if (mappedFields.month.length === 1) { | ||
var daysInMonth = CronExpression.daysInMonth[mappedFields.month[0] - 1]; | ||
if (mappedFields.dayOfMonth[0] > daysInMonth) { | ||
throw new Error('Invalid explicit day of month definition'); | ||
} | ||
mappedFields.dayOfMonth = mappedFields.dayOfMonth.filter(function(dayOfMonth) { | ||
return dayOfMonth === 'L' ? true : dayOfMonth <= daysInMonth; | ||
}); | ||
//sort | ||
mappedFields.dayOfMonth.sort(function(a,b) { | ||
var aIsNumber = typeof a === 'number'; | ||
var bIsNumber = typeof b === 'number'; | ||
if (aIsNumber && bIsNumber) { | ||
return a - b; | ||
} | ||
if(!aIsNumber) { | ||
return 1; | ||
} | ||
return -1; | ||
}); | ||
} | ||
var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields); | ||
mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth; | ||
return new CronExpression(mappedFields, options); | ||
@@ -898,2 +930,62 @@ | ||
/** | ||
* Convert cron fields back to Cron Expression | ||
* | ||
* @public | ||
* @param {Object} fields Input fields | ||
* @param {Object} [options] Parsing options | ||
* @return {Object} | ||
*/ | ||
CronExpression.fieldsToExpression = function fieldsToExpression(fields, options) { | ||
function validateConstraints (field, values, constraints) { | ||
if (!values) { | ||
throw new Error('Validation error, Field ' + field + ' is missing'); | ||
} | ||
if (values.length === 0) { | ||
throw new Error('Validation error, Field ' + field + ' contains no values'); | ||
} | ||
for (var i = 0, c = values.length; i < c; i++) { | ||
var value = values[i]; | ||
if (typeof value === 'string' && constraints.chars.indexOf(value) > -1) { | ||
continue; | ||
} | ||
// Check constraints | ||
if (typeof value !== 'number' || safeIsNaN(value) || value < constraints.min || value > constraints.max) { | ||
throw new Error( | ||
'Constraint error, got value ' + value + ' expected range ' + | ||
constraints.min + '-' + constraints.max | ||
); | ||
} | ||
} | ||
} | ||
var mappedFields = {}; | ||
for (var i = 0, c = CronExpression.map.length; i < c; ++i) { | ||
var field = CronExpression.map[i]; // Field name | ||
var values = fields[field]; | ||
validateConstraints( | ||
field, | ||
values, | ||
CronExpression.constraints[i] | ||
); | ||
var copy = []; | ||
var j = -1; | ||
while (++j < values.length) { | ||
copy[j] = values[j]; | ||
} | ||
values = copy.sort(CronExpression._sortCompareFn) | ||
.filter(function(item, pos, ary) { | ||
return !pos || item !== ary[pos - 1]; | ||
}); | ||
if (values.length !== copy.length) { | ||
throw new Error('Validation error, Field ' + field + ' contains duplicate values'); | ||
} | ||
mappedFields[field] = values; | ||
} | ||
var dayOfMonth = CronExpression._handleMaxDaysInMonth(mappedFields); | ||
mappedFields.dayOfMonth = dayOfMonth || mappedFields.dayOfMonth; | ||
return new CronExpression(mappedFields, options || {}); | ||
}; | ||
module.exports = CronExpression; |
@@ -40,7 +40,19 @@ 'use strict'; | ||
*/ | ||
CronParser.parseExpression = function parseExpression (expression, options, callback) { | ||
return CronExpression.parse(expression, options, callback); | ||
CronParser.parseExpression = function parseExpression (expression, options) { | ||
return CronExpression.parse(expression, options); | ||
}; | ||
/** | ||
* Wrapper for CronExpression.fieldsToExpression method | ||
* | ||
* @public | ||
* @param {Object} fields Input fields | ||
* @param {Object} [options] Parsing options | ||
* @return {Object} | ||
*/ | ||
CronParser.fieldsToExpression = function fieldsToExpression (fields, options) { | ||
return CronExpression.fieldsToExpression(fields, options); | ||
}; | ||
/** | ||
* Parse content string | ||
@@ -53,3 +65,2 @@ * | ||
CronParser.parseString = function parseString (data) { | ||
var self = this; | ||
var blocks = data.split('\n'); | ||
@@ -77,3 +88,3 @@ | ||
try { | ||
result = self._parseEntry('0 ' + entry); | ||
result = CronParser._parseEntry('0 ' + entry); | ||
response.expressions.push(result.interval); | ||
@@ -80,0 +91,0 @@ } catch (err) { |
{ | ||
"name": "cron-parser", | ||
"version": "3.2.0", | ||
"version": "3.3.0", | ||
"description": "Node.js library for parsing crontab instructions", | ||
"main": "lib/parser.js", | ||
"types": "lib/index.d.ts", | ||
"types": "index.d.ts", | ||
"typesVersions": { | ||
"<4.1": { | ||
"*": [ | ||
"types/ts3/*" | ||
] | ||
} | ||
}, | ||
"directories": { | ||
@@ -13,2 +20,3 @@ "test": "test" | ||
"test:unit": "TZ=UTC tap ./test/*.js", | ||
"test:cover": "TZ=UTC tap --coverage-report=html ./test/*.js", | ||
"lint": "eslint .", | ||
@@ -47,3 +55,4 @@ "lint:fix": "eslint --fix .", | ||
"Ian Graves <ian+diskimage@iangrav.es>", | ||
"Andy Thompson <me@andytson.com>" | ||
"Andy Thompson <me@andytson.com>", | ||
"Regev Brody <regevbr@gmail.com>" | ||
], | ||
@@ -68,4 +77,10 @@ "license": "MIT", | ||
"tsd": { | ||
"directory": "test" | ||
"directory": "test", | ||
"compilerOptions": { | ||
"lib": [ | ||
"es2017", | ||
"dom" | ||
] | ||
} | ||
} | ||
} |
@@ -7,4 +7,8 @@ cron-parser | ||
Node.js library for parsing crontab instructions. It includes support for timezones and DST transitions. | ||
Node.js library for parsing and manipulating crontab instructions. It includes support for timezones and DST transitions. | ||
__Compatibility__ | ||
Node >= 0.8 | ||
Typescript <= 4.2 | ||
Setup | ||
@@ -25,3 +29,3 @@ ======== | ||
│ │ │ │ └───── month (1 - 12) | ||
│ │ │ └────────── day of month (1 - 31) | ||
│ │ │ └────────── day of month (1 - 31, L) | ||
│ │ └─────────────── hour (0 - 23) | ||
@@ -112,2 +116,17 @@ │ └──────────────────── minute (0 - 59) | ||
Manipulation | ||
```javascript | ||
var parser = require('cron-parser'); | ||
var interval = parser.parseExpression('0 7 * * 0-4'); | ||
var fields = JSON.parse(JSON.stringify(interval.fields)); // Fields is immutable | ||
fields.hour = [8]; | ||
fields.minute = [29]; | ||
fields.dayOfWeek = [1,3,4,5,6,7]; | ||
var modifiedInterval = parser.fieldsToExpression(fields); | ||
var cronString = modifiedInterval.stringify(); | ||
console.log(cronString); // "29 8 * * 1,3-7" | ||
``` | ||
Options | ||
@@ -114,0 +133,0 @@ ======== |
@@ -594,2 +594,10 @@ var test = require('tap').test; | ||
test('expression using days of week strings - wrong alias', function(t) { | ||
t.throws(function () { | ||
CronExpression.parse('15 10 * * MON-TUR'); | ||
}, new Error('Validation error, cannot resolve alias "tur"')); | ||
t.end(); | ||
}); | ||
test('expression using mixed days of week strings', function(t) { | ||
@@ -596,0 +604,0 @@ try { |
@@ -1,11 +0,138 @@ | ||
import {expectType} from 'tsd'; | ||
import * as CronExpression from '../lib'; | ||
import {expectAssignable, expectError, expectNotAssignable, expectType} from 'tsd'; | ||
import { | ||
CronDate, | ||
CronExpression, | ||
CronFields, DateType, | ||
parseExpression, | ||
parseFile, ParserOptions, | ||
parseString, | ||
fieldsToExpression, | ||
StringResult | ||
} from '../index'; | ||
const interval = CronExpression.parseExpression('0 1 2 3 * 1-3,5'); | ||
const interval = parseExpression('0 1 2 3 * 1-3,5'); | ||
const intervalIterator = parseExpression('0 1 2 3 * 1-3,5', {iterator: true}); | ||
expectType<readonly number[]>(interval.fields.second); | ||
expectType<readonly number[]>(interval.fields.minute); | ||
expectType<readonly number[]>(interval.fields.hour); | ||
expectType<readonly number[]>(interval.fields.dayOfMonth); | ||
expectType<readonly number[]>(interval.fields.month); | ||
expectType<readonly number[]>(interval.fields.dayOfWeek); | ||
expectError(interval.fields = interval.fields); | ||
expectError(interval.fields.second = []); | ||
expectError(interval.fields.second.push(1)); | ||
expectError(interval.fields.minute = []); | ||
expectError(interval.fields.minute.push(1)); | ||
expectError(interval.fields.hour = []); | ||
expectError(interval.fields.hour.push(1)); | ||
expectError(interval.fields.dayOfMonth = []); | ||
expectError(interval.fields.dayOfMonth.push(1)); | ||
expectError(interval.fields.month = []); | ||
expectError(interval.fields.month.push(1)); | ||
expectError(interval.fields.dayOfWeek = []); | ||
expectError(interval.fields.dayOfWeek.push(1)); | ||
expectAssignable<typeof interval.fields.second[0]>(0); | ||
expectAssignable<typeof interval.fields.second[0]>(59); | ||
expectNotAssignable<typeof interval.fields.second[0]>(-1); | ||
expectNotAssignable<typeof interval.fields.second[0]>(60); | ||
expectAssignable<typeof interval.fields.minute[0]>(0); | ||
expectAssignable<typeof interval.fields.minute[0]>(59); | ||
expectNotAssignable<typeof interval.fields.minute[0]>(-1); | ||
expectNotAssignable<typeof interval.fields.minute[0]>(60); | ||
expectAssignable<typeof interval.fields.hour[0]>(0); | ||
expectAssignable<typeof interval.fields.hour[0]>(23); | ||
expectNotAssignable<typeof interval.fields.hour[0]>(-1); | ||
expectNotAssignable<typeof interval.fields.hour[0]>(24); | ||
expectAssignable<typeof interval.fields.dayOfMonth[0]>(1); | ||
expectAssignable<typeof interval.fields.dayOfMonth[0]>(31); | ||
expectAssignable<typeof interval.fields.dayOfMonth[0]>('L'); | ||
expectNotAssignable<typeof interval.fields.dayOfMonth[0]>(0); | ||
expectNotAssignable<typeof interval.fields.dayOfMonth[0]>(32); | ||
expectAssignable<typeof interval.fields.month[0]>(1); | ||
expectAssignable<typeof interval.fields.month[0]>(12); | ||
expectNotAssignable<typeof interval.fields.month[0]>(0); | ||
expectNotAssignable<typeof interval.fields.month[0]>(13); | ||
expectAssignable<typeof interval.fields.dayOfWeek[0]>(0); | ||
expectAssignable<typeof interval.fields.dayOfWeek[0]>(7); | ||
expectNotAssignable<typeof interval.fields.dayOfWeek[0]>(-1); | ||
expectNotAssignable<typeof interval.fields.dayOfWeek[0]>(8); | ||
const parseOptions: ParserOptions<true> = { | ||
currentDate: 'f', | ||
startDate: 4, | ||
endDate: new Date(), | ||
iterator: true, | ||
utc: true, | ||
tz: 'f', | ||
nthDayOfWeek: 5, | ||
} | ||
expectAssignable<{ | ||
currentDate?: string | number | Date | ||
startDate?: string | number | Date | ||
endDate?: string | number | Date | ||
iterator?: boolean | ||
utc?: boolean | ||
tz?: string | ||
nthDayOfWeek?: number | ||
}>(parseOptions) | ||
expectType<CronExpression>(parseExpression('0 1 2 3 * 1-3,5')) | ||
expectType<CronExpression<true>>(parseExpression('0 1 2 3 * 1-3,5', parseOptions)) | ||
const fields: CronFields = { | ||
second: [1, 1], | ||
minute: [1], | ||
hour: [1], | ||
dayOfMonth: [1], | ||
month: [1], | ||
dayOfWeek: [1], | ||
} | ||
expectType<CronExpression>(fieldsToExpression(fields)) | ||
expectType<CronExpression<true>>(fieldsToExpression(fields, parseOptions)) | ||
expectType<string>(fieldsToExpression(fields).stringify()) | ||
expectType<string>(fieldsToExpression(fields, parseOptions).stringify()) | ||
expectType<string>(fieldsToExpression(fields, parseOptions).stringify(true)) | ||
expectType<void>(parseFile('path', (err: any, data: StringResult) => console.log(data))) | ||
expectType<StringResult>(parseString('path')) | ||
const stringResult = parseString('path'); | ||
expectType<{ | ||
variables: Record<string, string>, | ||
expressions: CronExpression[], | ||
errors: Record<string, any>, | ||
}>(stringResult) | ||
expectType<CronFields>(interval.fields) | ||
expectType<CronDate>(interval.next()) | ||
expectType<CronDate>(interval.prev()) | ||
expectType<boolean>(interval.hasNext()) | ||
expectType<boolean>(interval.hasPrev()) | ||
expectType<string>(interval.stringify()) | ||
expectType<string>(interval.stringify(true)) | ||
expectType<void>(interval.reset()) | ||
expectType<void>(interval.reset("Sdf")) | ||
expectType<void>(interval.reset(5)) | ||
expectType<void>(interval.reset(new Date())) | ||
expectType<CronDate[]>(interval.iterate(5)) | ||
expectType<CronDate[]>(interval.iterate(5, (item: CronDate, i: number) => {})) | ||
expectAssignable<DateType>(new Date()) | ||
expectAssignable<DateType>(5) | ||
expectAssignable<DateType>("SDf") | ||
expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.next()) | ||
expectType<IteratorResult<CronDate, CronDate>>(intervalIterator.prev()) | ||
expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5)) | ||
expectType<IteratorResult<CronDate, CronDate>[]>(intervalIterator.iterate(5, (item: IteratorResult<CronDate, CronDate>, i: number) => {})) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
138889
32
3958
149