Calendar (JSCalendar)

JSCalendar (RFC 8984) primitive. Wraps the framework's existing b.safeIcal.parse (RFC 5545 grammar + bounded parser) with the JSON-native JSCalendar surface JMAP Calendars (RFC 8984 / draft- ietf-jmap-calendars) requires for cross-protocol interop.

Scope: - validate(jsCal) — assert JSCalendar Event / Task / Note / Group shape (RFC 8984 §5 / §6 / §1.4.4). - fromIcal(text, opts?) — VCALENDAR.VEVENT → Event, VTODO → Task, VJOURNAL → Note. Mixed-component VCALENDARs return an array. - toIcal(jsCal, opts?) — Event → VEVENT, Task → VTODO, Note → VJOURNAL. Group emits a single VCALENDAR wrapping every entry's component. - expandRecurrence(event, { from, to, max }) — RRULE expansion for FREQ=DAILY/WEEKLY/MONTHLY/YEARLY with UNTIL/COUNT/INTERVAL and BYDAY/BYMONTH/BYMONTHDAY/BYWEEKNO/BYYEARDAY/BYHOUR/ BYMINUTE/BYSECOND/BYSETPOS. Multiple recurrenceRules expand independently and UNION per RFC 8984 §4.3.2.

Deferred-with-condition (no operator demand yet): - RFC 7529 non-Gregorian calendars. - Floating-timezone resolution against IANA TZDB.

b.calendar.validate(jsCal) #

stable0.11.31

Validate a JSCalendar Event / Task / Note / Group object's required-field shape per RFC 8984 §5 (Event) / §6 (Task) / §7 (Note) / §1.4.4 (Group). Returns the input on success; throws CalendarError on refusal with a .code naming the specific shape rule that failed.

b.calendar.validate({
  "@type":      "Event",
  uid:          "0e612e8b-1c4f-4e30-8e6a-4adc4e8b1c4f",
  updated:      "2026-05-21T10:00:00Z",
  title:        "Sprint planning",
  start:        "2026-05-22T09:00:00",
  duration:     "PT1H",
  timeZone:     "America/Los_Angeles",
});

b.calendar.fromIcal(text, opts?) #

stable0.11.31
{
  safeIcalOpts: object,   // forwarded to b.safeIcal.parse (caps, allowExperimental, etc.)
}

Parse iCalendar text (RFC 5545) via b.safeIcal.parse and map each VEVENT → JSCalendar Event, VTODO → Task, and VJOURNAL → Note (RFC 8984 §5 / §6 / §7). Returns a single object when the VCALENDAR holds exactly one component, or an array across all components when there are several.

b.calendar.fromIcal(
  "BEGIN:VCALENDAR\\r\\nVERSION:2.0\\r\\n" +
  "BEGIN:VEVENT\\r\\nUID:a@b\\r\\nDTSTAMP:20260521T100000Z\\r\\n" +
  "DTSTART:20260522T090000Z\\r\\nDURATION:PT1H\\r\\n" +
  "SUMMARY:Sprint\\r\\nEND:VEVENT\\r\\nEND:VCALENDAR\\r\\n");
// → { "@type":"Event", uid:"a@b", updated:"2026-05-21T10:00:00Z", ... }

b.calendar.toIcal(jsCal, opts?) #

stable0.11.31
{
  prodid: string,   // PRODID value to emit; default "-//blamejs//Calendar//EN"
}

Render a JSCalendar object back to RFC 5545 iCalendar text — Event → VEVENT, Task → VTODO, Note → VJOURNAL, and a Group to a VCALENDAR carrying each member component. Returns a CRLF-terminated string wrapped in a BEGIN:VCALENDAR / VERSION:2.0 / PRODID:-//blamejs//Calendar//EN / … / END:VCALENDAR envelope per RFC 5545 §3.4.

b.calendar.toIcal({
  "@type":  "Event",
  uid:      "a@b",
  updated:  "2026-05-21T10:00:00Z",
  title:    "Sprint",
  start:    "2026-05-22T09:00:00",
  duration: "PT1H",
});

b.calendar.expandRecurrence(event, opts) #

stable0.11.31
{
  from: string,    // ISO 8601 UTC timestamp — lower bound of expansion window
  to:   string,    // ISO 8601 UTC timestamp — upper bound (window <= 10 years)
  max:  number,    // instance-count cap (default 4096; never exceeds MAX_EXPAND_INSTANCES)
}

Expand a JSCalendar Event's recurrenceRules into concrete start timestamps in the operator's [from, to] window. Returns an array of ISO 8601 UTC strings (yyyy-mm-ddTHH:MM:SSZ). Bounded by MAX_EXPAND_INSTANCES (4096) + MAX_EXPAND_SPAN_MS (10 years) to defend against the RRULE recurrence-bomb expansion class.

v1 supports FREQ=DAILY/WEEKLY/MONTHLY/YEARLY with INTERVAL, COUNT, UNTIL. BYDAY / BYMONTH / BYMONTHDAY / BYWEEKNO / BYYEARDAY / BYHOUR / BYMINUTE / BYSECOND refine the base frequency. BYSETPOS picks the Nth candidate from the BY*-filtered set within a FREQ interval (positive = 1-indexed from start, negative = from end); supported for FREQ=MONTHLY / YEARLY / WEEKLY with day-granularity candidates (time-of-day inherited from start). Multiple recurrenceRules are expanded independently and UNIONed; per RFC 8984 §4.3.2 each rule's count cap applies per-rule, not to the combined set. (RFC 7529 non-Gregorian calendars not in scope.)

b.calendar.expandRecurrence(
  { "@type": "Event", uid: "x", updated: "2026-05-21T10:00:00Z",
    start: "2026-05-22T09:00:00",
    recurrenceRules: [{ "@type": "RecurrenceRule", frequency: "daily", count: 5 }] },
  { from: "2026-05-22T00:00:00Z", to: "2026-06-01T00:00:00Z" });
// → ["2026-05-22T09:00:00Z", "2026-05-23T09:00:00Z", ..., "2026-05-26T09:00:00Z"]

Last updated 2026-08-08T16:39:49.652Z by seeder.