@remit/calendar-service 0.0.18 → 0.0.19
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/etag.test.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { computeEtag } from "./etag.js";
|
|
4
|
-
import { singleEvent } from "./fixtures.js";
|
|
5
|
-
import { parseCalendar, serializeCalendar } from "./parse.js";
|
|
6
|
-
|
|
7
|
-
const RESOURCE = singleEvent(
|
|
8
|
-
"DTSTART:20260826T090000Z",
|
|
9
|
-
"DTEND:20260826T100000Z",
|
|
10
|
-
"SUMMARY:Quarterly review",
|
|
11
|
-
"X-MICROSOFT-CDO-BUSYSTATUS:BUSY",
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
const reserialize = async (icalData: string): Promise<string> => {
|
|
15
|
-
const parsed = await parseCalendar(icalData);
|
|
16
|
-
assert.ok(parsed.ok);
|
|
17
|
-
return serializeCalendar(parsed.value.component);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
describe("computeEtag", () => {
|
|
21
|
-
it("is a bare sha256 hex digest, unquoted", () => {
|
|
22
|
-
assert.match(computeEtag(RESOURCE), /^[0-9a-f]{64}$/);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("is stable across a parse and serialize", async () => {
|
|
26
|
-
const once = await reserialize(RESOURCE);
|
|
27
|
-
|
|
28
|
-
assert.equal(computeEtag(await reserialize(once)), computeEtag(once));
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("moves when the event changes", () => {
|
|
32
|
-
assert.notEqual(
|
|
33
|
-
computeEtag(RESOURCE),
|
|
34
|
-
computeEtag(
|
|
35
|
-
singleEvent(
|
|
36
|
-
"DTSTART:20260826T090000Z",
|
|
37
|
-
"DTEND:20260826T110000Z",
|
|
38
|
-
"SUMMARY:Quarterly review",
|
|
39
|
-
"X-MICROSOFT-CDO-BUSYSTATUS:BUSY",
|
|
40
|
-
),
|
|
41
|
-
),
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("hashes the stored bytes, so line endings are never normalized away", () => {
|
|
46
|
-
// Two resources that differ only in line endings are two different
|
|
47
|
-
// resources: the store keeps what it was given, and a tag computed over a
|
|
48
|
-
// normalized copy would report them as the same bytes.
|
|
49
|
-
assert.notEqual(
|
|
50
|
-
computeEtag(RESOURCE),
|
|
51
|
-
computeEtag(RESOURCE.replace(/\r\n/g, "\n")),
|
|
52
|
-
);
|
|
53
|
-
});
|
|
54
|
-
});
|
package/src/expand.test.ts
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { CalendarEventStatus, CalendarTransparency } from "@remit/domain-enums";
|
|
4
|
-
import {
|
|
5
|
-
CALENDAR_EXPANSION_HORIZON_DAYS,
|
|
6
|
-
CALENDAR_EXPANSION_MAX_OCCURRENCES,
|
|
7
|
-
expandCalendar,
|
|
8
|
-
} from "./expand.js";
|
|
9
|
-
import { AMSTERDAM_VTIMEZONE, ical, singleEvent } from "./fixtures.js";
|
|
10
|
-
import { parseCalendar } from "./parse.js";
|
|
11
|
-
|
|
12
|
-
const expand = async (icalData: string, timezone = "") => {
|
|
13
|
-
const parsed = await parseCalendar(icalData);
|
|
14
|
-
assert.ok(parsed.ok, `expected a parse, got ${JSON.stringify(parsed)}`);
|
|
15
|
-
return expandCalendar(parsed.value, timezone);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const amsterdamSeries = (...eventLines: string[]): string =>
|
|
19
|
-
ical(
|
|
20
|
-
"BEGIN:VCALENDAR",
|
|
21
|
-
"VERSION:2.0",
|
|
22
|
-
...AMSTERDAM_VTIMEZONE,
|
|
23
|
-
"BEGIN:VEVENT",
|
|
24
|
-
"UID:weekly@example.com",
|
|
25
|
-
"DTSTART;TZID=Europe/Amsterdam:20261015T090000",
|
|
26
|
-
"DTEND;TZID=Europe/Amsterdam:20261015T100000",
|
|
27
|
-
...eventLines,
|
|
28
|
-
"END:VEVENT",
|
|
29
|
-
"END:VCALENDAR",
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
describe("expandCalendar", () => {
|
|
33
|
-
it("writes a single occurrence under an empty recurrenceId", async () => {
|
|
34
|
-
const expansion = await expand(
|
|
35
|
-
singleEvent("DTSTART:20260826T090000Z", "DTEND:20260826T100000Z"),
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
assert.deepEqual(expansion.occurrences, [
|
|
39
|
-
{
|
|
40
|
-
recurrenceId: "",
|
|
41
|
-
startAt: "2026-08-26T09:00:00Z",
|
|
42
|
-
endAt: "2026-08-26T10:00:00Z",
|
|
43
|
-
allDay: false,
|
|
44
|
-
summary: "",
|
|
45
|
-
status: CalendarEventStatus.Confirmed,
|
|
46
|
-
transparency: CalendarTransparency.Opaque,
|
|
47
|
-
},
|
|
48
|
-
]);
|
|
49
|
-
assert.equal(expansion.expandedThrough, "");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("keeps a weekly series at its local hour across the end of DST", async () => {
|
|
53
|
-
// 09:00 in Amsterdam is 07:00Z until the clocks go back on 2026-10-25 and
|
|
54
|
-
// 08:00Z after. An expansion that iterated instants instead of local times
|
|
55
|
-
// would move the meeting an hour for half the year.
|
|
56
|
-
const expansion = await expand(
|
|
57
|
-
amsterdamSeries("RRULE:FREQ=WEEKLY;COUNT=4"),
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
assert.deepEqual(
|
|
61
|
-
expansion.occurrences.map((occurrence) => occurrence.startAt),
|
|
62
|
-
[
|
|
63
|
-
"2026-10-15T07:00:00Z",
|
|
64
|
-
"2026-10-22T07:00:00Z",
|
|
65
|
-
"2026-10-29T08:00:00Z",
|
|
66
|
-
"2026-11-05T08:00:00Z",
|
|
67
|
-
],
|
|
68
|
-
);
|
|
69
|
-
assert.equal(expansion.expandedThrough, "");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("keeps the same hour when the zone is named but not defined in the resource", async () => {
|
|
73
|
-
const expansion = await expand(
|
|
74
|
-
ical(
|
|
75
|
-
"BEGIN:VCALENDAR",
|
|
76
|
-
"VERSION:2.0",
|
|
77
|
-
"BEGIN:VEVENT",
|
|
78
|
-
"UID:weekly@example.com",
|
|
79
|
-
"DTSTART;TZID=Europe/Berlin:20261015T090000",
|
|
80
|
-
"DTEND;TZID=Europe/Berlin:20261015T100000",
|
|
81
|
-
"RRULE:FREQ=WEEKLY;COUNT=4",
|
|
82
|
-
"END:VEVENT",
|
|
83
|
-
"END:VCALENDAR",
|
|
84
|
-
),
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
assert.deepEqual(
|
|
88
|
-
expansion.occurrences.map((occurrence) => occurrence.startAt),
|
|
89
|
-
[
|
|
90
|
-
"2026-10-15T07:00:00Z",
|
|
91
|
-
"2026-10-22T07:00:00Z",
|
|
92
|
-
"2026-10-29T08:00:00Z",
|
|
93
|
-
"2026-11-05T08:00:00Z",
|
|
94
|
-
],
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("carries each occurrence's own end", async () => {
|
|
99
|
-
const expansion = await expand(
|
|
100
|
-
amsterdamSeries("RRULE:FREQ=WEEKLY;COUNT=4"),
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
assert.deepEqual(
|
|
104
|
-
expansion.occurrences.map((occurrence) => occurrence.endAt),
|
|
105
|
-
[
|
|
106
|
-
"2026-10-15T08:00:00Z",
|
|
107
|
-
"2026-10-22T08:00:00Z",
|
|
108
|
-
"2026-10-29T09:00:00Z",
|
|
109
|
-
"2026-11-05T09:00:00Z",
|
|
110
|
-
],
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("drops an EXDATEd occurrence and keeps the rest at their slots", async () => {
|
|
115
|
-
const expansion = await expand(
|
|
116
|
-
amsterdamSeries(
|
|
117
|
-
"RRULE:FREQ=WEEKLY;COUNT=4",
|
|
118
|
-
"EXDATE;TZID=Europe/Amsterdam:20261022T090000",
|
|
119
|
-
),
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
assert.deepEqual(
|
|
123
|
-
expansion.occurrences.map((occurrence) => occurrence.recurrenceId),
|
|
124
|
-
["2026-10-15T07:00:00Z", "2026-10-29T08:00:00Z", "2026-11-05T08:00:00Z"],
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("moves an overridden occurrence but keeps the slot it replaces", async () => {
|
|
129
|
-
const expansion = await expand(
|
|
130
|
-
ical(
|
|
131
|
-
"BEGIN:VCALENDAR",
|
|
132
|
-
"VERSION:2.0",
|
|
133
|
-
...AMSTERDAM_VTIMEZONE,
|
|
134
|
-
"BEGIN:VEVENT",
|
|
135
|
-
"UID:weekly@example.com",
|
|
136
|
-
"DTSTART;TZID=Europe/Amsterdam:20261015T090000",
|
|
137
|
-
"DTEND;TZID=Europe/Amsterdam:20261015T100000",
|
|
138
|
-
"RRULE:FREQ=WEEKLY;COUNT=3",
|
|
139
|
-
"END:VEVENT",
|
|
140
|
-
"BEGIN:VEVENT",
|
|
141
|
-
"UID:weekly@example.com",
|
|
142
|
-
"RECURRENCE-ID;TZID=Europe/Amsterdam:20261022T090000",
|
|
143
|
-
"DTSTART;TZID=Europe/Amsterdam:20261022T140000",
|
|
144
|
-
"DTEND;TZID=Europe/Amsterdam:20261022T150000",
|
|
145
|
-
"END:VEVENT",
|
|
146
|
-
"END:VCALENDAR",
|
|
147
|
-
),
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const moved = expansion.occurrences.find(
|
|
151
|
-
(occurrence) => occurrence.recurrenceId === "2026-10-22T07:00:00Z",
|
|
152
|
-
);
|
|
153
|
-
assert.equal(moved?.startAt, "2026-10-22T12:00:00Z");
|
|
154
|
-
assert.equal(moved?.endAt, "2026-10-22T13:00:00Z");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("indexes an override whose slot the rule never produces", async () => {
|
|
158
|
-
// What a client writes after moving one instance and then editing the
|
|
159
|
-
// series: the override's RECURRENCE-ID names a slot the new rule does not
|
|
160
|
-
// generate. The iterator walks only the rule's slots, so nothing but an
|
|
161
|
-
// explicit pass over the VEVENTs reaches this event.
|
|
162
|
-
const expansion = await expand(
|
|
163
|
-
ical(
|
|
164
|
-
"BEGIN:VCALENDAR",
|
|
165
|
-
"VERSION:2.0",
|
|
166
|
-
...AMSTERDAM_VTIMEZONE,
|
|
167
|
-
"BEGIN:VEVENT",
|
|
168
|
-
"UID:weekly@example.com",
|
|
169
|
-
"DTSTART;TZID=Europe/Amsterdam:20261015T090000",
|
|
170
|
-
"DTEND;TZID=Europe/Amsterdam:20261015T100000",
|
|
171
|
-
"RRULE:FREQ=WEEKLY;COUNT=2",
|
|
172
|
-
"END:VEVENT",
|
|
173
|
-
"BEGIN:VEVENT",
|
|
174
|
-
"UID:weekly@example.com",
|
|
175
|
-
"RECURRENCE-ID;TZID=Europe/Amsterdam:20261112T090000",
|
|
176
|
-
"DTSTART;TZID=Europe/Amsterdam:20261112T140000",
|
|
177
|
-
"DTEND;TZID=Europe/Amsterdam:20261112T153000",
|
|
178
|
-
"END:VEVENT",
|
|
179
|
-
"END:VCALENDAR",
|
|
180
|
-
),
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
assert.deepEqual(
|
|
184
|
-
expansion.occurrences.map((occurrence) => occurrence.recurrenceId),
|
|
185
|
-
["2026-10-15T07:00:00Z", "2026-10-22T07:00:00Z", "2026-11-12T08:00:00Z"],
|
|
186
|
-
);
|
|
187
|
-
const stranded = expansion.occurrences.at(-1);
|
|
188
|
-
assert.equal(stranded?.startAt, "2026-11-12T13:00:00Z");
|
|
189
|
-
assert.equal(stranded?.endAt, "2026-11-12T14:30:00Z");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("indexes every instance of a resource that has overrides but no rule", async () => {
|
|
193
|
-
const expansion = await expand(
|
|
194
|
-
ical(
|
|
195
|
-
"BEGIN:VCALENDAR",
|
|
196
|
-
"VERSION:2.0",
|
|
197
|
-
"BEGIN:VEVENT",
|
|
198
|
-
"UID:edited@example.com",
|
|
199
|
-
"DTSTART:20260826T090000Z",
|
|
200
|
-
"DTEND:20260826T100000Z",
|
|
201
|
-
"END:VEVENT",
|
|
202
|
-
"BEGIN:VEVENT",
|
|
203
|
-
"UID:edited@example.com",
|
|
204
|
-
"RECURRENCE-ID:20260902T090000Z",
|
|
205
|
-
"DTSTART:20260902T110000Z",
|
|
206
|
-
"DTEND:20260902T120000Z",
|
|
207
|
-
"END:VEVENT",
|
|
208
|
-
"END:VCALENDAR",
|
|
209
|
-
),
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
assert.deepEqual(
|
|
213
|
-
expansion.occurrences.map((occurrence) => occurrence.startAt),
|
|
214
|
-
["2026-08-26T09:00:00Z", "2026-09-02T11:00:00Z"],
|
|
215
|
-
);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("reads an override's end in the override's own zone", async () => {
|
|
219
|
-
// The moved instance was rewritten in a different zone from the series,
|
|
220
|
-
// and its DTEND names a third. Resolving either with the master's TZID
|
|
221
|
-
// puts this instance an hour out and changes how long it lasts.
|
|
222
|
-
const expansion = await expand(
|
|
223
|
-
ical(
|
|
224
|
-
"BEGIN:VCALENDAR",
|
|
225
|
-
"VERSION:2.0",
|
|
226
|
-
"BEGIN:VEVENT",
|
|
227
|
-
"UID:crosszone@example.com",
|
|
228
|
-
"DTSTART;TZID=Europe/Berlin:20260826T090000",
|
|
229
|
-
"DTEND;TZID=Europe/Berlin:20260826T100000",
|
|
230
|
-
"RRULE:FREQ=WEEKLY;COUNT=2",
|
|
231
|
-
"END:VEVENT",
|
|
232
|
-
"BEGIN:VEVENT",
|
|
233
|
-
"UID:crosszone@example.com",
|
|
234
|
-
"RECURRENCE-ID;TZID=Europe/Berlin:20260902T090000",
|
|
235
|
-
"DTSTART;TZID=Europe/London:20260902T090000",
|
|
236
|
-
"DTEND;TZID=Europe/Lisbon:20260902T103000",
|
|
237
|
-
"END:VEVENT",
|
|
238
|
-
"END:VCALENDAR",
|
|
239
|
-
),
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
assert.deepEqual(
|
|
243
|
-
expansion.occurrences.map((occurrence) => [
|
|
244
|
-
occurrence.recurrenceId,
|
|
245
|
-
occurrence.startAt,
|
|
246
|
-
occurrence.endAt,
|
|
247
|
-
]),
|
|
248
|
-
[
|
|
249
|
-
[
|
|
250
|
-
"2026-08-26T07:00:00Z",
|
|
251
|
-
"2026-08-26T07:00:00Z",
|
|
252
|
-
"2026-08-26T08:00:00Z",
|
|
253
|
-
],
|
|
254
|
-
[
|
|
255
|
-
"2026-09-02T07:00:00Z",
|
|
256
|
-
"2026-09-02T08:00:00Z",
|
|
257
|
-
"2026-09-02T09:30:00Z",
|
|
258
|
-
],
|
|
259
|
-
],
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it("expands an all-day series as whole days", async () => {
|
|
264
|
-
const expansion = await expand(
|
|
265
|
-
singleEvent(
|
|
266
|
-
"DTSTART;VALUE=DATE:20260826",
|
|
267
|
-
"DTEND;VALUE=DATE:20260827",
|
|
268
|
-
"RRULE:FREQ=DAILY;COUNT=2",
|
|
269
|
-
),
|
|
270
|
-
"Europe/Berlin",
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
assert.deepEqual(expansion.occurrences, [
|
|
274
|
-
{
|
|
275
|
-
recurrenceId: "2026-08-25T22:00:00Z",
|
|
276
|
-
startAt: "2026-08-25T22:00:00Z",
|
|
277
|
-
endAt: "2026-08-26T22:00:00Z",
|
|
278
|
-
allDay: true,
|
|
279
|
-
summary: "",
|
|
280
|
-
status: CalendarEventStatus.Confirmed,
|
|
281
|
-
transparency: CalendarTransparency.Opaque,
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
recurrenceId: "2026-08-26T22:00:00Z",
|
|
285
|
-
startAt: "2026-08-26T22:00:00Z",
|
|
286
|
-
endAt: "2026-08-27T22:00:00Z",
|
|
287
|
-
allDay: true,
|
|
288
|
-
summary: "",
|
|
289
|
-
status: CalendarEventStatus.Confirmed,
|
|
290
|
-
transparency: CalendarTransparency.Opaque,
|
|
291
|
-
},
|
|
292
|
-
]);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it("stops an open-ended series at the horizon and says how far it got", async () => {
|
|
296
|
-
const expansion = await expand(
|
|
297
|
-
singleEvent(
|
|
298
|
-
"DTSTART:20260101T090000Z",
|
|
299
|
-
"DTEND:20260101T100000Z",
|
|
300
|
-
"RRULE:FREQ=DAILY",
|
|
301
|
-
),
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
assert.notEqual(expansion.expandedThrough, "");
|
|
305
|
-
const last = expansion.occurrences[expansion.occurrences.length - 1];
|
|
306
|
-
assert.equal(expansion.expandedThrough, last?.startAt);
|
|
307
|
-
assert.ok(
|
|
308
|
-
Date.parse(expansion.expandedThrough) -
|
|
309
|
-
Date.parse("2026-01-01T09:00:00Z") <=
|
|
310
|
-
CALENDAR_EXPANSION_HORIZON_DAYS * 24 * 60 * 60 * 1000,
|
|
311
|
-
"the last written occurrence is inside the horizon",
|
|
312
|
-
);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it("stops a dense series at the occurrence ceiling", async () => {
|
|
316
|
-
const expansion = await expand(
|
|
317
|
-
singleEvent(
|
|
318
|
-
"DTSTART:20260101T090000Z",
|
|
319
|
-
"DTEND:20260101T090100Z",
|
|
320
|
-
"RRULE:FREQ=MINUTELY",
|
|
321
|
-
),
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
assert.equal(
|
|
325
|
-
expansion.occurrences.length,
|
|
326
|
-
CALENDAR_EXPANSION_MAX_OCCURRENCES,
|
|
327
|
-
);
|
|
328
|
-
assert.notEqual(expansion.expandedThrough, "");
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it("marks a series whose first occurrence falls past the horizon", async () => {
|
|
332
|
-
// The rule's own first instance is excluded, so the iterator's first slot
|
|
333
|
-
// is 2029 and the horizon closes in 2027. Nothing is written, but the
|
|
334
|
-
// series is not complete: `""` here would hide it from the live expansion
|
|
335
|
-
// and the event would render nowhere, ever.
|
|
336
|
-
const expansion = await expand(
|
|
337
|
-
singleEvent(
|
|
338
|
-
"DTSTART:20250301T090000Z",
|
|
339
|
-
"DTEND:20250301T100000Z",
|
|
340
|
-
"RRULE:FREQ=YEARLY;INTERVAL=4",
|
|
341
|
-
"EXDATE:20250301T090000Z",
|
|
342
|
-
),
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
assert.deepEqual(expansion.occurrences, []);
|
|
346
|
-
assert.equal(expansion.expandedThrough, "2025-03-01T09:00:00Z");
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it("says nothing about a horizon for a series that ends inside it", async () => {
|
|
350
|
-
const expansion = await expand(
|
|
351
|
-
singleEvent(
|
|
352
|
-
"DTSTART:20260101T090000Z",
|
|
353
|
-
"DTEND:20260101T100000Z",
|
|
354
|
-
"RRULE:FREQ=WEEKLY;UNTIL=20260301T090000Z",
|
|
355
|
-
),
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
assert.equal(expansion.expandedThrough, "");
|
|
359
|
-
assert.equal(expansion.occurrences.length, 9);
|
|
360
|
-
});
|
|
361
|
-
});
|