@forcecalendar/core 2.4.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,10 +15,15 @@ export declare class RecurrenceEngineV2 {
15
15
  /**
16
16
  * Expand recurring event with advanced handling
17
17
  *
18
- * Occurrences before rangeStart are skipped without being generated:
19
- * daily, weekly, hourly and minutely rules seek straight to the range, so
20
- * a series that started years before the queried window is expanded at
21
- * the same cost as one that started yesterday.
18
+ * Occurrences before rangeStart are skipped without being generated for
19
+ * the rules seekToRange can seek: DAILY (without BYHOUR), WEEKLY (with or
20
+ * without BYDAY), HOURLY and MINUTELY. Such a series that started years
21
+ * before the queried window is expanded at the same cost as one that
22
+ * started yesterday. MONTHLY and YEARLY rules, and DAILY with BYHOUR,
23
+ * are stepped from DTSTART; they take few enough steps per year that
24
+ * this is cheap, but an expansion that would need more than
25
+ * MAX_ITERATIONS_HARD_LIMIT steps is truncated (with one console.warn
26
+ * per process).
22
27
  *
23
28
  * @param {import('./Event.js').Event} event - Recurring event
24
29
  * @param {Date} rangeStart - Start of expansion range
@@ -30,7 +35,7 @@ export declare class RecurrenceEngineV2 {
30
35
  * @param {boolean} [options.includeCancelled=false] - Return exception dates as cancelled occurrences
31
36
  * @param {string} [options.timezone] - Timezone for expansion (defaults to the event's)
32
37
  * @param {boolean} [options.handleDST=true] - Adjust occurrences across DST transitions
33
- * @returns {Array} Expanded occurrences
38
+ * @returns {import('../types.js').ExpandedOccurrence[]} Expanded occurrences
34
39
  */
35
40
  expandEvent(event: import('./Event.js').Event, rangeStart: Date, rangeEnd: Date, options?: {
36
41
  maxOccurrences?: number;
@@ -38,14 +43,107 @@ export declare class RecurrenceEngineV2 {
38
43
  includeCancelled?: boolean;
39
44
  timezone?: string;
40
45
  handleDST?: boolean;
41
- }): any[];
46
+ }): import('../types.js').ExpandedOccurrence[];
47
+ /**
48
+ * Lazily iterate the occurrences of an event in chronological order.
49
+ *
50
+ * Yields what expandEvent returns for the window, one occurrence at a
51
+ * time and without the expansion cache: stored instance modifications
52
+ * and exceptions are applied as each occurrence is produced, so changes
53
+ * made through addModifiedInstance or addException are visible on the
54
+ * next pull. Rules seekToRange can seek (daily, weekly, hourly,
55
+ * minutely) jump straight to `after`, and DST transitions are scanned
56
+ * lazily ahead of the cursor instead of for the whole window up front.
57
+ *
58
+ * Both bounds are exclusive unless `inclusive` is set: an occurrence that
59
+ * starts exactly at `after` or `before` is skipped by default, so
60
+ * iterating from a known occurrence's start continues the series without
61
+ * repeating it; with `inclusive: true` the window is closed on both ends
62
+ * like expandEvent's range. A non-recurring event yields its single
63
+ * occurrence when it falls inside the window. Iteration ends at COUNT or
64
+ * UNTIL, at `before`, or — as a guard for rules that produce no
65
+ * occurrences — after MAX_ITERATIONS_HARD_LIMIT consecutive steps
66
+ * without one. The generator is single-use; call again for a fresh one.
67
+ *
68
+ * @example
69
+ * const engine = new RecurrenceEngineV2();
70
+ * for (const occurrence of engine.iterateOccurrences(event, { after: new Date() })) {
71
+ * if (occurrence.start > deadline) break;
72
+ * schedule(occurrence);
73
+ * }
74
+ *
75
+ * @param {import('./Event.js').Event} event - The event to iterate
76
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
77
+ * @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>} Occurrences in chronological order
78
+ * @throws {TypeError} If `after` or `before` is not a valid Date or timestamp
79
+ */
80
+ iterateOccurrences(event: import('./Event.js').Event, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): Generator<import('../types.js').ExpandedOccurrence, void, undefined>;
81
+ /**
82
+ * First occurrence of an event after an instant, or null when the series
83
+ * has no occurrence after it. `after` is exclusive unless
84
+ * `options.inclusive` is set, so passing the start of a known occurrence
85
+ * returns the one that follows it.
86
+ *
87
+ * @example
88
+ * const upcoming = engine.nextOccurrence(event, new Date());
89
+ *
90
+ * @param {import('./Event.js').Event} event - The event to query
91
+ * @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
92
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Further options
93
+ * @returns {import('../types.js').ExpandedOccurrence|null} The next occurrence, or null
94
+ */
95
+ nextOccurrence(event: import('./Event.js').Event, after?: Date | number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence | null;
96
+ /**
97
+ * The first `count` occurrences of an event inside a window, generated
98
+ * lazily so an open-ended series costs only the occurrences taken.
99
+ * `count` is capped at MAX_OCCURRENCES_HARD_LIMIT; fewer are returned
100
+ * when the series or the window ends first.
101
+ *
102
+ * @example
103
+ * const nextFive = engine.takeOccurrences(event, 5, { after: new Date() });
104
+ *
105
+ * @param {import('./Event.js').Event} event - The event to query
106
+ * @param {number} count - Maximum number of occurrences to return (fractions are floored)
107
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
108
+ * @returns {import('../types.js').ExpandedOccurrence[]} Up to `count` occurrences in chronological order
109
+ */
110
+ takeOccurrences(event: import('./Event.js').Event, count: number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence[];
111
+ /**
112
+ * Yield a non-recurring event's single occurrence if it starts inside
113
+ * the window
114
+ * @param {import('./Event.js').Event} event - The event
115
+ * @param {{ startMs: number, endMs: number }} window - Inclusive bounds
116
+ * @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>}
117
+ * @private
118
+ */
119
+ private _iterateSingle;
120
+ /**
121
+ * Lazy counterpart of the expandEvent loop: seeks to the window, then
122
+ * steps the cursor and yields each in-window occurrence with the same
123
+ * DST adjustment, exception handling and instance modifications.
124
+ * @private
125
+ */
126
+ private _iterateRule;
127
+ /**
128
+ * Apply exceptions and stored instance modifications to a generated
129
+ * occurrence
130
+ * @param {import('./Event.js').Event} event - The recurring event
131
+ * @param {Object} occurrence - Occurrence from generateOccurrence
132
+ * @param {Object} rule - Parsed recurrence rule
133
+ * @param {boolean} includeCancelled - Return exception dates as cancelled occurrences
134
+ * @param {boolean} includeModified - Apply stored instance modifications
135
+ * @returns {Object|null} The occurrence, or null when it is excluded
136
+ * @private
137
+ */
138
+ private _applyOverrides;
42
139
  /**
43
140
  * Move the expansion cursor to the last occurrence before the range
44
141
  * without stepping through every occurrence in between.
45
142
  *
46
143
  * Applies to rules whose step is a fixed duration between system-timezone
47
- * transitions (plain DAILY and WEEKLY, HOURLY, MINUTELY); the step that
48
- * crosses a transition is taken with getNextDate so the result is exactly
144
+ * transitions (plain DAILY and WEEKLY, HOURLY, MINUTELY) and to WEEKLY
145
+ * rules with BYDAY, whose steps repeat in a weekly cycle; the steps that
146
+ * cross a transition are taken with getNextDate so the result is exactly
49
147
  * what stepping from DTSTART would produce. Never seeks past UNTIL, and
50
148
  * counts skipped steps against COUNT.
51
149
  *
@@ -56,6 +154,40 @@ export declare class RecurrenceEngineV2 {
56
154
  * @param {string} timezone - Expansion timezone
57
155
  */
58
156
  seekToRange(state: Object, rule: Object, rangeStart: Date, rangeEnd: Date, timezone: string): void;
157
+ /**
158
+ * Seek for WEEKLY rules with BYDAY. getNextWeekly picks the next weekday
159
+ * from the BYDAY list (in list order), so the step from each weekday is
160
+ * fixed and the walk from DTSTART settles into a cycle of weekdays that
161
+ * repeats every whole number of weeks. The cursor is stepped one
162
+ * occurrence at a time until it is on that cycle (at most six steps),
163
+ * then whole cycles are skipped arithmetically between system-timezone
164
+ * transitions, exactly as seekToRange does for fixed steps.
165
+ * @param {Object} state - Expansion state (currentDate and count are updated)
166
+ * @param {Object} rule - Parsed recurrence rule
167
+ * @param {Date} rangeStart - Start of expansion range
168
+ * @param {Date} rangeEnd - End of expansion range
169
+ * @param {string} timezone - Expansion timezone
170
+ * @private
171
+ */
172
+ private _seekWeekCycle;
173
+ /**
174
+ * Days getNextWeekly adds from each weekday (index 0-6) for a WEEKLY
175
+ * rule with BYDAY, or null when the rule cannot be seeked (an invalid
176
+ * interval or day code, which the expansion loop handles as before)
177
+ * @param {Object} rule - Parsed recurrence rule
178
+ * @returns {number[]|null} Delta table indexed by Date#getDay()
179
+ * @private
180
+ */
181
+ private _weekdayDeltas;
182
+ /**
183
+ * Weekday numbers (Date#getDay) of a rule's BYDAY entries in ascending
184
+ * order, computed once per parsed rule. Invalid day codes map to
185
+ * undefined and sort last.
186
+ * @param {Object} rule - Parsed recurrence rule with byDay
187
+ * @returns {number[]} Sorted weekday numbers
188
+ * @private
189
+ */
190
+ private _weekdayTargets;
59
191
  /**
60
192
  * Milliseconds per step for rules getNextDate advances by a fixed
61
193
  * duration while the system UTC offset is constant
@@ -111,12 +243,18 @@ export declare class RecurrenceEngineV2 {
111
243
  /**
112
244
  * Find DST transitions in date range
113
245
  */
114
- findDSTTransitions(start: any, end: any, timezone: any): {
115
- date: Date;
116
- oldOffset: number;
117
- newOffset: number;
118
- type: string;
119
- }[];
246
+ findDSTTransitions(start: any, end: any, timezone: any): any[];
247
+ /**
248
+ * Walk the scan cursor one day at a time up to untilMs, appending each
249
+ * offset change. The cursor and last offset persist in `scan`, so the
250
+ * walk can be resumed later on the same day grid.
251
+ * @param {{ cursor: Date, lastOffset: number }} scan - Resumable scan position (mutated)
252
+ * @param {Array} transitions - Transition list to append to
253
+ * @param {number} untilMs - Scan through this timestamp (inclusive)
254
+ * @param {string} timezone - Timezone to probe
255
+ * @private
256
+ */
257
+ private _scanDSTTransitions;
120
258
  /**
121
259
  * Adjust occurrence for DST transitions
122
260
  */
@@ -150,8 +288,27 @@ export declare class RecurrenceEngineV2 {
150
288
  getDateKey(date: any): string;
151
289
  /**
152
290
  * Create cache key
291
+ *
292
+ * When given the event itself the key also covers everything the
293
+ * expansion depends on (DTSTART, end, recurrence rule), so a series that
294
+ * is updated, replaced or re-added under the same id can never be served
295
+ * a stale expansion. Keys always start with `<eventId>_`, which is what
296
+ * {@link RecurrenceEngineV2#clearEventCache} matches on.
297
+ * @param {import('./Event.js').Event|string} event - Recurring event, or just its id
298
+ * @param {Date} start - Start of expansion range
299
+ * @param {Date} end - End of expansion range
300
+ * @param {Object} options - Expansion options
301
+ * @returns {string} Cache key
302
+ */
303
+ getCacheKey(event: import('./Event.js').Event | string, start: Date, end: Date, options: Object): string;
304
+ /**
305
+ * Stable text form of a recurrence rule for cache keys. A rule that
306
+ * cannot be serialised gets a unique fingerprint, i.e. is never cached.
307
+ * @param {string|Object} rule - RRULE string or rule object
308
+ * @returns {string} Fingerprint
309
+ * @private
153
310
  */
154
- getCacheKey(eventId: any, start: any, end: any, options: any): string;
311
+ private _ruleFingerprint;
155
312
  /**
156
313
  * Cache occurrences
157
314
  */
@@ -160,6 +317,12 @@ export declare class RecurrenceEngineV2 {
160
317
  * Clone occurrence results before returning or caching.
161
318
  */
162
319
  cloneOccurrences(occurrences: any): any;
320
+ /**
321
+ * Clone a single occurrence, copying its Date and array fields.
322
+ * @param {import('../types.js').ExpandedOccurrence} occurrence - Occurrence to clone
323
+ * @returns {import('../types.js').ExpandedOccurrence} Independent copy
324
+ */
325
+ cloneOccurrence(occurrence: import('../types.js').ExpandedOccurrence): import('../types.js').ExpandedOccurrence;
163
326
  /**
164
327
  * Clear cache for specific event
165
328
  */
package/types/index.d.ts CHANGED
@@ -18,5 +18,5 @@ export { RRuleParser } from './events/RRuleParser.js';
18
18
  export { TimezoneManager } from './timezone/TimezoneManager.js';
19
19
  export { ConflictDetector } from './conflicts/ConflictDetector.js';
20
20
  export { EnhancedCalendar } from './integration/EnhancedCalendar.js';
21
- export declare const VERSION = "2.4.0";
21
+ export declare const VERSION = "2.5.1";
22
22
  export { Calendar as default } from './calendar/Calendar.js';
@@ -13,16 +13,51 @@ export declare class EnhancedCalendar extends Calendar {
13
13
  expansionTime: never[];
14
14
  renderTime: never[];
15
15
  };
16
+ _unsubscribeCacheInvalidation: Function;
16
17
  _clearReindexTimeout: (() => void) | null | undefined;
17
18
  constructor(config: any);
19
+ /**
20
+ * Invalidate the enhanced engine's cached expansions for a store change
21
+ * @param {import('../types.js').EventStoreChange} change - Store change
22
+ * @private
23
+ */
24
+ private _invalidateOccurrenceCache;
18
25
  /**
19
26
  * Enhanced search with worker support
20
27
  */
21
28
  search(query: any, options?: {}): Promise<any>;
22
29
  /**
23
30
  * Get events with enhanced recurrence expansion
31
+ *
32
+ * Regular events overlapping the range are returned as stored. Every
33
+ * recurring series in the store is expanded with this calendar's
34
+ * RecurrenceEngineV2 (so instance modifications and cancellations apply),
35
+ * including series that started before the range. Occurrences are the
36
+ * engine's plain occurrence objects, with the id `<masterId>_<startMs>`
37
+ * (see `Event.occurrenceId`), `recurringEventId`, `isOccurrence: true` and
38
+ * `occurrenceStart`.
24
39
  */
25
- getEventsInRange(startDate: any, endDate: any, options?: {}): any[];
40
+ getEventsInRange(startDate: any, endDate: any, options?: {}): (import("../index.js").Event | {
41
+ id: string;
42
+ recurringEventId?: string;
43
+ title: string;
44
+ start: Date;
45
+ end: Date;
46
+ startUTC?: Date;
47
+ endUTC?: Date;
48
+ timezone: string;
49
+ originalStart?: Date;
50
+ allDay: boolean;
51
+ description?: string;
52
+ location?: string;
53
+ categories?: string[];
54
+ status?: import("../types.js").EventStatus;
55
+ cancellationReason?: string;
56
+ isRecurring: boolean;
57
+ isModified?: boolean;
58
+ isOccurrence: boolean;
59
+ occurrenceStart: Date;
60
+ })[];
26
61
  /**
27
62
  * Modify a single occurrence of a recurring event
28
63
  */
@@ -31,6 +66,43 @@ export declare class EnhancedCalendar extends Calendar {
31
66
  * Cancel a single occurrence of a recurring event
32
67
  */
33
68
  cancelOccurrence(eventId: any, occurrenceDate: any, reason?: string): void;
69
+ /**
70
+ * Lazily iterate the occurrences of an event through the enhanced
71
+ * engine, so occurrences changed with modifyOccurrence or cancelled with
72
+ * cancelOccurrence are reflected. Same semantics as
73
+ * Calendar#iterateOccurrences.
74
+ * @param {string} eventId - The event ID
75
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
76
+ * @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>} Occurrences in chronological order
77
+ * @throws {Error} If no event with the ID exists
78
+ */
79
+ iterateOccurrences(eventId: string, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): Generator<import('../types.js').ExpandedOccurrence, void, undefined>;
80
+ /**
81
+ * First occurrence of an event after an instant through the enhanced
82
+ * engine, or null. Same semantics as Calendar#getNextOccurrence.
83
+ * @param {string} eventId - The event ID
84
+ * @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
85
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Further options
86
+ * @returns {import('../types.js').ExpandedOccurrence|null} The next occurrence, or null
87
+ * @throws {Error} If no event with the ID exists
88
+ */
89
+ getNextOccurrence(eventId: string, after?: Date | number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence | null;
90
+ /**
91
+ * The first `count` occurrences of an event through the enhanced
92
+ * engine. Same semantics as Calendar#takeOccurrences.
93
+ * @param {string} eventId - The event ID
94
+ * @param {number} count - Maximum number of occurrences to return
95
+ * @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
96
+ * @returns {import('../types.js').ExpandedOccurrence[]} Up to `count` occurrences in chronological order
97
+ * @throws {Error} If no event with the ID exists
98
+ */
99
+ takeOccurrences(eventId: string, count: number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence[];
100
+ /**
101
+ * Resolve an occurrence query to the stored event and its options, with
102
+ * the timezone defaulted as getEventsInRange does
103
+ * @private
104
+ */
105
+ private _occurrenceQuery;
34
106
  /**
35
107
  * Bulk operations for recurring events
36
108
  */
package/types/types.d.ts CHANGED
@@ -789,6 +789,24 @@ export type EventsSetPayload = {
789
789
  */
790
790
  unchanged: import('./events/Event.js').Event[];
791
791
  };
792
+ export type EventSelectPayload = {
793
+ /**
794
+ * - The stored event (the master for an occurrence id)
795
+ */
796
+ event: import('./events/Event.js').Event;
797
+ /**
798
+ * - Id of the stored event, as kept in the state's selectedEventId
799
+ */
800
+ eventId: string;
801
+ /**
802
+ * - The occurrence id that was selected, or null for a stored event's id
803
+ */
804
+ occurrenceId: string | null;
805
+ /**
806
+ * - The selected occurrence as in view data, or null
807
+ */
808
+ occurrence: import('./events/Event.js').Event | null;
809
+ };
792
810
  export type QueryFilters = {
793
811
  /**
794
812
  * - Start date for range query
@@ -856,6 +874,132 @@ export type EventOccurrence = {
856
874
  * - ID of the parent recurring event
857
875
  */
858
876
  recurringEventId: string;
877
+ /**
878
+ * - Timezone the occurrence was expanded in
879
+ */
880
+ timezone?: string;
881
+ /**
882
+ * - Start of the series (DTSTART)
883
+ */
884
+ originalStart?: Date;
885
+ };
886
+ export type OccurrenceIteratorOptions = {
887
+ /**
888
+ * - Only occurrences starting after this instant (Date or timestamp)
889
+ */
890
+ after?: Date | number;
891
+ /**
892
+ * - Only occurrences starting before this instant (Date or timestamp)
893
+ */
894
+ before?: Date | number;
895
+ /**
896
+ * - Treat `after` and `before` as closed bounds
897
+ */
898
+ inclusive?: boolean;
899
+ /**
900
+ * - Timezone for expansion (defaults to the event's)
901
+ */
902
+ timezone?: string;
903
+ };
904
+ export type ExpandedOccurrenceIteratorOptions = {
905
+ /**
906
+ * - Only occurrences starting after this instant (Date or timestamp)
907
+ */
908
+ after?: Date | number;
909
+ /**
910
+ * - Only occurrences starting before this instant (Date or timestamp)
911
+ */
912
+ before?: Date | number;
913
+ /**
914
+ * - Treat `after` and `before` as closed bounds
915
+ */
916
+ inclusive?: boolean;
917
+ /**
918
+ * - Timezone for expansion (defaults to the event's)
919
+ */
920
+ timezone?: string;
921
+ /**
922
+ * - Apply stored instance modifications
923
+ */
924
+ includeModified?: boolean;
925
+ /**
926
+ * - Yield exception dates as cancelled occurrences
927
+ */
928
+ includeCancelled?: boolean;
929
+ /**
930
+ * - Adjust occurrences across DST transitions
931
+ */
932
+ handleDST?: boolean;
933
+ };
934
+ export type ExpandedOccurrence = {
935
+ /**
936
+ * - Occurrence ID (`<eventId>_<startTimestamp>` for recurring events)
937
+ */
938
+ id: string;
939
+ /**
940
+ * - ID of the parent recurring event
941
+ */
942
+ recurringEventId?: string;
943
+ /**
944
+ * - Event title
945
+ */
946
+ title: string;
947
+ /**
948
+ * - Occurrence start date
949
+ */
950
+ start: Date;
951
+ /**
952
+ * - Occurrence end date
953
+ */
954
+ end: Date;
955
+ /**
956
+ * - Occurrence start in UTC
957
+ */
958
+ startUTC?: Date;
959
+ /**
960
+ * - Occurrence end in UTC
961
+ */
962
+ endUTC?: Date;
963
+ /**
964
+ * - Timezone the occurrence was expanded in
965
+ */
966
+ timezone: string;
967
+ /**
968
+ * - Start of the series (DTSTART)
969
+ */
970
+ originalStart?: Date;
971
+ /**
972
+ * - Whether the event is all-day
973
+ */
974
+ allDay: boolean;
975
+ /**
976
+ * - Event description
977
+ */
978
+ description?: string;
979
+ /**
980
+ * - Event location
981
+ */
982
+ location?: string;
983
+ /**
984
+ * - Event categories
985
+ */
986
+ categories?: string[];
987
+ /**
988
+ * - 'confirmed', or 'cancelled' for exception dates yielded with includeCancelled
989
+ */
990
+ status?: EventStatus;
991
+ /**
992
+ * - Reason recorded for a cancelled occurrence
993
+ */
994
+ cancellationReason?: string;
995
+ /**
996
+ * - Whether the occurrence belongs to a recurring series
997
+ */
998
+ isRecurring: boolean;
999
+ /**
1000
+ * - Whether a stored instance modification was applied
1001
+ */
1002
+ isModified?: boolean;
859
1003
  };
860
1004
  export type CalendarPlugin = {
861
1005
  /**
@@ -1267,6 +1411,14 @@ export type ConflictSummary = {
1267
1411
  * @property {import('./events/Event.js').Event[]} removed - Events removed by the operation
1268
1412
  * @property {import('./events/Event.js').Event[]} unchanged - Events left untouched
1269
1413
  */
1414
+ /**
1415
+ * Payload of the Calendar `eventSelect` event
1416
+ * @typedef {Object} EventSelectPayload
1417
+ * @property {import('./events/Event.js').Event} event - The stored event (the master for an occurrence id)
1418
+ * @property {string} eventId - Id of the stored event, as kept in the state's selectedEventId
1419
+ * @property {string|null} occurrenceId - The occurrence id that was selected, or null for a stored event's id
1420
+ * @property {import('./events/Event.js').Event|null} occurrence - The selected occurrence as in view data, or null
1421
+ */
1270
1422
  /**
1271
1423
  * @typedef {Object} QueryFilters
1272
1424
  * @property {Date} [start] - Start date for range query
@@ -1288,6 +1440,55 @@ export type ConflictSummary = {
1288
1440
  * @property {Date} start - Occurrence start date
1289
1441
  * @property {Date} end - Occurrence end date
1290
1442
  * @property {string} recurringEventId - ID of the parent recurring event
1443
+ * @property {string} [timezone] - Timezone the occurrence was expanded in
1444
+ * @property {Date} [originalStart] - Start of the series (DTSTART)
1445
+ */
1446
+ /**
1447
+ * Window for lazy occurrence iteration. Both bounds are exclusive unless
1448
+ * `inclusive` is set: an occurrence starting exactly at `after` or `before`
1449
+ * is skipped by default, so iterating from a known occurrence's start
1450
+ * continues the series without repeating it. Omit a bound to leave that
1451
+ * end of the window open.
1452
+ * @typedef {Object} OccurrenceIteratorOptions
1453
+ * @property {Date|number} [after] - Only occurrences starting after this instant (Date or timestamp)
1454
+ * @property {Date|number} [before] - Only occurrences starting before this instant (Date or timestamp)
1455
+ * @property {boolean} [inclusive=false] - Treat `after` and `before` as closed bounds
1456
+ * @property {string} [timezone] - Timezone for expansion (defaults to the event's)
1457
+ */
1458
+ /**
1459
+ * Options for lazy occurrence iteration through RecurrenceEngineV2,
1460
+ * EventStore and Calendar: the OccurrenceIteratorOptions window plus the
1461
+ * expansion switches RecurrenceEngineV2.expandEvent accepts.
1462
+ * @typedef {Object} ExpandedOccurrenceIteratorOptions
1463
+ * @property {Date|number} [after] - Only occurrences starting after this instant (Date or timestamp)
1464
+ * @property {Date|number} [before] - Only occurrences starting before this instant (Date or timestamp)
1465
+ * @property {boolean} [inclusive=false] - Treat `after` and `before` as closed bounds
1466
+ * @property {string} [timezone] - Timezone for expansion (defaults to the event's)
1467
+ * @property {boolean} [includeModified=true] - Apply stored instance modifications
1468
+ * @property {boolean} [includeCancelled=false] - Yield exception dates as cancelled occurrences
1469
+ * @property {boolean} [handleDST=true] - Adjust occurrences across DST transitions
1470
+ */
1471
+ /**
1472
+ * Occurrence produced by RecurrenceEngineV2 (and therefore by EventStore
1473
+ * and Calendar occurrence queries)
1474
+ * @typedef {Object} ExpandedOccurrence
1475
+ * @property {string} id - Occurrence ID (`<eventId>_<startTimestamp>` for recurring events)
1476
+ * @property {string} [recurringEventId] - ID of the parent recurring event
1477
+ * @property {string} title - Event title
1478
+ * @property {Date} start - Occurrence start date
1479
+ * @property {Date} end - Occurrence end date
1480
+ * @property {Date} [startUTC] - Occurrence start in UTC
1481
+ * @property {Date} [endUTC] - Occurrence end in UTC
1482
+ * @property {string} timezone - Timezone the occurrence was expanded in
1483
+ * @property {Date} [originalStart] - Start of the series (DTSTART)
1484
+ * @property {boolean} allDay - Whether the event is all-day
1485
+ * @property {string} [description] - Event description
1486
+ * @property {string} [location] - Event location
1487
+ * @property {string[]} [categories] - Event categories
1488
+ * @property {EventStatus} [status] - 'confirmed', or 'cancelled' for exception dates yielded with includeCancelled
1489
+ * @property {string} [cancellationReason] - Reason recorded for a cancelled occurrence
1490
+ * @property {boolean} isRecurring - Whether the occurrence belongs to a recurring series
1491
+ * @property {boolean} [isModified] - Whether a stored instance modification was applied
1291
1492
  */
1292
1493
  /**
1293
1494
  * @typedef {Object} CalendarPlugin