# 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 (`@DAILY` and `@daily` are both accepted). - Whitespace-tolerant (leading and trailing whitespace stripped). - Returns `null` for 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 ```typescript 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 ```typescript 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, `null` for non-macro input, and throws `CronError` for unknown or unsupported `@` shortcuts. - `macroTable(): Readonly>` — 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.freeze`d 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.