3.0 KiB
expandMacro
(no submodules)
Purpose
expandMacro recognises the conventional crontab macro shortcuts —
@yearly/@annually, @monthly, @weekly, @daily/@midnight,
@hourly — and expands them to their canonical 5-field cron equivalents.
Macro expansion runs before field tokenization so the orchestrator only
has to know about the structural 5/6/7-field shape. The function is
deliberately conservative: it never silently rewrites non-macro input,
and it explicitly rejects @reboot (which has no wall-clock semantics in
a persistent process) with a typed CRON.UNSUPPORTED_SYNTAX error.
Features
- Seven supported macros mapped to their POSIX-compatible 5-field expansions.
- Case-insensitive (
@DAILYand@dailyare both accepted). - Whitespace-tolerant (leading and trailing whitespace stripped).
- Returns
nullfor non-macro inputs so the orchestrator can fall through to the standard field tokenizer. - Throws
CronError(CRON.UNSUPPORTED_SYNTAX)for@reboot. - Throws
CronError(CRON.INVALID_EXPRESSION)for unknown@shortcuts.
Usage: Basic Example
import { expandMacro } from "@suaveplan/cron";
console.log(expandMacro("@daily")); // "0 0 * * *"
console.log(expandMacro("@hourly")); // "0 * * * *"
console.log(expandMacro("0 * * * *")); // null — not a macro
Usage: Advanced Example
import { expandMacro, CRON_CODES, CronError } from "@suaveplan/cron";
function canonicalise(input: string): string {
try {
return expandMacro(input) ?? input;
} catch (err) {
if (err instanceof CronError && err.code === CRON_CODES.UNSUPPORTED_SYNTAX) {
// Persistent process — fall back to "every 24 hours".
return "0 0 * * *";
}
throw err;
}
}
console.log(canonicalise("@reboot")); // "0 0 * * *"
console.log(canonicalise("@weekly")); // "0 0 * * 0"
API Reference
expandMacro(expr: string): string | null— returns the 5-field expansion for a recognised macro,nullfor non-macro input, and throwsCronErrorfor unknown or unsupported@shortcuts.macroTable(): Readonly<Record<string, string>>— returns the frozen canonical lookup table used internally. Useful for documentation generation and admin UIs that render the supported macro set.
Implementation Notes
The macro table is Object.freezed at module load to guard against
accidental mutation. Lookup is by lower-cased trimmed key so the function
is O(1). @reboot is explicitly intercepted before the table lookup
because its semantics differ fundamentally from the other macros — it
fires at process start, not at a wall-clock instant, and is therefore not
representable as a 5-field expansion. Returning null for non-macro
input (rather than throwing) is deliberate so the orchestrator can
unconditionally invoke expandMacro before its own field tokenizer
without paying for exception construction on the hot path.