From 44d336c01ba15c8e6e17820797ade083210717d6 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 10 Jun 2026 11:17:13 -0400 Subject: [PATCH] feat(06-03): expose hasRrule on expanded occurrences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add hasRrule: boolean to CalendarOccurrence interface - Capture isRecurring = event.isRecurring() once before the branch - Set hasRrule: isRecurring in non-recurring push (always false) - Set hasRrule: isRecurring in recurring push (always true) - All 10 expand.test.ts tests pass (RED→GREEN) --- apps/api/src/broker/expand.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/api/src/broker/expand.ts b/apps/api/src/broker/expand.ts index e052b23..63888b2 100644 --- a/apps/api/src/broker/expand.ts +++ b/apps/api/src/broker/expand.ts @@ -64,6 +64,8 @@ export interface CalendarOccurrence { allDay: boolean location: string | null description: string | null + /** True when this occurrence belongs to a recurring series (has RRULE). False for single events. */ + hasRrule: boolean } /** @@ -219,8 +221,11 @@ export function expandOccurrences( const occurrences: CalendarOccurrence[] = [] + // Capture once — used in both the non-recurring and recurring branches to populate hasRrule. + const isRecurring = event.isRecurring() + // --- 4. Non-recurring event: single occurrence check --- - if (!event.isRecurring()) { + if (!isRecurring) { if (dtstart.compare(rangeStart) >= 0 && dtstart.compare(rangeEnd) < 0) { // Use ICAL.Event.endDate which derives end from DTEND, or DTSTART+DURATION, or sensible default. // Do NOT use getFirstPropertyValue('dtend') directly — events with only DURATION set return null, @@ -253,6 +258,7 @@ export function expandOccurrences( allDay, location: event.location ?? null, description: event.description ?? null, + hasRrule: isRecurring, // always false in the non-recurring branch }) } return occurrences @@ -299,6 +305,7 @@ export function expandOccurrences( allDay, location: event.location ?? null, description: event.description ?? null, + hasRrule: isRecurring, // always true in the recurring branch }) }