# tsdav Documentation (Full Text for LLMs) TypeScript WebDAV, CalDAV & CardDAV Client Library. ======================================== ## File: caldav/calendarMultiGet.md --- title: 'calendarMultiGet' sidebar_position: 2 description: "Retrieve specific calendar objects in bulk using RFC 4791 calendar-multiget REPORT." --- ## `calendarMultiGet` calendarMultiGet is used to retrieve specific calendar object resources from within a collection. If the Request-URI is a calendar object resource. This method is similar to the [calendarQuery](./calendarQuery.md), except that it takes a list of calendar object urls, instead of a filter, to determine which calendar objects to return. ```ts // fetch 2 specific objects from one calendar const calendarObjects = await calendarMultiGet({ url: 'https://caldav.icloud.com/1234567/calendars/personal/', props: { [`${DAVNamespaceShort.DAV}:getetag`]: {}, [`${DAVNamespaceShort.CALDAV}:calendar-data`]: {}, }, objectUrls: [ 'https://caldav.icloud.com/1234567/calendars/personal/1.ics', 'https://caldav.icloud.com/1234567/calendars/personal/2.ics', ], depth: '1', headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, url of CALDAV server - `objectUrls` **required**, urls of calendar object to get - `depth` **required**, [DAVDepth](../types/DAVDepth.md) of the request - `props` [CALDAV prop element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.4) in [ElementCompact](../types/ElementCompact.md) form - `filters` [CALDAV filter element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.7) in [ElementCompact](../types/ElementCompact.md) form - `timezone` timezone of the calendar - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVCalendarObject](../types/DAVCalendarObject.md) ### Behavior send caldav:calendar-multiget REPORT request and parse the response xml to extract an array of [DAVCalendarObject](../types/DAVCalendarObject.md) data. ---------------------------------------- ## File: caldav/calendarQuery.md --- title: 'calendarQuery' sidebar_position: 1 description: "Query CalDAV calendar events using RFC 4791 calendar-query REPORT with time-range filters." --- ## `calendarQuery` calendarQuery performs a search for all calendar object resources that match a specified filter. The response of this report will contain all the WebDAV properties and calendar object resource data specified in the request. In the case of the `calendarData` element, one can explicitly specify the calendar components and properties that should be returned in the calendar object resource data that matches the filter. ```ts // fetch all objects of the calendar const results = await calendarQuery({ url: 'https://caldav.icloud.com/1234567/calendars/personal/', props: [{ name: 'getetag', namespace: DAVNamespace.DAV }], filters: [ { 'comp-filter': { _attributes: { name: 'VCALENDAR', }, }, }, ], depth: '1', headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, request target url - `props` **required**, [CALDAV prop element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.4) in [ElementCompact](../types/ElementCompact.md) form - `filters` [CALDAV filter element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.7) in [ElementCompact](../types/ElementCompact.md) form - `depth` [DAVDepth](../types/DAVDepth.md) - `timezone` iana timezone name, like `America/Los_Angeles` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../types/DAVResponse.md) ### Behavior send a calendar-query REPORT request, after server applies the filters and parse the response xml to an array of [DAVResponse](../types/DAVResponse.md). ---------------------------------------- ## File: caldav/createCalendarObject.md --- title: 'createCalendarObject' sidebar_position: 8 description: "Create a new iCalendar event or task in a CalDAV calendar collection via HTTP PUT." --- ## `createCalendarObject` create one calendar object on the target calendar ```ts const result = await createCalendarObject({ calendar: calendars[0], filename: 'test.ics', iCalString: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nBEGIN:VEVENT\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212T182145Z\nDTEND:20080213T182145Z\nDTSTAMP:20150421T182145Z\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809 Sixteenth President (1861-1865) http\://AmericanHistoryCalendar.com\nURL:http\://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincoln\nEND:VEVENT\nEND:VCALENDAR', headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `calendar` **required**, the [DAVCalendar](../types/DAVCalendar.md) which the client wish to create object on. - `filename` **required**, file name of the new calendar object, should end in `.ics`. It is one path segment inside the calendar: `?`, `#`, `\`, a `%` that does not start a percent-escape, and whitespace are percent-encoded, while existing percent-escapes are kept. Names containing `/`, or equal to `.` or `..`, are rejected. - `iCalString` **required**, calendar file data - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use PUT request to create a new calendar object ### See Also - [Importing iCal Feeds (Airbnb, Booking.com, etc.)](./import-ical-feed.md) ---------------------------------------- ## File: caldav/deleteCalendarObject.md --- title: 'deleteCalendarObject' sidebar_position: 10 description: "Delete a calendar object from a CalDAV collection by object URL and ETag." --- ## `deleteCalendarObject` delete one calendar object on the target calendar ```ts const result = await deleteCalendarObject({ calendarObject: { url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', etag: '"63758758580"', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `calendarObject` **required**, [DAVCalendarObject](../types/DAVCalendarObject.md) to delete - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use DELETE request to delete a new calendar object ---------------------------------------- ## File: caldav/fetchCalendarObjects.md --- title: 'fetchCalendarObjects' sidebar_position: 7 description: "Fetch calendar objects and events (VEVENT, VTODO) within a CalDAV collection." --- ## `fetchCalendarObjects` get all/specified calendarObjects of the passed in calendar ```ts const objects = await fetchCalendarObjects({ calendar: calendars[0], headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `calendar` **required**, [DAVCalendar](../types/DAVCalendar.md) to fetch calendar objects from - `objectUrls` calendar object urls to fetch - `filters` [CALDAV filter element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.7) in [ElementCompact](../types/ElementCompact.md) form - `timeRange` time range in iso format - `start` start time in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), format that's not in ISO 8601 will cause an error be thrown. - `end` end time in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), format that's not in ISO 8601 will cause an error be thrown. - `start` must be earlier than `end`. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function :::info some calendar providers may return their objects with different suffix than .ics such as `http://api.xx/97ec5f81-5ecc-4505-9621-08806f6796a3` or `http://api.xx/calobj1.abc` in this case, you need to pass in your own calendar object name filter so that you can have results you need. ::: - `urlFilter` **default: function which only keep .ics objects** predicate function to filter urls from the calendar objects before fetching - `expand` requires `timeRange`; requests without it reject. - `expand` whether to [expand](https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.5) the calendar objects, forcing the server to expand recurring components into individual calendar objects. :::info some calendar providers may not support calendarMultiGet, then it's necessary to use calendarQuery to fetch calendar object data. ::: - `useMultiGet` **default: true** whether to use [calendarMultiGet](./calendarMultiGet.md) as underlying function to fetch calendar objects, if set to false, it will use [calendarQuery](./calendarQuery.md) to fetch instead. ### Return Value array of [DAVCalendarObject](../types/DAVCalendarObject.md) ### Behavior a mix of [calendarMultiGet](calendarMultiGet.md) and [calendarQuery](calendarQuery.md), you can specify both filters and objectUrls here. The default query selects `VEVENT` resources. For a complete collection snapshot, pass `filters: { "comp-filter": { _attributes: { name: "VCALENDAR" } } }` and a `urlFilter` that accepts every object URL except the collection URL, including extensionless resources. Built-in calendar sync and account loading use this complete query. ---------------------------------------- ## File: caldav/fetchCalendarUserAddresses.md --- title: 'fetchCalendarUserAddresses' sidebar_position: 4 description: "Fetch CalDAV user calendar addresses (mailto URIs) from the user principal." --- ## `fetchCalendarUserAddresses` Fetch all calendar user addresses of the passed in CALDAV account ```ts const addresses = await fetchCalendarUserAddresses({ account: { principalUrl, serverUrl: 'https://caldav.icloud.com/', rootUrl: 'https://caldav.icloud.com/', accountType: 'caldav', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` **required**, account with `principalUrl` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value Array of user addresses Example of calendar user address: - `mailto:john.doe@example.com` - `/12345679/principal` - `/aMjIxa0AwMRbhws5kZV25Wnb3-ZH0vD9R89O32XQwIxJV2zMDAwMTcwMjIxMD28Aa/principal/` - `urn:uuid:12345679` ### Behavior send calendar-user-address-set PROPFIND request and extract user addresses set from xml response ---------------------------------------- ## File: caldav/fetchCalendars.md --- title: 'fetchCalendars' sidebar_position: 3 description: "Discover and fetch remote CalDAV calendar collections for a user principal." --- ## `fetchCalendars` get all calendars of the passed in CALDAV account ```ts const calendars = await fetchCalendars({ account, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` [DAVAccount](../types/DAVAccount.md) - `header` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation - `props` [CALDAV prop element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.4) in [ElementCompact](../types/ElementCompact.md) form, overriding default props to fetch - `projectedProps` custom props projection object, used as a map to map fetched custom props to values :::caution when overriding props, supported-calendar-component-set and resourcetype are required ::: ### Return Value array of [DAVCalendar](../types/DAVCalendar.md) of the account ### Behavior use `PROPFIND` to get all the basic info about calendars on certain account ---------------------------------------- ## File: caldav/freeBusyQuery.md --- title: 'freeBusyQuery' sidebar_position: 10 description: "Perform free-busy scheduling queries against CalDAV collections using RFC 4791." --- ## `freeBusyQuery` query free busy data on calendar :::caution Support depends on the server. Check its supported reports and provider documentation before use. ::: ```ts import { freeBusyQuery } from 'tsdav'; const result = await freeBusyQuery({ url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/', timeRange: { start: '2022-01-28T16:25:33.125Z', end: '2022-02-28T16:25:33.125Z', }, depth: '0', headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, collection url - `timeRange` **required** time range in iso format - `start` start time in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), format that's not in ISO 8601 will cause an error be thrown. - `end` end time in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), format that's not in ISO 8601 will cause an error be thrown. - `start` must be earlier than `end`. - `depth` [DAVDepth](../types/DAVDepth.md) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [DAVResponse](../types/DAVResponse.md) ### Behavior send free-busy-query REPORT request to caldav server. ---------------------------------------- ## File: caldav/import-ical-feed.md --- title: 'Importing iCal feeds' sidebar_position: 15 description: "Import and parse remote or local iCalendar (.ics) feeds into CalDAV objects." --- # Importing iCal feeds Fetch the subscription feed, parse it with [ical.js](https://github.com/kewisch/ical.js), and upload one resource per UID. Install `ical.js` alongside `tsdav`. The parser and serializer preserve date values, escaped text, recurrence rules, and recurrence exceptions. Include the feed's timezone components in each resource. ```ts import ICAL from 'ical.js'; import { createCalendarObject, DAVCalendar } from 'tsdav'; export async function importExternalFeed( feedUrl: string, targetCalendar: DAVCalendar, authHeader: string, ) { const response = await fetch(feedUrl); if (!response.ok) throw new Error(`Feed download failed: ${response.status}`); const source = new ICAL.Component(ICAL.parse(await response.text())); const byUid = new Map(); for (const event of source.getAllSubcomponents('vevent')) { const uid = event.getFirstPropertyValue('uid'); if (typeof uid !== 'string' || !uid) throw new Error('Feed event has no UID'); const group = byUid.get(uid) ?? []; group.push(event); byUid.set(uid, group); } let imported = 0; for (const [uid, events] of byUid) { const resource = new ICAL.Component(['vcalendar', [], []]); resource.addPropertyWithValue('version', '2.0'); resource.addPropertyWithValue('prodid', '-//tsdav//Feed importer//EN'); for (const timezone of source.getAllSubcomponents('vtimezone')) { resource.addSubcomponent(new ICAL.Component(JSON.parse(JSON.stringify(timezone.toJSON())))); } for (const event of events) { resource.addSubcomponent(new ICAL.Component(JSON.parse(JSON.stringify(event.toJSON())))); } const result = await createCalendarObject({ calendar: targetCalendar, filename: `feed-${encodeURIComponent(uid)}.ics`, iCalString: resource.toString(), headers: { authorization: authHeader }, }); if (!result.ok) { throw new Error(`Import failed for ${uid}: ${result.status}`); } imported += 1; } return imported; } ``` This is an import, not a feed synchronization policy. `createCalendarObject` uses `If-None-Match: *`; a duplicate resource returns 412 and the example throws. For repeated polling, keep a UID-to-resource mapping and fetched etags, use `updateCalendarObject` with the current etag for changes, and decide explicitly whether a removed feed event should be deleted. A later failure can leave earlier uploads completed, so persist successful imports individually in that workflow. ---------------------------------------- ## File: caldav/makeCalendar.md --- title: 'makeCalendar' sidebar_position: 5 description: "Create a new CalDAV calendar collection on the server using MKCALENDAR." --- ## `makeCalendar` create a new calendar on target account ```ts const result = await makeCalendar({ url: 'https://caldav.icloud.com/12345676/calendars/c623f6be-a2d4-4c60-932a-043e67025dde/', props: { displayname: 'personal calendar', [`${DAVNamespaceShort.CALDAV}:calendar-description`]: 'calendar description', }, headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, the target url - `props` **required**, [CALDAV prop element](https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.4) in [ElementCompact](../types/ElementCompact.md) form - `depth` [DAVDepth](../types/DAVDepth.md) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../types/DAVResponse.md) ### Behavior send a MKCALENDAR request and calendar data that creates a new calendar ---------------------------------------- ## File: caldav/syncCalendars.md --- title: 'syncCalendars' sidebar_position: 4 description: "Discover and synchronize CalDAV collections and changes with CTAG and sync-token." --- ## `syncCalendars` and `syncCalendarsDetailed` sync local version of calendars with remote. ```ts const { created, updated, deleted } = await syncCalendarsDetailed({ oldCalendars: [ { displayName: 'personal calendar', syncToken: 'HwoQEgwAAAAAAAAAAAAAAAAYARgAIhsI4pnF4erDm4CsARDdl6K9rqa9/pYBKAA=', ctag: '63758742166', url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/', objects: [ { etag: '"63758758580"', id: '0003ffbe-cb71-49f5-bc7b-9fafdd756784', data: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nBEGIN:VEVENT\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212\nDTEND:20080213\nDTSTAMP:20150421T141403\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809\nSixteenth President (1861-1865)\n\n\n\n \nhttp://AmericanHistoryCalendar.com\nURL:http://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincol\n n\nEND:VEVENT\nEND:VCALENDAR', url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', }, ], }, ], headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `oldCalendars` **required**, locally version of calendars of this account, should contain [calendar objects](../types/DAVCalendarObject.md) as well when using `syncCalendars` - `account` the account which calendars belong to, - `detailedResult` deprecated, use `syncCalendarsDetailed` for the detailed result instead. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function :::info Both functions sync objects in updated calendars. Include previously stored objects to retain unchanged objects during incremental WebDAV sync. Newly discovered calendars contain metadata only; fetch their objects separately. ::: ### Return Value `syncCalendars` returns: array of [DAVCalendar](../types/DAVCalendar.md). Updated calendars contain synced objects; unchanged calendars retain the supplied objects, and newly discovered calendars contain metadata only. `syncCalendarsDetailed` returns: an object of - `created` array of [DAVCalendar](../types/DAVCalendar.md) without calendar objects. - `updated` array of [DAVCalendar](../types/DAVCalendar.md) with synced calendar objects. - `deleted` array of the previously supplied [DAVCalendar](../types/DAVCalendar.md). ### Behavior fetch the latest list of [DAVCalendar](../types/DAVCalendar.md) from remote, compare the provided list and the latest list to find out `created`, `updated`, and `deleted` calendars. When using `syncCalendars`, fetch the latest list of [DAVCalendarObject](../types/DAVCalendarObject.md) from updated calendars using [rfc6578 webdav sync](https://datatracker.ietf.org/doc/html/rfc6578) and [calendarMultiGet](calendarMultiGet.md) return latest list of calendars with latest list of objects for `updated` calendars. When using `syncCalendarsDetailed`, return separate lists for `created`, `updated`, and `deleted`, with the object behavior described above. Discovery and object-fetch failures reject before returning changes. Built-in sync queries all calendar components, including tasks and journals, and preserves the token returned by the sync REPORT. ---------------------------------------- ## File: caldav/updateCalendarObject.md --- title: 'updateCalendarObject' sidebar_position: 9 description: "Update an existing calendar object in CalDAV with optimistic concurrency using ETag." --- ## `updateCalendarObject` create one calendar object ```ts const result = updateCalendarObject({ calendarObject: { url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', data: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nBEGIN:VEVENT\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212\nDTEND:20080213\nDTSTAMP:20150421T141403\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809\nSixteenth President (1861-1865)\n\n\n\n \nhttp://AmericanHistoryCalendar.com\nURL:http://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincol\n n\nEND:VEVENT\nEND:VCALENDAR', etag: '"63758758580"', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `calendarObject` **required**, [DAVCalendarObject](../types/DAVCalendarObject.md) to update - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use PUT request to update a new calendar object ---------------------------------------- ## File: carddav/addressBookMultiGet.md --- title: 'addressBookMultiGet' sidebar_position: 2 description: "Fetch multiple contact vCards in a single RFC 6352 addressbook-multiget REPORT." --- ## `addressBookMultiGet` addressBookMultiGet is used to retrieve specific address object resources from within a collection. If the Request-URI is an address object resource. This report is similar to the [addressBookQuery](addressBookQuery.md) except that it takes a list of vcard urls, instead of a filter, to determine which vcards to return. ```ts // fetch 2 specific vcards from one addressBook const vcards = await addressBookMultiGet({ url: 'https://contacts.icloud.com/1234567/carddavhome/card/', props: { [`${DAVNamespaceShort.DAV}:getetag`]: {}, [`${DAVNamespaceShort.CARDDAV}:address-data`]: {}, }, objectUrls: [ 'https://contacts.icloud.com/1234567/carddavhome/card/1.vcf', 'https://contacts.icloud.com/1234567/carddavhome/card/2.vcf', ], depth: '1', headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, url of CARDDAV server - `objectUrls` **required**, urls of vcards to get - `props` **required**, [CARDDAV prop element](https://datatracker.ietf.org/doc/html/rfc6352#section-10.4.2) in [ElementCompact](../types/ElementCompact.md) form - `filters` [CARDDAV filter element](https://datatracker.ietf.org/doc/html/rfc6352#section-10.5) in [ElementCompact](../types/ElementCompact.md) form, overriding default filters - `depth` **required**, [DAVDepth](../types/DAVDepth.md) of the request - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVVCard](../types/DAVVCard.md) ### Behavior send carddav:addressbook-multiget REPORT request and parse the response xml to extract an array of [DAVVCard](../types/DAVVCard.md) data. ---------------------------------------- ## File: carddav/addressBookQuery.md --- title: 'addressBookQuery' sidebar_position: 1 description: "Search and filter CardDAV contacts using RFC 6352 addressbook-query REPORT." --- ## `addressBookQuery` performs a search for all address object resources that match a specified filter The response of this report will contain all the WebDAV properties and address object resource data specified in the request. In the case of the `addressData` element, one can explicitly specify the vCard properties that should be returned in the address object resource data that matches the filter. ```ts const addressbooks = await addressBookQuery({ url: 'https://contacts.icloud.com/123456/carddavhome/card/', props: { [`${DAVNamespaceShort.DAV}:getetag`]: {}, }, depth: '1', headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, request target url - `props` **required**, [CARDDAV prop element](https://datatracker.ietf.org/doc/html/rfc6352#section-10.4.2) in [ElementCompact](../types/ElementCompact.md) form - `filters` [CARDDAV filter element](https://datatracker.ietf.org/doc/html/rfc6352#section-10.5) in [ElementCompact](../types/ElementCompact.md) form - `depth` [DAVDepth](../types/DAVDepth.md) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../types/DAVResponse.md) ### Behavior send a addressbook-query REPORT request, after server applies the filters and parse the response xml to an array of [DAVResponse](../types/DAVResponse.md). ---------------------------------------- ## File: carddav/createVCard.md --- title: 'createVCard' sidebar_position: 5 description: "Create a new contact vCard in a CardDAV collection via HTTP PUT." --- ## `createVCard` create one vcard on the target addressBook ```ts const result = await createVCard({ addressBook: addressBooks[0], filename: 'test.vcf' vCardString: 'BEGIN:VCARD\nVERSION:3.0\nN:;Test BBB;;;\nFN:Test BBB\nUID:0976cf06-a0e8-44bd-9217-327f6907242c\nPRODID:-//Apple Inc.//iCloud Web Address Book 2109B35//EN\nREV:2021-06-16T01:28:23Z\nEND:VCARD', headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `addressBook` **required**, [DAVAddressBook](../types/DAVAddressBook.md) - `filename` **required**, file name of the new vcard, should end in `.vcf`. It is one path segment inside the address book: `?`, `#`, `\`, a `%` that does not start a percent-escape, and whitespace are percent-encoded, while existing percent-escapes are kept. Names containing `/`, or equal to `.` or `..`, are rejected. - `vCardString` **required**, vcard file data - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use PUT request to create a new vcard ---------------------------------------- ## File: carddav/deleteVCard.md --- title: 'deleteVCard' sidebar_position: 7 description: "Delete a contact vCard from a CardDAV address book collection." --- ## `deleteVCard` delete one vcard on the target addressBook ```ts const result = await deleteCalendarObject({ vCard: { url: 'https://contacts.icloud.com/123456/carddavhome/card/test.vcf', etag: '"63758758580"', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `vCard` **required**, [DAVVCard](../types/DAVVCard.md) to delete - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use DELETE request to delete a new vcard ---------------------------------------- ## File: carddav/fetchAddressBooks.md --- title: 'fetchAddressBooks' sidebar_position: 3 description: "Discover remote CardDAV address book collections for a user principal." --- ## `fetchAddressBooks` get all addressBooks of the passed in CARDDAV account ```ts const addressBooks = await fetchAddressBooks({ account, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` [DAVAccount](../types/DAVAccount.md) - `props` [CARDDAV prop element](https://datatracker.ietf.org/doc/html/rfc6352#section-10.4.2) in [ElementCompact](../types/ElementCompact.md) form, overriding default props to fetch. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation :::caution when overriding props, resourcetype is required ::: ### Return Value array of [DAVAddressBook](../types/DAVAddressBook.md) ### Behavior use `PROPFIND` and to get all the basic info about addressBook on certain account ---------------------------------------- ## File: carddav/fetchVCards.md --- title: 'fetchVCards' sidebar_position: 4 description: "Fetch contact cards (vCards) from a CardDAV address book collection." --- ## `fetchVCards` get all/specified vcards of the passed in addressBook ```ts const vcards = await fetchVCards({ addressBook: addressBooks[0], headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `addressBook` **required**, [DAVAddressBook](../types/DAVAddressBook.md) to fetch vcards from - `objectUrls` vcard urls to fetch - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function :::info some providers may return their objects with different suffixes such as `http://api.xx/97ec5f81-5ecc-4505-9621-08806f6796a3` or `http://api.xx/calobj1.abc` in this case, you can pass in your own object name filter ::: - `urlFilter` **default: no filter**predicate function to filter urls from the address book before fetching :::info some providers may not support addressBookMultiGet, then it's necessary to use addressBookQuery to fetch vcards. ::: - `useMultiGet` **default: true** whether to use [addressBookMultiGet](./addressBookMultiGet.md) as underlying function to fetch vcards, if set to false, it will use [addressBookQuery](./addressBookQuery.md) instead ### Return Value array of [DAVVCard](../types/DAVVCard.md) ### Behavior a mix of [addressBookMultiGet](addressBookMultiGet.md) and [addressBookQuery](addressBookQuery.md), you can specify objectUrls here. ---------------------------------------- ## File: carddav/updateVCard.md --- title: 'updateVCard' sidebar_position: 6 description: "Update an existing contact vCard in CardDAV with ETag optimistic locking." --- ## `updateVCard` create one vcard ```ts const result = updateVCard({ vCard: { url: 'https://contacts.icloud.com/123456/carddavhome/card/test.vcf', data: 'BEGIN:VCARD\nVERSION:3.0\nN:;Test BBB;;;\nFN:Test BBB\nUID:0976cf06-a0e8-44bd-9217-327f6907242c\nPRODID:-//Apple Inc.//iCloud Web Address Book 2109B35//EN\nREV:2021-06-16T01:28:23Z\nEND:VCARD', etag: '"63758758580"', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `vCard` **required**, [DAVVCard](../types/DAVVCard.md) to update - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior use PUT request to update a new vcard ---------------------------------------- ## File: cloud-providers.md --- title: 'Cloud providers' sidebar_position: 7 description: "Setup guides and provider quirks for Apple iCloud, Google Calendar, Fastmail, Nextcloud, and Zoho." --- # Cloud providers ### Prepare credentials ##### Apple For apple you want to go to [this page](https://support.apple.com/en-us/HT204397) and after following the guide, you will have Apple ID and app-specific password. ##### Google For google you want to go to Google Cloud Platform/Credentials page, then create a credential that suit your use case. You want `clientId` ,`client secret` and after this. Also you need to enable Google CALDAV/CARDDAV for your project. Also you need to setup oauth screen, add needed oauth2 scopes (`https://www.googleapis.com/auth/calendar` for CalDAV), use proper oauth2 grant flow and you might need to get your application verified by google in order to be able to use CALDAV/CARDDAV api. Refer to [this page](https://developers.google.com/identity/protocols/oauth2) for more details. After the oauth2 offline grant you should be able to obtain oauth2 refresh token. ##### Fastmail Generate an app specific password just like apple, follow [this guide](https://www.fastmail.help/hc/en-us/articles/360058752834) for more information. ##### Nextcloud Nextcloud supports CalDAV/CardDAV with Basic auth (username + app password) and Bearer auth (OIDC tokens). - **Basic Auth:** Recommended for most use cases. Use your username and an app-specific password. - **Bearer Auth:** Supported if Nextcloud is configured with OIDC (e.g., via `user_oidc` app). Use `authMethod: 'Bearer'` and provide your access token in `credentials.accessToken`. For browser clients, you may need to use a server-side proxy or trusted domain configuration to avoid CORS issues. A common server URL is: `https:///remote.php/dav` :::info Other cloud providers not listed are not currently tested, in theory any cloud with basic auth and oauth2 should work, stay tuned for updates. ::: ##### ZOHO Please follow [official guide](https://help.zoho.com/portal/en/kb/calendar/syncing-other-calendars/articles/setting-up-caldav-sync-in-zoho-calendar#Configuring_CalDAV_sync_between_Zoho_Calendar_and_your_device) ##### Baikal For a Baikal installation that requires HTTP Digest, use `authMethod: 'Digest'` with your username and password. Set `serverUrl` to the canonical DAV endpoint, for example `https://calendar.example.com/baikal/dav.php/`. The same authentication works for CalDAV and CardDAV. See [Digest authentication](./helpers/authHelpers.md#digest-authentication) for Node.js 18 setup and browser redirect requirements. ##### Forward Email Please follow [offical guide](https://forwardemail.net/en/faq#do-you-support-calendars-caldav) ### Caveats Some cloud providers' APIs are not very standard, they might includes several quirks. ##### Google 1. CALDAV will always use UID inside `ics` file as filename, for example if you created a new event with name `1.ics` but have `abc` as its UID inside, you can only access it using `abc.ics`. 2. CARDDAV will always generate a new UID for your `.vcf` files. For example a file created with name `1.vcf` with UID `abc`, it will have a UID like `1bcde2e` inside the actual created data. 3. Both CALDAV and CARDDAV will add custom data entries inside your `ics` and `vcf` files like new `PRODID`, new `TIMETAMP` and some custom `X-` fields. 4. For CARDDAV, you cannot create a `vcf` file with the same name again even if it's already deleted. ##### ZOHO 1. CALDAV will always use UID inside `ics` file as filename, for example if you created a new event with name `1.ics` but have `abc` as its UID inside, you can only access it using `abc.ics`. 2. Both CALDAV and CARDDAV will add custom data entries inside your `ics` and `vcf` files like new `PRODID`, new `TIMETAMP` and some custom `X-` fields. 3. For CALDAV, you cannot create a `ics` file with the same UUID again even if it's already deleted. ##### NextCloud 1. Object deletion will result in original object be renamed rather than actual deletion. This can cause problems at times. ### Platform Specifics #### KaiOS (mozSystem) KaiOS allows making cross-origin requests from privileged apps using `XMLHttpRequest` with `mozSystem: true`. You can use `tsdav` in KaiOS by providing a custom `fetch` implementation that wraps `XMLHttpRequest`. ```typescript const kaiOSFetch = (url, init) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest({ mozSystem: true }); xhr.open(init.method || 'GET', url); if (init.headers) { Object.entries(init.headers).forEach(([key, value]) => { xhr.setRequestHeader(key, value); }); } xhr.onload = () => { resolve({ ok: xhr.status >= 200 && xhr.status < 300, status: xhr.status, statusText: xhr.statusText, text: () => Promise.resolve(xhr.responseText), headers: { get: (name) => xhr.getResponseHeader(name), }, }); }; xhr.onerror = () => reject(new Error('Network error')); xhr.send(init.body); }); }; const client = new DAVClient({ serverUrl: '...', credentials: { ... }, fetch: kaiOSFetch as any, }); ``` ---------------------------------------- ## File: contributing.md --- title: 'Contributing' sidebar_position: 8 description: "Contribution guide, local setup, build commands, and testing workflow for the tsdav library and documentation." --- # Contributing Use Node.js 22.12+ for development and tests (Vitest 5 requires it). The published library continues to support Node.js 18+. First you need to clone the repo and install dependencies with `pnpm install`. The repo pins pnpm 10.34.4 for local, CI, and Vercel builds. Vercel installs the docs package with `pnpm install --frozen-lockfile`. ### Build Building release artifacts uses Rolldown and requires Node.js 20.19+ or 22.12+. ```bash pnpm build ``` #### Test Run `pnpm test:unit` for the unit suite; it does not require provider credentials. For live provider integration tests, configure a `.env` file from `.env.example`: ```bash cp .env.example .env ``` and fill in the missing values If you didn't add any new api, you should set MOCK_FETCH="true". If you added new api and need to test against cloud providers, you should set MOCK_FETCH="false" and RECORD_NETWORK_REQUESTS="true" to updated network request mock data. ### Docs dependency verification Run `pnpm --dir docs test` to verify the image parser used by Docusaurus. These tests run malformed images in child processes with timeouts, and also check valid image dimensions through CommonJS, ESM, buffer, and file APIs. The docs package overrides `image-size` with `image-size-next@2.1.1`, a compatible fork that fixes CVE-2025-71329 and CVE-2025-71330. Keep the override until Docusaurus depends on a fixed parser; removing it would reintroduce the vulnerable upstream package. See the [fork's changelog](https://github.com/lcf2212dev/image-size-next/blob/main/CHANGELOG.md). ### WEBDAV quick guide WEBDAV uses xml for all its data when communicating, the basic element is `object`, multiple `object`s can form a `collection`, webdav server have `account`s and an `account` have a `principal` resource (i.e the default, main resource) and under that principal resource we have `home set` of the said resource where your actual resources are. `syncToken` and `ctag` are basically like hash of the object/collection, if anything in it changes, this token will change. For caldav, the calendar data in caldav are in `rfc5545` ical format, there's `iCal2Js` and `js2iCal` function with my other project [pretty-jcal](https://github.com/natelindev/pretty-jcal) to help your convert them from/to js objects. Here's cheat sheet on webdav operations compared with rest:
Operation Webdav REST
Create collection
  • predefined id: `MKCOL /entities/$predefined_id`
  • no predefined id: not possible
  • can set attributes right away with extended-mkcol extension
  • Status: 201 Created
  • `POST /entities` with JSON body with attributes, response contains new id
  • Status: 200 with new id and optionally the whole object
