← All @molecule/* packages · App templates

@molecule/app-device-calendar

Native · native · App (browser) · v1.0.1 · Apache-2.0

Device/OS calendar access — read & write the user's real iOS/Android calendars (not the in-app calendar UI; that is @molecule/app-calendar)

npm install @molecule/app-device-calendar

npm · Source on GitHub

How it works

@molecule/app-device-calendar bridges the native core to the native platform layer of the app.

import {
  createEvent,
  getCalendars,
  getEvents,
  getPermissionStatus,
  hasProvider,
  requestPermission,
} from '@molecule/app-device-calendar'

async function addReminderToDeviceCalendar(): Promise<void> {
  if (!hasProvider()) return // no provider wired — feature-gate the UI
  if ((await getPermissionStatus()) !== 'granted') {
    if ((await requestPermission()) !== 'granted') return
  }
  const writable = (await getCalendars()).find((c) => !c.readOnly)
  if (!writable) return
  await createEvent({
    calendarId: writable.id,
    title: 'Dentist',
    startDate: '2026-08-01T09:00:00.000Z',
    endDate: '2026-08-01T09:30:00.000Z',
    allDay: false,
  })
}

Works with: @molecule/app-bond, @molecule/app-i18n

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Device calendar access interface for molecule.dev.

Framework-agnostic core for reading and writing events in the DEVICE's calendar store (the user's actual iOS/Android calendars) through a swappable CalendarProvider: list calendars, query/create/update/delete events, open the native calendar app, and manage permissions. Also ships pure helpers (eventsOverlap, getEventDuration, formatEventTimeRange, parseQuickEvent).

NOT the same package as @molecule/app-calendar — that is the calendar UI core (month/week/day views rendered inside your app). This package talks to the operating system's calendar database. Pick by need: rendering a calendar screen → app-calendar; syncing with the user's real calendars → app-device-calendar.

Quick Start

import {
  createEvent,
  getCalendars,
  getEvents,
  getPermissionStatus,
  hasProvider,
  requestPermission,
} from '@molecule/app-device-calendar'

async function addReminderToDeviceCalendar(): Promise<void> {
  if (!hasProvider()) return // no provider wired — feature-gate the UI
  if ((await getPermissionStatus()) !== 'granted') {
    if ((await requestPermission()) !== 'granted') return
  }
  const writable = (await getCalendars()).find((c) => !c.readOnly)
  if (!writable) return
  await createEvent({
    calendarId: writable.id,
    title: 'Dentist',
    startDate: '2026-08-01T09:00:00.000Z',
    endDate: '2026-08-01T09:30:00.000Z',
    allDay: false,
  })
}

Type

native

Installation

npm install @molecule/app-device-calendar @molecule/app-bond @molecule/app-i18n

API

Interfaces

Calendar

Device calendar metadata (ID, title, color, read-only flag, account, visibility).

interface Calendar {
  /** Calendar ID */
  id: string
  /** Calendar title/name */
  title: string
  /** Calendar color */
  color?: string
  /** Whether calendar is read-only */
  readOnly: boolean
  /** Calendar type */
  type: 'local' | 'subscription' | 'birthday' | 'exchange' | 'google' | 'other'
  /** Account name */
  accountName?: string
  /** Whether calendar is visible */
  visible: boolean
}

CalendarCapabilities

Calendar capabilities

interface CalendarCapabilities {
  /** Whether calendar access is supported */
  supported: boolean
  /** Whether reading is supported */
  canRead: boolean
  /** Whether writing is supported */
  canWrite: boolean
  /** Whether reminders are supported */
  supportsReminders: boolean
  /** Whether recurrence is supported */
  supportsRecurrence: boolean
  /** Whether attendees are supported */
  supportsAttendees: boolean
}

CalendarEvent

Full calendar event with title, dates, location, attendees, reminders, and recurrence rules.

interface CalendarEvent {
  /** Event ID */
  id: string
  /** Calendar ID */
  calendarId: string
  /** Event title */
  title: string
  /** Event description */
  description?: string
  /** Event location */
  location?: string
  /** Start date/time (ISO string) */
  startDate: string
  /** End date/time (ISO string) */
  endDate: string
  /** Whether event is all-day */
  allDay: boolean
  /** Event timezone */
  timezone?: string
  /** Event URL */
  url?: string
  /** Event notes */
  notes?: string
  /** Attendees */
  attendees?: EventAttendee[]
  /** Reminders */
  reminders?: EventReminder[]
  /** Recurrence rule */
  recurrence?: RecurrenceRule
  /** Event status */
  status?: 'confirmed' | 'tentative' | 'cancelled'
  /** Event availability */
  availability?: 'busy' | 'free' | 'tentative'
  /** Organizer info */
  organizer?: EventAttendee
}

CalendarProvider

Calendar provider interface

interface CalendarProvider {
  /**
   * Get all calendars available on the device.
   * @returns An array of Calendar objects with their metadata.
   */
  getCalendars(): Promise<Calendar[]>

  /**
   * Query calendar events matching the given options.
   * @param options - Query filters (calendar IDs, date range, search text, limit).
   * @returns An array of matching CalendarEvent objects.
   */
  getEvents(options?: EventQueryOptions): Promise<CalendarEvent[]>

  /**
   * Get a single event by ID
   * @param eventId - Event ID
   * @param calendarId - Calendar ID
   */
  getEventById(eventId: string, calendarId: string): Promise<CalendarEvent | null>

  /**
   * Create a new event
   * @param event - Event data
   */
  createEvent(event: EventInput): Promise<CalendarEvent>

  /**
   * Update an existing event
   * @param eventId - Event ID
   * @param event - Updated event data
   */
  updateEvent(eventId: string, event: Partial<EventInput>): Promise<CalendarEvent>

  /**
   * Delete an event
   * @param eventId - Event ID
   * @param calendarId - Calendar ID
   */
  deleteEvent(eventId: string, calendarId: string): Promise<void>

  /**
   * Open native calendar for a date
   * @param date - Date to show
   */
  openCalendar(date?: Date): Promise<void>

  /**
   * Open event in native calendar
   * @param eventId - Event ID
   */
  openEvent(eventId: string): Promise<void>

  /**
   * Get permission status
   */
  getPermissionStatus(): Promise<CalendarPermissionStatus>

  /**
   * Request permission
   */
  requestPermission(): Promise<CalendarPermissionStatus>

  /**
   * Open system settings for calendar permission
   */
  openSettings(): Promise<void>

  /**
   * Get the platform's calendar capabilities.
   * @returns The capabilities indicating which calendar features are supported.
   */
  getCapabilities(): Promise<CalendarCapabilities>
}

EventAttendee

A calendar event attendee with email, name, RSVP status, and organizer/optional flags.

interface EventAttendee {
  /** Attendee email */
  email: string
  /** Attendee name */
  name?: string
  /** Attendance status */
  status?: 'pending' | 'accepted' | 'declined' | 'tentative'
  /** Whether attendee is organizer */
  isOrganizer?: boolean
  /** Whether attendee is optional */
  isOptional?: boolean
}

EventQueryOptions

Event query options

interface EventQueryOptions {
  /** Calendar IDs to query (all if not specified) */
  calendarIds?: string[]
  /** Start date range (ISO string) */
  startDate?: string
  /** End date range (ISO string) */
  endDate?: string
  /** Search query */
  query?: string
  /** Maximum results */
  limit?: number
}

EventReminder

Event reminder/alarm

interface EventReminder {
  /** Minutes before event to trigger reminder */
  minutes: number
  /** Reminder method */
  method?: 'alert' | 'email' | 'sms'
}

RecurrenceRule

Recurrence rule

interface RecurrenceRule {
  /** Recurrence frequency */
  frequency: 'daily' | 'weekly' | 'monthly' | 'yearly'
  /** Interval between occurrences */
  interval?: number
  /** End date (ISO string) */
  endDate?: string
  /** Number of occurrences */
  count?: number
  /** Days of week (0-6, Sunday-Saturday) */
  daysOfWeek?: number[]
  /** Days of month (1-31) */
  daysOfMonth?: number[]
  /** Months (1-12) */
  months?: number[]
}

Types

CalendarPermissionStatus

Permission status

type CalendarPermissionStatus = 'granted' | 'denied' | 'limited' | 'prompt' | 'unsupported'

EventInput

Event input for creation/update

type EventInput = Omit<CalendarEvent, 'id'> & { id?: string }

Functions

createEvent(event)

Create a new calendar event.

function createEvent(event: EventInput): Promise<CalendarEvent>
  • event — The event data to create (title, dates, attendees, etc.).

Returns: The created CalendarEvent with its assigned ID.

deleteEvent(eventId, calendarId)

Delete a calendar event.

function deleteEvent(eventId: string, calendarId: string): Promise<void>
  • eventId — The ID of the event to delete.
  • calendarId — The calendar ID containing the event.

Returns: A promise that resolves when the event is deleted.

eventsOverlap(event1, event2)

Check if two calendar events overlap in time.

function eventsOverlap(event1: CalendarEvent, event2: CalendarEvent): boolean
  • event1 — The first event to compare.
  • event2 — The second event to compare.

Returns: Whether the two events have overlapping time ranges.

formatEventTimeRange(event, locale)

Format an event's time range as a human-readable string. Handles all-day events, same-day events, and multi-day events differently.

function formatEventTimeRange(event: CalendarEvent, locale?: string): string
  • event — The calendar event to format.
  • locale — The locale code for date/time formatting (default: 'en-US').

Returns: A formatted time range string (e.g., "9:00 AM - 10:00 AM" or "Mon, Jan 5").

getCalendars()

Get all calendars available on the device.

function getCalendars(): Promise<Calendar[]>

Returns: An array of Calendar objects with their metadata.

getCapabilities()

Get the platform's calendar capabilities.

function getCapabilities(): Promise<CalendarCapabilities>

Returns: The capabilities indicating which calendar features are supported.

getEventById(eventId, calendarId)

Get a single event by its ID.

function getEventById(eventId: string, calendarId: string): Promise<CalendarEvent | null>
  • eventId — The event ID to look up.
  • calendarId — The calendar ID containing the event.

Returns: The matching CalendarEvent, or null if not found.

getEventDuration(event)

Get the duration of a calendar event in minutes.

function getEventDuration(event: CalendarEvent): number
  • event — The calendar event to measure.

Returns: The event duration in minutes, rounded to the nearest integer.

getEvents(options)

Query calendar events matching the given options.

function getEvents(options?: EventQueryOptions): Promise<CalendarEvent[]>
  • options — Query filters (calendar IDs, date range, search text, limit).

Returns: An array of matching CalendarEvent objects.

getPermissionStatus()

Get the current calendar permission status.

function getPermissionStatus(): Promise<CalendarPermissionStatus>

Returns: The permission status: 'granted', 'denied', 'limited', 'prompt', or 'unsupported'.

getProvider()

Get the current device calendar provider.

function getProvider(): CalendarProvider

Returns: The active CalendarProvider instance.

hasProvider()

Check if a calendar provider has been registered.

function hasProvider(): boolean

Returns: Whether a CalendarProvider has been bonded.

openCalendar(date)

Open the native calendar app, optionally showing a specific date.

function openCalendar(date?: Date): Promise<void>
  • date — The date to navigate to (defaults to today).

Returns: A promise that resolves when the calendar app is opened.

openEvent(eventId)

Open a specific event in the native calendar app.

function openEvent(eventId: string): Promise<void>
  • eventId — The ID of the event to open.

Returns: A promise that resolves when the event is opened.

openSettings()

Open the system settings screen for calendar permissions.

function openSettings(): Promise<void>

Returns: A promise that resolves when the settings screen is opened.

parseQuickEvent(text, calendarId)

Parse a text string into a quick calendar event. Uses basic keyword matching for time references. Returns null if no title can be extracted.

function parseQuickEvent(text: string, calendarId: string): EventInput | null
  • text — Natural language event text (e.g., "Meeting tomorrow at 3pm").
  • calendarId — The calendar ID to assign the event to.

Returns: An EventInput for a 1-hour event starting at the next hour, or null if parsing fails.

requestPermission()

Request calendar permissions from the user.

function requestPermission(): Promise<CalendarPermissionStatus>

Returns: The resulting permission status after the request.

setProvider(provider)

Set the device calendar provider.

function setProvider(provider: CalendarProvider): void
  • provider — CalendarProvider implementation to register.

updateEvent(eventId, event)

Update an existing calendar event.

function updateEvent(eventId: string, event: Partial<EventInput>): Promise<CalendarEvent>
  • eventId — The ID of the event to update.
  • event — The partial event data to merge with the existing event.

Returns: The updated CalendarEvent.

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-bond ^1.0.1
  • @molecule/app-i18n ^1.0.1

Runtime Dependencies

  • @molecule/app-bond

  • @molecule/app-i18n

  • Every accessor THROWS until setProvider() is called — there is no web fallback and no prebuilt provider package ships with molecule; supply a CalendarProvider from your native runtime and gate the UI on hasProvider(). Browsers cannot read/write the OS calendar store — on web, integrate a server-side calendar API (@molecule/api-calendar) instead.

  • Do not confuse this with @molecule/app-calendar — they share nothing but a similar name. This package bonds its provider under the distinct 'device-calendar' key (the calendar UI core uses 'calendar'), so both can be wired in the same app without clobbering each other in the registry.

  • Request permission from a user gesture at the point of use; a denied prompt is remembered — recovery is openSettings(). iOS can grant 'limited' (write-only add) access.

  • Respect Calendar.readOnly (subscribed/birthday calendars reject writes) and check getCapabilities() before offering attendees or recurrence editing — support differs per platform.