
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
caldav-client
Advanced tools
A lightweight, modern CalDAV client designed specifically for calendar events (VEVENT). Fully supports iCloud, built on native fetch and fast-xml-parser.
Note: This version focuses on Calendar Events (VEVENT) management and does not include support for To-Do items (VTODO).
fast-xml-parser, lightweight and efficient.npm install caldav-client
Or using pnpm:
pnpm add caldav-client
Initialize with your iCloud email and App-Specific Password.
const { CalDAVClient } = require('caldav-client');
// It is recommended to use environment variables for credentials
const client = new CalDAVClient(
'your-email@icloud.com',
'your-app-specific-password' // Make sure to use an App-Specific Password
);
// Login and automatically discover the calendar home
const loginResult = await client.login();
if (loginResult.success) {
console.log('Login successful!');
} else {
console.error('Login failed:', loginResult.error);
}
// Get all calendars that support events
const calendars = await client.getCalendars();
calendars.forEach(cal => {
console.log(`Calendar: ${cal.displayName} (ID: ${cal.id})`);
});
// Create a red calendar named "Work Plan"
const newCalendar = await client.createCalendar('Work Plan', '#FF0000');
const eventData = {
summary: 'Project Meeting',
description: 'Discuss next week\'s development plan',
location: 'Meeting Room A',
startDate: '2025-12-01 14:00:00', // Supports 'YYYY-MM-DD HH:mm:ss' format
endDate: '2025-12-01 15:00:00',
};
// Create event in the specified calendar
const event = await client.createEvent(newCalendar, eventData);
console.log(`Event created successfully, UID: ${event.uid}`);
const eventWithAlarm = {
summary: 'Important Meeting',
startDate: '2025-12-02 10:00:00',
endDate: '2025-12-02 11:00:00',
// Add alarm: 15 minutes before
alarms: [
{ action: 'DISPLAY', minutes: 15, description: 'Meeting starts soon' }
]
};
await client.createEvent(newCalendar, eventWithAlarm);
const recurringEvent = {
summary: 'Daily Standup',
startDate: '2025-12-01 09:30:00',
endDate: '2025-12-01 09:45:00',
// Recurrence rule: repeat daily, 5 times
recurrence: {
frequency: 'DAILY', // DAILY, WEEKLY, MONTHLY, YEARLY
interval: 1, // Every 1 day
count: 5 // Repeat 5 times
// Or use until: '2025-12-31' to specify end date
}
};
await client.createEvent(newCalendar, recurringEvent);
// Get all events in the calendar
const events = await client.getCalendarObjects(newCalendar);
// Or query by time range
const rangeEvents = await client.getCalendarObjects(newCalendar, {
startDate: new Date('2025-12-01'),
endDate: new Date('2025-12-31')
});
// Update event title and time
await client.updateEvent(newCalendar, event.uid, {
summary: 'Project Meeting (Rescheduled)',
startDate: '2025-12-01 15:00:00',
endDate: '2025-12-01 16:00:00'
});
await client.deleteEvent(newCalendar, event.uid);
Use the syncCalendar method to efficiently check for calendar updates. It uses the CTag (Collection Tag) mechanism to fetch the full event list only when server-side data has changed.
// First sync, save ctag
let syncResult = await client.syncCalendar(newCalendar);
let lastCtag = syncResult.ctag;
console.log(`Current version: ${lastCtag}`);
// ... some time later ...
// Sync again, passing the previous ctag
syncResult = await client.syncCalendar(newCalendar, lastCtag);
if (syncResult.hasChanges) {
console.log('Calendar has updates!');
console.log('Latest events:', syncResult.events);
// Update local ctag
lastCtag = syncResult.ctag;
} else {
console.log('No changes, no update needed.');
}
CalDAVClient Classconstructor(username, password)Initialize the client.
username: iCloud email.password: App-Specific Password (generated on Apple ID website).async login()Perform service discovery to get Principal and Calendar Home URL.
{ success: boolean, error?: string }async getCalendars()Get all calendars that support VEVENT.
Array<Calendar>async createCalendar(name, color?)Create a new calendar.
name: Calendar display name.color: (Optional) Hex color code, e.g., #FF0000. Defaults to #3B82F6FF.Calendar objectasync updateCalendar(calendar, { name, color })Update calendar properties.
calendar: Target calendar object.name: (Optional) New display name.color: (Optional) New color code.async deleteCalendar(calendar)Delete a calendar.
calendar: Target calendar object.async getCalendarDetails(calendar)Get detailed calendar information.
calendar: Target calendar object.{ displayName, color, ctag, owner, privileges }async getCalendarObjects(calendar, options?)Get events from a calendar.
calendar: Target calendar object.options: (Optional) Query options:
startDate: Start date (Date object or string)endDate: End date (Date object or string)Array<Event>async getEvent(calendar, uid)Get a single event by UID.
calendar: Target calendar object.uid: Event UID.Event object or nullasync createEvent(calendar, eventData)Create a calendar event.
calendar: Target calendar object.eventData: Event data object (see below).async updateEvent(calendar, eventUid, eventData)Update an existing event.
calendar: Target calendar object.eventUid: The UID of the event.eventData: Fields to update (omitted fields will remain unchanged).async deleteEvent(calendar, eventUid)Delete a specified event.
calendar: Target calendar object.eventUid: The UID of the event.async syncCalendar(calendar, lastCtag?)Check if the calendar has updates.
calendar: Target calendar object.lastCtag: The CTag saved from the last sync.{ hasChanges: boolean, ctag: string, events: Array, message: string }{
id: "...", // Unique calendar ID
url: "...", // Calendar server path
displayName: "...", // Display name
color: "#...", // Color
components: ["VEVENT"] // Supported component types
}
Used for creating or updating events:
{
summary: "Title",
description: "Description",
location: "Location",
startDate: "2025-12-01 10:00:00", // or Date object
endDate: "2025-12-01 11:00:00", // or Date object
// Recurrence Rule (Optional, see table below)
recurrence: {
frequency: 'DAILY',
interval: 1,
count: 5
},
// Alarms (Optional, see table below)
alarms: [
{ minutes: 15, action: 'DISPLAY', description: 'Meeting starts soon' }
]
}
The recurrence object defines event repeat patterns (corresponds to iCalendar RRULE).
| Property | Type | Required | Description | Example |
|---|---|---|---|---|
frequency | String | Yes | Repeat frequency. Options: DAILY, WEEKLY, MONTHLY, YEARLY | 'WEEKLY' |
interval | Number | No | Repeat interval. Default is 1. E.g., frequency: 'WEEKLY', interval: 2 means every 2 weeks. | 2 |
count | Number | No | Total number of occurrences. Cannot be used with until. | 5 |
until | String/Date | No | End date for recurrence. Cannot be used with count. | '2025-12-31' |
byDay | String/Array | No | Specify days of week. Options: SU, MO, TU, WE, TH, FR, SA. | ['MO', 'WE'] |
byMonth | Number/Array | No | Specify months (1-12). | [6, 12] |
byMonthDay | Number/Array | No | Specify days of month (1-31 or negative for counting from end). | 15 |
weekStart | String | No | Week start day. Same options as byDay. | 'SU' |
The alarms array supports object format or number shorthand.
Object Format:
| Property | Type | Required | Description | Default |
|---|---|---|---|---|
minutes | Number | Yes | Minutes before event to trigger alarm. | - |
action | String | No | Alarm action. Currently supports DISPLAY. | 'DISPLAY' |
description | String | No | Text description for the alarm. | 'Reminder' |
Shorthand Format:
If array elements are numbers, they are treated as minutes with default values for other properties.
Example: alarms: [15, 60] equals two alarms at 15 and 60 minutes before.
This project includes complete test scripts.
test-full.js.node test-full.js
MIT License
FAQs
A lightweight CalDAV client for Node.js with deep iCloud support
We found that caldav-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.