first commit
This commit is contained in:
89
docs/foundation/timing/cron/src/fields/cron-fields.md
Normal file
89
docs/foundation/timing/cron/src/fields/cron-fields.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# parseField
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`parseField` is the single-field tokenizer at the heart of cron parsing.
|
||||
Each of the five (or six/seven) whitespace-separated positions in a cron
|
||||
expression is independently expanded into the sorted, unique numeric set
|
||||
of values it matches. The function is total over its `FieldKind`
|
||||
discriminator: it accepts the canonical crontab syntax surface (literals,
|
||||
ranges, lists, steps, wildcards, and alphabetic aliases for month +
|
||||
day-of-week) and rejects every Quartz extension (`L`/`W`/`#`/`?`) with
|
||||
typed errors so the caller can branch on `err.code`.
|
||||
|
||||
## Features
|
||||
|
||||
- Wildcards (`*`), literals, comma-separated lists, hyphenated ranges,
|
||||
step values (`*/n` and `a-b/n` and `a/n`), case-insensitive aliases
|
||||
(`JAN`-`DEC` for month, `SUN`-`SAT` for day-of-week).
|
||||
- Day-of-week normalization: both `0` and `7` are accepted for Sunday and
|
||||
collapse to `0` after sorting and deduplication.
|
||||
- Range bounds per field exposed via `rangeFor(kind)` for downstream
|
||||
next-run computations.
|
||||
- Typed errors via `CronError`: out-of-range numerics, unsupported tokens,
|
||||
and malformed atoms each surface a distinct `CronErrorCode`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { parseField } from "@suaveplan/cron";
|
||||
|
||||
const minutes = parseField("*/15", "minute");
|
||||
console.log(Array.from(minutes)); // [0, 15, 30, 45]
|
||||
|
||||
const weekdays = parseField("MON-FRI", "dayOfWeek");
|
||||
console.log(Array.from(weekdays)); // [1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { parseField, rangeFor, CronError, CRON_CODES } from "@suaveplan/cron";
|
||||
|
||||
function expandOrFallback(token: string, kind: Parameters<typeof parseField>[1]) {
|
||||
try {
|
||||
return parseField(token, kind);
|
||||
} catch (err) {
|
||||
if (err instanceof CronError && err.code === CRON_CODES.UNSUPPORTED_SYNTAX) {
|
||||
const range = rangeFor(kind);
|
||||
return Object.freeze(
|
||||
Array.from({ length: range.max - range.min + 1 }, (_, i) => range.min + i)
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(expandOrFallback("L", "dayOfMonth")); // falls back to full range
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `parseField(token: string, kind: FieldKind): readonly number[]` — returns a
|
||||
frozen, sorted, deduplicated value set. Throws `CronError` with codes
|
||||
`CRON.UNSUPPORTED_SYNTAX`, `CRON.FIELD_OUT_OF_RANGE`, or
|
||||
`CRON.INVALID_EXPRESSION` per the failure mode.
|
||||
- `rangeFor(kind: FieldKind): FieldRange` — inclusive `{min, max}` for the
|
||||
given field kind.
|
||||
- `FieldKind` — `"second" | "minute" | "hour" | "dayOfMonth" | "month" |
|
||||
"dayOfWeek" | "year"`.
|
||||
- `FieldRange` — `{ readonly min: number; readonly max: number }`.
|
||||
- `FieldAliases` — `Readonly<Record<string, number>>` (internal alias map
|
||||
shape).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The tokenizer walks comma-separated terms left-to-right, dispatching each
|
||||
term through the step parser before falling through to the range parser.
|
||||
Step extraction is bounded to a single `/` per term; nested steps
|
||||
(`*/15/2`) are rejected up front. Range expansion is O(n) over the result
|
||||
set and feeds a `Set` for deduplication before a final monotonically
|
||||
sorted `Array.from`. Day-of-week values are normalized after expansion so
|
||||
both `0,7` and `SAT,SUN` collapse correctly. Unsupported Quartz tokens
|
||||
(`L`/`W`/`#`/`?`) are caught by a single regex before any further parsing
|
||||
to keep the error path cheap; alias resolution is case-insensitive via
|
||||
`toUpperCase`. The output array is `Object.freeze`d to prevent downstream
|
||||
mutation.
|
||||
Reference in New Issue
Block a user