@arkstack/scheduler 0.19.2 → 0.19.3
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/README.md +31 -31
- package/dist/commands/ScheduleListCommand.js +1 -1
- package/dist/commands/ScheduleRunCommand.js +2 -2
- package/dist/commands/ScheduleWorkCommand.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{loader-CZcEYUX9.js → loader-DlxdvHFL.js} +96 -96
- package/dist/{runner-Bt-jQptE.js → runner-b2r6TZPd.js} +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -18,19 +18,19 @@ Full app templates include it and a `src/routes/console.ts` already.
|
|
|
18
18
|
In `src/routes/console.ts`:
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
|
-
import { Schedule } from '@arkstack/scheduler'
|
|
21
|
+
import { Schedule } from '@arkstack/scheduler'
|
|
22
22
|
|
|
23
|
-
Schedule.command('cache:prune').hourly()
|
|
23
|
+
Schedule.command('cache:prune').hourly()
|
|
24
24
|
|
|
25
25
|
Schedule.call(async () => {
|
|
26
|
-
await pruneTempFiles()
|
|
26
|
+
await pruneTempFiles()
|
|
27
27
|
})
|
|
28
28
|
.dailyAt('01:30')
|
|
29
|
-
.withoutOverlapping()
|
|
29
|
+
.withoutOverlapping()
|
|
30
30
|
|
|
31
|
-
Schedule.job(new SendDigest()).weeklyOn(1, '08:00')
|
|
31
|
+
Schedule.job(new SendDigest()).weeklyOn(1, '08:00')
|
|
32
32
|
|
|
33
|
-
Schedule.exec('backup.sh').daily().onOneServer()
|
|
33
|
+
Schedule.exec('backup.sh').daily().onOneServer()
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
| Task type | Runs |
|
|
@@ -58,36 +58,36 @@ ark schedule:list # list tasks with their next run time
|
|
|
58
58
|
## Frequencies
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
|
-
Schedule.command('x').everyMinute()
|
|
62
|
-
Schedule.command('x').everyFiveMinutes()
|
|
63
|
-
Schedule.command('x').everyThirtyMinutes()
|
|
64
|
-
Schedule.command('x').hourly()
|
|
65
|
-
Schedule.command('x').hourlyAt(15)
|
|
66
|
-
Schedule.command('x').daily()
|
|
67
|
-
Schedule.command('x').dailyAt('13:00')
|
|
68
|
-
Schedule.command('x').twiceDaily(1, 13)
|
|
69
|
-
Schedule.command('x').weekly()
|
|
70
|
-
Schedule.command('x').weeklyOn(1, '8:00')
|
|
71
|
-
Schedule.command('x').monthly()
|
|
72
|
-
Schedule.command('x').monthlyOn(15, '17:00')
|
|
73
|
-
Schedule.command('x').quarterly()
|
|
74
|
-
Schedule.command('x').yearly()
|
|
75
|
-
Schedule.command('x').cron('*/10 9-17 * * 1-5')
|
|
61
|
+
Schedule.command('x').everyMinute()
|
|
62
|
+
Schedule.command('x').everyFiveMinutes() // */5 * * * *
|
|
63
|
+
Schedule.command('x').everyThirtyMinutes()
|
|
64
|
+
Schedule.command('x').hourly()
|
|
65
|
+
Schedule.command('x').hourlyAt(15)
|
|
66
|
+
Schedule.command('x').daily()
|
|
67
|
+
Schedule.command('x').dailyAt('13:00')
|
|
68
|
+
Schedule.command('x').twiceDaily(1, 13)
|
|
69
|
+
Schedule.command('x').weekly()
|
|
70
|
+
Schedule.command('x').weeklyOn(1, '8:00') // Monday 08:00
|
|
71
|
+
Schedule.command('x').monthly()
|
|
72
|
+
Schedule.command('x').monthlyOn(15, '17:00')
|
|
73
|
+
Schedule.command('x').quarterly()
|
|
74
|
+
Schedule.command('x').yearly()
|
|
75
|
+
Schedule.command('x').cron('*/10 9-17 * * 1-5') // raw cron
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
Day-of-week constraints, with time layered on top:
|
|
79
79
|
|
|
80
80
|
```ts
|
|
81
|
-
Schedule.command('x').weekdays().at('9:00')
|
|
82
|
-
Schedule.command('x').weekends().hourly()
|
|
83
|
-
Schedule.command('x').mondays()
|
|
84
|
-
Schedule.command('x').days([1, 4])
|
|
81
|
+
Schedule.command('x').weekdays().at('9:00')
|
|
82
|
+
Schedule.command('x').weekends().hourly()
|
|
83
|
+
Schedule.command('x').mondays()
|
|
84
|
+
Schedule.command('x').days([1, 4]) // Monday & Thursday
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
Set the timezone the expression is evaluated in:
|
|
88
88
|
|
|
89
89
|
```ts
|
|
90
|
-
Schedule.command('x').dailyAt('09:00').timezone('America/New_York')
|
|
90
|
+
Schedule.command('x').dailyAt('09:00').timezone('America/New_York')
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
## Constraints
|
|
@@ -98,7 +98,7 @@ Schedule.command('x')
|
|
|
98
98
|
.when(async () => await featureEnabled('reports')) // run only when truthy
|
|
99
99
|
.skip(() => isHoliday()) // skip when truthy
|
|
100
100
|
.environments('production', 'staging') // limit by APP_ENV
|
|
101
|
-
.between('09:00', '17:00')
|
|
101
|
+
.between('09:00', '17:00') // only within a daily window
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
`unlessBetween('22:00', '06:00')` runs _outside_ a window (overnight ranges handled).
|
|
@@ -106,9 +106,9 @@ Schedule.command('x')
|
|
|
106
106
|
## Overlaps, single-server & background
|
|
107
107
|
|
|
108
108
|
```ts
|
|
109
|
-
Schedule.command('report:build').everyFiveMinutes().withoutOverlapping()
|
|
110
|
-
Schedule.exec('backup.sh').daily().onOneServer()
|
|
111
|
-
Schedule.exec('long-import.sh').daily().runInBackground()
|
|
109
|
+
Schedule.command('report:build').everyFiveMinutes().withoutOverlapping()
|
|
110
|
+
Schedule.exec('backup.sh').daily().onOneServer()
|
|
111
|
+
Schedule.exec('long-import.sh').daily().runInBackground()
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
`withoutOverlapping()` and `onOneServer()` coordinate through `@arkstack/cache`, so they work across processes and servers. Configure a shared cache store (`redis` or `database`) for `onOneServer()` to be effective; without a cache the scheduler falls back to a process-local lock.
|
|
@@ -121,7 +121,7 @@ Schedule.command('report:build')
|
|
|
121
121
|
.before(() => logger.info('building report'))
|
|
122
122
|
.onSuccess(() => notifyOk())
|
|
123
123
|
.onFailure((error) => notifyFailed(error))
|
|
124
|
-
.after(() => logger.info('done'))
|
|
124
|
+
.after(() => logger.info('done'))
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
## API
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as loadSchedule } from "../loader-
|
|
2
|
-
import { t as runDueEvents } from "../runner-
|
|
1
|
+
import { t as loadSchedule } from "../loader-DlxdvHFL.js";
|
|
2
|
+
import { t as runDueEvents } from "../runner-b2r6TZPd.js";
|
|
3
3
|
import { Command } from "@h3ravel/musket";
|
|
4
4
|
//#region src/commands/ScheduleRunCommand.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as loadSchedule } from "../loader-
|
|
2
|
-
import { t as runDueEvents } from "../runner-
|
|
1
|
+
import { t as loadSchedule } from "../loader-DlxdvHFL.js";
|
|
2
|
+
import { t as runDueEvents } from "../runner-b2r6TZPd.js";
|
|
3
3
|
import { Command } from "@h3ravel/musket";
|
|
4
4
|
//#region src/commands/ScheduleWorkCommand.ts
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as nextRun, i as isDue, n as Schedule, o as acquireLock, r as ScheduledEvent, s as releaseLock, t as loadSchedule } from "./loader-
|
|
2
|
-
import { t as runDueEvents } from "./runner-
|
|
1
|
+
import { a as nextRun, i as isDue, n as Schedule, o as acquireLock, r as ScheduledEvent, s as releaseLock, t as loadSchedule } from "./loader-DlxdvHFL.js";
|
|
2
|
+
import { t as runDueEvents } from "./runner-b2r6TZPd.js";
|
|
3
3
|
export { Schedule, ScheduledEvent, acquireLock, isDue, loadSchedule, nextRun, releaseLock, runDueEvents };
|
|
@@ -280,42 +280,42 @@ var ScheduledEvent = class {
|
|
|
280
280
|
this.timezoneValue = timezone;
|
|
281
281
|
return this;
|
|
282
282
|
}
|
|
283
|
-
/**
|
|
283
|
+
/**
|
|
284
284
|
* Only run when every registered `when` predicate is truthy.
|
|
285
|
-
*
|
|
286
|
-
* @param callback
|
|
287
|
-
* @returns
|
|
285
|
+
*
|
|
286
|
+
* @param callback
|
|
287
|
+
* @returns
|
|
288
288
|
*/
|
|
289
289
|
when(callback) {
|
|
290
290
|
this.filters.push(callback);
|
|
291
291
|
return this;
|
|
292
292
|
}
|
|
293
|
-
/**
|
|
293
|
+
/**
|
|
294
294
|
* Skip when any registered `skip` predicate is truthy.
|
|
295
|
-
*
|
|
296
|
-
* @param callback
|
|
297
|
-
* @returns
|
|
295
|
+
*
|
|
296
|
+
* @param callback
|
|
297
|
+
* @returns
|
|
298
298
|
*/
|
|
299
299
|
skip(callback) {
|
|
300
300
|
this.rejects.push(callback);
|
|
301
301
|
return this;
|
|
302
302
|
}
|
|
303
|
-
/**
|
|
303
|
+
/**
|
|
304
304
|
* Only run in these `APP_ENV` environments.
|
|
305
|
-
*
|
|
306
|
-
* @param callback
|
|
307
|
-
* @returns
|
|
305
|
+
*
|
|
306
|
+
* @param callback
|
|
307
|
+
* @returns
|
|
308
308
|
*/
|
|
309
309
|
environments(...environments) {
|
|
310
310
|
this.environmentsValue = environments.flat();
|
|
311
311
|
return this;
|
|
312
312
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Only run when the current time is within `[start, end]` (HH:MM, event tz).
|
|
315
|
-
*
|
|
316
|
-
* @param start
|
|
317
|
-
* @param end
|
|
318
|
-
* @returns
|
|
313
|
+
/**
|
|
314
|
+
* Only run when the current time is within `[start, end]` (HH:MM, event tz).
|
|
315
|
+
*
|
|
316
|
+
* @param start
|
|
317
|
+
* @param end
|
|
318
|
+
* @returns
|
|
319
319
|
*/
|
|
320
320
|
between(start, end) {
|
|
321
321
|
this.windows.push({
|
|
@@ -325,11 +325,11 @@ var ScheduledEvent = class {
|
|
|
325
325
|
});
|
|
326
326
|
return this;
|
|
327
327
|
}
|
|
328
|
-
/**
|
|
328
|
+
/**
|
|
329
329
|
* Only run when the current time is outside `[start, end]`.
|
|
330
|
-
*
|
|
331
|
-
* @param callback
|
|
332
|
-
* @returns
|
|
330
|
+
*
|
|
331
|
+
* @param callback
|
|
332
|
+
* @returns
|
|
333
333
|
*/
|
|
334
334
|
unlessBetween(start, end) {
|
|
335
335
|
this.windows.push({
|
|
@@ -343,52 +343,52 @@ var ScheduledEvent = class {
|
|
|
343
343
|
const [h, m] = parseTime(time);
|
|
344
344
|
return h * 60 + m;
|
|
345
345
|
}
|
|
346
|
-
/**
|
|
347
|
-
* Prevent the task from overlapping itself; the lock expires after `expiresMinutes`.
|
|
348
|
-
*
|
|
349
|
-
* @param expiresMinutes
|
|
350
|
-
* @returns
|
|
346
|
+
/**
|
|
347
|
+
* Prevent the task from overlapping itself; the lock expires after `expiresMinutes`.
|
|
348
|
+
*
|
|
349
|
+
* @param expiresMinutes
|
|
350
|
+
* @returns
|
|
351
351
|
*/
|
|
352
352
|
withoutOverlapping(expiresMinutes = 1440) {
|
|
353
353
|
this.withoutOverlappingValue = true;
|
|
354
354
|
this.overlapExpiresMinutes = expiresMinutes;
|
|
355
355
|
return this;
|
|
356
356
|
}
|
|
357
|
-
/**
|
|
357
|
+
/**
|
|
358
358
|
* Run on only one server per due minute (requires a shared cache store).
|
|
359
|
-
*
|
|
360
|
-
* @param callback
|
|
361
|
-
* @returns
|
|
359
|
+
*
|
|
360
|
+
* @param callback
|
|
361
|
+
* @returns
|
|
362
362
|
*/
|
|
363
363
|
onOneServer() {
|
|
364
364
|
this.onOneServerValue = true;
|
|
365
365
|
return this;
|
|
366
366
|
}
|
|
367
|
-
/**
|
|
367
|
+
/**
|
|
368
368
|
* Run the task in a detached background process (command/exec only).
|
|
369
|
-
*
|
|
370
|
-
* @param callback
|
|
371
|
-
* @returns
|
|
369
|
+
*
|
|
370
|
+
* @param callback
|
|
371
|
+
* @returns
|
|
372
372
|
*/
|
|
373
373
|
runInBackground() {
|
|
374
374
|
this.runInBackgroundValue = true;
|
|
375
375
|
return this;
|
|
376
376
|
}
|
|
377
|
-
/**
|
|
377
|
+
/**
|
|
378
378
|
* A human description shown by `schedule:list`.
|
|
379
|
-
*
|
|
380
|
-
* @param callback
|
|
381
|
-
* @returns
|
|
379
|
+
*
|
|
380
|
+
* @param callback
|
|
381
|
+
* @returns
|
|
382
382
|
*/
|
|
383
383
|
description(description) {
|
|
384
384
|
this.descriptionValue = description;
|
|
385
385
|
return this;
|
|
386
386
|
}
|
|
387
|
-
/**
|
|
387
|
+
/**
|
|
388
388
|
* An explicit mutex name (otherwise derived from the expression + description).
|
|
389
|
-
*
|
|
390
|
-
* @param name
|
|
391
|
-
* @returns
|
|
389
|
+
*
|
|
390
|
+
* @param name
|
|
391
|
+
* @returns
|
|
392
392
|
*/
|
|
393
393
|
name(name) {
|
|
394
394
|
this.mutexNameValue = name;
|
|
@@ -410,29 +410,29 @@ var ScheduledEvent = class {
|
|
|
410
410
|
this.failureHooks.push(hook);
|
|
411
411
|
return this;
|
|
412
412
|
}
|
|
413
|
-
/**
|
|
414
|
-
* Whether the cron expression is due at `date`.
|
|
415
|
-
*
|
|
416
|
-
* @param date
|
|
417
|
-
* @returns
|
|
413
|
+
/**
|
|
414
|
+
* Whether the cron expression is due at `date`.
|
|
415
|
+
*
|
|
416
|
+
* @param date
|
|
417
|
+
* @returns
|
|
418
418
|
*/
|
|
419
419
|
isDue(date = /* @__PURE__ */ new Date()) {
|
|
420
420
|
return isDue(this.expression, date, this.timezoneValue);
|
|
421
421
|
}
|
|
422
|
-
/**
|
|
423
|
-
* The next time this event will run after `from`.
|
|
424
|
-
*
|
|
425
|
-
* @param date
|
|
426
|
-
* @returns
|
|
422
|
+
/**
|
|
423
|
+
* The next time this event will run after `from`.
|
|
424
|
+
*
|
|
425
|
+
* @param date
|
|
426
|
+
* @returns
|
|
427
427
|
*/
|
|
428
428
|
nextRunAt(from = /* @__PURE__ */ new Date()) {
|
|
429
429
|
return nextRun(this.expression, from, this.timezoneValue);
|
|
430
430
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Whether environment, time-window, `when` and `skip` constraints all pass.
|
|
433
|
-
*
|
|
434
|
-
* @param date
|
|
435
|
-
* @returns
|
|
431
|
+
/**
|
|
432
|
+
* Whether environment, time-window, `when` and `skip` constraints all pass.
|
|
433
|
+
*
|
|
434
|
+
* @param date
|
|
435
|
+
* @returns
|
|
436
436
|
*/
|
|
437
437
|
async filtersPass(date = /* @__PURE__ */ new Date()) {
|
|
438
438
|
if (this.environmentsValue) {
|
|
@@ -447,10 +447,10 @@ var ScheduledEvent = class {
|
|
|
447
447
|
for (const reject of this.rejects) if (await reject()) return false;
|
|
448
448
|
return true;
|
|
449
449
|
}
|
|
450
|
-
/**
|
|
450
|
+
/**
|
|
451
451
|
* A stable mutex key derived from the expression + description (or an explicit name).
|
|
452
|
-
*
|
|
453
|
-
* @returns
|
|
452
|
+
*
|
|
453
|
+
* @returns
|
|
454
454
|
*/
|
|
455
455
|
mutexName() {
|
|
456
456
|
if (this.mutexNameValue) return `arkstack-schedule-${this.mutexNameValue}`;
|
|
@@ -568,70 +568,70 @@ const registry = () => {
|
|
|
568
568
|
* ```
|
|
569
569
|
*/
|
|
570
570
|
var Schedule = class {
|
|
571
|
-
/**
|
|
571
|
+
/**
|
|
572
572
|
* Run an Arkstack CLI command (`ark <name>`) on the schedule.
|
|
573
|
-
*
|
|
574
|
-
* @param name
|
|
575
|
-
* @param args
|
|
576
|
-
* @returns
|
|
573
|
+
*
|
|
574
|
+
* @param name
|
|
575
|
+
* @param args
|
|
576
|
+
* @returns
|
|
577
577
|
*/
|
|
578
578
|
static command(name, args = []) {
|
|
579
579
|
const event = new ScheduledEvent("command", `ark ${name}`).setProcess(name, args);
|
|
580
580
|
return this.add(event);
|
|
581
581
|
}
|
|
582
|
-
/**
|
|
583
|
-
* Run a callback on the schedule.
|
|
584
|
-
*
|
|
585
|
-
* @param callback
|
|
586
|
-
* @returns
|
|
582
|
+
/**
|
|
583
|
+
* Run a callback on the schedule.
|
|
584
|
+
*
|
|
585
|
+
* @param callback
|
|
586
|
+
* @returns
|
|
587
587
|
*/
|
|
588
588
|
static call(callback) {
|
|
589
589
|
const event = new ScheduledEvent("call", "Closure").setCall(callback);
|
|
590
590
|
return this.add(event);
|
|
591
591
|
}
|
|
592
|
-
/**
|
|
593
|
-
* Dispatch a queued job on the schedule (requires `@arkstack/jobs`).
|
|
594
|
-
*
|
|
595
|
-
* @param callback
|
|
596
|
-
* @returns
|
|
592
|
+
/**
|
|
593
|
+
* Dispatch a queued job on the schedule (requires `@arkstack/jobs`).
|
|
594
|
+
*
|
|
595
|
+
* @param callback
|
|
596
|
+
* @returns
|
|
597
597
|
*/
|
|
598
598
|
static job(job) {
|
|
599
599
|
const event = new ScheduledEvent("job", job?.constructor?.name ?? "Job").setJob(job);
|
|
600
600
|
return this.add(event);
|
|
601
601
|
}
|
|
602
|
-
/**
|
|
603
|
-
* Run a shell command on the schedule.
|
|
604
|
-
*
|
|
605
|
-
* @param callback
|
|
606
|
-
* @returns
|
|
602
|
+
/**
|
|
603
|
+
* Run a shell command on the schedule.
|
|
604
|
+
*
|
|
605
|
+
* @param callback
|
|
606
|
+
* @returns
|
|
607
607
|
*/
|
|
608
608
|
static exec(command, args = []) {
|
|
609
609
|
const event = new ScheduledEvent("exec", [command, ...args].join(" ")).setProcess(command, args);
|
|
610
610
|
return this.add(event);
|
|
611
611
|
}
|
|
612
|
-
/**
|
|
613
|
-
* All registered events.
|
|
614
|
-
*
|
|
615
|
-
* @param callback
|
|
616
|
-
* @returns
|
|
612
|
+
/**
|
|
613
|
+
* All registered events.
|
|
614
|
+
*
|
|
615
|
+
* @param callback
|
|
616
|
+
* @returns
|
|
617
617
|
*/
|
|
618
618
|
static events() {
|
|
619
619
|
return registry();
|
|
620
620
|
}
|
|
621
|
-
/**
|
|
622
|
-
* Events whose cron expression is due at `date`.
|
|
623
|
-
*
|
|
624
|
-
* @param callback
|
|
625
|
-
* @returns
|
|
621
|
+
/**
|
|
622
|
+
* Events whose cron expression is due at `date`.
|
|
623
|
+
*
|
|
624
|
+
* @param callback
|
|
625
|
+
* @returns
|
|
626
626
|
*/
|
|
627
627
|
static dueEvents(date = /* @__PURE__ */ new Date()) {
|
|
628
628
|
return registry().filter((event) => event.isDue(date));
|
|
629
629
|
}
|
|
630
|
-
/**
|
|
631
|
-
* Remove all registered events (used in tests).
|
|
632
|
-
*
|
|
633
|
-
* @param callback
|
|
634
|
-
* @returns
|
|
630
|
+
/**
|
|
631
|
+
* Remove all registered events (used in tests).
|
|
632
|
+
*
|
|
633
|
+
* @param callback
|
|
634
|
+
* @returns
|
|
635
635
|
*/
|
|
636
636
|
static clear() {
|
|
637
637
|
registry().length = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/scheduler",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Task scheduling for Arkstack: fluent frequencies, cron, overlap/one-server locks, hooks, and schedule:run/work/list commands.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net/guide/scheduling",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"croner": "^9.0.0",
|
|
36
|
-
"@arkstack/common": "^0.19.
|
|
36
|
+
"@arkstack/common": "^0.19.3"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@h3ravel/musket": "^2.2.13",
|
|
40
|
-
"@arkstack/contract": "^0.19.
|
|
41
|
-
"@arkstack/jobs": "^0.19.
|
|
42
|
-
"@arkstack/cache": "^0.19.
|
|
40
|
+
"@arkstack/contract": "^0.19.3",
|
|
41
|
+
"@arkstack/jobs": "^0.19.3",
|
|
42
|
+
"@arkstack/cache": "^0.19.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"@arkstack/cache": {
|