@herdctl/core 0.0.1 → 0.0.2

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 (275) hide show
  1. package/dist/config/__tests__/agent.test.js +31 -13
  2. package/dist/config/__tests__/agent.test.js.map +1 -1
  3. package/dist/config/__tests__/merge.test.js +9 -2
  4. package/dist/config/__tests__/merge.test.js.map +1 -1
  5. package/dist/config/__tests__/schema.test.js +350 -1
  6. package/dist/config/__tests__/schema.test.js.map +1 -1
  7. package/dist/config/index.d.ts +1 -1
  8. package/dist/config/index.d.ts.map +1 -1
  9. package/dist/config/index.js +3 -1
  10. package/dist/config/index.js.map +1 -1
  11. package/dist/config/schema.d.ts +828 -24
  12. package/dist/config/schema.d.ts.map +1 -1
  13. package/dist/config/schema.js +118 -6
  14. package/dist/config/schema.js.map +1 -1
  15. package/dist/fleet-manager/__tests__/coverage.test.js +11 -332
  16. package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
  17. package/dist/fleet-manager/__tests__/errors.test.js +1 -49
  18. package/dist/fleet-manager/__tests__/errors.test.js.map +1 -1
  19. package/dist/fleet-manager/__tests__/integration.test.js +109 -0
  20. package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
  21. package/dist/fleet-manager/__tests__/reload.test.js +1 -1
  22. package/dist/fleet-manager/__tests__/reload.test.js.map +1 -1
  23. package/dist/fleet-manager/config-reload.d.ts +164 -0
  24. package/dist/fleet-manager/config-reload.d.ts.map +1 -0
  25. package/dist/fleet-manager/config-reload.js +445 -0
  26. package/dist/fleet-manager/config-reload.js.map +1 -0
  27. package/dist/fleet-manager/context.d.ts +76 -0
  28. package/dist/fleet-manager/context.d.ts.map +1 -0
  29. package/dist/fleet-manager/context.js +11 -0
  30. package/dist/fleet-manager/context.js.map +1 -0
  31. package/dist/fleet-manager/errors.d.ts +0 -25
  32. package/dist/fleet-manager/errors.d.ts.map +1 -1
  33. package/dist/fleet-manager/errors.js +0 -38
  34. package/dist/fleet-manager/errors.js.map +1 -1
  35. package/dist/fleet-manager/event-emitters.d.ts +123 -0
  36. package/dist/fleet-manager/event-emitters.d.ts.map +1 -0
  37. package/dist/fleet-manager/event-emitters.js +136 -0
  38. package/dist/fleet-manager/event-emitters.js.map +1 -0
  39. package/dist/fleet-manager/event-types.d.ts +0 -15
  40. package/dist/fleet-manager/event-types.d.ts.map +1 -1
  41. package/dist/fleet-manager/fleet-manager.d.ts +40 -653
  42. package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
  43. package/dist/fleet-manager/fleet-manager.js +95 -1720
  44. package/dist/fleet-manager/fleet-manager.js.map +1 -1
  45. package/dist/fleet-manager/index.d.ts +13 -2
  46. package/dist/fleet-manager/index.d.ts.map +1 -1
  47. package/dist/fleet-manager/index.js +19 -6
  48. package/dist/fleet-manager/index.js.map +1 -1
  49. package/dist/fleet-manager/job-control.d.ts +64 -0
  50. package/dist/fleet-manager/job-control.d.ts.map +1 -0
  51. package/dist/fleet-manager/job-control.js +296 -0
  52. package/dist/fleet-manager/job-control.js.map +1 -0
  53. package/dist/fleet-manager/log-streaming.d.ts +171 -0
  54. package/dist/fleet-manager/log-streaming.d.ts.map +1 -0
  55. package/dist/fleet-manager/log-streaming.js +503 -0
  56. package/dist/fleet-manager/log-streaming.js.map +1 -0
  57. package/dist/fleet-manager/schedule-executor.d.ts +63 -0
  58. package/dist/fleet-manager/schedule-executor.d.ts.map +1 -0
  59. package/dist/fleet-manager/schedule-executor.js +209 -0
  60. package/dist/fleet-manager/schedule-executor.js.map +1 -0
  61. package/dist/fleet-manager/schedule-management.d.ts +71 -0
  62. package/dist/fleet-manager/schedule-management.d.ts.map +1 -0
  63. package/dist/fleet-manager/schedule-management.js +171 -0
  64. package/dist/fleet-manager/schedule-management.js.map +1 -0
  65. package/dist/fleet-manager/status-queries.d.ts +105 -0
  66. package/dist/fleet-manager/status-queries.d.ts.map +1 -0
  67. package/dist/fleet-manager/status-queries.js +247 -0
  68. package/dist/fleet-manager/status-queries.js.map +1 -0
  69. package/dist/fleet-manager/types.d.ts +0 -39
  70. package/dist/fleet-manager/types.d.ts.map +1 -1
  71. package/dist/runner/__tests__/job-executor.test.js +206 -1
  72. package/dist/runner/__tests__/job-executor.test.js.map +1 -1
  73. package/dist/runner/job-executor.d.ts +9 -0
  74. package/dist/runner/job-executor.d.ts.map +1 -1
  75. package/dist/runner/job-executor.js +78 -4
  76. package/dist/runner/job-executor.js.map +1 -1
  77. package/dist/runner/types.d.ts +2 -0
  78. package/dist/runner/types.d.ts.map +1 -1
  79. package/dist/scheduler/__tests__/cron.test.d.ts +2 -0
  80. package/dist/scheduler/__tests__/cron.test.d.ts.map +1 -0
  81. package/dist/scheduler/__tests__/cron.test.js +867 -0
  82. package/dist/scheduler/__tests__/cron.test.js.map +1 -0
  83. package/dist/scheduler/__tests__/scheduler.test.js +164 -5
  84. package/dist/scheduler/__tests__/scheduler.test.js.map +1 -1
  85. package/dist/scheduler/cron.d.ts +126 -0
  86. package/dist/scheduler/cron.d.ts.map +1 -0
  87. package/dist/scheduler/cron.js +390 -0
  88. package/dist/scheduler/cron.js.map +1 -0
  89. package/dist/scheduler/errors.d.ts +81 -1
  90. package/dist/scheduler/errors.d.ts.map +1 -1
  91. package/dist/scheduler/errors.js +81 -6
  92. package/dist/scheduler/errors.js.map +1 -1
  93. package/dist/scheduler/index.d.ts +1 -0
  94. package/dist/scheduler/index.d.ts.map +1 -1
  95. package/dist/scheduler/index.js +2 -0
  96. package/dist/scheduler/index.js.map +1 -1
  97. package/dist/scheduler/schedule-runner.d.ts +2 -2
  98. package/dist/scheduler/schedule-runner.d.ts.map +1 -1
  99. package/dist/scheduler/schedule-runner.js +20 -8
  100. package/dist/scheduler/schedule-runner.js.map +1 -1
  101. package/dist/scheduler/scheduler.d.ts +4 -4
  102. package/dist/scheduler/scheduler.d.ts.map +1 -1
  103. package/dist/scheduler/scheduler.js +86 -20
  104. package/dist/scheduler/scheduler.js.map +1 -1
  105. package/dist/scheduler/types.d.ts +1 -1
  106. package/dist/scheduler/types.d.ts.map +1 -1
  107. package/dist/state/schemas/job-metadata.d.ts +2 -2
  108. package/package.json +33 -8
  109. package/.turbo/turbo-build.log +0 -4
  110. package/.turbo/turbo-test.log +0 -219
  111. package/.turbo/turbo-typecheck.log +0 -4
  112. package/coverage/base.css +0 -224
  113. package/coverage/block-navigation.js +0 -87
  114. package/coverage/coverage-final.json +0 -51
  115. package/coverage/favicon.png +0 -0
  116. package/coverage/index.html +0 -251
  117. package/coverage/prettify.css +0 -1
  118. package/coverage/prettify.js +0 -2
  119. package/coverage/sort-arrow-sprite.png +0 -0
  120. package/coverage/sorter.js +0 -210
  121. package/coverage/src/config/index.html +0 -191
  122. package/coverage/src/config/index.ts.html +0 -442
  123. package/coverage/src/config/interpolate.ts.html +0 -652
  124. package/coverage/src/config/loader.ts.html +0 -1501
  125. package/coverage/src/config/merge.ts.html +0 -823
  126. package/coverage/src/config/parser.ts.html +0 -1213
  127. package/coverage/src/config/schema.ts.html +0 -1123
  128. package/coverage/src/fleet-manager/errors.ts.html +0 -2326
  129. package/coverage/src/fleet-manager/event-types.ts.html +0 -1219
  130. package/coverage/src/fleet-manager/fleet-manager.ts.html +0 -7030
  131. package/coverage/src/fleet-manager/index.html +0 -206
  132. package/coverage/src/fleet-manager/index.ts.html +0 -469
  133. package/coverage/src/fleet-manager/job-manager.ts.html +0 -2074
  134. package/coverage/src/fleet-manager/job-queue.ts.html +0 -2479
  135. package/coverage/src/fleet-manager/types.ts.html +0 -2602
  136. package/coverage/src/index.html +0 -116
  137. package/coverage/src/index.ts.html +0 -181
  138. package/coverage/src/runner/errors.ts.html +0 -1006
  139. package/coverage/src/runner/index.html +0 -191
  140. package/coverage/src/runner/index.ts.html +0 -256
  141. package/coverage/src/runner/job-executor.ts.html +0 -1429
  142. package/coverage/src/runner/message-processor.ts.html +0 -1150
  143. package/coverage/src/runner/sdk-adapter.ts.html +0 -658
  144. package/coverage/src/runner/types.ts.html +0 -559
  145. package/coverage/src/scheduler/errors.ts.html +0 -388
  146. package/coverage/src/scheduler/index.html +0 -206
  147. package/coverage/src/scheduler/index.ts.html +0 -244
  148. package/coverage/src/scheduler/interval.ts.html +0 -652
  149. package/coverage/src/scheduler/schedule-runner.ts.html +0 -1411
  150. package/coverage/src/scheduler/schedule-state.ts.html +0 -718
  151. package/coverage/src/scheduler/scheduler.ts.html +0 -1795
  152. package/coverage/src/scheduler/types.ts.html +0 -733
  153. package/coverage/src/state/directory.ts.html +0 -736
  154. package/coverage/src/state/errors.ts.html +0 -376
  155. package/coverage/src/state/fleet-state.ts.html +0 -937
  156. package/coverage/src/state/index.html +0 -221
  157. package/coverage/src/state/index.ts.html +0 -322
  158. package/coverage/src/state/job-metadata.ts.html +0 -1420
  159. package/coverage/src/state/job-output.ts.html +0 -1033
  160. package/coverage/src/state/schemas/fleet-state.ts.html +0 -445
  161. package/coverage/src/state/schemas/index.html +0 -176
  162. package/coverage/src/state/schemas/index.ts.html +0 -286
  163. package/coverage/src/state/schemas/job-metadata.ts.html +0 -628
  164. package/coverage/src/state/schemas/job-output.ts.html +0 -616
  165. package/coverage/src/state/schemas/session-info.ts.html +0 -361
  166. package/coverage/src/state/session.ts.html +0 -844
  167. package/coverage/src/state/types.ts.html +0 -262
  168. package/coverage/src/state/utils/atomic.ts.html +0 -748
  169. package/coverage/src/state/utils/index.html +0 -146
  170. package/coverage/src/state/utils/index.ts.html +0 -103
  171. package/coverage/src/state/utils/reads.ts.html +0 -1621
  172. package/coverage/src/work-sources/adapters/github.ts.html +0 -3583
  173. package/coverage/src/work-sources/adapters/index.html +0 -131
  174. package/coverage/src/work-sources/adapters/index.ts.html +0 -277
  175. package/coverage/src/work-sources/errors.ts.html +0 -298
  176. package/coverage/src/work-sources/index.html +0 -176
  177. package/coverage/src/work-sources/index.ts.html +0 -529
  178. package/coverage/src/work-sources/manager.ts.html +0 -1324
  179. package/coverage/src/work-sources/registry.ts.html +0 -619
  180. package/coverage/src/work-sources/types.ts.html +0 -568
  181. package/dist/fleet-manager/__tests__/event-helpers.test.d.ts +0 -7
  182. package/dist/fleet-manager/__tests__/event-helpers.test.d.ts.map +0 -1
  183. package/dist/fleet-manager/__tests__/event-helpers.test.js +0 -368
  184. package/dist/fleet-manager/__tests__/event-helpers.test.js.map +0 -1
  185. package/src/config/__tests__/agent.test.ts +0 -864
  186. package/src/config/__tests__/interpolate.test.ts +0 -644
  187. package/src/config/__tests__/loader.test.ts +0 -784
  188. package/src/config/__tests__/merge.test.ts +0 -751
  189. package/src/config/__tests__/parser.test.ts +0 -533
  190. package/src/config/__tests__/schema.test.ts +0 -873
  191. package/src/config/index.ts +0 -119
  192. package/src/config/interpolate.ts +0 -189
  193. package/src/config/loader.ts +0 -472
  194. package/src/config/merge.ts +0 -246
  195. package/src/config/parser.ts +0 -376
  196. package/src/config/schema.ts +0 -346
  197. package/src/fleet-manager/__tests__/coverage.test.ts +0 -2869
  198. package/src/fleet-manager/__tests__/errors.test.ts +0 -660
  199. package/src/fleet-manager/__tests__/event-helpers.test.ts +0 -448
  200. package/src/fleet-manager/__tests__/integration.test.ts +0 -1209
  201. package/src/fleet-manager/__tests__/job-control.test.ts +0 -283
  202. package/src/fleet-manager/__tests__/job-manager.test.ts +0 -869
  203. package/src/fleet-manager/__tests__/job-queue.test.ts +0 -401
  204. package/src/fleet-manager/__tests__/reload.test.ts +0 -751
  205. package/src/fleet-manager/__tests__/status-queries.test.ts +0 -595
  206. package/src/fleet-manager/__tests__/trigger.test.ts +0 -601
  207. package/src/fleet-manager/errors.ts +0 -747
  208. package/src/fleet-manager/event-types.ts +0 -378
  209. package/src/fleet-manager/fleet-manager.ts +0 -2315
  210. package/src/fleet-manager/index.ts +0 -128
  211. package/src/fleet-manager/job-manager.ts +0 -663
  212. package/src/fleet-manager/job-queue.ts +0 -798
  213. package/src/fleet-manager/types.ts +0 -839
  214. package/src/index.ts +0 -32
  215. package/src/runner/__tests__/errors.test.ts +0 -382
  216. package/src/runner/__tests__/job-executor.test.ts +0 -1708
  217. package/src/runner/__tests__/message-processor.test.ts +0 -960
  218. package/src/runner/__tests__/sdk-adapter.test.ts +0 -626
  219. package/src/runner/errors.ts +0 -307
  220. package/src/runner/index.ts +0 -57
  221. package/src/runner/job-executor.ts +0 -448
  222. package/src/runner/message-processor.ts +0 -355
  223. package/src/runner/sdk-adapter.ts +0 -191
  224. package/src/runner/types.ts +0 -158
  225. package/src/scheduler/__tests__/errors.test.ts +0 -159
  226. package/src/scheduler/__tests__/interval.test.ts +0 -515
  227. package/src/scheduler/__tests__/schedule-runner.test.ts +0 -798
  228. package/src/scheduler/__tests__/schedule-state.test.ts +0 -671
  229. package/src/scheduler/__tests__/scheduler.test.ts +0 -1280
  230. package/src/scheduler/errors.ts +0 -101
  231. package/src/scheduler/index.ts +0 -53
  232. package/src/scheduler/interval.ts +0 -189
  233. package/src/scheduler/schedule-runner.ts +0 -442
  234. package/src/scheduler/schedule-state.ts +0 -211
  235. package/src/scheduler/scheduler.ts +0 -570
  236. package/src/scheduler/types.ts +0 -216
  237. package/src/state/__tests__/directory.test.ts +0 -595
  238. package/src/state/__tests__/fleet-state.test.ts +0 -868
  239. package/src/state/__tests__/job-metadata-schema.test.ts +0 -414
  240. package/src/state/__tests__/job-metadata.test.ts +0 -831
  241. package/src/state/__tests__/job-output.test.ts +0 -856
  242. package/src/state/__tests__/session-schema.test.ts +0 -378
  243. package/src/state/__tests__/session.test.ts +0 -604
  244. package/src/state/directory.ts +0 -217
  245. package/src/state/errors.ts +0 -97
  246. package/src/state/fleet-state.ts +0 -284
  247. package/src/state/index.ts +0 -79
  248. package/src/state/job-metadata.ts +0 -445
  249. package/src/state/job-output.ts +0 -316
  250. package/src/state/schemas/__tests__/job-output.test.ts +0 -338
  251. package/src/state/schemas/fleet-state.ts +0 -120
  252. package/src/state/schemas/index.ts +0 -67
  253. package/src/state/schemas/job-metadata.ts +0 -181
  254. package/src/state/schemas/job-output.ts +0 -177
  255. package/src/state/schemas/session-info.ts +0 -92
  256. package/src/state/session.ts +0 -253
  257. package/src/state/types.ts +0 -59
  258. package/src/state/utils/__tests__/atomic.test.ts +0 -723
  259. package/src/state/utils/__tests__/reads.test.ts +0 -1071
  260. package/src/state/utils/atomic.ts +0 -221
  261. package/src/state/utils/index.ts +0 -6
  262. package/src/state/utils/reads.ts +0 -512
  263. package/src/work-sources/__tests__/github.test.ts +0 -1800
  264. package/src/work-sources/__tests__/manager.test.ts +0 -529
  265. package/src/work-sources/__tests__/registry.test.ts +0 -477
  266. package/src/work-sources/__tests__/types.test.ts +0 -479
  267. package/src/work-sources/adapters/github.ts +0 -1166
  268. package/src/work-sources/adapters/index.ts +0 -64
  269. package/src/work-sources/errors.ts +0 -71
  270. package/src/work-sources/index.ts +0 -148
  271. package/src/work-sources/manager.ts +0 -413
  272. package/src/work-sources/registry.ts +0 -178
  273. package/src/work-sources/types.ts +0 -161
  274. package/tsconfig.json +0 -9
  275. package/vitest.config.ts +0 -19
