first commit
This commit is contained in:
86
docs/foundation/timing/cron/src/parse/parse-cron.md
Normal file
86
docs/foundation/timing/cron/src/parse/parse-cron.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# parseCron
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`parseCron` is the top-level orchestrator that consumes a raw cron
|
||||
expression string and returns a frozen `CronExpression` whose `nextRun`,
|
||||
`nextRuns`, `previousRun`, and `matches` methods drive every scheduling
|
||||
query. The orchestrator macro-expands the input first, then dispatches
|
||||
the resulting tokens through `parseField` per position, applies optional
|
||||
Vixie strict-DoW semantics, and finally binds the result to the search
|
||||
engine in `next-run.ts`. `isValidCron` is the non-throwing sibling: it
|
||||
returns a boolean and guarantees no exception escape for any string
|
||||
input, as mandated by the spec.
|
||||
|
||||
## Features
|
||||
|
||||
- 5-field default; 6-field opt-in via `seconds: true`; 7-field Quartz
|
||||
opt-in via both `seconds` and `years` true.
|
||||
- Macro expansion (`@daily`, `@hourly`, ...) handled before tokenization.
|
||||
- `strictDoW` option enforces "either dom or dow, never both" Vixie
|
||||
semantics.
|
||||
- `nextRuns(count)` produces a strictly monotonic array.
|
||||
- `matches(when, tz)` is a constant-time membership check against the
|
||||
parsed field sets.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { parseCron, isValidCron } from "@suaveplan/cron";
|
||||
|
||||
const expr = parseCron("*/15 * * * *");
|
||||
console.log(expr.fields.minutes); // [0, 15, 30, 45]
|
||||
console.log(expr.nextRun(Date.UTC(2026, 4, 11, 10, 7, 0), "UTC"));
|
||||
// Date.UTC(2026, 4, 11, 10, 15, 0)
|
||||
|
||||
console.log(isValidCron("not a cron expression")); // false
|
||||
console.log(isValidCron("0 9 * * MON-FRI")); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { parseCron } from "@suaveplan/cron";
|
||||
|
||||
const quartz = parseCron("0 0 12 * * MON-FRI 2026-2030", {
|
||||
seconds: true,
|
||||
years: true,
|
||||
});
|
||||
const runs = quartz.nextRuns(3, Date.UTC(2026, 0, 1), "America/New_York");
|
||||
console.log(runs.map((t) => new Date(t).toISOString()));
|
||||
|
||||
const strict = parseCron("0 12 15 * 1", { strictDoW: true });
|
||||
// Rejects expressions where dom AND dow are both restricted.
|
||||
console.log(strict.matches(Date.UTC(2026, 0, 15, 12, 0, 0), "UTC"));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `parseCron(expr: string, options?: ParseOptions): CronExpression` —
|
||||
parses and returns the frozen expression handle.
|
||||
- `isValidCron(expr: string, options?: ParseOptions): boolean` — never
|
||||
throws.
|
||||
- `interface CronExpression { expr; fields; nextRun; nextRuns;
|
||||
previousRun; matches }`.
|
||||
- `interface ParseOptions { seconds?; years?; strictDoW? }`.
|
||||
- `interface CronFields { seconds?; minutes; hours; daysOfMonth; months;
|
||||
daysOfWeek; years? }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The orchestrator first invokes `expandMacro` so the rest of the pipeline
|
||||
only ever sees canonical 5/6/7-field strings. Tokenization is whitespace-
|
||||
based with the field count strictly validated against `ParseOptions`.
|
||||
Each field is independently parsed via the shared `parseField`
|
||||
tokenizer, which returns a sorted, deduplicated `readonly number[]`.
|
||||
`strictDoW` enforcement runs after tokenization so the error context
|
||||
carries the actual parsed sets for debug visibility. The returned
|
||||
`CronExpression` is `Object.freeze`d and binds its methods over the
|
||||
captured fields, so destructuring (`const { nextRun } = parseCron(...)`)
|
||||
is safe. `isValidCron` is a tight try/catch around the same code path so
|
||||
the two functions stay in lock-step with no semantic drift. All parse
|
||||
calls flow through `recordParse` so an outcome attribute lands on every
|
||||
invocation regardless of success or failure.
|
||||
Reference in New Issue
Block a user