@forcecalendar/core 2.4.0 → 2.5.0
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.
- package/core/calendar/Calendar.js +102 -10
- package/core/events/Event.js +61 -0
- package/core/events/EventStore.js +308 -53
- package/core/events/RecurrenceEngine.js +368 -17
- package/core/events/RecurrenceEngineV2.js +281 -48
- package/core/index.js +1 -1
- package/core/integration/EnhancedCalendar.js +88 -14
- package/core/types.js +52 -0
- package/package.json +1 -1
- package/types/calendar/Calendar.d.ts +77 -7
- package/types/events/Event.d.ts +39 -0
- package/types/events/EventStore.d.ts +163 -5
- package/types/events/RecurrenceEngine.d.ts +156 -0
- package/types/events/RecurrenceEngineV2.d.ts +112 -8
- package/types/index.d.ts +1 -1
- package/types/integration/EnhancedCalendar.d.ts +66 -1
- package/types/types.d.ts +175 -0
|
@@ -80,14 +80,20 @@ export declare class Calendar {
|
|
|
80
80
|
addEvent(eventData: import('../events/Event.js').Event | import('../types.js').EventData): import('../events/Event.js').Event;
|
|
81
81
|
/**
|
|
82
82
|
* Update an event
|
|
83
|
-
*
|
|
83
|
+
*
|
|
84
|
+
* An occurrence id taken from view data (see {@link Event.occurrenceId})
|
|
85
|
+
* updates the recurring master, i.e. the whole series.
|
|
86
|
+
* @param {string} eventId - Event id or occurrence id
|
|
84
87
|
* @param {Object} updates - Properties to update
|
|
85
|
-
* @returns {Event} The updated event
|
|
88
|
+
* @returns {Event} The updated event (the master for an occurrence id)
|
|
86
89
|
*/
|
|
87
90
|
updateEvent(eventId: string, updates: Object): Event;
|
|
88
91
|
/**
|
|
89
92
|
* Remove an event
|
|
90
|
-
*
|
|
93
|
+
*
|
|
94
|
+
* An occurrence id taken from view data (see {@link Event.occurrenceId})
|
|
95
|
+
* removes the recurring master, i.e. the whole series.
|
|
96
|
+
* @param {string} eventId - Event id or occurrence id
|
|
91
97
|
* @returns {boolean} True if removed
|
|
92
98
|
*/
|
|
93
99
|
removeEvent(eventId: string): boolean;
|
|
@@ -99,12 +105,16 @@ export declare class Calendar {
|
|
|
99
105
|
deleteEvent(eventId: string): boolean;
|
|
100
106
|
/**
|
|
101
107
|
* Get an event by ID
|
|
102
|
-
*
|
|
103
|
-
*
|
|
108
|
+
*
|
|
109
|
+
* Occurrence ids taken from view data (`<masterId>_<startMs>`, see
|
|
110
|
+
* {@link Event.occurrenceId}) resolve to the stored recurring master, so
|
|
111
|
+
* every id a renderer hands back can be looked up here.
|
|
112
|
+
* @param {string} eventId - Event id or occurrence id
|
|
113
|
+
* @returns {Event|null} The stored event (the master for an occurrence id) or null
|
|
104
114
|
*/
|
|
105
115
|
getEvent(eventId: string): Event | null;
|
|
106
116
|
/**
|
|
107
|
-
* Get all events
|
|
117
|
+
* Get all stored events (recurring masters, never their occurrences)
|
|
108
118
|
* @returns {Event[]}
|
|
109
119
|
*/
|
|
110
120
|
getEvents(): Event[];
|
|
@@ -177,12 +187,23 @@ export declare class Calendar {
|
|
|
177
187
|
*/
|
|
178
188
|
queryEvents(filters: Object): Event[];
|
|
179
189
|
/**
|
|
180
|
-
* Get events for a specific date
|
|
190
|
+
* Get events for a specific date, with recurring series expanded into occurrences
|
|
181
191
|
* @param {Date} date - The date
|
|
182
192
|
* @param {string} [timezone] - Timezone for the query (defaults to calendar timezone)
|
|
183
193
|
* @returns {Event[]}
|
|
184
194
|
*/
|
|
185
195
|
getEventsForDate(date: Date, timezone?: string): Event[];
|
|
196
|
+
/**
|
|
197
|
+
* Get the events for every day in a range, keyed by local date (YYYY-MM-DD)
|
|
198
|
+
*
|
|
199
|
+
* Recurring series are expanded once for the whole range; this is what the
|
|
200
|
+
* month and week views use. See `EventStore.getEventsByDate`.
|
|
201
|
+
* @param {Date} start - First day of the range
|
|
202
|
+
* @param {Date} end - Last day of the range
|
|
203
|
+
* @param {string} [timezone] - Timezone for the query (defaults to calendar timezone)
|
|
204
|
+
* @returns {Map<string, Event[]>} Local date string -> events on that day
|
|
205
|
+
*/
|
|
206
|
+
getEventsByDate(start: Date, end: Date, timezone?: string): Map<string, Event[]>;
|
|
186
207
|
/**
|
|
187
208
|
* Get events in a date range
|
|
188
209
|
* @param {Date} start - Start date
|
|
@@ -191,6 +212,55 @@ export declare class Calendar {
|
|
|
191
212
|
* @returns {Event[]}
|
|
192
213
|
*/
|
|
193
214
|
getEventsInRange(start: Date, end: Date, timezone?: string): Event[];
|
|
215
|
+
/**
|
|
216
|
+
* Lazily iterate the occurrences of an event in chronological order.
|
|
217
|
+
*
|
|
218
|
+
* Occurrences are produced one at a time, so taking the next few of an
|
|
219
|
+
* open-ended series does not expand the series. `after` and `before`
|
|
220
|
+
* are exclusive unless `inclusive` is set; see
|
|
221
|
+
* RecurrenceEngineV2.iterateOccurrences for the full semantics.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* for (const occurrence of calendar.iterateOccurrences('standup', { after: new Date() })) {
|
|
225
|
+
* if (occurrence.start > deadline) break;
|
|
226
|
+
* remind(occurrence);
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* @param {string} eventId - The event ID
|
|
230
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
231
|
+
* @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>} Occurrences in chronological order
|
|
232
|
+
* @throws {Error} If no event with the ID exists
|
|
233
|
+
*/
|
|
234
|
+
iterateOccurrences(eventId: string, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): Generator<import('../types.js').ExpandedOccurrence, void, undefined>;
|
|
235
|
+
/**
|
|
236
|
+
* First occurrence of an event after an instant, or null when the
|
|
237
|
+
* series has no occurrence after it. `after` is exclusive unless
|
|
238
|
+
* `options.inclusive` is set.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* const upcoming = calendar.getNextOccurrence('standup', new Date());
|
|
242
|
+
*
|
|
243
|
+
* @param {string} eventId - The event ID
|
|
244
|
+
* @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
|
|
245
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Further options
|
|
246
|
+
* @returns {import('../types.js').ExpandedOccurrence|null} The next occurrence, or null
|
|
247
|
+
* @throws {Error} If no event with the ID exists
|
|
248
|
+
*/
|
|
249
|
+
getNextOccurrence(eventId: string, after?: Date | number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence | null;
|
|
250
|
+
/**
|
|
251
|
+
* The first `count` occurrences of an event inside a window, generated
|
|
252
|
+
* lazily. `count` is capped at the engine's MAX_OCCURRENCES_HARD_LIMIT.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* const nextFive = calendar.takeOccurrences('standup', 5, { after: new Date() });
|
|
256
|
+
*
|
|
257
|
+
* @param {string} eventId - The event ID
|
|
258
|
+
* @param {number} count - Maximum number of occurrences to return
|
|
259
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
260
|
+
* @returns {import('../types.js').ExpandedOccurrence[]} Up to `count` occurrences in chronological order
|
|
261
|
+
* @throws {Error} If no event with the ID exists
|
|
262
|
+
*/
|
|
263
|
+
takeOccurrences(eventId: string, count: number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence[];
|
|
194
264
|
/**
|
|
195
265
|
* Set the calendar's timezone
|
|
196
266
|
* @param {string} timezone - IANA timezone identifier
|
package/types/events/Event.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ export declare class Event {
|
|
|
23
23
|
textColor: string | undefined;
|
|
24
24
|
recurring: boolean | undefined;
|
|
25
25
|
recurrenceRule: string | import("../types.js").RecurrenceRule | undefined;
|
|
26
|
+
/** @type {boolean} True when this instance is one occurrence of a recurring series */
|
|
27
|
+
isOccurrence: boolean;
|
|
28
|
+
/** @type {string|null} Id of the recurring master this occurrence belongs to */
|
|
29
|
+
recurringEventId: string | null;
|
|
30
|
+
/** @type {Date|null} Start of this occurrence as generated by the recurrence rule */
|
|
31
|
+
occurrenceStart: Date | null;
|
|
26
32
|
_originalTimeZone: string | null;
|
|
27
33
|
status: import("../types.js").EventStatus | undefined;
|
|
28
34
|
visibility: import("../types.js").EventVisibility | undefined;
|
|
@@ -192,6 +198,39 @@ export declare class Event {
|
|
|
192
198
|
* @throws {Error} If raw event data fails {@link Event.validate}
|
|
193
199
|
*/
|
|
194
200
|
static isEquivalent(a: Event | import('../types.js').EventData, b: Event | import('../types.js').EventData): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Build the id of one occurrence of a recurring series.
|
|
203
|
+
*
|
|
204
|
+
* The id is `<recurringEventId>_<startMs>` where `startMs` is the
|
|
205
|
+
* occurrence start as returned by `Date.prototype.getTime()`. It is
|
|
206
|
+
* deterministic, so the same occurrence gets the same id no matter which
|
|
207
|
+
* range it was expanded for, and it matches the ids generated by
|
|
208
|
+
* `RecurrenceEngineV2`. Use {@link Event.parseOccurrenceId} to get the
|
|
209
|
+
* master id back.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* Event.occurrenceId('standup', new Date(2025, 5, 16, 9)); // 'standup_1750028400000'
|
|
213
|
+
*
|
|
214
|
+
* @param {string} recurringEventId - Id of the recurring master event
|
|
215
|
+
* @param {Date|number|string} occurrenceStart - Start of the occurrence
|
|
216
|
+
* @returns {string} Occurrence id
|
|
217
|
+
*/
|
|
218
|
+
static occurrenceId(recurringEventId: string, occurrenceStart: Date | number | string): string;
|
|
219
|
+
/**
|
|
220
|
+
* Split an occurrence id built by {@link Event.occurrenceId} into the master
|
|
221
|
+
* id and the occurrence start.
|
|
222
|
+
*
|
|
223
|
+
* Returns `null` for ids that do not have the `<id>_<startMs>` shape. A
|
|
224
|
+
* positive result only means the id is well-formed; whether the master
|
|
225
|
+
* exists is for the caller (see `EventStore.getEvent`) to check.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} id - Candidate occurrence id
|
|
228
|
+
* @returns {{recurringEventId: string, occurrenceStart: Date}|null} Parsed parts or null
|
|
229
|
+
*/
|
|
230
|
+
static parseOccurrenceId(id: string): {
|
|
231
|
+
recurringEventId: string;
|
|
232
|
+
occurrenceStart: Date;
|
|
233
|
+
} | null;
|
|
195
234
|
/**
|
|
196
235
|
* Add an attendee to the event
|
|
197
236
|
* @param {import('../types.js').Attendee} attendee - Attendee to add
|
|
@@ -64,9 +64,12 @@ export declare class EventStore {
|
|
|
64
64
|
addEvent(event: Event | import('../types.js').EventData): Event;
|
|
65
65
|
/**
|
|
66
66
|
* Update an existing event
|
|
67
|
-
*
|
|
67
|
+
*
|
|
68
|
+
* An occurrence id (see {@link Event.occurrenceId}) updates the recurring
|
|
69
|
+
* master the occurrence belongs to, i.e. the whole series.
|
|
70
|
+
* @param {string} eventId - Event id or occurrence id
|
|
68
71
|
* @param {Partial<import('../types.js').EventData>} updates - Properties to update
|
|
69
|
-
* @returns {Event} The updated event
|
|
72
|
+
* @returns {Event} The updated event (the master for an occurrence id)
|
|
70
73
|
* @throws {Error} If event not found
|
|
71
74
|
*/
|
|
72
75
|
updateEvent(eventId: string, updates: Partial<import('../types.js').EventData>): Event;
|
|
@@ -80,7 +83,10 @@ export declare class EventStore {
|
|
|
80
83
|
private _replaceEvent;
|
|
81
84
|
/**
|
|
82
85
|
* Remove an event from the store
|
|
83
|
-
*
|
|
86
|
+
*
|
|
87
|
+
* An occurrence id (see {@link Event.occurrenceId}) removes the recurring
|
|
88
|
+
* master the occurrence belongs to, i.e. the whole series.
|
|
89
|
+
* @param {string} eventId - Event id or occurrence id
|
|
84
90
|
* @returns {boolean} True if removed, false if not found
|
|
85
91
|
*/
|
|
86
92
|
removeEvent(eventId: string): boolean;
|
|
@@ -92,10 +98,23 @@ export declare class EventStore {
|
|
|
92
98
|
private _detachEvent;
|
|
93
99
|
/**
|
|
94
100
|
* Get an event by ID
|
|
95
|
-
*
|
|
96
|
-
*
|
|
101
|
+
*
|
|
102
|
+
* Occurrence ids produced by {@link EventStore#expandRecurringEvent}
|
|
103
|
+
* (`<masterId>_<startMs>`, see {@link Event.occurrenceId}) resolve to the
|
|
104
|
+
* stored recurring master they were derived from, so an id taken from view
|
|
105
|
+
* data can always be looked up. Occurrences themselves are never stored.
|
|
106
|
+
* @param {string} eventId - Event id or occurrence id
|
|
107
|
+
* @returns {Event|null} The stored event (the master for an occurrence id) or null
|
|
97
108
|
*/
|
|
98
109
|
getEvent(eventId: string): Event | null;
|
|
110
|
+
/**
|
|
111
|
+
* Resolve an occurrence id to the stored recurring master it belongs to.
|
|
112
|
+
* Not cached: the master's own cache entry is the one kept in sync.
|
|
113
|
+
* @param {string} eventId - Candidate occurrence id
|
|
114
|
+
* @returns {Event|null} The master event or null
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
private _resolveOccurrenceMaster;
|
|
99
118
|
/**
|
|
100
119
|
* Get all events
|
|
101
120
|
* @returns {Event[]} Array of all events
|
|
@@ -109,11 +128,59 @@ export declare class EventStore {
|
|
|
109
128
|
queryEvents(filters?: import('../types.js').QueryFilters): Event[];
|
|
110
129
|
/**
|
|
111
130
|
* Get events for a specific date
|
|
131
|
+
*
|
|
132
|
+
* Recurring series are expanded for the day, so the result holds their
|
|
133
|
+
* occurrences (see {@link EventStore#expandRecurringEvent}) rather than the
|
|
134
|
+
* master events. When building a grid of days use
|
|
135
|
+
* {@link EventStore#getEventsByDate}, which expands once for the whole range.
|
|
112
136
|
* @param {Date} date - The date to query
|
|
113
137
|
* @param {string} [timezone] - Timezone for the query (defaults to store timezone)
|
|
114
138
|
* @returns {Event[]} Events occurring on the date, sorted by start time
|
|
115
139
|
*/
|
|
116
140
|
getEventsForDate(date: Date, timezone?: string): Event[];
|
|
141
|
+
/**
|
|
142
|
+
* Get the events for every day in a range, keyed by local date (YYYY-MM-DD)
|
|
143
|
+
*
|
|
144
|
+
* Recurring series are expanded once for the whole range rather than once
|
|
145
|
+
* per day, which is what a month or week grid needs. Every day in the range
|
|
146
|
+
* has an entry (an empty array when nothing occurs) and multi-day events
|
|
147
|
+
* appear under each day they span. Each array is sorted like
|
|
148
|
+
* {@link EventStore#getEventsForDate}.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* const byDate = store.getEventsByDate(gridStart, gridEnd);
|
|
152
|
+
* const events = byDate.get(DateUtils.getLocalDateString(cellDate)) || [];
|
|
153
|
+
*
|
|
154
|
+
* @param {Date} start - First day of the range
|
|
155
|
+
* @param {Date} end - Last day of the range
|
|
156
|
+
* @param {string} [timezone] - Timezone deciding which day an event falls on (defaults to store timezone)
|
|
157
|
+
* @returns {Map<string, Event[]>} Local date string -> events on that day
|
|
158
|
+
*/
|
|
159
|
+
getEventsByDate(start: Date, end: Date, timezone?: string): Map<string, Event[]>;
|
|
160
|
+
/**
|
|
161
|
+
* Collect the ids of stored events that may occur on a date.
|
|
162
|
+
* @param {Date} date - The date to query
|
|
163
|
+
* @returns {Set<string>} Candidate event ids
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
private _collectDateCandidateIds;
|
|
167
|
+
/**
|
|
168
|
+
* Keep the events that overlap a day in the given timezone, sorted by start.
|
|
169
|
+
* @param {Event[]} events - Candidate events
|
|
170
|
+
* @param {Date} dayStart - Start of the day
|
|
171
|
+
* @param {Date} dayEnd - End of the day
|
|
172
|
+
* @param {string} timezone - Timezone deciding whether an event falls on the day
|
|
173
|
+
* @returns {Event[]} Events on the day, sorted
|
|
174
|
+
* @private
|
|
175
|
+
*/
|
|
176
|
+
private _selectEventsForDay;
|
|
177
|
+
/**
|
|
178
|
+
* Comparator ordering events by start time in a timezone, longer events first.
|
|
179
|
+
* @param {string} timezone - Timezone used for the start comparison
|
|
180
|
+
* @returns {(a: Event, b: Event) => number} Comparator
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
private _compareByStart;
|
|
117
184
|
/**
|
|
118
185
|
* Get events that overlap with a given time range
|
|
119
186
|
* @param {Date} start - Start time
|
|
@@ -138,6 +205,15 @@ export declare class EventStore {
|
|
|
138
205
|
* @returns {Array<Event[]>} Array of event groups that overlap
|
|
139
206
|
*/
|
|
140
207
|
getOverlapGroups(date: Date, timedOnly?: boolean): Array<Event[]>;
|
|
208
|
+
/**
|
|
209
|
+
* Group a list of events into clusters of overlapping time slots
|
|
210
|
+
* Same result as {@link EventStore#getOverlapGroups} for events already fetched
|
|
211
|
+
* (for example one day of {@link EventStore#getEventsByDate}).
|
|
212
|
+
* @param {Event[]} events - Events to group; the array is not modified
|
|
213
|
+
* @param {boolean} [timedOnly=true] - Only include timed events (not all-day)
|
|
214
|
+
* @returns {Array<Event[]>} Array of event groups that overlap
|
|
215
|
+
*/
|
|
216
|
+
groupOverlappingEvents(events: Event[], timedOnly?: boolean): Array<Event[]>;
|
|
141
217
|
/**
|
|
142
218
|
* Calculate positions for overlapping events (for rendering)
|
|
143
219
|
* @param {Event[]} events - Array of overlapping events
|
|
@@ -159,6 +235,16 @@ export declare class EventStore {
|
|
|
159
235
|
getEventsInRange(start: Date, end: Date, expandRecurringOrOptions?: boolean | Object, timezone?: string): Event[];
|
|
160
236
|
/**
|
|
161
237
|
* Expand a recurring event into individual occurrences
|
|
238
|
+
*
|
|
239
|
+
* Returns every occurrence that overlaps the range, including ones that
|
|
240
|
+
* start before it but run into it (multi-day series). Each occurrence is an
|
|
241
|
+
* {@link Event} cloned from the master with:
|
|
242
|
+
* - `id` from {@link Event.occurrenceId} (`<masterId>_<startMs>`), stable
|
|
243
|
+
* across ranges and resolvable with {@link EventStore#getEvent},
|
|
244
|
+
* - `isOccurrence: true`, `recurringEventId` and `occurrenceStart`,
|
|
245
|
+
* - `metadata.recurringEventId`, `metadata.occurrenceId` (same as `id`) and
|
|
246
|
+
* `metadata.occurrenceIndex` (position within this expansion).
|
|
247
|
+
* Non-recurring events are returned as-is in a one-element array.
|
|
162
248
|
* @param {Event} event - The recurring event
|
|
163
249
|
* @param {Date} rangeStart - Start of the expansion range
|
|
164
250
|
* @param {Date} rangeEnd - End of the expansion range
|
|
@@ -166,6 +252,78 @@ export declare class EventStore {
|
|
|
166
252
|
* @returns {Event[]} Array of event occurrences
|
|
167
253
|
*/
|
|
168
254
|
expandRecurringEvent(event: Event, rangeStart: Date, rangeEnd: Date, timezone?: string): Event[];
|
|
255
|
+
/**
|
|
256
|
+
* Build the Event instance for one occurrence of a recurring master.
|
|
257
|
+
* @param {Event} event - The recurring master
|
|
258
|
+
* @param {{start: Date, end: Date, timezone?: string}} occurrence - Engine occurrence
|
|
259
|
+
* @param {string} eventTimezone - Timezone the series was expanded in
|
|
260
|
+
* @param {number} index - Position within the current expansion
|
|
261
|
+
* @returns {Event} Occurrence event
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
private _createOccurrence;
|
|
265
|
+
/**
|
|
266
|
+
* Lazily iterate the occurrences of a stored event in chronological order.
|
|
267
|
+
*
|
|
268
|
+
* Occurrences come one at a time from the store's recurrence engine
|
|
269
|
+
* (RecurrenceEngineV2 by default), so taking the next few occurrences of
|
|
270
|
+
* an open-ended series does not expand the series. `after` and `before`
|
|
271
|
+
* are exclusive unless `inclusive` is set; see
|
|
272
|
+
* RecurrenceEngineV2.iterateOccurrences for the full semantics. The
|
|
273
|
+
* expansion timezone defaults to the event's, then the store's.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* for (const occurrence of store.iterateOccurrences('standup', { after: new Date() })) {
|
|
277
|
+
* if (occurrence.start > deadline) break;
|
|
278
|
+
* remind(occurrence);
|
|
279
|
+
* }
|
|
280
|
+
*
|
|
281
|
+
* @param {string} eventId - The event ID
|
|
282
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
283
|
+
* @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>} Occurrences in chronological order
|
|
284
|
+
* @throws {Error} If no event with the ID exists
|
|
285
|
+
*/
|
|
286
|
+
iterateOccurrences(eventId: string, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): Generator<import('../types.js').ExpandedOccurrence, void, undefined>;
|
|
287
|
+
/**
|
|
288
|
+
* First occurrence of a stored event after an instant, or null when the
|
|
289
|
+
* series has no occurrence after it. `after` is exclusive unless
|
|
290
|
+
* `options.inclusive` is set.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* const upcoming = store.getNextOccurrence('standup', new Date());
|
|
294
|
+
*
|
|
295
|
+
* @param {string} eventId - The event ID
|
|
296
|
+
* @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
|
|
297
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Further options
|
|
298
|
+
* @returns {import('../types.js').ExpandedOccurrence|null} The next occurrence, or null
|
|
299
|
+
* @throws {Error} If no event with the ID exists
|
|
300
|
+
*/
|
|
301
|
+
getNextOccurrence(eventId: string, after?: Date | number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence | null;
|
|
302
|
+
/**
|
|
303
|
+
* The first `count` occurrences of a stored event inside a window,
|
|
304
|
+
* generated lazily. `count` is capped at the engine's
|
|
305
|
+
* MAX_OCCURRENCES_HARD_LIMIT.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* const nextFive = store.takeOccurrences('standup', 5, { after: new Date() });
|
|
309
|
+
*
|
|
310
|
+
* @param {string} eventId - The event ID
|
|
311
|
+
* @param {number} count - Maximum number of occurrences to return
|
|
312
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
313
|
+
* @returns {import('../types.js').ExpandedOccurrence[]} Up to `count` occurrences in chronological order
|
|
314
|
+
* @throws {Error} If no event with the ID exists
|
|
315
|
+
*/
|
|
316
|
+
takeOccurrences(eventId: string, count: number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence[];
|
|
317
|
+
/**
|
|
318
|
+
* Resolve an occurrence query to the stored event and its options, with
|
|
319
|
+
* the timezone defaulted as expandRecurringEvent does
|
|
320
|
+
* @param {string} eventId - The event ID
|
|
321
|
+
* @param {Object} options - Caller options
|
|
322
|
+
* @returns {{ event: Event, options: Object }}
|
|
323
|
+
* @throws {Error} If no event with the ID exists
|
|
324
|
+
* @private
|
|
325
|
+
*/
|
|
326
|
+
private _occurrenceQuery;
|
|
169
327
|
/**
|
|
170
328
|
* Clear all events
|
|
171
329
|
*/
|
|
@@ -26,6 +26,145 @@ export declare class RecurrenceEngine {
|
|
|
26
26
|
* @returns {import('../types.js').EventOccurrence[]} Array of occurrence objects with start/end dates
|
|
27
27
|
*/
|
|
28
28
|
static expandEvent(event: import('./Event.js').Event, rangeStart: Date, rangeEnd: Date, maxOccurrences?: number, timezone?: string): import('../types.js').EventOccurrence[];
|
|
29
|
+
/**
|
|
30
|
+
* Lazily iterate the occurrences of an event in chronological order.
|
|
31
|
+
*
|
|
32
|
+
* Yields the same occurrence objects, in the same order, that expandEvent
|
|
33
|
+
* returns for the window — but one at a time, so a caller can stop after
|
|
34
|
+
* any number of them without the rest of the series being generated.
|
|
35
|
+
* Daily, weekly and sub-daily rules are seeked to `after` arithmetically,
|
|
36
|
+
* so finding the first occurrence after a far-away instant does not step
|
|
37
|
+
* through the series from its start.
|
|
38
|
+
*
|
|
39
|
+
* Both bounds are exclusive unless `inclusive` is set: an occurrence that
|
|
40
|
+
* starts exactly at `after` or `before` is skipped by default, which lets
|
|
41
|
+
* `iterateOccurrences(event, { after: previous.start })` continue a series
|
|
42
|
+
* without repeating `previous`. With `inclusive: true` the window is
|
|
43
|
+
* closed on both ends, exactly like expandEvent's range. An omitted bound
|
|
44
|
+
* leaves that end of the window open.
|
|
45
|
+
*
|
|
46
|
+
* COUNT, UNTIL, INTERVAL, BYDAY/BYMONTHDAY/BYSETPOS and exception dates
|
|
47
|
+
* are honoured as in expandEvent; BYSETPOS rules are yielded one period at
|
|
48
|
+
* a time, since the set positions of a period are only known once it is
|
|
49
|
+
* complete. A non-recurring event yields its single occurrence when it
|
|
50
|
+
* falls inside the window. Iteration ends at COUNT or UNTIL, at `before`,
|
|
51
|
+
* or — as a guard for rules that produce no occurrences — after
|
|
52
|
+
* MAX_ITERATIONS_HARD_LIMIT consecutive steps without one.
|
|
53
|
+
*
|
|
54
|
+
* The generator is single-use; call this method again for a fresh one.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* for (const occurrence of RecurrenceEngine.iterateOccurrences(event, { after: new Date() })) {
|
|
58
|
+
* if (occurrence.start > deadline) break;
|
|
59
|
+
* schedule(occurrence);
|
|
60
|
+
* }
|
|
61
|
+
*
|
|
62
|
+
* @param {import('./Event.js').Event} event - The event to iterate
|
|
63
|
+
* @param {import('../types.js').OccurrenceIteratorOptions} [options={}] - Window and timezone
|
|
64
|
+
* @returns {Generator<import('../types.js').EventOccurrence, void, undefined>} Occurrences in chronological order
|
|
65
|
+
* @throws {TypeError} If `after` or `before` is not a valid Date or timestamp
|
|
66
|
+
*/
|
|
67
|
+
static iterateOccurrences(event: import('./Event.js').Event, options?: import('../types.js').OccurrenceIteratorOptions): Generator<import('../types.js').EventOccurrence, void, undefined>;
|
|
68
|
+
/**
|
|
69
|
+
* First occurrence of an event after an instant, or null when the series
|
|
70
|
+
* has no occurrence after it (past COUNT or UNTIL, or a non-recurring
|
|
71
|
+
* event that already started).
|
|
72
|
+
*
|
|
73
|
+
* `after` is exclusive unless `options.inclusive` is set, so passing the
|
|
74
|
+
* start of a known occurrence returns the one that follows it. Not to be
|
|
75
|
+
* confused with getNextOccurrence, which steps a parsed rule once
|
|
76
|
+
* without regard to COUNT, UNTIL or exceptions.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const upcoming = RecurrenceEngine.nextOccurrence(event, new Date());
|
|
80
|
+
*
|
|
81
|
+
* @param {import('./Event.js').Event} event - The event to query
|
|
82
|
+
* @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
|
|
83
|
+
* @param {import('../types.js').OccurrenceIteratorOptions} [options={}] - Further window options
|
|
84
|
+
* @returns {import('../types.js').EventOccurrence|null} The next occurrence, or null
|
|
85
|
+
*/
|
|
86
|
+
static nextOccurrence(event: import('./Event.js').Event, after?: Date | number, options?: import('../types.js').OccurrenceIteratorOptions): import('../types.js').EventOccurrence | null;
|
|
87
|
+
/**
|
|
88
|
+
* The first `count` occurrences of an event inside a window, generated
|
|
89
|
+
* lazily so an open-ended series costs only the occurrences taken.
|
|
90
|
+
* `count` is capped at MAX_OCCURRENCES_HARD_LIMIT; fewer are returned
|
|
91
|
+
* when the series or the window ends first.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const nextFive = RecurrenceEngine.takeOccurrences(event, 5, { after: new Date() });
|
|
95
|
+
*
|
|
96
|
+
* @param {import('./Event.js').Event} event - The event to query
|
|
97
|
+
* @param {number} count - Maximum number of occurrences to return
|
|
98
|
+
* @param {import('../types.js').OccurrenceIteratorOptions} [options={}] - Window and timezone
|
|
99
|
+
* @returns {import('../types.js').EventOccurrence[]} Up to `count` occurrences in chronological order
|
|
100
|
+
*/
|
|
101
|
+
static takeOccurrences(event: import('./Event.js').Event, count: number, options?: import('../types.js').OccurrenceIteratorOptions): import('../types.js').EventOccurrence[];
|
|
102
|
+
/**
|
|
103
|
+
* Resolve iterator options into a closed window on numeric timestamps.
|
|
104
|
+
* Exclusive bounds are shifted by one millisecond, the resolution of
|
|
105
|
+
* Date, so the expansion loops only ever compare inclusively.
|
|
106
|
+
* @param {import('../types.js').OccurrenceIteratorOptions} options - Iterator options
|
|
107
|
+
* @returns {{ startMs: number, endMs: number }} Inclusive bounds (infinite when open)
|
|
108
|
+
* @throws {TypeError} If a bound is not a valid Date or timestamp
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
private static _occurrenceWindow;
|
|
112
|
+
/**
|
|
113
|
+
* Timestamp of a window bound given as a Date or a number
|
|
114
|
+
* @param {Date|number} value - Bound to convert
|
|
115
|
+
* @param {string} name - Option name for the error message
|
|
116
|
+
* @returns {number} Timestamp in milliseconds
|
|
117
|
+
* @throws {TypeError} If the value is not a valid Date or timestamp
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
private static _boundMs;
|
|
121
|
+
/**
|
|
122
|
+
* Yield a single occurrence if it starts inside the window
|
|
123
|
+
* @param {import('../types.js').EventOccurrence} occurrence - The occurrence
|
|
124
|
+
* @param {{ startMs: number, endMs: number }} window - Inclusive bounds
|
|
125
|
+
* @returns {Generator<import('../types.js').EventOccurrence, void, undefined>}
|
|
126
|
+
* @private
|
|
127
|
+
*/
|
|
128
|
+
private static _iterateSingle;
|
|
129
|
+
/**
|
|
130
|
+
* Lazy counterpart of the expansion loops: seeks to the window, then
|
|
131
|
+
* advances a Date cursor per step and yields each in-window occurrence,
|
|
132
|
+
* applying the same DST adjustment and exception filtering.
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
private static _iterateRule;
|
|
136
|
+
/**
|
|
137
|
+
* Seek the iteration cursor to the last occurrence before rangeStartMs
|
|
138
|
+
* using the same arithmetic as expandEvent. The system-transition scan
|
|
139
|
+
* is bounded by the target itself, so an open-ended window costs no
|
|
140
|
+
* more than a closed one.
|
|
141
|
+
* @param {number} fromMs - Cursor position (DTSTART)
|
|
142
|
+
* @param {number} weekday - Weekday of the cursor
|
|
143
|
+
* @param {Object} rule - Parsed recurrence rule
|
|
144
|
+
* @param {number} rangeStartMs - Seek target
|
|
145
|
+
* @returns {{ ms: number, steps: number }|null} New cursor, or null for rules that cannot seek
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
private static _seekToWindow;
|
|
149
|
+
/**
|
|
150
|
+
* Milliseconds per _advanceInPlace step for every rule whose step is a
|
|
151
|
+
* fixed duration between system-timezone transitions: the sub-daily
|
|
152
|
+
* frequencies, DAILY and plain WEEKLY
|
|
153
|
+
* @param {Object} rule - Parsed recurrence rule
|
|
154
|
+
* @returns {number} Step length in milliseconds, or 0 when not fixed
|
|
155
|
+
* @private
|
|
156
|
+
*/
|
|
157
|
+
private static _seekStepMs;
|
|
158
|
+
/**
|
|
159
|
+
* Streaming BYSETPOS filter: buffers the occurrences of one period and
|
|
160
|
+
* yields its selected positions once the next period begins. Periods
|
|
161
|
+
* arrive contiguously and in order, so the result matches _applyBySetPos.
|
|
162
|
+
* @param {Iterable<import('../types.js').EventOccurrence>} source - Occurrences in order
|
|
163
|
+
* @param {Object} rule - Rule with bySetPos
|
|
164
|
+
* @returns {Generator<import('../types.js').EventOccurrence, void, undefined>}
|
|
165
|
+
* @private
|
|
166
|
+
*/
|
|
167
|
+
private static _iterateBySetPos;
|
|
29
168
|
/**
|
|
30
169
|
* General expansion loop: advances a Date cursor per step. Handles every
|
|
31
170
|
* frequency and degenerate rules (non-advancing dates, invalid intervals).
|
|
@@ -122,6 +261,23 @@ export declare class RecurrenceEngine {
|
|
|
122
261
|
* @private
|
|
123
262
|
*/
|
|
124
263
|
private static _applyBySetPos;
|
|
264
|
+
/**
|
|
265
|
+
* Key of the BYSETPOS period an occurrence belongs to
|
|
266
|
+
* @param {import('../types.js').EventOccurrence} occurrence - The occurrence
|
|
267
|
+
* @param {Object} rule - Recurrence rule
|
|
268
|
+
* @returns {string|number} Period key
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
private static _bySetPosKey;
|
|
272
|
+
/**
|
|
273
|
+
* Occurrences of one period selected by the rule's BYSETPOS positions,
|
|
274
|
+
* in BYSETPOS order
|
|
275
|
+
* @param {Array} group - Occurrences of a single period, in order
|
|
276
|
+
* @param {Object} rule - Rule with bySetPos
|
|
277
|
+
* @returns {Array} Selected occurrences
|
|
278
|
+
* @private
|
|
279
|
+
*/
|
|
280
|
+
private static _selectBySetPos;
|
|
125
281
|
/**
|
|
126
282
|
* Parse an RRULE string into a rule object
|
|
127
283
|
* @param {string|import('../types.js').RecurrenceRule} ruleString - RRULE string (e.g., "FREQ=DAILY;INTERVAL=1;COUNT=10") or rule object
|