import { type Maybe, type Minutes, type TimezoneString } from '@dereekb/util'; import { type FullDateCellScheduleRange } from '../date'; import { type ModelRecurrenceInfo } from './date.recurrence'; import { type RRuleLines, type RRuleStringLineSet } from './date.rrule.parse'; /** * Configuration for {@link dateCellScheduleRangeRRule}. */ export interface DateCellScheduleRangeRRuleConfig { /** * The schedule range to convert. */ readonly range: FullDateCellScheduleRange; /** * Emit `FREQ=DAILY` instead of `FREQ=WEEKLY` with all seven BYDAY tokens when every day of the week is * enabled. The two forms are provably equivalent — this changes only the emitted bytes — so the flag * exists to make that equivalence directly testable. Defaults to true. */ readonly preferDailyFrequency?: boolean; } /** * Fields shared by both outcomes of {@link dateCellScheduleRangeRRule}. */ export interface DateCellScheduleRangeRRuleResultBase { readonly timezone: TimezoneString; readonly duration: Minutes; /** * Every occurrence's startsAt instant, ascending. * * Produced by `expandDateCellScheduleRange()` itself rather than recomputed, so agreement with the * expansion is structural rather than merely tested. */ readonly occurrences: readonly Date[]; } /** * The result for a schedule range that produced at least one occurrence. */ export interface DateCellScheduleRangeRecurrenceRRule extends DateCellScheduleRangeRRuleResultBase { readonly recurs: true; /** * Stored form: exactly one RRULE line, then optional EXDATE and RDATE lines. Never a DTSTART. */ readonly rrule: RRuleLines; readonly rruleStringLineSet: RRuleStringLineSet; /** * The recurrence anchor: the first occurrence's startsAt. * * Feeds `ModelRecurrenceInfo.start` and `CalendarRecurringEventItem.sa`, and must be supplied back as * `options.date.startsAt` when expanding. */ readonly start: Date; /** * End of the final occurrence's duration. Feeds `ModelRecurrenceInfo.end` / `rea`. */ readonly end: Date; /** * Always false — a schedule range is always bounded. */ readonly forever: false; /** * The COUNT carried by the emitted rule. */ readonly count: number; /** * Occurrences the weekly pattern does not produce, matching the emitted RDATE line. */ readonly rdates: readonly Date[]; /** * Pattern occurrences removed by `ex`, matching the emitted EXDATE line. Feed these to * `CalendarRecurringEventItem.rex`. */ readonly exdates: readonly Date[]; /** * True when the emitted rule does not itself produce {@link start} — i.e. the anchor came only from `d`. * * RFC 5545 3.8.5.3 says DTSTART SHOULD match the pattern. `rrule` tolerates a mismatch by skipping the * anchor (which is why the anchor is always also in {@link rdates} when this is true), but a third-party * client's behavior is undefined, so callers get to see it. */ readonly anchorOffPattern: boolean; } /** * The result for a schedule range that produced no occurrences at all. */ export interface DateCellScheduleRangeNoRecurrenceRRule extends DateCellScheduleRangeRRuleResultBase { readonly recurs: false; /** * Deliberately undefined rather than an empty string: an empty rule parses to `{}`, and rrule defaults an * absent FREQ to YEARLY, so storing `''` would silently create a forever-yearly recurrence. */ readonly rrule?: undefined; readonly start?: undefined; readonly end?: undefined; readonly occurrences: readonly []; } /** * The result of {@link dateCellScheduleRangeRRule}, discriminated on `recurs`. */ export type DateCellScheduleRangeRRuleResult = DateCellScheduleRangeRecurrenceRRule | DateCellScheduleRangeNoRecurrenceRRule; /** * Converts a {@link FullDateCellScheduleRange} into the stored {@link RRuleLines} form plus the metadata * needed to build a {@link ModelRecurrenceInfo}. * * The range's `startsAt` is truncated to whole seconds, because `iCalendarUtcDateTimeString` has * second granularity while `DateSet` matches at millisecond precision — an unrounded anchor would emit * EXDATE values that silently fail to match anything. The stored `rex` field is unix seconds anyway, so the * storage model already requires this. * * @param config - The schedule range and emission preferences. * @returns The recurrence result, or the no-recurrence variant when the range yields no occurrences. * * @example * ```ts * const result = dateCellScheduleRangeRRule({ range }); * * if (result.recurs) { * // 'RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=6' * console.log(result.rrule); * } * ``` * * @__NO_SIDE_EFFECTS__ */ export declare function dateCellScheduleRangeRRule(config: DateCellScheduleRangeRRuleConfig): DateCellScheduleRangeRRuleResult; /** * Projects a {@link DateCellScheduleRangeRRuleResult} onto a {@link ModelRecurrenceInfo}. * * Computed by direct field reads rather than via * `ModelRecurrenceInfoUtility.expandModelRecurrenceStartToModelRecurrenceInfo()`, which drops the input * timezone and would therefore derive `start`/`end` at offset 0. * * @param result - The generator result. * @returns The recurrence info, or undefined when the range produced no occurrences. * * @__NO_SIDE_EFFECTS__ */ export declare function dateCellScheduleRangeModelRecurrenceInfo(result: DateCellScheduleRangeRRuleResult): Maybe;