@forcecalendar/core 2.3.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 +198 -14
- package/core/events/Event.js +173 -0
- package/core/events/EventStore.js +484 -92
- package/core/events/RecurrenceEngine.js +586 -20
- package/core/events/RecurrenceEngineV2.js +384 -51
- package/core/index.js +1 -1
- package/core/integration/EnhancedCalendar.js +88 -14
- package/core/types.js +95 -1
- package/package.json +1 -1
- package/types/calendar/Calendar.d.ts +137 -10
- package/types/events/Event.d.ts +75 -0
- package/types/events/EventStore.d.ts +213 -6
- package/types/events/RecurrenceEngine.d.ts +216 -1
- package/types/events/RecurrenceEngineV2.d.ts +156 -9
- package/types/index.d.ts +1 -1
- package/types/integration/EnhancedCalendar.d.ts +66 -1
- package/types/types.d.ts +296 -2
package/core/types.js
CHANGED
|
@@ -275,13 +275,55 @@
|
|
|
275
275
|
|
|
276
276
|
/**
|
|
277
277
|
* @typedef {Object} EventStoreChange
|
|
278
|
-
* @property {('add'|'update'|'remove'|'clear')} type - Type of change
|
|
278
|
+
* @property {('add'|'update'|'remove'|'clear'|'batch')} type - Type of change
|
|
279
279
|
* @property {import('./events/Event.js').Event} [event] - Affected event
|
|
280
280
|
* @property {import('./events/Event.js').Event} [oldEvent] - Previous event state (for updates)
|
|
281
281
|
* @property {import('./events/Event.js').Event[]} [oldEvents] - Previous events (for clear)
|
|
282
|
+
* @property {EventStoreChange[]} [changes] - Individual changes (for batch)
|
|
283
|
+
* @property {number} [count] - Number of individual changes (for batch)
|
|
282
284
|
* @property {number} version - Store version number
|
|
283
285
|
*/
|
|
284
286
|
|
|
287
|
+
/**
|
|
288
|
+
* @typedef {(a: import('./events/Event.js').Event, b: import('./events/Event.js').Event) => boolean} EventEquivalenceFn
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @typedef {Object} ReconcileOptions
|
|
293
|
+
* @property {boolean} [removeMissing=true] - Remove stored events that are absent from the snapshot
|
|
294
|
+
* @property {EventEquivalenceFn} [isEquivalent] - Comparator deciding whether a stored event is unchanged (defaults to Event.isEquivalent)
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @typedef {Object} ReconciledUpdate
|
|
299
|
+
* @property {import('./events/Event.js').Event} event - Event now in the store
|
|
300
|
+
* @property {import('./events/Event.js').Event} oldEvent - Event it replaced
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @typedef {Object} ReconcileResult
|
|
305
|
+
* @property {import('./events/Event.js').Event[]} added - Events that were not in the store before
|
|
306
|
+
* @property {ReconciledUpdate[]} updated - Events whose data changed
|
|
307
|
+
* @property {import('./events/Event.js').Event[]} removed - Events removed from the store
|
|
308
|
+
* @property {import('./events/Event.js').Event[]} unchanged - Stored events left untouched (same instances)
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @typedef {Object} SetEventsOptions
|
|
313
|
+
* @property {boolean} [reconcile=false] - Apply only the differences instead of clearing and re-adding
|
|
314
|
+
* @property {boolean} [removeMissing=true] - Reconcile only: remove stored events absent from the snapshot
|
|
315
|
+
* @property {EventEquivalenceFn} [isEquivalent] - Reconcile only: custom equivalence comparator
|
|
316
|
+
*/
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @typedef {Object} EventsSetPayload
|
|
320
|
+
* @property {import('./events/Event.js').Event[]} events - All events after the operation
|
|
321
|
+
* @property {import('./events/Event.js').Event[]} added - Events added by the operation
|
|
322
|
+
* @property {ReconciledUpdate[]} updated - Events replaced by the operation
|
|
323
|
+
* @property {import('./events/Event.js').Event[]} removed - Events removed by the operation
|
|
324
|
+
* @property {import('./events/Event.js').Event[]} unchanged - Events left untouched
|
|
325
|
+
*/
|
|
326
|
+
|
|
285
327
|
/**
|
|
286
328
|
* @typedef {Object} QueryFilters
|
|
287
329
|
* @property {Date} [start] - Start date for range query
|
|
@@ -304,6 +346,58 @@
|
|
|
304
346
|
* @property {Date} start - Occurrence start date
|
|
305
347
|
* @property {Date} end - Occurrence end date
|
|
306
348
|
* @property {string} recurringEventId - ID of the parent recurring event
|
|
349
|
+
* @property {string} [timezone] - Timezone the occurrence was expanded in
|
|
350
|
+
* @property {Date} [originalStart] - Start of the series (DTSTART)
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Window for lazy occurrence iteration. Both bounds are exclusive unless
|
|
355
|
+
* `inclusive` is set: an occurrence starting exactly at `after` or `before`
|
|
356
|
+
* is skipped by default, so iterating from a known occurrence's start
|
|
357
|
+
* continues the series without repeating it. Omit a bound to leave that
|
|
358
|
+
* end of the window open.
|
|
359
|
+
* @typedef {Object} OccurrenceIteratorOptions
|
|
360
|
+
* @property {Date|number} [after] - Only occurrences starting after this instant (Date or timestamp)
|
|
361
|
+
* @property {Date|number} [before] - Only occurrences starting before this instant (Date or timestamp)
|
|
362
|
+
* @property {boolean} [inclusive=false] - Treat `after` and `before` as closed bounds
|
|
363
|
+
* @property {string} [timezone] - Timezone for expansion (defaults to the event's)
|
|
364
|
+
*/
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Options for lazy occurrence iteration through RecurrenceEngineV2,
|
|
368
|
+
* EventStore and Calendar: the OccurrenceIteratorOptions window plus the
|
|
369
|
+
* expansion switches RecurrenceEngineV2.expandEvent accepts.
|
|
370
|
+
* @typedef {Object} ExpandedOccurrenceIteratorOptions
|
|
371
|
+
* @property {Date|number} [after] - Only occurrences starting after this instant (Date or timestamp)
|
|
372
|
+
* @property {Date|number} [before] - Only occurrences starting before this instant (Date or timestamp)
|
|
373
|
+
* @property {boolean} [inclusive=false] - Treat `after` and `before` as closed bounds
|
|
374
|
+
* @property {string} [timezone] - Timezone for expansion (defaults to the event's)
|
|
375
|
+
* @property {boolean} [includeModified=true] - Apply stored instance modifications
|
|
376
|
+
* @property {boolean} [includeCancelled=false] - Yield exception dates as cancelled occurrences
|
|
377
|
+
* @property {boolean} [handleDST=true] - Adjust occurrences across DST transitions
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Occurrence produced by RecurrenceEngineV2 (and therefore by EventStore
|
|
382
|
+
* and Calendar occurrence queries)
|
|
383
|
+
* @typedef {Object} ExpandedOccurrence
|
|
384
|
+
* @property {string} id - Occurrence ID (`<eventId>_<startTimestamp>` for recurring events)
|
|
385
|
+
* @property {string} [recurringEventId] - ID of the parent recurring event
|
|
386
|
+
* @property {string} title - Event title
|
|
387
|
+
* @property {Date} start - Occurrence start date
|
|
388
|
+
* @property {Date} end - Occurrence end date
|
|
389
|
+
* @property {Date} [startUTC] - Occurrence start in UTC
|
|
390
|
+
* @property {Date} [endUTC] - Occurrence end in UTC
|
|
391
|
+
* @property {string} timezone - Timezone the occurrence was expanded in
|
|
392
|
+
* @property {Date} [originalStart] - Start of the series (DTSTART)
|
|
393
|
+
* @property {boolean} allDay - Whether the event is all-day
|
|
394
|
+
* @property {string} [description] - Event description
|
|
395
|
+
* @property {string} [location] - Event location
|
|
396
|
+
* @property {string[]} [categories] - Event categories
|
|
397
|
+
* @property {EventStatus} [status] - 'confirmed', or 'cancelled' for exception dates yielded with includeCancelled
|
|
398
|
+
* @property {string} [cancellationReason] - Reason recorded for a cancelled occurrence
|
|
399
|
+
* @property {boolean} isRecurring - Whether the occurrence belongs to a recurring series
|
|
400
|
+
* @property {boolean} [isModified] - Whether a stored instance modification was applied
|
|
307
401
|
*/
|
|
308
402
|
|
|
309
403
|
/**
|
package/package.json
CHANGED
|
@@ -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,20 +105,81 @@ 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[];
|
|
111
121
|
/**
|
|
112
122
|
* Set all events (replaces existing)
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
*
|
|
124
|
+
* By default this clears the store and re-adds every entry, so every stored
|
|
125
|
+
* {@link Event} instance is replaced. Pass `{ reconcile: true }` to apply only
|
|
126
|
+
* the differences instead (see {@link Calendar#reconcileEvents}).
|
|
127
|
+
*
|
|
128
|
+
* Emits a single `eventsSet` event whose payload lists the resulting
|
|
129
|
+
* `events` plus the `added`, `updated`, `removed` and `unchanged` sets, so
|
|
130
|
+
* listeners can tell a snapshot load apart from user mutations (no
|
|
131
|
+
* `eventAdd`/`eventUpdate`/`eventRemove` events are emitted).
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* calendar.on('eventsSet', ({ added, updated, removed }) => {
|
|
135
|
+
* if (added.length || updated.length || removed.length) render();
|
|
136
|
+
* });
|
|
137
|
+
* calendar.setEvents(snapshot, { reconcile: true });
|
|
138
|
+
*
|
|
139
|
+
* @param {Array<import('../events/Event.js').Event|import('../types.js').EventData>} events - Array of events
|
|
140
|
+
* @param {import('../types.js').SetEventsOptions} [options={}] - Load options
|
|
141
|
+
* @returns {import('../types.js').EventsSetPayload} The applied change set
|
|
142
|
+
*/
|
|
143
|
+
setEvents(events: Array<import('../events/Event.js').Event | import('../types.js').EventData>, options?: import('../types.js').SetEventsOptions): import('../types.js').EventsSetPayload;
|
|
144
|
+
/**
|
|
145
|
+
* Reconcile the calendar with a snapshot of events, applying only the differences.
|
|
146
|
+
*
|
|
147
|
+
* Intended for consumers that receive periodic full snapshots (polling a
|
|
148
|
+
* server, a reactive `events` prop). Unchanged events keep their existing
|
|
149
|
+
* {@link Event} instance, changed ones are replaced, new ones are added and
|
|
150
|
+
* events missing from the snapshot are removed (unless
|
|
151
|
+
* `removeMissing: false`). Equivalence is decided by
|
|
152
|
+
* {@link Event.isEquivalent} unless an `isEquivalent` comparator is supplied.
|
|
153
|
+
* Plain event data without a `timeZone` defaults to the calendar timezone,
|
|
154
|
+
* exactly as with {@link Calendar#addEvent}.
|
|
155
|
+
*
|
|
156
|
+
* The store emits one `eventStoreChange` of type `batch` (or none when
|
|
157
|
+
* nothing differs) and the calendar emits a single `eventsSet` event
|
|
158
|
+
* carrying the change set. Per-event `eventAdd`/`eventUpdate`/`eventRemove`
|
|
159
|
+
* events are not emitted, so listeners that forward those to a backend are
|
|
160
|
+
* not triggered by a snapshot load.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* const { added, updated, removed, unchanged } = calendar.reconcileEvents(rows);
|
|
164
|
+
* updated.forEach(({ event, oldEvent }) => console.log(oldEvent.title, '->', event.title));
|
|
165
|
+
*
|
|
166
|
+
* @param {Array<import('../events/Event.js').Event|import('../types.js').EventData>} events - Complete snapshot of events
|
|
167
|
+
* @param {import('../types.js').ReconcileOptions} [options={}] - Reconcile options
|
|
168
|
+
* @returns {import('../types.js').EventsSetPayload} Resulting events and the applied change set
|
|
169
|
+
* @throws {Error} If an entry fails validation or two entries share an id
|
|
170
|
+
*/
|
|
171
|
+
reconcileEvents(events: Array<import('../events/Event.js').Event | import('../types.js').EventData>, options?: import('../types.js').ReconcileOptions): import('../types.js').EventsSetPayload;
|
|
172
|
+
/**
|
|
173
|
+
* Get the event store's change counter
|
|
174
|
+
*
|
|
175
|
+
* The counter increases with every add/update/remove/clear and every
|
|
176
|
+
* committed batch, so comparing two readings is a cheap way to find out
|
|
177
|
+
* whether {@link Calendar#setEvents} or {@link Calendar#reconcileEvents}
|
|
178
|
+
* changed anything.
|
|
179
|
+
*
|
|
180
|
+
* @returns {number} Current store version
|
|
181
|
+
*/
|
|
182
|
+
getEventsVersion(): number;
|
|
116
183
|
/**
|
|
117
184
|
* Query events with filters
|
|
118
185
|
* @param {Object} filters - Query filters
|
|
@@ -120,12 +187,23 @@ export declare class Calendar {
|
|
|
120
187
|
*/
|
|
121
188
|
queryEvents(filters: Object): Event[];
|
|
122
189
|
/**
|
|
123
|
-
* Get events for a specific date
|
|
190
|
+
* Get events for a specific date, with recurring series expanded into occurrences
|
|
124
191
|
* @param {Date} date - The date
|
|
125
192
|
* @param {string} [timezone] - Timezone for the query (defaults to calendar timezone)
|
|
126
193
|
* @returns {Event[]}
|
|
127
194
|
*/
|
|
128
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[]>;
|
|
129
207
|
/**
|
|
130
208
|
* Get events in a date range
|
|
131
209
|
* @param {Date} start - Start date
|
|
@@ -134,6 +212,55 @@ export declare class Calendar {
|
|
|
134
212
|
* @returns {Event[]}
|
|
135
213
|
*/
|
|
136
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[];
|
|
137
264
|
/**
|
|
138
265
|
* Set the calendar's timezone
|
|
139
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;
|
|
@@ -156,6 +162,75 @@ export declare class Event {
|
|
|
156
162
|
* @returns {boolean} True if events are equal
|
|
157
163
|
*/
|
|
158
164
|
equals(other: Event): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Fields compared by {@link Event.isEquivalent}, in comparison order.
|
|
167
|
+
* Scalars are compared with strict equality, dates by timestamp and
|
|
168
|
+
* structured fields (recurrence rule, organizer, attendees, reminders,
|
|
169
|
+
* categories, attachments, conference data, metadata) structurally.
|
|
170
|
+
* @type {ReadonlyArray<string>}
|
|
171
|
+
*/
|
|
172
|
+
static EQUIVALENCE_FIELDS: ReadonlyArray<string>;
|
|
173
|
+
/**
|
|
174
|
+
* Deep equivalence check over the full event data surface.
|
|
175
|
+
*
|
|
176
|
+
* Unlike {@link Event#equals} (which only looks at identity, title, dates,
|
|
177
|
+
* description, location, recurrence and status) this compares every field
|
|
178
|
+
* that can be supplied through {@link EventData}: timezones, all-day flag,
|
|
179
|
+
* colours, visibility, organizer, attendees, reminders, categories,
|
|
180
|
+
* attachments, conference data and metadata. Dates are compared by
|
|
181
|
+
* timestamp; structured fields are compared structurally (arrays are
|
|
182
|
+
* order-sensitive, object key order is ignored). Plain event data objects
|
|
183
|
+
* are normalized through the {@link Event} constructor before comparison so
|
|
184
|
+
* that `{ color: 'red' }` and `{ backgroundColor: 'red', borderColor: 'red' }`
|
|
185
|
+
* describe the same event. Two events with different ids are never
|
|
186
|
+
* equivalent.
|
|
187
|
+
*
|
|
188
|
+
* This is the default comparator used by `EventStore.reconcile()` to decide
|
|
189
|
+
* whether an incoming snapshot entry replaces the stored event.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* Event.isEquivalent(stored, { ...stored.toObject(), backgroundColor: '#f00' }); // false
|
|
193
|
+
* Event.isEquivalent(stored, stored.clone()); // true
|
|
194
|
+
*
|
|
195
|
+
* @param {Event|import('../types.js').EventData} a - First event or raw event data
|
|
196
|
+
* @param {Event|import('../types.js').EventData} b - Second event or raw event data
|
|
197
|
+
* @returns {boolean} True when both describe the same event data
|
|
198
|
+
* @throws {Error} If raw event data fails {@link Event.validate}
|
|
199
|
+
*/
|
|
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;
|
|
159
234
|
/**
|
|
160
235
|
* Add an attendee to the event
|
|
161
236
|
* @param {import('../types.js').Attendee} attendee - Attendee to add
|