@zudojs/scheduler 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -16
  3. package/dist/index.d.ts +7 -3
  4. package/dist/index.js +4 -3
  5. package/dist/scheduler/constants/index.d.ts +1 -1
  6. package/dist/scheduler/constants/index.js +1 -1
  7. package/dist/scheduler/constants/schedulerConstants.core.d.ts +0 -4
  8. package/dist/scheduler/constants/schedulerConstants.core.js +0 -4
  9. package/dist/scheduler/duration/duration.parser.d.ts +13 -5
  10. package/dist/scheduler/duration/duration.parser.js +57 -19
  11. package/dist/scheduler/errors/index.d.ts +1 -1
  12. package/dist/scheduler/errors/index.js +1 -1
  13. package/dist/scheduler/errors/scheduler.errors.d.ts +7 -1
  14. package/dist/scheduler/errors/scheduler.errors.js +7 -1
  15. package/dist/scheduler/executor/index.d.ts +1 -1
  16. package/dist/scheduler/executor/index.js +1 -1
  17. package/dist/scheduler/executor/jobExecutor.core.d.ts +24 -4
  18. package/dist/scheduler/executor/jobExecutor.core.js +154 -28
  19. package/dist/scheduler/job/jobOptions.type.d.ts +11 -0
  20. package/dist/scheduler/priorityQueue/schedulerPriorityQueue.core.d.ts +12 -0
  21. package/dist/scheduler/priorityQueue/schedulerPriorityQueue.core.js +32 -5
  22. package/dist/scheduler/registry/jobRegistry.core.d.ts +8 -0
  23. package/dist/scheduler/registry/jobRegistry.core.js +14 -0
  24. package/dist/scheduler/schedule/schedule.type.d.ts +11 -6
  25. package/dist/scheduler/schedule/schedule.type.js +10 -1
  26. package/dist/scheduler/schedule/scheduleOptions.type.d.ts +24 -1
  27. package/dist/scheduler/scheduleHandle/index.d.ts +1 -0
  28. package/dist/scheduler/scheduleHandle/scheduleHandle.type.d.ts +26 -3
  29. package/dist/scheduler/scheduleHandle/scheduleHandle.type.js +23 -7
  30. package/dist/scheduler/scheduler.core.d.ts +135 -11
  31. package/dist/scheduler/scheduler.core.js +478 -53
  32. package/dist/scheduler/trigger/cron.parser.d.ts +44 -0
  33. package/dist/scheduler/trigger/cron.parser.js +256 -0
  34. package/dist/scheduler/trigger/index.d.ts +2 -0
  35. package/dist/scheduler/trigger/index.js +1 -0
  36. package/dist/scheduler/trigger/schedulerTrigger.core.d.ts +22 -1
  37. package/dist/scheduler/trigger/schedulerTrigger.core.js +69 -6
  38. package/dist/scheduler/types/index.d.ts +12 -1
  39. package/dist/scheduler/types/index.js +7 -0
  40. package/dist/scheduler/types/schedulerTypes.core.d.ts +7 -87
  41. package/dist/scheduler/types/schedulerTypes.core.js +7 -1
  42. package/package.json +26 -15
  43. package/dist/.tsbuildinfo +0 -1
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js.map +0 -1
  46. package/dist/scheduler/clock/index.d.ts.map +0 -1
  47. package/dist/scheduler/clock/index.js.map +0 -1
  48. package/dist/scheduler/clock/schedulerClock.type.d.ts.map +0 -1
  49. package/dist/scheduler/clock/schedulerClock.type.js.map +0 -1
  50. package/dist/scheduler/constants/index.d.ts.map +0 -1
  51. package/dist/scheduler/constants/index.js.map +0 -1
  52. package/dist/scheduler/constants/schedulerConstants.core.d.ts.map +0 -1
  53. package/dist/scheduler/constants/schedulerConstants.core.js.map +0 -1
  54. package/dist/scheduler/duration/duration.parser.d.ts.map +0 -1
  55. package/dist/scheduler/duration/duration.parser.js.map +0 -1
  56. package/dist/scheduler/duration/index.d.ts.map +0 -1
  57. package/dist/scheduler/duration/index.js.map +0 -1
  58. package/dist/scheduler/errors/index.d.ts.map +0 -1
  59. package/dist/scheduler/errors/index.js.map +0 -1
  60. package/dist/scheduler/errors/scheduler.errors.d.ts.map +0 -1
  61. package/dist/scheduler/errors/scheduler.errors.js.map +0 -1
  62. package/dist/scheduler/executor/index.d.ts.map +0 -1
  63. package/dist/scheduler/executor/index.js.map +0 -1
  64. package/dist/scheduler/executor/jobExecutor.core.d.ts.map +0 -1
  65. package/dist/scheduler/executor/jobExecutor.core.js.map +0 -1
  66. package/dist/scheduler/job/index.d.ts.map +0 -1
  67. package/dist/scheduler/job/index.js.map +0 -1
  68. package/dist/scheduler/job/jobContext.type.d.ts.map +0 -1
  69. package/dist/scheduler/job/jobContext.type.js.map +0 -1
  70. package/dist/scheduler/job/jobDefinition.type.d.ts.map +0 -1
  71. package/dist/scheduler/job/jobDefinition.type.js.map +0 -1
  72. package/dist/scheduler/job/jobHandler.type.d.ts.map +0 -1
  73. package/dist/scheduler/job/jobHandler.type.js.map +0 -1
  74. package/dist/scheduler/job/jobOptions.type.d.ts.map +0 -1
  75. package/dist/scheduler/job/jobOptions.type.js.map +0 -1
  76. package/dist/scheduler/priorityQueue/index.d.ts.map +0 -1
  77. package/dist/scheduler/priorityQueue/index.js.map +0 -1
  78. package/dist/scheduler/priorityQueue/schedulerPriorityQueue.core.d.ts.map +0 -1
  79. package/dist/scheduler/priorityQueue/schedulerPriorityQueue.core.js.map +0 -1
  80. package/dist/scheduler/registry/index.d.ts.map +0 -1
  81. package/dist/scheduler/registry/index.js.map +0 -1
  82. package/dist/scheduler/registry/jobRegistry.core.d.ts.map +0 -1
  83. package/dist/scheduler/registry/jobRegistry.core.js.map +0 -1
  84. package/dist/scheduler/schedule/index.d.ts.map +0 -1
  85. package/dist/scheduler/schedule/index.js.map +0 -1
  86. package/dist/scheduler/schedule/schedule.type.d.ts.map +0 -1
  87. package/dist/scheduler/schedule/schedule.type.js.map +0 -1
  88. package/dist/scheduler/schedule/scheduleOptions.type.d.ts.map +0 -1
  89. package/dist/scheduler/schedule/scheduleOptions.type.js.map +0 -1
  90. package/dist/scheduler/scheduleHandle/index.d.ts.map +0 -1
  91. package/dist/scheduler/scheduleHandle/index.js.map +0 -1
  92. package/dist/scheduler/scheduleHandle/scheduleHandle.type.d.ts.map +0 -1
  93. package/dist/scheduler/scheduleHandle/scheduleHandle.type.js.map +0 -1
  94. package/dist/scheduler/scheduler.core.d.ts.map +0 -1
  95. package/dist/scheduler/scheduler.core.js.map +0 -1
  96. package/dist/scheduler/trigger/index.d.ts.map +0 -1
  97. package/dist/scheduler/trigger/index.js.map +0 -1
  98. package/dist/scheduler/trigger/schedulerTrigger.core.d.ts.map +0 -1
  99. package/dist/scheduler/trigger/schedulerTrigger.core.js.map +0 -1
  100. package/dist/scheduler/trigger/trigger.type.d.ts.map +0 -1
  101. package/dist/scheduler/trigger/trigger.type.js.map +0 -1
  102. package/dist/scheduler/types/index.d.ts.map +0 -1
  103. package/dist/scheduler/types/index.js.map +0 -1
  104. package/dist/scheduler/types/schedulerTypes.core.d.ts.map +0 -1
  105. package/dist/scheduler/types/schedulerTypes.core.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zudojs Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @zudojs/scheduler
2
2
 
3
- Scheduled task and job infrastructure with cron-like scheduling, persistence, and worker management.
3
+ In-process scheduling for delayed, recurring and cron-driven jobs.
4
4
 
5
5
  ## Installation
6
6
 
@@ -11,31 +11,185 @@ npm install @zudojs/scheduler
11
11
  ## Quick Start
12
12
 
13
13
  ```typescript
14
- import { createScheduler } from "@zudojs/scheduler";
14
+ import { Scheduler } from "@zudojs/scheduler";
15
15
 
16
- const scheduler = createScheduler();
16
+ const scheduler = new Scheduler({
17
+ onError: ({ jobId, error }) => log.error({ jobId, error }, "job failed"),
18
+ });
19
+
20
+ // Register the job, then schedule it. A job must be defined before it can be
21
+ // scheduled — scheduling an unregistered id throws.
22
+ scheduler.define({
23
+ id: "cleanup",
24
+ name: "Clean up old records",
25
+ handler: async (ctx) => {
26
+ await cleanupOldRecords({ signal: ctx.signal });
27
+ },
28
+ });
29
+
30
+ scheduler.cron("0 0 * * *", "cleanup", { timezone: "UTC" });
31
+
32
+ scheduler.start();
33
+
34
+ // On shutdown: aborts in-flight jobs and waits for them to settle.
35
+ await scheduler.stop({ timeoutMs: 30_000 });
36
+ ```
37
+
38
+ ## Scheduling
39
+
40
+ Four entry points, each returning a `ScheduleHandle`:
41
+
42
+ ```typescript
43
+ scheduler.after("30s", "job-id"); // once, after a delay
44
+ scheduler.at(new Date("2027-01-01"), "job-id"); // once, at an instant
45
+ scheduler.every("5m", "job-id"); // repeating interval
46
+ scheduler.cron("*/15 * * * *", "job-id"); // repeating cron
47
+ ```
17
48
 
18
- scheduler.add("cleanup", {
19
- cron: "0 0 * * *",
20
- handler: async () => {
21
- await cleanupOldRecords();
49
+ Durations accept `ms`, `s`, `m`, `h`, `d` and `w`, and compose: `"1h30m"`.
50
+ Zero, negative and out-of-range durations are rejected.
51
+
52
+ Cron expressions are standard five-field (`minute hour day-of-month month
53
+ day-of-week`) with ranges, lists, steps and three-letter names, plus the
54
+ `@daily`/`@hourly`/`@weekly`/`@monthly`/`@yearly` macros. An invalid expression
55
+ throws where the schedule is declared, not at fire time.
56
+
57
+ Only `timezone: "UTC"` and the system local zone are supported. Any other zone
58
+ is rejected rather than silently ignored — honouring an arbitrary IANA zone
59
+ needs real zone data this package does not carry.
60
+
61
+ ## Controlling a schedule
62
+
63
+ The handle is bound to its scheduler, so these reach the queue:
64
+
65
+ ```typescript
66
+ const handle = scheduler.every("1h", "sync");
67
+
68
+ handle.nextRun(); // Date — the real next fire time
69
+ await handle.pause(); // stops firing; recomputes from now on resume
70
+ await handle.resume();
71
+ await handle.cancel(); // aborts any in-flight run and removes the schedule
72
+ ```
73
+
74
+ ## Failure handling
75
+
76
+ Nothing is swallowed. A job that throws is reported to `onError`, after its
77
+ retry policy is exhausted:
78
+
79
+ ```typescript
80
+ scheduler.define({
81
+ id: "flaky",
82
+ name: "Flaky upstream call",
83
+ options: {
84
+ timeout: 10_000,
85
+ retry: {
86
+ attempts: 3,
87
+ strategy: "exponential", // or "fixed" | "linear"
88
+ delay: 1_000,
89
+ maxDelay: 30_000,
90
+ jitter: true,
91
+ },
22
92
  },
93
+ handler: callUpstream,
94
+ });
95
+ ```
96
+
97
+ A timeout raises `SchedulerJobTimeoutError` and **aborts the handler** via
98
+ `ctx.signal`; anything else raises `SchedulerJobExecutionError` carrying the
99
+ original error as `cause`.
100
+
101
+ ## Concurrency and overlap
102
+
103
+ `maxConcurrency` (default 10) bounds executions in flight across all schedules;
104
+ work over the ceiling waits for the next tick. Per schedule, `overlap` decides
105
+ what happens when a fire time arrives while the previous run is still going:
106
+
107
+ ```typescript
108
+ scheduler.every("10s", "report", { overlap: "skip" });
109
+ ```
110
+
111
+ | `overlap` | Behaviour when the previous run is still going |
112
+ | ------------------- | ----------------------------------------------- |
113
+ | `"allow"` (default) | starts the new run alongside it |
114
+ | `"skip"` | drops the fire time |
115
+ | `"queue"` | holds it and runs it when the current one ends |
116
+ | `"replace"` | aborts the running execution and starts a new one |
117
+
118
+ `JobOptions.overlap` sets the default for every schedule of a job;
119
+ `ScheduleOptions.overlap` overrides it per schedule.
120
+
121
+ `JobOptions.concurrency` caps how many executions of one job run at once
122
+ across every schedule that fires it. A fire time arriving at the ceiling is
123
+ held and dispatched when an execution finishes, not dropped:
124
+
125
+ ```typescript
126
+ scheduler.define({
127
+ id: "reindex",
128
+ name: "Reindex a tenant",
129
+ options: { concurrency: 2 },
130
+ handler: reindex,
23
131
  });
132
+ ```
24
133
 
25
- await scheduler.start();
134
+ `ScheduleOptions.priority` breaks ties between schedules due at the same
135
+ instant — higher runs first. It never lets a schedule jump ahead of one due
136
+ earlier.
137
+
138
+ ## Misfires
139
+
140
+ A fire time already in the past follows the schedule's `misfire` policy. This
141
+ is the case for `at(pastDate)`, for a schedule restored after a restart, and
142
+ for a recurring schedule whose process was blocked past its fire time.
143
+
144
+ | `misfire` | Behaviour |
145
+ | ---------------------- | ---------------------------------------------------- |
146
+ | `"run-once"` (default) | runs once immediately, then resumes from now |
147
+ | `"skip"` | refuses the schedule |
148
+ | `"catch-up"` | replays every missed occurrence until it is current |
149
+
150
+ ```typescript
151
+ scheduler.every("1h", "hourly-rollup", { misfire: "catch-up" });
152
+ ```
153
+
154
+ ## Execution history
155
+
156
+ The scheduler keeps the last 100 executions (`MAX_EXECUTION_HISTORY`), each a
157
+ `JobExecution` with its status, timings and error:
158
+
159
+ ```typescript
160
+ scheduler.getExecutions("cleanup");
161
+ // [{ id, jobId, scheduleId, status: "completed" | "failed" | "timed_out" |
162
+ // "cancelled" | "running", scheduledAt, startedAt, completedAt,
163
+ // duration, attempt, error? }]
26
164
  ```
27
165
 
28
166
  ## Features
29
167
 
30
- - Cron-like job scheduling
31
- - Persistent job storage
32
- - Worker management
33
- - Job retry and backoff
34
- - Job history and logs
168
+ - Delay, date, interval and cron triggers
169
+ - Real five-field cron parsing with macros, ranges, steps and names
170
+ - Bound schedule handles: pause, resume, cancel, next-run
171
+ - Retry policies with fixed, linear and exponential backoff, capping and jitter
172
+ - Per-job timeouts that abort the handler through `AbortSignal`
173
+ - Global concurrency ceiling, per-job concurrency limits, and per-schedule
174
+ overlap and misfire policies
175
+ - Bounded execution history
176
+ - Graceful shutdown with abort or drain
177
+ - Min-heap priority queue, O(log n) insert and remove
178
+
179
+ ## Not included
180
+
181
+ This package schedules work inside one process. It has no persistence, no
182
+ cross-process locking and no worker pool — a restart loses the schedule, and two
183
+ instances will each run the same job. Pair it with `@zudojs/queue` when you need
184
+ durability or distribution.
185
+
186
+ Because there is no store and no lock, the store, lock, not-started and
187
+ schedule-lookup error classes are not re-exported here; they live in
188
+ `@zudojs/errors` for a durable scheduler built on top.
35
189
 
36
190
  ## Use Cases
37
191
 
38
- - Scheduled maintenance tasks
192
+ - Scheduled maintenance and cleanup
193
+ - Periodic synchronisation
39
194
  - Report generation
40
- - Data cleanup jobs
41
- - Periodic synchronization
195
+ - Cache warming and refresh
package/dist/index.d.ts CHANGED
@@ -24,17 +24,21 @@
24
24
  * ```
25
25
  */
26
26
  export type { SchedulerJobId, ScheduleId, ExecutionId, ScheduleType, ScheduleState, JobState, OverlapPolicy, MisfirePolicy, RetryStrategy, RetryPolicy, JobOptions, JobDefinition, JobHandler, JobContext, JobExecution, JobExecutionResult, Schedule, ScheduleOptions, ScheduleHandle, Trigger, } from "./scheduler/types/index.js";
27
- export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, SCHEDULER_TICK_INTERVAL, } from "./scheduler/constants/index.js";
27
+ export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, } from "./scheduler/constants/index.js";
28
28
  export type { SchedulerErrorOptions } from "./scheduler/errors/index.js";
29
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "./scheduler/errors/index.js";
29
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "./scheduler/errors/index.js";
30
30
  export { createJobContext, createJobDefinition, } from "./scheduler/job/index.js";
31
31
  export { createSchedule } from "./scheduler/schedule/index.js";
32
32
  export { ScheduleHandleImpl } from "./scheduler/scheduleHandle/index.js";
33
+ export type { ScheduleHandleBinding } from "./scheduler/scheduleHandle/index.js";
33
34
  export { DateTrigger, DelayTrigger, IntervalTrigger, CronTrigger, } from "./scheduler/trigger/index.js";
35
+ export { parseCron, nextCronDate } from "./scheduler/trigger/index.js";
36
+ export type { ParsedCron } from "./scheduler/trigger/index.js";
34
37
  export { SystemClock, createSystemClock } from "./scheduler/clock/index.js";
35
38
  export { JobRegistry } from "./scheduler/registry/index.js";
36
- export { JobExecutor } from "./scheduler/executor/index.js";
39
+ export { JobExecutor, retryDelay } from "./scheduler/executor/index.js";
37
40
  export { PriorityQueue } from "./scheduler/priorityQueue/index.js";
38
41
  export { parseDuration } from "./scheduler/duration/index.js";
39
42
  export { Scheduler } from "./scheduler/scheduler.core.js";
43
+ export type { SchedulerOptions, SchedulerErrorEvent, } from "./scheduler/scheduler.core.js";
40
44
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -24,8 +24,8 @@
24
24
  * ```
25
25
  */
26
26
  // Constants
27
- export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, SCHEDULER_TICK_INTERVAL, } from "./scheduler/constants/index.js";
28
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "./scheduler/errors/index.js";
27
+ export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, } from "./scheduler/constants/index.js";
28
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "./scheduler/errors/index.js";
29
29
  // Job
30
30
  export { createJobContext, createJobDefinition, } from "./scheduler/job/index.js";
31
31
  // Schedule
@@ -34,12 +34,13 @@ export { createSchedule } from "./scheduler/schedule/index.js";
34
34
  export { ScheduleHandleImpl } from "./scheduler/scheduleHandle/index.js";
35
35
  // Trigger
36
36
  export { DateTrigger, DelayTrigger, IntervalTrigger, CronTrigger, } from "./scheduler/trigger/index.js";
37
+ export { parseCron, nextCronDate } from "./scheduler/trigger/index.js";
37
38
  // Clock
38
39
  export { SystemClock, createSystemClock } from "./scheduler/clock/index.js";
39
40
  // Registry
40
41
  export { JobRegistry } from "./scheduler/registry/index.js";
41
42
  // Executor
42
- export { JobExecutor } from "./scheduler/executor/index.js";
43
+ export { JobExecutor, retryDelay } from "./scheduler/executor/index.js";
43
44
  // Priority Queue
44
45
  export { PriorityQueue } from "./scheduler/priorityQueue/index.js";
45
46
  // Duration
@@ -1,2 +1,2 @@
1
- export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, SCHEDULER_TICK_INTERVAL, } from "./schedulerConstants.core.js";
1
+ export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, } from "./schedulerConstants.core.js";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, SCHEDULER_TICK_INTERVAL, } from "./schedulerConstants.core.js";
1
+ export { DEFAULT_JOB_TIMEOUT, DEFAULT_MAX_CONCURRENCY, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, MAX_TIMER_DELAY, MAX_JOBS, MAX_SCHEDULES, MAX_EXECUTION_HISTORY, DEFAULT_MISFIRE_POLICY, DEFAULT_OVERLAP_POLICY, } from "./schedulerConstants.core.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -45,8 +45,4 @@ export declare const DEFAULT_MISFIRE_POLICY: MisfirePolicy;
45
45
  * Default overlap policy.
