@gravito/horizon 3.0.0 → 3.2.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.
package/dist/index.d.ts CHANGED
@@ -1,309 +1,664 @@
1
1
  import { CacheManager } from '@gravito/stasis';
2
- import { GravitoOrbit, PlanetCore, ActionCallback, Logger, HookManager } from '@gravito/core';
2
+ import { ActionCallback, Logger, HookManager, GravitoOrbit, PlanetCore } from '@gravito/core';
3
3
 
4
+ /**
5
+ * High-performance cron expression evaluator with LRU caching and fallback support.
6
+ *
7
+ * Employs a tiered strategy for cron evaluation:
8
+ * 1. Cache: O(1) lookup for high-frequency checks.
9
+ * 2. Simple Parser: Lightweight evaluator for standard expressions.
10
+ * 3. Advanced Parser: Dynamic import of `cron-parser` for complex expressions.
11
+ *
12
+ * @internal
13
+ */
4
14
  declare class CronParser {
15
+ private static cache;
16
+ /** Cache duration in milliseconds (1 minute). */
17
+ private static readonly CACHE_TTL;
18
+ /** Maximum number of unique expression/timezone combinations to store. */
19
+ private static readonly MAX_CACHE_SIZE;
5
20
  /**
6
- * Get the next execution date based on a cron expression.
21
+ * Calculates the next occurrence of a cron expression.
7
22
  *
8
- * @param expression - Cron expression
9
- * @param timezone - Timezone identifier
10
- * @param currentDate - Reference date
23
+ * Dynamically loads `cron-parser` to minimize initial bundle size and memory footprint.
24
+ *
25
+ * @param expression - Valid 5-part cron expression.
26
+ * @param timezone - Target timezone for evaluation (default: "UTC").
27
+ * @param currentDate - Reference time to calculate from (default: now).
28
+ * @returns Resolves to the next execution Date object.
29
+ * @throws {Error} If the expression format is invalid.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const next = await CronParser.nextDate('0 0 * * *');
34
+ * ```
11
35
  */
12
36
  static nextDate(expression: string, timezone?: string, currentDate?: Date): Promise<Date>;
13
37
  /**
14
- * Check if the cron expression is due to run at the current time (minute precision).
38
+ * Determines if a task is due for execution at the specified time.
15
39
  *
16
- * @param expression - Cron expression
17
- * @param timezone - Timezone identifier
18
- * @param currentDate - Reference date
40
+ * Uses minute-precision caching to optimize repeated evaluations within
41
+ * the same scheduling window. Implements LRU eviction to maintain memory efficiency.
42
+ *
43
+ * @param expression - Cron expression to evaluate.
44
+ * @param timezone - Execution timezone.
45
+ * @param currentDate - Reference time for the check.
46
+ * @returns True if the expression matches the reference time.
19
47
  */
20
48
  static isDue(expression: string, timezone?: string, currentDate?: Date): Promise<boolean>;
49
+ /**
50
+ * Internal logic for tiered cron evaluation.
51
+ *
52
+ * @param expression - Cron expression.
53
+ * @param timezone - Target timezone.
54
+ * @param currentDate - Current time.
55
+ * @returns Boolean indicating if the task is due.
56
+ *
57
+ * @internal
58
+ */
59
+ private static computeIsDue;
60
+ /**
61
+ * Evicts the oldest entry from the cache when capacity is reached.
62
+ *
63
+ * @internal
64
+ */
65
+ private static cleanupCache;
66
+ /**
67
+ * Purges all entries from the internal cache.
68
+ * Useful for testing or when global timezone settings change.
69
+ */
70
+ static clearCache(): void;
71
+ /**
72
+ * Compares two dates with minute precision.
73
+ *
74
+ * @internal
75
+ */
21
76
  private static minuteMatches;
22
77
  }
23
78
 
79
+ /**
80
+ * Contract for distributed lock storage backends.
81
+ *
82
+ * Defines the essential operations required to manage mutual exclusion across
83
+ * multiple scheduler instances. Implementations must ensure atomicity for
84
+ * distributed safety.
85
+ *
86
+ * @public
87
+ * @since 3.0.0
88
+ */
24
89
  interface LockStore {
25
90
  /**
26
- * Attempt to acquire a lock
27
- * @param key The lock key
28
- * @param ttlSeconds Time to live in seconds
29
- * @returns true if lock acquired, false if lock already exists
91
+ * Attempts to acquire a mutex lock for a specific key.
92
+ *
93
+ * Must be atomic. If the key is already locked, it should return false
94
+ * immediately without blocking.
95
+ *
96
+ * @param key - The unique lock identifier.
97
+ * @param ttlSeconds - Expiration duration in seconds to prevent deadlocks.
98
+ * @returns True if the lock was successfully acquired.
30
99
  */
31
100
  acquire(key: string, ttlSeconds: number): Promise<boolean>;
32
101
  /**
33
- * Release a lock
34
- * @param key The lock key
102
+ * Explicitly releases a held lock.
103
+ *
104
+ * Should be idempotent; releasing a non-existent lock should not throw.
105
+ *
106
+ * @param key - The lock identifier to remove.
35
107
  */
36
108
  release(key: string): Promise<void>;
37
109
  /**
38
- * Force acquire a lock (overwrite existing)
39
- * @param key The lock key
40
- * @param ttlSeconds Time to live in seconds
110
+ * Forcibly acquires or refreshes a lock, overwriting any existing state.
111
+ *
112
+ * @param key - The lock identifier.
113
+ * @param ttlSeconds - New expiration duration.
41
114
  */
42
115
  forceAcquire(key: string, ttlSeconds: number): Promise<void>;
43
116
  /**
44
- * Check if a lock exists
45
- * @param key The lock key
117
+ * Checks if a lock is currently active and not expired.
118
+ *
119
+ * @param key - The lock identifier.
120
+ * @returns True if the lock exists.
46
121
  */
47
122
  exists(key: string): Promise<boolean>;
48
123
  }
