// Shared wire shapes for the /calendar and /contacts workspaces. // // These mirror what the officer-caldav sidecar returns from its JSON door under /api/caldav/_officer/* — // see src/servers/sidecar/caldav/{collections,ical}.ts, which is where they are produced. The sidecar // parses the iCalendar/vCard itself so nothing here ever sees DAV XML. /** One calendar or address book. `path` addresses it over DAV; `name` is what the URL selects it by. */ export type DavCollection = { /** The DAV path, `/dav///`. What every API call takes. */ path: string; /** The last segment of that path. Unique within a user, and the only part worth putting in a URL. */ name: string; displayName: string; kind: 'calendar' | 'addressbook'; color?: string; }; export type CalendarEvent = { uid: string; /** The DAV href — where this event lives. What edit and delete address; never derived from the uid. */ href?: string; summary: string; description?: string; location?: string; start?: string; end?: string; allDay: boolean; /** Raw RRULE, unexpanded — the sidecar does not expand a series, so this is a badge, not occurrences. */ rrule?: string; status?: string; }; export type DavContact = { uid: string; /** The DAV href — where this contact lives. What edit and delete address; never derived from the uid. */ href?: string; fullName: string; emails: string[]; phones: string[]; org?: string; title?: string; hasPhoto: boolean; }; /** * The query param both screens select with. Per the navigation rules the selection lives in the URL, and * a query param rather than a path segment because the detail IS the screen — there is nothing to route to. * * IT CARRIES THE NAME, NOT THE PATH. `?collection=personal`, not * `?collection=%2Fdav%2F1%2Fpersonal%2F`. The path is `/dav///`, so the escaped version * spent six characters on slashes to say `personal` and put the owner's user id in a URL people paste at * each other. The name is unique within a user, which is all a selector needs; the full path is * recovered from the collections list, which the screen has already loaded. */ export const COLLECTION_PARAM = 'collection'; /** The one place the two screens' URLs are spelled, so nav links and guards cannot drift. */ export const davCollectionPath = (base: '/calendar' | '/contacts', name: string) => `${base}?${COLLECTION_PARAM}=${encodeURIComponent(name)}`;