// DC-168: calendar event-cache semantics (shared by widget + tests). // Keyed by uid+start (recurring instances differ per occurrence); a fetched // window is authoritative for entries starting inside it (deletions and // reschedules propagate), entries outside it survive. 'use strict'; (function () { function eventKey(ev) { // Audit 2026-10-27: a null/invalid start threw RangeError out of merge() // and flipped the whole widget offline — degrade to a string key instead. const d = new Date(ev.start); const startIso = isNaN(d.getTime()) ? `invalid:${ev.start == null ? 'null' : String(ev.start)}` : d.toISOString(); return ev.uid ? `${ev.uid}@${startIso}` : `${startIso}|${ev.title || ''}`; } function createCache() { const map = new Map(); return { size: () => map.size, merge(body, range) { if (range) { for (const [k, ev] of map) { const t = new Date(ev.start).getTime(); if (t >= range.start.getTime() && t <= range.end.getTime()) map.delete(k); } } for (const ev of ((body && Array.isArray(body.events)) ? body.events : [])) { map.set(eventKey(ev), ev); } return { events: [...map.values()].sort((a, b) => new Date(a.start) - new Date(b.start)) }; }, }; } const api = { eventKey, createCache }; if (typeof module !== 'undefined' && module.exports) module.exports = api; if (typeof window !== 'undefined') window.CalendarCache = api; })();