New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

node-schedule

Package Overview
Dependencies
Maintainers
5
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-schedule - npm Package Compare versions

Comparing version 1.3.3 to 2.0.0

CHANGELOG.md

631

lib/schedule.js

@@ -1,2 +0,1 @@

'use strict';

@@ -9,275 +8,4 @@

var events = require('events'),
util = require('util'),
cronParser = require('cron-parser'),
CronDate = require('cron-parser/lib/date'),
lt = require('long-timeout'),
sorted = require('sorted-array-functions'),
nodeVersionUtils = require('./nodeVersionUtils')
const { Job, scheduledJobs } = require('./Job')
/* Job object */
var anonJobCounter = 0;
var scheduledJobs = {};
var promisesSupported = nodeVersionUtils.arePromisesSupported(nodeVersionUtils.getNodeVersion(nodeVersionUtils.nodeVersionString))
function isValidDate(date) {
// Taken from http://stackoverflow.com/a/12372720/1562178
// If getTime() returns NaN it'll return false anyway
return date.getTime() === date.getTime();
}
function Job(name, job, callback) {
// setup a private pendingInvocations variable
var pendingInvocations = [];
//setup a private number of invocations variable
var triggeredJobs = 0;
// Set scope vars
var jobName = name && typeof name === 'string' ? name : '<Anonymous Job ' + (++anonJobCounter) + '>';
this.job = name && typeof name === 'function' ? name : job;
// Make sure callback is actually a callback
if (this.job === name) {
// Name wasn't provided and maybe a callback is there
this.callback = typeof job === 'function' ? job : false;
} else {
// Name was provided, and maybe a callback is there
this.callback = typeof callback === 'function' ? callback : false;
}
// Check for generator
if (typeof this.job === 'function' &&
this.job.prototype &&
this.job.prototype.next) {
this.job = function() {
return this.next().value;
}.bind(this.job.call(this));
}
// define properties
Object.defineProperty(this, 'name', {
value: jobName,
writable: false,
enumerable: true
});
// method that require private access
this.trackInvocation = function(invocation) {
// add to our invocation list
sorted.add(pendingInvocations, invocation, sorter);
return true;
};
this.stopTrackingInvocation = function(invocation) {
var invIdx = pendingInvocations.indexOf(invocation);
if (invIdx > -1) {
pendingInvocations.splice(invIdx, 1);
return true;
}
return false;
};
this.triggeredJobs = function() {
return triggeredJobs;
};
this.setTriggeredJobs = function(triggeredJob) {
triggeredJobs = triggeredJob;
};
this.cancel = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : false;
var inv, newInv;
var newInvs = [];
for (var j = 0; j < pendingInvocations.length; j++) {
inv = pendingInvocations[j];
cancelInvocation(inv);
if (reschedule && (inv.recurrenceRule.recurs || inv.recurrenceRule.next)) {
newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
if (newInv !== null) {
newInvs.push(newInv);
}
}
}
pendingInvocations = [];
for (var k = 0; k < newInvs.length; k++) {
this.trackInvocation(newInvs[k]);
}
// remove from scheduledJobs if reschedule === false
if (!reschedule) {
if (this.name) {
delete scheduledJobs[this.name];
}
}
return true;
};
this.cancelNext = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : true;
if (!pendingInvocations.length) {
return false;
}
var newInv;
var nextInv = pendingInvocations.shift();
cancelInvocation(nextInv);
if (reschedule && (nextInv.recurrenceRule.recurs || nextInv.recurrenceRule.next)) {
newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
if (newInv !== null) {
this.trackInvocation(newInv);
}
}
return true;
};
this.reschedule = function(spec) {
var inv;
var cInvs = pendingInvocations.slice();
for (var j = 0; j < cInvs.length; j++) {
inv = cInvs[j];
cancelInvocation(inv);
}
pendingInvocations = [];
if (this.schedule(spec)) {
this.setTriggeredJobs(0);
return true;
} else {
pendingInvocations = cInvs;
return false;
}
};
this.nextInvocation = function() {
if (!pendingInvocations.length) {
return null;
}
return pendingInvocations[0].fireDate;
};
this.pendingInvocations = function() {
return pendingInvocations;
};
}
util.inherits(Job, events.EventEmitter);
Job.prototype.invoke = function(fireDate) {
if (typeof this.job == 'function') {
this.setTriggeredJobs(this.triggeredJobs() + 1);
return this.job(fireDate);
} else {
this.job.execute(fireDate);
}
};
Job.prototype.runOnDate = function(date) {
return this.schedule(date);
};
Job.prototype.schedule = function(spec) {
var self = this;
var success = false;
var inv;
var start;
var end;
var tz;
// save passed-in value before 'spec' is replaced
if (typeof spec === 'object' && 'tz' in spec) {
tz = spec.tz;
}
if (typeof spec === 'object' && spec.rule) {
start = spec.start || undefined;
end = spec.end || undefined;
spec = spec.rule;
if (start) {
if (!(start instanceof Date)) {
start = new Date(start);
}
start = new CronDate(start, tz);
if (!isValidDate(start) || start.getTime() < Date.now()) {
start = undefined;
}
}
if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
end = undefined;
}
if (end) {
end = new CronDate(end, tz);
}
}
try {
var res = cronParser.parseExpression(spec, { currentDate: start, tz: tz });
inv = scheduleNextRecurrence(res, self, start, end);
if (inv !== null) {
success = self.trackInvocation(inv);
}
} catch (err) {
var type = typeof spec;
if ((type === 'string') || (type === 'number')) {
spec = new Date(spec);
}
if ((spec instanceof Date) && (isValidDate(spec))) {
spec = new CronDate(spec);
if (spec.getTime() >= Date.now()) {
inv = new Invocation(self, spec);
scheduleInvocation(inv);
success = self.trackInvocation(inv);
}
} else if (type === 'object') {
if (!(spec instanceof RecurrenceRule)) {
var r = new RecurrenceRule();
if ('year' in spec) {
r.year = spec.year;
}
if ('month' in spec) {
r.month = spec.month;
}
if ('date' in spec) {
r.date = spec.date;
}
if ('dayOfWeek' in spec) {
r.dayOfWeek = spec.dayOfWeek;
}
if ('hour' in spec) {
r.hour = spec.hour;
}
if ('minute' in spec) {
r.minute = spec.minute;
}
if ('second' in spec) {
r.second = spec.second;
}
spec = r;
}
spec.tz = tz;
inv = scheduleNextRecurrence(spec, self, start, end);
if (inv !== null) {
success = self.trackInvocation(inv);
}
}
}
scheduledJobs[this.name] = this;
return success;
};
/* API

@@ -295,334 +23,19 @@ invoke()

/* DoesntRecur rule */
var DoesntRecur = new RecurrenceRule();
DoesntRecur.recurs = false;
/* Invocation object */
function Invocation(job, fireDate, recurrenceRule, endDate) {
this.job = job;
this.fireDate = fireDate;
this.endDate = endDate;
this.recurrenceRule = recurrenceRule || DoesntRecur;
this.timerID = null;
}
function sorter(a, b) {
return (a.fireDate.getTime() - b.fireDate.getTime());
}
/* Range object */
function Range(start, end, step) {
this.start = start || 0;
this.end = end || 60;
this.step = step || 1;
}
Range.prototype.contains = function(val) {
if (this.step === null || this.step === 1) {
return (val >= this.start && val <= this.end);
} else {
for (var i = this.start; i < this.end; i += this.step) {
if (i === val) {
return true;
}
}
return false;
}
};
/* RecurrenceRule object */
/*
Interpreting each property:
null - any value is valid
number - fixed value
Range - value must fall in range
array - value must validate against any item in list
NOTE: Cron months are 1-based, but RecurrenceRule months are 0-based.
*/
function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
this.recurs = true;
this.year = (year == null) ? null : year;
this.month = (month == null) ? null : month;
this.date = (date == null) ? null : date;
this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
this.hour = (hour == null) ? null : hour;
this.minute = (minute == null) ? null : minute;
this.second = (second == null) ? 0 : second;
}
RecurrenceRule.prototype.isValid = function() {
function isValidType(num) {
if (Array.isArray(num) || (num instanceof Array)) {
return num.every(function(e) {
return isValidType(e);
});
}
return !(Number.isNaN(Number(num)) && !(num instanceof Range));
}
if (this.month !== null && (this.month < 0 || this.month > 11 || !isValidType(this.month))) {
return false;
}
if (this.dayOfWeek !== null && (this.dayOfWeek < 0 || this.dayOfWeek > 6 || !isValidType(this.dayOfWeek))) {
return false;
}
if (this.hour !== null && (this.hour < 0 || this.hour > 23 || !isValidType(this.hour))) {
return false;
}
if (this.minute !== null && (this.minute < 0 || this.minute > 59 || !isValidType(this.minute))) {
return false;
}
if (this.second !== null && (this.second < 0 || this.second > 59 || !isValidType(this.second))) {
return false;
}
if (this.date !== null) {
if(!isValidType(this.date)) {
return false;
}
switch (this.month) {
case 3:
case 5:
case 8:
case 10:
if (this.date < 1 || this. date > 30) {
return false;
}
break;
case 1:
if (this.date < 1 || this. date > 29) {
return false;
}
break;
default:
if (this.date < 1 || this. date > 31) {
return false;
}
}
}
return true;
};
RecurrenceRule.prototype.nextInvocationDate = function(base) {
var next = this._nextInvocationDate(base);
return next ? next.toDate() : null;
};
RecurrenceRule.prototype._nextInvocationDate = function(base) {
base = ((base instanceof CronDate) || (base instanceof Date)) ? base : (new Date());
if (!this.recurs) {
return null;
}
if(!this.isValid()) {
return null;
}
var now = new CronDate(Date.now(), this.tz);
var fullYear = now.getFullYear();
if ((this.year !== null) &&
(typeof this.year == 'number') &&
(this.year < fullYear)) {
return null;
}
var next = new CronDate(base.getTime(), this.tz);
next.addSecond();
while (true) {
if (this.year !== null) {
fullYear = next.getFullYear();
if ((typeof this.year == 'number') && (this.year < fullYear)) {
next = null;
break;
}
if (!recurMatch(fullYear, this.year)) {
next.addYear();
next.setMonth(0);
next.setDate(1);
next.setHours(0);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
}
if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
next.addMonth();
continue;
}
if (this.date != null && !recurMatch(next.getDate(), this.date)) {
next.addDay();
continue;
}
if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
next.addDay();
continue;
}
if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
next.addHour();
continue;
}
if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
next.addMinute();
continue;
}
if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
next.addSecond();
continue;
}
break;
}
return next;
};
function recurMatch(val, matcher) {
if (matcher == null) {
return true;
}
if (typeof matcher === 'number') {
return (val === matcher);
} else if(typeof matcher === 'string') {
return (val === Number(matcher));
} else if (matcher instanceof Range) {
return matcher.contains(val);
} else if (Array.isArray(matcher) || (matcher instanceof Array)) {
for (var i = 0; i < matcher.length; i++) {
if (recurMatch(val, matcher[i])) {
return true;
}
}
}
return false;
}
/* Date-based scheduler */
function runOnDate(date, job) {
var now = Date.now();
var then = date.getTime();
return lt.setTimeout(function() {
if (then > Date.now())
runOnDate(date, job);
else
job();
}, (then < now ? 0 : then - now));
}
var invocations = [];
var currentInvocation = null;
function scheduleInvocation(invocation) {
sorted.add(invocations, invocation, sorter);
prepareNextInvocation();
var date = invocation.fireDate instanceof CronDate ? invocation.fireDate.toDate() : invocation.fireDate;
invocation.job.emit('scheduled', date);
}
function prepareNextInvocation() {
if (invocations.length > 0 && currentInvocation !== invocations[0]) {
if (currentInvocation !== null) {
lt.clearTimeout(currentInvocation.timerID);
currentInvocation.timerID = null;
currentInvocation = null;
}
currentInvocation = invocations[0];
var job = currentInvocation.job;
var cinv = currentInvocation;
currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() {
currentInvocationFinished();
if (job.callback) {
job.callback();
}
if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) {
var inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate, cinv.endDate);
if (inv !== null) {
inv.job.trackInvocation(inv);
}
}
job.stopTrackingInvocation(cinv);
try {
var result = job.invoke(cinv.fireDate instanceof CronDate ? cinv.fireDate.toDate() : cinv.fireDate);
job.emit('run');
if (promisesSupported && result instanceof Promise) {
result.catch(function (err) {
job.emit('error', err);
});
}
} catch (err) {
job.emit('error', err);
}
});
}
}
function currentInvocationFinished() {
invocations.shift();
currentInvocation = null;
prepareNextInvocation();
}
function cancelInvocation(invocation) {
var idx = invocations.indexOf(invocation);
if (idx > -1) {
invocations.splice(idx, 1);
if (invocation.timerID !== null) {
lt.clearTimeout(invocation.timerID);
}
if (currentInvocation === invocation) {
currentInvocation = null;
}
invocation.job.emit('canceled', invocation.fireDate);
prepareNextInvocation();
}
}
/* Recurrence scheduler */
function scheduleNextRecurrence(rule, job, prevDate, endDate) {
prevDate = (prevDate instanceof CronDate) ? prevDate : new CronDate();
var date = (rule instanceof RecurrenceRule) ? rule._nextInvocationDate(prevDate) : rule.next();
if (date === null) {
return null;
}
if ((endDate instanceof CronDate) && date.getTime() > endDate.getTime()) {
return null;
}
var inv = new Invocation(job, date, rule, endDate);
scheduleInvocation(inv);
return inv;
}
/* Convenience methods */
function scheduleJob() {
if (arguments.length < 2) {
return null;
throw new RangeError('Invalid number of arguments');
}
var name = (arguments.length >= 3 && typeof arguments[0] === 'string') ? arguments[0] : null;
var spec = name ? arguments[1] : arguments[0];
var method = name ? arguments[2] : arguments[1];
var callback = name ? arguments[3] : arguments[2];
const name = (arguments.length >= 3 && typeof arguments[0] === 'string') ? arguments[0] : null;
const spec = name ? arguments[1] : arguments[0];
const method = name ? arguments[2] : arguments[1];
const callback = name ? arguments[3] : arguments[2];
var job = new Job(name, method, callback);
if (typeof method !== 'function') {
throw new RangeError('The job method must be a function.');
}
const job = new Job(name, method, callback);
if (job.schedule(spec)) {

@@ -640,7 +53,9 @@ return job;

}
} else if (typeof job == 'string' || job instanceof String) {
if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
} else if (typeof job === 'string') {
if (scheduledJobs.hasOwnProperty(job)) {
if (scheduledJobs[job].reschedule(spec)) {
return scheduledJobs[job];
}
} else {
throw new Error('Cannot reschedule one-off job by name, pass job reference instead')
}

@@ -652,3 +67,3 @@ }

