@sockethub/platform-caldav 1.0.0-alpha.9 → 1.0.0-beta.1
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 +53 -5
- package/dist/ics.d.ts +25 -0
- package/dist/index.d.ts +29 -1
- package/dist/index.js +27903 -1392
- package/dist/index.js.map +124 -11
- package/dist/schema.d.ts +60 -12
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# `@sockethub/platform-caldav`
|
|
2
2
|
|
|
3
3
|
Sockethub platform for discovering CalDAV calendars and reading, creating,
|
|
4
|
-
updating, and deleting events (`VEVENT`) and tasks (`VTODO`).
|
|
4
|
+
updating, and deleting events (`VEVENT`) and tasks (`VTODO`). It can also read
|
|
5
|
+
published iCalendar (`.ics`) feeds, such as holiday calendars or read-only
|
|
6
|
+
subscription links, without a CalDAV account.
|
|
5
7
|
|
|
6
8
|
Requests accept only public HTTPS services by default. Administrators can opt
|
|
7
9
|
in to HTTP with `packageConfig.allowInsecureHttp` and private-network targets
|
|
8
10
|
with `packageConfig.allowPrivateAddresses`; these are server settings and
|
|
9
|
-
cannot be enabled by a connected client.
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
cannot be enabled by a connected client. Address checks use the shared
|
|
12
|
+
outbound URL guard, including IPv6 embeddings of private ranges and the
|
|
13
|
+
local-use NAT64 prefix `64:ff9b:1::/48`. See
|
|
14
|
+
[Outbound URL guard](../../docs/configuration.md#outbound-url-guard).
|
|
15
|
+
Credentials and OAuth tokens are sent only to the configured origin. Every
|
|
16
|
+
calendar and item target is checked against the authenticated account before
|
|
17
|
+
it is used.
|
|
12
18
|
|
|
13
19
|
## Authentication
|
|
14
20
|
|
|
@@ -63,6 +69,8 @@ never forwarded to another origin.
|
|
|
63
69
|
- `fetch`: discover calendars and their event/task support.
|
|
64
70
|
- `query`: read items from one discovered calendar, optionally filtering by
|
|
65
71
|
component type or UTC date range.
|
|
72
|
+
- `read`: list the items in a published `.ics` feed without credentials,
|
|
73
|
+
with the same optional filters as `query`.
|
|
66
74
|
- `create`: create an event or task with collision protection.
|
|
67
75
|
- `update`: replace an item only when its ETag still matches.
|
|
68
76
|
- `delete`: delete an item only when its ETag still matches.
|
|
@@ -113,6 +121,44 @@ Each returned item contains its resource `id`, `uid`, `etag`, and
|
|
|
113
121
|
`updateSupported`. Keep the ETag for later update or delete requests. Use date
|
|
114
122
|
ranges for large calendars; responses larger than 10 MiB are rejected.
|
|
115
123
|
|
|
124
|
+
### Read a published iCalendar feed
|
|
125
|
+
|
|
126
|
+
Many calendars are shared as a read-only `.ics` link (often shown as a
|
|
127
|
+
`webcal://` URL). The `read` action fetches such a feed and returns its
|
|
128
|
+
events and tasks as a collection. It needs no credentials and no `fetch`
|
|
129
|
+
step; `webcal:` links are fetched over HTTPS.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"type": "read",
|
|
134
|
+
"actor": { "id": "caldav:alice", "type": "person" },
|
|
135
|
+
"target": {
|
|
136
|
+
"id": "https://example.org/holidays/2026.ics",
|
|
137
|
+
"type": "feed"
|
|
138
|
+
},
|
|
139
|
+
"object": {
|
|
140
|
+
"type": "event",
|
|
141
|
+
"startTime": "2026-08-01T00:00:00Z",
|
|
142
|
+
"endTime": "2026-09-01T00:00:00Z"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The collection `summary` is the feed's `X-WR-CALNAME` when present. Items use
|
|
148
|
+
the same shape as `query` results, with `updateSupported` always `false` and
|
|
149
|
+
no `etag`; each item's `id` is the feed URL plus a fragment derived from its
|
|
150
|
+
UID, so it is unique within the feed but cannot be used for `update` or
|
|
151
|
+
`delete`. The optional `object` filters by component type and overlap with a
|
|
152
|
+
UTC date range, following the CalDAV time-range rules. Recurring items are not
|
|
153
|
+
expanded: a recurring event is returned once, with its `recurrence` rule,
|
|
154
|
+
whenever the rule can still produce an occurrence inside the range, and
|
|
155
|
+
rescheduled occurrences appear as additional items. Components the platform
|
|
156
|
+
cannot represent (for example an event without a summary) are skipped and
|
|
157
|
+
logged rather than failing the whole feed. An all-day event without an end
|
|
158
|
+
lasts one day when matching a range. Feeds larger than 10 MiB are rejected,
|
|
159
|
+
and the same HTTPS and private-address policies apply as for CalDAV servers,
|
|
160
|
+
including on every redirect.
|
|
161
|
+
|
|
116
162
|
### Create an event
|
|
117
163
|
|
|
118
164
|
```json
|
|
@@ -233,6 +279,7 @@ administrator must explicitly enable `allowPrivateAddresses` or
|
|
|
233
279
|
| Calendar listing | Supported | Discovers accessible calendars in the user's calendar home and their advertised `VEVENT`/`VTODO` component support. |
|
|
234
280
|
| Events and tasks | Supported | Reads and writes `VEVENT` and `VTODO`. `VJOURNAL` and other component types are not supported. |
|
|
235
281
|
| Calendar queries | Supported | Supports component and UTC time-range filters. Without a range, a query returns a calendar snapshot. |
|
|
282
|
+
| Published iCalendar feeds | Read-only | Reads `.ics` and `webcal://` subscription links without credentials, with component and time-range filters. Recurrences are not expanded. |
|
|
236
283
|
| Conditional create, update, and delete | Supported | Uses ETags with `If-None-Match` and `If-Match` to prevent accidental overwrites. |
|
|
237
284
|
| Recurrence | Partially supported | Common recurrence rules are parsed and generated. Resources with recurrence exceptions, exclusion dates, or multiple primary components are readable but rejected for update when rewriting could lose data. |
|
|
238
285
|
| Scheduling data | Partially supported | Organizer and attendee properties are represented, but CalDAV scheduling inbox/outbox delivery, invitation responses, and server-side email are not implemented. |
|
|
@@ -251,4 +298,5 @@ Stable errors include `caldav:authentication-failed`,
|
|
|
251
298
|
`caldav:unsupported-authentication`,
|
|
252
299
|
`caldav:connection-failed`, `caldav:invalid-calendar`,
|
|
253
300
|
`caldav:invalid-resource`, `caldav:unsupported-component`,
|
|
254
|
-
`caldav:conflict`,
|
|
301
|
+
`caldav:conflict`, `caldav:not-found`, `caldav:invalid-feed`, and
|
|
302
|
+
`caldav:feed-failed`.
|
package/dist/ics.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CalendarItem, QueryInput } from "./types.js";
|
|
2
|
+
export interface ICalendarFeed {
|
|
3
|
+
name?: string;
|
|
4
|
+
items: CalendarItem[];
|
|
5
|
+
skipped: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Whether an item overlaps the requested time range, following the CalDAV
|
|
9
|
+
* time-range rules: an item matches when it starts before the range ends and
|
|
10
|
+
* ends after the range starts (a zero-length item matches when it starts at
|
|
11
|
+
* or after the range start). Recurring items are never expanded: they match
|
|
12
|
+
* while the rule can still produce an occurrence inside the range: a bounded
|
|
13
|
+
* rule (UNTIL or COUNT) stops matching once its last possible occurrence,
|
|
14
|
+
* including the master duration, has ended. Items without any time match
|
|
15
|
+
* only when no range is given. An all-day event with no end lasts one day
|
|
16
|
+
* (RFC 5545 section 3.6.1).
|
|
17
|
+
*/
|
|
18
|
+
export declare function matchesRange(item: CalendarItem, query: QueryInput): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Parse every event and task in a published iCalendar file (an `.ics`
|
|
21
|
+
* subscription) into the item model used by CalDAV queries. Components the
|
|
22
|
+
* parser cannot represent are counted in `skipped` rather than failing the
|
|
23
|
+
* whole feed, since subscriptions are read-only and often third-party.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseICalendarFeed(body: string, sourceUrl: string, query?: QueryInput): ICalendarFeed;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
import type { ActivityStream, PlatformCallback, PlatformInterface, PlatformSchemaStruct, PlatformSession, StatelessPlatformConfig } from "@sockethub/schemas";
|
|
2
|
+
import { type Agent } from "undici";
|
|
2
3
|
import type { CalDavCredentials } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* `fetch` that honours an undici `dispatcher`. Defaults to undici's own
|
|
6
|
+
* `fetch` so the request and the guarded dispatcher come from the same build.
|
|
7
|
+
*/
|
|
8
|
+
type FeedFetch = (url: URL, init: RequestInit & {
|
|
9
|
+
dispatcher?: Agent;
|
|
10
|
+
}) => Promise<Response>;
|
|
3
11
|
export declare function assertCalendarResource(calendarId: string, resourceId: string): void;
|
|
4
12
|
export default class CalDav implements PlatformInterface {
|
|
5
13
|
private readonly log;
|
|
14
|
+
private readonly fetchImpl;
|
|
15
|
+
private feedDispatcher?;
|
|
6
16
|
config: StatelessPlatformConfig;
|
|
7
|
-
constructor(session: PlatformSession);
|
|
17
|
+
constructor(session: PlatformSession, fetchImpl?: FeedFetch);
|
|
8
18
|
get schema(): PlatformSchemaStruct;
|
|
9
19
|
isInitialized(): boolean;
|
|
10
20
|
cleanup(done: PlatformCallback): void;
|
|
11
21
|
fetch(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
22
|
+
/**
|
|
23
|
+
* Read a published iCalendar (`.ics`) file such as a holiday calendar or a
|
|
24
|
+
* read-only subscription link. No CalDAV account is involved, so this
|
|
25
|
+
* action takes no credentials; `webcal:` links are fetched over HTTPS.
|
|
26
|
+
*/
|
|
27
|
+
read(job: ActivityStream, done: PlatformCallback): void;
|
|
12
28
|
create(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
13
29
|
query(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
14
30
|
update(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
15
31
|
delete(job: ActivityStream, credentials: CalDavCredentials, done: PlatformCallback): void;
|
|
16
32
|
private client;
|
|
33
|
+
/**
|
|
34
|
+
* The SSRF-guarded dispatcher for feed reads, created once per instance
|
|
35
|
+
* (its policy comes from packageConfig, fixed before the first job) so
|
|
36
|
+
* connections are pooled across reads.
|
|
37
|
+
*/
|
|
38
|
+
private dispatcher;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch a feed, following redirects by hand so every hop is re-checked
|
|
41
|
+
* against the scheme policy; the guarded dispatcher only vets addresses.
|
|
42
|
+
*/
|
|
43
|
+
private fetchFeed;
|
|
44
|
+
private feedUrl;
|
|
17
45
|
private calendar;
|
|
18
46
|
private assertResource;
|
|
19
47
|
private mutationResponse;
|