@@ -1,101 +0,0 @@
1
- /**
2
- * Error classes for scheduler module
3
- *
4
- * Provides typed errors with descriptive messages for scheduler operations.
5
- */
6
-
7
- // =============================================================================
8
- // Base Error Class
9
- // =============================================================================
10
-
11
- /**
12
- * Base error class for all scheduler errors
13
- */
14
- export class SchedulerError extends Error {
15
- constructor(message: string, options?: { cause?: Error }) {
16
- super(message);
17
- this.name = "SchedulerError";
18
- this.cause = options?.cause;
19
- }
20
- }
21
-
22
- // =============================================================================
23
- // Interval Parse Errors
24
- // =============================================================================
25
-
26
- /**
27
- * Error thrown when an interval string cannot be parsed
28
- *
29
- * This error provides detailed information about what went wrong during parsing,
30
- * including the invalid input and suggestions for valid formats.
31
- */
32
- export class IntervalParseError extends SchedulerError {
33
- /** The original input string that failed to parse */
34
- public readonly input: string;
35
-
36
- constructor(message: string, input: string, options?: { cause?: Error }) {
37
- super(message, options);
38
- this.name = "IntervalParseError";
39
- this.input = input;
40
- }
41
- }
42
-
43
- // =============================================================================
44
- // Schedule Trigger Errors
45
- // =============================================================================
46
-
47
- /**
48
- * Error thrown when a schedule trigger fails during execution
49
- *
50
- * This error wraps the underlying cause and provides context about which
51
- * agent and schedule encountered the error. It is used internally by the
52
- * Scheduler to capture and report trigger failures while allowing the
53
- * scheduler to continue processing other schedules.
54
- */
55
- export class ScheduleTriggerError extends SchedulerError {
56
- /** The name of the agent that owns the schedule */
57
- public readonly agentName: string;
58
-
59
- /** The name of the schedule that failed */
60
- public readonly scheduleName: string;
61
-
62
- constructor(
63
- message: string,
64
- agentName: string,
65
- scheduleName: string,
66
- options?: { cause?: Error }
67
- ) {
68
- super(message, options);
69
- this.name = "ScheduleTriggerError";
70
- this.agentName = agentName;
71
- this.scheduleName = scheduleName;
72
- }
73
- }
74
-
75
- // =============================================================================
76
- // Scheduler Shutdown Errors
77
- // =============================================================================
78
-
79
- /**
80
- * Error thrown when scheduler shutdown encounters issues
81
- *
82
- * This error is thrown when the scheduler cannot shut down cleanly,
83
- * typically due to running jobs not completing within the configured timeout.
84
- */
85
- export class SchedulerShutdownError extends SchedulerError {
86
- /** Whether the shutdown timed out waiting for jobs to complete */
87
- public readonly timedOut: boolean;
88
-
89
- /** Number of jobs that were still running when shutdown completed/timed out */
90
- public readonly runningJobCount: number;
91
-
92
- constructor(
93
- message: string,
94
- options: { timedOut: boolean; runningJobCount: number; cause?: Error }
95
- ) {
96
- super(message, { cause: options.cause });
97
- this.name = "SchedulerShutdownError";
98
- this.timedOut = options.timedOut;
99
- this.runningJobCount = options.runningJobCount;
100
- }
101
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Scheduler module for herdctl
3
- *
4
- * Provides interval parsing, scheduling utilities, and the Scheduler class
5
- * for agent fleet management.
6
- */
7
-
8
- // Errors
9
- export * from "./errors.js";
10
-
11
- // Interval parsing and scheduling
12
- export {
13
- parseInterval,
14
- calculateNextTrigger,
15
- isScheduleDue,
16
- } from "./interval.js";
17
-
18
- // Schedule state management
19
- export {
20
- getScheduleState,
21
- updateScheduleState,
22
- getAgentScheduleStates,
23
- type ScheduleStateLogger,
24
- type ScheduleStateOptions,
25
- type ScheduleStateUpdates,
26
- } from "./schedule-state.js";
27
-
28
- // Scheduler types
29
- export type {
30
- SchedulerOptions,
31
- SchedulerStatus,
32
- SchedulerState,
33
- SchedulerLogger,
34
- ScheduleCheckResult,
35
- ScheduleSkipReason,
36
- TriggerInfo,
37
- SchedulerTriggerCallback,
38
- AgentScheduleInfo,
39
- StopOptions,
40
- } from "./types.js";
41
-
42
- // Scheduler class
43
- export { Scheduler } from "./scheduler.js";
44
-
45
- // Schedule runner
46
- export {
47
- runSchedule,
48
- buildSchedulePrompt,
49
- type RunScheduleOptions,
50
- type ScheduleRunResult,
51
- type ScheduleRunnerLogger,
52
- type TriggerMetadata,
53
- } from "./schedule-runner.js";
@@ -1,189 +0,0 @@
1
- /**
2
- * Interval parsing utilities for the scheduler module
3
- *
4
- * Parses human-readable interval strings like "5m", "1h", "30s" into milliseconds.
5
- */
6
-
7
- import { IntervalParseError } from "./errors.js";
8
-
9
- /**
10
- * Multipliers to convert time units to milliseconds
11
- */
12
- const UNIT_MULTIPLIERS: Record<string, number> = {
13
- s: 1000, // seconds
14
- m: 60 * 1000, // minutes
15
- h: 60 * 60 * 1000, // hours
16
- d: 24 * 60 * 60 * 1000, // days
17
- };
18
-
19
- /**
20
- * Valid time units
21
- */
22
- const VALID_UNITS = Object.keys(UNIT_MULTIPLIERS);
23
-
24
- /**
25
- * Parse an interval string into milliseconds
26
- *
27
- * Supports the format `{number}{unit}` where:
28
- * - `number` is a positive integer
29
- * - `unit` is one of: s (seconds), m (minutes), h (hours), d (days)
30
- *
31
- * @param interval - The interval string to parse (e.g., "5m", "1h", "30s", "1d")
32
- * @returns The interval in milliseconds
33
- * @throws {IntervalParseError} If the interval string is invalid
34
- *
35
- * @example
36
- * parseInterval("5s") // returns 5000
37
- * parseInterval("5m") // returns 300000
38
- * parseInterval("1h") // returns 3600000
39
- * parseInterval("1d") // returns 86400000
40
- */
41
- export function parseInterval(interval: string): number {
42
- // Handle empty string
43
- if (!interval || interval.trim() === "") {
44
- throw new IntervalParseError(
45
- 'Interval cannot be empty. Expected format: "{number}{unit}" where unit is s/m/h/d (e.g., "5m", "1h")',
46
- interval
47
- );
48
- }
49
-
50
- const trimmed = interval.trim();
51
-
52
- // Match the pattern: optional whitespace, digits, optional whitespace, unit letter
53
- const match = trimmed.match(/^(-?\d+)\s*([a-zA-Z]+)$/);
54
-
55
- if (!match) {
56
- // Check for common error patterns to provide better error messages
57
- if (/^\d+$/.test(trimmed)) {
58
- throw new IntervalParseError(
59
- `Missing time unit in interval "${interval}". Expected format: "{number}{unit}" where unit is s/m/h/d (e.g., "5m", "1h")`,
60
- interval
61
- );
62
- }
63
-
64
- if (/^[a-zA-Z]+$/.test(trimmed)) {
65
- throw new IntervalParseError(
66
- `Missing numeric value in interval "${interval}". Expected format: "{number}{unit}" where unit is s/m/h/d (e.g., "5m", "1h")`,
67
- interval
68
- );
69
- }
70
-
71
- if (/\d+\.\d+/.test(trimmed)) {
72
- throw new IntervalParseError(
73
- `Decimal values are not supported in interval "${interval}". Use integers only (e.g., "5m" instead of "5.5m")`,
74
- interval
75
- );
76
- }
77
-
78
- throw new IntervalParseError(
79
- `Invalid interval format "${interval}". Expected format: "{number}{unit}" where unit is s/m/h/d (e.g., "5m", "1h")`,
80
- interval
81
- );
82
- }
83
-
84
- const [, valueStr, unit] = match;
85
- const value = parseInt(valueStr, 10);
86
- const normalizedUnit = unit.toLowerCase();
87
-
88
- // Check for negative numbers
89
- if (value < 0) {
90
- throw new IntervalParseError(
91
- `Negative intervals are not allowed: "${interval}". Use a positive integer value`,
92
- interval
93
- );
94
- }
95
-
96
- // Check for zero
97
- if (value === 0) {
98
- throw new IntervalParseError(
99
- `Zero interval is not allowed: "${interval}". Use a positive integer value`,
100
- interval
101
- );
102
- }
103
-
104
- // Check for valid unit
105
- if (!VALID_UNITS.includes(normalizedUnit)) {
106
- throw new IntervalParseError(
107
- `Invalid time unit "${unit}" in interval "${interval}". Valid units are: s (seconds), m (minutes), h (hours), d (days)`,
108
- interval
109
- );
110
- }
111
-
112
- return value * UNIT_MULTIPLIERS[normalizedUnit];
113
- }
114
-
115
- /**
116
- * Calculate the next trigger time for a schedule
117
- *
118
- * @param lastCompletedAt - When the schedule last completed, or null for first run
119
- * @param interval - The interval string (e.g., "5m", "1h")
120
- * @param jitterPercent - Optional jitter percentage (0-10) to add randomness and prevent thundering herd
121
- * @returns The next trigger time as a Date
122
- *
123
- * @example
124
- * // First run - triggers immediately
125
- * calculateNextTrigger(null, "5m") // returns now
126
- *
127
- * // Subsequent run - triggers after interval
128
- * calculateNextTrigger(new Date("2024-01-01T00:00:00Z"), "5m")
129
- * // returns 2024-01-01T00:05:00Z (plus optional jitter)
130
- *
131
- * // With jitter to prevent thundering herd
132
- * calculateNextTrigger(lastRun, "1h", 5) // adds 0-5% jitter to interval
133
- */
134
- export function calculateNextTrigger(
135
- lastCompletedAt: Date | null,
136
- interval: string,
137
- jitterPercent?: number
138
- ): Date {
139
- const now = new Date();
140
-
141
- // If no previous run, trigger immediately
142
- if (lastCompletedAt === null) {
143
- return now;
144
- }
145
-
146
- const intervalMs = parseInterval(interval);
147
-
148
- // Calculate jitter if specified
149
- let jitterMs = 0;
150
- if (jitterPercent !== undefined && jitterPercent > 0) {
151
- // Clamp jitter to 0-10%
152
- const clampedJitter = Math.min(Math.max(jitterPercent, 0), 10);
153
- // Random jitter between 0 and clampedJitter% of interval
154
- jitterMs = Math.floor(Math.random() * (intervalMs * clampedJitter) / 100);
155
- }
156
-
157
- const nextTrigger = new Date(lastCompletedAt.getTime() + intervalMs + jitterMs);
158
-
159
- // Handle clock skew: if next trigger is in the past, trigger now
160
- if (nextTrigger.getTime() < now.getTime()) {
161
- return now;
162
- }
163
-
164
- return nextTrigger;
165
- }
166
-
167
- /**
168
- * Check if a schedule is due to run
169
- *
170
- * @param nextRunAt - The scheduled next run time
171
- * @param now - Optional current time (defaults to new Date())
172
- * @returns true if the schedule is due (nextRunAt <= now)
173
- *
174
- * @example
175
- * const nextRun = new Date("2024-01-01T00:05:00Z");
176
- *
177
- * // Before scheduled time
178
- * isScheduleDue(nextRun, new Date("2024-01-01T00:04:00Z")) // false
179
- *
180
- * // At scheduled time
181
- * isScheduleDue(nextRun, new Date("2024-01-01T00:05:00Z")) // true
182
- *
183
- * // After scheduled time
184
- * isScheduleDue(nextRun, new Date("2024-01-01T00:06:00Z")) // true
185
- */
186
- export function isScheduleDue(nextRunAt: Date, now?: Date): boolean {
187
- const currentTime = now ?? new Date();
188
- return nextRunAt.getTime() <= currentTime.getTime();
189
- }