Skip to content

fetchCalendars: accept calendars without supported-calendar-component-set#276

Open
ilyasturki wants to merge 1 commit intonatelindev:mainfrom
ilyasturki:fix/lenient-supported-component-set
Open

fetchCalendars: accept calendars without supported-calendar-component-set#276
ilyasturki wants to merge 1 commit intonatelindev:mainfrom
ilyasturki:fix/lenient-supported-component-set

Conversation

@ilyasturki
Copy link
Copy Markdown

Summary

fetchCalendars currently filters out any calendar whose supported-calendar-component-set property is empty or absent. Real-world CalDAV servers do this in violation of RFC 4791 § 5.2.3 — most notably Purelymail, which returns an empty <supported-calendar-component-set/> element on every calendar collection.

The earlier filter (Object.keys(resourcetype).includes('calendar')) has already confirmed the resource is a calendar by the time we reach the component-set check, so the second filter is acting as belt-and-suspenders defense rather than primary identification. When the property is absent, falling back to "accept" matches what tolerant clients like Thunderbird/Lightning, Evolution-DS, and DAVx5 do in practice.

Reproduction (against Purelymail)

import { DAVClient } from "tsdav";

const client = new DAVClient({
  serverUrl: "https://purelymail.com/",
  credentials: { username: "you@yours.com", password: "<app-password>" },
  authMethod: "Basic",
  defaultAccountType: "caldav",
});
await client.login();
console.log(await client.fetchCalendars());
// Before this PR: []
// After this PR: [{ url: "https://purelymail.com/webdav/<id>/caldav/default/", displayName: "Default", ... }]

DEBUG=tsdav:* confirms the full discovery chain succeeds (well-known redirect → principal → calendar-home-set) — only fetchCalendars swallows the actual calendar.

Diff summary

  • src/calendar.ts: when extractComponentNames returns an empty array, accept the calendar instead of rejecting it.
  • src/__tests__/unit/calendar.test.ts: regression test with two fixtures — empty supported-calendar-component-set element (Purelymail's actual shape) and the property being entirely absent.

Verification

  • pnpm test — 186/186 unit tests pass (12 files), including 2 new fixtures.
  • pnpm typecheck — clean.
  • pnpm lint — clean for changed files (2 pre-existing errors in src/account.ts are unrelated to this PR).
  • End-to-end against Purelymail's live CalDAV — fetchCalendars() now returns the Default calendar correctly; downstream fetchCalendarObjects and freeBusyQuery also work.

Compatibility

  • Existing test "should filter out non-iCal calendars" still passes — calendars that explicitly declare a non-iCal component (e.g. UNKNOWN_TYPE) still get filtered out. Only the absent/empty case changes behavior.
  • No public API surface change; no opt-out flag added (happy to add one if you'd prefer to keep strict-by-default).

…t is absent

Some CalDAV servers (notably Purelymail) violate RFC 4791 § 5.2.3 by failing to
return the supported-calendar-component-set property in PROPFIND responses, even
on real calendar collections. The current filter rejects every such calendar,
breaking discovery against otherwise functional servers.

Treat the absence of declared components as "unknown, accept" rather than
"reject". The earlier resourcetype filter has already confirmed the resource
declares <calendar/>, so we have independent evidence it's a calendar; the
component-set filter only meaningfully discriminates between iCal-style and
non-iCal calendars when a server actually populates the property.

Tolerant clients like Thunderbird and Evolution-DS accept these servers today;
this brings tsdav in line with their behavior. The added regression test covers
both an empty <supported-calendar-component-set/> element (Purelymail's actual
response shape) and the property being entirely absent from the response.
Copilot AI review requested due to automatic review settings May 1, 2026 17:31
@vercel
Copy link
Copy Markdown

vercel Bot commented May 1, 2026

@ilyasturki is attempting to deploy a commit to the llldar's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates fetchCalendars to be more tolerant of real-world CalDAV servers that omit or return an empty supported-calendar-component-set, while keeping existing filtering behavior when components are explicitly declared.

Changes:

  • Adjust calendar filtering to accept calendars when supported-calendar-component-set yields no component names.
  • Add unit test coverage for calendars where supported-calendar-component-set is empty or absent.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/calendar.ts Relaxes iCal component filtering to treat empty/missing component-set as “accept”.
src/__tests__/unit/calendar.test.ts Adds regression test for empty and missing supported-calendar-component-set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +379 to +383
it('should accept calendars when supported-calendar-component-set is missing', async () => {
// Some servers (e.g. Purelymail) violate RFC 4791 § 5.2.3 by not returning the
// supported-calendar-component-set property. The previous resourcetype filter has
// already confirmed these are calendar collections, so they should pass through.
mockedPropfind.mockResolvedValue([
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name says "supported-calendar-component-set is missing" but the fixture also covers the "present but empty" case (supportedCalendarComponentSet: {}); consider renaming the test to mention both (e.g. "missing or empty") or splitting into two tests to make the intent explicit.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants