@nocobase/plugin-auth 0.11.1-alpha.5 → 0.12.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.d.ts +2 -2
- package/client.js +1 -1
- package/dist/client/index.js +729 -0
- package/dist/index.js +20 -0
- package/dist/locale/zh-CN.js +20 -0
- package/dist/node_modules/cron/LICENSE +8 -0
- package/dist/node_modules/cron/lib/cron.js +1 -0
- package/dist/node_modules/cron/lib/job.js +215 -0
- package/dist/node_modules/cron/lib/time.js +817 -0
- package/dist/node_modules/cron/package.json +1 -0
- package/{lib → dist}/preset.d.ts +1 -1
- package/dist/preset.js +11 -0
- package/dist/server/actions/auth.js +22 -0
- package/dist/server/actions/authenticators.js +83 -0
- package/{src/server/basic-auth.ts → dist/server/basic-auth.js} +41 -42
- package/dist/server/collections/authenticators.js +97 -0
- package/dist/server/collections/token-blacklist.js +21 -0
- package/dist/server/collections/users-authenticators.js +71 -0
- package/dist/server/index.js +21 -0
- package/dist/server/locale/en-US.js +12 -0
- package/dist/server/locale/fr-FR.js +12 -0
- package/dist/server/locale/index.js +26 -0
- package/dist/server/locale/ja-JP.js +8 -0
- package/dist/server/locale/pt-BR.js +12 -0
- package/dist/server/locale/zh-CN.js +13 -0
- package/dist/server/migrations/20230506152253-basic-authenticator.js +26 -0
- package/dist/server/migrations/20230607174500-update-basic.js +29 -0
- package/{src/server/model/authenticator.ts → dist/server/model/authenticator.js} +19 -19
- package/dist/server/plugin.js +93 -0
- package/dist/server/token-blacklist.js +62 -0
- package/package.json +17 -28
- package/server.d.ts +2 -2
- package/server.js +1 -1
- package/lib/client/AuthProvider.js +0 -56
- package/lib/client/basic/Options.js +0 -51
- package/lib/client/basic/SigninPage.js +0 -100
- package/lib/client/basic/SignupPage.js +0 -131
- package/lib/client/index.js +0 -27
- package/lib/client/locale/index.js +0 -19
- package/lib/client/settings/Authenticator.js +0 -159
- package/lib/client/settings/Options.js +0 -56
- package/lib/client/settings/authType.js +0 -27
- package/lib/client/settings/schemas/authenticators.js +0 -438
- package/lib/index.js +0 -20
- package/lib/locale/zh-CN.js +0 -23
- package/lib/preset.js +0 -12
- package/lib/server/actions/auth.js +0 -51
- package/lib/server/actions/authenticators.js +0 -131
- package/lib/server/basic-auth.js +0 -183
- package/lib/server/collections/authenticators.js +0 -94
- package/lib/server/collections/token-blacklist.js +0 -21
- package/lib/server/collections/users-authenticators.js +0 -75
- package/lib/server/index.js +0 -20
- package/lib/server/locale/en-US.js +0 -15
- package/lib/server/locale/fr-FR.js +0 -15
- package/lib/server/locale/index.js +0 -27
- package/lib/server/locale/ja-JP.js +0 -11
- package/lib/server/locale/pt-BR.js +0 -15
- package/lib/server/locale/zh-CN.js +0 -16
- package/lib/server/migrations/20230506152253-basic-authenticator.js +0 -40
- package/lib/server/migrations/20230607174500-update-basic.js +0 -43
- package/lib/server/model/authenticator.js +0 -72
- package/lib/server/plugin.js +0 -129
- package/lib/server/token-blacklist.js +0 -82
- package/src/client/AuthProvider.tsx +0 -41
- package/src/client/basic/Options.tsx +0 -31
- package/src/client/basic/SigninPage.tsx +0 -65
- package/src/client/basic/SignupPage.tsx +0 -91
- package/src/client/index.tsx +0 -10
- package/src/client/locale/index.ts +0 -7
- package/src/client/settings/Authenticator.tsx +0 -95
- package/src/client/settings/Options.tsx +0 -34
- package/src/client/settings/authType.ts +0 -18
- package/src/client/settings/schemas/authenticators.ts +0 -402
- package/src/index.ts +0 -1
- package/src/locale/zh-CN.ts +0 -17
- package/src/preset.ts +0 -4
- package/src/server/__tests__/actions.test.ts +0 -142
- package/src/server/__tests__/token-blacklist.test.ts +0 -73
- package/src/server/actions/auth.ts +0 -20
- package/src/server/actions/authenticators.ts +0 -85
- package/src/server/collections/.gitkeep +0 -0
- package/src/server/collections/authenticators.ts +0 -98
- package/src/server/collections/token-blacklist.ts +0 -19
- package/src/server/collections/users-authenticators.ts +0 -73
- package/src/server/index.ts +0 -2
- package/src/server/locale/en-US.ts +0 -10
- package/src/server/locale/fr-FR.ts +0 -10
- package/src/server/locale/index.ts +0 -3
- package/src/server/locale/ja-JP.ts +0 -4
- package/src/server/locale/pt-BR.ts +0 -10
- package/src/server/locale/zh-CN.ts +0 -9
- package/src/server/migrations/20230506152253-basic-authenticator.ts +0 -22
- package/src/server/migrations/20230607174500-update-basic.ts +0 -25
- package/src/server/plugin.ts +0 -92
- package/src/server/token-blacklist.ts +0 -66
- /package/{lib → dist}/client/AuthProvider.d.ts +0 -0
- /package/{lib → dist}/client/basic/Options.d.ts +0 -0
- /package/{lib → dist}/client/basic/SigninPage.d.ts +0 -0
- /package/{lib → dist}/client/basic/SignupPage.d.ts +0 -0
- /package/{lib → dist}/client/index.d.ts +0 -0
- /package/{lib → dist}/client/locale/index.d.ts +0 -0
- /package/{lib → dist}/client/settings/Authenticator.d.ts +0 -0
- /package/{lib → dist}/client/settings/Options.d.ts +0 -0
- /package/{lib → dist}/client/settings/authType.d.ts +0 -0
- /package/{lib → dist}/client/settings/schemas/authenticators.d.ts +0 -0
- /package/{lib → dist}/index.d.ts +0 -0
- /package/{lib → dist}/locale/zh-CN.d.ts +0 -0
- /package/{lib → dist}/server/actions/auth.d.ts +0 -0
- /package/{lib → dist}/server/actions/authenticators.d.ts +0 -0
- /package/{lib → dist}/server/basic-auth.d.ts +0 -0
- /package/{lib → dist}/server/collections/authenticators.d.ts +0 -0
- /package/{lib → dist}/server/collections/token-blacklist.d.ts +0 -0
- /package/{lib → dist}/server/collections/users-authenticators.d.ts +0 -0
- /package/{lib → dist}/server/index.d.ts +0 -0
- /package/{lib → dist}/server/locale/en-US.d.ts +0 -0
- /package/{lib → dist}/server/locale/fr-FR.d.ts +0 -0
- /package/{lib → dist}/server/locale/index.d.ts +0 -0
- /package/{lib → dist}/server/locale/ja-JP.d.ts +0 -0
- /package/{lib → dist}/server/locale/pt-BR.d.ts +0 -0
- /package/{lib → dist}/server/locale/zh-CN.d.ts +0 -0
- /package/{lib → dist}/server/migrations/20230506152253-basic-authenticator.d.ts +0 -0
- /package/{lib → dist}/server/migrations/20230607174500-update-basic.d.ts +0 -0
- /package/{lib → dist}/server/model/authenticator.d.ts +0 -0
- /package/{lib → dist}/server/plugin.d.ts +0 -0
- /package/{lib → dist}/server/token-blacklist.d.ts +0 -0
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
const CONSTRAINTS = [
|
|
2
|
+
[0, 59],
|
|
3
|
+
[0, 59],
|
|
4
|
+
[0, 23],
|
|
5
|
+
[1, 31],
|
|
6
|
+
[0, 11],
|
|
7
|
+
[0, 6]
|
|
8
|
+
];
|
|
9
|
+
const MONTH_CONSTRAINTS = [
|
|
10
|
+
31,
|
|
11
|
+
29, // support leap year...not perfect
|
|
12
|
+
31,
|
|
13
|
+
30,
|
|
14
|
+
31,
|
|
15
|
+
30,
|
|
16
|
+
31,
|
|
17
|
+
31,
|
|
18
|
+
30,
|
|
19
|
+
31,
|
|
20
|
+
30,
|
|
21
|
+
31
|
|
22
|
+
];
|
|
23
|
+
const PARSE_DEFAULTS = ['0', '*', '*', '*', '*', '*'];
|
|
24
|
+
const ALIASES = {
|
|
25
|
+
jan: 0,
|
|
26
|
+
feb: 1,
|
|
27
|
+
mar: 2,
|
|
28
|
+
apr: 3,
|
|
29
|
+
may: 4,
|
|
30
|
+
jun: 5,
|
|
31
|
+
jul: 6,
|
|
32
|
+
aug: 7,
|
|
33
|
+
sep: 8,
|
|
34
|
+
oct: 9,
|
|
35
|
+
nov: 10,
|
|
36
|
+
dec: 11,
|
|
37
|
+
sun: 0,
|
|
38
|
+
mon: 1,
|
|
39
|
+
tue: 2,
|
|
40
|
+
wed: 3,
|
|
41
|
+
thu: 4,
|
|
42
|
+
fri: 5,
|
|
43
|
+
sat: 6
|
|
44
|
+
};
|
|
45
|
+
const TIME_UNITS = [
|
|
46
|
+
'second',
|
|
47
|
+
'minute',
|
|
48
|
+
'hour',
|
|
49
|
+
'dayOfMonth',
|
|
50
|
+
'month',
|
|
51
|
+
'dayOfWeek'
|
|
52
|
+
];
|
|
53
|
+
const TIME_UNITS_LEN = TIME_UNITS.length;
|
|
54
|
+
const PRESETS = {
|
|
55
|
+
'@yearly': '0 0 0 1 0 *',
|
|
56
|
+
'@monthly': '0 0 0 1 * *',
|
|
57
|
+
'@weekly': '0 0 0 * * 0',
|
|
58
|
+
'@daily': '0 0 0 * * *',
|
|
59
|
+
'@hourly': '0 0 * * * *',
|
|
60
|
+
'@minutely': '0 * * * * *',
|
|
61
|
+
'@secondly': '* * * * * *',
|
|
62
|
+
'@weekdays': '0 0 0 * * 1-5',
|
|
63
|
+
'@weekends': '0 0 0 * * 0,6'
|
|
64
|
+
};
|
|
65
|
+
const RE_WILDCARDS = /\*/g;
|
|
66
|
+
const RE_RANGE = /^(\d+)(?:-(\d+))?(?:\/(\d+))?$/g;
|
|
67
|
+
|
|
68
|
+
function CronTime(luxon) {
|
|
69
|
+
function CT(source, zone, utcOffset) {
|
|
70
|
+
this.source = source;
|
|
71
|
+
|
|
72
|
+
if (zone) {
|
|
73
|
+
const dt = luxon.DateTime.fromObject({}, { zone });
|
|
74
|
+
if (dt.invalid) {
|
|
75
|
+
throw new Error('Invalid timezone.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.zone = zone;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof utcOffset !== 'undefined') {
|
|
82
|
+
this.utcOffset = utcOffset;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const that = this;
|
|
86
|
+
TIME_UNITS.forEach(timeUnit => {
|
|
87
|
+
that[timeUnit] = {};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (this.source instanceof Date || this.source instanceof luxon.DateTime) {
|
|
91
|
+
if (this.source instanceof Date) {
|
|
92
|
+
this.source = luxon.DateTime.fromJSDate(this.source);
|
|
93
|
+
}
|
|
94
|
+
this.realDate = true;
|
|
95
|
+
} else {
|
|
96
|
+
this._parse(this.source);
|
|
97
|
+
this._verifyParse();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
CT.prototype = {
|
|
102
|
+
/*
|
|
103
|
+
* Ensure that the syntax parsed correctly and correct the specified values if needed.
|
|
104
|
+
*/
|
|
105
|
+
_verifyParse: function () {
|
|
106
|
+
const months = Object.keys(this.month);
|
|
107
|
+
const dom = Object.keys(this.dayOfMonth);
|
|
108
|
+
let ok = false;
|
|
109
|
+
|
|
110
|
+
/* if a dayOfMonth is not found in all months, we only need to fix the last
|
|
111
|
+
wrong month to prevent infinite loop */
|
|
112
|
+
let lastWrongMonth = NaN;
|
|
113
|
+
for (let i = 0; i < months.length; i++) {
|
|
114
|
+
const m = months[i];
|
|
115
|
+
const con = MONTH_CONSTRAINTS[parseInt(m, 10)];
|
|
116
|
+
|
|
117
|
+
for (let j = 0; j < dom.length; j++) {
|
|
118
|
+
const day = dom[j];
|
|
119
|
+
if (day <= con) {
|
|
120
|
+
ok = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!ok) {
|
|
125
|
+
// save the month in order to be fixed if all months fails (infinite loop)
|
|
126
|
+
lastWrongMonth = m;
|
|
127
|
+
console.warn(`Month '${m}' is limited to '${con}' days.`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// infinite loop detected (dayOfMonth is not found in all months)
|
|
132
|
+
if (!ok) {
|
|
133
|
+
const notOkCon = MONTH_CONSTRAINTS[parseInt(lastWrongMonth, 10)];
|
|
134
|
+
for (let k = 0; k < dom.length; k++) {
|
|
135
|
+
const notOkDay = dom[k];
|
|
136
|
+
if (notOkDay > notOkCon) {
|
|
137
|
+
delete this.dayOfMonth[notOkDay];
|
|
138
|
+
const fixedDay = Number(notOkDay) % notOkCon;
|
|
139
|
+
this.dayOfMonth[fixedDay] = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Calculate the "next" scheduled time
|
|
147
|
+
*/
|
|
148
|
+
sendAt: function (i) {
|
|
149
|
+
let date = this.realDate ? this.source : luxon.DateTime.local();
|
|
150
|
+
if (this.zone) {
|
|
151
|
+
date = date.setZone(this.zone);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (typeof this.utcOffset !== 'undefined') {
|
|
155
|
+
let offset =
|
|
156
|
+
this.utcOffset >= 60 || this.utcOffset <= -60
|
|
157
|
+
? this.utcOffset / 60
|
|
158
|
+
: this.utcOffset;
|
|
159
|
+
offset = parseInt(offset);
|
|
160
|
+
|
|
161
|
+
let utcZone = 'UTC';
|
|
162
|
+
if (offset < 0) {
|
|
163
|
+
utcZone += offset;
|
|
164
|
+
} else if (offset > 0) {
|
|
165
|
+
utcZone += `+${offset}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
date = date.setZone(utcZone);
|
|
169
|
+
|
|
170
|
+
if (date.invalid) {
|
|
171
|
+
throw new Error('ERROR: You specified an invalid UTC offset.');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (this.realDate) {
|
|
176
|
+
if (luxon.DateTime.local() > date) {
|
|
177
|
+
throw new Error('WARNING: Date in past. Will never be fired.');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return date;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isNaN(i) || i < 0) {
|
|
184
|
+
// just get the next scheduled time
|
|
185
|
+
return this._getNextDateFrom(date);
|
|
186
|
+
} else {
|
|
187
|
+
// return the next schedule times
|
|
188
|
+
const dates = [];
|
|
189
|
+
for (; i > 0; i--) {
|
|
190
|
+
date = this._getNextDateFrom(date);
|
|
191
|
+
dates.push(date);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return dates;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get the number of milliseconds in the future at which to fire our callbacks.
|
|
200
|
+
*/
|
|
201
|
+
getTimeout: function () {
|
|
202
|
+
return Math.max(-1, this.sendAt() - luxon.DateTime.local());
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* writes out a cron string
|
|
207
|
+
*/
|
|
208
|
+
toString: function () {
|
|
209
|
+
return this.toJSON().join(' ');
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Json representation of the parsed cron syntax.
|
|
214
|
+
*/
|
|
215
|
+
toJSON: function () {
|
|
216
|
+
const self = this;
|
|
217
|
+
return TIME_UNITS.map(function (timeName) {
|
|
218
|
+
return self._wcOrAll(timeName);
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
getNextDateFrom: function (start, zone) {
|
|
223
|
+
return this._getNextDateFrom(start, zone);
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get next date matching the specified cron time.
|
|
228
|
+
*
|
|
229
|
+
* Algorithm:
|
|
230
|
+
* - Start with a start date and a parsed crontime.
|
|
231
|
+
* - Loop until 5 seconds have passed, or we found the next date.
|
|
232
|
+
* - Within the loop:
|
|
233
|
+
* - If it took longer than 5 seconds to select a date, throw an exception.
|
|
234
|
+
* - Find the next month to run at.
|
|
235
|
+
* - Find the next day of the month to run at.
|
|
236
|
+
* - Find the next day of the week to run at.
|
|
237
|
+
* - Find the next hour to run at.
|
|
238
|
+
* - Find the next minute to run at.
|
|
239
|
+
* - Find the next second to run at.
|
|
240
|
+
* - Check that the chosen time does not equal the current execution.
|
|
241
|
+
* - Return the selected date object.
|
|
242
|
+
*/
|
|
243
|
+
_getNextDateFrom: function (start, zone) {
|
|
244
|
+
if (start instanceof Date) {
|
|
245
|
+
start = luxon.DateTime.fromJSDate(start);
|
|
246
|
+
}
|
|
247
|
+
let date = start;
|
|
248
|
+
const firstDate = start.toMillis();
|
|
249
|
+
if (zone) {
|
|
250
|
+
date = date.setZone(zone);
|
|
251
|
+
}
|
|
252
|
+
if (!this.realDate) {
|
|
253
|
+
if (date.millisecond > 0) {
|
|
254
|
+
date = date.set({ millisecond: 0, second: date.second + 1 });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (date.invalid) {
|
|
259
|
+
throw new Error('ERROR: You specified an invalid date.');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// it shouldn't take more than 5 seconds to find the next execution time
|
|
263
|
+
// being very generous with this. Throw error if it takes too long to find the next time to protect from
|
|
264
|
+
// infinite loop.
|
|
265
|
+
const timeout = Date.now() + 5000;
|
|
266
|
+
// determine next date
|
|
267
|
+
while (true) {
|
|
268
|
+
const diff = date - start;
|
|
269
|
+
|
|
270
|
+
// hard stop if the current date is after the expected execution
|
|
271
|
+
if (Date.now() > timeout) {
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Something went wrong. It took over five seconds to find the next execution time for the cron job.
|
|
274
|
+
Please refer to the canonical issue (https://github.com/kelektiv/node-cron/issues/467) and provide the following string if you would like to help debug:
|
|
275
|
+
Time Zone: ${zone || '""'} - Cron String: ${this} - UTC offset: ${date.offset}
|
|
276
|
+
- current Date: ${luxon.DateTime.local().toString()}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (
|
|
281
|
+
!(date.month - 1 in this.month) &&
|
|
282
|
+
Object.keys(this.month).length !== 12
|
|
283
|
+
) {
|
|
284
|
+
date = date.plus({ months: 1 });
|
|
285
|
+
date = date.set({ day: 1, hour: 0, minute: 0, second: 0 });
|
|
286
|
+
|
|
287
|
+
if (this._forwardDSTJump(0, 0, date)) {
|
|
288
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
289
|
+
date = newDate;
|
|
290
|
+
if (done) break;
|
|
291
|
+
}
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (
|
|
296
|
+
!(date.day in this.dayOfMonth) &&
|
|
297
|
+
Object.keys(this.dayOfMonth).length !== 31 &&
|
|
298
|
+
!(
|
|
299
|
+
date.getWeekDay() in this.dayOfWeek &&
|
|
300
|
+
Object.keys(this.dayOfWeek).length !== 7
|
|
301
|
+
)
|
|
302
|
+
) {
|
|
303
|
+
date = date.plus({ days: 1 });
|
|
304
|
+
date = date.set({ hour: 0, minute: 0, second: 0 });
|
|
305
|
+
|
|
306
|
+
if (this._forwardDSTJump(0, 0, date)) {
|
|
307
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
308
|
+
date = newDate;
|
|
309
|
+
if (done) break;
|
|
310
|
+
}
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (
|
|
315
|
+
!(date.getWeekDay() in this.dayOfWeek) &&
|
|
316
|
+
Object.keys(this.dayOfWeek).length !== 7 &&
|
|
317
|
+
!(
|
|
318
|
+
date.day in this.dayOfMonth &&
|
|
319
|
+
Object.keys(this.dayOfMonth).length !== 31
|
|
320
|
+
)
|
|
321
|
+
) {
|
|
322
|
+
date = date.plus({ days: 1 });
|
|
323
|
+
date = date.set({ hour: 0, minute: 0, second: 0 });
|
|
324
|
+
if (this._forwardDSTJump(0, 0, date)) {
|
|
325
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
326
|
+
date = newDate;
|
|
327
|
+
if (done) break;
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!(date.hour in this.hour) && Object.keys(this.hour).length !== 24) {
|
|
333
|
+
const expectedHour =
|
|
334
|
+
date.hour === 23 && diff > 86400000 ? 0 : date.hour + 1;
|
|
335
|
+
const expectedMinute = date.minute; // expect no change.
|
|
336
|
+
|
|
337
|
+
date = date.set({ hour: expectedHour });
|
|
338
|
+
date = date.set({ minute: 0, second: 0 });
|
|
339
|
+
|
|
340
|
+
// When this is the case, Asking luxon to go forward by 1 hour actually made us go forward by more hours...
|
|
341
|
+
// This indicates that somewhere between these two time points, a forward DST adjustment has happened.
|
|
342
|
+
// When this happens, the job should be scheduled to execute as though the time has come when the jump is made.
|
|
343
|
+
// Therefore, the job should be scheduled on the first tick after the forward jump.
|
|
344
|
+
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
345
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
346
|
+
date = newDate;
|
|
347
|
+
if (done) break;
|
|
348
|
+
}
|
|
349
|
+
// backwards jumps do not seem to have any problems (i.e. double activations),
|
|
350
|
+
// so they need not be handled in a similar way.
|
|
351
|
+
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
!(date.minute in this.minute) &&
|
|
357
|
+
Object.keys(this.minute).length !== 60
|
|
358
|
+
) {
|
|
359
|
+
const expectedMinute =
|
|
360
|
+
date.minute === 59 && diff > 3600000 ? 0 : date.minute + 1;
|
|
361
|
+
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
|
|
362
|
+
|
|
363
|
+
date = date.set({ minute: expectedMinute });
|
|
364
|
+
date = date.set({ second: 0 });
|
|
365
|
+
|
|
366
|
+
// Same case as with hours: DST forward jump.
|
|
367
|
+
// This must be accounted for if a minute increment pushed us to a jumping point.
|
|
368
|
+
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
369
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
370
|
+
date = newDate;
|
|
371
|
+
if (done) break;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (
|
|
378
|
+
!(date.second in this.second) &&
|
|
379
|
+
Object.keys(this.second).length !== 60
|
|
380
|
+
) {
|
|
381
|
+
const expectedSecond =
|
|
382
|
+
date.second === 59 && diff > 60000 ? 0 : date.second + 1;
|
|
383
|
+
const expectedMinute = date.minute + (expectedSecond === 60);
|
|
384
|
+
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
|
|
385
|
+
|
|
386
|
+
date = date.set({ second: expectedSecond });
|
|
387
|
+
|
|
388
|
+
// Seconds can cause it too, imagine 21:59:59 -> 23:00:00.
|
|
389
|
+
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
390
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
391
|
+
date = newDate;
|
|
392
|
+
if (done) break;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (date.toMillis() === firstDate) {
|
|
399
|
+
const expectedSecond = date.second + 1;
|
|
400
|
+
const expectedMinute = date.minute + (expectedSecond === 60);
|
|
401
|
+
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0);
|
|
402
|
+
|
|
403
|
+
date = date.set({ second: expectedSecond });
|
|
404
|
+
|
|
405
|
+
// Same as always.
|
|
406
|
+
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
407
|
+
const [done, newDate] = this._findPreviousDSTJump(date);
|
|
408
|
+
date = newDate;
|
|
409
|
+
if (done) break;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return date;
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Search backwards in time 1 minute at a time, to detect a DST forward jump.
|
|
423
|
+
* When the jump is found, the range of the jump is investigated to check for acceptable cron times.
|
|
424
|
+
*
|
|
425
|
+
* A pair is returned, whose first is a boolean representing if an acceptable time was found inside the jump,
|
|
426
|
+
* and whose second is a DateTime representing the first millisecond after the jump.
|
|
427
|
+
*
|
|
428
|
+
* The input date is expected to be decently close to a DST jump.
|
|
429
|
+
* Up to a day in the past is checked before an error is thrown.
|
|
430
|
+
* @param date
|
|
431
|
+
* @return [boolean, DateTime]
|
|
432
|
+
*/
|
|
433
|
+
_findPreviousDSTJump: function (date) {
|
|
434
|
+
/** @type number */
|
|
435
|
+
let expectedMinute, expectedHour, actualMinute, actualHour;
|
|
436
|
+
/** @type DateTime */
|
|
437
|
+
let maybeJumpingPoint = date;
|
|
438
|
+
|
|
439
|
+
// representing one day of backwards checking. If this is hit, the input must be wrong.
|
|
440
|
+
const iterationLimit = 60 * 24;
|
|
441
|
+
let iteration = 0;
|
|
442
|
+
do {
|
|
443
|
+
if (++iteration > iterationLimit) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
`ERROR: This DST checking related function assumes the input DateTime (${date.toISO()}) is within 24 hours of a DST jump.`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
expectedMinute = maybeJumpingPoint.minute - 1;
|
|
450
|
+
expectedHour = maybeJumpingPoint.hour;
|
|
451
|
+
|
|
452
|
+
if (expectedMinute < 0) {
|
|
453
|
+
expectedMinute += 60;
|
|
454
|
+
expectedHour = (expectedHour + 24 - 1) % 24; // Subtract 1 hour, but we must account for the -1 case.
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
maybeJumpingPoint = maybeJumpingPoint.minus({ minute: 1 });
|
|
458
|
+
|
|
459
|
+
actualMinute = maybeJumpingPoint.minute;
|
|
460
|
+
actualHour = maybeJumpingPoint.hour;
|
|
461
|
+
} while (expectedMinute === actualMinute && expectedHour === actualHour);
|
|
462
|
+
|
|
463
|
+
// Setting the seconds and milliseconds to zero is necessary for two reasons:
|
|
464
|
+
// Firstly, the range checking function needs the earliest moment after the jump.
|
|
465
|
+
// Secondly, this DateTime may be used for scheduling jobs, if there existed a job in the skipped range.
|
|
466
|
+
const afterJumpingPoint = maybeJumpingPoint
|
|
467
|
+
.plus({ minute: 1 }) // back to the first minute _after_ the jump
|
|
468
|
+
.set({ seconds: 0, millisecond: 0 });
|
|
469
|
+
|
|
470
|
+
// Get the lower bound of the range to check as well. This only has to be accurate down to minutes.
|
|
471
|
+
const beforeJumpingPoint = afterJumpingPoint.minus({ second: 1 });
|
|
472
|
+
|
|
473
|
+
if (
|
|
474
|
+
date.month in this.month &&
|
|
475
|
+
date.day in this.dayOfMonth &&
|
|
476
|
+
date.getWeekDay() in this.dayOfWeek
|
|
477
|
+
) {
|
|
478
|
+
return [
|
|
479
|
+
this._checkTimeInSkippedRange(beforeJumpingPoint, afterJumpingPoint),
|
|
480
|
+
afterJumpingPoint
|
|
481
|
+
];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// no valid time in the range for sure, units that didn't change from the skip mismatch.
|
|
485
|
+
return [false, afterJumpingPoint];
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Given 2 DateTimes, which represent 1 second before and immediately after a DST forward jump,
|
|
490
|
+
* checks if a time in the skipped range would have been a valid CronJob time.
|
|
491
|
+
*
|
|
492
|
+
* Could technically work with just one of these values, extracting the other by adding or subtracting seconds.
|
|
493
|
+
* However, this couples the input DateTime to actually being tied to a DST jump,
|
|
494
|
+
* which would make the function harder to test.
|
|
495
|
+
* This way the logic just tests a range of minutes and hours, regardless if there are skipped time points underneath.
|
|
496
|
+
*
|
|
497
|
+
* Assumes the DST jump started no earlier than 0:00 and jumped forward by at least 1 minute, to at most 23:59.
|
|
498
|
+
* i.e. The day is assumed constant, but the jump is not assumed to be an hour long.
|
|
499
|
+
* Empirically, it is almost always one hour, but very, very rarely 30 minutes.
|
|
500
|
+
*
|
|
501
|
+
* Assumes dayOfWeek, dayOfMonth and month match all match, so only the hours, minutes and seconds are to be checked.
|
|
502
|
+
* @param {DateTime} beforeJumpingPoint
|
|
503
|
+
* @param {DateTime} afterJumpingPoint
|
|
504
|
+
* @returns {boolean}
|
|
505
|
+
*/
|
|
506
|
+
_checkTimeInSkippedRange: function (beforeJumpingPoint, afterJumpingPoint) {
|
|
507
|
+
// start by getting the first minute & hour inside the skipped range.
|
|
508
|
+
const startingMinute = (beforeJumpingPoint.minute + 1) % 60;
|
|
509
|
+
const startingHour =
|
|
510
|
+
(beforeJumpingPoint.hour + (startingMinute === 0)) % 24;
|
|
511
|
+
|
|
512
|
+
const hourRangeSize = afterJumpingPoint.hour - startingHour + 1;
|
|
513
|
+
const isHourJump = startingMinute === 0 && afterJumpingPoint.minute === 0;
|
|
514
|
+
|
|
515
|
+
// There exist DST jumps other than 1 hour long, and the function is built to deal with it.
|
|
516
|
+
// It may be overkill to assume some cases, but it shouldn't cost much at runtime.
|
|
517
|
+
// https://en.wikipedia.org/wiki/Daylight_saving_time_by_country
|
|
518
|
+
if (hourRangeSize === 2 && isHourJump) {
|
|
519
|
+
// Exact 1 hour jump, most common real-world case.
|
|
520
|
+
// There is no need to check minutes and seconds, as any value would suffice.
|
|
521
|
+
return startingHour in this.hour;
|
|
522
|
+
} else if (hourRangeSize === 1) {
|
|
523
|
+
// less than 1 hour jump, rare but does exist.
|
|
524
|
+
return (
|
|
525
|
+
startingHour in this.hour &&
|
|
526
|
+
this._checkTimeInSkippedRangeSingleHour(
|
|
527
|
+
startingMinute,
|
|
528
|
+
afterJumpingPoint.minute
|
|
529
|
+
)
|
|
530
|
+
);
|
|
531
|
+
} else {
|
|
532
|
+
// non-round or multi-hour jump. (does not exist in the real world at the time of writing)
|
|
533
|
+
return this._checkTimeInSkippedRangeMultiHour(
|
|
534
|
+
startingHour,
|
|
535
|
+
startingMinute,
|
|
536
|
+
afterJumpingPoint.hour,
|
|
537
|
+
afterJumpingPoint.minute
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Component of checking if a CronJob time existed in a DateTime range skipped by DST.
|
|
544
|
+
* This subroutine makes a further assumption that the skipped range is fully contained in one hour,
|
|
545
|
+
* and that all other larger units are valid for the job.
|
|
546
|
+
*
|
|
547
|
+
* for example a jump from 02:00:00 to 02:30:00, but not from 02:00:00 to 03:00:00.
|
|
548
|
+
* @see _checkTimeInSkippedRange
|
|
549
|
+
*
|
|
550
|
+
* This is done by checking if any minute in startMinute - endMinute is valid, excluding endMinute.
|
|
551
|
+
* For endMinute, there is only a match if the 0th second is a valid time.
|
|
552
|
+
*/
|
|
553
|
+
_checkTimeInSkippedRangeSingleHour: function (startMinute, endMinute) {
|
|
554
|
+
for (let minute = startMinute; minute < endMinute; ++minute) {
|
|
555
|
+
if (minute in this.minute) return true;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Unless the very last second of the jump matched, there is no match.
|
|
559
|
+
return endMinute in this.minute && 0 in this.second;
|
|
560
|
+
},
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Component of checking if a CronJob time existed in a DateTime range skipped by DST.
|
|
564
|
+
* This subroutine assumes the jump touches at least 2 hours, but the jump does not necessarily fully contain these hours.
|
|
565
|
+
*
|
|
566
|
+
* @see _checkTimeInSkippedRange
|
|
567
|
+
*
|
|
568
|
+
* This is done by defining the minutes to check for the first and last hour,
|
|
569
|
+
* and checking all 60 minutes for any hours in between them.
|
|
570
|
+
*
|
|
571
|
+
* If any hour x minute combination is a valid time, true is returned.
|
|
572
|
+
* The endMinute x endHour combination is only checked with the 0th second, since the rest would be out of the range.
|
|
573
|
+
*
|
|
574
|
+
* @param startHour {number}
|
|
575
|
+
* @param startMinute {number}
|
|
576
|
+
* @param endHour {number}
|
|
577
|
+
* @param endMinute {number}
|
|
578
|
+
*/
|
|
579
|
+
_checkTimeInSkippedRangeMultiHour: function (
|
|
580
|
+
startHour,
|
|
581
|
+
startMinute,
|
|
582
|
+
endHour,
|
|
583
|
+
endMinute
|
|
584
|
+
) {
|
|
585
|
+
if (startHour >= endHour) {
|
|
586
|
+
throw new Error(
|
|
587
|
+
`ERROR: This DST checking related function assumes the forward jump starting hour (${startHour}) is less than the end hour (${endHour})`
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** @type number[] */
|
|
592
|
+
const firstHourMinuteRange = Array.from(
|
|
593
|
+
{ length: 60 - startMinute },
|
|
594
|
+
(_, k) => startMinute + k
|
|
595
|
+
);
|
|
596
|
+
/** @type {number[]} The final minute is not contained on purpose. Every minute in this range represents one for which any second is valid. */
|
|
597
|
+
const lastHourMinuteRange = Array.from(
|
|
598
|
+
{ length: endMinute },
|
|
599
|
+
(_, k) => k
|
|
600
|
+
);
|
|
601
|
+
/** @type number[] */
|
|
602
|
+
const middleHourMinuteRange = Array.from({ length: 60 }, (_, k) => k);
|
|
603
|
+
|
|
604
|
+
/** @type (number) => number[] */
|
|
605
|
+
const selectRange = forHour => {
|
|
606
|
+
if (forHour === startHour) {
|
|
607
|
+
return firstHourMinuteRange;
|
|
608
|
+
} else if (forHour === endHour) {
|
|
609
|
+
return lastHourMinuteRange;
|
|
610
|
+
} else {
|
|
611
|
+
return middleHourMinuteRange;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Include the endHour: Selecting the right range still ensures no values outside the skip are checked.
|
|
616
|
+
for (let hour = startHour; hour <= endHour; ++hour) {
|
|
617
|
+
if (!(hour in this.hour)) continue;
|
|
618
|
+
|
|
619
|
+
// The hour matches, so if the minute is in the range, we have a match!
|
|
620
|
+
const usingRange = selectRange(hour);
|
|
621
|
+
|
|
622
|
+
for (const minute of usingRange) {
|
|
623
|
+
// All minutes in any of the selected ranges represent minutes which are fully contained in the jump,
|
|
624
|
+
// So we need not check the seconds. If the minute is in there, it is a match.
|
|
625
|
+
if (minute in this.minute) return true;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// The endMinute of the endHour was not checked in the loop, because only the 0th second of it is in the range.
|
|
630
|
+
// Arriving here means no match was found yet, but this final check may turn up as a match.
|
|
631
|
+
return (
|
|
632
|
+
endHour in this.hour && endMinute in this.minute && 0 in this.second
|
|
633
|
+
);
|
|
634
|
+
},
|
|
635
|
+
/**
|
|
636
|
+
* Given expected and actual hours and minutes, report if a DST forward jump occurred.
|
|
637
|
+
*
|
|
638
|
+
* This is the case when the expected is smaller than the acutal.
|
|
639
|
+
*
|
|
640
|
+
* It is not sufficient to check only hours, because some parts of the world apply DST by shifting in minutes.
|
|
641
|
+
* Better to account for it by checking minutes too, before an Australian of Lord Howe Island call us.
|
|
642
|
+
* @param expectedHour
|
|
643
|
+
* @param expectedMinute
|
|
644
|
+
* @param {DateTime} actualDate
|
|
645
|
+
*/
|
|
646
|
+
_forwardDSTJump: function (expectedHour, expectedMinute, actualDate) {
|
|
647
|
+
const actualHour = actualDate.hour;
|
|
648
|
+
const actualMinute = actualDate.minute;
|
|
649
|
+
|
|
650
|
+
const hoursJumped = expectedHour % 24 < actualHour;
|
|
651
|
+
const minutesJumped = expectedMinute % 60 < actualMinute;
|
|
652
|
+
|
|
653
|
+
return hoursJumped || minutesJumped;
|
|
654
|
+
},
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* wildcard, or all params in array (for to string)
|
|
658
|
+
*/
|
|
659
|
+
_wcOrAll: function (type) {
|
|
660
|
+
if (this._hasAll(type)) {
|
|
661
|
+
return '*';
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const all = [];
|
|
665
|
+
for (const time in this[type]) {
|
|
666
|
+
all.push(time);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return all.join(',');
|
|
670
|
+
},
|
|
671
|
+
|
|
672
|
+
_hasAll: function (type) {
|
|
673
|
+
const constraints = CONSTRAINTS[TIME_UNITS.indexOf(type)];
|
|
674
|
+
|
|
675
|
+
for (let i = constraints[0], n = constraints[1]; i < n; i++) {
|
|
676
|
+
if (!(i in this[type])) {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return true;
|
|
682
|
+
},
|
|
683
|
+
|
|
684
|
+
/*
|
|
685
|
+
* Parse the cron syntax into something useful for selecting the next execution time.
|
|
686
|
+
*
|
|
687
|
+
* Algorithm:
|
|
688
|
+
* - Replace preset
|
|
689
|
+
* - Replace aliases in the source.
|
|
690
|
+
* - Trim string and split for processing.
|
|
691
|
+
* - Loop over split options (ms -> month):
|
|
692
|
+
* - Get the value (or default) in the current position.
|
|
693
|
+
* - Parse the value.
|
|
694
|
+
*/
|
|
695
|
+
_parse: function (source) {
|
|
696
|
+
source = source.toLowerCase();
|
|
697
|
+
|
|
698
|
+
if (source in PRESETS) {
|
|
699
|
+
source = PRESETS[source];
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
source = source.replace(/[a-z]{1,3}/gi, alias => {
|
|
703
|
+
if (alias in ALIASES) {
|
|
704
|
+
return ALIASES[alias];
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
throw new Error(`Unknown alias: ${alias}`);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const units = source.trim().split(/\s+/);
|
|
711
|
+
|
|
712
|
+
// seconds are optional
|
|
713
|
+
if (units.length < TIME_UNITS_LEN - 1) {
|
|
714
|
+
throw new Error('Too few fields');
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (units.length > TIME_UNITS_LEN) {
|
|
718
|
+
throw new Error('Too many fields');
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const unitsLen = units.length;
|
|
722
|
+
for (let i = 0; i < TIME_UNITS_LEN; i++) {
|
|
723
|
+
// If the split source string doesn't contain all digits,
|
|
724
|
+
// assume defaults for first n missing digits.
|
|
725
|
+
// This adds support for 5-digit standard cron syntax
|
|
726
|
+
const cur = units[i - (TIME_UNITS_LEN - unitsLen)] || PARSE_DEFAULTS[i];
|
|
727
|
+
this._parseField(cur, TIME_UNITS[i], CONSTRAINTS[i]);
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
/*
|
|
732
|
+
* Parse individual field from the cron syntax provided.
|
|
733
|
+
*
|
|
734
|
+
* Algorithm:
|
|
735
|
+
* - Split field by commas aand check for wildcards to ensure proper user.
|
|
736
|
+
* - Replace wildcard values with <low>-<high> boundaries.
|
|
737
|
+
* - Split field by commas and then iterate over ranges inside field.
|
|
738
|
+
* - If range matches pattern then map over matches using replace (to parse the range by the regex pattern)
|
|
739
|
+
* - Starting with the lower bounds of the range iterate by step up to the upper bounds and toggle the CronTime field value flag on.
|
|
740
|
+
*/
|
|
741
|
+
_parseField: function (value, type, constraints) {
|
|
742
|
+
const typeObj = this[type];
|
|
743
|
+
let pointer;
|
|
744
|
+
const low = constraints[0];
|
|
745
|
+
const high = constraints[1];
|
|
746
|
+
|
|
747
|
+
const fields = value.split(',');
|
|
748
|
+
fields.forEach(field => {
|
|
749
|
+
const wildcardIndex = field.indexOf('*');
|
|
750
|
+
if (wildcardIndex !== -1 && wildcardIndex !== 0) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`Field (${field}) has an invalid wildcard expression`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// * is a shortcut to [low-high] range for the field
|
|
758
|
+
value = value.replace(RE_WILDCARDS, `${low}-${high}`);
|
|
759
|
+
|
|
760
|
+
// commas separate information, so split based on those
|
|
761
|
+
const allRanges = value.split(',');
|
|
762
|
+
|
|
763
|
+
for (let i = 0; i < allRanges.length; i++) {
|
|
764
|
+
if (allRanges[i].match(RE_RANGE)) {
|
|
765
|
+
allRanges[i].replace(RE_RANGE, ($0, lower, upper, step) => {
|
|
766
|
+
lower = parseInt(lower, 10);
|
|
767
|
+
upper = parseInt(upper, 10) || undefined;
|
|
768
|
+
|
|
769
|
+
const wasStepDefined = !isNaN(parseInt(step, 10));
|
|
770
|
+
if (step === '0') {
|
|
771
|
+
throw new Error(`Field (${type}) has a step of zero`);
|
|
772
|
+
}
|
|
773
|
+
step = parseInt(step, 10) || 1;
|
|
774
|
+
|
|
775
|
+
if (upper && lower > upper) {
|
|
776
|
+
throw new Error(`Field (${type}) has an invalid range`);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const outOfRangeError =
|
|
780
|
+
lower < low ||
|
|
781
|
+
(upper && upper > high) ||
|
|
782
|
+
(!upper && lower > high);
|
|
783
|
+
|
|
784
|
+
if (outOfRangeError) {
|
|
785
|
+
throw new Error(`Field value (${value}) is out of range`);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Positive integer higher than constraints[0]
|
|
789
|
+
lower = Math.min(Math.max(low, ~~Math.abs(lower)), high);
|
|
790
|
+
|
|
791
|
+
// Positive integer lower than constraints[1]
|
|
792
|
+
if (upper) {
|
|
793
|
+
upper = Math.min(high, ~~Math.abs(upper));
|
|
794
|
+
} else {
|
|
795
|
+
// If step is provided, the default upper range is the highest value
|
|
796
|
+
upper = wasStepDefined ? high : lower;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Count from the lower barrier to the upper
|
|
800
|
+
pointer = lower;
|
|
801
|
+
|
|
802
|
+
do {
|
|
803
|
+
typeObj[pointer] = true; // mutates the field objects values inside CronTime
|
|
804
|
+
pointer += step;
|
|
805
|
+
} while (pointer <= upper);
|
|
806
|
+
});
|
|
807
|
+
} else {
|
|
808
|
+
throw new Error(`Field (${type}) cannot be parsed`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
return CT;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
module.exports = CronTime;
|