You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
2.1 KiB
TypeScript

/**
* Resolve a schedule interval in milliseconds.
*
* Priority:
* 1. env var if set and valid
* 2. fallbackMs default
*
* In development mode, enforces a minimum of 10s to avoid accidental API spam.
*/
export function resolveInterval(
envVar: string,
fallbackMs: number
): number {
const raw = Number(process.env[envVar]);
let interval = Number.isFinite(raw) && raw > 0 ? raw : fallbackMs;
if (process.env.NODE_ENV === 'development' && interval < 10_000) {
console.warn(
`Sync interval from ${envVar} too low (${interval}ms), using 10s minimum`
);
interval = 10_000;
}
return interval;
}
// ---------------------------------------------------------------------------
// Scheduler state holders (mutable, per-scheduler)
// ---------------------------------------------------------------------------
export interface SchedulerState {
running: boolean;
timer: ReturnType<typeof setInterval> | null;
intervalMs: number | null;
nextTickAt: number | null;
}
export function createSchedulerState(): SchedulerState {
return { running: false, timer: null, intervalMs: null, nextTickAt: null };
}
export function startScheduler(
state: SchedulerState,
intervalMs: number,
task: () => Promise<void>,
label: string
): void {
if (process.env.NODE_ENV === 'test') return;
if (state.running) return;
state.running = true;
state.intervalMs = intervalMs;
state.nextTickAt = Date.now() + intervalMs;
state.timer = setInterval(() => {
state.nextTickAt = Date.now() + (state.intervalMs ?? intervalMs);
task().catch(err =>
console.error(`${label} scheduler run error:`, err)
);
}, intervalMs);
}
export function stopScheduler(state: SchedulerState): void {
state.running = false;
if (state.timer) {
clearInterval(state.timer);
state.timer = null;
}
state.intervalMs = null;
state.nextTickAt = null;
}
export function secondsUntilNextTick(
state: SchedulerState,
nowMs: number = Date.now()
): number | null {
const next = state.nextTickAt;
if (next == null) return null;
const secs = (next - nowMs) / 1000;
return secs > 0 ? secs : 0;
}