first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View 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.