← All @molecule/* packages · App templates
@molecule/api-calendar-microsoftProvider bond · calendar · API (Node) · v1.0.1 · Apache-2.0
Microsoft Outlook / Office 365 Calendar bond for molecule.dev — read busy-blocks + write booking events via Microsoft Graph v1.0 REST API
npm install @molecule/api-calendar-microsoftnpm · Source on GitHub · Implements @molecule/api-calendar
@molecule/api-calendar-microsoft is a provider bond on the API (Node) side: it implements the calendar core interface (@molecule/api-calendar) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-calendar'
import { provider } from '@molecule/api-calendar-microsoft'
setProvider(provider)Works with: @molecule/api-bond, @molecule/api-calendar, @molecule/api-http, @molecule/api-secrets
Secrets: OAUTH_MICROSOFT_CLIENT_ID, OAUTH_MICROSOFT_CLIENT_SECRET
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.tsJSDoc, not this file.
Microsoft Calendar bond for molecule.dev.
Implements the {@link CalendarProvider} contract from
@molecule/api-calendar against the Microsoft Graph v1.0 REST API
(Outlook / Office 365 calendars).
Calendars.ReadWrite,
Schedule.Read (or Calendars.Read.Shared for the
getSchedule call), and offline_access so refresh tokens are
issued.OAUTH_MICROSOFT_CLIENT_ID and OAUTH_MICROSOFT_CLIENT_SECRET
in the API environment (or pass clientId / clientSecret to
{@link createProvider}).accessToken + refreshToken after OAuth
completion. Pass them to every @molecule/api-calendar call.import { setProvider } from '@molecule/api-calendar'
import { provider } from '@molecule/api-calendar-microsoft'
setProvider(provider)
provider
npm install @molecule/api-calendar-microsoft @molecule/api-bond @molecule/api-calendar @molecule/api-http @molecule/api-secrets
BusyBlockA contiguous busy block returned from a free/busy query.
interface BusyBlock {
/** ISO 8601 start of the busy block. */
start: string
/** ISO 8601 end of the busy block. */
end: string
/** Originating calendar id. */
calendarId: string
}
CalendarEventA calendar event.
Times are ISO 8601 strings. For all-day events, prefer date-only strings
(YYYY-MM-DD) and set {@link CalendarEvent.allDay} to true.
interface CalendarEvent {
/** Provider-specific event identifier. May be undefined for new events. */
id?: string
/** Event title (caller is responsible for any i18n on user-visible text). */
summary: string
/** Optional rich description / notes. */
description?: string
/** Event location string. */
location?: string
/** ISO 8601 start (date-time, or `YYYY-MM-DD` if all-day). */
start: string
/** ISO 8601 end (date-time, or `YYYY-MM-DD` if all-day). */
end: string
/** IANA time zone for the start/end values. */
timeZone?: string
/** Whether this event spans entire day(s). Defaults to `false`. */
allDay?: boolean
/** Optional attendee list. */
attendees?: EventAttendee[]
/** Provider-specific status flag (`confirmed`, `tentative`, `cancelled`). */
status?: 'confirmed' | 'tentative' | 'cancelled' | string
/** Conferencing / meeting link, if present. */
hangoutLink?: string
/** Arbitrary provider data (kept for round-tripping). */
metadata?: Record<string, unknown>
}
CalendarOperationResultWrapper returned from every {@link CalendarProvider} method so callers can persist refreshed credentials when the provider rotates them.
interface CalendarOperationResult<T> {
/** Operation payload. `void` operations return `undefined`. */
data: T
/**
* Refreshed credentials, present iff the access token was rotated during
* this call. Callers MUST persist these so subsequent calls succeed.
*/
credentials?: CalendarUserCredentials
}
CalendarProviderCalendar provider contract.
Every method takes user-scoped {@link CalendarUserCredentials} so the same provider instance can serve multiple users. Implementations are responsible for refreshing expired tokens transparently and returning any updated credentials via {@link CalendarOperationResult.credentials}.
interface CalendarProvider {
/**
* Lists the calendars accessible to the authenticated user.
*
* @param credentials - The user's OAuth credentials.
* @returns The user's calendar list and any refreshed credentials.
*/
listCalendars(
credentials: CalendarUserCredentials,
): Promise<CalendarOperationResult<CalendarSummary[]>>
/**
* Lists events on a specific calendar within a time window.
*
* @param credentials - The user's OAuth credentials.
* @param calendarId - Provider-specific calendar id.
* @param options - Time range and paging options.
*/
listEvents(
credentials: CalendarUserCredentials,
calendarId: string,
options: ListEventsOptions,
): Promise<CalendarOperationResult<CalendarEvent[]>>
/**
* Creates a new event on a calendar.
*
* @param credentials - The user's OAuth credentials.
* @param calendarId - Provider-specific calendar id.
* @param event - Event payload (id is ignored; provider assigns one).
*/
createEvent(
credentials: CalendarUserCredentials,
calendarId: string,
event: CalendarEvent,
): Promise<CalendarOperationResult<CalendarEvent>>
/**
* Updates an existing event by id.
*
* @param credentials - The user's OAuth credentials.
* @param calendarId - Provider-specific calendar id.
* @param eventId - Event id to update.
* @param updates - Partial event payload to merge.
*/
updateEvent(
credentials: CalendarUserCredentials,
calendarId: string,
eventId: string,
updates: Partial<Omit<CalendarEvent, 'id'>>,
): Promise<CalendarOperationResult<CalendarEvent>>
/**
* Deletes an event by id.
*
* @param credentials - The user's OAuth credentials.
* @param calendarId - Provider-specific calendar id.
* @param eventId - Event id to delete.
*/
deleteEvent(
credentials: CalendarUserCredentials,
calendarId: string,
eventId: string,
): Promise<CalendarOperationResult<void>>
/**
* Computes free slots across one or more calendars.
*
* @param credentials - The user's OAuth credentials.
* @param calendarIds - One or more provider-specific calendar ids.
* @param options - Time window and slot duration.
*/
findFreeSlots(
credentials: CalendarUserCredentials,
calendarIds: string[],
options: FindFreeSlotsOptions,
): Promise<CalendarOperationResult<FreeBusyResult>>
}
CalendarSummaryA single calendar in a user's calendar list.
interface CalendarSummary {
/** Provider-specific calendar identifier (opaque). */
id: string
/** Human-readable calendar name. */
summary: string
/** Optional longer description. */
description?: string
/** IANA time zone, e.g. `America/Los_Angeles`. */
timeZone?: string
/** Whether this calendar is the user's primary calendar. */
primary?: boolean
/** Access role granted to the authenticated user (provider-specific). */
accessRole?: string
}
CalendarUserCredentialsOAuth token bundle for a single user. Bonds use these to call the provider's API on behalf of that user, refreshing the access token automatically when it expires.
interface CalendarUserCredentials {
/** Current access token. */
accessToken: string
/** Refresh token used to mint new access tokens. */
refreshToken: string
/** Optional epoch-millis timestamp for the access token's expiry. */
expiresAt?: number
}
EventAttendeeAttendee information attached to a calendar event.
interface EventAttendee {
/** Attendee email address. */
email: string
/** Optional display name. */
displayName?: string
/** Whether attendance is optional. */
optional?: boolean
/** RSVP response status (provider-specific values). */
responseStatus?: 'needsAction' | 'declined' | 'tentative' | 'accepted' | string
}
FindFreeSlotsOptionsQuery parameters for the free/busy / free-slot computation.
interface FindFreeSlotsOptions {
/** ISO 8601 lower bound (inclusive). */
timeMin: string
/** ISO 8601 upper bound (exclusive). */
timeMax: string
/** Required slot duration in minutes. */
durationMinutes: number
/** Optional IANA time zone for the returned slot boundaries. */
timeZone?: string
}
FreeBusyResultResult of a free/busy lookup plus computed free slots.
interface FreeBusyResult {
/** Aggregated busy blocks across all queried calendars. */
busy: BusyBlock[]
/** Computed free slots that fit `durationMinutes`. */
freeSlots: FreeSlot[]
}
FreeSlotA free slot of {@link FindFreeSlotsOptions.durationMinutes} length.
interface FreeSlot {
/** ISO 8601 slot start. */
start: string
/** ISO 8601 slot end. */
end: string
}
ListEventsOptionsQuery parameters for listing events on a calendar.
interface ListEventsOptions {
/** ISO 8601 lower bound (inclusive). */
timeMin: string
/** ISO 8601 upper bound (exclusive). */
timeMax: string
/** Maximum number of events to return. Provider-defined cap when omitted. */
maxResults?: number
/** When true, recurring events are returned as individual instances. */
singleEvents?: boolean
/** Optional sort order — `startTime` is provider-supported by Google. */
orderBy?: 'startTime' | 'updated'
}
MicrosoftCalendarProviderOptionsConfiguration options for {@link createProvider}.
interface MicrosoftCalendarProviderOptions {
/**
* OAuth client id used to refresh user access tokens. Defaults to
* `process.env.OAUTH_MICROSOFT_CLIENT_ID`.
*/
clientId?: string
/**
* OAuth client secret used to refresh user access tokens. Defaults to
* `process.env.OAUTH_MICROSOFT_CLIENT_SECRET`.
*/
clientSecret?: string
/**
* Override the Microsoft Graph API base URL. Useful for tests pointing
* at a fake Microsoft Graph server. Defaults to
* `https://graph.microsoft.com/v1.0`.
*/
apiBaseUrl?: string
/**
* Override the Microsoft OAuth token endpoint. Useful for tests. Defaults
* to `https://login.microsoftonline.com/common/oauth2/v2.0/token`.
*/
tokenUrl?: string
/**
* OAuth scope requested when refreshing access tokens. Defaults to
* `https://graph.microsoft.com/.default offline_access`.
*/
scope?: string
/**
* Request timeout in milliseconds. Defaults to `15_000`.
*/
timeoutMs?: number
}
computeFreeSlots(busyBlocks, windowStart, windowEnd, durationMinutes)Computes free slots from a list of busy blocks within a window.
Same algorithm as the Google bond's computeFreeSlots; inlined here so
sibling provider bonds remain independent of each other.
function computeFreeSlots(
busyBlocks: { start: string; end: string }[],
windowStart: string,
windowEnd: string,
durationMinutes: number,
): FreeSlot[]
busyBlocks — Busy blocks (any order; will be sorted).windowStart — ISO 8601 lower bound of the search window.windowEnd — ISO 8601 upper bound of the search window.durationMinutes — Required slot duration in minutes.Returns: Free slots that fully fit within the window.
createProvider(options)Creates a Microsoft Calendar provider.
function createProvider(options?: MicrosoftCalendarProviderOptions): CalendarProvider
options — Optional configuration. Falls back to OAUTH_MICROSOFT_CLIENT_ID / OAUTH_MICROSOFT_CLIENT_SECRET env vars when clientId / clientSecret are omitted.Returns: A {@link CalendarProvider} implementation.
calendarMicrosoftSecretDefinitionsSecret definitions required by the Microsoft calendar bond.
const calendarMicrosoftSecretDefinitions: SecretDefinition[]
providerThe Microsoft Calendar provider. Lazily initialized on first use so that environment variables are read at call time rather than import time.
const provider: CalendarProvider
Implements @molecule/api-calendar interface.
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-calendar'
import { provider } from '@molecule/api-calendar-microsoft'
export function setupCalendarMicrosoft(): void {
setProvider(provider)
}
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-calendar ^1.0.1@molecule/api-http ^1.0.1@molecule/api-secrets ^1.0.1OAUTH_MICROSOFT_CLIENT_ID (required) — Microsoft application (client) ID
OAUTH_MICROSOFT_CLIENT_SECRET (required) — Microsoft client secret
@molecule/api-bond@molecule/api-calendar@molecule/api-http@molecule/api-secretsIntegration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
timeZone and
ISO 8601 start/end, so a UTC/offset bug surfaces as a wrong displayed hour.