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