function cancelJob(job) {
var success = false;
let success = false;
if (job instanceof Job) {

@@ -666,9 +81,7 @@ success = job.cancel();

/* Public API */
module.exports.Job = Job;
module.exports.Range = Range;
module.exports.RecurrenceRule = RecurrenceRule;
module.exports.Invocation = Invocation;
module.exports.scheduleJob = scheduleJob;
module.exports.rescheduleJob = rescheduleJob;
module.exports.scheduledJobs = scheduledJobs;
module.exports.cancelJob = cancelJob;
module.exports = {
scheduleJob,
rescheduleJob,
scheduledJobs,
cancelJob
}
{
"name": "node-schedule",
"version": "1.3.3",
"version": "2.0.0",
"description": "A cron-like and not-cron-like job scheduler for Node.",

@@ -10,10 +10,12 @@ "keywords": [

"cron",
"recurrent",
"in-memory"
],
"license": "MIT",
"main": "./lib/schedule.js",
"main": "index.js",
"scripts": {
"test": "nodeunit",
"test:coverage": "istanbul cover ./node_modules/.bin/nodeunit test",
"test:coverage-win": "istanbul cover ./node_modules/nodeunit/bin/nodeunit test",
"test": "tape test/*.js",
"test:browser": "airtap test/cancel-long-running-jobs.js test/convenience-method-test.js test/date-convenience-methods-test.js test/range-test.js test/recurrence-rule-test.js test/schedule-cron-jobs.js test/start-end-test.js",
"coveralls": "nyc report --reporter=lcov",
"test:coverage": "nyc tape test/*.js",
"lint": "eslint lib test",

@@ -38,3 +40,3 @@ "lint:fix": "eslint --fix lib test"

"dependencies": {
"cron-parser": "^2.18.0",
"cron-parser": "^3.1.0",
"long-timeout": "0.1.1",

@@ -44,9 +46,15 @@ "sorted-array-functions": "^1.3.0"

"devDependencies": {
"airtap": "^4.0.1",
"eslint": "^7.18.0",
"istanbul": "^0.4.5",
"nodeunit": "~0.10.2",
"sinon": "^2.4.1"
"nyc": "^15.1.0",
"sinon": "^9.2.4",
"tape": "^5.1.1"
},
"engines": {
"node": ">=6"
},
"files": [
"CHANGELOG.md",
"README.md",
"UPGRADING.md",
"LICENSE",

@@ -53,0 +61,0 @@ "lib/*"

@@ -16,21 +16,13 @@ # Node Schedule

## Usage
Node 6+ is supported.
### Installation
## Overview
You can install using [npm](https://www.npmjs.com/package/node-schedule).
Node Schedule is for time-based scheduling, not interval-based scheduling.
```
npm install node-schedule
```
### Overview
Node Schedule is for time-based scheduling, not interval-based scheduling.
While you can easily bend it to your will, if you only want to do something like
"run this function every 5 minutes", you'll find `setInterval` much easier to use,
and far more appropriate. But if you want to, say, "run this function at the :20
"run this function every 5 minutes", [toad-scheduler](https://github.com/kibertoad/toad-scheduler) would be a better choice. But if you want to, say, "run this function at the :20
and :50 of every hour on the third Tuesday of every month," you'll find that
Node Schedule suits your needs better. Additionally, Node Schedule has Windows
support unlike true cron since the node runtime is now fully supported.
support, unlike true `cron`, since the node runtime is now fully supported.

@@ -42,2 +34,16 @@ Note that Node Schedule is designed for in-process scheduling, i.e. scheduled jobs

In case you need durable jobs that persist across restarts and lock system compatible with multi-node deployments,
try [agenda](https://github.com/agenda/agenda) or [bree](https://github.com/breejs/bree).
## Usage
### Installation
You can install using [npm](https://www.npmjs.com/package/node-schedule).
```
npm install node-schedule
```
### Jobs and Scheduling

@@ -81,5 +87,5 @@

```js
var schedule = require('node-schedule');
const schedule = require('node-schedule');
var j = schedule.scheduleJob('42 * * * *', function(){
const job = schedule.scheduleJob('42 * * * *', function(){
console.log('The answer to life, the universe, and everything!');

@@ -94,3 +100,3 @@ });

```js
var j = schedule.scheduleJob('0 17 ? * 0,4-6', function(){
const job = schedule.scheduleJob('0 17 ? * 0,4-6', function(){
console.log('Today is recognized by Rebecca Black!');

@@ -104,3 +110,3 @@ });

```js
var j = schedule.scheduleJob('0 1 * * *', function(fireDate){
const job = schedule.scheduleJob('0 1 * * *', function(fireDate){
console.log('This job was supposed to run at ' + fireDate + ', but actually ran at ' + new Date());

@@ -124,6 +130,6 @@ });

```js
var schedule = require('node-schedule');
var date = new Date(2012, 11, 21, 5, 30, 0);
const schedule = require('node-schedule');
const date = new Date(2012, 11, 21, 5, 30, 0);
var j = schedule.scheduleJob(date, function(){
const job = schedule.scheduleJob(date, function(){
console.log('The world is going to end today.');

@@ -136,6 +142,6 @@ });

```js
var schedule = require('node-schedule');
var date = new Date(2012, 11, 21, 5, 30, 0);
var x = 'Tada!';
var j = schedule.scheduleJob(date, function(y){
const schedule = require('node-schedule');
const date = new Date(2012, 11, 21, 5, 30, 0);
const x = 'Tada!';
const job = schedule.scheduleJob(date, function(y){
console.log(y);

@@ -154,8 +160,8 @@ }.bind(null,x));

```js
var schedule = require('node-schedule');
const schedule = require('node-schedule');
var rule = new schedule.RecurrenceRule();
const rule = new schedule.RecurrenceRule();
rule.minute = 42;
var j = schedule.scheduleJob(rule, function(){
const job = schedule.scheduleJob(rule, function(){
console.log('The answer to life, the universe, and everything!');

@@ -170,3 +176,3 @@ });

```js
var rule = new schedule.RecurrenceRule();
const rule = new schedule.RecurrenceRule();
rule.dayOfWeek = [0, new schedule.Range(4, 6)];

@@ -176,3 +182,3 @@ rule.hour = 17;

var j = schedule.scheduleJob(rule, function(){
const job = schedule.scheduleJob(rule, function(){
console.log('Today is recognized by Rebecca Black!');

@@ -185,7 +191,7 @@ });

```js
var rule = new schedule.RecurrenceRule();
const rule = new schedule.RecurrenceRule();
rule.hour = 0;
rule.tz = 'Etc/UTC';
var j = schedule.scheduleJob(rule, function(){
const job = schedule.scheduleJob(rule, function(){
console.log('A new day has begun in the UTC timezone!');

@@ -220,3 +226,3 @@ });

```js
var j = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){
const job = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){
console.log('Time for tea!');

@@ -232,5 +238,5 @@ });

```js
let startTime = new Date(Date.now() + 5000);
let endTime = new Date(startTime.getTime() + 5000);
var j = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, function(){
const startTime = new Date(Date.now() + 5000);
const endTime = new Date(startTime.getTime() + 5000);
const job = schedule.scheduleJob({ start: startTime, end: endTime, rule: '*/1 * * * * *' }, function(){
console.log('Time for tea!');

@@ -237,0 +243,0 @@ });

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc