Files
2026-05-27 23:29:53 +01:00

2.8 KiB

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

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

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.