46
46
  */
47
47
  export declare const DEFAULT_OVERLAP_POLICY: OverlapPolicy;
48
- /**
49
- * Tick interval for the scheduler timer (1 second).
50
- */
51
- export declare const SCHEDULER_TICK_INTERVAL = 1000;
52
48
  //# sourceMappingURL=schedulerConstants.core.d.ts.map
@@ -43,8 +43,4 @@ export const DEFAULT_MISFIRE_POLICY = "run-once";
43
43
  * Default overlap policy.
44
44
  */
45
45
  export const DEFAULT_OVERLAP_POLICY = "allow";
46
- /**
47
- * Tick interval for the scheduler timer (1 second).
48
- */
49
- export const SCHEDULER_TICK_INTERVAL = 1_000;
50
46
  //# sourceMappingURL=schedulerConstants.core.js.map
@@ -1,11 +1,19 @@
1
1
  /**
2
2
  * Parses a human-readable duration string into milliseconds.
3
3
  *
4
- * Supported formats:
5
- * - "5s" - 5 seconds
6
- * - "10m" - 10 minutes
7
- * - "2h" - 2 hours
8
- * - "3d" - 3 days
4
+ * Supported units: `ms`, `s`, `m`, `h`, `d`, `w`. Components may be combined
5
+ * (`"1h30m"`), and each must be a non-negative integer. The total must be
6
+ * greater than zero — a zero duration would schedule a job that fires
7
+ * continuously — and within roughly a century.
8
+ *
9
+ * @example
10
+ * parseDuration("5s") // 5000
11
+ * parseDuration("1h30m") // 5400000
12
+ * parseDuration("250ms") // 250
13
+ *
14
+ * @param duration - The duration string.
15
+ * @returns The duration in milliseconds.
16
+ * @throws {InvalidDurationError} when the string is malformed or out of range.
9
17
  */
