@mcp-monorepo/ics 1.1.0 → 1.3.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.
Files changed (71) hide show
  1. package/dist/ics-parser/errors.d.ts +16 -0
  2. package/dist/ics-parser/errors.d.ts.map +1 -0
  3. package/dist/ics-parser/errors.js +31 -0
  4. package/dist/ics-parser/errors.js.map +1 -0
  5. package/dist/ics-parser/event-expander.d.ts +71 -0
  6. package/dist/ics-parser/event-expander.d.ts.map +1 -0
  7. package/dist/ics-parser/event-expander.js +328 -0
  8. package/dist/ics-parser/event-expander.js.map +1 -0
  9. package/dist/ics-parser/index.d.ts +67 -0
  10. package/dist/ics-parser/index.d.ts.map +1 -0
  11. package/dist/ics-parser/index.js +91 -0
  12. package/dist/ics-parser/index.js.map +1 -0
  13. package/dist/ics-parser/parser.d.ts +26 -0
  14. package/dist/ics-parser/parser.d.ts.map +1 -0
  15. package/dist/ics-parser/parser.js +99 -0
  16. package/dist/ics-parser/parser.js.map +1 -0
  17. package/dist/ics-parser/property-parsers.d.ts +66 -0
  18. package/dist/ics-parser/property-parsers.d.ts.map +1 -0
  19. package/dist/ics-parser/property-parsers.js +226 -0
  20. package/dist/ics-parser/property-parsers.js.map +1 -0
  21. package/dist/ics-parser/rrule-expander.d.ts +10 -0
  22. package/dist/ics-parser/rrule-expander.d.ts.map +1 -0
  23. package/dist/ics-parser/rrule-expander.js +305 -0
  24. package/dist/ics-parser/rrule-expander.js.map +1 -0
  25. package/dist/ics-parser/timezone-processor.d.ts +40 -0
  26. package/dist/ics-parser/timezone-processor.d.ts.map +1 -0
  27. package/dist/ics-parser/timezone-processor.js +219 -0
  28. package/dist/ics-parser/timezone-processor.js.map +1 -0
  29. package/dist/ics-parser/types.d.ts +119 -0
  30. package/dist/ics-parser/types.d.ts.map +1 -0
  31. package/dist/ics-parser/types.js +2 -0
  32. package/dist/ics-parser/types.js.map +1 -0
  33. package/dist/ics-parser/utils.d.ts +16 -0
  34. package/dist/ics-parser/utils.d.ts.map +1 -0
  35. package/dist/ics-parser/utils.js +19 -0
  36. package/dist/ics-parser/utils.js.map +1 -0
  37. package/dist/index.js +9 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/lib/config.d.ts +10 -0
  40. package/dist/lib/config.d.ts.map +1 -0
  41. package/dist/lib/config.js +39 -0
  42. package/dist/lib/config.js.map +1 -0
  43. package/dist/lib/event-store-2.d.ts +11 -0
  44. package/dist/lib/event-store-2.d.ts.map +1 -0
  45. package/dist/lib/event-store-2.js +90 -0
  46. package/dist/lib/event-store-2.js.map +1 -0
  47. package/dist/lib/format-date.d.ts +7 -1
  48. package/dist/lib/format-date.d.ts.map +1 -1
  49. package/dist/lib/format-date.js +9 -2
  50. package/dist/lib/format-date.js.map +1 -1
  51. package/dist/lib/types.d.ts +0 -34
  52. package/dist/lib/types.d.ts.map +1 -1
  53. package/dist/tools/fetch-events.d.ts.map +1 -1
  54. package/dist/tools/fetch-events.js +31 -26
  55. package/dist/tools/fetch-events.js.map +1 -1
  56. package/dist/tools/search-events.d.ts.map +1 -1
  57. package/dist/tools/search-events.js +50 -17
  58. package/dist/tools/search-events.js.map +1 -1
  59. package/package.json +10 -6
  60. package/dist/lib/event-store.d.ts +0 -9
  61. package/dist/lib/event-store.d.ts.map +0 -1
  62. package/dist/lib/event-store.js +0 -88
  63. package/dist/lib/event-store.js.map +0 -1
  64. package/dist/lib/filter-events.d.ts +0 -3
  65. package/dist/lib/filter-events.d.ts.map +0 -1
  66. package/dist/lib/filter-events.js +0 -10
  67. package/dist/lib/filter-events.js.map +0 -1
  68. package/dist/lib/recurrrence.d.ts +0 -3
  69. package/dist/lib/recurrrence.d.ts.map +0 -1
  70. package/dist/lib/recurrrence.js +0 -49
  71. package/dist/lib/recurrrence.js.map +0 -1