49
124
 
125
+ /**
126
+ * Distributed lock implementation backed by Gravito Stasis.
127
+ *
128
+ * Leverages the shared cache system (Redis, Memcached, etc.) to provide
129
+ * atomic locking across multiple application nodes.
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const store = new CacheLockStore(cacheManager);
134
+ * const locked = await store.acquire('nightly-sync', 600);
135
+ * ```
136
+ *
137
+ * @since 3.0.0
138
+ * @public
139
+ */
50
140
  declare class CacheLockStore implements LockStore {
51
141
  private cache;
52
142
  private prefix;
143
+ /**
144
+ * Initializes the store with a cache manager.
145
+ *
146
+ * @param cache - The Stasis cache instance.
147
+ * @param prefix - Key prefix to avoid collisions in the shared namespace.
148
+ */
53
149
  constructor(cache: CacheManager, prefix?: string);
150
+ /**
151
+ * Computes the fully qualified cache key.
152
+ *
153
+ * @internal
154
+ */
54
155
  private getKey;
156
+ /**
157
+ * Atomic 'add' operation ensures only one node succeeds.
158
+ *
159
+ * @param key - Lock key.
160
+ * @param ttlSeconds - Expiration.
161
+ */
55
162
  acquire(key: string, ttlSeconds: number): Promise<boolean>;
163
+ /**
164
+ * Removes the lock key from cache.
165
+ *
166
+ * @param key - Lock key.
167
+ */
56
168
  release(key: string): Promise<void>;
169
+ /**
170
+ * Overwrites the lock key, effectively resetting the TTL.
171
+ *
172
+ * @param key - Lock key.
173
+ * @param ttlSeconds - New expiration.
174
+ */
57
175
  forceAcquire(key: string, ttlSeconds: number): Promise<void>;
176
+ /**
177
+ * Validates if the lock key is present in the cache.
178
+ *
179
+ * @param key - Lock key.
180
+ */
58
181
  exists(key: string): Promise<boolean>;
59
182
  }
60
183
 
184
+ /**
185
+ * Orchestrator for distributed locks to prevent concurrent task execution.
186
+ *
187
+ * Implements a pluggable storage pattern, allowing the scheduler to operate
188
+ * in both single-node (Memory) and multi-node (Cache/Redis) environments.
189
+ * Provides a unified interface for safe lock acquisition and release.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * // Production setup with distributed cache
194
+ * const locks = new LockManager('cache', { cache: cacheManager });
195
+ *
196
+ * // Attempt to acquire a mutex lock for 5 minutes
197
+ * const acquired = await locks.acquire('task:db-backup', 300);
198
+ * ```
199
+ *
200
+ * @since 3.0.0
201
+ * @public
202
+ */
61
203
  declare class LockManager {
62
204
  private store;
205
+ /**
206
+ * Initializes the manager with a specific storage driver.
207
+ *
208
+ * @param driver - Strategy identifier or a custom `LockStore` implementation.
209
+ * @param context - Dependencies required by certain drivers (e.g., CacheManager).
210
+ * @throws {Error} If 'cache' driver is selected but no `CacheManager` is provided.
211
+ */
63
212
  constructor(driver: 'memory' | 'cache' | LockStore, context?: {
64
213
  cache?: CacheManager;
65
214
  });
215
+ /**
216
+ * Attempts to acquire a lock. Fails if the key is already locked.
217
+ *
218
+ * @param key - Unique identifier for the lock.
219
+ * @param ttlSeconds - Time-to-live in seconds before the lock expires.
220
+ * @returns True if the lock was successfully acquired.
221
+ */
66
222
  acquire(key: string, ttlSeconds: number): Promise<boolean>;
223
+ /**
224
+ * Explicitly releases a held lock.
225
+ *
226
+ * @param key - The lock identifier to remove.
227
+ * @returns Resolves when the lock is deleted.
228
+ */
67
229
  release(key: string): Promise<void>;
230
+ /**
231
+ * Forcibly acquires or overwrites an existing lock.
232
+ *
233
+ * Used for execution locks where the latest attempt should take precedence
234
+ * if the previous one is deemed expired.
235
+ *
236
+ * @param key - Lock identifier.
237
+ * @param ttlSeconds - Expiration duration.
238
+ */
68
239
  forceAcquire(key: string, ttlSeconds: number): Promise<void>;
240
+ /**
241
+ * Checks if a specific lock currently exists and is not expired.
242
+ *
243
+ * @param key - Lock identifier.
244
+ * @returns True if the key is locked and active.
245
+ */
69
246
  exists(key: string): Promise<boolean>;
70
247
  }
