node-schedule
Advanced tools
Comparing version 0.2.1 to 0.2.3
@@ -10,3 +10,3 @@ # Contribution Guidelines | ||
3. Use a non-`master` branch for ongoing work. | ||
4. Adhere to [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript). | ||
4. Adhere to existing code style as much as possible. | ||
5. Run `npm test` locally before submitting your PR to catch easy-to-miss issues | ||
@@ -13,0 +13,0 @@ |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
/* | ||
@@ -11,25 +13,25 @@ node-schedule | ||
function addDateConvenienceMethods(Date){ | ||
if(typeof Date.addYear !== 'function'){ | ||
Date.addYear = function(){ | ||
function addDateConvenienceMethods(Date) { | ||
if (typeof Date.addYear !== 'function') { | ||
Date.addYear = function() { | ||
this.setFullYear(this.getFullYear() + 1); | ||
}; | ||
Date.addMonth = function(){ | ||
Date.addMonth = function() { | ||
this.setMonth(this.getMonth() + 1); | ||
}; | ||
Date.addDay = function(){ | ||
Date.addDay = function() { | ||
this.setDate(this.getDate() + 1); | ||
}; | ||
Date.addHour = function(){ | ||
Date.addHour = function() { | ||
this.setTime(this.getTime() + (60 * 60 * 1000)); | ||
}; | ||
Date.addMinute = function(){ | ||
Date.addMinute = function() { | ||
this.setTime(this.getTime() + (60 * 1000)); | ||
}; | ||
Date.addSecond = function(){ | ||
Date.addSecond = function() { | ||
this.setTime(this.getTime() + 1000); | ||
@@ -36,0 +38,0 @@ }; |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
/* | ||
@@ -7,10 +9,11 @@ node-schedule | ||
var events = require('events'), | ||
util = require('util'), | ||
increment = require('./increment.js'), | ||
cron_parser = require('cron-parser'), | ||
lt = require('long-timeout'); | ||
util = require('util'), | ||
increment = require('./increment.js'), | ||
cron_parser = require('cron-parser'), | ||
lt = require('long-timeout'); | ||
/* Job object */ | ||
var anonJobCounter = 0; | ||
function Job(){ | ||
function Job() { | ||
var name; | ||
@@ -20,4 +23,3 @@ | ||
var arg; | ||
for (var i = 0; i < arguments.length; i++) | ||
{ | ||
for (var i = 0; i < arguments.length; i++) { | ||
arg = arguments[i]; | ||
@@ -46,3 +48,3 @@ if (typeof(arg) == 'string' || arg instanceof String) | ||
// method that require private access | ||
this.trackInvocation = function(invocation){ | ||
this.trackInvocation = function(invocation) { | ||
// add to our invocation list | ||
@@ -56,6 +58,5 @@ pendingInvocations.push(invocation); | ||
}; | ||
this.stopTrackingInvocation = function(invocation){ | ||
this.stopTrackingInvocation = function(invocation) { | ||
var invIdx = pendingInvocations.indexOf(invocation); | ||
if (invIdx > -1) | ||
{ | ||
if (invIdx > -1) { | ||
pendingInvocations.splice(invIdx, 1); | ||
@@ -67,3 +68,3 @@ return true; | ||
}; | ||
this.cancel = function(reschedule){ | ||
this.cancel = function(reschedule) { | ||
reschedule = (typeof(reschedule) == 'boolean') ? reschedule : false; | ||
@@ -73,4 +74,3 @@ | ||
var newInvs = []; | ||
for (var i = 0; i < pendingInvocations.length; i++) | ||
{ | ||
for (var i = 0; i < pendingInvocations.length; i++) { | ||
inv = pendingInvocations[i]; | ||
@@ -80,4 +80,3 @@ | ||
if (reschedule && inv.recurrenceRule.recurs) | ||
{ | ||
if (reschedule && inv.recurrenceRule.recurs) { | ||
newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate); | ||
@@ -96,3 +95,3 @@ if (newInv !== null) | ||
}; | ||
this.cancelNext = function(reschedule){ | ||
this.cancelNext = function(reschedule) { | ||
reschedule = (typeof(reschedule) == 'boolean') ? reschedule : true; | ||
@@ -108,4 +107,3 @@ | ||
if (reschedule && nextInv.recurrenceRule.recurs) | ||
{ | ||
if (reschedule && nextInv.recurrenceRule.recurs) { | ||
newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate); | ||
@@ -118,3 +116,3 @@ if (newInv !== null) | ||
}; | ||
this.nextInvocation = function(){ | ||
this.nextInvocation = function() { | ||
if (pendingInvocations.length == 0) | ||
@@ -124,3 +122,3 @@ return null; | ||
}; | ||
this.pendingInvocations = function(){ | ||
this.pendingInvocations = function() { | ||
return pendingInvocations; | ||
@@ -132,3 +130,3 @@ }; | ||
Job.prototype.invoke = function(){ | ||
Job.prototype.invoke = function() { | ||
if (typeof(this.job) == 'function') { | ||
@@ -141,53 +139,53 @@ this.job(); | ||
Job.prototype.runOnDate = function(date){ | ||
Job.prototype.runOnDate = function(date) { | ||
return this.schedule(date); | ||
}; | ||
Job.prototype.schedule = function(spec){ | ||
var self = this; | ||
var success = false; | ||
try { | ||
var res = cron_parser.parseExpression(spec); | ||
var inv = scheduleNextRecurrence(res, self); | ||
if (inv !== null) { | ||
success = self.trackInvocation(inv); | ||
} | ||
} catch (err) { | ||
var type = typeof(spec); | ||
if (type === 'string') { | ||
spec = new Date(spec); | ||
} | ||
Job.prototype.schedule = function(spec) { | ||
var self = this; | ||
var success = false; | ||
try { | ||
var res = cron_parser.parseExpression(spec); | ||
var inv = scheduleNextRecurrence(res, self); | ||
if (inv !== null) { | ||
success = self.trackInvocation(inv); | ||
} | ||
} catch (err) { | ||
var type = typeof(spec); | ||
if (type === 'string') { | ||
spec = new Date(spec); | ||
} | ||
if (spec instanceof Date) { | ||
var 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; | ||
if (spec instanceof Date) { | ||
var 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 = r; | ||
} | ||
var inv = scheduleNextRecurrence(spec, self); | ||
if (inv !== null) | ||
success = self.trackInvocation(inv); | ||
} | ||
var inv = scheduleNextRecurrence(spec, self); | ||
if (inv !== null) | ||
success = self.trackInvocation(inv); | ||
} | ||
} | ||
return success; | ||
return success; | ||
}; | ||
@@ -208,3 +206,3 @@ | ||
/* Invocation object */ | ||
function Invocation(job, fireDate, recurrenceRule){ | ||
function Invocation(job, fireDate, recurrenceRule) { | ||
this.job = job; | ||
@@ -217,3 +215,3 @@ this.fireDate = fireDate; | ||
function sorter(a, b){ | ||
function sorter(a, b) { | ||
return (a.fireDate.getTime() - b.fireDate.getTime()); | ||
@@ -223,3 +221,3 @@ } | ||
/* Range object */ | ||
function Range(start, end, step){ | ||
function Range(start, end, step) { | ||
this.start = start || 0; | ||
@@ -230,9 +228,7 @@ this.end = end || 60; | ||
Range.prototype.contains = function(val){ | ||
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) | ||
{ | ||
else { | ||
for (var i = this.start; i < this.end; i += this.step) { | ||
if (i == val) | ||
@@ -249,10 +245,10 @@ return true; | ||
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 | ||
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){ | ||
function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) { | ||
this.recurs = true; | ||
@@ -269,3 +265,3 @@ | ||
RecurrenceRule.prototype.validate = function(){ | ||
RecurrenceRule.prototype.validate = function() { | ||
// TODO: validation | ||
@@ -275,3 +271,3 @@ return true; | ||
RecurrenceRule.prototype.nextInvocationDate = function(base){ | ||
RecurrenceRule.prototype.nextInvocationDate = function(base) { | ||
base = (base instanceof Date) ? base : (new Date()); | ||
@@ -291,6 +287,4 @@ increment.addDateConvenienceMethods(base); | ||
while (true) | ||
{ | ||
if (this.year != null && !recurMatch(next.getFullYear(), this.year)) | ||
{ | ||
while (true) { | ||
if (this.year != null && !recurMatch(next.getFullYear(), this.year)) { | ||
next.addYear(); | ||
@@ -304,4 +298,3 @@ next.setMonth(0); | ||
} | ||
if (this.month != null && !recurMatch(next.getMonth(), this.month)) | ||
{ | ||
if (this.month != null && !recurMatch(next.getMonth(), this.month)) { | ||
next.addMonth(); | ||
@@ -314,4 +307,3 @@ next.setDate(1); | ||
} | ||
if (this.date != null && !recurMatch(next.getDate(), this.date)) | ||
{ | ||
if (this.date != null && !recurMatch(next.getDate(), this.date)) { | ||
next.addDay(); | ||
@@ -323,4 +315,3 @@ next.setHours(0); | ||
} | ||
if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) | ||
{ | ||
if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) { | ||
next.addDay(); | ||
@@ -332,4 +323,3 @@ next.setHours(0); | ||
} | ||
if (this.hour != null && !recurMatch(next.getHours(), this.hour)) | ||
{ | ||
if (this.hour != null && !recurMatch(next.getHours(), this.hour)) { | ||
next.addHour(); | ||
@@ -340,4 +330,3 @@ next.setMinutes(0); | ||
} | ||
if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) | ||
{ | ||
if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) { | ||
next.addMinute(); | ||
@@ -347,4 +336,3 @@ next.setSeconds(0); | ||
} | ||
if (this.second != null && !recurMatch(next.getSeconds(), this.second)) | ||
{ | ||
if (this.second != null && !recurMatch(next.getSeconds(), this.second)) { | ||
next.addSecond(); | ||
@@ -360,3 +348,3 @@ continue; | ||
function recurMatch(val, matcher){ | ||
function recurMatch(val, matcher) { | ||
if (matcher == null) | ||
@@ -369,6 +357,4 @@ return true; | ||
return matcher.contains(val); | ||
else if (Array.isArray(matcher) || (typeof(matcher) == 'object' && matcher instanceof Array)) | ||
{ | ||
for (var i = 0; i < matcher.length; i++) | ||
{ | ||
else if (Array.isArray(matcher) || (typeof(matcher) == 'object' && matcher instanceof Array)) { | ||
for (var i = 0; i < matcher.length; i++) { | ||
if (recurMatch(val, matcher[i])) | ||
@@ -388,12 +374,11 @@ return true; | ||
/* Date-based scheduler */ | ||
function runOnDate(date, job){ | ||
function runOnDate(date, job) { | ||
var now = (new Date()).getTime(); | ||
var then = date.getTime(); | ||
if (then < now) | ||
{ | ||
// if (now - then < 1000) | ||
process.nextTick(job); | ||
return null; | ||
} | ||
if (then < now) { | ||
// if (now - then < 1000) | ||
process.nextTick(job); | ||
return null; | ||
} | ||
@@ -405,3 +390,4 @@ return lt.setTimeout(job, (then - now)); | ||
var currentInvocation = null; | ||
function scheduleInvocation(invocation){ | ||
function scheduleInvocation(invocation) { | ||
invocations.push(invocation); | ||
@@ -413,7 +399,5 @@ invocations.sort(sorter); | ||
function prepareNextInvocation(){ | ||
if (invocations.length > 0 && currentInvocation != invocations[0]) | ||
{ | ||
if (currentInvocation !== null) | ||
{ | ||
function prepareNextInvocation() { | ||
if (invocations.length > 0 && currentInvocation != invocations[0]) { | ||
if (currentInvocation !== null) { | ||
lt.clearTimeout(currentInvocation.timerID); | ||
@@ -428,7 +412,6 @@ currentInvocation.timerID = null; | ||
var cinv = currentInvocation; | ||
currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function(){ | ||
currentInvocation.timerID = runOnDate(currentInvocation.fireDate, function() { | ||
currentInvocationFinished(); | ||
if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) | ||
{ | ||
if (cinv.recurrenceRule.recurs || cinv.recurrenceRule._endDate === null) { | ||
var inv = scheduleNextRecurrence(cinv.recurrenceRule, cinv.job, cinv.fireDate); | ||
@@ -447,3 +430,3 @@ if (inv !== null) | ||
function currentInvocationFinished(){ | ||
function currentInvocationFinished() { | ||
invocations.shift(); | ||
@@ -454,6 +437,5 @@ currentInvocation = null; | ||
function cancelInvocation(invocation){ | ||
function cancelInvocation(invocation) { | ||
var idx = invocations.indexOf(invocation); | ||
if (idx > -1) | ||
{ | ||
if (idx > -1) { | ||
invocations.splice(idx, 1); | ||
@@ -472,3 +454,3 @@ if (invocation.timerID !== null) | ||
/* Recurrence scheduler */ | ||
function scheduleNextRecurrence(rule, job, prevDate){ | ||
function scheduleNextRecurrence(rule, job, prevDate) { | ||
prevDate = (prevDate instanceof Date) ? prevDate : (new Date()); | ||
@@ -488,2 +470,3 @@ | ||
var scheduledJobs = {}; | ||
function scheduleJob() { | ||
@@ -493,30 +476,25 @@ if (arguments.length < 2) { | ||
} | ||
var name = (arguments.length >= 3) ? arguments[0] : null; | ||
var spec = (arguments.length >= 3) ? arguments[1] : arguments[0]; | ||
var method = (arguments.length >= 3) ? arguments[2] : arguments[1]; | ||
var job = new Job(name, method); | ||
if (job.schedule(spec)) { | ||
scheduledJobs[job.name] = job; | ||
return job; | ||
} | ||
return null; | ||
} | ||
function cancelJob(job){ | ||
function cancelJob(job) { | ||
var success = false; | ||
if (job instanceof Job) | ||
{ | ||
if (job instanceof Job) { | ||
success = job.cancel(); | ||
if (success) | ||
{ | ||
for (var name in scheduledJobs) | ||
{ | ||
if (scheduledJobs.hasOwnProperty(name)) | ||
{ | ||
if (scheduledJobs[name] == job) | ||
{ | ||
if (success) { | ||
for (var name in scheduledJobs) { | ||
if (scheduledJobs.hasOwnProperty(name)) { | ||
if (scheduledJobs[name] == job) { | ||
delete scheduledJobs[name]; | ||
@@ -528,7 +506,4 @@ break; | ||
} | ||
} | ||
else if (typeof(job) == 'string' || job instanceof String) | ||
{ | ||
if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) | ||
{ | ||
} else if (typeof(job) == 'string' || job instanceof String) { | ||
if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) { | ||
success = scheduledJobs[job].cancel(); | ||
@@ -535,0 +510,0 @@ if (success) |
{ | ||
"name": "node-schedule", | ||
"version": "0.2.1", | ||
"version": "0.2.3", | ||
"description": "A cron-like and not-cron-like job scheduler for Node.", | ||
@@ -19,3 +19,2 @@ "keywords": ["schedule", "task", "job", "cron"], | ||
}, | ||
"dependencies": { | ||
@@ -22,0 +21,0 @@ "cron-parser": "~0.6.1", |
170
README.md
@@ -1,166 +0,46 @@ | ||
node-schedule | ||
============= | ||
# node-schedule | ||
node-schedule is a flexible both cron-like and not-cron-like job scheduler for Node.js. It allows you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute), and is MIT-licensed (see below). | ||
[![NPM](https://nodei.co/npm/node-schedule.png?downloads=true)](https://nodei.co/npm/node-schedule/) | ||
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 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. | ||
node-schedule is a flexible both cron-like and not-cron-like job scheduler for Node.js. It allows you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute). | ||
Note that node-schedule is designed for in-process scheduling, i.e. scheduled jobs will only fire as long as your script is running, and the schedule will disappear when execution completes. If you need to schedule jobs that will persist even when your script *isn't* running, consider using the actual [cron](http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5). | ||
Read more about the module's core functions on the [About](https://github.com/mattpat/node-schedule/wiki/About) page of the wiki. | ||
## Usage | ||
Jobs and Scheduling | ||
------------------- | ||
Check out our wonderful [wiki] for usage instructions. | ||
Every scheduled job in node-schedule is represented by a `Job` object. You can create jobs manually, then execute the `schedule()` method to apply a schedule, or use the convenience function `scheduleJob()` as demonstrated below. | ||
`Job` objects are `EventEmitter`'s, and emit a `run` event after each execution. They also emit a `scheduled` event each time they're scheduled to run, and a `canceled` event when an invocation is canceled before it's executed (both events receive a JavaScript date object as a parameter). Note that jobs are scheduled the first time immediately, so if you create a job using the `scheduleJob()` convenience method, you'll miss the first `scheduled` event. Also note that `canceled` is the single-L American spelling. | ||
## Contributing | ||
We're committed to a loosely-coupled architecture for node-schedule and would love to get your contributions. | ||
Date-based Scheduling | ||
--------------------- | ||
Before jumping in, check out our **[Contributing] [contributing]** page on the wiki! | ||
Say you very specifically want a function to execute at 5:30am on December 21, 2012. | ||
```js | ||
var schedule = require('node-schedule'); | ||
var date = new Date(2012, 12, 21, 5, 30, 0); | ||
## Core Team | ||
var j = schedule.scheduleJob(date, function(){ | ||
console.log('The world is going to end today.'); | ||
}); | ||
``` | ||
This module was originally developed by [Matt Patenaude], but the new team is as follows: | ||
You can invalidate the job with the `cancel()` method: | ||
**Tejas Manohar** | ||
```js | ||
j.cancel(); | ||
``` | ||
- <https://twitter.com/tejasmanohar> | ||
- <https://github.com/tejasmanohar> | ||
To use current data in the future you can use binding: | ||
**Santiago Gimeno** | ||
```js | ||
var schedule = require('node-schedule'); | ||
var date = new Date(2012, 12, 21, 5, 30, 0); | ||
var x = 'Tada!'; | ||
var j = schedule.scheduleJob(date, function(y){ | ||
console.log(y); | ||
}.bind(null,x)); | ||
x = 'Changing Data'; | ||
``` | ||
This will log 'Tada!' when the scheduled Job runs, rather than 'Changing Data', which x changes to immediately after scheduling. | ||
- <https://github.com/santigimeno> | ||
## Copyright and license | ||
Recurrence Rule Scheduling | ||
-------------------------- | ||
node-schedule is copyright 2011-2015 Matt Patenaude. | ||
You can build recurrence rules to specify when a job should recur. For instance, consider this rule, which executes the function every hour at 42 minutes after the hour: | ||
Licensed under the **[MIT License] [license]**; | ||
you may only use this software in compliance with the License. | ||
```js | ||
var schedule = require('node-schedule'); | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.minute = 42; | ||
var j = schedule.scheduleJob(rule, function(){ | ||
console.log('The answer to life, the universe, and everything!'); | ||
}); | ||
``` | ||
You can also use arrays to specify a list of acceptable values, and the `Range` object to specify a range of start and end values, with an optional step parameter. For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm: | ||
```js | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.dayOfWeek = [0, new schedule.Range(4, 6)]; | ||
rule.hour = 17; | ||
rule.minute = 0; | ||
var j = schedule.scheduleJob(rule, function(){ | ||
console.log('Today is recognized by Rebecca Black!'); | ||
}); | ||
``` | ||
It's worth noting that the default value of a component of a recurrence rule is `null` (except for seconds, which is 0 for familiarity with cron). If we did not explicitly set `minute` to 0 above, the message would have instead been logged at 5:00pm, 5:01pm, 5:02pm, ..., 5:59pm. Probably not what you want. | ||
### Object Literal Syntax | ||
To make things a little easier, an object literal syntax is also supported, like in this example which will log a message every Sunday at 2:30pm: | ||
```js | ||
var j = schedule.scheduleJob({hour: 14, minute: 30, dayOfWeek: 0}, function(){ | ||
console.log('Time for tea!'); | ||
}); | ||
``` | ||
Cron-style Scheduling | ||
--------------------- | ||
>The cron format consists of: | ||
> `[MINUTE] [HOUR] [DAY OF MONTH] [MONTH OF YEAR] [DAY OF WEEK] [YEAR (optional)]` | ||
If you're a fan of cron, you can instead define your recurrence rules using a syntax similar to what you might write in your crontab. For example, the above examples rewritten using this style: | ||
```js | ||
var schedule = require('node-schedule'); | ||
var j = schedule.scheduleJob('42 * * * *', function(){ | ||
console.log('The answer to life, the universe, and everything!'); | ||
}); | ||
``` | ||
And: | ||
```js | ||
var j = schedule.scheduleJob('0 17 ? * 0,4-6', function(){ | ||
console.log('Today is recognized by Rebecca Black!'); | ||
}); | ||
``` | ||
### Unsupported Cron Features | ||
Currently, `W` (nearest weekday), `L` (last day of month/week), and `#` (nth weekday of the month) are not supported. Also, in the day-of-week field, 7 is currently not recognized as a legal value for Sunday (use 0). Most other features supported by popular cron implementations should work just fine. | ||
It is also entirely possible at this point that constructing a cron string that can *only* exist in the past will cause an infinite loop. This is only possible if a year is specified. If the specified year is a number (i.e., not a range), node-schedule will perform a sanity check before attempting to schedule something in the past. [cron-parser](https://github.com/harrisiirak/cron-parser) is used to parse crontab instructions. | ||
Installing | ||
---------- | ||
You can install using [npm](https://www.npmjs.com/package/node-schedule) in the usual way. | ||
``` | ||
npm install node-schedule | ||
``` | ||
Acknowledgements | ||
----------------- | ||
This module was originally developed by [Matt Pat](https://github.com/mattpat), but is now maintained by [Tejas Manohar](https://github.com/tejasmanohar). | ||
That said, we have a lot of contributors that help this project stay alive! Find a bug? File an issue! Know how to fix? Submit a PR! | ||
License | ||
------- | ||
Copyright (C) 2011 Matt Patenaude. | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
[cron]: http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5 | ||
[wiki]: https://github.com/mattpat/node-schedule/wiki | ||
[contributing]: https://github.com/mattpat/node-schedule/blob/master/CONTRIBUTING.md | ||
[Matt Patenaude]: https://github.com/mattpat | ||
[license]: https://github.com/mattpat/node-schedule/blob/master/LICENSE |
@@ -0,5 +1,12 @@ | ||
var sinon = require('sinon'); | ||
var main = require('../package.json').main; | ||
var schedule = require('../' + main); | ||
var clock; | ||
module.exports = { | ||
setUp: function(cb) { | ||
clock = sinon.useFakeTimers(); | ||
cb(); | ||
}, | ||
".scheduleJob": { | ||
@@ -26,23 +33,28 @@ "Returns Job instance": function(test) { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
"Job doesn't emit initial 'scheduled' event": function(test) { | ||
var job = schedule.scheduleJob(new Date(Date.now() + 1000), function() {}); | ||
var job = schedule.scheduleJob(new Date(Date.now() + 1000), function() {}); | ||
job.on('scheduled', function() { | ||
test.ok(false); | ||
}); | ||
job.on('scheduled', function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
test.done(); | ||
}, 1250); | ||
}/*, | ||
"Won't run job if scheduled in the past": function(test) { | ||
schedule.scheduleJob(new Date(Date.now() - 3000), function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
test.done(); | ||
}, 1250); | ||
setTimeout(function() { | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
clock.tick(1250); | ||
} | ||
/*, | ||
"Won't run job if scheduled in the past": function(test) { | ||
schedule.scheduleJob(new Date(Date.now() - 3000), function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
}, | ||
@@ -64,36 +76,41 @@ ".scheduleJob(RecurrenceRule, fn)": { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
"Job doesn't emit initial 'scheduled' event": function(test) { | ||
/* | ||
* If this was Job#schedule it'd fire 4 times. | ||
*/ | ||
test.expect(3); | ||
/* | ||
* If this was Job#schedule it'd fire 4 times. | ||
*/ | ||
test.expect(3); | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.second = null; // fire every second | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.second = null; // fire every second | ||
var job = new schedule.scheduleJob(rule, function() {}); | ||
var job = new schedule.scheduleJob(rule, function() {}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
}/*, | ||
"Doesn't invoke job if recur rule schedules it in the past": function(test) { | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.year = 2000; | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
var job = schedule.scheduleJob(rule, function() { | ||
test.ok(false); | ||
}); | ||
clock.tick(3250); | ||
} | ||
/*, | ||
"Doesn't invoke job if recur rule schedules it in the past": function(test) { | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.year = 2000; | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
var job = schedule.scheduleJob(rule, function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
}, | ||
@@ -114,37 +131,42 @@ ".scheduleJob({...}, fn)": { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
"Job doesn't emit initial 'scheduled' event": function(test) { | ||
/* | ||
* With Job#schedule this would be 3: | ||
* scheduled at time 0 | ||
* scheduled at time 1000 | ||
* scheduled at time 2000 | ||
*/ | ||
test.expect(2); | ||
/* | ||
* With Job#schedule this would be 3: | ||
* scheduled at time 0 | ||
* scheduled at time 1000 | ||
* scheduled at time 2000 | ||
*/ | ||
test.expect(2); | ||
var job = schedule.scheduleJob({ | ||
second: null // fire every second | ||
}, function() {}); | ||
var job = schedule.scheduleJob({ | ||
second: null // fire every second | ||
}, function() {}); | ||
job.on('scheduled', function() { | ||
test.ok(true); | ||
}); | ||
job.on('scheduled', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 2250); | ||
}/*, | ||
"Doesn't invoke job if object schedules it in the past": function(test) { | ||
var job = new schedule.scheduleJob({ | ||
year: 2000 | ||
}, function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 2250); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
clock.tick(2250); | ||
} | ||
/*, | ||
"Doesn't invoke job if object schedules it in the past": function(test) { | ||
var job = new schedule.scheduleJob({ | ||
year: 2000 | ||
}, function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
}, | ||
@@ -168,2 +190,4 @@ ".cancelJob(Job)": { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
@@ -188,2 +212,4 @@ "Can cancel Jobs scheduled with Job#schedule": function(test) { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
@@ -205,2 +231,4 @@ "Job emits 'canceled' event": function(test) { | ||
}, 1250); | ||
clock.tick(1250); | ||
} | ||
@@ -225,2 +253,4 @@ }, | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
@@ -232,15 +262,15 @@ /* | ||
var job = new schedule.Job(function() { | ||
test.ok(true); | ||
test.ok(true); | ||
}); | ||
job.schedule({ | ||
second: null | ||
second: null | ||
}); | ||
setTimeout(function() { | ||
schedule.cancelJob(job.name); | ||
schedule.cancelJob(job.name); | ||
}, 2250); | ||
setTimeout(function() { | ||
test.done(); | ||
test.done(); | ||
}, 3250); | ||
@@ -263,2 +293,4 @@ },*/ | ||
}, 1250); | ||
clock.tick(1250); | ||
}, | ||
@@ -283,2 +315,4 @@ "Does nothing if no job found by that name": function(test) { | ||
}, 3250); | ||
clock.tick(3250); | ||
} | ||
@@ -296,3 +330,7 @@ }, | ||
} | ||
}, | ||
tearDown: function(cb) { | ||
clock.restore(); | ||
cb(); | ||
} | ||
}; |
@@ -0,7 +1,14 @@ | ||
var sinon = require('sinon'); | ||
var main = require('../package.json').main; | ||
var schedule = require('../' + main); | ||
var clock; | ||
module.exports = { | ||
"Date Enhancements":{ | ||
"Should Not add date convenience methods unless explicitly specified": function(test){ | ||
setUp: function(cb) { | ||
clock = sinon.useFakeTimers(); | ||
cb(); | ||
}, | ||
"Date Enhancements": { | ||
"Should Not add date convenience methods unless explicitly specified": function(test) { | ||
test.ok(typeof Date.addYear !== 'function'); | ||
@@ -15,3 +22,3 @@ test.ok(typeof Date.addMonth !== 'function'); | ||
}, | ||
"Should add date convenience methods when explicitly specified": function(test){ | ||
"Should add date convenience methods when explicitly specified": function(test) { | ||
schedule.addDateConvenienceMethods(Date); | ||
@@ -27,4 +34,4 @@ test.ok(typeof Date.addYear === 'function'); | ||
}, | ||
"Date string":{ | ||
"Should accept a valid date string": function(test){ | ||
"Date string": { | ||
"Should accept a valid date string": function(test) { | ||
test.expect(1); | ||
@@ -39,5 +46,10 @@ | ||
}, 1250); | ||
clock.tick(1250); | ||
} | ||
}, | ||
tearDown: function(cb) { | ||
clock.restore(); | ||
cb(); | ||
} | ||
}; | ||
@@ -0,8 +1,15 @@ | ||
var sinon = require('sinon'); | ||
var main = require('../package.json').main; | ||
var schedule = require('../' + main); | ||
var clock; | ||
module.exports = { | ||
setUp: function(cb) { | ||
clock = sinon.useFakeTimers(); | ||
cb(); | ||
}, | ||
"Job constructor": { | ||
"Accepts Job name and function to run": function(test) { | ||
var job = new schedule.Job('the job', function(){}); | ||
var job = new schedule.Job('the job', function() {}); | ||
@@ -39,2 +46,4 @@ test.equal(job.name, 'the job'); | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
@@ -46,3 +55,3 @@ /* No jobs will run after this test for some reason - hide for now | ||
var job = new schedule.Job(function() { | ||
test.ok(true); | ||
test.ok(true); | ||
}); | ||
@@ -53,3 +62,3 @@ | ||
setTimeout(function() { | ||
test.done(); | ||
test.done(); | ||
}, 1000); | ||
@@ -61,4 +70,4 @@ }, | ||
var pastJob = new schedule.Job(function() { | ||
// Should not run, blow up if it does | ||
test.ok(false); | ||
// Should not run, blow up if it does | ||
test.ok(false); | ||
}); | ||
@@ -68,3 +77,3 @@ pastJob.schedule(new Date(Date.now() - 3000)); | ||
var job = new schedule.Job(function() { | ||
test.ok(true); | ||
test.ok(true); | ||
}); | ||
@@ -74,3 +83,3 @@ job.schedule(new Date(Date.now() + 3000)); | ||
setTimeout(function() { | ||
test.done(); | ||
test.done(); | ||
}, 3250); | ||
@@ -91,2 +100,3 @@ },*/ | ||
job.schedule(date); | ||
clock.tick(3250); | ||
} | ||
@@ -111,41 +121,46 @@ }, | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
"Job emits 'scheduled' event for every next invocation": function(test) { | ||
// Job will run 3 times but be scheduled 4 times, 4th run never happens | ||
// due to cancel. | ||
test.expect(4); | ||
// Job will run 3 times but be scheduled 4 times, 4th run never happens | ||
// due to cancel. | ||
test.expect(4); | ||
var job = new schedule.Job(function() {}); | ||
var job = new schedule.Job(function() {}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.second = null; // fire every second | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.second = null; // fire every second | ||
job.schedule(rule); | ||
job.schedule(rule); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
}/*, | ||
"Doesn't invoke job if recur rule schedules it in the past": function(test) { | ||
test.expect(0); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
var job = new schedule.Job(function() { | ||
test.ok(false); | ||
}); | ||
clock.tick(3250); | ||
} | ||
/*, | ||
"Doesn't invoke job if recur rule schedules it in the past": function(test) { | ||
test.expect(0); | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.year = 2000; | ||
var job = new schedule.Job(function() { | ||
test.ok(false); | ||
}); | ||
job.schedule(rule); | ||
var rule = new schedule.RecurrenceRule(); | ||
rule.year = 2000; | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
job.schedule(rule); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
}, | ||
@@ -168,39 +183,44 @@ "#schedule({...})": { | ||
}, 3250); | ||
clock.tick(3250); | ||
}, | ||
"Job emits 'scheduled' event for every next invocation": function(test) { | ||
// Job will run 3 times but be scheduled 4 times, 4th run never happens | ||
// due to cancel. | ||
test.expect(4); | ||
// Job will run 3 times but be scheduled 4 times, 4th run never happens | ||
// due to cancel. | ||
test.expect(4); | ||
var job = new schedule.Job(function() {}); | ||
var job = new schedule.Job(function() {}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
job.on('scheduled', function(runOnDate) { | ||
test.ok(true); | ||
}); | ||
job.schedule({ | ||
second: null // Fire every second | ||
}); | ||
job.schedule({ | ||
second: null // Fire every second | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
}/*, | ||
"Doesn't invoke job if object schedules it in the past": function(test) { | ||
test.expect(0); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 3250); | ||
var job = new schedule.Job(function() { | ||
test.ok(false); | ||
}); | ||
clock.tick(3250); | ||
} | ||
/*, | ||
"Doesn't invoke job if object schedules it in the past": function(test) { | ||
test.expect(0); | ||
job.schedule({ | ||
year: 2000 | ||
}); | ||
var job = new schedule.Job(function() { | ||
test.ok(false); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
job.schedule({ | ||
year: 2000 | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, 1000); | ||
}*/ | ||
}, | ||
@@ -226,2 +246,4 @@ "#cancel": { | ||
}, 2250); | ||
clock.tick(2250); | ||
}, | ||
@@ -248,2 +270,4 @@ "Job emits 'canceled' event": function(test) { | ||
}, 2250); | ||
clock.tick(2250); | ||
} | ||
@@ -266,4 +290,10 @@ }, | ||
}, 3250); | ||
clock.tick(3250); | ||
} | ||
}, | ||
tearDown: function(cb) { | ||
clock.restore(); | ||
cb(); | ||
} | ||
}; |
@@ -43,3 +43,3 @@ var main = require('../package.json').main; | ||
this.range = new schedule.Range(2, 6, 2); | ||
done(); | ||
@@ -46,0 +46,0 @@ }, |
@@ -235,2 +235,1 @@ var main = require('../package.json').main; | ||
}; | ||
@@ -8,125 +8,126 @@ var sinon = require('sinon'); | ||
module.exports = { | ||
".scheduleJob(cron_expr, fn)": { | ||
setUp: function(cb) { | ||
clock = sinon.useFakeTimers(); | ||
cb(); | ||
}, | ||
"Runs job every second": function(test) { | ||
test.expect(3); | ||
".scheduleJob(cron_expr, fn)": { | ||
setUp: function(cb) { | ||
var now = Date.now(); | ||
clock = sinon.useFakeTimers(); | ||
clock.tick(now); | ||
cb(); | ||
}, | ||
"Runs job every second": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 1000; | ||
var timeout = 3 * 1000; | ||
var job = schedule.scheduleJob('* * * * * *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('* * * * * *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every minute": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every minute": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 60 * 1000; | ||
var timeout = 3 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 * * * * *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 * * * * *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every hour": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every hour": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 60 * 60 * 1000; | ||
var timeout = 3 * 60 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 0 * * * *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 0 * * * *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every day": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every day": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 24 * 60 * 60 * 1000; | ||
var timeout = 3 * 24 * 60 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 0 0 * * *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 0 0 * * *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every week": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every week": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 7 * 24 * 60 * 60 * 1000; | ||
var timeout = 3 * 7 * 24 * 60 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 0 0 * * 1', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 0 0 * * 1', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every month": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every month": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 31 * 24 * 60 * 60 * 1000; | ||
var timeout = 3 * 29.53 * 24 * 60 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 0 0 1 * *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 0 0 1 * *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every year": function(test) { | ||
test.expect(3); | ||
clock.tick(timeout); | ||
}, | ||
"Runs job every year": function(test) { | ||
test.expect(3); | ||
var timeout = 3 * 366 * 24 * 60 * 60 * 1000; | ||
var timeout = 3 * 365.25 * 24 * 60 * 60 * 1000; | ||
var job = schedule.scheduleJob('0 0 0 1 1 *', function() { | ||
test.ok(true); | ||
}); | ||
var job = schedule.scheduleJob('0 0 0 1 1 *', function() { | ||
test.ok(true); | ||
}); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
setTimeout(function() { | ||
job.cancel(); | ||
test.done(); | ||
}, timeout); | ||
clock.tick(timeout); | ||
}, | ||
tearDown: function(cb) { | ||
console.log('restore'); | ||
clock.restore(); | ||
cb(); | ||
} | ||
clock.tick(timeout); | ||
}, | ||
tearDown: function(cb) { | ||
clock.restore(); | ||
cb(); | ||
} | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
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
1313
43856
47