@remit/calendar-service 0.0.18 → 0.0.20
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/dist/accept.d.ts +71 -0
- package/dist/accept.js +159 -0
- package/dist/build.d.ts +79 -0
- package/dist/build.js +196 -0
- package/dist/correct.d.ts +4 -0
- package/dist/correct.js +22 -0
- package/dist/errors.d.ts +56 -0
- package/dist/errors.js +1 -0
- package/dist/etag.d.ts +9 -0
- package/{src/etag.ts → dist/etag.js} +1 -3
- package/dist/expand.d.ts +73 -0
- package/dist/expand.js +188 -0
- package/dist/feed.d.ts +81 -0
- package/dist/feed.js +157 -0
- package/dist/fixtures.d.ts +19 -0
- package/dist/fixtures.js +37 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +15 -0
- package/dist/memory-store.d.ts +105 -0
- package/dist/memory-store.js +319 -0
- package/dist/parse.d.ts +39 -0
- package/dist/parse.js +87 -0
- package/dist/project.d.ts +30 -0
- package/dist/project.js +57 -0
- package/dist/put.d.ts +52 -0
- package/dist/put.js +78 -0
- package/dist/reopen.d.ts +14 -0
- package/dist/reopen.js +92 -0
- package/dist/scope.d.ts +70 -0
- package/dist/scope.js +687 -0
- package/dist/subscribe.d.ts +96 -0
- package/dist/subscribe.js +298 -0
- package/dist/suggest.d.ts +74 -0
- package/dist/suggest.js +138 -0
- package/dist/time.d.ts +76 -0
- package/dist/time.js +164 -0
- package/dist/window.d.ts +85 -0
- package/dist/window.js +161 -0
- package/package.json +40 -35
- package/src/accept.test.ts +0 -403
- package/src/accept.ts +0 -250
- package/src/build.test.ts +0 -113
- package/src/build.ts +0 -311
- package/src/correct.test.ts +0 -173
- package/src/correct.ts +0 -40
- package/src/errors.ts +0 -62
- package/src/etag.test.ts +0 -54
- package/src/expand.test.ts +0 -361
- package/src/expand.ts +0 -303
- package/src/feed.test.ts +0 -334
- package/src/feed.ts +0 -203
- package/src/feeds/google-secret-address.ics +0 -99
- package/src/fixtures.ts +0 -52
- package/src/index.ts +0 -128
- package/src/memory-store.ts +0 -485
- package/src/parse.test.ts +0 -264
- package/src/parse.ts +0 -138
- package/src/project.test.ts +0 -247
- package/src/project.ts +0 -122
- package/src/put.test.ts +0 -318
- package/src/put.ts +0 -133
- package/src/reopen.test.ts +0 -529
- package/src/reopen.ts +0 -199
- package/src/scope.test.ts +0 -1421
- package/src/scope.ts +0 -1003
- package/src/subscribe.test.ts +0 -582
- package/src/subscribe.ts +0 -433
- package/src/suggest.test.ts +0 -376
- package/src/suggest.ts +0 -244
- package/src/time.ts +0 -260
- package/src/window.test.ts +0 -648
- package/src/window.ts +0 -281
- package/tsconfig.json +0 -8
package/src/feed.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
import type {
|
|
3
|
-
CalendarCollectionItem,
|
|
4
|
-
CalendarObjectItem,
|
|
5
|
-
} from "@remit/data-ports";
|
|
6
|
-
import ICAL from "ical.js";
|
|
7
|
-
import { CALENDAR_PRODID } from "./build.js";
|
|
8
|
-
import { computeEtag } from "./etag.js";
|
|
9
|
-
import { serializeCalendar } from "./parse.js";
|
|
10
|
-
|
|
11
|
-
/** Random bytes behind a feed token (issue #1067). */
|
|
12
|
-
export const CALENDAR_FEED_TOKEN_BYTES = 32;
|
|
13
|
-
|
|
14
|
-
/** Where a feed lives, as the one place the shape of that path is written. */
|
|
15
|
-
export const CALENDAR_FEED_PATH_PREFIX = "/feeds/calendar/";
|
|
16
|
-
export const CALENDAR_FEED_PATH_SUFFIX = ".ics";
|
|
17
|
-
|
|
18
|
-
/** base64url over `CALENDAR_FEED_TOKEN_BYTES`, so the length is fixed. */
|
|
19
|
-
const TOKEN_LENGTH = Math.ceil((CALENDAR_FEED_TOKEN_BYTES * 4) / 3);
|
|
20
|
-
const TOKEN_SHAPE = new RegExp(`^[A-Za-z0-9_-]{${TOKEN_LENGTH}}$`);
|
|
21
|
-
|
|
22
|
-
/** A minted feed address: the secret, handed out once, and what is stored. */
|
|
23
|
-
export interface CalendarFeedSecret {
|
|
24
|
-
token: string;
|
|
25
|
-
tokenHash: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const hashCalendarFeedToken = (token: string): string =>
|
|
29
|
-
createHash("sha256").update(token, "utf8").digest("hex");
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* A fresh feed token and the hash that will stand in for it.
|
|
33
|
-
*
|
|
34
|
-
* 32 random bytes rather than a stretched password: nothing about this value is
|
|
35
|
-
* user-chosen, so there is no dictionary to search and a slow hash would only
|
|
36
|
-
* tax every poll of every subscribed client.
|
|
37
|
-
*/
|
|
38
|
-
export const mintCalendarFeedToken = (): CalendarFeedSecret => {
|
|
39
|
-
const token = randomBytes(CALENDAR_FEED_TOKEN_BYTES).toString("base64url");
|
|
40
|
-
return { token, tokenHash: hashCalendarFeedToken(token) };
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Whether a path segment can be a feed token at all.
|
|
45
|
-
*
|
|
46
|
-
* Checked before anything is hashed or read, so a request carrying a megabyte
|
|
47
|
-
* of path or a `../` never reaches the store. A refusal here is the same 404 a
|
|
48
|
-
* revoked token gets — the caller learns nothing from the difference.
|
|
49
|
-
*/
|
|
50
|
-
export const isCalendarFeedToken = (token: string): boolean =>
|
|
51
|
-
TOKEN_SHAPE.test(token);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* The token a request path carries, or `null` when the path is not a feed
|
|
55
|
-
* address. One segment only: a slash inside it would name a different route.
|
|
56
|
-
*/
|
|
57
|
-
export const readCalendarFeedToken = (path: string): string | null => {
|
|
58
|
-
if (!path.startsWith(CALENDAR_FEED_PATH_PREFIX)) return null;
|
|
59
|
-
if (!path.endsWith(CALENDAR_FEED_PATH_SUFFIX)) return null;
|
|
60
|
-
const token = path.slice(
|
|
61
|
-
CALENDAR_FEED_PATH_PREFIX.length,
|
|
62
|
-
-CALENDAR_FEED_PATH_SUFFIX.length,
|
|
63
|
-
);
|
|
64
|
-
if (token.includes("/")) return null;
|
|
65
|
-
return token;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* The feed path with its token taken out, for a log line or a metric label.
|
|
70
|
-
*
|
|
71
|
-
* The token is the whole credential, so a path that carries one is not a field
|
|
72
|
-
* that can be recorded. Redacted rather than dropped: which route was served is
|
|
73
|
-
* what an operator reads a log for, and a blank path loses that too.
|
|
74
|
-
*/
|
|
75
|
-
export const redactCalendarFeedPath = (path: string): string =>
|
|
76
|
-
readCalendarFeedToken(path) === null
|
|
77
|
-
? path
|
|
78
|
-
: `${CALENDAR_FEED_PATH_PREFIX}<redacted>${CALENDAR_FEED_PATH_SUFFIX}`;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* A TEXT value as RFC 5545 3.3.11 writes one.
|
|
82
|
-
*
|
|
83
|
-
* ical.js escapes TEXT only for the properties in its design set, and no `X-`
|
|
84
|
-
* property is in it, so a value goes out verbatim: a comma or a semicolon
|
|
85
|
-
* reads back as a value list, and a line break as the start of a whole
|
|
86
|
-
* component the subscriber never had. TEXT has no encoding for a control
|
|
87
|
-
* character, so one is dropped rather than served.
|
|
88
|
-
*/
|
|
89
|
-
const escapeIcalText = (value: string): string =>
|
|
90
|
-
value
|
|
91
|
-
.replace(/\\/g, "\\\\")
|
|
92
|
-
.replace(/;/g, "\\;")
|
|
93
|
-
.replace(/,/g, "\\,")
|
|
94
|
-
.replace(/\r\n|\r|\n/g, "\\n")
|
|
95
|
-
// biome-ignore lint/suspicious/noControlCharactersInRegex: RFC 5545 TEXT excludes CONTROL.
|
|
96
|
-
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
|
|
97
|
-
|
|
98
|
-
/** The bytes a feed serves, and what a conditional request needs to skip them. */
|
|
99
|
-
export interface CalendarFeed {
|
|
100
|
-
icalData: string;
|
|
101
|
-
etag: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* One VCALENDAR carrying every event in a collection.
|
|
106
|
-
*
|
|
107
|
-
* The stored components are copied across as they are: a recurring event
|
|
108
|
-
* travels as its master plus its RRULE, never as an expansion, because a
|
|
109
|
-
* subscriber renders occurrences itself and expects to keep doing so past any
|
|
110
|
-
* horizon this server would have chosen. VTIMEZONE definitions come along for
|
|
111
|
-
* the same reason — without them a client reads a TZID it cannot resolve.
|
|
112
|
-
*
|
|
113
|
-
* `X-WR-CALNAME` is a property of the calendar, not of the response: Apple
|
|
114
|
-
* Calendar, Google Calendar and Thunderbird all name a subscription from it.
|
|
115
|
-
*/
|
|
116
|
-
export const buildCalendarFeed = (
|
|
117
|
-
collection: CalendarCollectionItem,
|
|
118
|
-
objects: readonly CalendarObjectItem[],
|
|
119
|
-
): CalendarFeed => {
|
|
120
|
-
const feed = new ICAL.Component("vcalendar");
|
|
121
|
-
feed.updatePropertyWithValue("prodid", CALENDAR_PRODID);
|
|
122
|
-
feed.updatePropertyWithValue("version", "2.0");
|
|
123
|
-
feed.updatePropertyWithValue("calscale", "GREGORIAN");
|
|
124
|
-
feed.updatePropertyWithValue(
|
|
125
|
-
"x-wr-calname",
|
|
126
|
-
escapeIcalText(collection.displayName),
|
|
127
|
-
);
|
|
128
|
-
if (collection.timezone !== "") {
|
|
129
|
-
feed.updatePropertyWithValue(
|
|
130
|
-
"x-wr-timezone",
|
|
131
|
-
escapeIcalText(collection.timezone),
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const zonesSeen = new Set<string>();
|
|
136
|
-
for (const object of objects) {
|
|
137
|
-
const stored = new ICAL.Component(ICAL.parse(object.icalData));
|
|
138
|
-
// A copy of the list, not the list: adding a component reparents it,
|
|
139
|
-
// which splices it out of the array `getAllSubcomponents` handed back and
|
|
140
|
-
// would drop whatever followed it.
|
|
141
|
-
for (const child of [...stored.getAllSubcomponents()]) {
|
|
142
|
-
if (child.name === "vtimezone") {
|
|
143
|
-
const tzid = child.getFirstPropertyValue("tzid");
|
|
144
|
-
const key = typeof tzid === "string" ? tzid : "";
|
|
145
|
-
if (zonesSeen.has(key)) continue;
|
|
146
|
-
zonesSeen.add(key);
|
|
147
|
-
}
|
|
148
|
-
feed.addSubcomponent(child);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ical.js leaves the last content line unterminated. Every line in an
|
|
153
|
-
// iCalendar stream ends with CRLF (RFC 5545 3.1), END:VCALENDAR included.
|
|
154
|
-
const serialized = serializeCalendar(feed);
|
|
155
|
-
const icalData = serialized.endsWith("\r\n")
|
|
156
|
-
? serialized
|
|
157
|
-
: `${serialized}\r\n`;
|
|
158
|
-
return { icalData, etag: computeEtag(icalData) };
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Whether an `If-None-Match` covers the tag the feed would serve.
|
|
163
|
-
*
|
|
164
|
-
* Accepts the weak form and `*`, and reads the header as the list RFC 9110
|
|
165
|
-
* 13.1.2 says it is — a subscriber that has polled through a proxy may get its
|
|
166
|
-
* own tag back with a `W/` on it, and answering 200 to that resends the whole
|
|
167
|
-
* calendar for nothing.
|
|
168
|
-
*/
|
|
169
|
-
export const calendarFeedIsUnchanged = (
|
|
170
|
-
ifNoneMatch: string | undefined,
|
|
171
|
-
etag: string,
|
|
172
|
-
): boolean => {
|
|
173
|
-
if (ifNoneMatch === undefined || ifNoneMatch === "") return false;
|
|
174
|
-
if (ifNoneMatch.trim() === "*") return true;
|
|
175
|
-
return ifNoneMatch
|
|
176
|
-
.split(",")
|
|
177
|
-
.map((candidate) =>
|
|
178
|
-
candidate.trim().replace(/^W\//, "").replace(/^"|"$/g, ""),
|
|
179
|
-
)
|
|
180
|
-
.includes(etag);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Whether the calendar has stood still since the date an `If-Modified-Since`
|
|
185
|
-
* carries.
|
|
186
|
-
*
|
|
187
|
-
* Compared at whole seconds, which is the resolution an HTTP-date has: a
|
|
188
|
-
* `Last-Modified` served from a millisecond timestamp is rounded down on the
|
|
189
|
-
* way out, and comparing the unrounded value against what comes back declares
|
|
190
|
-
* every calendar modified within the second it was last written.
|
|
191
|
-
*
|
|
192
|
-
* An unreadable date is not a condition (RFC 9110 13.1.3) — the full calendar
|
|
193
|
-
* is served rather than a 304 nobody asked for.
|
|
194
|
-
*/
|
|
195
|
-
export const calendarFeedIsUnmodifiedSince = (
|
|
196
|
-
ifModifiedSince: string | undefined,
|
|
197
|
-
lastModifiedAt: number,
|
|
198
|
-
): boolean => {
|
|
199
|
-
if (ifModifiedSince === undefined || ifModifiedSince === "") return false;
|
|
200
|
-
const since = Date.parse(ifModifiedSince);
|
|
201
|
-
if (Number.isNaN(since)) return false;
|
|
202
|
-
return Math.floor(lastModifiedAt / 1000) * 1000 <= since;
|
|
203
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
BEGIN:VCALENDAR
|
|
2
|
-
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
|
3
|
-
VERSION:2.0
|
|
4
|
-
CALSCALE:GREGORIAN
|
|
5
|
-
METHOD:PUBLISH
|
|
6
|
-
X-WR-CALNAME:Harbour rota
|
|
7
|
-
X-WR-TIMEZONE:Europe/Amsterdam
|
|
8
|
-
BEGIN:VTIMEZONE
|
|
9
|
-
TZID:Europe/Amsterdam
|
|
10
|
-
X-LIC-LOCATION:Europe/Amsterdam
|
|
11
|
-
BEGIN:DAYLIGHT
|
|
12
|
-
TZOFFSETFROM:+0100
|
|
13
|
-
TZOFFSETTO:+0200
|
|
14
|
-
TZNAME:CEST
|
|
15
|
-
DTSTART:19700329T020000
|
|
16
|
-
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
17
|
-
END:DAYLIGHT
|
|
18
|
-
BEGIN:STANDARD
|
|
19
|
-
TZOFFSETFROM:+0200
|
|
20
|
-
TZOFFSETTO:+0100
|
|
21
|
-
TZNAME:CET
|
|
22
|
-
DTSTART:19701025T030000
|
|
23
|
-
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
24
|
-
END:STANDARD
|
|
25
|
-
END:VTIMEZONE
|
|
26
|
-
BEGIN:VTIMEZONE
|
|
27
|
-
TZID:America/New_York
|
|
28
|
-
BEGIN:DAYLIGHT
|
|
29
|
-
TZOFFSETFROM:-0500
|
|
30
|
-
TZOFFSETTO:-0400
|
|
31
|
-
TZNAME:EDT
|
|
32
|
-
DTSTART:19700308T020000
|
|
33
|
-
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
|
34
|
-
END:DAYLIGHT
|
|
35
|
-
BEGIN:STANDARD
|
|
36
|
-
TZOFFSETFROM:-0400
|
|
37
|
-
TZOFFSETTO:-0500
|
|
38
|
-
TZNAME:EST
|
|
39
|
-
DTSTART:19701101T020000
|
|
40
|
-
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
|
41
|
-
END:STANDARD
|
|
42
|
-
END:VTIMEZONE
|
|
43
|
-
BEGIN:VEVENT
|
|
44
|
-
DTSTART;TZID=Europe/Amsterdam:20261005T090000
|
|
45
|
-
DTEND;TZID=Europe/Amsterdam:20261005T093000
|
|
46
|
-
RRULE:FREQ=WEEKLY;COUNT=6;BYDAY=MO
|
|
47
|
-
EXDATE;TZID=Europe/Amsterdam:20261019T090000
|
|
48
|
-
DTSTAMP:20260925T120000Z
|
|
49
|
-
UID:pilot-standup@google.com
|
|
50
|
-
CREATED:20260901T080000Z
|
|
51
|
-
LAST-MODIFIED:20260920T080000Z
|
|
52
|
-
SEQUENCE:0
|
|
53
|
-
STATUS:CONFIRMED
|
|
54
|
-
SUMMARY:Pilot standup
|
|
55
|
-
TRANSP:OPAQUE
|
|
56
|
-
END:VEVENT
|
|
57
|
-
BEGIN:VEVENT
|
|
58
|
-
DTSTART;TZID=Europe/Amsterdam:20261012T100000
|
|
59
|
-
DTEND;TZID=Europe/Amsterdam:20261012T103000
|
|
60
|
-
DTSTAMP:20260925T120000Z
|
|
61
|
-
UID:pilot-standup@google.com
|
|
62
|
-
RECURRENCE-ID;TZID=Europe/Amsterdam:20261012T090000
|
|
63
|
-
SEQUENCE:1
|
|
64
|
-
STATUS:CONFIRMED
|
|
65
|
-
SUMMARY:Pilot standup (moved)
|
|
66
|
-
TRANSP:OPAQUE
|
|
67
|
-
END:VEVENT
|
|
68
|
-
BEGIN:VEVENT
|
|
69
|
-
DTSTART;TZID=Europe/Amsterdam:20261102T090000
|
|
70
|
-
DTEND;TZID=Europe/Amsterdam:20261102T093000
|
|
71
|
-
DTSTAMP:20260925T120000Z
|
|
72
|
-
UID:pilot-standup@google.com
|
|
73
|
-
RECURRENCE-ID;TZID=Europe/Amsterdam:20261102T090000
|
|
74
|
-
SEQUENCE:1
|
|
75
|
-
STATUS:CANCELLED
|
|
76
|
-
SUMMARY:Pilot standup
|
|
77
|
-
TRANSP:OPAQUE
|
|
78
|
-
END:VEVENT
|
|
79
|
-
BEGIN:VEVENT
|
|
80
|
-
DTSTART;VALUE=DATE:20261007
|
|
81
|
-
DTEND;VALUE=DATE:20261009
|
|
82
|
-
DTSTAMP:20260925T120000Z
|
|
83
|
-
UID:quay-offsite@google.com
|
|
84
|
-
SEQUENCE:0
|
|
85
|
-
STATUS:CONFIRMED
|
|
86
|
-
SUMMARY:Quay offsite
|
|
87
|
-
TRANSP:TRANSPARENT
|
|
88
|
-
END:VEVENT
|
|
89
|
-
BEGIN:VEVENT
|
|
90
|
-
DTSTART:20261008T140000Z
|
|
91
|
-
DTEND:20261008T150000Z
|
|
92
|
-
DTSTAMP:20260925T120000Z
|
|
93
|
-
UID:tide-review@google.com
|
|
94
|
-
SEQUENCE:0
|
|
95
|
-
STATUS:CONFIRMED
|
|
96
|
-
SUMMARY:Tide table review
|
|
97
|
-
TRANSP:OPAQUE
|
|
98
|
-
END:VEVENT
|
|
99
|
-
END:VCALENDAR
|
package/src/fixtures.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* iCalendar fixtures, assembled from lines so the CRLF the RFC requires is
|
|
3
|
-
* explicit rather than a property of this file's own line endings.
|
|
4
|
-
*/
|
|
5
|
-
export const ical = (...lines: string[]): string => `${lines.join("\r\n")}\r\n`;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The same resource as a client that ignores RFC 5545 3.1 would send it. Real
|
|
9
|
-
* `.ics` files arrive LF-only often enough — anything that has been through a
|
|
10
|
-
* text-mode checkout, an editor, or a naive copy — that reading them is part of
|
|
11
|
-
* the job, not an edge case.
|
|
12
|
-
*/
|
|
13
|
-
export const asLf = (icalData: string): string =>
|
|
14
|
-
icalData.replace(/\r\n/g, "\n");
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Europe/Amsterdam as a client writes it into a resource. Present so a
|
|
18
|
-
* DST-crossing series resolves against the definition the resource itself
|
|
19
|
-
* carries, which is the only zone data RFC 5545 promises a server.
|
|
20
|
-
*/
|
|
21
|
-
export const AMSTERDAM_VTIMEZONE = [
|
|
22
|
-
"BEGIN:VTIMEZONE",
|
|
23
|
-
"TZID:Europe/Amsterdam",
|
|
24
|
-
"BEGIN:DAYLIGHT",
|
|
25
|
-
"TZOFFSETFROM:+0100",
|
|
26
|
-
"TZOFFSETTO:+0200",
|
|
27
|
-
"TZNAME:CEST",
|
|
28
|
-
"DTSTART:19700329T020000",
|
|
29
|
-
"RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU",
|
|
30
|
-
"END:DAYLIGHT",
|
|
31
|
-
"BEGIN:STANDARD",
|
|
32
|
-
"TZOFFSETFROM:+0200",
|
|
33
|
-
"TZOFFSETTO:+0100",
|
|
34
|
-
"TZNAME:CET",
|
|
35
|
-
"DTSTART:19701025T030000",
|
|
36
|
-
"RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU",
|
|
37
|
-
"END:STANDARD",
|
|
38
|
-
"END:VTIMEZONE",
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
export const singleEvent = (...eventLines: string[]): string =>
|
|
42
|
-
ical(
|
|
43
|
-
"BEGIN:VCALENDAR",
|
|
44
|
-
"VERSION:2.0",
|
|
45
|
-
"PRODID:-//Remit//Calendar Tests//EN",
|
|
46
|
-
"BEGIN:VEVENT",
|
|
47
|
-
"UID:fixture@example.com",
|
|
48
|
-
"DTSTAMP:20260801T090000Z",
|
|
49
|
-
...eventLines,
|
|
50
|
-
"END:VEVENT",
|
|
51
|
-
"END:VCALENDAR",
|
|
52
|
-
);
|
package/src/index.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
type AcceptCalendarSuggestionInput,
|
|
3
|
-
type AcceptedCalendarSuggestion,
|
|
4
|
-
acceptCalendarSuggestion,
|
|
5
|
-
buildAcceptedCalendar,
|
|
6
|
-
} from "./accept.js";
|
|
7
|
-
export {
|
|
8
|
-
applyEventFields,
|
|
9
|
-
buildEventCalendar,
|
|
10
|
-
CALENDAR_PRODID,
|
|
11
|
-
type CalendarEventFields,
|
|
12
|
-
eventTimeFields,
|
|
13
|
-
OFFSET_BEARING,
|
|
14
|
-
readEventTime,
|
|
15
|
-
readRecurrenceRule,
|
|
16
|
-
} from "./build.js";
|
|
17
|
-
export { correctCalendarSuggestion } from "./correct.js";
|
|
18
|
-
export type {
|
|
19
|
-
CalendarResult,
|
|
20
|
-
CalendarValidationCode,
|
|
21
|
-
CalendarValidationError,
|
|
22
|
-
} from "./errors.js";
|
|
23
|
-
export { computeEtag } from "./etag.js";
|
|
24
|
-
export {
|
|
25
|
-
CALENDAR_EXPANSION_HORIZON_DAYS,
|
|
26
|
-
CALENDAR_EXPANSION_MAX_OCCURRENCES,
|
|
27
|
-
CALENDAR_WINDOW_MAX_STEPS,
|
|
28
|
-
type CalendarExpansion,
|
|
29
|
-
expandCalendar,
|
|
30
|
-
expandCalendarWindow,
|
|
31
|
-
} from "./expand.js";
|
|
32
|
-
export {
|
|
33
|
-
buildCalendarFeed,
|
|
34
|
-
CALENDAR_FEED_PATH_PREFIX,
|
|
35
|
-
CALENDAR_FEED_PATH_SUFFIX,
|
|
36
|
-
CALENDAR_FEED_TOKEN_BYTES,
|
|
37
|
-
type CalendarFeed,
|
|
38
|
-
type CalendarFeedSecret,
|
|
39
|
-
calendarFeedIsUnchanged,
|
|
40
|
-
calendarFeedIsUnmodifiedSince,
|
|
41
|
-
hashCalendarFeedToken,
|
|
42
|
-
isCalendarFeedToken,
|
|
43
|
-
mintCalendarFeedToken,
|
|
44
|
-
readCalendarFeedToken,
|
|
45
|
-
redactCalendarFeedPath,
|
|
46
|
-
} from "./feed.js";
|
|
47
|
-
export {
|
|
48
|
-
type ParsedCalendar,
|
|
49
|
-
parseCalendar,
|
|
50
|
-
serializeCalendar,
|
|
51
|
-
} from "./parse.js";
|
|
52
|
-
export {
|
|
53
|
-
type CalendarObjectProjection,
|
|
54
|
-
projectCalendar,
|
|
55
|
-
} from "./project.js";
|
|
56
|
-
export {
|
|
57
|
-
DEFAULT_CALENDAR_URL_SEGMENT,
|
|
58
|
-
deleteCalendarObject,
|
|
59
|
-
deleteCalendarObjectWrite,
|
|
60
|
-
type PutCalendarObjectInput,
|
|
61
|
-
prepareCalendarObject,
|
|
62
|
-
provisionDefaultCalendar,
|
|
63
|
-
putCalendarObject,
|
|
64
|
-
readWrittenObject,
|
|
65
|
-
} from "./put.js";
|
|
66
|
-
export {
|
|
67
|
-
answersOvertakenBy,
|
|
68
|
-
type ReopenCalendarSuggestionInput,
|
|
69
|
-
reopenCalendarSuggestion,
|
|
70
|
-
} from "./reopen.js";
|
|
71
|
-
export {
|
|
72
|
-
applyScopedDelete,
|
|
73
|
-
applyScopedUpdate,
|
|
74
|
-
findOccurrence,
|
|
75
|
-
followingUidOf,
|
|
76
|
-
followingUidPrefix,
|
|
77
|
-
type RecurrenceScopeValue,
|
|
78
|
-
type ScopedWrite,
|
|
79
|
-
type ScopedWriteInput,
|
|
80
|
-
} from "./scope.js";
|
|
81
|
-
export {
|
|
82
|
-
CALENDAR_SUBSCRIPTION_MAX_BYTES,
|
|
83
|
-
CALENDAR_SUBSCRIPTION_TIMEOUT_MS,
|
|
84
|
-
type CalendarFeedFetch,
|
|
85
|
-
type CalendarFeedFetcher,
|
|
86
|
-
type CalendarFeedResource,
|
|
87
|
-
type CalendarFeedSplit,
|
|
88
|
-
type CalendarFeedWrite,
|
|
89
|
-
type CalendarSubscriptionRefresh,
|
|
90
|
-
type CalendarSubscriptionTarget,
|
|
91
|
-
fetchCalendarFeed,
|
|
92
|
-
isSubscriptionDue,
|
|
93
|
-
readCalendarFeed,
|
|
94
|
-
readSubscriptionUrl,
|
|
95
|
-
recordCalendarFeedFailure,
|
|
96
|
-
refreshCalendarSubscription,
|
|
97
|
-
splitCalendarFeed,
|
|
98
|
-
writeCalendarFeed,
|
|
99
|
-
} from "./subscribe.js";
|
|
100
|
-
export {
|
|
101
|
-
type CalendarSuggestionProjection,
|
|
102
|
-
inviteMethodOf,
|
|
103
|
-
mailAddressOf,
|
|
104
|
-
projectSuggestion,
|
|
105
|
-
type RecordCalendarSuggestionInput,
|
|
106
|
-
type RecordedCalendarSuggestion,
|
|
107
|
-
recordCalendarSuggestion,
|
|
108
|
-
} from "./suggest.js";
|
|
109
|
-
export {
|
|
110
|
-
civilInZone,
|
|
111
|
-
isResolvableZone,
|
|
112
|
-
type ResolvedTime,
|
|
113
|
-
resolveTime,
|
|
114
|
-
toOffsetIso,
|
|
115
|
-
toUtcIso,
|
|
116
|
-
} from "./time.js";
|
|
117
|
-
export {
|
|
118
|
-
type BusySpan,
|
|
119
|
-
CALENDAR_WINDOW_LOOKBACK_DAYS,
|
|
120
|
-
type CalendarInstance,
|
|
121
|
-
type CalendarWindow,
|
|
122
|
-
type CalendarWindowRepositories,
|
|
123
|
-
isBusy,
|
|
124
|
-
listBusySpans,
|
|
125
|
-
listCalendarInstances,
|
|
126
|
-
listObjectInstances,
|
|
127
|
-
mergeBusySpans,
|
|
128
|
-
} from "./window.js";
|