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,95 @@
# recurrence-generate
## Purpose
`recurrence-generate` provides the internal generator functions that produce infinite sequences of candidate dates for each recurrence frequency supported by `@suaveplan/datetime`. Each generator yields dates strictly after a given `from` anchor, and the `generateCandidates` dispatcher routes to the correct generator based on `RecurrenceRule.freq`.
This module is an internal implementation detail. Public callers should use `expandRecurrence` and `nextOccurrence` from `recurrence.ts`.
## Features
- `generateDaily` — daily step with optional `BYDAY` weekday filter
- `generateWeekly` — weekly step, either plain (same weekday) or with `BYDAY` multi-day selection
- `generateMonthly` — monthly step supporting `BYMONTHDAY`, `BYDAY`, or plain monthly recurrence
- `generateYearly` — yearly step supporting `BYMONTHDAY`, `BYDAY`, or plain yearly recurrence
- `generateCandidates` — dispatcher that routes to the correct generator from `RecurrenceRule.freq`
- All generators are infinite; termination is enforced by callers (`count` / `until` / `AbortSignal`)
## Usage: Basic Example
```ts
import { generateCandidates } from "./recurrence-generate.js";
const rule = { freq: "daily" as const, count: 5 };
const from = new Date(2024, 0, 1);
const gen = generateCandidates(rule, from);
for (let i = 0; i < 5; i++) {
console.log(gen.next().value?.toDateString());
}
// Tue Jan 02 2024 … Sat Jan 06 2024
```
## Usage: Advanced Example
```ts
import { generateCandidates } from "./recurrence-generate.js";
// Monthly with BYMONTHDAY — yields the 15th of each month
const rule = {
freq: "monthly" as const,
byMonthDay: [15],
};
const from = new Date(2024, 0, 1);
const gen = generateCandidates(rule, from);
const first = gen.next().value!;
console.log(first.toDateString()); // Mon Jan 15 2024
const second = gen.next().value!;
console.log(second.toDateString()); // Thu Feb 15 2024
// Weekly with BYDAY — yields Mon/Wed/Fri
const weeklyRule = {
freq: "weekly" as const,
byDay: [1, 3, 5] as const,
};
const weeklyGen = generateCandidates(weeklyRule, new Date(2024, 2, 11));
console.log(weeklyGen.next().value?.getDay()); // 3 (Wed Mar 13)
```
## API Reference
### `generateCandidates(rule: RecurrenceRule, from: Date): Generator<Date>`
Unified dispatcher. Routes to the frequency-specific generator based on `rule.freq`. All yielded dates are strictly after `from`.
### `generateDaily(rule, from, interval): Generator<Date>`
Steps `interval` days at a time. If `rule.byDay` is set, only yields dates whose `getDay()` is in the list.
### `generateWeekly(rule, from, interval): Generator<Date>`
If `rule.byDay` is set: anchors to the Sunday of the current week, then emits the specified weekdays in sorted order each `interval * 7` days. Without `byDay`: yields the same weekday every `interval * 7` days.
### `generateMonthly(rule, from, interval): Generator<Date>`
Three modes:
1. `byMonthDay` — emits the specified day-of-month values (sorted) in each month, skipping months where a day exceeds `daysInMonth`.
2. `byDay` — scans each month and emits all days matching the weekday list.
3. Plain — yields the same day-of-month each `interval` months using `addMonths` clamping.
### `generateYearly(rule, from, interval): Generator<Date>`
Three modes:
1. `byMonthDay` — emits specified day-of-month values in each year.
2. `byDay` — advances one day at a time from the year anchor, yielding matching weekdays.
3. Plain — yields the same date each `interval` years using `addYears`.
## Implementation Notes
All generators are implemented as native ES `Generator` functions (`function*`) to enable lazy evaluation. They produce an infinite sequence; the caller (`expandRecurrence`, `nextOccurrence`) is responsible for applying `count`, `until`, and `AbortSignal` termination conditions.
The `generateWeekly` with `BYDAY` mode anchors to the start of the week (Sunday) on the first iteration, which means it may emit dates in the current week before advancing. Dates before or equal to `from` are filtered out with a `candidate > from` guard.