| import fs from "node:fs"; |
| import { normalizeStoredCronJobs } from "../store-migration.js"; |
| import { loadCronStore, saveCronStore } from "../store.js"; |
| import type { CronJob } from "../types.js"; |
| import { recomputeNextRuns } from "./jobs.js"; |
| import type { CronServiceState } from "./state.js"; |
|
|
| async function getFileMtimeMs(path: string): Promise<number | null> { |
| try { |
| const stats = await fs.promises.stat(path); |
| return stats.mtimeMs; |
| } catch { |
| return null; |
| } |
| } |
|
|
| export async function ensureLoaded( |
| state: CronServiceState, |
| opts?: { |
| forceReload?: boolean; |
| /** Skip recomputing nextRunAtMs after load so the caller can run due |
| * jobs against the persisted values first (see onTimer). */ |
| skipRecompute?: boolean; |
| }, |
| ) { |
| |
| |
| if (state.store && !opts?.forceReload) { |
| return; |
| } |
| |
| |
|
|
| const fileMtimeMs = await getFileMtimeMs(state.deps.storePath); |
| const loaded = await loadCronStore(state.deps.storePath); |
| const jobs = (loaded.jobs ?? []) as unknown as Array<Record<string, unknown>>; |
| const { mutated } = normalizeStoredCronJobs(jobs); |
| state.store = { version: 1, jobs: jobs as unknown as CronJob[] }; |
| state.storeLoadedAtMs = state.deps.nowMs(); |
| state.storeFileMtimeMs = fileMtimeMs; |
|
|
| if (!opts?.skipRecompute) { |
| recomputeNextRuns(state); |
| } |
|
|
| if (mutated) { |
| await persist(state, { skipBackup: true }); |
| } |
| } |
|
|
| export function warnIfDisabled(state: CronServiceState, action: string) { |
| if (state.deps.cronEnabled) { |
| return; |
| } |
| if (state.warnedDisabled) { |
| return; |
| } |
| state.warnedDisabled = true; |
| state.deps.log.warn( |
| { enabled: false, action, storePath: state.deps.storePath }, |
| "cron: scheduler disabled; jobs will not run automatically", |
| ); |
| } |
|
|
| export async function persist(state: CronServiceState, opts?: { skipBackup?: boolean }) { |
| if (!state.store) { |
| return; |
| } |
| await saveCronStore(state.deps.storePath, state.store, opts); |
| |
| state.storeFileMtimeMs = await getFileMtimeMs(state.deps.storePath); |
| } |
|
|