10
18
  export declare function parseDuration(duration: string): number;
11
19
  //# sourceMappingURL=duration.parser.d.ts.map
@@ -1,31 +1,69 @@
1
1
  import { InvalidDurationError } from "../errors/scheduler.errors.js";
2
+ /**
3
+ * Largest duration that can safely be added to "now" and still produce a valid
4
+ * Date. Beyond this the result is an Invalid Date whose `getTime()` is `NaN`,
5
+ * and every heap comparison against `NaN` is false — so the entry never sinks
6
+ * and parks itself at the head of the queue, blocking everything behind it.
7
+ */
8
+ const MAX_DURATION_MS = 100 * 365 * 24 * 60 * 60 * 1000; // ~100 years
9
+ /** Milliseconds per supported unit. */
10
+ const UNIT_MS = {
11
+ ms: 1,
12
+ s: 1000,
13
+ m: 60 * 1000,
14
+ h: 60 * 60 * 1000,
15
+ d: 24 * 60 * 60 * 1000,
16
+ w: 7 * 24 * 60 * 60 * 1000,
17
+ };
2
18
  /**
3
19
  * Parses a human-readable duration string into milliseconds.
4
20
  *
5
- * Supported formats:
6
- * - "5s" - 5 seconds
7
- * - "10m" - 10 minutes
8
- * - "2h" - 2 hours
9
- * - "3d" - 3 days
21
+ * Supported units: `ms`, `s`, `m`, `h`, `d`, `w`. Components may be combined
22
+ * (`"1h30m"`), and each must be a non-negative integer. The total must be
23
+ * greater than zero — a zero duration would schedule a job that fires
24
+ * continuously — and within roughly a century.
25
+ *
26
+ * @example
27
+ * parseDuration("5s") // 5000
28
+ * parseDuration("1h30m") // 5400000
29
+ * parseDuration("250ms") // 250
30
+ *
31
+ * @param duration - The duration string.
32
+ * @returns The duration in milliseconds.
33
+ * @throws {InvalidDurationError} when the string is malformed or out of range.
10
34
  */
