npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mildshield14/ical-booker

v1.1.1

Published

A lightweight, modern CalDAV client for Node.js - discover calendars, check availability, and create bookings

Readme

@mildshield14/ical-booker

A lightweight, modern CalDAV client for Node.js that makes it easy to discover calendars, check availability, and create bookings. Perfect for building scheduling applications, availability checkers, and calendar integrations.

✨ Features

  • 🔍 Auto-discovery: Automatically discovers calendar collections from CalDAV servers
  • 📅 Multi-provider support: Works with iCloud, Google Calendar, Fastmail, and other CalDAV servers
  • Busy time checking: Quickly fetch busy/free information for scheduling
  • 📝 Event creation: Create calendar events with attendees
  • 🔒 Secure authentication: Supports app-specific passwords and OAuth
  • 🪶 Lightweight: Minimal dependencies, ESM-first design

🚀 Quick Start

npm install @mildshield14/ical-booker

Basic Usage

import { discoverCalendars, getBusyEvents, createBooking } from '@mildshield14/ical-booker';

const creds = {
    principal: 'https://p55-caldav.icloud.com',  // CalDAV server URL
    username: '[email protected]',           // Full email address
    password: 'your-app-specific-password'       // App-specific password
};

// 1. Discover available calendars
const calendars = await discoverCalendars(creds);
console.log('Available calendars:', calendars.map(c => c.displayName));

// 2. Check busy times for the next 24 hours
const now = new Date();
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const busyEvents = await getBusyEvents(creds, calendars, now.toISOString(), tomorrow.toISOString());

console.log('Busy times:');
busyEvents.forEach(event => 
    console.log(`${event.start} - ${event.end}: ${event.title}`)
);

// 3. Create a new booking
const booking = await createBooking(
    { ...creds, calendarURL: calendars[0].url },
    {
        start: '2024-03-15T14:00:00Z',
        end: '2024-03-15T15:00:00Z',
        title: 'Team Meeting',
        attendee: '[email protected]'
    }
);

📚 API Reference

discoverCalendars(credentials)

Discovers all writable calendar collections for the given CalDAV account.

Parameters:

  • credentials (Object):
    • principal (string): CalDAV server URL
    • username (string): Full email address
    • password (string): App-specific password

Returns: Promise<Calendar[]>

Example:

const calendars = await discoverCalendars({
    principal: 'https://p55-caldav.icloud.com',
    username: '[email protected]',
    password: 'app-specific-password'
});

getBusyEvents(credentials, calendars, startTime, endTime)

Fetches busy/occupied time slots from the specified calendars.

Parameters:

  • credentials (Object): CalDAV credentials
  • calendars (Calendar[]): Array of calendar objects from discoverCalendars
  • startTime (string): ISO 8601 start time
  • endTime (string): ISO 8601 end time

Returns: Promise<BusyEvent[]>

Example:

const busyEvents = await getBusyEvents(
    creds,
    calendars,
    '2024-03-15T00:00:00Z',
    '2024-03-15T23:59:59Z'
);

createBooking(credentials, eventDetails)

Creates a new calendar event/booking.

Parameters:

  • credentials (Object): CalDAV credentials + calendarURL
  • eventDetails (Object):
    • start (string): ISO 8601 start time
    • end (string): ISO 8601 end time
    • title (string): Event title
    • attendee (string, optional): Attendee email

Returns: Promise<BookingResult>

Example:

const result = await createBooking(
    { ...creds, calendarURL: calendars[0].url },
    {
        start: '2024-03-15T14:00:00Z',
        end: '2024-03-15T15:00:00Z',
        title: 'Important Meeting',
        attendee: '[email protected]'
    }
);

🔧 Provider Setup

iCloud Setup

  1. Generate App-Specific Password:

    • Go to appleid.apple.com
    • Sign in and go to Security section
    • Generate an app-specific password for "CalDAV Access"
  2. Find your CalDAV URL:

    • Use https://pXX-caldav.icloud.com (where XX is your partition number)
    • The library will auto-discover the correct partition
const icloudCreds = {
    principal: 'https://p55-caldav.icloud.com', // Try different partition numbers
    username: '[email protected]',
    password: 'your-app-specific-password'
};

Google Calendar Setup

  1. Enable CalDAV in Google Calendar settings
  2. Use OAuth2 or App Password:
const googleCreds = {
    principal: 'https://apidata.googleusercontent.com/caldav/v2/',
    username: '[email protected]',
    password: 'your-app-password'
};

Fastmail Setup

const fastmailCreds = {
    principal: 'https://caldav.fastmail.com/',
    username: '[email protected]',
    password: 'your-password'
};

Generic CalDAV Server

const genericCreds = {
    principal: 'https://your-caldav-server.com/',
    username: 'your-username',
    password: 'your-password'
};

🛡️ Error Handling

The library throws descriptive errors for common issues:

try {
    const calendars = await discoverCalendars(creds);
} catch (error) {
    if (error.message.includes('calendar-home-set not found')) {
        console.error('CalDAV server configuration issue');
    } else if (error.message.includes('401')) {
        console.error('Authentication failed - check credentials');
    } else {
        console.error('Unexpected error:', error.message);
    }
}

🏗️ Building Scheduling Apps

Check Availability

async function findFreeSlots(calendars, date, duration = 60) {
    const dayStart = new Date(date);
    dayStart.setHours(9, 0, 0, 0); // 9 AM
    
    const dayEnd = new Date(date);
    dayEnd.setHours(17, 0, 0, 0); // 5 PM
    
    const busyEvents = await getBusyEvents(
        creds,
        calendars,
        dayStart.toISOString(),
        dayEnd.toISOString()
    );
    
    // Find gaps between busy periods
    const freeSlots = [];
    let currentTime = dayStart;
    
    for (const event of busyEvents.sort((a, b) => new Date(a.start) - new Date(b.start))) {
        const eventStart = new Date(event.start);
        const gap = eventStart - currentTime;
        
        if (gap >= duration * 60 * 1000) { // Duration in milliseconds
            freeSlots.push({
                start: currentTime.toISOString(),
                end: eventStart.toISOString(),
                duration: gap / (60 * 1000) // Minutes
            });
        }
        
        currentTime = new Date(Math.max(currentTime, new Date(event.end)));
    }
    
    return freeSlots;
}

Batch Operations

async function checkMultipleCalendars(credentialsList, timeRange) {
    const results = await Promise.all(
        credentialsList.map(async (creds) => {
            try {
                const calendars = await discoverCalendars(creds);
                const busyEvents = await getBusyEvents(creds, calendars, timeRange.start, timeRange.end);
                return { creds, calendars, busyEvents, success: true };
            } catch (error) {
                return { creds, error: error.message, success: false };
            }
        })
    );
    
    return results;
}

🧪 Testing

# Run tests
npm test

# Run with coverage
npm run test:coverage

# Test with specific provider
CALDAV_PROVIDER=icloud npm test

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

📝 License

MIT License - see LICENSE file for details.

🔗 Links

📊 Changelog

v1.0.0

  • Initial release
  • iCloud, Google Calendar, and Fastmail support
  • Calendar discovery and busy time checking
  • Event creation functionality