@sockethub/platform-caldav 1.0.0-beta.4 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -13
- package/dist/dav.d.ts +12 -2
- package/dist/ical.d.ts +8 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +479 -69
- package/dist/index.js.map +9 -8
- package/dist/patch.d.ts +13 -0
- package/dist/schema.d.ts +672 -0
- package/dist/types.d.ts +14 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ it is used.
|
|
|
18
18
|
|
|
19
19
|
## Discovery cache
|
|
20
20
|
|
|
21
|
-
Each `query`, `create`, `update`, and `delete` needs the account's calendar list to
|
|
21
|
+
Each `query`, `sync`, `create`, `update`, and `delete` needs the account's calendar list to
|
|
22
22
|
check the target. The platform caches that discovery result in memory for
|
|
23
23
|
`packageConfig.discoveryCacheTtlMs` (default `300000`, 5 minutes; `0`
|
|
24
24
|
disables the cache). Entries are keyed by an HMAC of the protocol, service URL,
|
|
@@ -82,6 +82,8 @@ never forwarded to another origin.
|
|
|
82
82
|
- `fetch`: discover calendars and their event/task support.
|
|
83
83
|
- `query`: read items from one discovered calendar, optionally filtering by
|
|
84
84
|
component type or UTC date range.
|
|
85
|
+
- `sync`: report what changed in one discovered calendar since a sync token
|
|
86
|
+
(RFC 6578 WebDAV Sync).
|
|
85
87
|
- `read`: list the items in a published `.ics` feed without credentials,
|
|
86
88
|
with the same optional filters as `query`.
|
|
87
89
|
- `create`: create an event or task with collision protection.
|
|
@@ -131,9 +133,86 @@ The collection contains calendar objects such as:
|
|
|
131
133
|
```
|
|
132
134
|
|
|
133
135
|
Each returned item contains its resource `id`, `uid`, `etag`, and
|
|
134
|
-
`updateSupported
|
|
136
|
+
`updateSupported`, plus `created` and `lastModified` (UTC) when the resource
|
|
137
|
+
records them; `lastModified` falls back to `DTSTAMP`. These two are output
|
|
138
|
+
only and ignored on create and update. Keep the ETag for later update or
|
|
139
|
+
delete requests. Use date
|
|
135
140
|
ranges for large calendars; responses larger than 10 MiB are rejected.
|
|
136
141
|
|
|
142
|
+
### Sync changes
|
|
143
|
+
|
|
144
|
+
`sync` reports changes without downloading the whole calendar each time. Send
|
|
145
|
+
it without an `object` (or without `object.syncToken`) for the initial sync:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"type": "sync",
|
|
150
|
+
"actor": { "id": "caldav:alice", "type": "person" },
|
|
151
|
+
"target": {
|
|
152
|
+
"id": "https://calendar.example/calendars/alice/personal/",
|
|
153
|
+
"type": "calendar"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The response is a collection with every event and task, plus a token:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"type": "collection",
|
|
163
|
+
"summary": "CalDAV changes",
|
|
164
|
+
"totalItems": 1,
|
|
165
|
+
"items": [
|
|
166
|
+
{
|
|
167
|
+
"id": "https://calendar.example/calendars/alice/personal/a.ics",
|
|
168
|
+
"type": "task",
|
|
169
|
+
"uid": "a",
|
|
170
|
+
"name": "Pay rent",
|
|
171
|
+
"etag": "\"2\"",
|
|
172
|
+
"updateSupported": true
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
"removed": [],
|
|
176
|
+
"syncToken": "http://sabre.io/ns/sync/42",
|
|
177
|
+
"truncated": false
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Store `syncToken` as an opaque string per calendar and send it back next time
|
|
182
|
+
as `"object": { "syncToken": "..." }`. The response then holds only the items
|
|
183
|
+
added or changed since that token, with their current ETag and content, and
|
|
184
|
+
`removed` lists the ids of items deleted since then. Items in the same shape
|
|
185
|
+
as `query` results replace the stored copy with the same `id`.
|
|
186
|
+
|
|
187
|
+
When `truncated` is `true`, the server limited the result. The returned token
|
|
188
|
+
covers exactly the changes delivered, so store it and sync again until
|
|
189
|
+
`truncated` is `false`.
|
|
190
|
+
|
|
191
|
+
To track changes without downloading a calendar's history, start from the
|
|
192
|
+
current token instead of an initial sync. Send `"object": { "tokenOnly": true }`
|
|
193
|
+
to get the calendar's current `syncToken` with empty `items` and `removed`,
|
|
194
|
+
then `query` the date range you need, then `sync` with the token on later
|
|
195
|
+
passes. Read the token before the `query`, so nothing changed in between is
|
|
196
|
+
missed. Later syncs still report changes to any item in the calendar,
|
|
197
|
+
including old ones. A server that exposes no token returns
|
|
198
|
+
`caldav:sync-unsupported`.
|
|
199
|
+
|
|
200
|
+
Calendar resources that hold neither an event nor a task, such as journals, are
|
|
201
|
+
left out of `items`.
|
|
202
|
+
|
|
203
|
+
Handle these errors:
|
|
204
|
+
|
|
205
|
+
- `caldav:sync-token-invalid`: the server no longer accepts the token (it
|
|
206
|
+
expired, or the calendar was reset). Drop the stored token and sync again
|
|
207
|
+
without one.
|
|
208
|
+
- `caldav:sync-unsupported`: the server does not offer WebDAV Sync. Fall back
|
|
209
|
+
to `query`, which returns a full snapshot.
|
|
210
|
+
- `caldav:sync-failed`: any other sync error.
|
|
211
|
+
|
|
212
|
+
When a server rejects a token without saying why (a 400, 409, or 412 with
|
|
213
|
+
no `DAV:` precondition), the platform reports `caldav:sync-token-invalid`. A
|
|
214
|
+
retry without a token then reports the real cause.
|
|
215
|
+
|
|
137
216
|
### Read a published iCalendar feed
|
|
138
217
|
|
|
139
218
|
Many calendars are shared as a read-only `.ics` link (often shown as a
|
|
@@ -232,8 +311,12 @@ Update sends the complete event or task returned by `query`, including `id`,
|
|
|
232
311
|
`uid`, and `etag`. Increment `sequence` when changing scheduling information.
|
|
233
312
|
An update is rejected when `updateSupported` is false, which protects recurrence
|
|
234
313
|
exceptions, exclusion dates, and multi-component resources from being lost.
|
|
235
|
-
|
|
236
|
-
|
|
314
|
+
Update changes only the stored properties whose fields differ from the stored
|
|
315
|
+
item, plus `DTSTAMP` and `LAST-MODIFIED`. Everything else stays as it was,
|
|
316
|
+
including `CATEGORIES`, `X-` properties, `CREATED`, `VTIMEZONE` blocks, and
|
|
317
|
+
parameters on unchanged attendees and alarms. A field left out of the update
|
|
318
|
+
removes its property. A changed field is written from the item model, so
|
|
319
|
+
parameters the model does not carry are dropped from that property.
|
|
237
320
|
|
|
238
321
|
```json
|
|
239
322
|
{
|
|
@@ -257,7 +340,8 @@ Query again, reconcile the change, and retry with the new ETag.
|
|
|
257
340
|
## Interoperability and security
|
|
258
341
|
|
|
259
342
|
The implementation follows WebDAV/CalDAV discovery, calendar-query REPORT,
|
|
260
|
-
iCalendar content-line escaping/folding,
|
|
343
|
+
iCalendar content-line escaping/folding, conditional PUT/DELETE semantics, and
|
|
344
|
+
WebDAV Sync (RFC 6578).
|
|
261
345
|
|
|
262
346
|
### Server and provider compatibility
|
|
263
347
|
|
|
@@ -269,8 +353,8 @@ until they are included in automated or repeatable manual testing.
|
|
|
269
353
|
|
|
270
354
|
| Service | Deployment | Verification | Authentication exercised | Notes |
|
|
271
355
|
| --- | --- | --- | --- | --- |
|
|
272
|
-
| Radicale 3.7.7 | Self-hosted | Automated integration | Basic | Complete discovery and event/task create, query, update, and delete lifecycle. |
|
|
273
|
-
| Baïkal 0.10.1 | Self-hosted | Automated integration | Digest MD5 with `qop=auth` | Complete lifecycle through Baïkal's `dav.php/` endpoint, including its encoded ETags. |
|
|
356
|
+
| Radicale 3.7.7 | Self-hosted | Automated integration | Basic | Complete discovery and event/task create, query, update, and delete lifecycle, and incremental sync. |
|
|
357
|
+
| Baïkal 0.10.1 | Self-hosted | Automated integration | Digest MD5 with `qop=auth` | Complete lifecycle and incremental sync through Baïkal's `dav.php/` endpoint, including its encoded ETags. |
|
|
274
358
|
| Nextcloud | Self-hosted or hosted | Manually verified during initial development | Basic with username and password or app password | Use the final `/remote.php/dav/` endpoint when cross-origin discovery redirects are involved. OAuth is not covered by this verification. |
|
|
275
359
|
| ownCloud | Self-hosted or hosted | Not yet verified | Not yet verified | Expected to work when standard discovery and a supported authentication method are enabled. |
|
|
276
360
|
| DAViCal | Self-hosted | Not yet verified | Not yet verified | Expected to work when standard discovery and a supported authentication method are enabled. |
|
|
@@ -300,17 +384,15 @@ administrator must explicitly enable `allowPrivateAddresses` or
|
|
|
300
384
|
| Shared and delegated calendars | Read-only discovery only | Calendars returned from the user's calendar home can be queried, but creating shares, accepting invitations, proxy delegation, and ACL management are not implemented. |
|
|
301
385
|
| Calendar collection management | Not supported | The platform does not create, rename, or delete calendar collections. |
|
|
302
386
|
| Free/busy queries | Not supported | CalDAV free-busy REPORT and scheduling availability are not implemented. |
|
|
303
|
-
| Incremental sync |
|
|
387
|
+
| Incremental sync | Supported | `sync` uses RFC 6578 WebDAV Sync tokens, including truncated results. Members returned without calendar data are fetched with one `calendar-multiget`. |
|
|
304
388
|
| Managed attachments | Not supported | iCalendar URL and inline attachment properties are supported, but CalDAV managed attachments are not. |
|
|
305
389
|
|
|
306
390
|
<!-- markdownlint-enable MD013 -->
|
|
307
391
|
|
|
308
|
-
Queries currently return calendar snapshots. CalDAV sync tokens (RFC 6578) are
|
|
309
|
-
the intended follow-up for efficient incremental synchronization.
|
|
310
|
-
|
|
311
392
|
Stable errors include `caldav:authentication-failed`,
|
|
312
393
|
`caldav:unsupported-authentication`,
|
|
313
394
|
`caldav:connection-failed`, `caldav:invalid-calendar`,
|
|
314
395
|
`caldav:invalid-resource`, `caldav:unsupported-component`,
|
|
315
|
-
`caldav:conflict`, `caldav:not-found`, `caldav:invalid-feed`,
|
|
316
|
-
`caldav:feed-failed
|
|
396
|
+
`caldav:conflict`, `caldav:not-found`, `caldav:invalid-feed`,
|
|
397
|
+
`caldav:feed-failed`, `caldav:sync-token-invalid`,
|
|
398
|
+
`caldav:sync-unsupported`, and `caldav:sync-failed`.
|
package/dist/dav.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DavAuthentication, DavClient, DavFailure, type DavNetworkOptions } from "@sockethub/dav";
|
|
2
|
-
import type { CalendarDescription, CalendarItem, QueryInput } from "./types.js";
|
|
2
|
+
import type { CalendarDescription, CalendarItem, CalendarSyncResult, QueryInput } from "./types.js";
|
|
3
3
|
export declare const CalDavFailure: typeof DavFailure;
|
|
4
4
|
export declare class CalDavClient extends DavClient {
|
|
5
5
|
constructor(url: string, authentication: DavAuthentication, timeoutMs?: number, networkOptions?: DavNetworkOptions);
|
|
@@ -10,7 +10,17 @@ export declare class CalDavClient extends DavClient {
|
|
|
10
10
|
etag?: string;
|
|
11
11
|
}>;
|
|
12
12
|
query(calendar: CalendarDescription, query?: QueryInput): Promise<CalendarItem[]>;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Incremental sync (RFC 6578). Without `syncToken`, lists every event and
|
|
15
|
+
* task in the calendar; with one, lists only what changed since then.
|
|
16
|
+
*/
|
|
17
|
+
sync(calendar: CalendarDescription, syncToken?: string): Promise<CalendarSyncResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Replace a resource if its ETag still matches. `body` is either the new
|
|
20
|
+
* resource or a function that builds it from the stored one, so changes
|
|
21
|
+
* can be applied without losing what the item model does not carry.
|
|
22
|
+
*/
|
|
23
|
+
update(id: string, etag: string, body: string | ((stored: string) => string)): Promise<{
|
|
14
24
|
id: string;
|
|
15
25
|
etag?: string;
|
|
16
26
|
}>;
|
package/dist/ical.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { CalendarItem, CalendarObjectInput } from "./types.js";
|
|
2
2
|
/** Fold one iCalendar content line at 75 UTF-8 octets (74 after continuation). */
|
|
3
3
|
export declare function foldLine(line: string): string;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Serialize an item as a single-component iCalendar resource. `created`
|
|
6
|
+
* stamps `CREATED` with `now`; pass it only for a new resource, so an update
|
|
7
|
+
* does not move the creation time.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildICalendar(input: CalendarObjectInput, now?: Date, { created }?: {
|
|
10
|
+
created?: boolean;
|
|
11
|
+
}): {
|
|
5
12
|
uid: string;
|
|
6
13
|
body: string;
|
|
7
14
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,22 @@ export default class CalDav implements PlatformInterface {
|
|
|
30
30
|
read(job: ActivityStream, done: PlatformCallback): void;
|
|
31
31
|
create(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
32
32
|
query(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
33
|
+
/**
|
|
34
|
+
* Incremental calendar sync (RFC 6578 WebDAV Sync). Without
|
|
35
|
+
* `object.syncToken`, returns every event and task plus a token; with
|
|
36
|
+
* one, returns only items changed and ids removed since that token.
|
|
37
|
+
* Store the returned `syncToken` opaquely and send it next time. When
|
|
38
|
+
* `truncated` is true, sync again with the new token before relying on
|
|
39
|
+
* the result being complete. `caldav:sync-token-invalid` means start
|
|
40
|
+
* over without a token; `caldav:sync-unsupported` means fall back to
|
|
41
|
+
* `query`.
|
|
42
|
+
*
|
|
43
|
+
* With `object.tokenOnly: true`, returns the calendar's current token
|
|
44
|
+
* and no items, so a client can start tracking changes without
|
|
45
|
+
* downloading history: get the token first, then `query` the range it
|
|
46
|
+
* needs, then `sync` with the token on later passes.
|
|
47
|
+
*/
|
|
48
|
+
sync(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
33
49
|
update(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
34
50
|
delete(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
35
51
|
private client;
|