Create entity
  • predefined id: `PUT /entities/$predefined_id` with body, empty response
  • no predefined id: `POST /entities`, receive id as part of Content-Location header
  • can't set attributes right away, need subsequent PROPPATCH
  • Status: 201 Created
Update entity body
  • `PUT /entities/$predefined_id` with new body (no attributes)
  • Status: 204 No Content
  • Full: `PUT /entities/$id` with full JSON body with attributes
  • Status 200, receive full object back
  • Partial: `PATCH /entities/$id` with partial JSON containing only attributes to update.
  • Status 200, full/partial object returned
Update entity attributes
  • `PROPPATCH /entities/$id` with XML body of attributes to change
  • Status: 207, XML body with accepted attributes
Delete entity
  • `DELETE /entities/$id`
  • Sattus: 204 no content
List entities
  • `PROPFIND /entities` with XML body of attributes to fetch
  • Status 207 multi-status XML response with multiple entities and their respective attributes
  • `GET /entities`
  • Status: 200 OK, receive JSON response array with JSON body of entity attributes
Get entity
  • `GET /entities/$id`
  • Status: 200 OK with entitiy body
  • `GET /entities/$id`
  • Status 200 OK, receive JSON body of entity attributes
Get entity attributes
  • `PROPFIND /entities/$id` with XML body of attributes to fetch
  • Status 207 multi-status XML response with entity attributes
