first commit
This commit is contained in:
78
docs/foundation/timing/cron/src/macros/macros.md
Normal file
78
docs/foundation/timing/cron/src/macros/macros.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# expandMacro
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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<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.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.
|
||||
Reference in New Issue
Block a user