71
248
 
249
+ /**
250
+ * Lightweight, in-memory lock store for local development and single-node deployments.
251
+ *
252
+ * Implements the `LockStore` contract using a local `Map`. It does not support
253
+ * shared state across processes or servers.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * const store = new MemoryLockStore();
258
+ * const ok = await store.acquire('local-job', 60);
259
+ * ```
260
+ *
261
+ * @since 3.0.0
262
+ * @public
263
+ */
72
264
  declare class MemoryLockStore implements LockStore {
265
+ /** Map of lock keys to their expiration timestamps (ms). */
73
266
  private locks;
267
+ /**
268
+ * Acquires a local lock if the key is not already active.
269
+ *
270
+ * @param key - Lock identifier.
271
+ * @param ttlSeconds - Expiration duration.
272
+ */
74
273
  acquire(key: string, ttlSeconds: number): Promise<boolean>;
274
+ /**
275
+ * Deletes the lock from local memory.
276
+ *
277
+ * @param key - Lock identifier.
278
+ */
75
279
  release(key: string): Promise<void>;
280
+ /**
281
+ * Sets or overwrites a local lock.
282
+ *
283
+ * @param key - Lock identifier.
284
+ * @param ttlSeconds - Expiration.
285
+ */
76
286
  forceAcquire(key: string, ttlSeconds: number): Promise<void>;
77
- exists(key: string): Promise<boolean>;
78
- }
79
-
80
- declare class OrbitHorizon implements GravitoOrbit {
81
287
  /**
82
- * Install the Horizon Orbit into PlanetCore.
288
+ * Checks if a local lock is present and hasn't expired.
83
289
  *
84
- * @param core - The PlanetCore instance.
290
+ * Automatically purges expired locks upon checking.
291
+ *
292
+ * @param key - Lock identifier.
85
293
  */
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>;
294
+ exists(key: string): Promise<boolean>;
98
295
  }
99
296
 
297
+ /**
298
+ * Represents the configuration and state of a scheduled task.
299
+ *
300
+ * Encapsulates all metadata required by the scheduler to evaluate frequency,
301
+ * manage distributed locking, and execute task logic with reliability controls.
302
+ *
303
+ * @public
304
+ * @since 3.0.0
305
+ */
100
306
  interface ScheduledTask {
307
+ /**
308
+ * Unique identifier for the task.
309
+ * Used for logging, hook identification, and as part of the lock key.
310
+ */
101
311
  name: string;
312
+ /**
313
+ * Standard 5-part cron expression defining execution frequency.
314
+ * Format: "minute hour day month weekday"
315
+ */
102
316
  expression: string;
317
+ /**
318
+ * Timezone identifier for evaluating the cron expression.
319
+ * @defaultValue "UTC"
320
+ */
103
321
  timezone: string;
322
+ /**
323
+ * The asynchronous task logic to be executed.
324
+ */
104
325
  callback: () => void | Promise<void>;
326
+ /**
327
+ * Whether to ensure single-point execution across a distributed environment.
328
+ * When true, uses a time-window lock to prevent multiple servers from running
329
+ * the same task in the same scheduling window.
330
+ */
105
331
  shouldRunOnOneServer: boolean;
332
+ /**
333
+ * Duration in seconds to hold the distributed time-window lock.
334
+ * @defaultValue 300
335
+ */
106
336
  lockTtl: number;
337
+ /**
338
+ * Whether to execute the task in a non-blocking background mode.
339
+ * If true, the scheduler won't wait for completion before processing the next task.
340
+ */
107
341
  background: boolean;
342
+ /**
343
+ * Optional target node role required for execution.
344
+ * Tasks will only run if the current node matches this role.
345
+ */
108
346
  nodeRole?: string;
347
+ /**
348
+ * Raw shell command if the task was created via `scheduler.exec()`.
349
+ */
109
350
  command?: string;
351
+ /**
352
+ * Maximum execution time in milliseconds before the task is aborted.
353
+ * @defaultValue 3600000
354
+ */
355
+ timeout?: number;
356
+ /**
357
+ * Number of times to retry a failed task.
358
+ * @defaultValue 0
359
+ */
360
+ retries?: number;
361
+ /**
362
+ * Delay between retry attempts in milliseconds.
363
+ * @defaultValue 1000
364
+ */
365
+ retryDelay?: number;
366
+ /**
367
+ * Whether to prevent concurrent executions of the same task.
368
+ * If true, a new instance won't start if the previous one is still running.
369
+ */
370
+ preventOverlapping: boolean;
371
+ /**
372
+ * Maximum duration in seconds for the execution lock.
373
+ * Prevents deadlocks if a task crashes without releasing the lock.
374
+ * @defaultValue 3600
375
+ */
376
+ overlappingExpiresAt: number;
377
+ /**
378
+ * Collection of callbacks executed upon successful task completion.
379
+ */
110
380
  onSuccessCallbacks: ActionCallback[];
381
+ /**
382
+ * Collection of callbacks executed when the task fails after all retries.
383
+ */
111
384
  onFailureCallbacks: ActionCallback[];
112
385
  }
