# calendar-parser _(no submodules)_ ## Purpose Parses iCalendar (RFC 5545) / ICS payloads embedded in emails — these arrive either as `text/calendar` MIME parts or as attachments with the `application/ics` media type. Only the subset of fields meaningful to email automation is extracted: summary, description, start/end, location, organizer, attendees, UID, and METHOD (REQUEST / REPLY / CANCEL). The parser is deliberately tolerant of folded continuation lines and ICS escape sequences. ## Features - Handles RFC 5545 line folding — both `CRLF + SP` and `CRLF + TAB` continuations. - Unescapes the four standard ICS escapes: `\\n`, `\\,`, `\\;`, `\\\\`. - Extracts `mailto:` addresses from `ORGANIZER` and `ATTENDEE` lines regardless of preceding `CN` parameters. - Captures the calendar-level `METHOD` and surfaces it on every event so consumers can branch on REQUEST vs CANCEL. - Ignores lines outside `BEGIN:VEVENT` / `END:VEVENT` blocks so `VCALENDAR` boilerplate does not leak into event fields. ## Usage: Basic ```ts import { parseICalendar } from "@suaveplan/email-processing/calendar"; const events = parseICalendar(icsAttachment); for (const event of events) { if (event.method === "CANCEL") { await cancelMeeting(event.uid); } } ``` ## Usage: Advanced ```ts import { parseICalendar } from "@suaveplan/email-processing/calendar"; const events = parseICalendar(icsString); for (const event of events) { const attendees = event.attendees.map((mailto) => mailto.toLowerCase()); await db.calendar.upsert({ where: { uid: event.uid ?? hash(event.summary ?? "") }, update: { summary: event.summary ?? null, dtStart: event.dtStart ?? null, dtEnd: event.dtEnd ?? null, location: event.location ?? null, attendees, }, create: { ... }, }); } ``` ## API Reference - `CalendarEvent` — `{ summary?, description?, dtStart?, dtEnd?, location?, organizer?, attendees, uid?, method? }`. - `parseICalendar(icsContent: string): CalendarEvent[]` — returns an empty array when no `VEVENT` blocks are present. Throws `EmailProcessingError(INVALID_INPUT)` for non-string input. ## Implementation Notes DTSTART and DTEND are returned as the raw ICS date-time strings (e.g. `20250101T120000Z`) without conversion. Consumers that need `Date`/`Temporal` instances should call their preferred parser — the package deliberately does not pick one, since `text/calendar` payloads arrive in many timezone shapes (floating, UTC, TZID-suffixed) and correct handling requires knowledge of the surrounding email context. The unfolding regexes operate on `\r\n` first so that `\n`-only ICS payloads still split correctly on the second `\r?\n` split call.