Notes
  • cannot always set attributes right away at creation time, need subsequent `PROPPATCH`
  • no concept of body vs attributes
  • entity can be either collection or model (for collection `/entities/$collectionId/$itemId`)
---------------------------------------- ## File: helper.mdx --- title: Interactive DAV Converter description: Inspect normalized DAV responses or serialize compact XML request objects. --- import { Converter } from '../../components/Converter'; Inspect normalized DAV responses or serialize compact XML request objects. The normalized response keys are for reading responses; request keys use the exact XML names and namespace prefixes. All object inputs use JSON. ---------------------------------------- ## File: helpers/authHelpers.md --- title: 'AuthHelpers' description: "Authentication utility functions for Basic, Bearer, and Digest credentials." --- # AuthHelpers ### getBasicAuthHeaders convert the `username:password` into base64 auth header string: ```ts const result = getBasicAuthHeaders({ username: 'test', password: '12345', }); ``` #### Return Value ```ts { authorization: 'Basic dGVzdDoxMjM0NQ=='; } ``` ### fetchOauthTokens fetch oauth token using code obtained from oauth2 authorization code grant ```ts const tokens = await fetchOauthTokens({ authorizationCode: '123', clientId: 'clientId', clientSecret: 'clientSecret', tokenUrl: 'https://oauth.example.com/tokens', redirectUrl: 'https://yourdomain.com/oauth-callback', }); ``` #### Return Value ```ts { access_token: 'kTKGQ2TBEqn03KJMM9AqIA'; refresh_token: 'iHwWwqytfW3AfOjNbM1HLg'; expires_in: 12800; id_token: 'TKfsafGQ2JMM9AqIA'; token_type: 'bearer'; scope: 'openid email'; } ``` ### refreshAccessToken using refresh token to fetch access token from given token endpoint ```ts const result = await refreshAccessToken({ clientId: 'clientId', clientSecret: 'clientSecret', tokenUrl: 'https://oauth.example.com/tokens', refreshToken: 'iHwWwqytfW3AfOjNbM1HLg', }); ``` #### Return Value ```ts { access_token: 'eeMCxYgdCF3xfLxgd1NE8A'; expires_in: 12800; } ``` ### getOauthHeaders the combination of `fetchOauthTokens` and `refreshAccessToken`, it will return the authorization header needed for authorizing the requests as well as automatically renewing the access token using refresh token obtained from server when it expires. ```ts const result = await getOauthHeaders({ authorizationCode: '123', clientId: 'clientId', clientSecret: 'clientSecret', tokenUrl: 'https://oauth.example.com/tokens', redirectUrl: 'https://yourdomain.com/oauth-callback', }); ``` #### Return Value ```ts { tokens: { access_token: 'kTKGQ2TBEqn03KJMM9AqIA'; refresh_token: 'iHwWwqytfW3AfOjNbM1HLg'; expires_in: 12800; id_token: 'TKfsafGQ2JMM9AqIA'; token_type: 'bearer'; scope: 'openid email'; }, headers: { authorization: `Bearer q-2OCH2g3RctZOJOG9T2Q`, }, } ``` Both client APIs check OAuth expiry before each request and share one refresh for concurrent requests. The credentials object is updated with the access token, a rotated refresh token when supplied, and an expiration in milliseconds when `expires_in` is supplied. Persist those updated fields with your credential storage. A refresh failure rejects the client request before sending DAV traffic. A valid access token can be reused without a refresh token; an unknown expiry with an available refresh token triggers refresh when authentication is first resolved. ### defaultParam :::caution Internal function, not intended to be used outside. ::: Provide default parameter for passed in function and allows default parameters be overridden when the function was actually passed with same parameters. would only work on functions that have only one object style parameter. ```ts const fn1 = (params: { a?: number; b?: number }) => { const { a = 0, b = 0 } = params; return a + b; }; const fn2 = defaultParam(fn1, { b: 10 }); ``` ### Digest authentication Pass `username` and `password` with `authMethod: 'Digest'`. Both `createDAVClient` and `DAVClient` answer the server's `WWW-Authenticate` challenge and compute authorization for each request, including discovery, object writes, and per-call `fetch` overrides. ```ts import { createDAVClient } from 'tsdav'; const client = await createDAVClient({ serverUrl: 'https://baikal.example.com/dav.php/', credentials: { username: 'user', password: 'password' }, authMethod: 'Digest', defaultAccountType: 'caldav', }); ``` Supported algorithms are MD5, MD5-sess, SHA-256, and SHA-256-sess with `qop=auth`. MD5 and SHA-256 also support the older form without `qop`. SHA-256 is preferred when the runtime can compute it. Nonce counts increase per session, session keys persist for `-sess` algorithms, and an expired nonce gets one retry. A 401 on that retry is returned to the caller. Challenges without `domain` cover the origin; a supplied `domain` limits preemptive authorization to those URL prefixes. Separate realms retain separate sessions. The default `authMethod: 'Basic'` switches to Digest when a 401 from the same origin offers Digest without Basic and WebCrypto is available. Without WebCrypto it retains the original 401 behavior. A client using Digest does not fall back to Basic. #### Runtime requirements Digest requires `globalThis.crypto.getRandomValues`; SHA-256 also needs `crypto.subtle`. These are available by default in Node.js >=19, Bun, Deno, Workers, and secure browser contexts. On a browser's insecure HTTP origin, MD5 can work with `getRandomValues`, but SHA-256 requires a secure context. Explicit Digest authentication throws `DigestUnsupportedError` if the required crypto API is unavailable. Node.js 18 remains supported. To enable Digest there, supply its built-in WebCrypto before making client requests: ```ts import { webcrypto } from 'node:crypto'; if (!globalThis.crypto) { Object.defineProperty(globalThis, 'crypto', { value: webcrypto, configurable: true }); } ``` #### Redirects and browser use Use canonical server and collection URLs, including any required trailing slash. When fetch exposes redirect statuses and `Location`, tsdav signs each same-origin hop, preserves fetch's method/body conversion rules, and strips authorization when a chain leaves the original origin. It does not answer challenges on that foreign origin. `redirect: 'manual'` and `'error'` remain under the caller's control. Responses from redirects followed manually can report `redirected: false`; compare `response.url` with the requested URL instead. Browsers return `opaqueredirect` for manual redirects and hide both their target and status. For GET/HEAD, tsdav follows an unsigned request to resolve the final URL and answers a same-origin challenge there. Other methods, including PROPFIND, REPORT, PUT, and DELETE, throw a descriptive `TypeError` instead of replaying a request whose redirect semantics are unknown. Use the final DAV URLs or a custom transport that exposes redirects for those methods. An authenticated redirect that cannot be resolved without credentials also needs the final URL. Cross-origin servers must allow CORS and expose `WWW-Authenticate` to the browser. While handling Digest, tsdav defaults fetch's `credentials` to `'omit'` to prevent the browser's built-in HTTP authentication dialog from intercepting the challenge. Explicit `fetchOptions.credentials` values are preserved; set `'include'` or `'same-origin'` if your server also requires cookies. A browser client using Basic with automatic Digest negotiation can set `fetchOptions: { credentials: 'omit' }` for the initial challenge too. #### Overrides and limitations A supplied `credentials.digestString` is still sent as-is with `authMethod: 'Digest'`, even when username/password are present. This legacy option leaves challenge handling to you. Explicit `Authorization` headers in request headers or `fetchOptions.headers` also bypass automatic negotiation, and `headersToExclude: ['Authorization']` disables it. `qop=auth-int`, SHA-512-256, username hashing (`userhash`), and `-sess` without `qop` are not implemented. Credentials are hashed as UTF-8. `Authentication-Info`/`nextnonce` is not processed; nonce rotation is handled by the next 401. Stream request bodies are not retried. Use HTTPS to protect authentication and calendar/contact data in transit. ### Custom authentication Pass an `authFunction` to return your request headers. It receives `DAVCredentials`, including any additional values you supply in `customData`. ### getBearerAuthHeaders Generate Bearer authorization headers from an access token (useful for OIDC providers such as Nextcloud when supported). ```ts const result = getBearerAuthHeaders({ accessToken: 'YOUR_OIDC_ACCESS_TOKEN', }); ``` #### Return Value ```ts { authorization: 'Bearer YOUR_OIDC_ACCESS_TOKEN', } ``` ---------------------------------------- ## File: helpers/constants.md --- title: 'Constants' description: "DAV namespaces, XML tags, HTTP headers, and common CalDAV/CardDAV constants." --- # Constants ### DAVNamespace xml namespace enum for convenience | Name | Value | Description | | --------------- | ------------------------------ | ---------------------------- | | CALENDAR_SERVER | http://calendarserver.org/ns/ | calendarserver.org namespace | | CALDAV_APPLE | http://apple.com/ns/ical/ | Apple CALDAV namespace | | CALDAV | urn:ietf:params:xml:ns:caldav | CALDAV namespace | | CARDDAV | urn:ietf:params:xml:ns:carddav | CARDDAV namespace | | DAV | DAV: | WEBDAV namespace | ### DAVNamespaceShort shortened xml namespace enum | Name | Value | | --------------- | ----- | | CALENDAR_SERVER | cs | | CALDAV_APPLE | ca | | CALDAV | c | | CARDDAV | card | | DAV | d | ### DAVAttributeMap map WEBDAV namespace to attributes to allow better readability when dealing with raw xml data | Name | Value | | --------------- | ---------- | | CALDAV | xmlns:c | | CARDDAV | xmlns:card | | CALENDAR_SERVER | xmlns:cs | | CALDAV_APPLE | xmlns:ca | | DAV | xmlns:d | ---------------------------------------- ## File: helpers/requestHelpers.md --- title: 'RequestHelpers' description: "XML payload builders, prop filters, and header helpers for WebDAV requests." --- # RequestHelpers :::caution All functions below are intended to only be used internally and may subject to change without notice. ::: ### urlEquals Check if two urls are mostly equal Will first trim out white spaces, and can omit the last `/` For example, `https://www.example.com/` and ` https://www.example.com` are considered equal ### urlContains Basically `urlEqual`, but without length constraint. For example, `https://www.example.com/` and ` www.example.com` are considered contains. ### getDAVAttribute Convert `DAVNamespace` to intended format to be consumed by `xml-js` to be used as `xml` attributes. ### cleanupFalsy Clean up `falsy` values within an object, this is useful when sending headers, Where undefined object property will cause an error. ### excludeHeaders Remove header value by its key value useful when sending headers ---------------------------------------- ## File: index.mdx --- title: tsdav Documentation description: Complete tsdav documentation — Your universal WebDAV, CalDAV, and CardDAV client library for TypeScript and JavaScript. template: splash hero: tagline: Lightweight, RFC-compliant TypeScript client with zero Node-only dependencies. Runs seamlessly in Node.js, Browsers, Cloudflare Workers, Bun, and Deno. image: file: ../../assets/logo.svg actions: - text: Quick Start link: /intro/ icon: right-arrow variant: primary - text: CalDAV Guide link: /caldav/fetchcalendars/ icon: open-book variant: secondary - text: View on GitHub link: https://github.com/natelindev/tsdav icon: external variant: minimal --- import { Card, CardGrid, Tabs, TabItem, Code } from '@astrojs/starlight/components'; export const exampleSnippet = `import { createDAVClient } from 'tsdav'; const client = await createDAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { username: 'YOUR_APPLE_ID', password: 'YOUR_APP_SPECIFIC_PASSWORD', }, authMethod: 'Basic', defaultAccountType: 'caldav', }); // Discover all remote calendars const calendars = await client.fetchCalendars(); console.log(\`Found \${calendars.length} calendars:\`, calendars.map((c) => c.displayName)); // Fetch all calendar events const events = await client.fetchCalendarObjects({ calendar: calendars[0], }); console.log(\`Fetched \${events.length} calendar events.\`);`;
✨ tsdav v2.5.0 is live • Zero Node-only globals • Universal Portable Client
## Quick Start Guides Get started quickly with comprehensive guides and API references: Learn how to install, configure authentication (Basic, OAuth), and initialize your first DAV client in less than 60 seconds.
[**Get Started →**](/intro/)
Discover calendar collections, query events by time range, handle recurring events, and manage attendees.
[**Explore CalDAV →**](/caldav/fetchcalendars/)
Connect to remote address books, retrieve and parse vCards, and perform atomic contact updates.
[**Explore CardDAV →**](/carddav/fetchaddressbooks/)
Production-ready architectural guide for bi-directional remote-to-local sync with CTAGs and delta tokens.
[**Read Sync Guide →**](/smart-calendar-sync/)
Setup instructions and quirk workarounds for Apple iCloud, Google Calendar, Fastmail, Nextcloud, and Zoho.
[**View Providers →**](/cloud-providers/)
Real-time playground to convert and inspect WebDAV XML request bodies and normalized response structures.
[**Open Playground →**](/helper/)
--- ## Quick Installation Install `tsdav` using your preferred package manager: --- ## 30-Second Example Connecting to Apple iCloud CalDAV: --- ## Key Features Designed with zero Node-only globals (`fs`, `net`, `stream`). Works out of the box in browsers, Node.js, Cloudflare Workers, Electron, Bun, and Deno. Native support for RFC 6578 sync-collection reports and WebDAV `getctag` headers for instant, low-bandwidth delta updates. Pre-configured normalization routines for Google's UID renames, Apple's custom namespaces, and Nextcloud's deletion quirks. Provides structured [`/llms.txt`](/llms/) and plain-text feeds optimized for ingestion by Cursor, Claude, and custom LLM workflows. ---------------------------------------- ## File: intro.md --- title: 'Intro' sidebar_position: 1 description: "Getting started with tsdav: universal WebDAV, CalDAV, and CardDAV client library for TypeScript and JavaScript." --- # Intro [WEBDAV](https://tools.ietf.org/html/rfc4918), `Web Distributed Authoring and Versioning`, is an extension of the HTTP to allow handling distributed authoring, versioning of various resources. It's very common to be used for cloud storage(limited support), as well as calendar, contacts information syncing. ### Cloud provider support status | Provider name | CalDAV | CardDAV | | ------------- | ------ | ------- | | Apple | ✅ | ✅ | | Google | ✅ | ✅ | | Fastmail | ✅ | ✅ | | Nextcloud | ✅ | ✅ | | Baikal | ✅ | ✅ | | ZOHO | ✅ | ✅ | | DAViCal | ✅ | ⛔️ | | Forward Email | ✅ | ✅ | These entries describe calendar and contact interoperability. Support for individual WebDAV methods varies by server; they do not establish general file-storage or collection-management support. Google, for example, excludes `MKCOL`, `MKCALENDAR`, `COPY`, `MOVE`, `LOCK`, and `UNLOCK` from its [CalDAV implementation](https://developers.google.com/workspace/calendar/caldav/v2/guide). For more information on cloud providers, go to [cloud providers](./cloud%20providers.md) for more information. ### Install ```bash yarn add tsdav ``` or ```bash npm install tsdav ``` ### Browser usage Use the ESM bundle in modern browsers: ```html ``` Browser requests to CalDAV/CardDAV endpoints are often blocked by CORS. Prefer running tsdav in a server environment, proxying requests through your backend, or using a [custom transport](#custom-transport-electroncors). For servers that require HTTP Digest, pass `authMethod: 'Digest'` and username/password. Browser DAV requests need canonical URLs to avoid opaque redirects; see [Digest authentication](./helpers/authHelpers.md#digest-authentication) for configuration and runtime requirements. ### Bun `tsdav` runs under [Bun](https://bun.sh) without any additional configuration. Install and import as you would under Node.js: ```bash bun add tsdav ``` ```ts import { createDAVClient } from 'tsdav'; ``` Bun's built-in `fetch` is used automatically. ### Deno `tsdav` is consumable under [Deno](https://deno.com) via the `npm:` specifier in Deno 2.x and later: ```ts import { createDAVClient } from 'npm:tsdav'; const client = await createDAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { username: Deno.env.get('APPLE_ID')!, password: Deno.env.get('APPLE_APP_PASSWORD')!, }, authMethod: 'Basic', defaultAccountType: 'caldav', }); ``` Run with at minimum `--allow-net` (for CalDAV/CardDAV servers) and `--allow-env` (if you read credentials from the environment). Deno's native `fetch` is used automatically. ### Cloudflare Workers `tsdav` is compatible with Cloudflare Workers. It automatically detects and uses the native `fetch` provided by the Workers runtime. Example (using standard Worker syntax): ```ts import { createDAVClient } from 'tsdav'; export default { async fetch(request, env) { const client = await createDAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { username: env.APPLE_ID, password: env.APPLE_PASSWORD, }, authMethod: 'Basic', defaultAccountType: 'caldav', }); const calendars = await client.fetchCalendars(); return new Response(JSON.stringify(calendars), { headers: { 'Content-Type': 'application/json' }, }); }, }; ``` ### Custom Transport (Electron/CORS) If you are using `tsdav` in an environment like an Electron renderer process where `fetch` is restricted by CORS (especially for providers like iCloud or Gmail), you can provide a custom `fetch` implementation to route requests through a proxy or Electron's main process. ```ts const client = await createDAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { ... }, authMethod: 'Basic', // Custom fetch override fetch: async (url, options) => { // Implement your own transport here, e.g., IPC to main process const response = await window.electronAPI.makeRequest(url, options); return response; }, }); ``` The custom `fetch` should follow the standard Fetch API interface. This is also supported in the `DAVClient` constructor and most high-level functions. ### Basic usage #### Import the dependency ```ts import { createDAVClient } from 'tsdav'; ``` or ```ts import tsdav from 'tsdav'; ``` #### Create Client By creating a client, you can now use all tsdav methods without supplying authentication header or accounts. However, you can always pass in custom header or account to override the default for each request. For Google ```ts const client = await createDAVClient({ serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/', credentials: { tokenUrl: 'https://accounts.google.com/o/oauth2/token', username: 'YOUR_EMAIL_ADDRESS', refreshToken: 'YOUR_REFRESH_TOKEN_WITH_CALDAV_PERMISSION', clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', }, authMethod: 'Oauth', defaultAccountType: 'caldav', }); ``` or ```ts const client = await createDAVClient({ serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/', credentials: { authorizationCode: 'AUTH_CODE_OBTAINED_FROM_OAUTH_CALLBACK', tokenUrl: 'https://accounts.google.com/o/oauth2/token', clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', }, authMethod: 'Oauth', defaultAccountType: 'caldav', }); ``` For Apple ```ts const client = await createDAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { username: 'YOUR_APPLE_ID', password: 'YOUR_APP_SPECIFIC_PASSWORD', }, authMethod: 'Basic', defaultAccountType: 'caldav', }); ``` Need help generating app-specific passwords? See the [Apple app-specific password guide](https://support.apple.com/en-us/HT204397). After `v1.1.0`, you have a new way of creating clients. :::info You need to call `client.login()` with this method before using the functions ::: For Google ```ts const client = new DAVClient({ serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/', credentials: { tokenUrl: 'https://accounts.google.com/o/oauth2/token', username: 'YOUR_EMAIL_ADDRESS', refreshToken: 'YOUR_REFRESH_TOKEN_WITH_CALDAV_PERMISSION', clientId: 'YOUR_CLIENT_ID', clientSecret: 'YOUR_CLIENT_SECRET', }, authMethod: 'Oauth', defaultAccountType: 'caldav', }); ``` For Apple ```ts const client = new DAVClient({ serverUrl: 'https://caldav.icloud.com', credentials: { username: 'YOUR_APPLE_ID', password: 'YOUR_APP_SPECIFIC_PASSWORD', }, authMethod: 'Basic', defaultAccountType: 'caldav', }); ``` #### Get calendars If you are using the class-based `DAVClient`, call `await client.login()` once before fetching calendars or other resources. ```ts const calendars = await client.fetchCalendars(); ``` #### Get calendar objects on calendars ```ts const calendarObjects = await client.fetchCalendarObjects({ calendar: myCalendar, }); ``` #### Get specific calendar objects on calendar using urls ```ts const calendarObjects = await client.fetchCalendarObjects({ calendar: myCalendar, objectUrls: urlArray, }); ``` ##### Get calendars changes from remote ```ts const { created, updated, deleted } = await client.syncCalendarsDetailed({ oldCalendars: myCalendars, }); ``` #### Get calendar object changes on a calendar from remote ```ts const { created, updated, deleted } = ( await client.smartCollectionSyncDetailed({ collection: { url: localCalendar.url, ctag: localCalendar.ctag, syncToken: localCalendar.syncToken, objects: localCalendarObjects, objectMultiGet: client.calendarMultiGet, }, method: 'webdav', }) ).objects; ``` ---------------------------------------- ## File: llms.md --- sidebar_position: 100 title: LLM Integration description: AI-ready routes for LLMs to understand and interact with tsdav documentation --- # LLM Integration This documentation site provides special routes designed for Large Language Models (LLMs) to better understand and interact with the tsdav documentation. Whether you're using ChatGPT, Claude, Cursor, Copilot, or any other AI assistant, these routes make it easy to feed tsdav docs directly into your workflow. ## Available Routes ### Full Documentation #### [`/llms-full.txt`](https://tsdav.vercel.app/llms-full.txt) A plain-text representation of the **entire** documentation, optimized for LLM consumption. This file concatenates all documentation content into a single, easily parseable text file that includes: - Page titles and URLs - Page descriptions - Full content of each documentation page Use this when you want an AI assistant to have complete knowledge of the tsdav library. ### Documentation Index #### [`/llms.txt`](https://tsdav.vercel.app/llms.txt) A lightweight index of all documentation pages with titles, descriptions, and links. Use this to give an LLM an overview of available documentation and let it decide which pages to read in detail. ### Individual Pages as Markdown #### `/docs/[path].md` You can access the raw Markdown source of any individual documentation page by appending `.md` to the docs URL. For example: - [`/docs/intro.md`](https://tsdav.vercel.app/docs/intro.md) — Getting started guide - [`/docs/caldav/fetchCalendars.md`](https://tsdav.vercel.app/docs/caldav/fetchCalendars.md) — CalDAV calendar fetching - [`/docs/carddav/fetchVCards.md`](https://tsdav.vercel.app/docs/carddav/fetchVCards.md) — CardDAV vCard fetching This is useful when you want an AI assistant to focus on a specific topic without loading the entire documentation. ## How to Use with AI Assistants ### ChatGPT / Claude / Other Chat UIs Paste the full documentation URL into your prompt: ``` Please read https://tsdav.vercel.app/llms-full.txt and help me set up CalDAV sync with Google Calendar. ``` ### Cursor / Copilot / AI-Enabled Editors Add the documentation URL as context in your AI-enabled editor: - **Cursor**: Use `@docs` or paste the URL in chat to provide tsdav context - **Copilot**: Reference the docs URL when asking questions about tsdav ### Custom LLM Applications If you're building an application that uses LLMs, you can programmatically fetch the documentation: ```typescript // Fetch full documentation for RAG or context injection const response = await fetch('https://tsdav.vercel.app/llms-full.txt'); const docs = await response.text(); // Or fetch a specific page const calendarDocs = await fetch('https://tsdav.vercel.app/docs/caldav/fetchCalendars.md'); const calendarContent = await calendarDocs.text(); ``` ## What's Included The LLM-friendly output covers all tsdav documentation: | Section | Description | |---------|-------------| | **Introduction** | Library overview, installation, and quick start | | **WebDAV** | Core WebDAV operations — `davRequest`, `propfind`, account discovery | | **CalDAV** | Calendar operations — fetch, create, update, delete calendar objects | | **CardDAV** | Contact operations — fetch, create, update, delete vCards | | **Types** | TypeScript type definitions and models | | **Helpers** | Authentication helpers, request utilities, and constants | | **Smart Calendar Sync** | End-to-end sync workflow with database schema guidance | | **Cloud Providers** | Provider-specific setup for Apple, Google, Fastmail, and more | ## Why LLM-Friendly Docs? - **Faster onboarding**: AI assistants can instantly learn the full API surface - **Better code generation**: With complete type information and examples, AI produces more accurate code - **Reduced hallucination**: Grounding AI responses in actual documentation prevents incorrect API usage - **Always up-to-date**: The routes serve the latest built documentation automatically ---------------------------------------- ## File: migration.mdx --- title: Migration Guide (v1 to v2) description: Transitioning from tsdav 1.1.x to 2.x --- import { Converter } from '../../components/Converter'; ### Upgrading from 1.1.x to 2.x `tsdav` v2 unified the CalDAV and CardDAV clients and modernized the request and response shapes. #### Interactive Prop & Filter Migration Tool Use the interactive converter below to automatically transition your v1 props and filter definitions to modern compact formats: ---------------------------------------- ## File: smart-calendar-sync.md --- title: 'Smart calendar sync' sidebar_position: 8 description: "Production architecture and best practices for bi-directional calendar sync with CTAGs and RFC 6578 sync tokens." --- # Smart calendar sync Store each calendar's URL, ctag, sync token, supported reports, and object URLs, etags, and original calendar data. This example uses Basic authentication with a username and application password. Keep credentials in your application's protected credential storage. An incremental sync needs the previous object list to distinguish created and updated objects and retain unchanged objects. The following adapter keeps database operations explicit. Implement `CalendarStore` with your existing database; its transaction must commit all object changes and their tokens together. ```ts import { DAVClient, DAVCalendar, DAVObject, DAVCredentials } from 'tsdav'; interface CalendarTransaction { putCalendar(calendar: DAVCalendar): Promise; removeCalendar(url: string): Promise; putObject(calendarUrl: string, object: DAVObject): Promise; removeObject(calendarUrl: string, objectUrl: string): Promise; } interface CalendarStore { readCalendars(): Promise; transaction(work: (tx: CalendarTransaction) => Promise): Promise; } export async function synchronizeCalendars( serverUrl: string, credentials: DAVCredentials, store: CalendarStore, ) { const client = new DAVClient({ serverUrl, credentials, defaultAccountType: 'caldav', }); await client.login(); const local = await store.readCalendars(); const remote = await client.fetchCalendars(); const byUrl = new Map(local.map((calendar) => [calendar.url, calendar])); const remoteUrls = new Set(remote.map((calendar) => calendar.url)); const results = await Promise.all(remote.map(async (calendar) => { const previous = byUrl.get(calendar.url); return client.smartCollectionSyncDetailed({ collection: { ...calendar, ctag: previous?.ctag, syncToken: previous?.syncToken, objects: previous?.objects ?? [], objectMultiGet: client.calendarMultiGet, fetchObjects: async (params: Parameters>[0]) => { if (!params) throw new Error('A collection is required'); const { collection, ...options } = params; return client.fetchCalendarObjects({ ...options, calendar: collection, filters: { 'comp-filter': { _attributes: { name: 'VCALENDAR' } } }, urlFilter: (url) => new URL(url, collection.url).href !== collection.url, }); }, }, }); })); await store.transaction(async (tx) => { for (const calendar of local) { if (!remoteUrls.has(calendar.url)) await tx.removeCalendar(calendar.url); } for (const result of results) { const { objects, objectMultiGet, fetchObjects, ...metadata } = result; for (const object of [...objects.created, ...objects.updated]) { await tx.putObject(result.url, { ...object, url: new URL(object.url, result.url).href }); } for (const object of objects.deleted) { await tx.removeObject(result.url, new URL(object.url, result.url).href); } await tx.putCalendar(metadata); } }); } ``` Method selection uses the discovered reports: WebDAV sync retrieves changed objects through `objectMultiGet`; basic sync uses ctag and a complete collection query through `fetchObjects`. The client's multiget methods are bound and can be used as callbacks. Store the returned token, which represents the successfully fetched changes. If discovery, sync, fetching, or the database transaction fails, retain the previous tokens and retry from that state. For local changes, serialize a complete iCalendar resource and call `createCalendarObject`, `updateCalendarObject`, or `deleteCalendarObject`. These helpers return a Fetch `Response`; check `response.ok` before updating local state. A 412 response indicates a precondition conflict and requires fetching the current remote object before deciding how to reconcile it. Preserve recurrence, all-day dates, timezones, and exceptions when editing the data; see [feed import](./caldav/import-ical-feed.md). ---------------------------------------- ## File: types/DAVAccount.md --- title: 'DAVAccount' description: "TypeScript type definition for DAVAccount representing a discovered WebDAV account." --- ```ts export type DAVAccount = { accountType: 'caldav' | 'carddav'; serverUrl: string; credentials?: DAVCredentials; rootUrl?: string; principalUrl?: string; homeUrl?: string; calendars?: DAVCalendar[]; addressBooks?: DAVAddressBook[]; }; ``` - `accountType` can be `caldav` or `carddav` - `serverUrl` server url of the account - `credentials` [DAVCredentials](DAVCredentials.md) - `rootUrl` root url of the account - `principalUrl` principal resource url - `homeUrl` resource home set url - `calendars` calendars of the account, will only be populated by [createAccount](../webdav/account/createAccount.md) - `addressBooks` addressBooks of the account, will only be populated by [createAccount](../webdav/account/createAccount.md) ---------------------------------------- ## File: types/DAVAddressBook.md --- title: 'DAVAddressBook' description: "TypeScript type definition for DAVAddressBook representing a CardDAV address book collection." --- ```ts export type DAVAddressBook = DAVCollection; ``` alias of [DAVCollection](DAVCollection.md) ---------------------------------------- ## File: types/DAVCalendar.md --- title: 'DAVCalendar' description: "TypeScript type definition for DAVCalendar representing a CalDAV calendar collection." --- ```ts export type DAVCalendar = { components?: string[]; timezone?: string; projectedProps?: Record; } & DAVCollection; ``` alias of [DAVCollection](DAVCollection.md) with - `timezone` iana timezone name of calendar - `components` array of calendar components defined in [rfc5455](https://datatracker.ietf.org/doc/html/rfc5545#section-3.6) - `projectedProps` object of fetched additional props by passing custom props to [fetchCalendars](../caldav/fetchCalendars.md) function ---------------------------------------- ## File: types/DAVCalendarObject.md --- title: 'DAVCalendarObject' description: "TypeScript type definition for DAVCalendarObject representing a calendar event or task." --- ```ts export type DAVCalendarObject = DAVObject; ``` alias of [DAVObject](DAVObject.md) ---------------------------------------- ## File: types/DAVCollection.md --- title: 'DAVCollection' description: "TypeScript type definition for DAVCollection representing a generic WebDAV collection." --- ```ts export type DAVCollection = { objects?: DAVObject[]; ctag?: string; description?: string; displayName?: string | Record; reports?: any; resourcetype?: any; syncToken?: string; url: string; }; ``` - `objects` objects of the - `ctag` [Collection Entity Tag](https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-ctag.txt) - `description` description of the collection - `displayName` [display name of the collection](https://datatracker.ietf.org/doc/html/rfc2518#section-13.2) - `reports` list of [reports that are supported by the resource](https://datatracker.ietf.org/doc/html/rfc3253#section-3.1.5). (in camel case), usually a string array - `resourcetype` [type of the resource](https://datatracker.ietf.org/doc/html/rfc2518#section-13.9), usually a string array - `syncToken` [the value of the synchronization token](https://datatracker.ietf.org/doc/html/rfc6578#section-4) - `url` url of the resource ---------------------------------------- ## File: types/DAVCredentials.md --- title: 'DAVCredentials' description: "TypeScript type definition for credentials supporting Basic, Bearer (OAuth), and Digest auth." --- ```ts export type DAVCredentials = { username?: string; password?: string; clientId?: string; clientSecret?: string; authorizationCode?: string; redirectUrl?: string; tokenUrl?: string; accessToken?: string; refreshToken?: string; expiration?: number; digestString?: string; customData?: Record; }; ``` refer to [this page](https://developers.google.com/identity/protocols/oauth2) for more on what these fields mean - `username` Basic or Digest auth username - `password` Basic or Digest auth password - `clientId` oauth client id - `clientSecret` oauth client secret - `authorizationCode` oauth callback auth code - `redirectUrl` oauth callback redirect url - `tokenUrl` oauth api token url - `accessToken` oauth access token - `refreshToken` oauth refresh token - `expiration` oauth access token expiration time - `digestString` a precomputed digest string, sent as-is with `authMethod: 'Digest'`. Prefer `username` and `password`, which let tsdav answer the server's challenge - `customData` custom data used for custom auth, can be anything See [authentication helpers](../helpers/authHelpers.md#digest-authentication) for Digest configuration, runtime requirements, redirects, and supported algorithms. ---------------------------------------- ## File: types/DAVDepth.md --- title: 'DAVDepth' description: "TypeScript type definition for DAVDepth header values ('0', '1', 'infinity')." --- ```ts export type DAVDepth = '0' | '1' | 'infinity'; ``` [depth header](https://datatracker.ietf.org/doc/html/rfc4918#section-10.2) of webdav requests | Depth | Action apply to | | -------- | ------------------------------------------ | | 0 | only the resource | | 1 | the resource and its internal members only | | infinity | the resource and all its members | ---------------------------------------- ## File: types/DAVObject.md --- title: 'DAVObject' description: "TypeScript type definition for DAVObject representing a remote WebDAV resource." --- ```ts export type DAVObject = { data?: any; etag?: string; url: string; }; ``` - `data` the raw content of an WEBDAV object. - `etag` the version string of content, if [etag](https://tools.ietf.org/id/draft-reschke-http-etag-on-write-08.html) changed, `data` must have been changed. - `url` url of the WEBDAV object. ---------------------------------------- ## File: types/DAVRequest.md --- title: 'DAVRequest' description: "TypeScript type definition for low-level WebDAV HTTP request options." --- ```ts export type DAVRequest = { headers?: Record; method: DAVMethods | HTTPMethods; body: any; namespace?: string; attributes?: Record; }; ``` [davRequest](../webdav/davRequest.md) init body - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `method` request method - `body` request body - `namespace` default namespace for all xml nodes - `attributes` root node xml attributes ---------------------------------------- ## File: types/DAVResponse.md --- title: 'DAVResponse' description: "TypeScript type definition for parsed WebDAV multi-status HTTP response." --- ```ts export type DAVResponse = { raw?: any; href?: string; parseError?: string; propStats?: DAVPropStat[]; status: number; statusText: string; ok: boolean; error?: { [key: string]: any }; responsedescription?: string; props?: { [key: string]: { status: number; statusText: string; ok: boolean; value: any } | any }; }; ``` sample DAVResponse ```json { "raw": { "multistatus": { "response": { "href": "/", "propstat": { "prop": { "currentUserPrincipal": { "href": "/123456/principal/" } }, "status": "HTTP/1.1 200 OK" } } } }, "href": "/", "status": 207, "statusText": "Multi-Status", "ok": true, "props": { "currentUserPrincipal": { "href": "/123456/principal/" } } } ``` response type of [davRequest](../webdav/davRequest.md) - `raw` the entire [response](https://datatracker.ietf.org/doc/html/rfc4918#section-14.24) object, useful when need something that is not a prop or href - `href` [content element URI](https://datatracker.ietf.org/doc/html/rfc2518#section-12.3) - `status` resource status, an all-failed property status, or the HTTP response status - `statusText` fetch response statusText - `ok` whether the resource response has a successful status; false when all returned properties fail - `error` error object from error response - `responsedescription` [information about a status response within a Multi-Status](https://datatracker.ietf.org/doc/html/rfc4918#section-14.25) - `props` response [propstat](https://datatracker.ietf.org/doc/html/rfc4918#section-14.22) props with camel case names. `propStats` preserves each property's status, properties, namespace URI map (`namespaces`), and optional error/description. `props` contains only successful properties. A response whose every property failed has `ok: false`; mixed results require inspecting the statuses of the properties you need. XML text stays a string, including numeric-looking display names and opaque tokens. Pure CDATA uses `{ _cdata: string }`; mixed text and CDATA are joined in document order. When namespaces share a local name, DAV names retain their usual key and colliding names use `{namespaceURI}localName`. `raw` preserves the complete body for unparsed responses. A malformed XML response has `ok: false` and `parseError`, so callers can distinguish parsing failures from a successful empty result. ---------------------------------------- ## File: types/DAVTokens.md --- title: 'DAVTokens' description: "TypeScript type definition for OAuth token refresh and access credentials." --- ```ts export type DAVTokens = { access_token?: string; refresh_token?: string; expires_in?: number; id_token?: string; token_type?: string; scope?: string; }; ``` oauth token response - `access_token` oauth access token - `refresh_token` oauth refresh token - `expires_in` token expires time in ms - `id_token` oauth id token - `token_type` oauth token type, usually `Bearer` - `scope` oauth token scope ---------------------------------------- ## File: types/DAVVCard.md --- title: 'DAVVCard' description: "TypeScript type definition for DAVVCard representing a parsed CardDAV vCard contact." --- ```ts export type DAVVCard = DAVObject; ``` alias of [DAVObject](DAVObject.md) ---------------------------------------- ## File: types/ElementCompact.md --- title: 'ElementCompact' description: "TypeScript type definition for xml-js ElementCompact XML representation." --- ```ts export interface ElementCompact { [key: string]: any; _declaration?: { _attributes?: DeclarationAttributes; }; _instruction?: { [key: string]: string; }; _attributes?: Attributes; _cdata?: string; _doctype?: string; _comment?: string; _text?: string | number; } ``` [xml-js defined](https://github.com/nashwaan/xml-js/blob/f0376f265c4f299100fb4766828ebf066a0edeec/types/index.d.ts#L11) compact js object representation of xml elements. you can use this [helper](../helper.mdx) to help converting xml between js objects. ---------------------------------------- ## File: webdav/account/createAccount.md --- title: 'createAccount' sidebar_position: 1 description: "Initialize and discover a WebDAV, CalDAV, or CardDAV account structure." --- ## `createAccount` construct webdav account information need to requests ```ts const account = await createAccount({ account: { serverUrl: 'https://caldav.icloud.com/', accountType: 'caldav', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` **required**, account with `serverUrl` and `accountType` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation - `loadCollections` defaults to false, whether to load all collections of the account - `loadObjects` defaults to false, whether to load all objects of collections as well, must be used with `loadCollections` set to `true` ### Return Value created [DAVAccount](../../types/DAVAccount.md) ### Behavior perform [serviceDiscovery](serviceDiscovery.md),[fetchHomeUrl](fetchHomeUrl.md), [fetchPrincipalUrl](fetchPrincipalUrl.md) for account information gathering. make fetch collections & fetch objects requests depend on options. ---------------------------------------- ## File: webdav/account/fetchHomeUrl.md --- title: 'fetchHomeUrl' sidebar_position: 4 description: "Fetch calendar-home-set or addressbook-home-set URLs for a principal." --- ## `fetchHomeUrl` fetch resource home set url ```ts const url = await fetchHomeUrl({ account: { principalUrl, serverUrl: 'https://caldav.icloud.com/', rootUrl: 'https://caldav.icloud.com/', accountType: 'caldav', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` **required**, account with `principalUrl` and `accountType` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation ### Return Value resource home set url ### Behavior send calendar-home-set or addressbook-home-set (based on accountType) PROPFIND request and extract resource home set url from xml response ---------------------------------------- ## File: webdav/account/fetchPrincipalUrl.md --- title: 'fetchPrincipalUrl' sidebar_position: 3 description: "Discover current user principal URL using DAV:current-user-principal." --- ## `fetchPrincipalUrl` fetch resource principal collection url ```ts const url = await fetchPrincipalUrl({ account: { serverUrl: 'https://caldav.icloud.com/', rootUrl: 'https://caldav.icloud.com/', accountType: 'caldav', }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` **required**, account with `rootUrl` and `accountType` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation ### Return Value principal collection url ### Behavior send current-user-principal PROPFIND request and extract principal collection url from xml response ---------------------------------------- ## File: webdav/account/serviceDiscovery.md --- title: 'serviceDiscovery' sidebar_position: 2 description: "Discover CalDAV, CardDAV, and WebDAV endpoints via .well-known DNS/HTTP." --- ## `serviceDiscovery` automatically discover service root url ```ts const url = await serviceDiscovery({ account: { serverUrl: 'https://caldav.icloud.com/', accountType: 'caldav' }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `account` **required**, account with `serverUrl` and `accountType` - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation ### Return Value root url ### Behavior Requests `/.well-known/caldav` or `/.well-known/carddav` on the server's origin using `PROPFIND`, then tries `GET` if necessary. Relative `Location` headers resolve against that discovery request URL. For example, `Location: ../dav/` resolves to `/dav/`, even when `serverUrl` contains a nested calendar path. Path-relative redirects retain the request's origin and port. Absolute and protocol-relative redirects use their own host and port, including the protocol's default port when none is specified. Discovery controls the method, body, and manual redirect handling. A `body` supplied in `fetchOptions` is not sent with the `GET` fallback; other transport options and headers still apply. If neither request redirects, the function returns `serverUrl` as a normalized URL. ---------------------------------------- ## File: webdav/collection/collectionQuery.md --- title: 'collectionQuery' sidebar_position: 1 description: "Query collection child resources and properties." --- ## `collectionQuery` query on [DAVCollection](../../types/DAVCollection.md) ```ts const result = await collectionQuery({ url: 'https://contacts.icloud.com/123456/carddavhome/card/', body: { 'addressbook-query': { _attributes: getDAVAttribute([DAVNamespace.CARDDAV, DAVNamespace.DAV]), [`${DAVNamespaceShort.DAV}:prop`]: props, filter: { 'prop-filter': { _attributes: { name: 'FN', }, }, }, }, }, defaultNamespace: DAVNamespaceShort.CARDDAV, depth: '1', headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, collection url - `body` **required**, query request body - `depth` [DAVDepth](../../types/DAVDepth.md) - `defaultNamespace` defaults to `DAVNamespaceShort.DAV`, default namespace for the the request body - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation ### Return Value array of [DAVResponse](../../types/DAVResponse.md) ### Behavior Sends a REPORT request to the target collection and parses the response XML into an array of [DAVResponse](../../types/DAVResponse.md). ### Error Handling `collectionQuery` will reject with an error if: - The server returns a non-OK HTTP response (e.g., 504 Gateway Timeout, 401 Unauthorized). - Any individual response within a Multi-Status payload has a status code >= 400, except a lone collection-level 404 with no properties, propstat, or error element for a `calendar-query` using the CalDAV default namespace. Its href must match the queried collection (relative hrefs and trailing slashes are supported). This accommodates older Stalwart servers that used that response for empty queries instead of the empty `` required by [RFC 4791 §7.8](https://www.rfc-editor.org/rfc/rfc4791.html#section-7.8). Object-level 404s and failures in other REPORT types still reject. - The server response is not valid XML when a Multi-Status response is expected. ---------------------------------------- ## File: webdav/collection/isCollectionDirty.md --- title: 'isCollectionDirty' sidebar_position: 3 description: "Check whether a collection has modified objects using CTAG or sync-token." --- ## `isCollectionDirty` detect if the collection have changed ```ts const { isDirty, newCtag } = await isCollectionDirty({ collection: calendars[0], headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `collection` **required**, [DAVCollection](../../types/DAVCollection.md) to detect - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value - `isDirty` a boolean indicate if the collection is dirty - `newCtag` the remote ctag, or `undefined` when the server does not provide one. An unavailable ctag means `isDirty: true` and basic sync fetches a complete snapshot. ### Behavior use PROPFIND to fetch new ctag of the collection and compare it with current ctag, if the ctag changed, it means collection changed. ---------------------------------------- ## File: webdav/collection/makeCollection.md --- title: 'makeCollection' sidebar_position: 2 description: "Create a new WebDAV collection folder using HTTP MKCOL." --- ## `makeCollection` [create a new collection](https://datatracker.ietf.org/doc/html/rfc5689#section-3) ```ts const response = await makeCollection({ url: 'https://caldav.icloud.com/12345676/calendars/c623f6be-a2d4-4c60-932a-043e67025dde/', props: { displayname: 'my-calendar', [`${DAVNamespaceShort.CALDAV}:calendar-description`]: 'calendar description', }, headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, url of the collection to create - `props` [ElementCompact](../../types/ElementCompact.md) prop for the collection - `depth` [DAVDepth](../../types/DAVDepth.md) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../../types/DAVResponse.md) ### Behavior send MKCOL request and parse response xml into array of [DAVResponse](../../types/DAVResponse.md) ---------------------------------------- ## File: webdav/collection/smartCollectionSync.md --- title: 'smartCollectionSync' sidebar_position: 6 description: "High-level sync abstraction automating initial crawl and incremental delta sync." --- ## `smartCollectionSync` and `smartCollectionSyncDetailed` smart version of collection sync that combines ctag based sync with webdav sync. ```ts const { created, updated, deleted } = ( await smartCollectionSyncDetailed({ collection: { url: 'https://caldav.icloud.com/12345676/calendars/c623f6be-a2d4-4c60-932a-043e67025dde/', ctag: 'eWd9Vz8OwS0DE==', syncToken: 'eWdLSfo8439Vz8OwS0DE==', objects: [ { etag: '"63758758580"', id: '0003ffbe-cb71-49f5-bc7b-9fafdd756784', data: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nBEGIN:VEVENT\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212\nDTEND:20080213\nDTSTAMP:20150421T141403\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809\nSixteenth President (1861-1865)\n\n\n\n \nhttp://AmericanHistoryCalendar.com\nURL:http://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincol\n n\nEND:VEVENT\nEND:VCALENDAR', url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', }, ], objectMultiGet: calendarMultiGet, }, method: 'webdav', account: { accountType: 'caldav', homeUrl: 'https://caldav.icloud.com/123456/calendars/', }, headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }) ).objects; ``` ### Arguments - `collection` **required**, the target collection to sync - `collection.objectMultiGet` required when WebDAV sync reports created or updated objects - `collection.fetchObjects` required when basic sync detects a changed collection - `method` defaults to auto detect, one of `basic` and `webdav` - `account` [DAVAccount](../../types/DAVAccount.md) to sync - `detailedResult` deprecated, use `smartCollectionSyncDetailed` for the detailed result instead. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation :::info Provide the previously stored `collection.objects` to distinguish created from updated objects. Without that baseline, changed remote objects are classified as created. Basic sync also needs the baseline to identify deletions. ::: ### Return Value `smartCollectionSync` returns: the supplied collection with an `objects` array containing the latest [DAVObject](../../types/DAVObject.md) and the resulting `syncToken` or `ctag`. `smartCollectionSyncDetailed` returns: the supplied collection with the resulting `syncToken` or `ctag` and - `objects` - `created` array of [DAVObject](../../types/DAVObject.md) - `updated` array of [DAVObject](../../types/DAVObject.md) - `deleted` array of [DAVObject](../../types/DAVObject.md) ### Behavior detect if collection support sync-collection REPORT if they supports, use [rfc6578 webdav sync](https://datatracker.ietf.org/doc/html/rfc6578) to detect if collection changed, else use ctag to detect if collection changed. if collection changed, fetch the latest list of [DAVObject](../../types/DAVObject.md) from remote, compare the provided list and the latest list to find out `created`, `updated`, and `deleted` objects. The sync rejects with a clear error if the callback required by the selected method is missing. This prevents advancing a sync token or treating all local objects as deleted without first retrieving the remote changes. Failed sync REPORTs and failed multi-get responses also reject rather than returning a successful sync or advancing the token. Object-level 404 responses in a sync REPORT still represent deletions. A successful response with no changed objects retains the new server sync token. Without a `collection.syncToken`, the WebDAV sync is an initial sync that lists every object, so local objects missing from it are reported as `deleted`. When the server rejects the stored sync token (the `DAV:valid-sync-token` precondition, for example after the token expires), the sync is repeated once from an empty token and returns a fresh token, as RFC 6578 requires. When using `smartCollectionSync`, fetch the latest list of [DAVObject](../../types/DAVObject.md) from changed collection using [rfc6578 webdav sync](https://datatracker.ietf.org/doc/html/rfc6578) and `objectMultiGet` When using `smartCollectionSyncDetailed`, return three list of separate objects for `created`, `updated`, and `deleted` ---------------------------------------- ## File: webdav/collection/supportedReportSet.md --- title: 'supportedReportSet' sidebar_position: 4 description: "Query supported report types (sync-collection, calendar-query) on a collection." --- ## `supportedReportSet` identifies the [reports that are supported by the resource](https://datatracker.ietf.org/doc/html/rfc3253#section-3.1.5) ```ts const reports = await supportedReportSet({ collection, headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `collection` **required**, [DAVCollection](../../types/DAVCollection.md) to query on - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of supported REPORT name in camelCase ### Behavior send supported-report-set PROPFIND request and parse response xml to extract names and convert them to camel case. ---------------------------------------- ## File: webdav/collection/syncCollection.md --- title: 'syncCollection' sidebar_position: 5 description: "Perform RFC 6578 sync-collection REPORT to retrieve incremental changes." --- ## `syncCollection` [One way to synchronize data between two entities is to use some form of synchronization token](https://datatracker.ietf.org/doc/html/rfc6578#section-3.2) ```ts const result = await syncCollection({ url: 'https://caldav.icloud.com/12345676/calendars/c623f6be-a2d4-4c60-932a-043e67025dde/', props: { [`${DAVNamespaceShort.DAV}:getetag`]: {}, [`${DAVNamespaceShort.CALDAV}:calendar-data`]: {}, [`${DAVNamespaceShort.DAV}:displayname`]: {}, }, syncLevel: 1, syncToken: 'bb399205ff6ff07', headers: { authorization: 'Basic x0C9ueWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, target collection url - `props` **required**, [ElementCompact](../../types/ElementCompact.md) - `syncLevel` [Indicates the "scope" of the synchronization report request](https://datatracker.ietf.org/doc/html/rfc6578#section-6.3) - `syncToken` [The synchronization token provided by the server and returned by the client](https://datatracker.ietf.org/doc/html/rfc6578#section-6.2) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../../types/DAVResponse.md) ### Behavior send sync-collection REPORT to target collection url, parse response xml into array of [DAVResponse](../../types/DAVResponse.md) ---------------------------------------- ## File: webdav/createObject.md --- title: 'createObject' sidebar_position: 3 description: "Upload or create an arbitrary resource object on a WebDAV server." --- ## `createObject` create an object ```ts const response = await createObject({ url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', data: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nBEGIN:VEVENT\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212\nDTEND:20080213\nDTSTAMP:20150421T141403\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809\nSixteenth President (1861-1865)\n\n\n\n \nhttp://AmericanHistoryCalendar.com\nURL:http://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincol\n n\nEND:VEVENT\nEND:VCALENDAR', headers: { 'content-type': 'text/calendar; charset=utf-8', authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, object url - `data` **required**, object data - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior send PUT request to target url with body of data, with `If-None-Match` header `*` to avoid accidental overwrite. ---------------------------------------- ## File: webdav/davRequest.md --- title: 'davRequest' sidebar_position: 1 description: "Execute low-level WebDAV HTTP requests with multi-status XML parsing and headers." --- ## `davRequest` core request function of the library, built on top of the standards-compliant `fetch` exposed by every supported runtime (Node.js >= 18, modern browsers, Bun, Deno, Cloudflare Workers, Electron). `xml-js` is used so that plain JS objects can be passed as the request body. ```ts const [result] = await davRequest({ url: 'https://caldav.icloud.com/', init: { method: 'PROPFIND', namespace: 'd', body: { propfind: { _attributes: { 'xmlns:d': 'DAV:', }, prop: { 'd:current-user-principal': {} }, }, }, headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }, }); ``` ### Arguments - `url` **required**, request url - `init` **required**, [DAVRequest](davRequest.md) Object - `convertIncoming` defaults to `true`, whether to convert the passed in init object request body, if `false`, davRequest would expect `init->body` is `xml` string, and would send it directly to target `url` without processing. - `parseOutgoing` defaults to `true`, whether to parse the return value in response body, if `false`, the response `raw` would be raw `xml` string returned from server. - `headersToExclude` case-insensitive header names to remove after merging defaults, request headers, and `fetchOptions.headers` - `fetchOptions` options to pass to underlying fetch function - `fetch` custom fetch implementation to override the runtime's native `fetch` ### Return Value array of [DAVResponse](../types/DAVResponse.md) - response-> raw will be `string` if `parseOutgoing` is `false` or request failed. ### Behavior depend on options, use `xml-js` to convert passed in json object into valid xml request, also use `xml-js` to convert received xml response into json object. if request failed, response-> raw will be raw response text returned from server. Multistatus responses without resource entries still return one result with the parsed `raw` payload, so callers can read collection metadata such as `raw.multistatus.syncToken`. Status codes are parsed even when the server omits the reason phrase. Successful unparsed response bodies are preserved in full. Parsed property failures remain available in `propStats`; `props` includes only successful properties, and an all-failed propstat response has `ok: false`. Malformed XML has `ok: false` and a `parseError`. ---------------------------------------- ## File: webdav/deleteObject.md --- title: 'deleteObject' sidebar_position: 5 description: "Delete a file or resource object from a WebDAV server." --- ## `deleteObject` delete an object ```ts const response = await deleteObject({ url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', etag: '"63758758580"', headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, url of object to delete - `etag` the version string of content, if [etag](https://tools.ietf.org/id/draft-reschke-http-etag-on-write-08.html) changed, `data` must have been changed. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior send DELETE request with etag header object will not be deleted if etag do not match ---------------------------------------- ## File: webdav/propfind.md --- title: 'propfind' sidebar_position: 2 description: "Query WebDAV resource properties and collections using HTTP PROPFIND." --- ## `propfind` The [PROPFIND](https://datatracker.ietf.org/doc/html/rfc4918#section-9.1) method retrieves properties defined on the resource identified by the Request-URI ```ts const [result] = await propfind({ url: 'https://caldav.icloud.com/', props: [{ name: 'current-user-principal', namespace: DAVNamespace.DAV }], headers: { authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, request url - `props` **required**, [ElementCompact](../types/ElementCompact.md) props to find - `depth` [DAVDepth](../types/DAVDepth.md) - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value array of [DAVResponse](../types/DAVResponse.md) ### Behavior send a prop find request, parse the response xml to an array of [DAVResponse](../types/DAVResponse.md). ---------------------------------------- ## File: webdav/updateObject.md --- title: 'updateObject' sidebar_position: 4 description: "Update an existing resource object on a WebDAV server." --- ## `updateObject` update an object ```ts const response = await updateObject({ url: 'https://caldav.icloud.com/123456/calendars/A5639426-B73B-4F90-86AB-D70F7F603E75/test.ics', data: 'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ZContent.net//Zap Calendar 1.0//EN\nCALSCALE:GREGORIAN\nMETHOD:PUBLISH\nBEGIN:VEVENT\nSUMMARY:Abraham Lincoln\nUID:c7614cff-3549-4a00-9152-d25cc1fe077d\nSEQUENCE:0\nSTATUS:CONFIRMED\nTRANSP:TRANSPARENT\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYMONTHDAY=12\nDTSTART:20080212\nDTEND:20080213\nDTSTAMP:20150421T141403\nCATEGORIES:U.S. Presidents,Civil War People\nLOCATION:Hodgenville, Kentucky\nGEO:37.5739497;-85.7399606\nDESCRIPTION:Born February 12, 1809\nSixteenth President (1861-1865)\n\n\n\n \nhttp://AmericanHistoryCalendar.com\nURL:http://americanhistorycalendar.com/peoplecalendar/1,328-abraham-lincol\n n\nEND:VEVENT\nEND:VCALENDAR', etag: '"63758758580"', headers: { 'content-type': 'text/calendar; charset=utf-8', authorization: 'Basic x0C9uFWd9Vz8OwS0DEAtkAlj', }, }); ``` ### Arguments - `url` **required**, url of object to update - `data` **required**, new object content - `etag` the version string of content, if [etag](https://tools.ietf.org/id/draft-reschke-http-etag-on-write-08.html) changed, `data` must have been changed. - `headers` request headers - `headersToExclude` array of keys of the headers you want to exclude - `fetchOptions` options to pass to underlying fetch function ### Return Value [fetch api response](https://developer.mozilla.org/en-US/docs/Web/API/Response) ### Behavior send PUT request to with data body and etag header object will not be updated if etag do not match ----------------------------------------