@stacksjs/scheduler 0.59.11 → 0.61.0
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/dist/index.js +151 -150
- package/package.json +4 -3
- package/src/job.ts +47 -82
- package/src/schedule.ts +5 -5
- package/src/time.ts +92 -196
- package/src/types/cron.ts +23 -49
- package/src/types/utils.ts +2 -9
- package/src/utils.ts +4 -7
- package/dist/constants.d.ts +0 -74
- package/dist/errors.d.ts +0 -5
- package/dist/index.d.ts +0 -7
- package/dist/job.d.ts +0 -32
- package/dist/schedule.d.ts +0 -29
- package/dist/time.d.ts +0 -159
- package/dist/types/cron.d.ts +0 -49
- package/dist/types/utils.d.ts +0 -3
- package/dist/utils.d.ts +0 -12
package/src/time.ts
CHANGED
|
@@ -14,14 +14,7 @@ import {
|
|
|
14
14
|
TIME_UNITS_MAP,
|
|
15
15
|
} from './constants'
|
|
16
16
|
import { CronError, ExclusiveParametersError } from './errors'
|
|
17
|
-
import type {
|
|
18
|
-
CronJobParams,
|
|
19
|
-
DayOfMonthRange,
|
|
20
|
-
MonthRange,
|
|
21
|
-
Ranges,
|
|
22
|
-
TimeUnit,
|
|
23
|
-
TimeUnitField,
|
|
24
|
-
} from './types/cron'
|
|
17
|
+
import type { CronJobParams, DayOfMonthRange, MonthRange, Ranges, TimeUnit, TimeUnitField } from './types/cron'
|
|
25
18
|
import { getRecordKeys } from './utils'
|
|
26
19
|
|
|
27
20
|
export class CronTime {
|
|
@@ -37,42 +30,29 @@ export class CronTime {
|
|
|
37
30
|
private month: TimeUnitField<'month'> = {}
|
|
38
31
|
private dayOfWeek: TimeUnitField<'dayOfWeek'> = {}
|
|
39
32
|
|
|
40
|
-
constructor(
|
|
41
|
-
|
|
42
|
-
timeZone?: CronJobParams['timeZone'],
|
|
43
|
-
utcOffset?: null
|
|
44
|
-
)
|
|
45
|
-
constructor(
|
|
46
|
-
source: CronJobParams['cronTime'],
|
|
47
|
-
timeZone?: null,
|
|
48
|
-
utcOffset?: CronJobParams['utcOffset']
|
|
49
|
-
)
|
|
33
|
+
constructor(source: CronJobParams['cronTime'], timeZone?: CronJobParams['timeZone'], utcOffset?: null)
|
|
34
|
+
constructor(source: CronJobParams['cronTime'], timeZone?: null, utcOffset?: CronJobParams['utcOffset'])
|
|
50
35
|
constructor(
|
|
51
36
|
source: CronJobParams['cronTime'],
|
|
52
37
|
timeZone?: CronJobParams['timeZone'],
|
|
53
38
|
utcOffset?: CronJobParams['utcOffset'],
|
|
54
39
|
) {
|
|
55
40
|
// runtime check for JS users
|
|
56
|
-
if (timeZone != null && utcOffset != null)
|
|
57
|
-
throw new ExclusiveParametersError('timeZone', 'utcOffset')
|
|
41
|
+
if (timeZone != null && utcOffset != null) throw new ExclusiveParametersError('timeZone', 'utcOffset')
|
|
58
42
|
|
|
59
43
|
if (timeZone) {
|
|
60
44
|
const dt = DateTime.fromObject({}, { zone: timeZone })
|
|
61
|
-
if (!dt.isValid)
|
|
62
|
-
throw new CronError('Invalid timezone.')
|
|
45
|
+
if (!dt.isValid) throw new CronError('Invalid timezone.')
|
|
63
46
|
|
|
64
47
|
this.timeZone = timeZone
|
|
65
48
|
}
|
|
66
49
|
|
|
67
|
-
if (utcOffset != null)
|
|
68
|
-
this.utcOffset = utcOffset
|
|
50
|
+
if (utcOffset != null) this.utcOffset = utcOffset
|
|
69
51
|
|
|
70
52
|
if (source instanceof Date || source instanceof DateTime) {
|
|
71
|
-
this.source
|
|
72
|
-
= source instanceof Date ? DateTime.fromJSDate(source) : source
|
|
53
|
+
this.source = source instanceof Date ? DateTime.fromJSDate(source) : source
|
|
73
54
|
this.realDate = true
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
55
|
+
} else {
|
|
76
56
|
this.source = source
|
|
77
57
|
this._parse(this.source)
|
|
78
58
|
this._verifyParse()
|
|
@@ -101,8 +81,7 @@ export class CronTime {
|
|
|
101
81
|
const con = MONTH_CONSTRAINTS[m]
|
|
102
82
|
|
|
103
83
|
for (const day of daysOfMonth) {
|
|
104
|
-
if (day <= con)
|
|
105
|
-
isOk = true
|
|
84
|
+
if (day <= con) isOk = true
|
|
106
85
|
}
|
|
107
86
|
|
|
108
87
|
if (!isOk) {
|
|
@@ -131,12 +110,8 @@ export class CronTime {
|
|
|
131
110
|
sendAt(): DateTime
|
|
132
111
|
sendAt(i: number): DateTime[]
|
|
133
112
|
sendAt(i?: number): DateTime | DateTime[] {
|
|
134
|
-
let date
|
|
135
|
-
|
|
136
|
-
? this.source
|
|
137
|
-
: DateTime.local()
|
|
138
|
-
if (this.timeZone)
|
|
139
|
-
date = date.setZone(this.timeZone)
|
|
113
|
+
let date = this.realDate && this.source instanceof DateTime ? this.source : DateTime.local()
|
|
114
|
+
if (this.timeZone) date = date.setZone(this.timeZone)
|
|
140
115
|
|
|
141
116
|
if (this.utcOffset !== undefined) {
|
|
142
117
|
const sign = this.utcOffset < 0 ? '-' : '+'
|
|
@@ -151,13 +126,11 @@ export class CronTime {
|
|
|
151
126
|
|
|
152
127
|
date = date.setZone(utcZone)
|
|
153
128
|
|
|
154
|
-
if (!date.isValid)
|
|
155
|
-
throw new CronError('ERROR: You specified an invalid UTC offset.')
|
|
129
|
+
if (!date.isValid) throw new CronError('ERROR: You specified an invalid UTC offset.')
|
|
156
130
|
}
|
|
157
131
|
|
|
158
132
|
if (this.realDate) {
|
|
159
|
-
if (DateTime.local() > date)
|
|
160
|
-
throw new CronError('WARNING: Date in past. Will never be fired.')
|
|
133
|
+
if (DateTime.local() > date) throw new CronError('WARNING: Date in past. Will never be fired.')
|
|
161
134
|
|
|
162
135
|
return date
|
|
163
136
|
}
|
|
@@ -166,16 +139,15 @@ export class CronTime {
|
|
|
166
139
|
// just get the next scheduled time
|
|
167
140
|
return this.getNextDateFrom(date)
|
|
168
141
|
}
|
|
169
|
-
else {
|
|
170
|
-
// return the next schedule times
|
|
171
|
-
const dates: DateTime[] = []
|
|
172
|
-
for (; i > 0; i--) {
|
|
173
|
-
date = this.getNextDateFrom(date)
|
|
174
|
-
dates.push(date)
|
|
175
|
-
}
|
|
176
142
|
|
|
177
|
-
|
|
143
|
+
// return the next schedule times
|
|
144
|
+
const dates: DateTime[] = []
|
|
145
|
+
for (; i > 0; i--) {
|
|
146
|
+
date = this.getNextDateFrom(date)
|
|
147
|
+
dates.push(date)
|
|
178
148
|
}
|
|
149
|
+
|
|
150
|
+
return dates
|
|
179
151
|
}
|
|
180
152
|
|
|
181
153
|
/**
|
|
@@ -219,21 +191,17 @@ export class CronTime {
|
|
|
219
191
|
* - Return the selected date object.
|
|
220
192
|
*/
|
|
221
193
|
getNextDateFrom(start: Date | DateTime, timeZone?: string | Zone) {
|
|
222
|
-
if (start instanceof Date)
|
|
223
|
-
start = DateTime.fromJSDate(start)
|
|
194
|
+
if (start instanceof Date) start = DateTime.fromJSDate(start)
|
|
224
195
|
|
|
225
196
|
let date = start
|
|
226
197
|
const firstDate = start.toMillis()
|
|
227
|
-
if (timeZone)
|
|
228
|
-
date = date.setZone(timeZone)
|
|
198
|
+
if (timeZone) date = date.setZone(timeZone)
|
|
229
199
|
|
|
230
200
|
if (!this.realDate) {
|
|
231
|
-
if (date.millisecond > 0)
|
|
232
|
-
date = date.set({ millisecond: 0, second: date.second + 1 })
|
|
201
|
+
if (date.millisecond > 0) date = date.set({ millisecond: 0, second: date.second + 1 })
|
|
233
202
|
}
|
|
234
203
|
|
|
235
|
-
if (!date.isValid)
|
|
236
|
-
throw new CronError('ERROR: You specified an invalid date.')
|
|
204
|
+
if (!date.isValid) throw new CronError('ERROR: You specified an invalid date.')
|
|
237
205
|
|
|
238
206
|
/**
|
|
239
207
|
* maximum match interval is 8 years:
|
|
@@ -252,37 +220,28 @@ export class CronTime {
|
|
|
252
220
|
throw new CronError(
|
|
253
221
|
`Something went wrong. No execution date was found in the next 8 years.
|
|
254
222
|
Please provide the following string if you would like to help debug:
|
|
255
|
-
Time Zone: ${
|
|
256
|
-
timeZone?.toString() ?? '""'
|
|
257
|
-
} - Cron String: ${this.source.toString()} - UTC offset: ${
|
|
223
|
+
Time Zone: ${timeZone?.toString() ?? '""'} - Cron String: ${this.source.toString()} - UTC offset: ${
|
|
258
224
|
date.offset
|
|
259
225
|
} - current Date: ${DateTime.local().toString()}`,
|
|
260
226
|
)
|
|
261
227
|
}
|
|
262
228
|
|
|
263
|
-
if (
|
|
264
|
-
!(date.month in this.month)
|
|
265
|
-
&& Object.keys(this.month).length !== 12
|
|
266
|
-
) {
|
|
229
|
+
if (!(date.month in this.month) && Object.keys(this.month).length !== 12) {
|
|
267
230
|
date = date.plus({ months: 1 })
|
|
268
231
|
date = date.set({ day: 1, hour: 0, minute: 0, second: 0 })
|
|
269
232
|
|
|
270
233
|
if (this._forwardDSTJump(0, 0, date)) {
|
|
271
234
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
272
235
|
date = newDate
|
|
273
|
-
if (isDone)
|
|
274
|
-
break
|
|
236
|
+
if (isDone) break
|
|
275
237
|
}
|
|
276
238
|
continue
|
|
277
239
|
}
|
|
278
240
|
|
|
279
241
|
if (
|
|
280
|
-
!(date.day in this.dayOfMonth)
|
|
281
|
-
|
|
282
|
-
&&
|
|
283
|
-
this._getWeekDay(date) in this.dayOfWeek
|
|
284
|
-
&& Object.keys(this.dayOfWeek).length !== 7
|
|
285
|
-
)
|
|
242
|
+
!(date.day in this.dayOfMonth) &&
|
|
243
|
+
Object.keys(this.dayOfMonth).length !== 31 &&
|
|
244
|
+
!(this._getWeekDay(date) in this.dayOfWeek && Object.keys(this.dayOfWeek).length !== 7)
|
|
286
245
|
) {
|
|
287
246
|
date = date.plus({ days: 1 })
|
|
288
247
|
date = date.set({ hour: 0, minute: 0, second: 0 })
|
|
@@ -290,34 +249,28 @@ export class CronTime {
|
|
|
290
249
|
if (this._forwardDSTJump(0, 0, date)) {
|
|
291
250
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
292
251
|
date = newDate
|
|
293
|
-
if (isDone)
|
|
294
|
-
break
|
|
252
|
+
if (isDone) break
|
|
295
253
|
}
|
|
296
254
|
continue
|
|
297
255
|
}
|
|
298
256
|
|
|
299
257
|
if (
|
|
300
|
-
!(this._getWeekDay(date) in this.dayOfWeek)
|
|
301
|
-
|
|
302
|
-
&&
|
|
303
|
-
date.day in this.dayOfMonth
|
|
304
|
-
&& Object.keys(this.dayOfMonth).length !== 31
|
|
305
|
-
)
|
|
258
|
+
!(this._getWeekDay(date) in this.dayOfWeek) &&
|
|
259
|
+
Object.keys(this.dayOfWeek).length !== 7 &&
|
|
260
|
+
!(date.day in this.dayOfMonth && Object.keys(this.dayOfMonth).length !== 31)
|
|
306
261
|
) {
|
|
307
262
|
date = date.plus({ days: 1 })
|
|
308
263
|
date = date.set({ hour: 0, minute: 0, second: 0 })
|
|
309
264
|
if (this._forwardDSTJump(0, 0, date)) {
|
|
310
265
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
311
266
|
date = newDate
|
|
312
|
-
if (isDone)
|
|
313
|
-
break
|
|
267
|
+
if (isDone) break
|
|
314
268
|
}
|
|
315
269
|
continue
|
|
316
270
|
}
|
|
317
271
|
|
|
318
272
|
if (!(date.hour in this.hour) && Object.keys(this.hour).length !== 24) {
|
|
319
|
-
const expectedHour
|
|
320
|
-
= date.hour === 23 && diff > 86400000 ? 0 : date.hour + 1
|
|
273
|
+
const expectedHour = date.hour === 23 && diff > 86400000 ? 0 : date.hour + 1
|
|
321
274
|
const expectedMinute = date.minute // expect no change.
|
|
322
275
|
|
|
323
276
|
date = date.set({ hour: expectedHour })
|
|
@@ -330,8 +283,7 @@ export class CronTime {
|
|
|
330
283
|
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
331
284
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
332
285
|
date = newDate
|
|
333
|
-
if (isDone)
|
|
334
|
-
break
|
|
286
|
+
if (isDone) break
|
|
335
287
|
}
|
|
336
288
|
// backwards jumps do not seem to have any problems (i.e. double activations),
|
|
337
289
|
// so they need not be handled in a similar way.
|
|
@@ -339,12 +291,8 @@ export class CronTime {
|
|
|
339
291
|
continue
|
|
340
292
|
}
|
|
341
293
|
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
&& Object.keys(this.minute).length !== 60
|
|
345
|
-
) {
|
|
346
|
-
const expectedMinute
|
|
347
|
-
= date.minute === 59 && diff > 3600000 ? 0 : date.minute + 1
|
|
294
|
+
if (!(date.minute in this.minute) && Object.keys(this.minute).length !== 60) {
|
|
295
|
+
const expectedMinute = date.minute === 59 && diff > 3600000 ? 0 : date.minute + 1
|
|
348
296
|
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0)
|
|
349
297
|
|
|
350
298
|
date = date.set({ minute: expectedMinute })
|
|
@@ -355,19 +303,14 @@ export class CronTime {
|
|
|
355
303
|
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
356
304
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
357
305
|
date = newDate
|
|
358
|
-
if (isDone)
|
|
359
|
-
break
|
|
306
|
+
if (isDone) break
|
|
360
307
|
}
|
|
361
308
|
|
|
362
309
|
continue
|
|
363
310
|
}
|
|
364
311
|
|
|
365
|
-
if (
|
|
366
|
-
|
|
367
|
-
&& Object.keys(this.second).length !== 60
|
|
368
|
-
) {
|
|
369
|
-
const expectedSecond
|
|
370
|
-
= date.second === 59 && diff > 60000 ? 0 : date.second + 1
|
|
312
|
+
if (!(date.second in this.second) && Object.keys(this.second).length !== 60) {
|
|
313
|
+
const expectedSecond = date.second === 59 && diff > 60000 ? 0 : date.second + 1
|
|
371
314
|
const expectedMinute = date.minute + (expectedSecond === 60 ? 1 : 0)
|
|
372
315
|
const expectedHour = date.hour + (expectedMinute === 60 ? 1 : 0)
|
|
373
316
|
|
|
@@ -377,8 +320,7 @@ export class CronTime {
|
|
|
377
320
|
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
378
321
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
379
322
|
date = newDate
|
|
380
|
-
if (isDone)
|
|
381
|
-
break
|
|
323
|
+
if (isDone) break
|
|
382
324
|
}
|
|
383
325
|
|
|
384
326
|
continue
|
|
@@ -395,8 +337,7 @@ export class CronTime {
|
|
|
395
337
|
if (this._forwardDSTJump(expectedHour, expectedMinute, date)) {
|
|
396
338
|
const [isDone, newDate] = this._findPreviousDSTJump(date)
|
|
397
339
|
date = newDate
|
|
398
|
-
if (isDone)
|
|
399
|
-
break
|
|
340
|
+
if (isDone) break
|
|
400
341
|
}
|
|
401
342
|
|
|
402
343
|
continue
|
|
@@ -421,8 +362,11 @@ export class CronTime {
|
|
|
421
362
|
* @return [boolean, DateTime]
|
|
422
363
|
*/
|
|
423
364
|
private _findPreviousDSTJump(date: DateTime): [boolean, DateTime] {
|
|
424
|
-
|
|
425
|
-
let
|
|
365
|
+
let expectedMinute: number
|
|
366
|
+
let expectedHour: number
|
|
367
|
+
let actualMinute: number
|
|
368
|
+
let actualHour: number
|
|
369
|
+
|
|
426
370
|
/** @type DateTime */
|
|
427
371
|
let maybeJumpingPoint = date
|
|
428
372
|
|
|
@@ -462,15 +406,8 @@ export class CronTime {
|
|
|
462
406
|
// Get the lower bound of the range to check as well. This only has to be accurate down to minutes.
|
|
463
407
|
const beforeJumpingPoint = afterJumpingPoint.minus({ second: 1 })
|
|
464
408
|
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
&& date.day in this.dayOfMonth
|
|
468
|
-
&& this._getWeekDay(date) in this.dayOfWeek
|
|
469
|
-
) {
|
|
470
|
-
return [
|
|
471
|
-
this._checkTimeInSkippedRange(beforeJumpingPoint, afterJumpingPoint),
|
|
472
|
-
afterJumpingPoint,
|
|
473
|
-
]
|
|
409
|
+
if (date.month + 1 in this.month && date.day in this.dayOfMonth && this._getWeekDay(date) in this.dayOfWeek) {
|
|
410
|
+
return [this._checkTimeInSkippedRange(beforeJumpingPoint, afterJumpingPoint), afterJumpingPoint]
|
|
474
411
|
}
|
|
475
412
|
|
|
476
413
|
// no valid time in the range for sure, units that didn't change from the skip mismatch.
|
|
@@ -495,14 +432,10 @@ export class CronTime {
|
|
|
495
432
|
* @param {DateTime} afterJumpingPoint
|
|
496
433
|
* @returns {boolean} True if a valid CronJob time exists within the skipped DST range, false otherwise.
|
|
497
434
|
*/
|
|
498
|
-
private _checkTimeInSkippedRange(
|
|
499
|
-
beforeJumpingPoint: DateTime,
|
|
500
|
-
afterJumpingPoint: DateTime,
|
|
501
|
-
) {
|
|
435
|
+
private _checkTimeInSkippedRange(beforeJumpingPoint: DateTime, afterJumpingPoint: DateTime) {
|
|
502
436
|
// start by getting the first minute & hour inside the skipped range.
|
|
503
437
|
const startingMinute = (beforeJumpingPoint.minute + 1) % 60
|
|
504
|
-
const startingHour
|
|
505
|
-
= (beforeJumpingPoint.hour + (startingMinute === 0 ? 1 : 0)) % 24
|
|
438
|
+
const startingHour = (beforeJumpingPoint.hour + (startingMinute === 0 ? 1 : 0)) % 24
|
|
506
439
|
|
|
507
440
|
const hourRangeSize = afterJumpingPoint.hour - startingHour + 1
|
|
508
441
|
const isHourJump = startingMinute === 0 && afterJumpingPoint.minute === 0
|
|
@@ -515,25 +448,21 @@ export class CronTime {
|
|
|
515
448
|
// There is no need to check minutes and seconds, as any value would suffice.
|
|
516
449
|
return startingHour in this.hour
|
|
517
450
|
}
|
|
518
|
-
|
|
451
|
+
|
|
452
|
+
if (hourRangeSize === 1) {
|
|
519
453
|
// less than 1 hour jump, rare but does exist.
|
|
520
454
|
return (
|
|
521
|
-
startingHour in this.hour
|
|
522
|
-
&& this._checkTimeInSkippedRangeSingleHour(
|
|
523
|
-
startingMinute,
|
|
524
|
-
afterJumpingPoint.minute,
|
|
525
|
-
)
|
|
526
|
-
)
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
// non-round or multi-hour jump. (does not exist in the real world at the time of writing)
|
|
530
|
-
return this._checkTimeInSkippedRangeMultiHour(
|
|
531
|
-
startingHour,
|
|
532
|
-
startingMinute,
|
|
533
|
-
afterJumpingPoint.hour,
|
|
534
|
-
afterJumpingPoint.minute,
|
|
455
|
+
startingHour in this.hour && this._checkTimeInSkippedRangeSingleHour(startingMinute, afterJumpingPoint.minute)
|
|
535
456
|
)
|
|
536
457
|
}
|
|
458
|
+
|
|
459
|
+
// non-round or multi-hour jump. (does not exist in the real world at the time of writing)
|
|
460
|
+
return this._checkTimeInSkippedRangeMultiHour(
|
|
461
|
+
startingHour,
|
|
462
|
+
startingMinute,
|
|
463
|
+
afterJumpingPoint.hour,
|
|
464
|
+
afterJumpingPoint.minute,
|
|
465
|
+
)
|
|
537
466
|
}
|
|
538
467
|
|
|
539
468
|
/**
|
|
@@ -547,13 +476,9 @@ export class CronTime {
|
|
|
547
476
|
* This is done by checking if any minute in startMinute - endMinute is valid, excluding endMinute.
|
|
548
477
|
* For endMinute, there is only a match if the 0th second is a valid time.
|
|
549
478
|
*/
|
|
550
|
-
private _checkTimeInSkippedRangeSingleHour(
|
|
551
|
-
startMinute: number,
|
|
552
|
-
endMinute: number,
|
|
553
|
-
) {
|
|
479
|
+
private _checkTimeInSkippedRangeSingleHour(startMinute: number, endMinute: number) {
|
|
554
480
|
for (let minute = startMinute; minute < endMinute; ++minute) {
|
|
555
|
-
if (minute in this.minute)
|
|
556
|
-
return true
|
|
481
|
+
if (minute in this.minute) return true
|
|
557
482
|
}
|
|
558
483
|
|
|
559
484
|
// Unless the very last second of the jump matched, there is no match.
|
|
@@ -590,10 +515,7 @@ export class CronTime {
|
|
|
590
515
|
}
|
|
591
516
|
|
|
592
517
|
/** @type number[] */
|
|
593
|
-
const firstHourMinuteRange = Array.from(
|
|
594
|
-
{ length: 60 - startMinute },
|
|
595
|
-
(_, k) => startMinute + k,
|
|
596
|
-
)
|
|
518
|
+
const firstHourMinuteRange = Array.from({ length: 60 - startMinute }, (_, k) => startMinute + k)
|
|
597
519
|
/** @type {number[]} The final minute is not contained on purpose. Every minute in this range represents one for which any second is valid. */
|
|
598
520
|
const lastHourMinuteRange = Array.from({ length: endMinute }, (_, k) => k)
|
|
599
521
|
/** @type number[] */
|
|
@@ -601,18 +523,14 @@ export class CronTime {
|
|
|
601
523
|
|
|
602
524
|
/** @type (number) => number[] */
|
|
603
525
|
const selectRange = (forHour: number) => {
|
|
604
|
-
if (forHour === startHour)
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
return lastHourMinuteRange
|
|
608
|
-
else
|
|
609
|
-
return middleHourMinuteRange
|
|
526
|
+
if (forHour === startHour) return firstHourMinuteRange
|
|
527
|
+
if (forHour === endHour) return lastHourMinuteRange
|
|
528
|
+
return middleHourMinuteRange
|
|
610
529
|
}
|
|
611
530
|
|
|
612
531
|
// Include the endHour: Selecting the right range still ensures no values outside the skip are checked.
|
|
613
532
|
for (let hour = startHour; hour <= endHour; ++hour) {
|
|
614
|
-
if (!(hour in this.hour))
|
|
615
|
-
continue
|
|
533
|
+
if (!(hour in this.hour)) continue
|
|
616
534
|
|
|
617
535
|
// The hour matches, so if the minute is in the range, we have a match!
|
|
618
536
|
const usingRange = selectRange(hour)
|
|
@@ -620,8 +538,7 @@ export class CronTime {
|
|
|
620
538
|
for (const minute of usingRange) {
|
|
621
539
|
// All minutes in any of the selected ranges represent minutes which are fully contained in the jump,
|
|
622
540
|
// So we need not check the seconds. If the minute is in there, it is a match.
|
|
623
|
-
if (minute in this.minute)
|
|
624
|
-
return true
|
|
541
|
+
if (minute in this.minute) return true
|
|
625
542
|
}
|
|
626
543
|
}
|
|
627
544
|
|
|
@@ -641,11 +558,7 @@ export class CronTime {
|
|
|
641
558
|
* @param expectedMinute
|
|
642
559
|
* @param {DateTime} actualDate
|
|
643
560
|
*/
|
|
644
|
-
private _forwardDSTJump(
|
|
645
|
-
expectedHour: number,
|
|
646
|
-
expectedMinute: number,
|
|
647
|
-
actualDate: DateTime,
|
|
648
|
-
) {
|
|
561
|
+
private _forwardDSTJump(expectedHour: number, expectedMinute: number, actualDate: DateTime) {
|
|
649
562
|
const actualHour = actualDate.hour
|
|
650
563
|
const actualMinute = actualDate.minute
|
|
651
564
|
|
|
@@ -659,12 +572,10 @@ export class CronTime {
|
|
|
659
572
|
* wildcard, or all params in array (for to string)
|
|
660
573
|
*/
|
|
661
574
|
private _wcOrAll(unit: TimeUnit) {
|
|
662
|
-
if (this._hasAll(unit))
|
|
663
|
-
return '*'
|
|
575
|
+
if (this._hasAll(unit)) return '*'
|
|
664
576
|
|
|
665
577
|
const all = []
|
|
666
|
-
for (const time in this[unit])
|
|
667
|
-
all.push(time)
|
|
578
|
+
for (const time in this[unit]) all.push(time)
|
|
668
579
|
|
|
669
580
|
return all.join(',')
|
|
670
581
|
}
|
|
@@ -672,12 +583,10 @@ export class CronTime {
|
|
|
672
583
|
private _hasAll(unit: TimeUnit) {
|
|
673
584
|
const constraints = CONSTRAINTS[unit]
|
|
674
585
|
const low = constraints[0]
|
|
675
|
-
const high
|
|
676
|
-
= unit === TIME_UNITS_MAP.DAY_OF_WEEK ? constraints[1] - 1 : constraints[1]
|
|
586
|
+
const high = unit === TIME_UNITS_MAP.DAY_OF_WEEK ? constraints[1] - 1 : constraints[1]
|
|
677
587
|
|
|
678
588
|
for (let i = low, n = high; i < n; i++) {
|
|
679
|
-
if (!(i in this[unit]))
|
|
680
|
-
return false
|
|
589
|
+
if (!(i in this[unit])) return false
|
|
681
590
|
}
|
|
682
591
|
|
|
683
592
|
return true
|
|
@@ -697,12 +606,10 @@ export class CronTime {
|
|
|
697
606
|
private _parse(source: string) {
|
|
698
607
|
source = source.toLowerCase()
|
|
699
608
|
|
|
700
|
-
if (Object.keys(PRESETS).includes(source))
|
|
701
|
-
source = PRESETS[source as keyof typeof PRESETS]
|
|
609
|
+
if (Object.keys(PRESETS).includes(source)) source = PRESETS[source as keyof typeof PRESETS]
|
|
702
610
|
|
|
703
611
|
source = source.replace(/[a-z]{1,3}/gi, (alias: string) => {
|
|
704
|
-
if (Object.keys(ALIASES).includes(alias))
|
|
705
|
-
return ALIASES[alias as keyof typeof ALIASES].toString()
|
|
612
|
+
if (Object.keys(ALIASES).includes(alias)) return ALIASES[alias as keyof typeof ALIASES].toString()
|
|
706
613
|
|
|
707
614
|
throw new CronError(`Unknown alias: ${alias}`)
|
|
708
615
|
})
|
|
@@ -710,11 +617,9 @@ export class CronTime {
|
|
|
710
617
|
const units = source.trim().split(/\s+/)
|
|
711
618
|
|
|
712
619
|
// seconds are optional
|
|
713
|
-
if (units.length < TIME_UNITS_LEN - 1)
|
|
714
|
-
throw new CronError('Too few fields')
|
|
620
|
+
if (units.length < TIME_UNITS_LEN - 1) throw new CronError('Too few fields')
|
|
715
621
|
|
|
716
|
-
if (units.length > TIME_UNITS_LEN)
|
|
717
|
-
throw new CronError('Too many fields')
|
|
622
|
+
if (units.length > TIME_UNITS_LEN) throw new CronError('Too many fields')
|
|
718
623
|
|
|
719
624
|
const unitsLen = units.length
|
|
720
625
|
for (const unit of TIME_UNITS) {
|
|
@@ -750,9 +655,7 @@ export class CronTime {
|
|
|
750
655
|
fields.forEach((field) => {
|
|
751
656
|
const wildcardIndex = field.indexOf('*')
|
|
752
657
|
if (wildcardIndex !== -1 && wildcardIndex !== 0) {
|
|
753
|
-
throw new CronError(
|
|
754
|
-
`Field (${field}) has an invalid wildcard expression`,
|
|
755
|
-
)
|
|
658
|
+
throw new CronError(`Field (${field}) has an invalid wildcard expression`)
|
|
756
659
|
}
|
|
757
660
|
})
|
|
758
661
|
|
|
@@ -771,19 +674,14 @@ export class CronTime {
|
|
|
771
674
|
|
|
772
675
|
const wasStepDefined = mStep !== undefined
|
|
773
676
|
const step = Number.parseInt(mStep ?? '1', 10)
|
|
774
|
-
if (step === 0)
|
|
775
|
-
throw new CronError(`Field (${unit}) has a step of zero`)
|
|
677
|
+
if (step === 0) throw new CronError(`Field (${unit}) has a step of zero`)
|
|
776
678
|
|
|
777
|
-
if (upper !== undefined && lower > upper)
|
|
778
|
-
throw new CronError(`Field (${unit}) has an invalid range`)
|
|
679
|
+
if (upper !== undefined && lower > upper) throw new CronError(`Field (${unit}) has an invalid range`)
|
|
779
680
|
|
|
780
|
-
const isOutOfRange
|
|
781
|
-
|
|
782
|
-
|| (upper !== undefined && upper > high)
|
|
783
|
-
|| (upper === undefined && lower > high)
|
|
681
|
+
const isOutOfRange =
|
|
682
|
+
lower < low || (upper !== undefined && upper > high) || (upper === undefined && lower > high)
|
|
784
683
|
|
|
785
|
-
if (isOutOfRange)
|
|
786
|
-
throw new CronError(`Field value (${value}) is out of range`)
|
|
684
|
+
if (isOutOfRange) throw new CronError(`Field value (${value}) is out of range`)
|
|
787
685
|
|
|
788
686
|
// Positive integer higher than constraints[0]
|
|
789
687
|
lower = Math.min(Math.max(low, ~~Math.abs(lower)), high)
|
|
@@ -791,8 +689,7 @@ export class CronTime {
|
|
|
791
689
|
// Positive integer lower than constraints[1]
|
|
792
690
|
if (upper !== undefined) {
|
|
793
691
|
upper = Math.min(high, ~~Math.abs(upper))
|
|
794
|
-
}
|
|
795
|
-
else {
|
|
692
|
+
} else {
|
|
796
693
|
// If step is provided, the default upper range is the highest value
|
|
797
694
|
upper = wasStepDefined ? high : lower
|
|
798
695
|
}
|
|
@@ -810,12 +707,11 @@ export class CronTime {
|
|
|
810
707
|
// merge day 7 into day 0 (both Sunday), and remove day 7
|
|
811
708
|
// since we work with day-of-week 0-6 under the hood
|
|
812
709
|
if (unit === 'dayOfWeek') {
|
|
813
|
-
if (!typeObj[0] && !!typeObj[7])
|
|
814
|
-
|
|
815
|
-
|
|
710
|
+
if (!typeObj[0] && !!typeObj[7]) typeObj[0] = typeObj[7]
|
|
711
|
+
// delete typeObj[7]
|
|
712
|
+
typeObj[7] = undefined
|
|
816
713
|
}
|
|
817
|
-
}
|
|
818
|
-
else {
|
|
714
|
+
} else {
|
|
819
715
|
throw new CronError(`Field (${unit}) cannot be parsed`)
|
|
820
716
|
}
|
|
821
717
|
}
|
package/src/types/cron.ts
CHANGED
|
@@ -4,10 +4,7 @@ import type { CONSTRAINTS, TIME_UNITS_MAP } from '../constants'
|
|
|
4
4
|
import type { CronJob } from '../job'
|
|
5
5
|
import type { IntRange } from './utils'
|
|
6
6
|
|
|
7
|
-
interface BaseCronJobParams<
|
|
8
|
-
OC extends CronOnCompleteCommand | null = null,
|
|
9
|
-
C = null,
|
|
10
|
-
> {
|
|
7
|
+
interface BaseCronJobParams<OC extends CronOnCompleteCommand | null = null, C = null> {
|
|
11
8
|
cronTime: string | Date | DateTime
|
|
12
9
|
onTick: CronCommand<C, WithOnComplete<OC>>
|
|
13
10
|
onComplete?: OC
|
|
@@ -17,26 +14,23 @@ interface BaseCronJobParams<
|
|
|
17
14
|
unrefTimeout?: boolean | null
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
export type CronJobParams<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
timeZone?: never
|
|
31
|
-
utcOffset?: number | null
|
|
32
|
-
}
|
|
17
|
+
export type CronJobParams<OC extends CronOnCompleteCommand | null = null, C = null> = BaseCronJobParams<OC, C> &
|
|
18
|
+
(
|
|
19
|
+
| {
|
|
20
|
+
timeZone?: string | null
|
|
21
|
+
utcOffset?: never
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
timeZone?: never
|
|
25
|
+
utcOffset?: number | null
|
|
26
|
+
}
|
|
33
27
|
)
|
|
34
28
|
|
|
35
29
|
export type CronContext<C> = C extends null ? CronJob : NonNullable<C>
|
|
36
30
|
|
|
37
31
|
export type CronCallback<C, WithOnCompleteBool extends boolean = false> = (
|
|
38
32
|
this: CronContext<C>,
|
|
39
|
-
onComplete: WithOnCompleteBool extends true ? CronOnCompleteCallback : never
|
|
33
|
+
onComplete: WithOnCompleteBool extends true ? CronOnCompleteCallback : never,
|
|
40
34
|
) => void | Promise<void>
|
|
41
35
|
|
|
42
36
|
export type CronOnCompleteCallback = () => void | Promise<void>
|
|
@@ -44,10 +38,10 @@ export type CronOnCompleteCallback = () => void | Promise<void>
|
|
|
44
38
|
export type CronSystemCommand =
|
|
45
39
|
| string
|
|
46
40
|
| {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
command: string
|
|
42
|
+
args?: readonly string[] | null
|
|
43
|
+
options?: SpawnOptions | null
|
|
44
|
+
}
|
|
51
45
|
|
|
52
46
|
export type CronCommand<C, WithOnCompleteBool extends boolean = false> =
|
|
53
47
|
| CronCallback<C, WithOnCompleteBool>
|
|
@@ -59,9 +53,7 @@ export type WithOnComplete<OC> = OC extends null ? false : true
|
|
|
59
53
|
|
|
60
54
|
export type TimeUnit = (typeof TIME_UNITS_MAP)[keyof typeof TIME_UNITS_MAP]
|
|
61
55
|
|
|
62
|
-
export type TimeUnitField<T extends TimeUnit> = Partial<
|
|
63
|
-
Record<Ranges[T], boolean>
|
|
64
|
-
>
|
|
56
|
+
export type TimeUnitField<T extends TimeUnit> = Partial<Record<Ranges[T], boolean>>
|
|
65
57
|
|
|
66
58
|
export interface Ranges {
|
|
67
59
|
second: SecondRange
|
|
@@ -72,27 +64,9 @@ export interface Ranges {
|
|
|
72
64
|
dayOfWeek: DayOfWeekRange
|
|
73
65
|
}
|
|
74
66
|
|
|
75
|
-
export type SecondRange = IntRange<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
>
|
|
79
|
-
export type
|
|
80
|
-
|
|
81
|
-
(typeof CONSTRAINTS)['minute'][1]
|
|
82
|
-
>
|
|
83
|
-
export type HourRange = IntRange<
|
|
84
|
-
(typeof CONSTRAINTS)['hour'][0],
|
|
85
|
-
(typeof CONSTRAINTS)['hour'][1]
|
|
86
|
-
>
|
|
87
|
-
export type DayOfMonthRange = IntRange<
|
|
88
|
-
(typeof CONSTRAINTS)['dayOfMonth'][0],
|
|
89
|
-
(typeof CONSTRAINTS)['dayOfMonth'][1]
|
|
90
|
-
>
|
|
91
|
-
export type MonthRange = IntRange<
|
|
92
|
-
(typeof CONSTRAINTS)['month'][0],
|
|
93
|
-
(typeof CONSTRAINTS)['month'][1]
|
|
94
|
-
>
|
|
95
|
-
export type DayOfWeekRange = IntRange<
|
|
96
|
-
(typeof CONSTRAINTS)['dayOfWeek'][0],
|
|
97
|
-
(typeof CONSTRAINTS)['dayOfWeek'][1]
|
|
98
|
-
>
|
|
67
|
+
export type SecondRange = IntRange<(typeof CONSTRAINTS)['second'][0], (typeof CONSTRAINTS)['second'][1]>
|
|
68
|
+
export type MinuteRange = IntRange<(typeof CONSTRAINTS)['minute'][0], (typeof CONSTRAINTS)['minute'][1]>
|
|
69
|
+
export type HourRange = IntRange<(typeof CONSTRAINTS)['hour'][0], (typeof CONSTRAINTS)['hour'][1]>
|
|
70
|
+
export type DayOfMonthRange = IntRange<(typeof CONSTRAINTS)['dayOfMonth'][0], (typeof CONSTRAINTS)['dayOfMonth'][1]>
|
|
71
|
+
export type MonthRange = IntRange<(typeof CONSTRAINTS)['month'][0], (typeof CONSTRAINTS)['month'][1]>
|
|
72
|
+
export type DayOfWeekRange = IntRange<(typeof CONSTRAINTS)['dayOfWeek'][0], (typeof CONSTRAINTS)['dayOfWeek'][1]>
|