11
35
  export function parseDuration(duration) {
12
- const match = duration.match(/^(\d+)(s|m|h|d)$/);
13
- if (!match) {
36
+ const trimmed = duration.trim();
37
+ if (trimmed.length === 0) {
14
38
  throw new InvalidDurationError(duration);
15
39
  }
16
- const value = Number(match[1]);
17
- const unit = match[2];
18
- switch (unit) {
19
- case "s":
20
- return value * 1000;
21
- case "m":
22
- return value * 60 * 1000;
23
- case "h":
24
- return value * 60 * 60 * 1000;
25
- case "d":
26
- return value * 24 * 60 * 60 * 1000;
27
- default:
40
+ // Each component is digits followed by a unit. `ms` is matched before `m`.
41
+ // Sticky, not global: a failed component ends the loop instead of rescanning
42
+ // from the next offset, so a long digit run cannot be re-matched per offset.
43
+ // (`g` alongside `y` was redundant — sticky already wins for `exec`.)
44
+ const pattern = /(\d+)(ms|s|m|h|d|w)/y; // codeql[js/polynomial-redos]
45
+ let total = 0;
46
+ let matched = 0;
47
+ let match;
48
+ while ((match = pattern.exec(trimmed)) !== null) {
49
+ const amount = Number(match[1]);
50
+ const unit = match[2];
51
+ if (!Number.isSafeInteger(amount)) {
52
+ throw new InvalidDurationError(duration);
53
+ }
54
+ total += amount * UNIT_MS[unit];
55
+ matched = pattern.lastIndex;
56
+ if (total > MAX_DURATION_MS) {
28
57
  throw new InvalidDurationError(duration);
58
+ }
59
+ }
60
+ // Anything left over means the string was not fully consumed.
61
+ if (matched !== trimmed.length) {
62
+ throw new InvalidDurationError(duration);
63
+ }
64
+ if (total <= 0) {
65
+ throw new InvalidDurationError(duration);
29
66
  }
67
+ return total;
30
68
  }
31
69
  //# sourceMappingURL=duration.parser.js.map
@@ -1,3 +1,3 @@
1
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "./scheduler.errors.js";
1
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "./scheduler.errors.js";
2
2
  export type { SchedulerErrorOptions } from "./scheduler.errors.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "./scheduler.errors.js";
1
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "./scheduler.errors.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -4,7 +4,13 @@
4
4
  * Scheduler-specific error classes for the Zudojs framework.
5
5
  *
6
6
  * Re-exported from @zudojs/errors for convenience.
7
+ *
8
+ * Only the classes this package can actually raise are re-exported. The
9
+ * store, lock, not-started and schedule-lookup errors were re-exported here
10
+ * too, and nothing in an in-process scheduler with no persistence and no
11
+ * cross-process locking could ever throw one. They remain available from
12
+ * @zudojs/errors for a durable scheduler built on top.
7
13
  */
8
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "@zudojs/errors";
14
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "@zudojs/errors";
9
15
  export type { SchedulerErrorOptions } from "@zudojs/errors";
10
16
  //# sourceMappingURL=scheduler.errors.d.ts.map
@@ -4,6 +4,12 @@
4
4
  * Scheduler-specific error classes for the Zudojs framework.
5
5
  *
6
6
  * Re-exported from @zudojs/errors for convenience.
7
+ *
8
+ * Only the classes this package can actually raise are re-exported. The
9
+ * store, lock, not-started and schedule-lookup errors were re-exported here
10
+ * too, and nothing in an in-process scheduler with no persistence and no
11
+ * cross-process locking could ever throw one. They remain available from
12
+ * @zudojs/errors for a durable scheduler built on top.
7
13
  */
8
- export { SchedulerError, SchedulerNotStartedError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, ScheduleNotFoundError, ScheduleAlreadyExistsError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, SchedulerStoreError, SchedulerLockError, createSchedulerError, isSchedulerError, } from "@zudojs/errors";
14
+ export { SchedulerError, SchedulerAlreadyStartedError, SchedulerStoppedError, SchedulerJobNotFoundError, SchedulerJobAlreadyExistsError, InvalidJobError, InvalidScheduleError, CronParseError, InvalidDurationError, SchedulerJobExecutionError, SchedulerJobTimeoutError, SchedulerJobCancelledError, createSchedulerError, isSchedulerError, } from "@zudojs/errors";
9
15
  //# sourceMappingURL=scheduler.errors.js.map
@@ -1,2 +1,2 @@
1
- export { JobExecutor } from "./jobExecutor.core.js";
1
+ export { JobExecutor, retryDelay } from "./jobExecutor.core.js";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { JobExecutor } from "./jobExecutor.core.js";
1
+ export { JobExecutor, retryDelay } from "./jobExecutor.core.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -1,19 +1,39 @@
1
1
  import type { JobDefinition } from "../job/jobDefinition.type.js";
2
+ import type { RetryPolicy } from "../job/jobOptions.type.js";
2
3
  import type { JobExecutionResult } from "../types/schedulerTypes.core.js";
3
4
  import type { Clock } from "../clock/schedulerClock.type.js";
4
5
  /**
5
- * Executes a job with timeout support.
6
+ * Executes a job with timeout, cancellation and retry support.
6
7
  */
7
8
  export declare class JobExecutor {
8
9
  private readonly clock;
9
10
  constructor(clock: Clock);
10
11
  /**
11
- * Executes a job.
12
+ * Executes a job, retrying according to its retry policy.
13
+ *
14
+ * @param job - The job definition.
15
+ * @param executionId - Identifier for this execution.
16
+ * @param scheduledAt - The time the job was scheduled to run.
17
+ * @param attempt - The attempt number to start from (1-based).
18
+ * @param signal - Signal that aborts the job and stops further retries.
19
+ * @param data - Optional payload handed to the handler.
20
+ * @returns The execution result.
12
21
  */
13
- execute(job: JobDefinition, executionId: string, scheduledAt: Date, attempt: number, signal: AbortSignal): Promise<JobExecutionResult>;
22
+ execute(job: JobDefinition, executionId: string, scheduledAt: Date, attempt: number, signal: AbortSignal, data?: unknown): Promise<JobExecutionResult>;
23
+ /** Runs the handler once, under a timeout that also aborts it. */
24
+ private runOnce;
25
+ /** Maps a thrown value onto the scheduler's error taxonomy. */
26
+ private classify;
14
27
  /**
15
- * Wraps a promise with a timeout using AbortSignal.
28
+ * Wraps an operation with a timeout that aborts it.
16
29
  */
17
30
  private withTimeout;
18
31
  }
32
+ /**
33
+ * Computes the delay before the next retry attempt.
34
+ *
35
+ * `RetryPolicy` has carried strategy, delay, maxDelay and jitter since the
36
+ * package was written; none of it was ever applied.
37
+ */
38
+ export declare function retryDelay(policy: RetryPolicy | undefined, attempt: number): number;
19
39
  //# sourceMappingURL=jobExecutor.core.d.ts.map