@legonode/cron 0.0.1
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 +96 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/moduleCache.d.ts +3 -0
- package/dist/moduleCache.d.ts.map +1 -0
- package/dist/moduleCache.js +19 -0
- package/dist/moduleCache.js.map +1 -0
- package/dist/parseSchedule.d.ts +15 -0
- package/dist/parseSchedule.d.ts.map +1 -0
- package/dist/parseSchedule.js +111 -0
- package/dist/parseSchedule.js.map +1 -0
- package/dist/plugin.d.ts +19 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +120 -0
- package/dist/plugin.js.map +1 -0
- package/dist/runScheduler.d.ts +14 -0
- package/dist/runScheduler.d.ts.map +1 -0
- package/dist/runScheduler.js +58 -0
- package/dist/runScheduler.js.map +1 -0
- package/dist/scheduleLoader.d.ts +14 -0
- package/dist/scheduleLoader.d.ts.map +1 -0
- package/dist/scheduleLoader.js +122 -0
- package/dist/scheduleLoader.js.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @legonode/cron
|
|
2
|
+
|
|
3
|
+
Cron schedules and plugin for `legonode`.
|
|
4
|
+
|
|
5
|
+
This package provides:
|
|
6
|
+
- A `createCronPlugin()` plugin that runs tasks from `app/cron/*.cron.ts`
|
|
7
|
+
- Manual task triggering via `ctx.schedule(name, payload, options?)`
|
|
8
|
+
- Schedule loader/runner utilities if you want low-level control
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
In this monorepo it is a workspace package.
|
|
13
|
+
|
|
14
|
+
For external usage:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add @legonode/cron
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`legonode` is a peer dependency.
|
|
21
|
+
|
|
22
|
+
## Basic Usage
|
|
23
|
+
|
|
24
|
+
Add the plugin in `legonode.config.ts`:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import type { LegonodeConfig } from "legonode";
|
|
28
|
+
import { createCronPlugin } from "@legonode/cron";
|
|
29
|
+
|
|
30
|
+
const config: LegonodeConfig = {
|
|
31
|
+
plugins: [createCronPlugin()]
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default config;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Disable Cron In Dev
|
|
38
|
+
|
|
39
|
+
If you want cron files to not run during `legonode dev`:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
plugins: [createCronPlugin({ disableDevCron: true })]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This only disables scheduled/background runs in dev mode. The plugin still keeps cron lookup available for manual `ctx.schedule(...)`.
|
|
46
|
+
|
|
47
|
+
## Cron File Format
|
|
48
|
+
|
|
49
|
+
Create files in `app/cron` with names ending in `.cron.ts` (or `.js`, `.mts`, `.mjs`).
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import type { ScheduleDef, TaskContext } from "@legonode/cron";
|
|
55
|
+
|
|
56
|
+
export const schedule: ScheduleDef = { every: "10s" };
|
|
57
|
+
|
|
58
|
+
export async function run(ctx: TaskContext) {
|
|
59
|
+
console.log("cron run", ctx.payload);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Supported schedule styles:
|
|
64
|
+
- `{ every: "minute" }`
|
|
65
|
+
- `{ every: "hour" }`
|
|
66
|
+
- `{ every: "day", at: "09:30" }`
|
|
67
|
+
- `{ every: "week", day: "monday", at: "10:00" }`
|
|
68
|
+
- `{ every: "month", date: 1, at: "00:00" }`
|
|
69
|
+
- `{ every: "10s" }`, `{ every: "5m" }`, `{ every: "2h" }`, etc.
|
|
70
|
+
|
|
71
|
+
## Manual Trigger From Request Context
|
|
72
|
+
|
|
73
|
+
The plugin augments `LegonodeContext` with:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
ctx.schedule(name, payload?, { delayMs?, at? })
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await ctx.schedule("interval.example", { source: "api" }, { delayMs: 2000 });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`at` accepts:
|
|
86
|
+
- `Date`
|
|
87
|
+
- epoch milliseconds (`number`)
|
|
88
|
+
- Date-parsable string
|
|
89
|
+
|
|
90
|
+
## Exports
|
|
91
|
+
|
|
92
|
+
Top-level exports include:
|
|
93
|
+
- `createCronPlugin`
|
|
94
|
+
- `ScheduleDef`, `Schedule`, `TaskContext`, `TaskRunFn`, `LoadedTask`
|
|
95
|
+
- `loadSchedulesFromApp`, `scanScheduleFiles`, `createScheduleRunner`, `runScheduler`, and related helpers
|
|
96
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { ScheduleDef, Schedule, TaskContext, TaskRunFn, LoadedTask } from "./types.js";
|
|
2
|
+
export type { ScheduleRunnerFn, ScannedSchedule } from "./scheduleLoader.js";
|
|
3
|
+
export { loadSchedulesFromApp, scanScheduleFiles, loadScheduleModule, createScheduleRunner, getOrCreateScheduleRunner, clearScheduleRunnerCache } from "./scheduleLoader.js";
|
|
4
|
+
export { runScheduler } from "./runScheduler.js";
|
|
5
|
+
export type { RunSchedulerOptions } from "./runScheduler.js";
|
|
6
|
+
export { getIntervalMs, isAtSchedule, getNextRunMs } from "./parseSchedule.js";
|
|
7
|
+
export { createCronPlugin } from "./plugin.js";
|
|
8
|
+
export type { CronPluginOptions, ScheduleWhenOptions, ScheduleInvokeFn } from "./plugin.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { loadSchedulesFromApp, scanScheduleFiles, loadScheduleModule, createScheduleRunner, getOrCreateScheduleRunner, clearScheduleRunnerCache } from "./scheduleLoader.js";
|
|
2
|
+
export { runScheduler } from "./runScheduler.js";
|
|
3
|
+
export { getIntervalMs, isAtSchedule, getNextRunMs } from "./parseSchedule.js";
|
|
4
|
+
export { createCronPlugin } from "./plugin.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moduleCache.d.ts","sourceRoot":"","sources":["../src/moduleCache.ts"],"names":[],"mappings":"AAMA,qGAAqG;AACrG,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAS5D"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
const _require = createRequire(import.meta.url);
|
|
5
|
+
/** Invalidate Node/Bun module cache for a file so the next import() loads fresh (dev hot reload). */
|
|
6
|
+
export function invalidateModuleCache(filePath) {
|
|
7
|
+
if (!_require.cache)
|
|
8
|
+
return;
|
|
9
|
+
const url = pathToFileURL(filePath).href;
|
|
10
|
+
const resolved = resolve(filePath);
|
|
11
|
+
const keys = new Set([url, resolved]);
|
|
12
|
+
for (const key of Object.keys(_require.cache)) {
|
|
13
|
+
if (key === resolved || key.endsWith(filePath) || key.endsWith(resolved))
|
|
14
|
+
keys.add(key);
|
|
15
|
+
}
|
|
16
|
+
for (const k of keys)
|
|
17
|
+
delete _require.cache[k];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=moduleCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moduleCache.js","sourceRoot":"","sources":["../src/moduleCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEhD,qGAAqG;AACrG,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,IAAI,CAAC,QAAQ,CAAC,KAAK;QAAE,OAAO;IAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9C,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1F,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ScheduleDef } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns interval in ms for setInterval, or null if this is an "at" schedule (day/week/month).
|
|
4
|
+
*/
|
|
5
|
+
export declare function getIntervalMs(def: ScheduleDef): number | null;
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if this schedule runs at a specific time (day at, week day at, month date at).
|
|
8
|
+
*/
|
|
9
|
+
export declare function isAtSchedule(def: ScheduleDef): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Get ms until the next run for "at" schedules (day at HH:MM, week day at HH:MM, month date at HH:MM).
|
|
12
|
+
* Never returns 0 so we avoid setTimeout(..., 0) and immediate re-runs.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getNextRunMs(def: ScheduleDef): number;
|
|
15
|
+
//# sourceMappingURL=parseSchedule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseSchedule.d.ts","sourceRoot":"","sources":["../src/parseSchedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA0C9C;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAM7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAKtD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAkCrD"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
2
|
+
const HOUR_MS = 60 * 60 * 1000;
|
|
3
|
+
const MINUTE_MS = 60 * 1000;
|
|
4
|
+
const WEEKDAYS = {
|
|
5
|
+
sunday: 0,
|
|
6
|
+
monday: 1,
|
|
7
|
+
tuesday: 2,
|
|
8
|
+
wednesday: 3,
|
|
9
|
+
thursday: 4,
|
|
10
|
+
friday: 5,
|
|
11
|
+
saturday: 6
|
|
12
|
+
};
|
|
13
|
+
function parseTime(at) {
|
|
14
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(at.trim());
|
|
15
|
+
if (!match)
|
|
16
|
+
return { hours: 0, minutes: 0 };
|
|
17
|
+
return {
|
|
18
|
+
hours: Math.min(23, Math.max(0, parseInt(match[1], 10))),
|
|
19
|
+
minutes: Math.min(59, Math.max(0, parseInt(match[2], 10)))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Parse interval string like "5m", "10s", "1h" to ms. */
|
|
23
|
+
function parseIntervalEvery(every) {
|
|
24
|
+
const s = every.trim().toLowerCase();
|
|
25
|
+
const match = /^(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/.exec(s);
|
|
26
|
+
if (match) {
|
|
27
|
+
const n = parseInt(match[1], 10);
|
|
28
|
+
const unit = match[2];
|
|
29
|
+
if (unit === "s" || unit === "sec")
|
|
30
|
+
return n * 1000;
|
|
31
|
+
if (unit === "m" || unit === "min")
|
|
32
|
+
return n * MINUTE_MS;
|
|
33
|
+
if (unit === "h" || unit === "hr")
|
|
34
|
+
return n * HOUR_MS;
|
|
35
|
+
if (unit === "d" || unit === "day")
|
|
36
|
+
return n * DAY_MS;
|
|
37
|
+
}
|
|
38
|
+
if (s === "minute" || s === "1m")
|
|
39
|
+
return MINUTE_MS;
|
|
40
|
+
if (s === "hour" || s === "1h")
|
|
41
|
+
return HOUR_MS;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns interval in ms for setInterval, or null if this is an "at" schedule (day/week/month).
|
|
46
|
+
*/
|
|
47
|
+
export function getIntervalMs(def) {
|
|
48
|
+
if (def.every === "minute")
|
|
49
|
+
return MINUTE_MS;
|
|
50
|
+
if (def.every === "hour")
|
|
51
|
+
return HOUR_MS;
|
|
52
|
+
const interval = parseIntervalEvery(def.every);
|
|
53
|
+
if (interval !== null)
|
|
54
|
+
return interval;
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns true if this schedule runs at a specific time (day at, week day at, month date at).
|
|
59
|
+
*/
|
|
60
|
+
export function isAtSchedule(def) {
|
|
61
|
+
if (def.every === "day" && "at" in def)
|
|
62
|
+
return true;
|
|
63
|
+
if (def.every === "week")
|
|
64
|
+
return true;
|
|
65
|
+
if (def.every === "month")
|
|
66
|
+
return true;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get ms until the next run for "at" schedules (day at HH:MM, week day at HH:MM, month date at HH:MM).
|
|
71
|
+
* Never returns 0 so we avoid setTimeout(..., 0) and immediate re-runs.
|
|
72
|
+
*/
|
|
73
|
+
export function getNextRunMs(def) {
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const at = "at" in def && typeof def.at === "string" ? def.at : "00:00";
|
|
76
|
+
const { hours, minutes } = parseTime(at);
|
|
77
|
+
let ms;
|
|
78
|
+
if (def.every === "day") {
|
|
79
|
+
let next = new Date(now);
|
|
80
|
+
next.setHours(hours, minutes, 0, 0);
|
|
81
|
+
if (next.getTime() <= now.getTime())
|
|
82
|
+
next = new Date(next.getTime() + DAY_MS);
|
|
83
|
+
ms = next.getTime() - now.getTime();
|
|
84
|
+
}
|
|
85
|
+
else if (def.every === "week") {
|
|
86
|
+
const dayName = def.day ?? "monday";
|
|
87
|
+
const targetDay = WEEKDAYS[dayName.toLowerCase()] ?? 1;
|
|
88
|
+
let next = new Date(now);
|
|
89
|
+
next.setHours(hours, minutes, 0, 0);
|
|
90
|
+
let diff = targetDay - next.getDay();
|
|
91
|
+
if (diff < 0)
|
|
92
|
+
diff += 7;
|
|
93
|
+
else if (diff === 0 && next.getTime() <= now.getTime())
|
|
94
|
+
diff = 7;
|
|
95
|
+
next = new Date(next.getTime() + diff * DAY_MS);
|
|
96
|
+
ms = next.getTime() - now.getTime();
|
|
97
|
+
}
|
|
98
|
+
else if (def.every === "month") {
|
|
99
|
+
const date = def.date ?? 1;
|
|
100
|
+
let next = new Date(now.getFullYear(), now.getMonth(), date, hours, minutes, 0, 0);
|
|
101
|
+
if (next.getTime() <= now.getTime()) {
|
|
102
|
+
next = new Date(now.getFullYear(), now.getMonth() + 1, date, hours, minutes, 0, 0);
|
|
103
|
+
}
|
|
104
|
+
ms = next.getTime() - now.getTime();
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
ms = DAY_MS;
|
|
108
|
+
}
|
|
109
|
+
return Math.max(1, ms);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=parseSchedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseSchedule.js","sourceRoot":"","sources":["../src/parseSchedule.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LegonodePlugin } from "legonode";
|
|
2
|
+
export type CronPluginOptions = {
|
|
3
|
+
/** Disable scheduler in `disableDevCron` command (default: false). */
|
|
4
|
+
disableDevCron?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type ScheduleWhenOptions = {
|
|
7
|
+
/** Run at an absolute time (Date, epoch ms, or Date-parsable string). */
|
|
8
|
+
at?: Date | number | string;
|
|
9
|
+
/** Run after this delay (ms). */
|
|
10
|
+
delayMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ScheduleInvokeFn = (name: string, payload?: unknown, options?: ScheduleWhenOptions) => void | Promise<void>;
|
|
13
|
+
declare module "legonode" {
|
|
14
|
+
interface LegonodeContext {
|
|
15
|
+
schedule: ScheduleInvokeFn;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export declare function createCronPlugin(options?: CronPluginOptions): LegonodePlugin;
|
|
19
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAS/C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,yEAAyE;IACzE,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC5B,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,EACjB,OAAO,CAAC,EAAE,mBAAmB,KAC1B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,eAAe;QACvB,QAAQ,EAAE,gBAAgB,CAAC;KAC5B;CACF;AAsBD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAmGhF"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { clearScheduleRunnerCache, getOrCreateScheduleRunner, loadSchedulesFromApp, } from "./scheduleLoader.js";
|
|
2
|
+
import { runScheduler } from "./runScheduler.js";
|
|
3
|
+
function parseAtMs(at) {
|
|
4
|
+
if (typeof at === "number")
|
|
5
|
+
return Number.isFinite(at) ? at : null;
|
|
6
|
+
if (at instanceof Date) {
|
|
7
|
+
const ms = at.getTime();
|
|
8
|
+
return Number.isFinite(ms) ? ms : null;
|
|
9
|
+
}
|
|
10
|
+
if (typeof at === "string") {
|
|
11
|
+
const ms = Date.parse(at);
|
|
12
|
+
return Number.isFinite(ms) ? ms : null;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function isCronFile(filename) {
|
|
17
|
+
if (!filename)
|
|
18
|
+
return false;
|
|
19
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
20
|
+
// Bun/Node watch commonly reports paths like "src/cron/x.cron.ts", not "cron/x.cron.ts".
|
|
21
|
+
return normalized.includes("/cron/") && /\.cron\.(ts|js|mts|mjs)$/i.test(normalized);
|
|
22
|
+
}
|
|
23
|
+
export function createCronPlugin(options = {}) {
|
|
24
|
+
const disableDevCron = options.disableDevCron === true;
|
|
25
|
+
let stopScheduler = null;
|
|
26
|
+
let currentAppDir = "";
|
|
27
|
+
function attachSchedule(ctx) {
|
|
28
|
+
ctx.schedule = async (name, payload, when) => {
|
|
29
|
+
const appDir = currentAppDir;
|
|
30
|
+
if (!appDir)
|
|
31
|
+
return;
|
|
32
|
+
const runner = await getOrCreateScheduleRunner(appDir);
|
|
33
|
+
const delayMs = typeof when?.delayMs === "number" && Number.isFinite(when.delayMs)
|
|
34
|
+
? Math.max(0, when.delayMs)
|
|
35
|
+
: null;
|
|
36
|
+
const atMs = when?.at !== undefined ? parseAtMs(when.at) : null;
|
|
37
|
+
if (delayMs === null && atMs === null) {
|
|
38
|
+
await runner(name, payload);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const computedDelay = delayMs !== null ? delayMs : Math.max(0, (atMs ?? Date.now()) - Date.now());
|
|
42
|
+
if (computedDelay <= 0) {
|
|
43
|
+
await runner(name, payload);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
void runner(name, payload);
|
|
48
|
+
}, computedDelay);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function startScheduler(appDir, cacheBust = false) {
|
|
52
|
+
if (!appDir)
|
|
53
|
+
return;
|
|
54
|
+
if (cacheBust) {
|
|
55
|
+
process.env.LEGONODE_CRON_RELOAD_TOKEN = String(Date.now());
|
|
56
|
+
clearScheduleRunnerCache(appDir);
|
|
57
|
+
}
|
|
58
|
+
stopScheduler?.();
|
|
59
|
+
stopScheduler = null;
|
|
60
|
+
currentAppDir = appDir;
|
|
61
|
+
const tasks = await loadSchedulesFromApp(appDir, process.env.LEGONODE_CRON_RELOAD_TOKEN);
|
|
62
|
+
if (tasks.length === 0)
|
|
63
|
+
return;
|
|
64
|
+
stopScheduler = runScheduler(tasks, {
|
|
65
|
+
onError: (name, error) => {
|
|
66
|
+
console.error(`[legonode cron ${name}]`, error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function stopCurrent() {
|
|
71
|
+
stopScheduler?.();
|
|
72
|
+
stopScheduler = null;
|
|
73
|
+
if (currentAppDir)
|
|
74
|
+
clearScheduleRunnerCache(currentAppDir);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
name: "@legonode/cron",
|
|
78
|
+
onRequest(ctx) {
|
|
79
|
+
attachSchedule(ctx);
|
|
80
|
+
},
|
|
81
|
+
async onDevStart(ctx) {
|
|
82
|
+
// Keep appDir for manual ctx.schedule(...) even when dev scheduler is disabled.
|
|
83
|
+
if (ctx.appDir)
|
|
84
|
+
currentAppDir = ctx.appDir;
|
|
85
|
+
if (disableDevCron)
|
|
86
|
+
return;
|
|
87
|
+
await startScheduler(ctx.appDir);
|
|
88
|
+
},
|
|
89
|
+
async onDevFileChange(ctx) {
|
|
90
|
+
const changed = ctx.changedPath ?? ctx.filename;
|
|
91
|
+
if (!isCronFile(changed))
|
|
92
|
+
return;
|
|
93
|
+
// Manual schedule() uses the cached runner too; bust it on cron file changes.
|
|
94
|
+
if (ctx.appDir) {
|
|
95
|
+
currentAppDir = ctx.appDir;
|
|
96
|
+
process.env.LEGONODE_CRON_RELOAD_TOKEN = String(Date.now());
|
|
97
|
+
clearScheduleRunnerCache(ctx.appDir);
|
|
98
|
+
}
|
|
99
|
+
if (disableDevCron)
|
|
100
|
+
return;
|
|
101
|
+
await startScheduler(ctx.appDir, true);
|
|
102
|
+
},
|
|
103
|
+
onDevRestart() {
|
|
104
|
+
stopCurrent();
|
|
105
|
+
},
|
|
106
|
+
onDevStop() {
|
|
107
|
+
stopCurrent();
|
|
108
|
+
},
|
|
109
|
+
async onStartListening(ctx) {
|
|
110
|
+
await startScheduler(ctx.appDir);
|
|
111
|
+
},
|
|
112
|
+
onStartStop() {
|
|
113
|
+
stopCurrent();
|
|
114
|
+
},
|
|
115
|
+
onStartError() {
|
|
116
|
+
stopCurrent();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LoadedTask, TaskContext } from "./types.js";
|
|
2
|
+
export type RunSchedulerOptions = {
|
|
3
|
+
ctx?: TaskContext;
|
|
4
|
+
onRun?: (taskName: string, ctx: TaskContext) => void | Promise<void>;
|
|
5
|
+
onComplete?: (taskName: string, ctx: TaskContext) => void | Promise<void>;
|
|
6
|
+
onError?: (taskName: string, err: unknown) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Start the scheduler for the given tasks. Uses setInterval for interval-based schedules
|
|
10
|
+
* and setTimeout for "at" schedules (day/week/month), then reschedules.
|
|
11
|
+
* Does not return; call from a long-running process.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runScheduler(tasks: LoadedTask[], options?: RunSchedulerOptions): () => void;
|
|
14
|
+
//# sourceMappingURL=runScheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runScheduler.d.ts","sourceRoot":"","sources":["../src/runScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,IAAI,CAiD/F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getIntervalMs, isAtSchedule, getNextRunMs } from "./parseSchedule.js";
|
|
2
|
+
/**
|
|
3
|
+
* Start the scheduler for the given tasks. Uses setInterval for interval-based schedules
|
|
4
|
+
* and setTimeout for "at" schedules (day/week/month), then reschedules.
|
|
5
|
+
* Does not return; call from a long-running process.
|
|
6
|
+
*/
|
|
7
|
+
export function runScheduler(tasks, options = {}) {
|
|
8
|
+
const ctx = options.ctx ?? {};
|
|
9
|
+
const onRun = options.onRun ?? null;
|
|
10
|
+
const onComplete = options.onComplete ?? null;
|
|
11
|
+
const onError = options.onError ?? ((name, err) => console.error(`[legonode task ${name}]`, err));
|
|
12
|
+
const timeouts = [];
|
|
13
|
+
const intervals = [];
|
|
14
|
+
function runTask(task) {
|
|
15
|
+
void (async () => {
|
|
16
|
+
try {
|
|
17
|
+
if (onRun)
|
|
18
|
+
await onRun(task.name, ctx);
|
|
19
|
+
await task.run(ctx);
|
|
20
|
+
if (onComplete)
|
|
21
|
+
await onComplete(task.name, ctx);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
onError(task.name, err);
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
}
|
|
28
|
+
function scheduleAt(task, def) {
|
|
29
|
+
// Use at least 1s delay so we don't reschedule in the same second and risk multiple runs (e.g. hot reload).
|
|
30
|
+
const ms = Math.max(1000, getNextRunMs(def));
|
|
31
|
+
const t = setTimeout(() => {
|
|
32
|
+
runTask(task);
|
|
33
|
+
scheduleAt(task, def);
|
|
34
|
+
}, ms);
|
|
35
|
+
timeouts.push(t);
|
|
36
|
+
}
|
|
37
|
+
for (const task of tasks) {
|
|
38
|
+
for (const def of task.schedule) {
|
|
39
|
+
const intervalMs = getIntervalMs(def);
|
|
40
|
+
if (intervalMs !== null) {
|
|
41
|
+
// First run after intervalMs, then every intervalMs (no run at startup)
|
|
42
|
+
const t = setInterval(() => runTask(task), intervalMs);
|
|
43
|
+
intervals.push(t);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isAtSchedule(def)) {
|
|
47
|
+
scheduleAt(task, def);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return function stop() {
|
|
52
|
+
for (const t of timeouts)
|
|
53
|
+
clearTimeout(t);
|
|
54
|
+
for (const t of intervals)
|
|
55
|
+
clearInterval(t);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=runScheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runScheduler.js","sourceRoot":"","sources":["../src/runScheduler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAS/E;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAmB,EAAE,UAA+B,EAAE;IACjF,MAAM,GAAG,GAAgB,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;IACpC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAoC,EAAE,CAAC;IACrD,MAAM,SAAS,GAAqC,EAAE,CAAC;IAEvD,SAAS,OAAO,CAAC,IAAgB;QAC/B,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,IAAI,KAAK;oBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACvC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpB,IAAI,UAAU;oBAAE,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACnD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,SAAS,UAAU,CAAC,IAAgB,EAAE,GAAqC;QACzE,4GAA4G;QAC5G,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxB,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,wEAAwE;gBACxE,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;gBACvD,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,SAAS;YACX,CAAC;YACD,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,IAAI;QAClB,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LoadedTask } from "./types.js";
|
|
2
|
+
export type ScheduleRunnerFn = (name: string, payload?: unknown) => void | Promise<void>;
|
|
3
|
+
/** Build a runner that invokes a schedule's run by name (e.g. "interval.example"). */
|
|
4
|
+
export declare function createScheduleRunner(tasks: LoadedTask[]): ScheduleRunnerFn;
|
|
5
|
+
export declare function getOrCreateScheduleRunner(appDir: string): Promise<ScheduleRunnerFn>;
|
|
6
|
+
export declare function clearScheduleRunnerCache(appDir?: string): void;
|
|
7
|
+
export type ScannedSchedule = {
|
|
8
|
+
name: string;
|
|
9
|
+
filePath: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function scanScheduleFiles(appDir: string): ScannedSchedule[];
|
|
12
|
+
export declare function loadScheduleModule(filePath: string, cacheBust?: string): Promise<LoadedTask | null>;
|
|
13
|
+
export declare function loadSchedulesFromApp(appDir: string, cacheBust?: string): Promise<LoadedTask[]>;
|
|
14
|
+
//# sourceMappingURL=scheduleLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduleLoader.d.ts","sourceRoot":"","sources":["../src/scheduleLoader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAyB,UAAU,EAA0B,MAAM,YAAY,CAAC;AAuB5F,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAIzF,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,gBAAgB,CAc1E;AAED,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CASzF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAG9D;AAOD,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAoBnE;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmB5B;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CAQvB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { join, resolve, basename } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { invalidateModuleCache } from "./moduleCache.js";
|
|
5
|
+
const CRON_EXTENSIONS = [".cron.ts", ".cron.js", ".cron.mts", ".cron.mjs"];
|
|
6
|
+
const CRON_DIR = "cron";
|
|
7
|
+
/** Convert task name (e.g. intervalExample) to schedule name (e.g. interval.example). */
|
|
8
|
+
function toScheduleName(name) {
|
|
9
|
+
if (!name.length)
|
|
10
|
+
return "";
|
|
11
|
+
const parts = [];
|
|
12
|
+
let current = name[0].toLowerCase();
|
|
13
|
+
for (let i = 1; i < name.length; i++) {
|
|
14
|
+
const c = name[i];
|
|
15
|
+
if (c >= "A" && c <= "Z") {
|
|
16
|
+
parts.push(current);
|
|
17
|
+
current = c.toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
current += c;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
parts.push(current);
|
|
24
|
+
return parts.join(".");
|
|
25
|
+
}
|
|
26
|
+
const scheduleRunnerCache = new Map();
|
|
27
|
+
/** Build a runner that invokes a schedule's run by name (e.g. "interval.example"). */
|
|
28
|
+
export function createScheduleRunner(tasks) {
|
|
29
|
+
const byName = new Map();
|
|
30
|
+
for (const task of tasks) {
|
|
31
|
+
byName.set(toScheduleName(task.name), task);
|
|
32
|
+
}
|
|
33
|
+
return (name, payload) => {
|
|
34
|
+
const task = byName.get(name);
|
|
35
|
+
if (!task) {
|
|
36
|
+
console.warn(`[legonode] schedule not found: "${name}"`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const ctx = { payload };
|
|
40
|
+
return Promise.resolve(task.run(ctx));
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export async function getOrCreateScheduleRunner(appDir) {
|
|
44
|
+
const key = resolve(appDir);
|
|
45
|
+
let runner = scheduleRunnerCache.get(key);
|
|
46
|
+
if (runner)
|
|
47
|
+
return runner;
|
|
48
|
+
const cacheBust = typeof process !== "undefined" ? process.env.LEGONODE_CRON_RELOAD_TOKEN : undefined;
|
|
49
|
+
const tasks = await loadSchedulesFromApp(appDir, cacheBust);
|
|
50
|
+
runner = createScheduleRunner(tasks);
|
|
51
|
+
scheduleRunnerCache.set(key, runner);
|
|
52
|
+
return runner;
|
|
53
|
+
}
|
|
54
|
+
export function clearScheduleRunnerCache(appDir) {
|
|
55
|
+
if (appDir)
|
|
56
|
+
scheduleRunnerCache.delete(resolve(appDir));
|
|
57
|
+
else
|
|
58
|
+
scheduleRunnerCache.clear();
|
|
59
|
+
}
|
|
60
|
+
function normalizeSchedule(schedule) {
|
|
61
|
+
if (Array.isArray(schedule))
|
|
62
|
+
return schedule;
|
|
63
|
+
return [schedule];
|
|
64
|
+
}
|
|
65
|
+
export function scanScheduleFiles(appDir) {
|
|
66
|
+
const base = resolve(appDir, CRON_DIR);
|
|
67
|
+
const list = [];
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = readdirSync(base, { withFileTypes: true }).map((e) => ({
|
|
71
|
+
name: e.name,
|
|
72
|
+
isFile: e.isFile()
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return list;
|
|
77
|
+
}
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
if (!entry.isFile)
|
|
80
|
+
continue;
|
|
81
|
+
const ext = CRON_EXTENSIONS.find((e) => entry.name.endsWith(e));
|
|
82
|
+
if (!ext)
|
|
83
|
+
continue;
|
|
84
|
+
const name = entry.name.slice(0, -ext.length);
|
|
85
|
+
list.push({ name, filePath: join(base, entry.name) });
|
|
86
|
+
}
|
|
87
|
+
return list;
|
|
88
|
+
}
|
|
89
|
+
export async function loadScheduleModule(filePath, cacheBust) {
|
|
90
|
+
try {
|
|
91
|
+
if (cacheBust)
|
|
92
|
+
invalidateModuleCache(filePath);
|
|
93
|
+
const url = pathToFileURL(filePath).href;
|
|
94
|
+
const importUrl = cacheBust ? `${url}?t=${cacheBust}` : url;
|
|
95
|
+
const mod = await import(importUrl);
|
|
96
|
+
const schedule = mod.schedule;
|
|
97
|
+
const run = mod.run;
|
|
98
|
+
if (schedule == null || typeof run !== "function")
|
|
99
|
+
return null;
|
|
100
|
+
const scheduleList = normalizeSchedule(schedule);
|
|
101
|
+
const name = (mod.name ?? basename(filePath).replace(/\.cron\.(ts|js|mts|mjs)$/i, "")) || "task";
|
|
102
|
+
return {
|
|
103
|
+
name,
|
|
104
|
+
schedule: scheduleList,
|
|
105
|
+
run: run
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export async function loadSchedulesFromApp(appDir, cacheBust) {
|
|
113
|
+
const scanned = scanScheduleFiles(appDir);
|
|
114
|
+
const tasks = [];
|
|
115
|
+
for (const { filePath } of scanned) {
|
|
116
|
+
const task = await loadScheduleModule(filePath, cacheBust);
|
|
117
|
+
if (task)
|
|
118
|
+
tasks.push(task);
|
|
119
|
+
}
|
|
120
|
+
return tasks;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=scheduleLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduleLoader.js","sourceRoot":"","sources":["../src/scheduleLoader.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-based schedule definition (no cron strings).
|
|
3
|
+
* Supports: every minute/hour, every day at, every week day at, every month date at, or interval (e.g. "5m", "10s").
|
|
4
|
+
*/
|
|
5
|
+
export type ScheduleDef = {
|
|
6
|
+
every: "minute";
|
|
7
|
+
} | {
|
|
8
|
+
every: "hour";
|
|
9
|
+
} | {
|
|
10
|
+
every: "day";
|
|
11
|
+
at?: string;
|
|
12
|
+
} | {
|
|
13
|
+
every: "week";
|
|
14
|
+
day?: string;
|
|
15
|
+
at?: string;
|
|
16
|
+
} | {
|
|
17
|
+
every: "month";
|
|
18
|
+
date?: number;
|
|
19
|
+
at?: string;
|
|
20
|
+
} | {
|
|
21
|
+
every: string;
|
|
22
|
+
};
|
|
23
|
+
/** One task can have multiple schedules. */
|
|
24
|
+
export type Schedule = ScheduleDef | ScheduleDef[];
|
|
25
|
+
export type TaskContext = {
|
|
26
|
+
/** Reserved for app-specific services (e.g. db, reportService). */
|
|
27
|
+
services?: Record<string, unknown>;
|
|
28
|
+
/** Set when the schedule is triggered via ctx.schedule(name, payload). */
|
|
29
|
+
payload?: unknown;
|
|
30
|
+
};
|
|
31
|
+
export type TaskRunFn = (ctx: TaskContext) => void | Promise<void>;
|
|
32
|
+
export type LoadedTask = {
|
|
33
|
+
name: string;
|
|
34
|
+
schedule: ScheduleDef[];
|
|
35
|
+
run: TaskRunFn;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GACnB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACjB;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,EAAE,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG;IACxB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,GAAG,EAAE,SAAS,CAAC;CAChB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@legonode/cron",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Cron schedules, loader, and runner for Legonode apps (app/cron/*.cron.ts)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"legonode": ">=0.0.4"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"legonode": "workspace:*"
|
|
29
|
+
}
|
|
30
|
+
}
|