113
386
  /**
114
- * Fluent API for defining task schedules.
387
+ * Fluent builder for defining and configuring scheduled tasks.
388
+ *
389
+ * Provides a human-readable API for setting execution frequency, constraints,
390
+ * reliability settings, and lifecycle hooks.
115
391
  *
116
392
  * @example
117
- * scheduler.task('backup')
118
- * .daily()
119
- * .at('02:00')
120
- * .onOneServer()
393
+ * ```typescript
394
+ * scheduler.task('backup-db', async () => {
395
+ * await db.backup();
396
+ * })
397
+ * .dailyAt('03:00')
398
+ * .onOneServer()
399
+ * .withoutOverlapping()
400
+ * .retry(3, 5000);
401
+ * ```
402
+ *
403
+ * @public
404
+ * @since 3.0.0
121
405
  */
122
406
  declare class TaskSchedule {
123
407
  private task;
124
408
  /**
125
- * Create a new TaskSchedule instance.
409
+ * Initializes a new schedule instance with default settings.
126
410
  *
127
- * @param name - The unique name of the task.
128
- * @param callback - The function to execute.
411
+ * @param name - Unique task name used for identification and locking.
412
+ * @param callback - Logic to execute on schedule.
129
413
  */
130
414
  constructor(name: string, callback: () => void | Promise<void>);
131
415
  /**
132
- * Set a custom cron expression.
416
+ * Configures a raw 5-part cron expression.
133
417
  *
134
- * @param expression - Standard cron expression (e.g., "* * * * *")
135
- * @returns The TaskSchedule instance.
418
+ * @param expression - Cron string (e.g., "0 0 * * *").
419
+ * @returns The TaskSchedule instance for chaining.
420
+ * @throws {Error} If expression format is invalid or contains forbidden characters.
421
+ *
422
+ * @example
423
+ * ```typescript
424
+ * schedule.cron('0 9-17 * * 1-5'); // 9-5 on weekdays
425
+ * ```
136
426
  */
137
427
  cron(expression: string): this;
138
428
  /**
139
- * Run the task every minute.
429
+ * Schedules execution every minute.
140
430
  *
141
- * @returns The TaskSchedule instance.
431
+ * @returns The TaskSchedule instance for chaining.
142
432
  */
143
433
  everyMinute(): this;
144
434
  /**
145
- * Run the task every 5 minutes.
435
+ * Schedules execution every five minutes.
146
436
  *
147
- * @returns The TaskSchedule instance.
437
+ * @returns The TaskSchedule instance for chaining.
148
438
  */
149
439
  everyFiveMinutes(): this;
150
440
  /**
151
- * Run the task every 10 minutes.
441
+ * Schedules execution every ten minutes.
152
442
  *
153
- * @returns The TaskSchedule instance.
443
+ * @returns The TaskSchedule instance for chaining.
154
444
  */
155
445
  everyTenMinutes(): this;
156
446
  /**
157
- * Run the task every 15 minutes.
447
+ * Schedules execution every fifteen minutes.
158
448
  *
159
- * @returns The TaskSchedule instance.
449
+ * @returns The TaskSchedule instance for chaining.
160
450
  */
161
451
  everyFifteenMinutes(): this;
162
452
  /**
163
- * Run the task every 30 minutes.
453
+ * Schedules execution every thirty minutes.
164
454
  *
165
- * @returns The TaskSchedule instance.
455
+ * @returns The TaskSchedule instance for chaining.
166
456
  */
167
457
  everyThirtyMinutes(): this;
168
458
  /**
169
- * Run the task hourly (at minute 0).
459
+ * Schedules execution hourly at the top of the hour.
170
460
  *
171
- * @returns The TaskSchedule instance.
461
+ * @returns The TaskSchedule instance for chaining.
172
462
  */
173
463
  hourly(): this;
174
464
  /**
175
- * Run the task hourly at a specific minute.
465
+ * Schedules execution hourly at a specific minute.
176
466
  *
177
- * @param minute - Minute (0-59)
178
- * @returns The TaskSchedule instance.
467
+ * @param minute - Target minute (0-59).
468
+ * @returns The TaskSchedule instance for chaining.
469
+ * @throws {Error} If minute is outside 0-59 range.
179
470
  */
180
471
  hourlyAt(minute: number): this;
181
472
  /**
182
- * Run the task daily at midnight (00:00).
473
+ * Schedules execution daily at midnight.
183
474
  *
184
- * @returns The TaskSchedule instance.
475
+ * @returns The TaskSchedule instance for chaining.
185
476
  */
186
477
  daily(): this;
187
478
  /**
188
- * Run the task daily at a specific time.
479
+ * Schedules execution daily at a specific time.
480
+ *
481
+ * @param time - 24-hour time string in "HH:mm" format.
482
+ * @returns The TaskSchedule instance for chaining.
483
+ * @throws {Error} If time format is invalid or values are out of range.
189
484
  *
190
- * @param time - Time in "HH:mm" format (24h)
191
- * @returns The TaskSchedule instance.
485
+ * @example
486
+ * ```typescript
487
+ * schedule.dailyAt('14:30');
488
+ * ```
192
489
  */
193
490
  dailyAt(time: string): this;
194
491
  /**
195
- * Run the task weekly on Sunday at midnight.
492
+ * Schedules execution weekly on Sunday at midnight.
196
493
  *
197
- * @returns The TaskSchedule instance.
494
+ * @returns The TaskSchedule instance for chaining.
198
495
  */
199
496
  weekly(): this;
200
497
  /**
201
- * Run the task weekly on a specific day and time.
498
+ * Schedules execution weekly on a specific day and time.
202
499
  *
203
- * @param day - Day of week (0-7, 0 or 7 is Sunday)
204
- * @param time - Time in "HH:mm" format (default "00:00")
205
- * @returns The TaskSchedule instance.
500
+ * @param day - Day index (0-6, where 0 is Sunday).
501
+ * @param time - Optional 24-hour time string "HH:mm" (default "00:00").
502
+ * @returns The TaskSchedule instance for chaining.
503
+ * @throws {Error} If day index or time format is invalid.
504
+ *
505
+ * @example
506
+ * ```typescript
507
+ * schedule.weeklyOn(1, '09:00'); // Mondays at 9 AM
508
+ * ```
206
509
  */
207
510
  weeklyOn(day: number, time?: string): this;
208
511
  /**
209
- * Run the task monthly on the 1st day at midnight.
512
+ * Schedules execution monthly on the first day at midnight.
210
513
  *
211
- * @returns The TaskSchedule instance.
514
+ * @returns The TaskSchedule instance for chaining.
212
515
  */
213
516
  monthly(): this;
214
517
  /**
215
- * Run the task monthly on a specific day and time.
518
+ * Schedules execution monthly on a specific day and time.
216
519
  *
217
- * @param day - Day of month (1-31)
218
- * @param time - Time in "HH:mm" format (default "00:00")
219
- * @returns The TaskSchedule instance.
520
+ * @param day - Day of month (1-31).
521
+ * @param time - Optional 24-hour time string "HH:mm" (default "00:00").
522
+ * @returns The TaskSchedule instance for chaining.
523
+ * @throws {Error} If day or time format is invalid.
220
524
  */
221
525
  monthlyOn(day: number, time?: string): this;
222
526
  /**
223
- * Set the timezone for the task execution.
527
+ * Specifies the timezone for evaluating schedule frequency.
224
528
  *
225
- * @param timezone - Timezone identifier (e.g., "Asia/Taipei", "UTC")
226
- * @returns The TaskSchedule instance.
529
+ * @param timezone - IANA timezone identifier (e.g., "America/New_York").
530
+ * @returns The TaskSchedule instance for chaining.
531
+ * @throws {Error} If the timezone identifier is not recognized by the system.
227
532
  */
228
533
  timezone(timezone: string): this;
229
534
  /**
230
- * Set the time of execution for the current frequency.
231
- * Useful when chaining with daily(), weekly(), etc.
535
+ * Modifies the execution time of the existing frequency.
536
+ * Typically used after frequency methods like daily() or weekly().
232
537
  *
233
- * @param time - Time in "HH:mm" format
234
- * @returns The TaskSchedule instance.
538
+ * @param time - 24-hour time string in "HH:mm" format.
539
+ * @returns The TaskSchedule instance for chaining.
540
+ * @throws {Error} If time format is invalid.
235
541
  */
236
542
  at(time: string): this;
237
543
  /**
238
- * Ensure task runs on only one server at a time (Distributed Locking).
239
- * Requires a configured LockStore (Cache or Redis).
544
+ * Enables distributed locking to ensure only one instance runs globally.
545
+ * Prevents duplicate execution when multiple servers are polling the same schedule.
240
546
  *
241
- * @param lockTtlSeconds - Time in seconds to hold the lock (default 300)
242
- * @returns The TaskSchedule instance.
547
+ * @param lockTtlSeconds - Duration in seconds to hold the window lock (default 300).
548
+ * @returns The TaskSchedule instance for chaining.
243
549
  */
244
550
  onOneServer(lockTtlSeconds?: number): this;
245
551
  /**
246
- * Alias for onOneServer.
247
- * Prevents overlapping executions of the same task.
552
+ * Prevents a new task instance from starting if the previous one is still running.
553
+ *
554
+ * Unlike `onOneServer()` which uses a time-window lock, this uses an execution lock
555
+ * to handle long-running tasks that might span multiple scheduling intervals.
248
556
  *
249
- * @param expiresAt - Lock TTL in seconds
250
- * @returns The TaskSchedule instance.
557
+ * @param expiresAt - Max duration in seconds to keep the execution lock (default 3600).
558
+ * @returns The TaskSchedule instance for chaining.
559
+ *
560
+ * @example
561
+ * ```typescript
562
+ * // Prevents overlapping if the heavy operation takes > 1 minute
563
+ * scheduler.task('data-sync', heavySync)
564
+ * .everyMinute()
565
+ * .withoutOverlapping();
566
+ * ```
251
567
  */
252
568
  withoutOverlapping(expiresAt?: number): this;
253
569
  /**
254
- * Run task in background (do not wait for completion in the loop).
255
- * Note: In Node.js non-blocking environment this is largely semantic,
256
- * but affects how we handle error catching and lock release.
570
+ * Executes the task in background mode.
571
+ * The scheduler will not wait for this task to finish before proceeding to the next.
257
572
  *
258
- * @returns The TaskSchedule instance.
573
+ * @returns The TaskSchedule instance for chaining.
259
574
  */
260
575
  runInBackground(): this;
261
576
  /**
262
- * Restrict task execution to a specific node role.
577
+ * Restricts task execution to nodes with a specific role.
263
578
  *
264
- * @param role - The required node role (e.g., 'api', 'worker')
265
- * @returns The TaskSchedule instance.
579
+ * @param role - Target role identifier (e.g., "worker").
580
+ * @returns The TaskSchedule instance for chaining.
266
581
  */
267
582
  onNode(role: string): this;
268
583
  /**
269
- * Set the command string for exec tasks.
584
+ * Defines a maximum execution time for the task.
585
+ *
586
+ * @param ms - Timeout duration in milliseconds.
587
+ * @returns The TaskSchedule instance for chaining.
588
+ * @throws {Error} If timeout is not a positive number.
589
+ */
590
+ timeout(ms: number): this;
591
+ /**
592
+ * Configures automatic retry behavior for failed executions.
593
+ *
594
+ * @param attempts - Max number of retries (default 3).
595
+ * @param delayMs - Wait time between retries in milliseconds (default 1000).
596
+ * @returns The TaskSchedule instance for chaining.
597
+ * @throws {Error} If attempts or delay are negative.
598
+ */
599
+ retry(attempts?: number, delayMs?: number): this;
600
+ /**
601
+ * Sets the shell command for execution-based tasks.
270
602
  *
271
603
  * @param command - The command string.
272
- * @returns The TaskSchedule instance.
604
+ * @returns The TaskSchedule instance for chaining.
273
605
  * @internal
274
606
  */
275
607
  setCommand(command: string): this;
276
608
  /**
277
- * Register a callback to run on task success.
609
+ * Registers a callback to execute upon successful task completion.
278
610
  *
279
- * @param callback - The callback function.
280
- * @returns The TaskSchedule instance.
611
+ * @param callback - Function to run on success.
612
+ * @returns The TaskSchedule instance for chaining.
281
613
  */
282
614
  onSuccess(callback: ActionCallback): this;
283
615
  /**
284
- * Register a callback to run on task failure.
616
+ * Registers a callback to execute when the task fails.
285
617
  *
286
- * @param callback - The callback function.
287
- * @returns The TaskSchedule instance.
618
+ * @param callback - Function to run on failure.
619
+ * @returns The TaskSchedule instance for chaining.
288
620
  */
289
621
  onFailure(callback: ActionCallback): this;
290
622
  /**
291
- * Set a description for the task (useful for listing).
623
+ * Attaches a human-readable description to the task.
292
624
  *
293
- * @param _text - The description text.
294
- * @returns The TaskSchedule instance.
625
+ * @param _text - Description text.
626
+ * @returns The TaskSchedule instance for chaining.
295
627
  */
296
628
  description(_text: string): this;
297
629
  /**
298
- * Get the underlying task configuration.
630
+ * Returns the final task configuration.
299
631
  *
300
- * @returns The ScheduledTask object.
632
+ * @returns The constructed ScheduledTask object.
301
633
  */
302
634
  getTask(): ScheduledTask;
303
635
  }
