|
1 | 1 | import { createHash } from "node:crypto"; |
| 2 | +import { resolveIntegerOption } from "./numeric-options.js"; |
| 3 | + |
| 4 | +function resolvePositiveIntervalMs(value: number): number { |
| 5 | +return resolveIntegerOption(value, 1, { min: 1 }); |
| 6 | +} |
2 | 7 | |
3 | 8 | function normalizeModulo(value: number, divisor: number) { |
4 | 9 | return ((value % divisor) + divisor) % divisor; |
@@ -9,7 +14,7 @@ export function resolveHeartbeatPhaseMs(params: {
|
9 | 14 | agentId: string; |
10 | 15 | intervalMs: number; |
11 | 16 | }) { |
12 | | -const intervalMs = Math.max(1, Math.floor(params.intervalMs)); |
| 17 | +const intervalMs = resolvePositiveIntervalMs(params.intervalMs); |
13 | 18 | const digest = createHash("sha256").update(`${params.schedulerSeed}:${params.agentId}`).digest(); |
14 | 19 | return digest.readUInt32BE(0) % intervalMs; |
15 | 20 | } |
@@ -19,9 +24,12 @@ export function computeNextHeartbeatPhaseDueMs(params: {
|
19 | 24 | intervalMs: number; |
20 | 25 | phaseMs: number; |
21 | 26 | }) { |
22 | | -const intervalMs = Math.max(1, Math.floor(params.intervalMs)); |
23 | | -const nowMs = Math.floor(params.nowMs); |
24 | | -const phaseMs = normalizeModulo(Math.floor(params.phaseMs), intervalMs); |
| 27 | +const intervalMs = resolvePositiveIntervalMs(params.intervalMs); |
| 28 | +const nowMs = Number.isFinite(params.nowMs) ? Math.floor(params.nowMs) : 0; |
| 29 | +const phaseMs = normalizeModulo( |
| 30 | +Number.isFinite(params.phaseMs) ? Math.floor(params.phaseMs) : 0, |
| 31 | +intervalMs, |
| 32 | +); |
25 | 33 | const cyclePositionMs = normalizeModulo(nowMs, intervalMs); |
26 | 34 | let deltaMs = normalizeModulo(phaseMs - cyclePositionMs, intervalMs); |
27 | 35 | if (deltaMs === 0) { |
@@ -40,8 +48,11 @@ export function resolveNextHeartbeatDueMs(params: {
|
40 | 48 | nextDueMs: number; |
41 | 49 | }; |
42 | 50 | }) { |
43 | | -const intervalMs = Math.max(1, Math.floor(params.intervalMs)); |
44 | | -const phaseMs = normalizeModulo(Math.floor(params.phaseMs), intervalMs); |
| 51 | +const intervalMs = resolvePositiveIntervalMs(params.intervalMs); |
| 52 | +const phaseMs = normalizeModulo( |
| 53 | +Number.isFinite(params.phaseMs) ? Math.floor(params.phaseMs) : 0, |
| 54 | +intervalMs, |
| 55 | +); |
45 | 56 | const prev = params.prev; |
46 | 57 | if ( |
47 | 58 | prev && |
@@ -81,7 +92,7 @@ export function seekNextActivePhaseDueMs(params: {
|
81 | 92 | if (!isActive) { |
82 | 93 | return params.startMs; |
83 | 94 | } |
84 | | -const intervalMs = Math.max(1, Math.floor(params.intervalMs)); |
| 95 | +const intervalMs = resolvePositiveIntervalMs(params.intervalMs); |
85 | 96 | const horizonMs = params.startMs + MAX_SEEK_HORIZON_MS; |
86 | 97 | let candidateMs = params.startMs; |
87 | 98 | let iterations = 0; |
|