import { requestGoogle } from './core.ts'
import type { JsonObject } from './types.ts'
export type CalendarListParams = { account: string; maxResults?: number; pageToken?: string; minAccessRole?: string; showDeleted?: boolean; showHidden?: boolean; fields?: string }
export type CalendarEventsParams = { account: string; calendarId?: string; timeMin?: string; timeMax?: string; maxResults?: number; q?: string; singleEvents?: boolean; orderBy?: string; pageToken?: string; fields?: string }
export type CalendarEventsAcrossCalendarsParams = CalendarEventsParams & { calendarMaxResults?: number; showHiddenCalendars?: boolean }
export type CalendarEventMutationParams = { account: string; calendarId?: string; event?: JsonObject; dryRun?: boolean; sendUpdates?: string }
export type CalendarEventDeleteParams = { account: string; calendarId?: string; eventId: string; sendUpdates?: string }
export type CalendarEventGetParams = { account: string; calendarId?: string; eventId: string; fields?: string }
export type CalendarEventMoveParams = { account: string; calendarId?: string; eventId: string; destination: string; sendUpdates?: string }
export type CalendarEventQuickAddParams = { account: string; calendarId?: string; text: string; sendUpdates?: string }
export type CalendarAclListParams = { account: string; calendarId?: string; maxResults?: number; pageToken?: string; showDeleted?: boolean; syncToken?: string; fields?: string }
export type CalendarGetParams = { account: string; calendarId?: string; fields?: string }
export type CalendarFreeBusyParams = { account: string; request?: JsonObject; items?: Array<{ id: string }>; timeMin?: string; timeMax?: string; timeZone?: string; groupExpansionMax?: number; calendarExpansionMax?: number; fields?: string }
export async function listCalendars(params: CalendarListParams): Promise<JsonObject> { const { maxResults = 50, pageToken, minAccessRole, showDeleted, showHidden, fields } = params; return await requestGoogle({ ...params, api: 'google', path: '/calendar/v3/users/me/calendarList', query: { maxResults, pageToken, minAccessRole, showDeleted, showHidden, fields } }) as JsonObject }
export async function getCalendar(params: CalendarGetParams): Promise<JsonObject> { const { calendarId = 'primary', fields } = params; return await requestGoogle({ ...params, api: 'google', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId), query: { fields } }) as JsonObject }
export async function listEvents(params: CalendarEventsParams): Promise<JsonObject> { const { calendarId = 'primary', timeMin, timeMax, maxResults = 10, q, singleEvents = true, orderBy = singleEvents ? 'startTime' : undefined, pageToken, fields } = params; return await requestGoogle({ ...params, api: 'google', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events', query: { timeMin, timeMax, maxResults, q, singleEvents, orderBy, pageToken, fields } }) as JsonObject }
export async function listEventsAcrossCalendars(params: CalendarEventsAcrossCalendarsParams): Promise<JsonObject> {
const { account, calendarMaxResults = 250, showHiddenCalendars = true } = params
const calendars: Array<{ id: string; summary: string }> = []
const failures: string[] = []
let calendarPageToken: string | undefined
try {
do {
const result = await listCalendars({
account,
maxResults: calendarMaxResults,
pageToken: calendarPageToken,
showHidden: showHiddenCalendars,
})
for (const calendar of (result.items || []) as Array<JsonObject>) {
if (!calendar.id) continue
calendars.push({
id: String(calendar.id),
summary: String(calendar.summary || calendar.id),
})
}
calendarPageToken = result.nextPageToken ? String(result.nextPageToken) : undefined
} while (calendarPageToken)
} catch (error) {
failures.push(error instanceof Error ? error.message : String(error))
}
if (!calendars.length) calendars.push({ id: 'primary', summary: 'Primary' })
const items: JsonObject[] = []
for (const calendar of calendars) {
let eventPageToken: string | undefined
do {
const result = await listEvents({
...params,
calendarId: calendar.id,
maxResults: params.maxResults ?? 250,
pageToken: eventPageToken,
})
for (const event of (result.items || []) as Array<JsonObject>) {
items.push({
...event,
calendarId: calendar.id,
calendarSummary: calendar.summary,
})
}
eventPageToken = result.nextPageToken ? String(result.nextPageToken) : undefined
} while (eventPageToken)
}
return { items, calendars, failures }
}
export async function createEvent(params: CalendarEventMutationParams): Promise<JsonObject> { const { calendarId = 'primary', event, dryRun = false, sendUpdates } = params; if (!event || typeof event !== 'object') throw new Error('createEvent requires event.'); if (dryRun) return { dryRun: true, account: params.account, calendarId, event }; return await requestGoogle({ ...params, api: 'google', method: 'POST', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events', query: { sendUpdates }, body: event }) as JsonObject }
export async function getEvent(params: CalendarEventGetParams): Promise<JsonObject> { const { calendarId = 'primary', eventId, fields } = params; if (!eventId) throw new Error('getEvent requires eventId.'); return await requestGoogle({ ...params, api: 'google', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events/' + encodeURIComponent(eventId), query: { fields } }) as JsonObject }
export async function updateEvent(params: CalendarEventMutationParams & { eventId: string }): Promise<JsonObject> { const { calendarId = 'primary', eventId, event, dryRun = false, sendUpdates } = params; if (!eventId) throw new Error('updateEvent requires eventId.'); if (!event || typeof event !== 'object') throw new Error('updateEvent requires event.'); if (dryRun) return { dryRun: true, account: params.account, calendarId, eventId, event }; return await requestGoogle({ ...params, api: 'google', method: 'PATCH', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events/' + encodeURIComponent(eventId), query: { sendUpdates }, body: event }) as JsonObject }
export async function deleteEvent(params: CalendarEventDeleteParams): Promise<void> { const { calendarId = 'primary', eventId, sendUpdates } = params; if (!eventId) throw new Error('deleteEvent requires eventId.'); await requestGoogle({ ...params, api: 'google', method: 'DELETE', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events/' + encodeURIComponent(eventId), query: { sendUpdates } }) }
export async function moveEvent(params: CalendarEventMoveParams): Promise<JsonObject> { const { calendarId = 'primary', eventId, destination, sendUpdates } = params; if (!eventId) throw new Error('moveEvent requires eventId.'); if (!destination) throw new Error('moveEvent requires destination.'); return await requestGoogle({ ...params, api: 'google', method: 'POST', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events/' + encodeURIComponent(eventId) + '/move', query: { destination, sendUpdates } }) as JsonObject }
export async function quickAddEvent(params: CalendarEventQuickAddParams): Promise<JsonObject> { const { calendarId = 'primary', text, sendUpdates } = params; if (!text) throw new Error('quickAddEvent requires text.'); return await requestGoogle({ ...params, api: 'google', method: 'POST', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/events/quickAdd', query: { text, sendUpdates } }) as JsonObject }
export async function listCalendarAcl(params: CalendarAclListParams): Promise<JsonObject> { const { calendarId = 'primary', maxResults, pageToken, showDeleted, syncToken, fields } = params; return await requestGoogle({ ...params, api: 'google', path: '/calendar/v3/calendars/' + encodeURIComponent(calendarId) + '/acl', query: { maxResults, pageToken, showDeleted, syncToken, fields } }) as JsonObject }
export async function queryFreeBusy(params: CalendarFreeBusyParams): Promise<JsonObject> { const { request, items, timeMin, timeMax, timeZone, groupExpansionMax, calendarExpansionMax, fields } = params; const body = request || { timeMin, timeMax, timeZone, groupExpansionMax, calendarExpansionMax, items }; if (!request && !items) throw new Error('queryFreeBusy requires request or items.'); return await requestGoogle({ ...params, api: 'google', method: 'POST', path: '/calendar/v3/freeBusy', query: { fields }, body }) as JsonObject }
/**
* Return the Google Calendar helper namespace for calendars, events, ACL, and free/busy.
* @example
* import calendar from 'kody:@kentcdodds/google/calendar'
* const events = await calendar().listEvents({ account: 'business', maxResults: 5 })
* // => { items: [...], summary: '...' }
*/
export default function calendar() { return { listCalendars, getCalendar, listEvents, listEventsAcrossCalendars, createEvent, getEvent, updateEvent, deleteEvent, moveEvent, quickAddEvent, listCalendarAcl, queryFreeBusy } }