@@ -0,0 +1,16 @@
1
+ export declare class IcsError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class TimeZoneDefinitionNotFoundError extends IcsError {
5
+ constructor(tzid: string);
6
+ }
7
+ export declare class InvalidObservanceError extends IcsError {
8
+ constructor();
9
+ }
10
+ export declare class CalendarNotFoundError extends IcsError {
11
+ constructor();
12
+ }
13
+ export declare class InvalidRruleError extends IcsError {
14
+ constructor(message: string);
15
+ }
16
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/ics-parser/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,+BAAgC,SAAQ,QAAQ;gBAC/C,IAAI,EAAE,MAAM;CAIzB;AAED,qBAAa,sBAAuB,SAAQ,QAAQ;;CAKnD;AAED,qBAAa,qBAAsB,SAAQ,QAAQ;;CAKlD;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,31 @@
1
+ export class IcsError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'IcsError';
5
+ }
6
+ }
7
+ export class TimeZoneDefinitionNotFoundError extends IcsError {
8
+ constructor(tzid) {
9
+ super(`Timezone definition for TZID="${tzid}" not found.`);
10
+ this.name = 'TimeZoneDefinitionNotFoundError';
11
+ }
12
+ }
13
+ export class InvalidObservanceError extends IcsError {
14
+ constructor() {
15
+ super('Time zone data contains no valid observances.');
16
+ this.name = 'InvalidObservanceError';
17
+ }
18
+ }
19
+ export class CalendarNotFoundError extends IcsError {
20
+ constructor() {
21
+ super('Invalid ICS: No VCALENDAR component found.');
22
+ this.name = 'CalendarNotFoundError';
23
+ }
24
+ }
25
+ export class InvalidRruleError extends IcsError {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = 'InvalidRruleError';
29
+ }
30
+ }
31
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/ics-parser/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,UAAU,CAAA;IACxB,CAAC;CACF;AAED,MAAM,OAAO,+BAAgC,SAAQ,QAAQ;IAC3D,YAAY,IAAY;QACtB,KAAK,CAAC,iCAAiC,IAAI,cAAc,CAAC,CAAA;QAC1D,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAA;IAC/C,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,QAAQ;IAClD;QACE,KAAK,CAAC,+CAA+C,CAAC,CAAA;QACtD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACtC,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,QAAQ;IACjD;QACE,KAAK,CAAC,4CAA4C,CAAC,CAAA;QACnD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;IACrC,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,QAAQ;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;IACjC,CAAC;CACF"}
@@ -0,0 +1,71 @@
1
+ import { DateTime, Duration } from 'luxon';
2
+ import { type ExpandedEvent, type TimeZoneResolver, type UnexpandedEvent, type VComponent } from './types.js';
3
+ /**
4
+ * Gathers all standard and custom properties from a VEVENT component.
5
+ * @param event - The VEVENT component.
6
+ * @returns A partially filled ExpandedEvent object containing all static details.
7
+ */
8
+ export declare function extractEventDetails(event: VComponent, resolver?: TimeZoneResolver): Omit<ExpandedEvent, 'start' | 'end' | 'allDay'>;
9
+ /**
10
+ * Parses a single VEVENT component into a structured but unexpanded event object,
11
+ * including parsed recurrence rules.
12
+ * @param event - The VEVENT component.
13
+ * @param resolver - The timezone resolver function.
14
+ * @returns An UnexpandedEvent object.
15
+ * @throws {IcsError} If the VEVENT component is missing a DTSTART property.
16
+ */
17
+ export declare function parseUnexpandedEvent(event: VComponent, resolver: TimeZoneResolver): UnexpandedEvent;
18
+ /**
19
+ * Determines the duration of an event from its DTEND or DURATION property.
20
+ * @param event - The VEVENT component.
21
+ * @param dtstart - The start time of the event.
22
+ * @param isAllDay - Whether the event is an all-day event.
23
+ * @returns A Luxon Duration object.
24
+ */
25
+ export declare function getEventDuration(event: VComponent, dtstart: DateTime, isAllDay: boolean, resolver?: TimeZoneResolver): Duration;
26
+ /**
27
+ * Gathers all inclusion dates from RRULE and RDATE properties.
28
+ * @returns A Set of timestamps in milliseconds.
29
+ */
30
+ export declare function getInclusionDates(event: VComponent, dtstart: DateTime, rangeStart: DateTime, rangeEnd: DateTime, resolver: TimeZoneResolver): Set<number>;
31
+ /**
32
+ * Gathers all exclusion dates from the EXDATE property.
33
+ * @returns A Set of timestamps in milliseconds.
34
+ */
35
+ export declare function getExclusionDates(event: VComponent, resolver: TimeZoneResolver): Set<number>;
36
+ /**
37
+ * Converts a single local occurrence to a UTC start/end object using a timezone resolver.
38
+ * @returns UTC start and end DateTimes.
39
+ */
40
+ export declare function applyTimeZone(occurrenceStart: DateTime, duration: Duration, tzid: string | undefined, isAllDay: boolean, resolver: TimeZoneResolver): {
41
+ startUtc: DateTime;
42
+ endUtc: DateTime;
43
+ };
44
+ /**
45
+ * Expands a single VEVENT component into a list of concrete occurrences.
46
+ * This is a low-level function that handles recurrence for a single component.
47
+ * Use `expandCalendar` for processing a full calendar with UID-based modifications.
48
+ *
49
+ * @param event - The VEVENT component to expand.
50
+ * @param options - Expansion options including the resolver and time range.
51
+ * @returns An array of ExpandedEvent objects.
52
+ * @throws {IcsError} If the VEVENT component is missing a DTSTART property.
53
+ */
54
+ export declare function expandEvent(event: VComponent, options: {
55
+ resolver: TimeZoneResolver;
56
+ rangeStart: DateTime;
57
+ rangeEnd: DateTime;
58
+ exclusions?: Set<number>;
59
+ }): ExpandedEvent[];
60
+ /**
61
+ * Expands all VEVENT components from a calendar, correctly handling recurring events
62
+ * and their modifications (exceptions) based on UID and RECURRENCE-ID.
63
+ *
64
+ * @param components - All VComponents parsed from an iCalendar file.
65
+ * @param resolver - The timezone resolver function.
66
+ * @param rangeStart - The start of the query range.
67
+ * @param rangeEnd - The end of the query range.
68
+ * @returns A consolidated array of all expanded event occurrences.
69
+ */
70
+ export declare function expandCalendar(components: VComponent[], resolver: TimeZoneResolver, rangeStart: DateTime, rangeEnd: DateTime): ExpandedEvent[];
71
+ //# sourceMappingURL=event-expander.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-expander.d.ts","sourceRoot":"","sources":["../../src/ics-parser/event-expander.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAc1C,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAA;AAenB;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,CA8DjD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,GAAG,eAAe,CAqCnG;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,QAAQ,CAYV;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,QAAQ,EACjB,UAAU,EAAE,QAAQ,EACpB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,gBAAgB,GACzB,GAAG,CAAC,MAAM,CAAC,CAqBb;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAQ5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,eAAe,EAAE,QAAQ,EACzB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,gBAAgB,GACzB;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CA4B1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE;IACP,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,UAAU,EAAE,QAAQ,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAClB,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACzB,GACA,aAAa,EAAE,CAsCjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,UAAU,EAAE,EACxB,QAAQ,EAAE,gBAAgB,EAC1B,UAAU,EAAE,QAAQ,EACpB,QAAQ,EAAE,QAAQ,GACjB,aAAa,EAAE,CA0EjB"}
@@ -0,0 +1,328 @@
1
+ import { DateTime, Duration } from 'luxon';
2
+ import { IcsError } from './errors.js';
3
+ import { parseCalAddress, parseCategories, parseDateTimeList, parseGeo, parseIcsDateTime, parseInteger, parseRruleString, parseString, } from './property-parsers.js';
4
+ import { expandRrule } from './rrule-expander.js';
5
+ import { findProperty, findProperties } from './utils.js';
6
+ /**
7
+ * Parses a date-time property into an ISO 8601 string.
8
+ * @param prop - The ICS property.
9
+ * @returns An ISO 8601 string or undefined.
10
+ */
11
+ function parseDateTimeValue(prop, resolver) {
12
+ if (!prop)
13
+ return undefined;
14
+ // Note: This does not resolve timezones, it just formats the parsed time to UTC.
15
+ // This is suitable for properties like CREATED and LAST-MODIFIED which are often in UTC.
16
+ return parseIcsDateTime(prop, resolver).toUTC().toISO() ?? undefined;
17
+ }
18
+ /**
19
+ * Gathers all standard and custom properties from a VEVENT component.
20
+ * @param event - The VEVENT component.
21
+ * @returns A partially filled ExpandedEvent object containing all static details.
22
+ */
23
+ export function extractEventDetails(event, resolver) {
24
+ const customProperties = {};
25
+ const knownKeys = new Set([
26
+ 'UID',
27
+ 'SUMMARY',
28
+ 'DTSTART',
29
+ 'DTEND',
30
+ 'DURATION',
31
+ 'RRULE',
32
+ 'RDATE',
33
+ 'EXDATE',
34
+ 'DESCRIPTION',
35
+ 'LOCATION',
36
+ 'STATUS',
37
+ 'CLASS',
38
+ 'CREATED',
39
+ 'LAST-MODIFIED',
40
+ 'SEQUENCE',
41
+ 'ORGANIZER',
42
+ 'ATTENDEE',
43
+ 'CATEGORIES',
44
+ 'PRIORITY',
45
+ 'TRANSP',
46
+ 'URL',
47
+ 'GEO',
48
+ 'RECURRENCE-ID',
49
+ ]);
50
+ for (const prop of event.properties) {
51
+ if (prop.key.startsWith('X-') || !knownKeys.has(prop.key)) {
52
+ // Per RFC 5545, non-standard and IANA properties can be included.
53
+ // We store them in a dedicated nested object.
54
+ customProperties[prop.key] = prop;
55
+ }
56
+ }
57
+ const attendees = findProperties(event, 'ATTENDEE')
58
+ .map(parseCalAddress)
59
+ .filter((a) => !!a);
60
+ const categories = parseCategories(findProperties(event, 'CATEGORIES')).filter((a) => !!a);
61
+ return {
62
+ uid: findProperty(event, 'UID')?.value ?? `generated-${Math.random()}`,
63
+ summary: parseString(findProperty(event, 'SUMMARY')) ?? '(No Summary)',
64
+ description: parseString(findProperty(event, 'DESCRIPTION')),
65
+ location: parseString(findProperty(event, 'LOCATION')),
66
+ status: parseString(findProperty(event, 'STATUS')),
67
+ class: parseString(findProperty(event, 'CLASS')),
68
+ created: parseDateTimeValue(findProperty(event, 'CREATED'), resolver),
69
+ lastModified: parseDateTimeValue(findProperty(event, 'LAST-MODIFIED'), resolver),
70
+ sequence: parseInteger(findProperty(event, 'SEQUENCE')),
71
+ organizer: parseCalAddress(findProperty(event, 'ORGANIZER')),
72
+ attendees: attendees.length > 0 ? attendees : undefined,
73
+ categories: categories.length > 0 ? categories : undefined,
74
+ priority: parseInteger(findProperty(event, 'PRIORITY')),
75
+ transp: parseString(findProperty(event, 'TRANSP')),
76
+ url: parseString(findProperty(event, 'URL')),
77
+ geo: parseGeo(findProperty(event, 'GEO')),
78
+ recurrenceId: parseDateTimeValue(findProperty(event, 'RECURRENCE-ID'), resolver),
79
+ customProperties: Object.keys(customProperties).length > 0 ? customProperties : undefined,
80
+ };
81
+ }
82
+ /**
83
+ * Parses a single VEVENT component into a structured but unexpanded event object,
84
+ * including parsed recurrence rules.
85
+ * @param event - The VEVENT component.
86
+ * @param resolver - The timezone resolver function.
87
+ * @returns An UnexpandedEvent object.
88
+ * @throws {IcsError} If the VEVENT component is missing a DTSTART property.
89
+ */
90
+ export function parseUnexpandedEvent(event, resolver) {
91
+ const eventDetails = extractEventDetails(event, resolver);
92
+ const dtstartProp = findProperty(event, 'DTSTART');
93
+ if (!dtstartProp) {
94
+ throw new IcsError(`Event with UID "${eventDetails.uid}" is missing a DTSTART property.`);
95
+ }
96
+ const tzid = dtstartProp.params['TZID'];
97
+ const isAllDay = dtstartProp.params['VALUE'] === 'DATE';
98
+ const dtstart = parseIcsDateTime(dtstartProp, resolver);
99
+ const duration = getEventDuration(event, dtstart, isAllDay, resolver);
100
+ const { startUtc, endUtc } = applyTimeZone(dtstart, duration, tzid, isAllDay, resolver);
101
+ const rruleProp = findProperty(event, 'RRULE');
102
+ const rrule = rruleProp ? parseRruleString(rruleProp.value) : undefined;
103
+ const rdates = findProperties(event, 'RDATE')
104
+ .flatMap((prop) => parseDateTimeList(prop, resolver))
105
+ .map((dt) => dt.toISO())
106
+ .filter((d) => !!d);
107
+ const exdates = findProperties(event, 'EXDATE')
108
+ .flatMap((prop) => parseDateTimeList(prop, resolver))
109
+ .map((dt) => dt.toISO())
110
+ .filter((d) => !!d);
111
+ return {
112
+ ...eventDetails,
113
+ start: startUtc.toISO(),
114
+ end: endUtc.toISO() ?? undefined,
115
+ allDay: isAllDay,
116
+ rrule: rrule,
117
+ rdates: rdates.length > 0 ? rdates : undefined,
118
+ exdates: exdates.length > 0 ? exdates : undefined,
119
+ };
120
+ }
121
+ // --- Core Expansion Logic ---
122
+ /**
123
+ * Determines the duration of an event from its DTEND or DURATION property.
124
+ * @param event - The VEVENT component.
125
+ * @param dtstart - The start time of the event.
126
+ * @param isAllDay - Whether the event is an all-day event.
127
+ * @returns A Luxon Duration object.
128
+ */
129
+ export function getEventDuration(event, dtstart, isAllDay, resolver) {
130
+ const dtendProp = findProperty(event, 'DTEND');
131
+ if (dtendProp) {
132
+ return parseIcsDateTime(dtendProp, resolver).diff(dtstart);
133
+ }
134
+ const durationProp = findProperty(event, 'DURATION');
135
+ if (durationProp) {
136
+ return Duration.fromISO(durationProp.value);
137
+ }
138
+ // For all-day events without DTEND/DURATION, the duration is one day.
139
+ // For timed events, it's zero length (ends at the same time it starts).
140
+ return isAllDay ? Duration.fromObject({ days: 1 }) : Duration.fromMillis(0);
141
+ }
142
+ /**
143
+ * Gathers all inclusion dates from RRULE and RDATE properties.
144
+ * @returns A Set of timestamps in milliseconds.
145
+ */
146
+ export function getInclusionDates(event, dtstart, rangeStart, rangeEnd, resolver) {
147
+ const dates = new Set();
148
+ dates.add(dtstart.toMillis());
149
+ const rruleProp = findProperty(event, 'RRULE');
150
+ if (rruleProp) {
151
+ const rule = parseRruleString(rruleProp.value);
152
+ const instances = expandRrule({ dtstart, rule, rangeStart, rangeEnd });
153
+ if (instances) {
154
+ for (const instance of instances) {
155
+ dates.add(instance.toMillis());
156
+ }
157
+ }
158
+ }
159
+ findProperties(event, 'RDATE').forEach((prop) => {
160
+ for (const date of parseDateTimeList(prop, resolver)) {
161
+ dates.add(date.toMillis());
162
+ }
163
+ });
164
+ return dates;
165
+ }
166
+ /**
167
+ * Gathers all exclusion dates from the EXDATE property.
168
+ * @returns A Set of timestamps in milliseconds.
169
+ */
170
+ export function getExclusionDates(event, resolver) {
171
+ const dates = new Set();
172
+ findProperties(event, 'EXDATE').forEach((prop) => {
173
+ for (const date of parseDateTimeList(prop, resolver)) {
174
+ dates.add(date.toMillis());
175
+ }
176
+ });
177
+ return dates;
178
+ }
179
+ /**
180
+ * Converts a single local occurrence to a UTC start/end object using a timezone resolver.
181
+ * @returns UTC start and end DateTimes.
182
+ */
183
+ export function applyTimeZone(occurrenceStart, duration, tzid, isAllDay, resolver) {
184
+ // All-day events don't have a time-of-day, so we treat them as starting at midnight
185
+ // in the specified timezone, or in UTC if no timezone is given.
186
+ if (isAllDay) {
187
+ const startOfDay = occurrenceStart.startOf('day');
188
+ const startUtc = tzid
189
+ ? startOfDay.setZone('utc', { keepLocalTime: true }).minus({ minutes: resolver(startOfDay, tzid) })
190
+ : startOfDay.toUTC();
191
+ const endUtc = startUtc.plus(duration);
192
+ return { startUtc, endUtc };
193
+ }
194
+ // Floating time (no TZID) is interpreted as UTC per our convention.
195
+ if (!tzid) {
196
+ const startUtc = occurrenceStart.setZone('utc', { keepLocalTime: true });
197
+ return { startUtc, endUtc: startUtc.plus(duration) };
198
+ }
199
+ // For timed events with a TZID, we convert the local time to UTC by subtracting the offset.
200
+ const startUtc = occurrenceStart
201
+ .setZone('utc', { keepLocalTime: true })
202
+ .minus({ minutes: resolver(occurrenceStart, tzid) });
203
+ // The end time offset might be different if the event crosses a DST boundary.
204
+ const occurrenceEnd = occurrenceStart.plus(duration);
205
+ const endUtc = occurrenceEnd.setZone('utc', { keepLocalTime: true }).minus({ minutes: resolver(occurrenceEnd, tzid) });
206
+ return { startUtc, endUtc };
207
+ }
208
+ /**
209
+ * Expands a single VEVENT component into a list of concrete occurrences.
210
+ * This is a low-level function that handles recurrence for a single component.
211
+ * Use `expandCalendar` for processing a full calendar with UID-based modifications.
212
+ *
213
+ * @param event - The VEVENT component to expand.
214
+ * @param options - Expansion options including the resolver and time range.
215
+ * @returns An array of ExpandedEvent objects.
216
+ * @throws {IcsError} If the VEVENT component is missing a DTSTART property.
217
+ */
218
+ export function expandEvent(event, options) {
219
+ const { resolver, rangeStart, rangeEnd, exclusions } = options;
220
+ const dtstartProp = findProperty(event, 'DTSTART');
221
+ const eventDetails = extractEventDetails(event, resolver);
222
+ if (!dtstartProp) {
223
+ // According to RFC 5545, DTSTART is required for VEVENTs in most cases.
224
+ throw new IcsError(`Event with UID "${eventDetails.uid}" is missing a DTSTART property.`);
225
+ }
226
+ const tzid = dtstartProp.params['TZID'] === 'Romance Standard Time' ? 'Europe/Paris' : dtstartProp.params['TZID'];
227
+ // The 'VALUE=DATE' parameter indicates an all-day event.
228
+ const isAllDay = dtstartProp.params['VALUE'] === 'DATE';
229
+ const dtstart = parseIcsDateTime(dtstartProp, resolver);
230
+ const duration = getEventDuration(event, dtstart, isAllDay, resolver);
231
+ const inclusionDates = getInclusionDates(event, dtstart, rangeStart, rangeEnd, resolver);
232
+ const exclusionDates = exclusions ?? getExclusionDates(event, resolver);
233
+ const finalEvents = [];
234
+ for (const millis of inclusionDates) {
235
+ if (exclusionDates.has(millis))
236
+ continue;
237
+ const occurrenceStart = DateTime.fromMillis(millis, { zone: dtstart.zone });
238
+ const { startUtc, endUtc } = applyTimeZone(occurrenceStart, duration, tzid, isAllDay, resolver);
239
+ // Filter out occurrences that do not overlap with the query range.
240
+ if (startUtc < rangeEnd && endUtc > rangeStart) {
241
+ finalEvents.push({
242
+ ...eventDetails,
243
+ start: startUtc.toISO(),
244
+ end: endUtc.toISO() ?? undefined,
245
+ allDay: isAllDay,
246
+ });
247
+ }
248
+ }
249
+ return finalEvents;
250
+ }
251
+ /**
252
+ * Expands all VEVENT components from a calendar, correctly handling recurring events
253
+ * and their modifications (exceptions) based on UID and RECURRENCE-ID.
254
+ *
255
+ * @param components - All VComponents parsed from an iCalendar file.
256
+ * @param resolver - The timezone resolver function.
257
+ * @param rangeStart - The start of the query range.
258
+ * @param rangeEnd - The end of the query range.
259
+ * @returns A consolidated array of all expanded event occurrences.
260
+ */
261
+ export function expandCalendar(components, resolver, rangeStart, rangeEnd) {
262
+ const events = components.filter((c) => c.type === 'VEVENT');
263
+ const eventsByUid = new Map();
264
+ const eventsWithoutUid = [];
265
+ // 1. Group all VEVENTs by their UID.
266
+ for (const event of events) {
267
+ const uid = findProperty(event, 'UID')?.value;
268
+ if (uid) {
269
+ if (!eventsByUid.has(uid)) {
270
+ eventsByUid.set(uid, []);
271
+ }
272
+ eventsByUid.get(uid)?.push(event);
273
+ }
274
+ else {
275
+ // Handle events without a UID as standalone. They cannot be part of a
276
+ // recurrence set with overrides.
277
+ eventsWithoutUid.push(event);
278
+ }
279
+ }
280
+ const allExpandedEvents = [];
281
+ // 2. Process each UID group.
282
+ for (const group of eventsByUid.values()) {
283
+ const masterEvent = group.find((e) => findProperty(e, 'RRULE') && !findProperty(e, 'RECURRENCE-ID'));
284
+ const exceptionEvents = group.filter((e) => findProperty(e, 'RECURRENCE-ID'));
285
+ const singleEvents = group.filter((e) => !findProperty(e, 'RRULE') && !findProperty(e, 'RECURRENCE-ID'));
286
+ // 3. Gather all dates to be excluded from the master recurring event.
287
+ // This includes both EXDATEs from the master and RECURRENCE-IDs from exceptions.
288
+ const combinedExclusions = new Set();
289
+ if (masterEvent) {
290
+ getExclusionDates(masterEvent, resolver).forEach((d) => combinedExclusions.add(d));
291
+ }
292
+ for (const ex of exceptionEvents) {
293
+ const recurrenceIdProp = findProperty(ex, 'RECURRENCE-ID');
294
+ if (recurrenceIdProp) {
295
+ // Parse RECURRENCE-ID to get a comparable millisecond timestamp.
296
+ const recurrenceDate = parseIcsDateTime(recurrenceIdProp, resolver);
297
+ combinedExclusions.add(recurrenceDate.toMillis());
298
+ }
299
+ }
300
+ // 4. Expand the master event, applying the combined exclusions.
301
+ if (masterEvent) {
302
+ const masterOccurrences = expandEvent(masterEvent, {
303
+ resolver,
304
+ rangeStart,
305
+ rangeEnd,
306
+ exclusions: combinedExclusions,
307
+ });
308
+ allExpandedEvents.push(...masterOccurrences);
309
+ }
310
+ // 5. Expand all exception events. These are treated as individual appointments.
311
+ for (const ex of exceptionEvents) {
312
+ const exceptionOccurrence = expandEvent(ex, { resolver, rangeStart, rangeEnd });
313
+ allExpandedEvents.push(...exceptionOccurrence);
314
+ }
315
+ // 6. Expand any standalone (non-recurring) events in the group.
316
+ for (const single of singleEvents) {
317
+ const singleOccurrence = expandEvent(single, { resolver, rangeStart, rangeEnd });
318
+ allExpandedEvents.push(...singleOccurrence);
319
+ }
320
+ }
321
+ // 7. Expand all events that didn't have a UID.
322
+ for (const single of eventsWithoutUid) {
323
+ const singleOccurrence = expandEvent(single, { resolver, rangeStart, rangeEnd });
324
+ allExpandedEvents.push(...singleOccurrence);
325
+ }
326
+ return allExpandedEvents;
327
+ }
328
+ //# sourceMappingURL=event-expander.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-expander.js","sourceRoot":"","sources":["../../src/ics-parser/event-expander.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { type ExpandedEvent, type TimeZoneData, type UnexpandedEvent, type VComponent } from './types.js';
2
+ export { ExpandedEvent, UnexpandedEvent } from './types.js';
3
+ /**
4
+ * Represents the pre-processed, structured data from an ICS file.
5
+ * This object contains the VEVENT components and a `Map` of processed VTIMEZONE
6
+ * data. While the `Map` itself isn't directly serializable, the `serialize`
7
+ * and `deserialize` functions handle its conversion to/from a JSON-compatible format.
8
+ */
9
+ export interface PreparedIcs {
10
+ /**
11
+ * An array of all VEVENT components found in the ICS file.
12
+ */
13
+ events: VComponent[];
14
+ /**
15
+ * A map from a Time Zone ID (TZID) to its structured `TimeZoneData`.
16
+ */
17
+ tzData: Map<string, TimeZoneData>;
18
+ }
19
+ /**
20
+ * Parses a raw ICS string and prepares it for event expansion.
21
+ * This function performs the initial parsing of the ICS data into a structured
22
+ * format containing event components and processed timezone information.
23
+ * The result can be serialized for caching.
24
+ *
25
+ * @param icsString - The full content of the .ics file.
26
+ * @returns A `PreparedIcs` object containing parsed events and a `Map` of timezone data.
27
+ */
28
+ export declare function prepare(icsString: string): PreparedIcs;
29
+ /**
30
+ * Serializes the prepared ICS data into a JSON string for caching.
31
+ * It converts the `tzData` Map into an array of `[key, value]` pairs under
32
+ * a `timezones` key to ensure it is JSON-serializable.
33
+ *
34
+ * @param preparedIcs - The `PreparedIcs` object to serialize.
35
+ * @returns A JSON string representation of the prepared data.
36
+ */
37
+ export declare function serialize({ events, tzData }: PreparedIcs): string;
38
+ /**
39
+ * Deserializes a JSON string back into a `PreparedIcs` object.
40
+ * It expects the JSON to contain an `events` array and a `timezones` array,
41
+ * which it uses to reconstruct the `tzData` Map.
42
+ *
43
+ * @param jsonString - The JSON string to deserialize from a cache.
44
+ * @returns The deserialized `PreparedIcs` object with a reconstructed `tzData` Map.
45
+ */
46
+ export declare function deserialize(jsonString: string): PreparedIcs;
47
+ /**
48
+ * Expands all recurring events from the prepared data within a given time range.
49
+ * This is the second step after `prepare`, taking the structured data and a date range
50
+ * to generate the final list of event occurrences.
51
+ *
52
+ * @param preparedIcs - The `PreparedIcs` object from `prepare` or `deserialize`.
53
+ * @param rangeStartIso - The start of the desired time frame (UTC ISO string).
54
+ * @param rangeEndIso - The end of the desired time frame (UTC ISO string).
55
+ * @returns An array of all event occurrences within the range, sorted by start time.
56
+ */
57
+ export declare function getEventsBetween({ events, tzData }: PreparedIcs, rangeStartIso: string, rangeEndIso: string): ExpandedEvent[];
58
+ /**
59
+ * Parses all VEVENT components into a structured, but not expanded, format.
60
+ * This is ideal for searching, as it provides all event details without generating
61
+ * every single recurrence instance.
62
+ *
63
+ * @param preparedIcs - The `PreparedIcs` object from `prepare` or `deserialize`.
64
+ * @returns An array of `UnexpandedEvent` objects.
65
+ */
66
+ export declare function getUnexpandedEvents({ events, tzData }: PreparedIcs): UnexpandedEvent[];
67
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ics-parser/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACzG,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB;;OAEG;IACH,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAQtD;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,MAAM,CAKjE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAS3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAC/B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,aAAa,EAAE,CAUjB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,eAAe,EAAE,CActF"}
@@ -0,0 +1,91 @@
1
+ import { DateTime } from 'luxon';
2
+ import { expandCalendar, parseUnexpandedEvent } from './event-expander.js';
3
+ import { parseIcs } from './parser.js';
4
+ import { buildTimeZoneData, createTimeZoneResolver } from './timezone-processor.js';
5
+ /**
6
+ * Parses a raw ICS string and prepares it for event expansion.
7
+ * This function performs the initial parsing of the ICS data into a structured
8
+ * format containing event components and processed timezone information.
9
+ * The result can be serialized for caching.
10
+ *
11
+ * @param icsString - The full content of the .ics file.
12
+ * @returns A `PreparedIcs` object containing parsed events and a `Map` of timezone data.
13
+ */
14
+ export function prepare(icsString) {
15
+ const { timezones, events } = parseIcs(icsString);
16
+ const tzData = buildTimeZoneData(timezones);
17
+ return {
18
+ events,
19
+ tzData,
20
+ };
21
+ }
22
+ /**
23
+ * Serializes the prepared ICS data into a JSON string for caching.
24
+ * It converts the `tzData` Map into an array of `[key, value]` pairs under
25
+ * a `timezones` key to ensure it is JSON-serializable.
26
+ *
27
+ * @param preparedIcs - The `PreparedIcs` object to serialize.
28
+ * @returns A JSON string representation of the prepared data.
29
+ */
30
+ export function serialize({ events, tzData }) {
31
+ return JSON.stringify({
32
+ events: events,
33
+ timezones: Array.from(tzData.entries()),
34
+ });
35
+ }
36
+ /**
37
+ * Deserializes a JSON string back into a `PreparedIcs` object.
38
+ * It expects the JSON to contain an `events` array and a `timezones` array,
39
+ * which it uses to reconstruct the `tzData` Map.
40
+ *
41
+ * @param jsonString - The JSON string to deserialize from a cache.
42
+ * @returns The deserialized `PreparedIcs` object with a reconstructed `tzData` Map.
43
+ */
44
+ export function deserialize(jsonString) {
45
+ const { events, timezones } = JSON.parse(jsonString);
46
+ return {
47
+ events,
48
+ tzData: new Map(timezones),
49
+ };
50
+ }
51
+ /**
52
+ * Expands all recurring events from the prepared data within a given time range.
53
+ * This is the second step after `prepare`, taking the structured data and a date range
54
+ * to generate the final list of event occurrences.
55
+ *
56
+ * @param preparedIcs - The `PreparedIcs` object from `prepare` or `deserialize`.
57
+ * @param rangeStartIso - The start of the desired time frame (UTC ISO string).
58
+ * @param rangeEndIso - The end of the desired time frame (UTC ISO string).
59
+ * @returns An array of all event occurrences within the range, sorted by start time.
60
+ */
61
+ export function getEventsBetween({ events, tzData }, rangeStartIso, rangeEndIso) {
62
+ const timeZoneResolver = createTimeZoneResolver(tzData);
63
+ const rangeStart = DateTime.fromISO(rangeStartIso, { zone: 'utc' });
64
+ const rangeEnd = DateTime.fromISO(rangeEndIso, { zone: 'utc' });
65
+ const finalEvents = expandCalendar(events, timeZoneResolver, rangeStart, rangeEnd);
66
+ finalEvents.sort((a, b) => a.start.localeCompare(b.start));
67
+ return finalEvents;
68
+ }
69
+ /**
70
+ * Parses all VEVENT components into a structured, but not expanded, format.
71
+ * This is ideal for searching, as it provides all event details without generating
72
+ * every single recurrence instance.
73
+ *
74
+ * @param preparedIcs - The `PreparedIcs` object from `prepare` or `deserialize`.
75
+ * @returns An array of `UnexpandedEvent` objects.
76
+ */
77
+ export function getUnexpandedEvents({ events, tzData }) {
78
+ const timeZoneResolver = createTimeZoneResolver(tzData);
79
+ const unexpandedEvents = [];
80
+ for (const event of events) {
81
+ try {
82
+ unexpandedEvents.push(parseUnexpandedEvent(event, timeZoneResolver));
83
+ }
84
+ catch (e) {
85
+ // Silently ignore events that fail to parse (e.g., missing DTSTART).
86
+ // This is acceptable for search, where we just skip unsearchable items.
87
+ }
88
+ }
89
+ return unexpandedEvents;
90
+ }
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ics-parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAqBnF;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,SAAiB;IACvC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;IACjD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;IAE3C,OAAO;QACL,MAAM;QACN,MAAM;KACP,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAe;IACvD,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;KACxC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAGlD,CAAA;IACD,OAAO;QACL,MAAM;QACN,MAAM,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC;KAC3B,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,EAAE,MAAM,EAAE,MAAM,EAAe,EAC/B,aAAqB,EACrB,WAAmB;IAEnB,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;IAEvD,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAE/D,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IAClF,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IAE1D,OAAO,WAAW,CAAA;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAe;IACjE,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,gBAAgB,GAAsB,EAAE,CAAA;IAE9C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,gBAAgB,CAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAA;QACtE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,qEAAqE;YACrE,wEAAwE;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,gBAAgB,CAAA;AACzB,CAAC"}