// Pure conversion helpers behind the editor's DurationField control. // // Every cadence in the app is stored as a bare integer in one fixed unit — // minutes for the sync/sweep/backoff intervals, seconds for the heartbeat. That // keeps settings.json and every existing validator simple, but it means an // operator reads `10080` and has to do the arithmetic to learn it means weekly. // DurationField shows the same number as an amount + unit pair; these helpers do // the conversion both ways, and are kept here (rather than beside the component) // because `lib/*.test.ts` is what the common package's unit-test glob picks up. // // The invariant that matters: splitDuration only ever picks a unit that divides // the stored value EXACTLY, so a hand-edited `137` round-trips as "137 minutes" // instead of being silently rounded to "2 hours". export type DurationUnit = "seconds" | "minutes" | "hours" | "days" | "weeks"; // Posted by DurationField's "Leave unchanged" mode, and understood by the // actions that read it. A distinct token rather than "", because "" already // means "clear this key and inherit the global default" — a bulk edit that // skips a field must be distinguishable from one that resets it. Lives here // rather than beside the component so a server action can import it without // crossing the "use client" boundary. export const DURATION_KEEP = "keep"; // Seconds per unit. Ordered smallest-first; the order is load-bearing for // splitDuration, which walks it backwards to find the largest even divisor. const UNIT_SECONDS: Record = { seconds: 1, minutes: 60, hours: 3600, days: 86400, weeks: 604800, }; const UNIT_ORDER: DurationUnit[] = [ "seconds", "minutes", "hours", "days", "weeks", ]; export function isDurationUnit(value: unknown): value is DurationUnit { return typeof value === "string" && value in UNIT_SECONDS; } // Label for a unit, pluralised for `amount`. Used by both the unit