@gravito/horizon 2.0.0 → 3.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/dist/index.cjs +45 -118
- package/dist/index.d.cts +179 -30
- package/dist/index.d.ts +179 -30
- package/dist/index.js +45 -118
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -7822,128 +7822,59 @@ __export(index_exports, {
|
|
|
7822
7822
|
module.exports = __toCommonJS(index_exports);
|
|
7823
7823
|
|
|
7824
7824
|
// src/SimpleCronParser.ts
|
|
7825
|
-
var SimpleCronParser = {
|
|
7825
|
+
var SimpleCronParser = class {
|
|
7826
7826
|
/**
|
|
7827
|
-
* Check if a cron expression
|
|
7828
|
-
* Only supports standard 5-field cron expressions.
|
|
7829
|
-
*
|
|
7830
|
-
* Fields: minute hour day-of-month month day-of-week
|
|
7831
|
-
* Values:
|
|
7832
|
-
* - * (any)
|
|
7833
|
-
* - numbers (0-59, 1-31, 0-11, 0-7)
|
|
7834
|
-
* - ranges (1-5)
|
|
7835
|
-
* - lists (1,2,3)
|
|
7836
|
-
* - steps (*\/5, 1-10/2)
|
|
7827
|
+
* Check if a cron expression is due at the given date/time
|
|
7837
7828
|
*/
|
|
7838
|
-
isDue(expression, timezone, date) {
|
|
7839
|
-
const
|
|
7840
|
-
if (
|
|
7841
|
-
throw new Error(
|
|
7842
|
-
}
|
|
7843
|
-
let targetDate = date;
|
|
7844
|
-
if (timezone && timezone !== "UTC") {
|
|
7845
|
-
try {
|
|
7846
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
7847
|
-
timeZone: timezone,
|
|
7848
|
-
year: "numeric",
|
|
7849
|
-
month: "numeric",
|
|
7850
|
-
day: "numeric",
|
|
7851
|
-
hour: "numeric",
|
|
7852
|
-
minute: "numeric",
|
|
7853
|
-
second: "numeric",
|
|
7854
|
-
hour12: false
|
|
7855
|
-
}).formatToParts(date);
|
|
7856
|
-
const partMap = {};
|
|
7857
|
-
parts.forEach((p) => {
|
|
7858
|
-
partMap[p.type] = p.value;
|
|
7859
|
-
});
|
|
7860
|
-
targetDate = new Date(
|
|
7861
|
-
parseInt(partMap.year ?? "0", 10),
|
|
7862
|
-
parseInt(partMap.month ?? "1", 10) - 1,
|
|
7863
|
-
parseInt(partMap.day ?? "1", 10),
|
|
7864
|
-
parseInt(partMap.hour ?? "0", 10) === 24 ? 0 : parseInt(partMap.hour ?? "0", 10),
|
|
7865
|
-
parseInt(partMap.minute ?? "0", 10),
|
|
7866
|
-
0
|
|
7867
|
-
);
|
|
7868
|
-
} catch (_e) {
|
|
7869
|
-
throw new Error(`Invalid timezone: ${timezone}`);
|
|
7870
|
-
}
|
|
7871
|
-
} else if (timezone === "UTC") {
|
|
7872
|
-
targetDate = new Date(
|
|
7873
|
-
date.getUTCFullYear(),
|
|
7874
|
-
date.getUTCMonth(),
|
|
7875
|
-
date.getUTCDate(),
|
|
7876
|
-
date.getUTCHours(),
|
|
7877
|
-
date.getUTCMinutes(),
|
|
7878
|
-
0
|
|
7879
|
-
);
|
|
7880
|
-
}
|
|
7881
|
-
const [minute, hour, dayOfMonth, month, dayOfWeek] = fields;
|
|
7882
|
-
if (minute === void 0 || hour === void 0 || dayOfMonth === void 0 || month === void 0 || dayOfWeek === void 0) {
|
|
7883
|
-
throw new Error("Invalid cron expression");
|
|
7884
|
-
}
|
|
7885
|
-
const matchMinute = matchField(minute, targetDate.getMinutes(), 0, 59);
|
|
7886
|
-
const matchHour = matchField(hour, targetDate.getHours(), 0, 23);
|
|
7887
|
-
const matchDayOfMonth = matchField(dayOfMonth, targetDate.getDate(), 1, 31);
|
|
7888
|
-
const matchMonth = matchField(month, targetDate.getMonth() + 1, 1, 12);
|
|
7889
|
-
const matchDayOfWeek = matchField(dayOfWeek, targetDate.getDay(), 0, 7);
|
|
7890
|
-
return matchMinute && matchHour && matchDayOfMonth && matchMonth && matchDayOfWeek;
|
|
7891
|
-
}
|
|
7892
|
-
};
|
|
7893
|
-
function matchField(pattern, value, min, max) {
|
|
7894
|
-
if (pattern === "*") {
|
|
7895
|
-
return true;
|
|
7896
|
-
}
|
|
7897
|
-
if (pattern.includes("/")) {
|
|
7898
|
-
const [range, stepStr] = pattern.split("/");
|
|
7899
|
-
if (range === void 0 || stepStr === void 0) {
|
|
7900
|
-
return false;
|
|
7901
|
-
}
|
|
7902
|
-
const step = parseInt(stepStr, 10);
|
|
7903
|
-
if (range === "*") {
|
|
7904
|
-
return (value - min) % step === 0;
|
|
7905
|
-
}
|
|
7906
|
-
if (range.includes("-")) {
|
|
7907
|
-
const [startStr, endStr] = range.split("-");
|
|
7908
|
-
if (startStr === void 0 || endStr === void 0) {
|
|
7909
|
-
return false;
|
|
7910
|
-
}
|
|
7911
|
-
const start = parseInt(startStr, 10);
|
|
7912
|
-
const end = parseInt(endStr, 10);
|
|
7913
|
-
if (value >= start && value <= end) {
|
|
7914
|
-
return (value - start) % step === 0;
|
|
7915
|
-
}
|
|
7916
|
-
return false;
|
|
7829
|
+
static isDue(expression, timezone = "UTC", date = /* @__PURE__ */ new Date()) {
|
|
7830
|
+
const parts = expression.trim().split(/\s+/);
|
|
7831
|
+
if (parts.length !== 5) {
|
|
7832
|
+
throw new Error(`Invalid cron expression: ${expression}`);
|
|
7917
7833
|
}
|
|
7834
|
+
const targetDate = this.getDateInTimezone(date, timezone);
|
|
7835
|
+
const minutes = targetDate.getMinutes();
|
|
7836
|
+
const hours = targetDate.getHours();
|
|
7837
|
+
const dayOfMonth = targetDate.getDate();
|
|
7838
|
+
const month = targetDate.getMonth() + 1;
|
|
7839
|
+
const dayOfWeek = targetDate.getDay();
|
|
7840
|
+
return this.match(parts[0], minutes, 0, 59) && this.match(parts[1], hours, 0, 23) && this.match(parts[2], dayOfMonth, 1, 31) && this.match(parts[3], month, 1, 12) && this.match(parts[4], dayOfWeek, 0, 6, true);
|
|
7918
7841
|
}
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7842
|
+
static match(pattern, value, _min, _max, isDayOfWeek = false) {
|
|
7843
|
+
if (pattern === "*") return true;
|
|
7844
|
+
if (pattern.includes(",")) {
|
|
7845
|
+
return pattern.split(",").some((p) => this.match(p, value, _min, _max, isDayOfWeek));
|
|
7846
|
+
}
|
|
7847
|
+
const stepMatch = pattern.match(/^(\*|\d+(-\d+)?)\/(\d+)$/);
|
|
7848
|
+
if (stepMatch) {
|
|
7849
|
+
const range = stepMatch[1];
|
|
7850
|
+
const step = parseInt(stepMatch[3], 10);
|
|
7851
|
+
if (range === "*") return value % step === 0;
|
|
7852
|
+
const [rMin, rMax] = range.split("-").map((n) => parseInt(n, 10));
|
|
7853
|
+
return value >= rMin && value <= rMax && (value - rMin) % step === 0;
|
|
7854
|
+
}
|
|
7855
|
+
if (pattern.includes("-")) {
|
|
7856
|
+
const [rMin, rMax] = pattern.split("-").map((n) => parseInt(n, 10));
|
|
7857
|
+
return value >= rMin && value <= rMax;
|
|
7858
|
+
}
|
|
7859
|
+
const patternVal = parseInt(pattern, 10);
|
|
7860
|
+
if (isDayOfWeek && patternVal === 7 && value === 0) return true;
|
|
7861
|
+
return patternVal === value;
|
|
7922
7862
|
}
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7863
|
+
static getDateInTimezone(date, timezone) {
|
|
7864
|
+
try {
|
|
7865
|
+
const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
|
|
7866
|
+
if (isNaN(tzDate.getTime())) throw new Error();
|
|
7867
|
+
return tzDate;
|
|
7868
|
+
} catch {
|
|
7869
|
+
throw new Error(`Invalid timezone: ${timezone}`);
|
|
7927
7870
|
}
|
|
7928
|
-
const start = parseInt(startStr, 10);
|
|
7929
|
-
const end = parseInt(endStr, 10);
|
|
7930
|
-
return value >= start && value <= end;
|
|
7931
7871
|
}
|
|
7932
|
-
|
|
7933
|
-
if (max === 7 && expected === 7 && value === 0) {
|
|
7934
|
-
return true;
|
|
7935
|
-
}
|
|
7936
|
-
return value === expected;
|
|
7937
|
-
}
|
|
7872
|
+
};
|
|
7938
7873
|
|
|
7939
7874
|
// src/CronParser.ts
|
|
7940
7875
|
var CronParser = class {
|
|
7941
7876
|
/**
|
|
7942
7877
|
* Get the next execution date based on a cron expression.
|
|
7943
|
-
*
|
|
7944
|
-
* @param expression - Cron expression
|
|
7945
|
-
* @param timezone - Timezone identifier
|
|
7946
|
-
* @param currentDate - Reference date
|
|
7947
7878
|
*/
|
|
7948
7879
|
static async nextDate(expression, timezone = "UTC", currentDate = /* @__PURE__ */ new Date()) {
|
|
7949
7880
|
try {
|
|
@@ -7959,10 +7890,6 @@ var CronParser = class {
|
|
|
7959
7890
|
}
|
|
7960
7891
|
/**
|
|
7961
7892
|
* Check if the cron expression is due to run at the current time (minute precision).
|
|
7962
|
-
*
|
|
7963
|
-
* @param expression - Cron expression
|
|
7964
|
-
* @param timezone - Timezone identifier
|
|
7965
|
-
* @param currentDate - Reference date
|
|
7966
7893
|
*/
|
|
7967
7894
|
static async isDue(expression, timezone = "UTC", currentDate = /* @__PURE__ */ new Date()) {
|
|
7968
7895
|
try {
|
|
@@ -8524,6 +8451,7 @@ var SchedulerManager = class {
|
|
|
8524
8451
|
var OrbitHorizon = class {
|
|
8525
8452
|
/**
|
|
8526
8453
|
* Install the Horizon Orbit into PlanetCore.
|
|
8454
|
+
* Registers the SchedulerManager and configures the distributed lock driver.
|
|
8527
8455
|
*
|
|
8528
8456
|
* @param core - The PlanetCore instance.
|
|
8529
8457
|
*/
|
|
@@ -8534,7 +8462,7 @@ var OrbitHorizon = class {
|
|
|
8534
8462
|
const nodeRole = config.nodeRole;
|
|
8535
8463
|
let lockManager;
|
|
8536
8464
|
if (lockDriver === "cache") {
|
|
8537
|
-
const cacheManager = core.
|
|
8465
|
+
const cacheManager = core.container.make("cache");
|
|
8538
8466
|
if (!cacheManager) {
|
|
8539
8467
|
core.logger.warn(
|
|
8540
8468
|
"[OrbitHorizon] Cache driver requested but cache service not found (ensure orbit-cache is loaded first). Falling back to Memory lock."
|
|
@@ -8547,11 +8475,10 @@ var OrbitHorizon = class {
|
|
|
8547
8475
|
lockManager = new LockManager("memory");
|
|
8548
8476
|
}
|
|
8549
8477
|
const scheduler = new SchedulerManager(lockManager, core.logger, core.hooks, nodeRole);
|
|
8550
|
-
core.
|
|
8478
|
+
core.container.instance(exposeAs, scheduler);
|
|
8551
8479
|
core.adapter.use("*", async (c, next) => {
|
|
8552
8480
|
c.set("scheduler", scheduler);
|
|
8553
|
-
await next();
|
|
8554
|
-
return void 0;
|
|
8481
|
+
return await next();
|
|
8555
8482
|
});
|
|
8556
8483
|
core.logger.info(`[OrbitHorizon] Initialized (Driver: ${lockDriver})`);
|
|
8557
8484
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { CacheManager } from '@gravito/stasis';
|
|
2
|
-
import {
|
|
2
|
+
import { ActionCallback, Logger, HookManager, GravitoOrbit, PlanetCore } from '@gravito/core';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Advanced cron expression parser with fallback support.
|
|
6
|
+
*/
|
|
4
7
|
declare class CronParser {
|
|
5
8
|
/**
|
|
6
9
|
* Get the next execution date based on a cron expression.
|
|
7
|
-
*
|
|
8
|
-
* @param expression - Cron expression
|
|
9
|
-
* @param timezone - Timezone identifier
|
|
10
|
-
* @param currentDate - Reference date
|
|
11
10
|
*/
|
|
12
11
|
static nextDate(expression: string, timezone?: string, currentDate?: Date): Promise<Date>;
|
|
13
12
|
/**
|
|
14
13
|
* Check if the cron expression is due to run at the current time (minute precision).
|
|
15
|
-
*
|
|
16
|
-
* @param expression - Cron expression
|
|
17
|
-
* @param timezone - Timezone identifier
|
|
18
|
-
* @param currentDate - Reference date
|
|
19
14
|
*/
|
|
20
15
|
static isDue(expression: string, timezone?: string, currentDate?: Date): Promise<boolean>;
|
|
21
16
|
private static minuteMatches;
|
|
22
17
|
}
|
|
23
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Interface for distributed lock storage backends.
|
|
21
|
+
*
|
|
22
|
+
* Provides methods for acquiring, releasing, and managing
|
|
23
|
+
* distributed locks to prevent concurrent task execution.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
* @since 3.0.0
|
|
27
|
+
*/
|
|
24
28
|
interface LockStore {
|
|
25
29
|
/**
|
|
26
30
|
* Attempt to acquire a lock
|
|
@@ -47,6 +51,21 @@ interface LockStore {
|
|
|
47
51
|
exists(key: string): Promise<boolean>;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Cache-based lock store using Gravito Stasis.
|
|
56
|
+
*
|
|
57
|
+
* Stores locks in the cache system for distributed locking
|
|
58
|
+
* across multiple application instances.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const store = new CacheLockStore(cacheManager, 'locks:')
|
|
63
|
+
* const acquired = await store.acquire('task-123', 300)
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @since 3.0.0
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
50
69
|
declare class CacheLockStore implements LockStore {
|
|
51
70
|
private cache;
|
|
52
71
|
private prefix;
|
|
@@ -58,6 +77,34 @@ declare class CacheLockStore implements LockStore {
|
|
|
58
77
|
exists(key: string): Promise<boolean>;
|
|
59
78
|
}
|
|
60
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Manager for distributed locks to prevent concurrent task execution.
|
|
82
|
+
*
|
|
83
|
+
* Supports multiple storage backends (memory, cache, custom) and provides
|
|
84
|
+
* a unified interface for acquiring and releasing locks.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // Using memory store
|
|
89
|
+
* const locks = new LockManager('memory')
|
|
90
|
+
*
|
|
91
|
+
* // Using cache store
|
|
92
|
+
* const locks = new LockManager('cache', { cache: cacheManager })
|
|
93
|
+
*
|
|
94
|
+
* // Acquire a lock
|
|
95
|
+
* const acquired = await locks.acquire('task:send-emails', 300)
|
|
96
|
+
* if (acquired) {
|
|
97
|
+
* try {
|
|
98
|
+
* // Execute task
|
|
99
|
+
* } finally {
|
|
100
|
+
* await locks.release('task:send-emails')
|
|
101
|
+
* }
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @since 3.0.0
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
61
108
|
declare class LockManager {
|
|
62
109
|
private store;
|
|
63
110
|
constructor(driver: 'memory' | 'cache' | LockStore, context?: {
|
|
@@ -69,6 +116,25 @@ declare class LockManager {
|
|
|
69
116
|
exists(key: string): Promise<boolean>;
|
|
70
117
|
}
|
|
71
118
|
|
|
119
|
+
/**
|
|
120
|
+
* In-memory lock store for single-instance deployments.
|
|
121
|
+
*
|
|
122
|
+
* Stores locks in memory with automatic expiration.
|
|
123
|
+
* Not suitable for multi-instance deployments.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const store = new MemoryLockStore()
|
|
128
|
+
* const acquired = await store.acquire('task-123', 300)
|
|
129
|
+
* if (acquired) {
|
|
130
|
+
* // Execute task
|
|
131
|
+
* await store.release('task-123')
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @since 3.0.0
|
|
136
|
+
* @public
|
|
137
|
+
*/
|
|
72
138
|
declare class MemoryLockStore implements LockStore {
|
|
73
139
|
private locks;
|
|
74
140
|
acquire(key: string, ttlSeconds: number): Promise<boolean>;
|
|
@@ -77,47 +143,49 @@ declare class MemoryLockStore implements LockStore {
|
|
|
77
143
|
exists(key: string): Promise<boolean>;
|
|
78
144
|
}
|
|
79
145
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
install(core: PlanetCore): void;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
interface ProcessResult {
|
|
90
|
-
exitCode: number;
|
|
91
|
-
stdout: string;
|
|
92
|
-
stderr: string;
|
|
93
|
-
success: boolean;
|
|
94
|
-
}
|
|
95
|
-
declare function runProcess(command: string): Promise<ProcessResult>;
|
|
96
|
-
declare class Process {
|
|
97
|
-
static run(command: string): Promise<ProcessResult>;
|
|
98
|
-
}
|
|
99
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Represents a configuration for a scheduled task (Cron job).
|
|
148
|
+
*
|
|
149
|
+
* @public
|
|
150
|
+
* @since 3.0.0
|
|
151
|
+
*/
|
|
100
152
|
interface ScheduledTask {
|
|
153
|
+
/** Unique name for the task */
|
|
101
154
|
name: string;
|
|
155
|
+
/** Standard cron expression mapping the frequency */
|
|
102
156
|
expression: string;
|
|
157
|
+
/** Timezone for evaluating the cron expression (default: UTC) */
|
|
103
158
|
timezone: string;
|
|
159
|
+
/** The function or task to execute */
|
|
104
160
|
callback: () => void | Promise<void>;
|
|
161
|
+
/** If true, uses a distributed lock to ensure only one server runs the task */
|
|
105
162
|
shouldRunOnOneServer: boolean;
|
|
163
|
+
/** Time-to-live for the distributed lock in seconds (default: 300) */
|
|
106
164
|
lockTtl: number;
|
|
165
|
+
/** If true, the task runs in the background and doesn't block the scheduler loop */
|
|
107
166
|
background: boolean;
|
|
167
|
+
/** Optional node role required to run this task (e.g., 'worker') */
|
|
108
168
|
nodeRole?: string;
|
|
169
|
+
/** Command string if the task was registered via a shell command */
|
|
109
170
|
command?: string;
|
|
171
|
+
/** Callbacks executed after successful task completion */
|
|
110
172
|
onSuccessCallbacks: ActionCallback[];
|
|
173
|
+
/** Callbacks executed after task failure */
|
|
111
174
|
onFailureCallbacks: ActionCallback[];
|
|
112
175
|
}
|
|
113
176
|
/**
|
|
114
177
|
* Fluent API for defining task schedules.
|
|
115
178
|
*
|
|
116
179
|
* @example
|
|
180
|
+
* ```typescript
|
|
117
181
|
* scheduler.task('backup')
|
|
118
182
|
* .daily()
|
|
119
183
|
* .at('02:00')
|
|
120
|
-
* .onOneServer()
|
|
184
|
+
* .onOneServer();
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* @public
|
|
188
|
+
* @since 3.0.0
|
|
121
189
|
*/
|
|
122
190
|
declare class TaskSchedule {
|
|
123
191
|
private task;
|
|
@@ -363,4 +431,85 @@ declare class SchedulerManager {
|
|
|
363
431
|
private executeTask;
|
|
364
432
|
}
|
|
365
433
|
|
|
434
|
+
/**
|
|
435
|
+
* OrbitHorizon is the official Task Scheduler for Gravito.
|
|
436
|
+
* It provides a fluent API for defining Cron jobs that can run on a schedule,
|
|
437
|
+
* with support for distributed locking (run on one server) and node roles.
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* ```typescript
|
|
441
|
+
* const horizon = new OrbitHorizon();
|
|
442
|
+
* core.addOrbit(horizon);
|
|
443
|
+
*
|
|
444
|
+
* // Defining a task
|
|
445
|
+
* const scheduler = core.container.make('scheduler');
|
|
446
|
+
* scheduler.call('cleanup', async () => { ... }).daily().at('03:00').onOneServer();
|
|
447
|
+
* ```
|
|
448
|
+
* @public
|
|
449
|
+
*/
|
|
450
|
+
declare class OrbitHorizon implements GravitoOrbit {
|
|
451
|
+
/**
|
|
452
|
+
* Install the Horizon Orbit into PlanetCore.
|
|
453
|
+
* Registers the SchedulerManager and configures the distributed lock driver.
|
|
454
|
+
*
|
|
455
|
+
* @param core - The PlanetCore instance.
|
|
456
|
+
*/
|
|
457
|
+
install(core: PlanetCore): void;
|
|
458
|
+
}
|
|
459
|
+
declare module '@gravito/core' {
|
|
460
|
+
interface GravitoVariables {
|
|
461
|
+
/** Scheduler manager for task scheduling */
|
|
462
|
+
scheduler?: SchedulerManager;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Result of a process execution.
|
|
468
|
+
*
|
|
469
|
+
* @public
|
|
470
|
+
* @since 3.0.0
|
|
471
|
+
*/
|
|
472
|
+
interface ProcessResult {
|
|
473
|
+
/** Exit code of the process (0 indicates success). */
|
|
474
|
+
exitCode: number;
|
|
475
|
+
/** Standard output from the process. */
|
|
476
|
+
stdout: string;
|
|
477
|
+
/** Standard error output from the process. */
|
|
478
|
+
stderr: string;
|
|
479
|
+
/** Whether the process completed successfully (exitCode === 0). */
|
|
480
|
+
success: boolean;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Execute a shell command and capture its output.
|
|
484
|
+
*
|
|
485
|
+
* @param command - The command string to execute.
|
|
486
|
+
* @returns A promise that resolves to the process result.
|
|
487
|
+
*
|
|
488
|
+
* @public
|
|
489
|
+
* @since 3.0.0
|
|
490
|
+
*/
|
|
491
|
+
declare function runProcess(command: string): Promise<ProcessResult>;
|
|
492
|
+
/**
|
|
493
|
+
* Process execution utility for running shell commands.
|
|
494
|
+
*
|
|
495
|
+
* Provides a simple interface for executing shell commands
|
|
496
|
+
* and capturing their output.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const result = await Process.run('ls -la')
|
|
501
|
+
* if (result.success) {
|
|
502
|
+
* console.log(result.stdout)
|
|
503
|
+
* } else {
|
|
504
|
+
* console.error(result.stderr)
|
|
505
|
+
* }
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* @since 3.0.0
|
|
509
|
+
* @public
|
|
510
|
+
*/
|
|
511
|
+
declare class Process {
|
|
512
|
+
static run(command: string): Promise<ProcessResult>;
|
|
513
|
+
}
|
|
514
|
+
|
|
366
515
|
export { CacheLockStore, CronParser, LockManager, type LockStore, MemoryLockStore, OrbitHorizon, Process, type ProcessResult, type ScheduledTask, SchedulerManager, TaskSchedule, runProcess };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { CacheManager } from '@gravito/stasis';
|
|
2
|
-
import {
|
|
2
|
+
import { ActionCallback, Logger, HookManager, GravitoOrbit, PlanetCore } from '@gravito/core';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Advanced cron expression parser with fallback support.
|
|
6
|
+
*/
|
|
4
7
|
declare class CronParser {
|
|
5
8
|
/**
|
|
6
9
|
* Get the next execution date based on a cron expression.
|
|
7
|
-
*
|
|
8
|
-
* @param expression - Cron expression
|
|
9
|
-
* @param timezone - Timezone identifier
|
|
10
|
-
* @param currentDate - Reference date
|
|
11
10
|
*/
|
|
12
11
|
static nextDate(expression: string, timezone?: string, currentDate?: Date): Promise<Date>;
|
|
13
12
|
/**
|
|
14
13
|
* Check if the cron expression is due to run at the current time (minute precision).
|
|
15
|
-
*
|
|
16
|
-
* @param expression - Cron expression
|
|
17
|
-
* @param timezone - Timezone identifier
|
|
18
|
-
* @param currentDate - Reference date
|
|
19
14
|
*/
|
|
20
15
|
static isDue(expression: string, timezone?: string, currentDate?: Date): Promise<boolean>;
|
|
21
16
|
private static minuteMatches;
|
|
22
17
|
}
|
|
23
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Interface for distributed lock storage backends.
|
|
21
|
+
*
|
|
22
|
+
* Provides methods for acquiring, releasing, and managing
|
|
23
|
+
* distributed locks to prevent concurrent task execution.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
* @since 3.0.0
|
|
27
|
+
*/
|
|
24
28
|
interface LockStore {
|
|
25
29
|
/**
|
|
26
30
|
* Attempt to acquire a lock
|
|
@@ -47,6 +51,21 @@ interface LockStore {
|
|
|
47
51
|
exists(key: string): Promise<boolean>;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Cache-based lock store using Gravito Stasis.
|
|
56
|
+
*
|
|
57
|
+
* Stores locks in the cache system for distributed locking
|
|
58
|
+
* across multiple application instances.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const store = new CacheLockStore(cacheManager, 'locks:')
|
|
63
|
+
* const acquired = await store.acquire('task-123', 300)
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @since 3.0.0
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
50
69
|
declare class CacheLockStore implements LockStore {
|
|
51
70
|
private cache;
|
|
52
71
|
private prefix;
|
|
@@ -58,6 +77,34 @@ declare class CacheLockStore implements LockStore {
|
|
|
58
77
|
exists(key: string): Promise<boolean>;
|
|
59
78
|
}
|
|
60
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Manager for distributed locks to prevent concurrent task execution.
|
|
82
|
+
*
|
|
83
|
+
* Supports multiple storage backends (memory, cache, custom) and provides
|
|
84
|
+
* a unified interface for acquiring and releasing locks.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // Using memory store
|
|
89
|
+
* const locks = new LockManager('memory')
|
|
90
|
+
*
|
|
91
|
+
* // Using cache store
|
|
92
|
+
* const locks = new LockManager('cache', { cache: cacheManager })
|
|
93
|
+
*
|
|
94
|
+
* // Acquire a lock
|
|
95
|
+
* const acquired = await locks.acquire('task:send-emails', 300)
|
|
96
|
+
* if (acquired) {
|
|
97
|
+
* try {
|
|
98
|
+
* // Execute task
|
|
99
|
+
* } finally {
|
|
100
|
+
* await locks.release('task:send-emails')
|
|
101
|
+
* }
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @since 3.0.0
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
61
108
|
declare class LockManager {
|
|
62
109
|
private store;
|
|
63
110
|
constructor(driver: 'memory' | 'cache' | LockStore, context?: {
|
|
@@ -69,6 +116,25 @@ declare class LockManager {
|
|
|
69
116
|
exists(key: string): Promise<boolean>;
|
|
70
117
|
}
|
|
71
118
|
|
|
119
|
+
/**
|
|
120
|
+
* In-memory lock store for single-instance deployments.
|
|
121
|
+
*
|
|
122
|
+
* Stores locks in memory with automatic expiration.
|
|
123
|
+
* Not suitable for multi-instance deployments.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const store = new MemoryLockStore()
|
|
128
|
+
* const acquired = await store.acquire('task-123', 300)
|
|
129
|
+
* if (acquired) {
|
|
130
|
+
* // Execute task
|
|
131
|
+
* await store.release('task-123')
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @since 3.0.0
|
|
136
|
+
* @public
|
|
137
|
+
*/
|
|
72
138
|
declare class MemoryLockStore implements LockStore {
|
|
73
139
|
private locks;
|
|
74
140
|
acquire(key: string, ttlSeconds: number): Promise<boolean>;
|
|
@@ -77,47 +143,49 @@ declare class MemoryLockStore implements LockStore {
|
|
|
77
143
|
exists(key: string): Promise<boolean>;
|
|
78
144
|
}
|
|
79
145
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
install(core: PlanetCore): void;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
interface ProcessResult {
|
|
90
|
-
exitCode: number;
|
|
91
|
-
stdout: string;
|
|
92
|
-
stderr: string;
|
|
93
|
-
success: boolean;
|
|
94
|
-
}
|
|
95
|
-
declare function runProcess(command: string): Promise<ProcessResult>;
|
|
96
|
-
declare class Process {
|
|
97
|
-
static run(command: string): Promise<ProcessResult>;
|
|
98
|
-
}
|
|
99
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Represents a configuration for a scheduled task (Cron job).
|
|
148
|
+
*
|
|
149
|
+
* @public
|
|
150
|
+
* @since 3.0.0
|
|
151
|
+
*/
|
|
100
152
|
interface ScheduledTask {
|
|
153
|
+
/** Unique name for the task */
|
|
101
154
|
name: string;
|
|
155
|
+
/** Standard cron expression mapping the frequency */
|
|
102
156
|
expression: string;
|
|
157
|
+
/** Timezone for evaluating the cron expression (default: UTC) */
|
|
103
158
|
timezone: string;
|
|
159
|
+
/** The function or task to execute */
|
|
104
160
|
callback: () => void | Promise<void>;
|
|
161
|
+
/** If true, uses a distributed lock to ensure only one server runs the task */
|
|
105
162
|
shouldRunOnOneServer: boolean;
|
|
163
|
+
/** Time-to-live for the distributed lock in seconds (default: 300) */
|
|
106
164
|
lockTtl: number;
|
|
165
|
+
/** If true, the task runs in the background and doesn't block the scheduler loop */
|
|
107
166
|
background: boolean;
|
|
167
|
+
/** Optional node role required to run this task (e.g., 'worker') */
|
|
108
168
|
nodeRole?: string;
|
|
169
|
+
/** Command string if the task was registered via a shell command */
|
|
109
170
|
command?: string;
|
|
171
|
+
/** Callbacks executed after successful task completion */
|
|
110
172
|
onSuccessCallbacks: ActionCallback[];
|
|
173
|
+
/** Callbacks executed after task failure */
|
|
111
174
|
onFailureCallbacks: ActionCallback[];
|
|
112
175
|
}
|
|
113
176
|
/**
|
|
114
177
|
* Fluent API for defining task schedules.
|
|
115
178
|
*
|
|
116
179
|
* @example
|
|
180
|
+
* ```typescript
|
|
117
181
|
* scheduler.task('backup')
|
|
118
182
|
* .daily()
|
|
119
183
|
* .at('02:00')
|
|
120
|
-
* .onOneServer()
|
|
184
|
+
* .onOneServer();
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* @public
|
|
188
|
+
* @since 3.0.0
|
|
121
189
|
*/
|
|
122
190
|
declare class TaskSchedule {
|
|
123
191
|
private task;
|
|
@@ -363,4 +431,85 @@ declare class SchedulerManager {
|
|
|
363
431
|
private executeTask;
|
|
364
432
|
}
|
|
365
433
|
|
|
434
|
+
/**
|
|
435
|
+
* OrbitHorizon is the official Task Scheduler for Gravito.
|
|
436
|
+
* It provides a fluent API for defining Cron jobs that can run on a schedule,
|
|
437
|
+
* with support for distributed locking (run on one server) and node roles.
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* ```typescript
|
|
441
|
+
* const horizon = new OrbitHorizon();
|
|
442
|
+
* core.addOrbit(horizon);
|
|
443
|
+
*
|
|
444
|
+
* // Defining a task
|
|
445
|
+
* const scheduler = core.container.make('scheduler');
|
|
446
|
+
* scheduler.call('cleanup', async () => { ... }).daily().at('03:00').onOneServer();
|
|
447
|
+
* ```
|
|
448
|
+
* @public
|
|
449
|
+
*/
|
|
450
|
+
declare class OrbitHorizon implements GravitoOrbit {
|
|
451
|
+
/**
|
|
452
|
+
* Install the Horizon Orbit into PlanetCore.
|
|
453
|
+
* Registers the SchedulerManager and configures the distributed lock driver.
|
|
454
|
+
*
|
|
455
|
+
* @param core - The PlanetCore instance.
|
|
456
|
+
*/
|
|
457
|
+
install(core: PlanetCore): void;
|
|
458
|
+
}
|
|
459
|
+
declare module '@gravito/core' {
|
|
460
|
+
interface GravitoVariables {
|
|
461
|
+
/** Scheduler manager for task scheduling */
|
|
462
|
+
scheduler?: SchedulerManager;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Result of a process execution.
|
|
468
|
+
*
|
|
469
|
+
* @public
|
|
470
|
+
* @since 3.0.0
|
|
471
|
+
*/
|
|
472
|
+
interface ProcessResult {
|
|
473
|
+
/** Exit code of the process (0 indicates success). */
|
|
474
|
+
exitCode: number;
|
|
475
|
+
/** Standard output from the process. */
|
|
476
|
+
stdout: string;
|
|
477
|
+
/** Standard error output from the process. */
|
|
478
|
+
stderr: string;
|
|
479
|
+
/** Whether the process completed successfully (exitCode === 0). */
|
|
480
|
+
success: boolean;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Execute a shell command and capture its output.
|
|
484
|
+
*
|
|
485
|
+
* @param command - The command string to execute.
|
|
486
|
+
* @returns A promise that resolves to the process result.
|
|
487
|
+
*
|
|
488
|
+
* @public
|
|
489
|
+
* @since 3.0.0
|
|
490
|
+
*/
|
|
491
|
+
declare function runProcess(command: string): Promise<ProcessResult>;
|
|
492
|
+
/**
|
|
493
|
+
* Process execution utility for running shell commands.
|
|
494
|
+
*
|
|
495
|
+
* Provides a simple interface for executing shell commands
|
|
496
|
+
* and capturing their output.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const result = await Process.run('ls -la')
|
|
501
|
+
* if (result.success) {
|
|
502
|
+
* console.log(result.stdout)
|
|
503
|
+
* } else {
|
|
504
|
+
* console.error(result.stderr)
|
|
505
|
+
* }
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* @since 3.0.0
|
|
509
|
+
* @public
|
|
510
|
+
*/
|
|
511
|
+
declare class Process {
|
|
512
|
+
static run(command: string): Promise<ProcessResult>;
|
|
513
|
+
}
|
|
514
|
+
|
|
366
515
|
export { CacheLockStore, CronParser, LockManager, type LockStore, MemoryLockStore, OrbitHorizon, Process, type ProcessResult, type ScheduledTask, SchedulerManager, TaskSchedule, runProcess };
|
package/dist/index.js
CHANGED
|
@@ -1,128 +1,59 @@
|
|
|
1
1
|
import "./chunk-MCKGQKYU.js";
|
|
2
2
|
|
|
3
3
|
// src/SimpleCronParser.ts
|
|
4
|
-
var SimpleCronParser = {
|
|
5
|
-
/**
|
|
6
|
-
* Check if a cron expression
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* - numbers (0-59, 1-31, 0-11, 0-7)
|
|
13
|
-
* - ranges (1-5)
|
|
14
|
-
* - lists (1,2,3)
|
|
15
|
-
* - steps (*\/5, 1-10/2)
|
|
16
|
-
*/
|
|
17
|
-
isDue(expression, timezone, date) {
|
|
18
|
-
const fields = expression.trim().split(/\s+/);
|
|
19
|
-
if (fields.length !== 5) {
|
|
20
|
-
throw new Error("SimpleCronParser only supports 5-field cron expressions");
|
|
4
|
+
var SimpleCronParser = class {
|
|
5
|
+
/**
|
|
6
|
+
* Check if a cron expression is due at the given date/time
|
|
7
|
+
*/
|
|
8
|
+
static isDue(expression, timezone = "UTC", date = /* @__PURE__ */ new Date()) {
|
|
9
|
+
const parts = expression.trim().split(/\s+/);
|
|
10
|
+
if (parts.length !== 5) {
|
|
11
|
+
throw new Error(`Invalid cron expression: ${expression}`);
|
|
21
12
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}).formatToParts(date);
|
|
35
|
-
const partMap = {};
|
|
36
|
-
parts.forEach((p) => {
|
|
37
|
-
partMap[p.type] = p.value;
|
|
38
|
-
});
|
|
39
|
-
targetDate = new Date(
|
|
40
|
-
parseInt(partMap.year ?? "0", 10),
|
|
41
|
-
parseInt(partMap.month ?? "1", 10) - 1,
|
|
42
|
-
parseInt(partMap.day ?? "1", 10),
|
|
43
|
-
parseInt(partMap.hour ?? "0", 10) === 24 ? 0 : parseInt(partMap.hour ?? "0", 10),
|
|
44
|
-
parseInt(partMap.minute ?? "0", 10),
|
|
45
|
-
0
|
|
46
|
-
);
|
|
47
|
-
} catch (_e) {
|
|
48
|
-
throw new Error(`Invalid timezone: ${timezone}`);
|
|
49
|
-
}
|
|
50
|
-
} else if (timezone === "UTC") {
|
|
51
|
-
targetDate = new Date(
|
|
52
|
-
date.getUTCFullYear(),
|
|
53
|
-
date.getUTCMonth(),
|
|
54
|
-
date.getUTCDate(),
|
|
55
|
-
date.getUTCHours(),
|
|
56
|
-
date.getUTCMinutes(),
|
|
57
|
-
0
|
|
58
|
-
);
|
|
13
|
+
const targetDate = this.getDateInTimezone(date, timezone);
|
|
14
|
+
const minutes = targetDate.getMinutes();
|
|
15
|
+
const hours = targetDate.getHours();
|
|
16
|
+
const dayOfMonth = targetDate.getDate();
|
|
17
|
+
const month = targetDate.getMonth() + 1;
|
|
18
|
+
const dayOfWeek = targetDate.getDay();
|
|
19
|
+
return this.match(parts[0], minutes, 0, 59) && this.match(parts[1], hours, 0, 23) && this.match(parts[2], dayOfMonth, 1, 31) && this.match(parts[3], month, 1, 12) && this.match(parts[4], dayOfWeek, 0, 6, true);
|
|
20
|
+
}
|
|
21
|
+
static match(pattern, value, _min, _max, isDayOfWeek = false) {
|
|
22
|
+
if (pattern === "*") return true;
|
|
23
|
+
if (pattern.includes(",")) {
|
|
24
|
+
return pattern.split(",").some((p) => this.match(p, value, _min, _max, isDayOfWeek));
|
|
59
25
|
}
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
|
|
26
|
+
const stepMatch = pattern.match(/^(\*|\d+(-\d+)?)\/(\d+)$/);
|
|
27
|
+
if (stepMatch) {
|
|
28
|
+
const range = stepMatch[1];
|
|
29
|
+
const step = parseInt(stepMatch[3], 10);
|
|
30
|
+
if (range === "*") return value % step === 0;
|
|
31
|
+
const [rMin, rMax] = range.split("-").map((n) => parseInt(n, 10));
|
|
32
|
+
return value >= rMin && value <= rMax && (value - rMin) % step === 0;
|
|
63
33
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const matchMonth = matchField(month, targetDate.getMonth() + 1, 1, 12);
|
|
68
|
-
const matchDayOfWeek = matchField(dayOfWeek, targetDate.getDay(), 0, 7);
|
|
69
|
-
return matchMinute && matchHour && matchDayOfMonth && matchMonth && matchDayOfWeek;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
function matchField(pattern, value, min, max) {
|
|
73
|
-
if (pattern === "*") {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
if (pattern.includes("/")) {
|
|
77
|
-
const [range, stepStr] = pattern.split("/");
|
|
78
|
-
if (range === void 0 || stepStr === void 0) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
const step = parseInt(stepStr, 10);
|
|
82
|
-
if (range === "*") {
|
|
83
|
-
return (value - min) % step === 0;
|
|
84
|
-
}
|
|
85
|
-
if (range.includes("-")) {
|
|
86
|
-
const [startStr, endStr] = range.split("-");
|
|
87
|
-
if (startStr === void 0 || endStr === void 0) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
const start = parseInt(startStr, 10);
|
|
91
|
-
const end = parseInt(endStr, 10);
|
|
92
|
-
if (value >= start && value <= end) {
|
|
93
|
-
return (value - start) % step === 0;
|
|
94
|
-
}
|
|
95
|
-
return false;
|
|
34
|
+
if (pattern.includes("-")) {
|
|
35
|
+
const [rMin, rMax] = pattern.split("-").map((n) => parseInt(n, 10));
|
|
36
|
+
return value >= rMin && value <= rMax;
|
|
96
37
|
}
|
|
38
|
+
const patternVal = parseInt(pattern, 10);
|
|
39
|
+
if (isDayOfWeek && patternVal === 7 && value === 0) return true;
|
|
40
|
+
return patternVal === value;
|
|
97
41
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return false;
|
|
42
|
+
static getDateInTimezone(date, timezone) {
|
|
43
|
+
try {
|
|
44
|
+
const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
|
|
45
|
+
if (isNaN(tzDate.getTime())) throw new Error();
|
|
46
|
+
return tzDate;
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error(`Invalid timezone: ${timezone}`);
|
|
106
49
|
}
|
|
107
|
-
const start = parseInt(startStr, 10);
|
|
108
|
-
const end = parseInt(endStr, 10);
|
|
109
|
-
return value >= start && value <= end;
|
|
110
50
|
}
|
|
111
|
-
|
|
112
|
-
if (max === 7 && expected === 7 && value === 0) {
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
return value === expected;
|
|
116
|
-
}
|
|
51
|
+
};
|
|
117
52
|
|
|
118
53
|
// src/CronParser.ts
|
|
119
54
|
var CronParser = class {
|
|
120
55
|
/**
|
|
121
56
|
* Get the next execution date based on a cron expression.
|
|
122
|
-
*
|
|
123
|
-
* @param expression - Cron expression
|
|
124
|
-
* @param timezone - Timezone identifier
|
|
125
|
-
* @param currentDate - Reference date
|
|
126
57
|
*/
|
|
127
58
|
static async nextDate(expression, timezone = "UTC", currentDate = /* @__PURE__ */ new Date()) {
|
|
128
59
|
try {
|
|
@@ -138,10 +69,6 @@ var CronParser = class {
|
|
|
138
69
|
}
|
|
139
70
|
/**
|
|
140
71
|
* Check if the cron expression is due to run at the current time (minute precision).
|
|
141
|
-
*
|
|
142
|
-
* @param expression - Cron expression
|
|
143
|
-
* @param timezone - Timezone identifier
|
|
144
|
-
* @param currentDate - Reference date
|
|
145
72
|
*/
|
|
146
73
|
static async isDue(expression, timezone = "UTC", currentDate = /* @__PURE__ */ new Date()) {
|
|
147
74
|
try {
|
|
@@ -703,6 +630,7 @@ var SchedulerManager = class {
|
|
|
703
630
|
var OrbitHorizon = class {
|
|
704
631
|
/**
|
|
705
632
|
* Install the Horizon Orbit into PlanetCore.
|
|
633
|
+
* Registers the SchedulerManager and configures the distributed lock driver.
|
|
706
634
|
*
|
|
707
635
|
* @param core - The PlanetCore instance.
|
|
708
636
|
*/
|
|
@@ -713,7 +641,7 @@ var OrbitHorizon = class {
|
|
|
713
641
|
const nodeRole = config.nodeRole;
|
|
714
642
|
let lockManager;
|
|
715
643
|
if (lockDriver === "cache") {
|
|
716
|
-
const cacheManager = core.
|
|
644
|
+
const cacheManager = core.container.make("cache");
|
|
717
645
|
if (!cacheManager) {
|
|
718
646
|
core.logger.warn(
|
|
719
647
|
"[OrbitHorizon] Cache driver requested but cache service not found (ensure orbit-cache is loaded first). Falling back to Memory lock."
|
|
@@ -726,11 +654,10 @@ var OrbitHorizon = class {
|
|
|
726
654
|
lockManager = new LockManager("memory");
|
|
727
655
|
}
|
|
728
656
|
const scheduler = new SchedulerManager(lockManager, core.logger, core.hooks, nodeRole);
|
|
729
|
-
core.
|
|
657
|
+
core.container.instance(exposeAs, scheduler);
|
|
730
658
|
core.adapter.use("*", async (c, next) => {
|
|
731
659
|
c.set("scheduler", scheduler);
|
|
732
|
-
await next();
|
|
733
|
-
return void 0;
|
|
660
|
+
return await next();
|
|
734
661
|
});
|
|
735
662
|
core.logger.info(`[OrbitHorizon] Initialized (Driver: ${lockDriver})`);
|
|
736
663
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravito/horizon",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Distributed task scheduler for Gravito framework",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"test": "bun test",
|
|
24
24
|
"lint": "biome lint ./src",
|
|
25
25
|
"typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
|
|
26
|
-
"test:coverage": "bun test --coverage --coverage-threshold=
|
|
27
|
-
"test:ci": "bun test --coverage --coverage-threshold=
|
|
26
|
+
"test:coverage": "bun test --coverage --coverage-threshold=60",
|
|
27
|
+
"test:ci": "bun test --coverage --coverage-threshold=60"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"gravito",
|