/** * Runs the server's recurring tasks. Each module that needs one exports a * {@link Job}, and `main.ts` passes them all to {@link startJobs} when it runs * directly, so nothing recurs in the tests, which call a job's `run` when * they need it. * * @module */ /** A recurring task, exported by its module for `main.ts` to start. */ export interface Job { /** Names it in the log, as `[digest]`. */ name: string; /** How long from one run's start to the next. */ everyMs: number; /** How long after the start the first run waits, `everyMs` by default. */ firstAfterMs?: number; /** Logs what it did itself, if anything; its errors are logged here. */ run(): void | Promise; } /** * Starts running each of `jobs` on its own timer. A run still going when the * next is due makes that one skip, and a run that throws or rejects is logged * and the next one runs anyway. */ export function startJobs(jobs: readonly Job[]): void { for (const job of jobs) { let running = false; const tick = async () => { if (running) return; running = true; try { await job.run(); } catch (err) { console.error(`[${job.name}] run failed:`, err); } finally { running = false; } }; setTimeout(() => { tick(); setInterval(tick, job.everyMs); }, job.firstAfterMs ?? job.everyMs); } }