304
636
 
305
637
  /**
306
- * Core Scheduler Manager responsible for managing and executing tasks.
638
+ * Central registry and execution engine for scheduled tasks.
639
+ *
640
+ * Orchestrates the lifecycle of scheduled jobs by evaluating frequencies,
641
+ * managing distributed locks to prevent duplicate execution, and handling
642
+ * reliability features like retries and timeouts.
643
+ *
644
+ * Architecture Principles:
645
+ * - Distributed Safety: Uses lock keys scoped to minute precision for global deduplication.
646
+ * - Non-blocking: Executes tasks in parallel using fire-and-forget patterns.
647
+ * - Observability: Emits granular lifecycle hooks for monitoring and alerting.
648
+ *
649
+ * @example
650
+ * ```typescript
651
+ * const scheduler = new SchedulerManager(lockManager, logger, hooks, 'worker');
652
+ *
653
+ * // Define a maintenance task
654
+ * scheduler.task('daily-cleanup', async () => {
655
+ * await db.cleanup();
656
+ * })
657
+ * .dailyAt('03:00')
658
+ * .onOneServer();
659
+ * ```
660
+ *
661
+ * @public
307
662
  */
308
663
  declare class SchedulerManager {
309
664
  lockManager: LockManager;
@@ -311,56 +666,225 @@ declare class SchedulerManager {
311
666
  private hooks?;
312
667
  private currentNodeRole?;
313
668
  private tasks;
669
+ /**
670
+ * Initializes the scheduler engine.
671
+ *
672
+ * @param lockManager - Backend for distributed locking.
673
+ * @param logger - Optional logger for operational visibility.
674
+ * @param hooks - Optional manager for lifecycle event hooks.
675
+ * @param currentNodeRole - Role identifier for the local node (used for filtering).
676
+ */
314
677
  constructor(lockManager: LockManager, logger?: Logger | undefined, hooks?: HookManager | undefined, currentNodeRole?: string | undefined);
315
678
  /**
316
- * Define a new scheduled task.
679
+ * Registers a new callback-based scheduled task.
317
680
  *
318
- * @param name - Unique name for the task
319
- * @param callback - Function to execute
320
- * @returns The newly created TaskSchedule.
681
+ * @param name - Unique task name used for identification and locking.
682
+ * @param callback - Asynchronous function containing the task logic.
683
+ * @returns A fluent TaskSchedule instance for further configuration.
684
+ *
685
+ * @example
686
+ * ```typescript
687
+ * scheduler.task('process-queues', async () => {
688
+ * await queue.process();
689
+ * }).everyMinute();
690
+ * ```
321
691
  */
322
692
  task(name: string, callback: () => void | Promise<void>): TaskSchedule;
323
693
  /**
324
- * Define a new scheduled command execution task.
694
+ * Registers a shell command as a scheduled task.
695
+ *
696
+ * Executes the command via `sh -c` on matching nodes.
697
+ *
698
+ * @param name - Unique identifier for the command task.
699
+ * @param command - Raw shell command string.
700
+ * @returns A fluent TaskSchedule instance.
701
+ * @throws {Error} If the shell command returns a non-zero exit code during execution.
325
702
  *
326
- * @param name - Unique name for the task
327
- * @param command - Shell command to execute
328
- * @returns The newly created TaskSchedule.
703
+ * @example
704
+ * ```typescript
705
+ * scheduler.exec('log-rotate', 'logrotate /etc/logrotate.conf')
706
+ * .daily()
707
+ * .onNode('worker');
708
+ * ```
329
709
  */
330
710
  exec(name: string, command: string): TaskSchedule;
331
711
  /**
332
- * Add a pre-configured task schedule object.
712
+ * Injects a pre-configured TaskSchedule instance into the registry.
333
713
  *
334
- * @param schedule - The task schedule to add.
714
+ * @param schedule - Configured task schedule to register.
335
715
  */
336
716
  add(schedule: TaskSchedule): void;
337
717
  /**
338
- * Get all registered task definitions.
718
+ * Exports all registered tasks for external inspection or serialization.
339
719
  *
340
- * @returns An array of scheduled tasks.
720
+ * @returns An array of raw task configurations.
341
721
  */
342
722
  getTasks(): ScheduledTask[];
343
723
  /**
344
- * Trigger the scheduler to check and run due tasks.
345
- * This is typically called every minute by a system cron or worker loop.
724
+ * Main evaluation loop that triggers tasks due for execution.
346
725
  *
347
- * @param date - The current reference date (default: now)
348
- * @returns A promise that resolves when the scheduler run is complete.
726
+ * Should be invoked every minute by a system timer (systemd/cron) or daemon.
727
+ * Performs frequency checks, role filtering, and parallel execution.
728
+ *
729
+ * @param date - Reference time for cron evaluation (default: current time).
730
+ * @returns Resolves when all due tasks have been initiated.
349
731
  */
350
732
  run(date?: Date): Promise<void>;
351
733
  /**
352
- * Execute a specific task with locking logic.
734
+ * Executes an individual task after validating execution constraints.
735
+ *
736
+ * Evaluates node roles, overlapping prevention, and distributed time-window locks
737
+ * before initiating the actual task logic.
738
+ *
739
+ * @param task - Target task configuration.
740
+ * @param date - Reference time used for lock key generation.
353
741
  *
354
- * @param task - The task to execute.
355
742
  * @internal
356
743
  */
357
744
  runTask(task: ScheduledTask, date?: Date): Promise<void>;
358
745
  /**
359
- * Execute the task callback and handle hooks.
746
+ * Internal wrapper for executing task logic with reliability controls.
747
+ *
748
+ * Handles timeouts, retries, and emits lifecycle hooks for monitoring.
360
749
  *
361
- * @param task - The task to execute.
750
+ * @param task - The scheduled task to execute.
751
+ * @returns Resolves when the task (and its retries) completes or fails permanently.
752
+ *
753
+ * @internal
362
754
  */
363
755
  private executeTask;
364
756
  }
365
757
 
758
+ /**
759
+ * Enterprise task scheduler orbit enabling distributed cron-based job execution.
760
+ *
761
+ * Provides the infrastructure for defining scheduled tasks with distributed locking,
762
+ * preventing duplicate execution across multi-server deployments. Integrates seamlessly
763
+ * with Gravito's orbit system to expose the scheduler in both the IoC container and
764
+ * request context.
765
+ *
766
+ * Design rationale: Uses pluggable lock backends (memory/cache) to support both
767
+ * development (single-node) and production (multi-node) environments without code changes.
768
+ *
769
+ * @example
770
+ * Single-node development setup:
771
+ * ```typescript
772
+ * await PlanetCore.boot({
773
+ * config: {
774
+ * scheduler: {
775
+ * lock: { driver: 'memory' }
776
+ * }
777
+ * },
778
+ * orbits: [OrbitHorizon]
779
+ * })
780
+ * ```
781
+ *
782
+ * @example
783
+ * Multi-node production with distributed locking:
784
+ * ```typescript
785
+ * import { OrbitCache } from '@gravito/stasis'
786
+ *
787
+ * await PlanetCore.boot({
788
+ * config: {
789
+ * scheduler: {
790
+ * lock: { driver: 'cache' },
791
+ * nodeRole: 'worker'
792
+ * }
793
+ * },
794
+ * orbits: [
795
+ * OrbitCache, // Must load before Horizon
796
+ * OrbitHorizon
797
+ * ]
798
+ * })
799
+ * ```
800
+ *
801
+ * @public
802
+ */
803
+ declare class OrbitHorizon implements GravitoOrbit {
804
+ /**
805
+ * Integrates the Horizon scheduler into the PlanetCore lifecycle.
806
+ *
807
+ * Orchestrates the initialization sequence:
808
+ * 1. Resolves lock backend (memory or cache-based).
809
+ * 2. Instantiates SchedulerManager with global dependencies (logger, hooks).
810
+ * 3. Registers the scheduler in the IoC container for CLI and global access.
811
+ * 4. Injects the scheduler into the request context via middleware.
812
+ *
813
+ * @param core - PlanetCore instance providing configuration and service container.
814
+ *
815
+ * @throws {Error} If the cache driver is explicitly requested but `CacheManager` is unavailable.
816
+ */
817
+ install(core: PlanetCore): void;
818
+ }
819
+ declare module '@gravito/core' {
820
+ interface GravitoVariables {
821
+ /** Scheduler manager for task scheduling */
822
+ scheduler?: SchedulerManager;
823
+ }
824
+ }
825
+
826
+ /**
827
+ * Encapsulates the outcome of a child process execution.
828
+ *
829
+ * Provides status codes and captured stream outputs for programmatic inspection.
830
+ *
831
+ * @public
832
+ * @since 3.0.0
833
+ */
834
+ interface ProcessResult {
835
+ /** numeric exit code returned by the operating system (0 typically denotes success). */
836
+ exitCode: number;
837
+ /** Captured UTF-8 encoded text from the standard output stream. */
838
+ stdout: string;
839
+ /** Captured UTF-8 encoded text from the standard error stream. */
840
+ stderr: string;
841
+ /** Semantic indicator of successful completion (mapped from exitCode === 0). */
842
+ success: boolean;
843
+ }
844
+ /**
845
+ * Spawns a shell command and asynchronously captures its full output.
846
+ *
847
+ * Leverages the Gravito runtime adapter to ensure compatibility across different
848
+ * JavaScript runtimes (Bun, Node.js). Executes commands within a shell (`sh -c`)
849
+ * to support pipes, redirects, and environment variables.
850
+ *
851
+ * @param command - Raw shell command string to execute.
852
+ * @returns Resolves to a detailed `ProcessResult` object.
853
+ *
854
+ * @example
855
+ * ```typescript
856
+ * const result = await runProcess('ls -lh /var/logs');
857
+ * if (result.success) {
858
+ * processLogs(result.stdout);
859
+ * }
860
+ * ```
861
+ *
862
+ * @public
863
+ * @since 3.0.0
864
+ */
865
+ declare function runProcess(command: string): Promise<ProcessResult>;
866
+ /**
867
+ * Utility class for managing child process lifecycles.
868
+ *
869
+ * Acts as a wrapper for `runProcess` to maintain compatibility with earlier
870
+ * versions of the framework.
871
+ *
872
+ * @example
873
+ * ```typescript
874
+ * const { stdout } = await Process.run('bun -v');
875
+ * ```
876
+ *
877
+ * @since 3.0.0
878
+ * @public
879
+ */
880
+ declare class Process {
881
+ /**
882
+ * Static alias for `runProcess`.
883
+ *
884
+ * @param command - Command to execute.
885
+ * @returns Process outcome.
886
+ */
887
+ static run(command: string): Promise<ProcessResult>;
888
+ }
889
+
366
890
  export { CacheLockStore, CronParser, LockManager, type LockStore, MemoryLockStore, OrbitHorizon, Process, type ProcessResult, type ScheduledTask, SchedulerManager, TaskSchedule, runProcess };