@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.
- package/core/calendar/Calendar.js +191 -23
- package/core/events/Event.js +106 -14
- package/core/events/EventStore.js +482 -63
- package/core/events/RRuleParser.js +19 -3
- package/core/events/RecurrenceEngine.js +427 -31
- package/core/events/RecurrenceEngineV2.js +497 -88
- package/core/index.js +1 -1
- package/core/integration/EnhancedCalendar.js +126 -14
- package/core/timezone/TimezoneManager.js +45 -9
- package/core/types.js +61 -0
- package/package.json +1 -1
- package/types/calendar/Calendar.d.ts +123 -9
- package/types/events/Event.d.ts +50 -0
- package/types/events/EventStore.d.ts +235 -13
- package/types/events/RRuleParser.d.ts +10 -1
- package/types/events/RecurrenceEngine.d.ts +159 -1
- package/types/events/RecurrenceEngineV2.d.ts +178 -15
- package/types/index.d.ts +1 -1
- package/types/integration/EnhancedCalendar.d.ts +73 -1
- package/types/types.d.ts +201 -0
|
@@ -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;
|
|
@@ -90,12 +96,67 @@ export declare class EventStore {
|
|
|
90
96
|
* @private
|
|
91
97
|
*/
|
|
92
98
|
private _detachEvent;
|
|
99
|
+
/**
|
|
100
|
+
* Drop the recurrence engine's cached expansions of one series, or of
|
|
101
|
+
* every series when no id is given. The engine is pluggable, so both
|
|
102
|
+
* hooks are optional.
|
|
103
|
+
* @param {string} [eventId] - Series to invalidate; omit to clear everything
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
private _invalidateOccurrenceCache;
|
|
93
107
|
/**
|
|
94
108
|
* Get an event by ID
|
|
95
|
-
*
|
|
96
|
-
*
|
|
109
|
+
*
|
|
110
|
+
* Occurrence ids produced by {@link EventStore#expandRecurringEvent}
|
|
111
|
+
* (`<masterId>_<startMs>`, see {@link Event.occurrenceId}) resolve to the
|
|
112
|
+
* stored recurring master they were derived from, so an id taken from view
|
|
113
|
+
* data can always be looked up. Occurrences themselves are never stored.
|
|
114
|
+
* @param {string} eventId - Event id or occurrence id
|
|
115
|
+
* @returns {Event|null} The stored event (the master for an occurrence id) or null
|
|
97
116
|
*/
|
|
98
117
|
getEvent(eventId: string): Event | null;
|
|
118
|
+
/**
|
|
119
|
+
* Resolve an occurrence id to the stored recurring master it belongs to.
|
|
120
|
+
* Not cached: the master's own cache entry is the one kept in sync.
|
|
121
|
+
* @param {string} eventId - Candidate occurrence id
|
|
122
|
+
* @returns {Event|null} The master event or null
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
private _resolveOccurrenceMaster;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve an id taken from anywhere (store, view data, a drag or click
|
|
128
|
+
* handler) to the id of the stored event it refers to.
|
|
129
|
+
*
|
|
130
|
+
* Returns the id itself for a stored event, the master's id for an
|
|
131
|
+
* occurrence id (see {@link Event.occurrenceId}) whose master is a stored
|
|
132
|
+
* recurring event, and `null` when nothing stored matches. This is the
|
|
133
|
+
* same resolution {@link EventStore#getEvent}, updateEvent and removeEvent
|
|
134
|
+
* apply, exposed for consumers that only need the id.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const masterId = store.resolveEventId(chip.dataset.eventId); // 'standup' for 'standup_1750028400000'
|
|
138
|
+
*
|
|
139
|
+
* @param {string} id - Event id or occurrence id
|
|
140
|
+
* @returns {string|null} Id of the stored event, or null
|
|
141
|
+
*/
|
|
142
|
+
resolveEventId(id: string): string | null;
|
|
143
|
+
/**
|
|
144
|
+
* Get the occurrence an occurrence id stands for, as an {@link Event}
|
|
145
|
+
* exactly like the ones {@link EventStore#expandRecurringEvent} returns.
|
|
146
|
+
*
|
|
147
|
+
* Returns `null` when the id is not an occurrence id, its master is not a
|
|
148
|
+
* stored recurring event, or the series has no occurrence starting at the
|
|
149
|
+
* encoded instant. A master id is never an occurrence, so it yields null
|
|
150
|
+
* too; use {@link EventStore#getEvent} for the master.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* const occurrence = store.getOccurrence('standup_1750028400000');
|
|
154
|
+
*
|
|
155
|
+
* @param {string} occurrenceId - Occurrence id (`<masterId>_<startMs>`)
|
|
156
|
+
* @param {string} [timezone] - Timezone for the expansion (defaults to the store timezone)
|
|
157
|
+
* @returns {Event|null} The occurrence, or null
|
|
158
|
+
*/
|
|
159
|
+
getOccurrence(occurrenceId: string, timezone?: string): Event | null;
|
|
99
160
|
/**
|
|
100
161
|
* Get all events
|
|
101
162
|
* @returns {Event[]} Array of all events
|
|
@@ -109,11 +170,59 @@ export declare class EventStore {
|
|
|
109
170
|
queryEvents(filters?: import('../types.js').QueryFilters): Event[];
|
|
110
171
|
/**
|
|
111
172
|
* Get events for a specific date
|
|
173
|
+
*
|
|
174
|
+
* Recurring series are expanded for the day, so the result holds their
|
|
175
|
+
* occurrences (see {@link EventStore#expandRecurringEvent}) rather than the
|
|
176
|
+
* master events. When building a grid of days use
|
|
177
|
+
* {@link EventStore#getEventsByDate}, which expands once for the whole range.
|
|
112
178
|
* @param {Date} date - The date to query
|
|
113
179
|
* @param {string} [timezone] - Timezone for the query (defaults to store timezone)
|
|
114
180
|
* @returns {Event[]} Events occurring on the date, sorted by start time
|
|
115
181
|
*/
|
|
116
182
|
getEventsForDate(date: Date, timezone?: string): Event[];
|
|
183
|
+
/**
|
|
184
|
+
* Get the events for every day in a range, keyed by local date (YYYY-MM-DD)
|
|
185
|
+
*
|
|
186
|
+
* Recurring series are expanded once for the whole range rather than once
|
|
187
|
+
* per day, which is what a month or week grid needs. Every day in the range
|
|
188
|
+
* has an entry (an empty array when nothing occurs) and multi-day events
|
|
189
|
+
* appear under each day they span. Each array is sorted like
|
|
190
|
+
* {@link EventStore#getEventsForDate}.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* const byDate = store.getEventsByDate(gridStart, gridEnd);
|
|
194
|
+
* const events = byDate.get(DateUtils.getLocalDateString(cellDate)) || [];
|
|
195
|
+
*
|
|
196
|
+
* @param {Date} start - First day of the range
|
|
197
|
+
* @param {Date} end - Last day of the range
|
|
198
|
+
* @param {string} [timezone] - Timezone deciding which day an event falls on (defaults to store timezone)
|
|
199
|
+
* @returns {Map<string, Event[]>} Local date string -> events on that day
|
|
200
|
+
*/
|
|
201
|
+
getEventsByDate(start: Date, end: Date, timezone?: string): Map<string, Event[]>;
|
|
202
|
+
/**
|
|
203
|
+
* Collect the ids of stored events that may occur on a date.
|
|
204
|
+
* @param {Date} date - The date to query
|
|
205
|
+
* @returns {Set<string>} Candidate event ids
|
|
206
|
+
* @private
|
|
207
|
+
*/
|
|
208
|
+
private _collectDateCandidateIds;
|
|
209
|
+
/**
|
|
210
|
+
* Keep the events that overlap a day in the given timezone, sorted by start.
|
|
211
|
+
* @param {Event[]} events - Candidate events
|
|
212
|
+
* @param {Date} dayStart - Start of the day
|
|
213
|
+
* @param {Date} dayEnd - End of the day
|
|
214
|
+
* @param {string} timezone - Timezone deciding whether an event falls on the day
|
|
215
|
+
* @returns {Event[]} Events on the day, sorted
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
private _selectEventsForDay;
|
|
219
|
+
/**
|
|
220
|
+
* Comparator ordering events by start time in a timezone, longer events first.
|
|
221
|
+
* @param {string} timezone - Timezone used for the start comparison
|
|
222
|
+
* @returns {(a: Event, b: Event) => number} Comparator
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
private _compareByStart;
|
|
117
226
|
/**
|
|
118
227
|
* Get events that overlap with a given time range
|
|
119
228
|
* @param {Date} start - Start time
|
|
@@ -138,6 +247,15 @@ export declare class EventStore {
|
|
|
138
247
|
* @returns {Array<Event[]>} Array of event groups that overlap
|
|
139
248
|
*/
|
|
140
249
|
getOverlapGroups(date: Date, timedOnly?: boolean): Array<Event[]>;
|
|
250
|
+
/**
|
|
251
|
+
* Group a list of events into clusters of overlapping time slots
|
|
252
|
+
* Same result as {@link EventStore#getOverlapGroups} for events already fetched
|
|
253
|
+
* (for example one day of {@link EventStore#getEventsByDate}).
|
|
254
|
+
* @param {Event[]} events - Events to group; the array is not modified
|
|
255
|
+
* @param {boolean} [timedOnly=true] - Only include timed events (not all-day)
|
|
256
|
+
* @returns {Array<Event[]>} Array of event groups that overlap
|
|
257
|
+
*/
|
|
258
|
+
groupOverlappingEvents(events: Event[], timedOnly?: boolean): Array<Event[]>;
|
|
141
259
|
/**
|
|
142
260
|
* Calculate positions for overlapping events (for rendering)
|
|
143
261
|
* @param {Event[]} events - Array of overlapping events
|
|
@@ -159,6 +277,16 @@ export declare class EventStore {
|
|
|
159
277
|
getEventsInRange(start: Date, end: Date, expandRecurringOrOptions?: boolean | Object, timezone?: string): Event[];
|
|
160
278
|
/**
|
|
161
279
|
* Expand a recurring event into individual occurrences
|
|
280
|
+
*
|
|
281
|
+
* Returns every occurrence that overlaps the range, including ones that
|
|
282
|
+
* start before it but run into it (multi-day series). Each occurrence is an
|
|
283
|
+
* {@link Event} cloned from the master with:
|
|
284
|
+
* - `id` from {@link Event.occurrenceId} (`<masterId>_<startMs>`), stable
|
|
285
|
+
* across ranges and resolvable with {@link EventStore#getEvent},
|
|
286
|
+
* - `isOccurrence: true`, `recurringEventId` and `occurrenceStart`,
|
|
287
|
+
* - `metadata.recurringEventId`, `metadata.occurrenceId` (same as `id`) and
|
|
288
|
+
* `metadata.occurrenceIndex` (position within this expansion).
|
|
289
|
+
* Non-recurring events are returned as-is in a one-element array.
|
|
162
290
|
* @param {Event} event - The recurring event
|
|
163
291
|
* @param {Date} rangeStart - Start of the expansion range
|
|
164
292
|
* @param {Date} rangeEnd - End of the expansion range
|
|
@@ -166,6 +294,78 @@ export declare class EventStore {
|
|
|
166
294
|
* @returns {Event[]} Array of event occurrences
|
|
167
295
|
*/
|
|
168
296
|
expandRecurringEvent(event: Event, rangeStart: Date, rangeEnd: Date, timezone?: string): Event[];
|
|
297
|
+
/**
|
|
298
|
+
* Build the Event instance for one occurrence of a recurring master.
|
|
299
|
+
* @param {Event} event - The recurring master
|
|
300
|
+
* @param {{start: Date, end: Date, timezone?: string}} occurrence - Engine occurrence
|
|
301
|
+
* @param {string} eventTimezone - Timezone the series was expanded in
|
|
302
|
+
* @param {number} index - Position within the current expansion
|
|
303
|
+
* @returns {Event} Occurrence event
|
|
304
|
+
* @private
|
|
305
|
+
*/
|
|
306
|
+
private _createOccurrence;
|
|
307
|
+
/**
|
|
308
|
+
* Lazily iterate the occurrences of a stored event in chronological order.
|
|
309
|
+
*
|
|
310
|
+
* Occurrences come one at a time from the store's recurrence engine
|
|
311
|
+
* (RecurrenceEngineV2 by default), so taking the next few occurrences of
|
|
312
|
+
* an open-ended series does not expand the series. `after` and `before`
|
|
313
|
+
* are exclusive unless `inclusive` is set; see
|
|
314
|
+
* RecurrenceEngineV2.iterateOccurrences for the full semantics. The
|
|
315
|
+
* expansion timezone defaults to the event's, then the store's.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* for (const occurrence of store.iterateOccurrences('standup', { after: new Date() })) {
|
|
319
|
+
* if (occurrence.start > deadline) break;
|
|
320
|
+
* remind(occurrence);
|
|
321
|
+
* }
|
|
322
|
+
*
|
|
323
|
+
* @param {string} eventId - Event id or occurrence id (resolved to its master)
|
|
324
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
325
|
+
* @returns {Generator<import('../types.js').ExpandedOccurrence, void, undefined>} Occurrences in chronological order
|
|
326
|
+
* @throws {Error} If no event with the ID exists
|
|
327
|
+
*/
|
|
328
|
+
iterateOccurrences(eventId: string, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): Generator<import('../types.js').ExpandedOccurrence, void, undefined>;
|
|
329
|
+
/**
|
|
330
|
+
* First occurrence of a stored event after an instant, or null when the
|
|
331
|
+
* series has no occurrence after it. `after` is exclusive unless
|
|
332
|
+
* `options.inclusive` is set.
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* const upcoming = store.getNextOccurrence('standup', new Date());
|
|
336
|
+
*
|
|
337
|
+
* @param {string} eventId - Event id or occurrence id (resolved to its master)
|
|
338
|
+
* @param {Date|number} [after=null] - Instant to search from (defaults to the series start)
|
|
339
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Further options
|
|
340
|
+
* @returns {import('../types.js').ExpandedOccurrence|null} The next occurrence, or null
|
|
341
|
+
* @throws {Error} If no event with the ID exists
|
|
342
|
+
*/
|
|
343
|
+
getNextOccurrence(eventId: string, after?: Date | number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence | null;
|
|
344
|
+
/**
|
|
345
|
+
* The first `count` occurrences of a stored event inside a window,
|
|
346
|
+
* generated lazily. `count` is capped at the engine's
|
|
347
|
+
* MAX_OCCURRENCES_HARD_LIMIT.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* const nextFive = store.takeOccurrences('standup', 5, { after: new Date() });
|
|
351
|
+
*
|
|
352
|
+
* @param {string} eventId - Event id or occurrence id (resolved to its master)
|
|
353
|
+
* @param {number} count - Maximum number of occurrences to return (fractions are floored)
|
|
354
|
+
* @param {import('../types.js').ExpandedOccurrenceIteratorOptions} [options={}] - Window and expansion options
|
|
355
|
+
* @returns {import('../types.js').ExpandedOccurrence[]} Up to `count` occurrences in chronological order
|
|
356
|
+
* @throws {Error} If no event with the ID exists
|
|
357
|
+
*/
|
|
358
|
+
takeOccurrences(eventId: string, count: number, options?: import('../types.js').ExpandedOccurrenceIteratorOptions): import('../types.js').ExpandedOccurrence[];
|
|
359
|
+
/**
|
|
360
|
+
* Resolve an occurrence query to the stored event and its options, with
|
|
361
|
+
* the timezone defaulted as expandRecurringEvent does
|
|
362
|
+
* @param {string} eventId - The event ID
|
|
363
|
+
* @param {Object} options - Caller options
|
|
364
|
+
* @returns {{ event: Event, options: Object }}
|
|
365
|
+
* @throws {Error} If no event with the ID exists
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
private _occurrenceQuery;
|
|
169
369
|
/**
|
|
170
370
|
* Clear all events
|
|
171
371
|
*/
|
|
@@ -185,13 +385,23 @@ export declare class EventStore {
|
|
|
185
385
|
* - adds events whose id is not in the store (`add` change),
|
|
186
386
|
* - removes stored events missing from the snapshot (`remove` change),
|
|
187
387
|
* unless `removeMissing` is `false`,
|
|
388
|
+
* - treats occurrences of a recurring series (entries with
|
|
389
|
+
* `isOccurrence: true`, the plain object of such an occurrence, or an id
|
|
390
|
+
* that {@link EventStore#getEvent} resolves to a stored recurring master)
|
|
391
|
+
* as a reference to their master: the master is kept as unchanged and is
|
|
392
|
+
* never replaced by an occurrence. An occurrence whose master is neither
|
|
393
|
+
* stored nor in the snapshot is an error,
|
|
188
394
|
* - emits a single `batch` notification listing those changes, or nothing at
|
|
189
395
|
* all when the snapshot matches the store. When called while a batch is
|
|
190
396
|
* already open the changes are queued on that batch instead.
|
|
191
397
|
*
|
|
192
|
-
* Input is validated up front: invalid event data
|
|
193
|
-
* before the store is modified.
|
|
194
|
-
*
|
|
398
|
+
* Input is validated up front: invalid event data, duplicate ids and
|
|
399
|
+
* occurrences without a master throw before the store is modified. When
|
|
400
|
+
* reconcile opens the batch itself, any error raised while applying the
|
|
401
|
+
* diff rolls the store back to its previous state; inside a batch opened
|
|
402
|
+
* by the caller the changes applied so far stay queued on that batch, and
|
|
403
|
+
* it is the caller's rollbackBatch() that undoes them (a custom
|
|
404
|
+
* `isEquivalent` that throws is the usual way to get there).
|
|
195
405
|
*
|
|
196
406
|
* @example
|
|
197
407
|
* // periodic server snapshot
|
|
@@ -204,6 +414,16 @@ export declare class EventStore {
|
|
|
204
414
|
* @throws {Error} If an entry fails validation or two entries share an id
|
|
205
415
|
*/
|
|
206
416
|
reconcile(events: Array<Event | import('../types.js').EventData>, options?: import('../types.js').ReconcileOptions): import('../types.js').ReconcileResult;
|
|
417
|
+
/**
|
|
418
|
+
* Id of the recurring master a reconcile entry is an occurrence of, or
|
|
419
|
+
* null when the entry is an event in its own right. Recognises Event
|
|
420
|
+
* occurrences (isOccurrence), their toObject() form (occurrence markers
|
|
421
|
+
* in metadata) and ids that resolve to a stored recurring master.
|
|
422
|
+
* @param {Event|import('../types.js').EventData} eventData - Reconcile entry
|
|
423
|
+
* @returns {string|null} Master id or null
|
|
424
|
+
* @private
|
|
425
|
+
*/
|
|
426
|
+
private _occurrenceMasterId;
|
|
207
427
|
/**
|
|
208
428
|
* Subscribe to store changes
|
|
209
429
|
* @param {Function} callback - Callback function
|
|
@@ -241,16 +461,18 @@ export declare class EventStore {
|
|
|
241
461
|
*/
|
|
242
462
|
private _removeFromReferencedIndex;
|
|
243
463
|
/**
|
|
244
|
-
*
|
|
464
|
+
* Deliver a change now, or queue it when a batch is open
|
|
465
|
+
* @param {import('../types.js').EventStoreChange} change - Change to deliver
|
|
245
466
|
* @private
|
|
246
467
|
*/
|
|
468
|
+
private _queueChange;
|
|
247
469
|
/**
|
|
248
|
-
* Deliver a change
|
|
470
|
+
* Deliver a change to every subscriber now, regardless of batch mode.
|
|
471
|
+
* A listener that throws is reported and does not stop the others.
|
|
249
472
|
* @param {import('../types.js').EventStoreChange} change - Change to deliver
|
|
250
473
|
* @private
|
|
251
474
|
*/
|
|
252
|
-
private
|
|
253
|
-
_notifyChange(change: any): void;
|
|
475
|
+
private _notifyChange;
|
|
254
476
|
/**
|
|
255
477
|
* Get store statistics
|
|
256
478
|
* @returns {Object}
|
|
@@ -308,7 +530,7 @@ export declare class EventStore {
|
|
|
308
530
|
*/
|
|
309
531
|
getPerformanceMetrics(): Object;
|
|
310
532
|
/**
|
|
311
|
-
* Clear all caches
|
|
533
|
+
* Clear all caches, including the recurrence engine's cached expansions
|
|
312
534
|
*/
|
|
313
535
|
clearCaches(): void;
|
|
314
536
|
/**
|
|
@@ -36,7 +36,16 @@ export declare class RRuleParser {
|
|
|
36
36
|
*/
|
|
37
37
|
private static parseExceptionDates;
|
|
38
38
|
/**
|
|
39
|
-
* Validate and normalize rule
|
|
39
|
+
* Validate and normalize a rule.
|
|
40
|
+
*
|
|
41
|
+
* Works on a copy: rule objects handed in are typically the stored
|
|
42
|
+
* recurrenceRule of an Event, and normalising them in place would make
|
|
43
|
+
* the stored event differ from the data it was created from (a spurious
|
|
44
|
+
* update on the next reconcile) and let the engines' per-rule caches leak
|
|
45
|
+
* into it. The copy is shallow except for the array fields, which are
|
|
46
|
+
* copied as well.
|
|
47
|
+
* @param {Object} rule - Rule object (not modified)
|
|
48
|
+
* @returns {Object} Normalised copy
|
|
40
49
|
* @private
|
|
41
50
|
*/
|
|
42
51
|
private static validateRule;
|
|
@@ -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 (fractions are floored)
|
|
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).
|
|
@@ -100,7 +239,9 @@ export declare class RecurrenceEngine {
|
|
|
100
239
|
private static _seekWeekCycle;
|
|
101
240
|
/**
|
|
102
241
|
* Find the next system-timezone offset transition after fromMs.
|
|
103
|
-
* Cached module-wide: the system timezone is fixed for the process.
|
|
242
|
+
* Cached module-wide: the system timezone is fixed for the process. The
|
|
243
|
+
* cache grows incrementally, so extending coverage (a view navigating
|
|
244
|
+
* forward, a series with a far-past DTSTART) scans only the new span.
|
|
104
245
|
* @param {number} fromMs - Search from this timestamp (exclusive)
|
|
105
246
|
* @param {number} toMs - Extend cache coverage at least this far
|
|
106
247
|
* @returns {number} Transition timestamp, or Infinity if none within coverage
|
|
@@ -122,6 +263,23 @@ export declare class RecurrenceEngine {
|
|
|
122
263
|
* @private
|
|
123
264
|
*/
|
|
124
265
|
private static _applyBySetPos;
|
|
266
|
+
/**
|
|
267
|
+
* Key of the BYSETPOS period an occurrence belongs to
|
|
268
|
+
* @param {import('../types.js').EventOccurrence} occurrence - The occurrence
|
|
269
|
+
* @param {Object} rule - Recurrence rule
|
|
270
|
+
* @returns {string|number} Period key
|
|
271
|
+
* @private
|
|
272
|
+
*/
|
|
273
|
+
private static _bySetPosKey;
|
|
274
|
+
/**
|
|
275
|
+
* Occurrences of one period selected by the rule's BYSETPOS positions,
|
|
276
|
+
* in BYSETPOS order
|
|
277
|
+
* @param {Array} group - Occurrences of a single period, in order
|
|
278
|
+
* @param {Object} rule - Rule with bySetPos
|
|
279
|
+
* @returns {Array} Selected occurrences
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
private static _selectBySetPos;
|
|
125
283
|
/**
|
|
126
284
|
* Parse an RRULE string into a rule object
|
|
127
285
|
* @param {string|import('../types.js').RecurrenceRule} ruleString - RRULE string (e.g., "FREQ=DAILY;INTERVAL=1;COUNT=10") or rule object
|