@aalis/plugin-cron-engine 0.1.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.
@@ -0,0 +1,7 @@
1
+ import type { Context } from '@aalis/core';
2
+ export declare const name = "@aalis/plugin-cron-engine";
3
+ export declare const displayName = "Cron \u8C03\u5EA6\u5F15\u64CE";
4
+ export declare const subsystem = "scheduler";
5
+ export declare const provides: string[];
6
+ export declare function apply(ctx: Context): void;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAU3C,eAAO,MAAM,IAAI,8BAA8B,CAAC;AAChD,eAAO,MAAM,WAAW,kCAAc,CAAC;AACvC,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,QAAQ,UAAkB,CAAC;AAexC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAkHxC"}
package/dist/index.js ADDED
@@ -0,0 +1,135 @@
1
+ // ============================================================
2
+ // @aalis/plugin-cron-engine — 共享 cron 调度引擎
3
+ //
4
+ // 提供 'cron-engine' 服务:
5
+ // - 多订阅者共享同一个整分钟 tick(避免每个插件各起 setInterval)
6
+ // - 统一处理 cron / 别名 / @every interval 三种表达式
7
+ // - 失败的 handler 仅记录日志、不影响其他订阅者
8
+ //
9
+ // 由 scheduler / workflow 等上层插件 inject.required 后调用 subscribe()。
10
+ // ============================================================
11
+ import { matchesCron, normalizeCronExpr, validateCronExpr, } from '@aalis/plugin-cron-engine-api';
12
+ export const name = '@aalis/plugin-cron-engine';
13
+ export const displayName = 'Cron 调度引擎';
14
+ export const subsystem = 'scheduler';
15
+ export const provides = ['cron-engine'];
16
+ export function apply(ctx) {
17
+ const logger = ctx.logger;
18
+ const cronSubs = new Map();
19
+ const intervalSubs = new Map();
20
+ let nextId = 1;
21
+ let tickTimer = null;
22
+ let alignTimer = null;
23
+ function ensureCronLoop() {
24
+ if (tickTimer || alignTimer)
25
+ return;
26
+ const now = new Date();
27
+ const msToNextMinute = (60 - now.getSeconds()) * 1000 - now.getMilliseconds();
28
+ alignTimer = setTimeout(() => {
29
+ alignTimer = null;
30
+ cronTick();
31
+ tickTimer = setInterval(cronTick, 60_000);
32
+ }, msToNextMinute);
33
+ logger.debug('cron-engine 主循环已对齐启动');
34
+ }
35
+ function cronTick() {
36
+ const now = new Date();
37
+ now.setSeconds(0, 0);
38
+ for (const sub of cronSubs.values()) {
39
+ if (matchesCron(sub.normalized, now, sub.timeZone)) {
40
+ try {
41
+ const r = sub.handler();
42
+ if (r instanceof Promise) {
43
+ r.catch(err => logger.error(`cron handler (id=${sub.id}, expr="${sub.normalized}") 异步异常:`, err));
44
+ }
45
+ }
46
+ catch (err) {
47
+ logger.error(`cron handler (id=${sub.id}, expr="${sub.normalized}") 同步异常:`, err);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ const service = {
53
+ subscribe(expr, handler, options) {
54
+ const v = validateCronExpr(expr);
55
+ if (!v.ok)
56
+ throw new Error(v.reason);
57
+ const id = nextId++;
58
+ if (v.kind === 'interval') {
59
+ const ms = (v.intervalSeconds ?? 0) * 1000;
60
+ const timer = setInterval(() => {
61
+ try {
62
+ const r = handler();
63
+ if (r instanceof Promise) {
64
+ r.catch(err => logger.error(`interval handler (id=${id}, expr="${expr}") 异步异常:`, err));
65
+ }
66
+ }
67
+ catch (err) {
68
+ logger.error(`interval handler (id=${id}, expr="${expr}") 同步异常:`, err);
69
+ }
70
+ }, ms);
71
+ intervalSubs.set(id, { id, timer });
72
+ return () => {
73
+ const s = intervalSubs.get(id);
74
+ if (!s)
75
+ return;
76
+ clearInterval(s.timer);
77
+ intervalSubs.delete(id);
78
+ };
79
+ }
80
+ // cron
81
+ const normalized = normalizeCronExpr(expr);
82
+ if (!normalized)
83
+ throw new Error(`非法 cron 表达式: ${expr}`);
84
+ const tz = options?.timeZone?.trim() || undefined;
85
+ // 提前验证 tz 可用,薄弱文本验证会静默接受乱填、到了分钟才报
86
+ if (tz) {
87
+ try {
88
+ new Intl.DateTimeFormat('en-US', { timeZone: tz });
89
+ }
90
+ catch {
91
+ throw new Error(`非法时区: ${tz}`);
92
+ }
93
+ }
94
+ cronSubs.set(id, { id, normalized, handler, timeZone: tz });
95
+ ensureCronLoop();
96
+ return () => {
97
+ cronSubs.delete(id);
98
+ };
99
+ },
100
+ validate(expr) {
101
+ return validateCronExpr(expr);
102
+ },
103
+ nextFireTime(expr, from = new Date(), lookaheadMinutes = 366 * 24 * 60, options) {
104
+ const v = validateCronExpr(expr);
105
+ if (!v.ok)
106
+ return null;
107
+ if (v.kind === 'interval') {
108
+ return from.getTime() + (v.intervalSeconds ?? 0) * 1000;
109
+ }
110
+ const tz = options?.timeZone?.trim() || undefined;
111
+ const start = new Date(from);
112
+ start.setSeconds(0, 0);
113
+ start.setMinutes(start.getMinutes() + 1); // 下一整分钟起
114
+ for (let i = 0; i < lookaheadMinutes; i++) {
115
+ const candidate = new Date(start.getTime() + i * 60_000);
116
+ if (matchesCron(v.normalized, candidate, tz))
117
+ return candidate.getTime();
118
+ }
119
+ return null;
120
+ },
121
+ };
122
+ ctx.provide('cron-engine', service);
123
+ ctx.onDispose(() => {
124
+ if (tickTimer)
125
+ clearInterval(tickTimer);
126
+ if (alignTimer)
127
+ clearTimeout(alignTimer);
128
+ for (const s of intervalSubs.values())
129
+ clearInterval(s.timer);
130
+ cronSubs.clear();
131
+ intervalSubs.clear();
132
+ });
133
+ logger.info('cron-engine 已就绪');
134
+ }
135
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,2CAA2C;AAC3C,EAAE;AACF,uBAAuB;AACvB,8CAA8C;AAC9C,6CAA6C;AAC7C,iCAAiC;AACjC,EAAE;AACF,gEAAgE;AAChE,+DAA+D;AAG/D,OAAO,EAGL,WAAW,EACX,iBAAiB,EAEjB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,IAAI,GAAG,2BAA2B,CAAC;AAChD,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,aAAa,CAAC,CAAC;AAexC,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA4B,CAAC;IACrD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC7D,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,SAAS,GAA0C,IAAI,CAAC;IAC5D,IAAI,UAAU,GAAyC,IAAI,CAAC;IAE5D,SAAS,cAAc;QACrB,IAAI,SAAS,IAAI,UAAU;YAAE,OAAO;QACpC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;QAC9E,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,UAAU,GAAG,IAAI,CAAC;YAClB,QAAQ,EAAE,CAAC;YACX,SAAS,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvC,CAAC;IAED,SAAS,QAAQ;QACf,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrB,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;oBACxB,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC;wBACzB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,UAAU,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;oBACnG,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,UAAU,UAAU,EAAE,GAAG,CAAC,CAAC;gBACnF,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAe;QAC1B,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAA8B;YACrD,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,CAAC,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;YACpB,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC1B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;gBAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;oBAC7B,IAAI,CAAC;wBACH,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;wBACpB,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC;4BACzB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,WAAW,IAAI,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;wBACzF,CAAC;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,WAAW,IAAI,UAAU,EAAE,GAAG,CAAC,CAAC;oBACzE,CAAC;gBACH,CAAC,EAAE,EAAE,CAAC,CAAC;gBACP,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpC,OAAO,GAAG,EAAE;oBACV,MAAM,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAC/B,IAAI,CAAC,CAAC;wBAAE,OAAO;oBACf,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;oBACvB,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC1B,CAAC,CAAC;YACJ,CAAC;YACD,OAAO;YACP,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;YAClD,kCAAkC;YAClC,IAAI,EAAE,EAAE,CAAC;gBACP,IAAI,CAAC;oBACH,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;gBACrD,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBACjC,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5D,cAAc,EAAE,CAAC;YACjB,OAAO,GAAG,EAAE;gBACV,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACtB,CAAC,CAAC;QACJ,CAAC;QAED,QAAQ,CAAC,IAAI;YACX,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QAED,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,EAAE,EAAE,gBAAgB,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,EAAE,OAA8B;YACpG,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,CAAC,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YACvB,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAC1D,CAAC;YACD,MAAM,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACvB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;gBACzD,IAAI,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC;oBAAE,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC;YAC3E,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;IAEF,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAEpC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;QACjB,IAAI,SAAS;YAAE,aAAa,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,UAAU;YAAE,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,MAAM,EAAE;YAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,YAAY,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AACjC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@aalis/plugin-cron-engine",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "keywords": [
6
+ "aalis-plugin"
7
+ ],
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "dependencies": {
14
+ "@aalis/plugin-cron-engine-api": "^0.1.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.0.0",
18
+ "typescript": "^5.7.0",
19
+ "@aalis/core": "0.1.0"
20
+ },
21
+ "peerDependencies": {
22
+ "@aalis/core": "^0.1.0"
23
+ },
24
+ "aalis": {
25
+ "service": {
26
+ "provides": [
27
+ "cron-engine"
28
+ ]
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "dev": "tsc --watch"
34
+ }
35
+ }