Files
cobble/src/utils/next-cron-run.ts
T
tbenandClaude Opus 5.5 c44048dbab Add bot schedules (routines), open Activity, add Help and a Desktop computer view
Bots create, list, update and delete schedules (cron_jobs, Bun.cron in UTC);
a per-minute cron queues due runs as tasks. Routines, Activity (super admins)
and Help get their own rail aside. Help covers familiar problems, starting
with signing in to Google through Cookie-Editor over the new Desktop (noVNC)
view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 10:52:32 +01:00

51 lines
1.6 KiB
TypeScript

import { AppData } from "@/src/data/app-data";
import AppError from "./app-error";
type Params = {
/**
* A Bun.cron expression: 5 fields or a nickname like `@daily`, in UTC.
*/
schedule: string;
/**
* Unix ms to look from (the next run is after it). Defaults to now.
*/
from?: number;
/**
* Refuse schedules that run more often than
* `AppData.CronJobMinIntervalMs`. Set when a schedule is saved.
*/
check_interval?: boolean;
};
/**
* The next run of a schedule after `from`, in unix ms, or null when it
* never runs again. Throws an `AppError` for an invalid expression (or,
* with `check_interval`, one that runs too often).
*/
export default function nextCronRun({ schedule, from = Date.now(), check_interval }: Params): number | null {
const expression = schedule.trim() as Bun.CronWithAutocomplete;
let next: Date | null;
try {
next = Bun.cron.parse(expression, from);
} catch {
throw new AppError(
`"${schedule}" isn't a valid schedule. Use 5 cron fields (minute hour day month weekday, e.g. "0 9 * * MON-FRI") or a nickname like @daily.`,
);
}
if (!next) return null;
if (check_interval) {
const after = Bun.cron.parse(expression, next);
if (after && after.getTime() - next.getTime() < AppData.CronJobMinIntervalMs) {
throw new AppError(
`"${schedule}" runs too often. Leave at least ${Math.round(AppData.CronJobMinIntervalMs / 60000)} minutes between runs.`,
);
}
}
return next.getTime();
}