Initial Commit
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# next-run-fields
|
||||
|
||||
## Purpose
|
||||
|
||||
`next-run-fields` houses the low-level field-walking helpers used by the
|
||||
forward and backward next-run search loops in `next-run.ts`. Splitting the
|
||||
helpers into a sibling module keeps each file under the 300-line hard
|
||||
limit while keeping the call sites in `next-run.ts` declarative — the
|
||||
forward loop reads as `snapForward → fromWallClock → verify`, with the
|
||||
fine-grained carry/rollover logic isolated here. None of these helpers
|
||||
are part of the package's public API; they exist purely to support the
|
||||
internal next-run engine.
|
||||
|
||||
## Features
|
||||
|
||||
- `climbFields(wall, fields)` — single forward step across seconds →
|
||||
minutes → hours → days, honoring per-field carries.
|
||||
- `climbFieldsBackward(wall, fields)` — mirror walk used by
|
||||
`previousRun`.
|
||||
- `advanceUntilMonthMatches`, `advanceUntilDayMatches`,
|
||||
`advanceUntilYearMatches` — coarse jump helpers that snap a wall-clock
|
||||
forward to the next legal month / day / year for the field set.
|
||||
- `nextValueAtLeast` / `previousValueAtMost` — sorted-array lookup
|
||||
helpers shared by every field walker.
|
||||
- `copyWall` — converts an immutable `WallClock` to a `MutableWall` so
|
||||
the search loop can mutate without aliasing.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
advanceUntilMonthMatches,
|
||||
climbFields,
|
||||
copyWall,
|
||||
} from "@suaveplan/cron/next-run/next-run";
|
||||
import { toWallClock } from "@suaveplan/cron";
|
||||
|
||||
const fields = {
|
||||
minutes: [0],
|
||||
hours: [9],
|
||||
daysOfMonth: Array.from({ length: 31 }, (_, i) => i + 1),
|
||||
months: [6, 12],
|
||||
daysOfWeek: [0, 1, 2, 3, 4, 5, 6],
|
||||
};
|
||||
|
||||
const wall = copyWall(toWallClock(Date.now(), "UTC"));
|
||||
climbFields(wall, fields);
|
||||
advanceUntilMonthMatches(wall, fields);
|
||||
console.log(wall.month);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
climbFieldsBackward,
|
||||
copyWall,
|
||||
} from "@suaveplan/cron/next-run/next-run";
|
||||
import { toWallClock } from "@suaveplan/cron";
|
||||
|
||||
const fields = {
|
||||
minutes: [0, 15, 30, 45],
|
||||
hours: [9, 10, 11, 12, 13, 14, 15, 16, 17],
|
||||
daysOfMonth: Array.from({ length: 31 }, (_, i) => i + 1),
|
||||
months: Array.from({ length: 12 }, (_, i) => i + 1),
|
||||
daysOfWeek: [1, 2, 3, 4, 5],
|
||||
};
|
||||
|
||||
const wall = copyWall(toWallClock(Date.UTC(2026, 4, 11, 12, 47), "UTC"));
|
||||
climbFieldsBackward(wall, fields);
|
||||
console.log(wall.hour, wall.minute); // 12, 45
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `HORIZON_YEARS: 8`, `MS_PER_SECOND: 1000`, `MS_PER_MINUTE: 60000`.
|
||||
- `MutableWall` — mutable counterpart of `WallClock` minus `weekday`.
|
||||
- `nextValueAtLeast(sorted, current)`,
|
||||
`previousValueAtMost(sorted, current)`.
|
||||
- `daysInMonth(year, month)` — calendar-correct day count.
|
||||
- `copyWall(w)`.
|
||||
- `climbFields(wall, fields)`, `climbFieldsBackward(wall, fields)`.
|
||||
- `advanceUntilMonthMatches`, `advanceUntilDayMatches`,
|
||||
`advanceUntilYearMatches`, `yearMatches`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The forward and backward walkers are written as small step functions so
|
||||
each carry case can be reasoned about independently. Day-of-month and
|
||||
day-of-week interaction follows Vixie OR semantics: when one field is
|
||||
the full range and the other is restricted, only the restricted field
|
||||
matters; when both are restricted, either match is sufficient.
|
||||
`advanceUntilDayMatches` walks one calendar day at a time so the OR
|
||||
semantics fall out naturally rather than requiring a more elaborate
|
||||
bitset intersection. Safety bounds in the loops cap iterations at
|
||||
`366 × (HORIZON_YEARS + 1)` so pathological input cannot livelock the
|
||||
search; the outer search functions in `next-run.ts` then convert the
|
||||
resulting wall clock into an epoch instant and verify the result still
|
||||
matches the field set, catching DST-gap edge cases.
|
||||
Reference in New Issue
Block a user