@stonyx/cron 0.2.1-alpha.10 → 0.2.1-alpha.11
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 +2 -0
- package/dist/main.d.ts +11 -0
- package/dist/main.js +41 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ When a job is executed, its next trigger time is updated, and it is re-inserted
|
|
|
35
35
|
| `register` | `key: string, callback: Function, interval: number, runOnInit?: boolean` | Register a new job with a given interval in seconds. If `runOnInit` is true, the job runs immediately upon registration. |
|
|
36
36
|
| `unregister` | `key: string` | Remove a previously registered job. |
|
|
37
37
|
|
|
38
|
+
> Callbacks are invoked fire-and-forget: `Cron` never waits for one to settle. Two *different* jobs that fall due on the same tick may therefore overlap, and a job that is still running when it next falls due is skipped for that tick (a warning is logged). Both synchronous throws and asynchronous rejections are caught and logged; neither can stop the scheduler.
|
|
39
|
+
|
|
38
40
|
> `MinHeap` is also exported as a public subpath (`@stonyx/cron/min-heap`) and can be imported directly for advanced usage.
|
|
39
41
|
|
|
40
42
|
## Configuration
|
package/dist/main.d.ts
CHANGED
|
@@ -9,10 +9,21 @@ export default class Cron {
|
|
|
9
9
|
jobs: Record<string, CronJob>;
|
|
10
10
|
heap: MinHeap<CronJob>;
|
|
11
11
|
timer: ReturnType<typeof setTimeout> | null;
|
|
12
|
+
/** Keys whose previous invocation has not settled yet. */
|
|
13
|
+
inFlight: Set<string>;
|
|
12
14
|
constructor();
|
|
13
15
|
init(): Promise<void>;
|
|
14
16
|
scheduleNextRun(): void;
|
|
15
17
|
runDueJobs(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* The one safe way this class invokes a consumer callback.
|
|
20
|
+
*
|
|
21
|
+
* Never blocks the caller, catches synchronous throws and asynchronous
|
|
22
|
+
* rejections alike, and skips the invocation entirely when the job's previous
|
|
23
|
+
* invocation has not settled yet (fire-and-forget would otherwise let a slow
|
|
24
|
+
* job stack invocations on itself).
|
|
25
|
+
*/
|
|
26
|
+
safeInvoke(job: CronJob, runOnInit?: boolean): void;
|
|
16
27
|
register(key: string, callback: () => void | Promise<void>, interval: string, runOnInit?: boolean): void;
|
|
17
28
|
unregister(key: string): void;
|
|
18
29
|
setNextTrigger(job: CronJob): void;
|
package/dist/main.js
CHANGED
|
@@ -22,6 +22,8 @@ export default class Cron {
|
|
|
22
22
|
jobs = {};
|
|
23
23
|
heap = new MinHeap();
|
|
24
24
|
timer = null;
|
|
25
|
+
/** Keys whose previous invocation has not settled yet. */
|
|
26
|
+
inFlight = new Set();
|
|
25
27
|
constructor() {
|
|
26
28
|
if (Cron.instance)
|
|
27
29
|
return Cron.instance;
|
|
@@ -55,17 +57,47 @@ export default class Cron {
|
|
|
55
57
|
const job = heap.pop();
|
|
56
58
|
if (config.debug)
|
|
57
59
|
this.log('job has been triggered', job.key);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
log.error(`Cron job "${job.key}" failed:`, err);
|
|
63
|
-
}
|
|
60
|
+
// Reschedule *before* invoking. The callback's result is not used by this
|
|
61
|
+
// class (`runDueJobs` returns void), so awaiting it bought nothing and
|
|
62
|
+
// cost the scheduler: a callback that never settled left the job absent
|
|
63
|
+
// from the heap and stopped the timer from ever re-arming.
|
|
64
64
|
this.setNextTrigger(job);
|
|
65
65
|
heap.push(job);
|
|
66
|
+
this.safeInvoke(job);
|
|
66
67
|
}
|
|
67
68
|
this.scheduleNextRun();
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* The one safe way this class invokes a consumer callback.
|
|
72
|
+
*
|
|
73
|
+
* Never blocks the caller, catches synchronous throws and asynchronous
|
|
74
|
+
* rejections alike, and skips the invocation entirely when the job's previous
|
|
75
|
+
* invocation has not settled yet (fire-and-forget would otherwise let a slow
|
|
76
|
+
* job stack invocations on itself).
|
|
77
|
+
*/
|
|
78
|
+
safeInvoke(job, runOnInit = false) {
|
|
79
|
+
const { key } = job;
|
|
80
|
+
const context = runOnInit ? 'failed on init:' : 'failed:';
|
|
81
|
+
if (this.inFlight.has(key)) {
|
|
82
|
+
log.warn(`Cron job "${key}" is still running; skipping this tick`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.inFlight.add(key);
|
|
86
|
+
try {
|
|
87
|
+
const result = job.callback();
|
|
88
|
+
if (result && typeof result.then === 'function') {
|
|
89
|
+
Promise.resolve(result)
|
|
90
|
+
.catch(err => log.error(`Cron job "${key}" ${context}`, err))
|
|
91
|
+
.finally(() => this.inFlight.delete(key));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.inFlight.delete(key);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
this.inFlight.delete(key);
|
|
98
|
+
log.error(`Cron job "${key}" ${context}`, err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
69
101
|
register(key, callback, interval, runOnInit = false) {
|
|
70
102
|
const job = { callback, interval, key, nextTrigger: 0 };
|
|
71
103
|
this.jobs[key] = job;
|
|
@@ -74,14 +106,8 @@ export default class Cron {
|
|
|
74
106
|
if (config.debug) {
|
|
75
107
|
this.log(`job has been registered with interval: ${interval}`, key);
|
|
76
108
|
}
|
|
77
|
-
if (runOnInit)
|
|
78
|
-
|
|
79
|
-
callback();
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
log.error(`Cron job "${key}" failed on init:`, err);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
109
|
+
if (runOnInit)
|
|
110
|
+
this.safeInvoke(job, true);
|
|
85
111
|
this.scheduleNextRun();
|
|
86
112
|
}
|
|
87
113
|
unregister(key) {
|
|
@@ -91,6 +117,7 @@ export default class Cron {
|
|
|
91
117
|
return;
|
|
92
118
|
delete jobs[key];
|
|
93
119
|
heap.remove(job);
|
|
120
|
+
this.inFlight.delete(key);
|
|
94
121
|
if (config.debug)
|
|
95
122
|
this.log('job has been unregistered', key);
|
|
96
123
|
this.scheduleNextRun();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"keywords": [
|
|
4
4
|
"stonyx-module"
|
|
5
5
|
],
|
|
6
|
-
"version": "0.2.1-alpha.
|
|
6
|
+
"version": "0.2.1-alpha.11",
|
|
7
7
|
"description": "Cron/job scheduler for Stonyx framework",
|
|
8
8
|
"main": "dist/main.js",
|
|
9
9
|
"types": "dist/main.d.ts",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"homepage": "https://github.com/abofs/stonyx-cron#readme",
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@stonyx/utils": "0.2.3-beta.
|
|
72
|
+
"@stonyx/utils": "0.2.3-beta.26",
|
|
73
73
|
"@types/node": "^25.5.2",
|
|
74
74
|
"@types/qunit": "^2.19.13",
|
|
75
75
|
"@types/sinon": "^21.0.1",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"typescript": "^5.8.3"
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
|
-
"stonyx": "0.2.3-beta.
|
|
82
|
+
"stonyx": "0.2.3-beta.76"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
85
|
"build": "tsc",
|