@craftguild/jscalendar 0.2.0 → 0.3.0
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 +63 -0
- package/dist/__tests__/builders.test.d.ts +1 -0
- package/dist/__tests__/builders.test.js +82 -0
- package/dist/__tests__/calendar-extra.test.js +36 -0
- package/dist/__tests__/recurrence.test.js +123 -0
- package/dist/__tests__/search.test.js +27 -0
- package/dist/__tests__/utils.test.js +3 -0
- package/dist/__tests__/validation.test.js +113 -0
- package/dist/ical.d.ts +6 -0
- package/dist/ical.js +71 -3
- package/dist/jscal/base.d.ts +90 -0
- package/dist/jscal/base.js +181 -0
- package/dist/jscal/builders.d.ts +135 -0
- package/dist/jscal/builders.js +220 -0
- package/dist/jscal/constants.d.ts +11 -0
- package/dist/jscal/constants.js +11 -0
- package/dist/jscal/datetime.d.ts +14 -0
- package/dist/jscal/datetime.js +42 -0
- package/dist/jscal/defaults.d.ts +31 -0
- package/dist/jscal/defaults.js +102 -0
- package/dist/jscal/duration.d.ts +43 -0
- package/dist/jscal/duration.js +72 -0
- package/dist/jscal/event.d.ts +17 -0
- package/dist/jscal/event.js +71 -0
- package/dist/jscal/group.d.ts +25 -0
- package/dist/jscal/group.js +62 -0
- package/dist/jscal/guards.d.ts +19 -0
- package/dist/jscal/guards.js +25 -0
- package/dist/jscal/ids.d.ts +11 -0
- package/dist/jscal/ids.js +77 -0
- package/dist/jscal/normalize.d.ts +32 -0
- package/dist/jscal/normalize.js +45 -0
- package/dist/jscal/task.d.ts +17 -0
- package/dist/jscal/task.js +60 -0
- package/dist/jscal/types.d.ts +38 -0
- package/dist/jscal/types.js +1 -0
- package/dist/jscal.d.ts +77 -70
- package/dist/jscal.js +77 -465
- package/dist/patch.d.ts +13 -0
- package/dist/patch.js +166 -41
- package/dist/recurrence/constants.d.ts +13 -0
- package/dist/recurrence/constants.js +13 -0
- package/dist/recurrence/date-utils.d.ts +125 -0
- package/dist/recurrence/date-utils.js +259 -0
- package/dist/recurrence/expand.d.ts +23 -0
- package/dist/recurrence/expand.js +294 -0
- package/dist/recurrence/rule-candidates.d.ts +21 -0
- package/dist/recurrence/rule-candidates.js +120 -0
- package/dist/recurrence/rule-generate.d.ts +11 -0
- package/dist/recurrence/rule-generate.js +36 -0
- package/dist/recurrence/rule-matchers.d.ts +34 -0
- package/dist/recurrence/rule-matchers.js +120 -0
- package/dist/recurrence/rule-normalize.d.ts +9 -0
- package/dist/recurrence/rule-normalize.js +57 -0
- package/dist/recurrence/rule-selectors.d.ts +7 -0
- package/dist/recurrence/rule-selectors.js +21 -0
- package/dist/recurrence/rules.d.ts +14 -0
- package/dist/recurrence/rules.js +57 -0
- package/dist/recurrence/types.d.ts +27 -0
- package/dist/recurrence/types.js +1 -0
- package/dist/recurrence.d.ts +2 -15
- package/dist/recurrence.js +1 -674
- package/dist/search.d.ts +30 -0
- package/dist/search.js +92 -8
- package/dist/timezones/chunk_1.d.ts +2 -0
- package/dist/timezones/chunk_1.js +72 -0
- package/dist/timezones/chunk_2.d.ts +2 -0
- package/dist/timezones/chunk_2.js +72 -0
- package/dist/timezones/chunk_3.d.ts +2 -0
- package/dist/timezones/chunk_3.js +72 -0
- package/dist/timezones/chunk_4.d.ts +2 -0
- package/dist/timezones/chunk_4.js +72 -0
- package/dist/timezones/chunk_5.d.ts +2 -0
- package/dist/timezones/chunk_5.js +72 -0
- package/dist/timezones/chunk_6.d.ts +2 -0
- package/dist/timezones/chunk_6.js +72 -0
- package/dist/timezones/chunk_7.d.ts +2 -0
- package/dist/timezones/chunk_7.js +6 -0
- package/dist/timezones.d.ts +5 -0
- package/dist/timezones.js +14 -3
- package/dist/utils.d.ts +72 -0
- package/dist/utils.js +85 -1
- package/dist/validate/asserts.d.ts +155 -0
- package/dist/validate/asserts.js +381 -0
- package/dist/validate/constants.d.ts +25 -0
- package/dist/validate/constants.js +33 -0
- package/dist/validate/error.d.ts +19 -0
- package/dist/validate/error.js +25 -0
- package/dist/validate/validators-common.d.ts +64 -0
- package/dist/validate/validators-common.js +385 -0
- package/dist/validate/validators-objects.d.ts +8 -0
- package/dist/validate/validators-objects.js +70 -0
- package/dist/validate/validators-recurrence.d.ts +15 -0
- package/dist/validate/validators-recurrence.js +115 -0
- package/dist/validate/validators.d.ts +1 -0
- package/dist/validate/validators.js +1 -0
- package/dist/validate.d.ts +2 -6
- package/dist/validate.js +2 -745
- package/package.json +1 -1
package/dist/jscal.js
CHANGED
|
@@ -1,453 +1,17 @@
|
|
|
1
|
-
import { applyPatch } from "./patch.js";
|
|
2
|
-
import { deepClone, normalizeUtcDateTime, nowUtc } from "./utils.js";
|
|
3
1
|
import { toICal } from "./ical.js";
|
|
4
2
|
import { expandRecurrence, expandRecurrencePaged } from "./recurrence.js";
|
|
5
|
-
import {
|
|
6
|
-
import { filterByDateRange, filterByText, filterByType, findByUid, groupByType, } from "./search.js";
|
|
3
|
+
import { filterByDateRange, filterByText, filterByType, findByUid, groupByType } from "./search.js";
|
|
7
4
|
import { resolveTimeZone, TimeZones } from "./timezones.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (cryptoObj) {
|
|
19
|
-
cryptoObj.getRandomValues(bytes);
|
|
20
|
-
return bytes;
|
|
21
|
-
}
|
|
22
|
-
for (let i = 0; i < length; i += 1) {
|
|
23
|
-
bytes[i] = Math.floor(Math.random() * 256);
|
|
24
|
-
}
|
|
25
|
-
return bytes;
|
|
26
|
-
}
|
|
27
|
-
function base64UrlEncode(bytes) {
|
|
28
|
-
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
29
|
-
let output = "";
|
|
30
|
-
for (let i = 0; i < bytes.length; i += 3) {
|
|
31
|
-
const b0 = bytes[i] ?? 0;
|
|
32
|
-
const b1 = bytes[i + 1] ?? 0;
|
|
33
|
-
const b2 = bytes[i + 2] ?? 0;
|
|
34
|
-
const triplet = ((b0 ?? 0) << 16) | ((b1 ?? 0) << 8) | (b2 ?? 0);
|
|
35
|
-
output += alphabet[(triplet >> 18) & 0x3f] ?? "";
|
|
36
|
-
output += alphabet[(triplet >> 12) & 0x3f] ?? "";
|
|
37
|
-
output += i + 1 < bytes.length ? alphabet[(triplet >> 6) & 0x3f] ?? "" : "=";
|
|
38
|
-
output += i + 2 < bytes.length ? alphabet[triplet & 0x3f] ?? "" : "=";
|
|
39
|
-
}
|
|
40
|
-
return output.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
41
|
-
}
|
|
42
|
-
export function createUid() {
|
|
43
|
-
const cryptoObj = getCrypto();
|
|
44
|
-
if (cryptoObj?.randomUUID) {
|
|
45
|
-
return cryptoObj.randomUUID();
|
|
46
|
-
}
|
|
47
|
-
const bytes = getRandomBytes(16);
|
|
48
|
-
if (bytes.length > 6) {
|
|
49
|
-
const b6 = bytes[6] ?? 0;
|
|
50
|
-
bytes[6] = (b6 & 0x0f) | 0x40;
|
|
51
|
-
}
|
|
52
|
-
if (bytes.length > 8) {
|
|
53
|
-
const b8 = bytes[8] ?? 0;
|
|
54
|
-
bytes[8] = (b8 & 0x3f) | 0x80;
|
|
55
|
-
}
|
|
56
|
-
const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
57
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
58
|
-
}
|
|
59
|
-
export function createId() {
|
|
60
|
-
return base64UrlEncode(getRandomBytes(16));
|
|
61
|
-
}
|
|
62
|
-
function pad2(value) {
|
|
63
|
-
return value.toString().padStart(2, "0");
|
|
64
|
-
}
|
|
65
|
-
function toLocalDateTime(value) {
|
|
66
|
-
if (typeof value === "string")
|
|
67
|
-
return value;
|
|
68
|
-
return [
|
|
69
|
-
value.getFullYear().toString().padStart(4, "0"),
|
|
70
|
-
"-",
|
|
71
|
-
pad2(value.getMonth() + 1),
|
|
72
|
-
"-",
|
|
73
|
-
pad2(value.getDate()),
|
|
74
|
-
"T",
|
|
75
|
-
pad2(value.getHours()),
|
|
76
|
-
":",
|
|
77
|
-
pad2(value.getMinutes()),
|
|
78
|
-
":",
|
|
79
|
-
pad2(value.getSeconds()),
|
|
80
|
-
].join("");
|
|
81
|
-
}
|
|
82
|
-
function toUtcDateTime(value) {
|
|
83
|
-
if (typeof value === "string")
|
|
84
|
-
return value;
|
|
85
|
-
return normalizeUtcDateTime(value.toISOString());
|
|
86
|
-
}
|
|
87
|
-
function durationFromSeconds(totalSeconds) {
|
|
88
|
-
const clamped = Math.max(0, Math.floor(totalSeconds));
|
|
89
|
-
const days = Math.floor(clamped / 86400);
|
|
90
|
-
let remaining = clamped % 86400;
|
|
91
|
-
const hours = Math.floor(remaining / 3600);
|
|
92
|
-
remaining %= 3600;
|
|
93
|
-
const minutes = Math.floor(remaining / 60);
|
|
94
|
-
const seconds = remaining % 60;
|
|
95
|
-
const datePart = days > 0 ? `${days}D` : "";
|
|
96
|
-
const timeParts = [];
|
|
97
|
-
if (hours > 0)
|
|
98
|
-
timeParts.push(`${hours}H`);
|
|
99
|
-
if (minutes > 0)
|
|
100
|
-
timeParts.push(`${minutes}M`);
|
|
101
|
-
if (seconds > 0 || (datePart === "" && timeParts.length === 0)) {
|
|
102
|
-
timeParts.push(`${seconds}S`);
|
|
103
|
-
}
|
|
104
|
-
const timePart = timeParts.length > 0 ? `T${timeParts.join("")}` : "";
|
|
105
|
-
return `P${datePart}${timePart}`;
|
|
106
|
-
}
|
|
107
|
-
const Duration = {
|
|
108
|
-
seconds(value) {
|
|
109
|
-
return durationFromSeconds(value);
|
|
110
|
-
},
|
|
111
|
-
minutes(value) {
|
|
112
|
-
return durationFromSeconds(value * 60);
|
|
113
|
-
},
|
|
114
|
-
hours(value) {
|
|
115
|
-
return durationFromSeconds(value * 3600);
|
|
116
|
-
},
|
|
117
|
-
days(value) {
|
|
118
|
-
return durationFromSeconds(value * 86400);
|
|
119
|
-
},
|
|
120
|
-
from(parts) {
|
|
121
|
-
const seconds = (parts.days ?? 0) * 86400 +
|
|
122
|
-
(parts.hours ?? 0) * 3600 +
|
|
123
|
-
(parts.minutes ?? 0) * 60 +
|
|
124
|
-
(parts.seconds ?? 0);
|
|
125
|
-
return durationFromSeconds(seconds);
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
function applyCommonDefaults(data) {
|
|
129
|
-
if (data.sequence === undefined)
|
|
130
|
-
data.sequence = 0;
|
|
131
|
-
if (data.title === undefined)
|
|
132
|
-
data.title = "";
|
|
133
|
-
if (data.description === undefined)
|
|
134
|
-
data.description = "";
|
|
135
|
-
if (data.descriptionContentType === undefined)
|
|
136
|
-
data.descriptionContentType = "text/plain";
|
|
137
|
-
if (data.showWithoutTime === undefined)
|
|
138
|
-
data.showWithoutTime = false;
|
|
139
|
-
if (data.recurrenceIdTimeZone === undefined)
|
|
140
|
-
data.recurrenceIdTimeZone = null;
|
|
141
|
-
if (data.excluded === undefined)
|
|
142
|
-
data.excluded = false;
|
|
143
|
-
if (data.priority === undefined)
|
|
144
|
-
data.priority = 0;
|
|
145
|
-
if (data.freeBusyStatus === undefined)
|
|
146
|
-
data.freeBusyStatus = "busy";
|
|
147
|
-
if (data.privacy === undefined)
|
|
148
|
-
data.privacy = "public";
|
|
149
|
-
if (data.useDefaultAlerts === undefined)
|
|
150
|
-
data.useDefaultAlerts = false;
|
|
151
|
-
return data;
|
|
152
|
-
}
|
|
153
|
-
function applyEventDefaults(data) {
|
|
154
|
-
if (data.duration === undefined)
|
|
155
|
-
data.duration = "PT0S";
|
|
156
|
-
if (data.status === undefined)
|
|
157
|
-
data.status = "confirmed";
|
|
158
|
-
return data;
|
|
159
|
-
}
|
|
160
|
-
function applyTaskDefaults(data) {
|
|
161
|
-
if (data.progress === undefined) {
|
|
162
|
-
const participants = data.participants ? Object.values(data.participants) : [];
|
|
163
|
-
if (participants.length === 0) {
|
|
164
|
-
data.progress = "needs-action";
|
|
165
|
-
}
|
|
166
|
-
else if (participants.every((p) => p.progress === "completed")) {
|
|
167
|
-
data.progress = "completed";
|
|
168
|
-
}
|
|
169
|
-
else if (participants.some((p) => p.progress === "failed")) {
|
|
170
|
-
data.progress = "failed";
|
|
171
|
-
}
|
|
172
|
-
else if (participants.some((p) => p.progress === "in-process")) {
|
|
173
|
-
data.progress = "in-process";
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
data.progress = "needs-action";
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return data;
|
|
180
|
-
}
|
|
181
|
-
function applyParticipantDefaults(participant) {
|
|
182
|
-
if (participant.participationStatus === undefined)
|
|
183
|
-
participant.participationStatus = "needs-action";
|
|
184
|
-
if (participant.expectReply === undefined)
|
|
185
|
-
participant.expectReply = false;
|
|
186
|
-
if (participant.scheduleAgent === undefined)
|
|
187
|
-
participant.scheduleAgent = "server";
|
|
188
|
-
if (participant.scheduleForceSend === undefined)
|
|
189
|
-
participant.scheduleForceSend = false;
|
|
190
|
-
if (participant.scheduleSequence === undefined)
|
|
191
|
-
participant.scheduleSequence = 0;
|
|
192
|
-
return participant;
|
|
193
|
-
}
|
|
194
|
-
function applyAlertDefaults(alert) {
|
|
195
|
-
if (alert.action === undefined)
|
|
196
|
-
alert.action = "display";
|
|
197
|
-
if (alert.trigger["@type"] === "OffsetTrigger") {
|
|
198
|
-
if (alert.trigger.relativeTo === undefined) {
|
|
199
|
-
alert.trigger.relativeTo = "start";
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return alert;
|
|
203
|
-
}
|
|
204
|
-
export function isEvent(obj) {
|
|
205
|
-
return obj["@type"] === "Event";
|
|
206
|
-
}
|
|
207
|
-
export function isTask(obj) {
|
|
208
|
-
return obj["@type"] === "Task";
|
|
209
|
-
}
|
|
210
|
-
export function isGroup(obj) {
|
|
211
|
-
return obj["@type"] === "Group";
|
|
212
|
-
}
|
|
213
|
-
class Base {
|
|
214
|
-
data;
|
|
215
|
-
constructor(data) {
|
|
216
|
-
this.data = data;
|
|
217
|
-
}
|
|
218
|
-
eject() {
|
|
219
|
-
return deepClone(this.data);
|
|
220
|
-
}
|
|
221
|
-
clone() {
|
|
222
|
-
return new Base(deepClone(this.data));
|
|
223
|
-
}
|
|
224
|
-
get(key) {
|
|
225
|
-
return this.data[key];
|
|
226
|
-
}
|
|
227
|
-
set(key, value) {
|
|
228
|
-
this.data[key] = value;
|
|
229
|
-
return this.touchKeys([String(key)]);
|
|
230
|
-
}
|
|
231
|
-
update(values, options = {}) {
|
|
232
|
-
const next = { ...this.data, ...values };
|
|
233
|
-
if (options.validate !== false) {
|
|
234
|
-
validateJsCalendarObject(next);
|
|
235
|
-
}
|
|
236
|
-
this.data = next;
|
|
237
|
-
return this.touchKeys(Object.keys(values), options);
|
|
238
|
-
}
|
|
239
|
-
patch(patch, options = {}) {
|
|
240
|
-
const next = applyPatch(this.data, patch);
|
|
241
|
-
if (options.validate !== false) {
|
|
242
|
-
validateJsCalendarObject(next);
|
|
243
|
-
}
|
|
244
|
-
this.data = next;
|
|
245
|
-
return this.touchFromPatch(patch, options);
|
|
246
|
-
}
|
|
247
|
-
addLocation(location, id) {
|
|
248
|
-
const actualId = id ?? createId();
|
|
249
|
-
if (!this.data.locations)
|
|
250
|
-
this.data.locations = {};
|
|
251
|
-
this.data.locations[actualId] = { ...location, "@type": "Location" };
|
|
252
|
-
this.touchKeys(["locations"]);
|
|
253
|
-
return actualId;
|
|
254
|
-
}
|
|
255
|
-
addVirtualLocation(location, id) {
|
|
256
|
-
const actualId = id ?? createId();
|
|
257
|
-
if (!this.data.virtualLocations)
|
|
258
|
-
this.data.virtualLocations = {};
|
|
259
|
-
const name = location.name ?? "";
|
|
260
|
-
this.data.virtualLocations[actualId] = { ...location, name, "@type": "VirtualLocation" };
|
|
261
|
-
this.touchKeys(["virtualLocations"]);
|
|
262
|
-
return actualId;
|
|
263
|
-
}
|
|
264
|
-
addParticipant(participant, id) {
|
|
265
|
-
const actualId = id ?? createId();
|
|
266
|
-
if (!this.data.participants)
|
|
267
|
-
this.data.participants = {};
|
|
268
|
-
const filled = applyParticipantDefaults({ ...participant, "@type": "Participant" });
|
|
269
|
-
this.data.participants[actualId] = filled;
|
|
270
|
-
this.touchKeys(["participants"], { sequence: false });
|
|
271
|
-
return actualId;
|
|
272
|
-
}
|
|
273
|
-
addAlert(alert, id) {
|
|
274
|
-
const actualId = id ?? createId();
|
|
275
|
-
if (!this.data.alerts)
|
|
276
|
-
this.data.alerts = {};
|
|
277
|
-
const filled = applyAlertDefaults({ ...alert, "@type": "Alert" });
|
|
278
|
-
this.data.alerts[actualId] = filled;
|
|
279
|
-
this.touchKeys(["alerts"]);
|
|
280
|
-
return actualId;
|
|
281
|
-
}
|
|
282
|
-
touchKeys(keys, options = {}) {
|
|
283
|
-
if (options.touch === false)
|
|
284
|
-
return this;
|
|
285
|
-
const now = options.now ?? nowUtc;
|
|
286
|
-
this.data.updated = now();
|
|
287
|
-
if (options.sequence === false)
|
|
288
|
-
return this;
|
|
289
|
-
const onlyParticipants = keys.length > 0 && keys.every((key) => key === "participants");
|
|
290
|
-
if (!onlyParticipants) {
|
|
291
|
-
const current = typeof this.data.sequence === "number" ? this.data.sequence : 0;
|
|
292
|
-
this.data.sequence = current + 1;
|
|
293
|
-
}
|
|
294
|
-
return this;
|
|
295
|
-
}
|
|
296
|
-
touchFromPatch(patch, options = {}) {
|
|
297
|
-
if (options.touch === false)
|
|
298
|
-
return this;
|
|
299
|
-
const now = options.now ?? nowUtc;
|
|
300
|
-
this.data.updated = now();
|
|
301
|
-
if (options.sequence === false)
|
|
302
|
-
return this;
|
|
303
|
-
const pointers = Object.keys(patch);
|
|
304
|
-
const onlyParticipants = pointers.length > 0 &&
|
|
305
|
-
pointers.every((pointer) => {
|
|
306
|
-
const normalized = pointer.startsWith("/") ? pointer.slice(1) : pointer;
|
|
307
|
-
return normalized.startsWith("participants");
|
|
308
|
-
});
|
|
309
|
-
if (!onlyParticipants) {
|
|
310
|
-
const current = typeof this.data.sequence === "number" ? this.data.sequence : 0;
|
|
311
|
-
this.data.sequence = current + 1;
|
|
312
|
-
}
|
|
313
|
-
return this;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
class EventObject extends Base {
|
|
317
|
-
constructor(input, options = {}) {
|
|
318
|
-
if (typeof input.start !== "string" && !(input.start instanceof Date)) {
|
|
319
|
-
throw new Error("Event.start is required");
|
|
320
|
-
}
|
|
321
|
-
if (typeof input.start === "string" && input.start.length === 0) {
|
|
322
|
-
throw new Error("Event.start is required");
|
|
323
|
-
}
|
|
324
|
-
const now = options.now ?? nowUtc;
|
|
325
|
-
const { start: rawStart, duration: rawDuration, updated: rawUpdated, created: rawCreated, timeZone: rawTimeZone, ...rest } = input;
|
|
326
|
-
const start = toLocalDateTime(rawStart);
|
|
327
|
-
const updated = rawUpdated ? toUtcDateTime(rawUpdated) : now();
|
|
328
|
-
let timeZone;
|
|
329
|
-
if (rawTimeZone === null) {
|
|
330
|
-
timeZone = null;
|
|
331
|
-
}
|
|
332
|
-
else if (rawTimeZone) {
|
|
333
|
-
timeZone = resolveTimeZone(rawTimeZone);
|
|
334
|
-
}
|
|
335
|
-
const data = {
|
|
336
|
-
...rest,
|
|
337
|
-
"@type": "Event",
|
|
338
|
-
start,
|
|
339
|
-
uid: input.uid ?? createUid(),
|
|
340
|
-
updated,
|
|
341
|
-
};
|
|
342
|
-
if (timeZone !== undefined)
|
|
343
|
-
data.timeZone = timeZone;
|
|
344
|
-
if (rawDuration !== undefined) {
|
|
345
|
-
data.duration = typeof rawDuration === "number"
|
|
346
|
-
? durationFromSeconds(rawDuration)
|
|
347
|
-
: rawDuration;
|
|
348
|
-
}
|
|
349
|
-
if (rawCreated) {
|
|
350
|
-
data.created = toUtcDateTime(rawCreated);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
data.created = updated;
|
|
354
|
-
}
|
|
355
|
-
applyCommonDefaults(data);
|
|
356
|
-
applyEventDefaults(data);
|
|
357
|
-
if (options.validate !== false) {
|
|
358
|
-
validateJsCalendarObject(data);
|
|
359
|
-
}
|
|
360
|
-
super(data);
|
|
361
|
-
}
|
|
362
|
-
clone() {
|
|
363
|
-
const cloneData = deepClone(this.data);
|
|
364
|
-
const { "@type": _type, ...rest } = cloneData;
|
|
365
|
-
return new EventObject(rest);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
class TaskObject extends Base {
|
|
369
|
-
constructor(input = {}, options = {}) {
|
|
370
|
-
const now = options.now ?? nowUtc;
|
|
371
|
-
const { start: rawStart, due: rawDue, updated: rawUpdated, created: rawCreated, timeZone: rawTimeZone, ...rest } = input;
|
|
372
|
-
const updated = rawUpdated ? toUtcDateTime(rawUpdated) : now();
|
|
373
|
-
let timeZone;
|
|
374
|
-
if (rawTimeZone === null) {
|
|
375
|
-
timeZone = null;
|
|
376
|
-
}
|
|
377
|
-
else if (rawTimeZone) {
|
|
378
|
-
timeZone = resolveTimeZone(rawTimeZone);
|
|
379
|
-
}
|
|
380
|
-
const data = {
|
|
381
|
-
...rest,
|
|
382
|
-
"@type": "Task",
|
|
383
|
-
uid: input.uid ?? createUid(),
|
|
384
|
-
updated,
|
|
385
|
-
};
|
|
386
|
-
if (rawStart)
|
|
387
|
-
data.start = toLocalDateTime(rawStart);
|
|
388
|
-
if (rawDue)
|
|
389
|
-
data.due = toLocalDateTime(rawDue);
|
|
390
|
-
if (timeZone !== undefined)
|
|
391
|
-
data.timeZone = timeZone;
|
|
392
|
-
if (rawCreated) {
|
|
393
|
-
data.created = toUtcDateTime(rawCreated);
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
data.created = updated;
|
|
397
|
-
}
|
|
398
|
-
applyCommonDefaults(data);
|
|
399
|
-
applyTaskDefaults(data);
|
|
400
|
-
if (options.validate !== false) {
|
|
401
|
-
validateJsCalendarObject(data);
|
|
402
|
-
}
|
|
403
|
-
super(data);
|
|
404
|
-
}
|
|
405
|
-
clone() {
|
|
406
|
-
const cloneData = deepClone(this.data);
|
|
407
|
-
const { "@type": _type, ...rest } = cloneData;
|
|
408
|
-
return new TaskObject(rest);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
class GroupObject extends Base {
|
|
412
|
-
constructor(input, options = {}) {
|
|
413
|
-
if (!Array.isArray(input.entries)) {
|
|
414
|
-
throw new Error("Group.entries is required");
|
|
415
|
-
}
|
|
416
|
-
const now = options.now ?? nowUtc;
|
|
417
|
-
const { updated: rawUpdated, created: rawCreated, ...rest } = input;
|
|
418
|
-
const entries = input.entries;
|
|
419
|
-
const updated = rawUpdated ? toUtcDateTime(rawUpdated) : now();
|
|
420
|
-
const data = {
|
|
421
|
-
...rest,
|
|
422
|
-
"@type": "Group",
|
|
423
|
-
entries,
|
|
424
|
-
uid: input.uid ?? createUid(),
|
|
425
|
-
updated,
|
|
426
|
-
};
|
|
427
|
-
if (rawCreated) {
|
|
428
|
-
data.created = toUtcDateTime(rawCreated);
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
data.created = updated;
|
|
432
|
-
}
|
|
433
|
-
applyCommonDefaults(data);
|
|
434
|
-
if (options.validate !== false) {
|
|
435
|
-
validateJsCalendarObject(data);
|
|
436
|
-
}
|
|
437
|
-
super(data);
|
|
438
|
-
}
|
|
439
|
-
addEntry(entry) {
|
|
440
|
-
const entries = [...this.data.entries, entry];
|
|
441
|
-
this.data.entries = entries;
|
|
442
|
-
this.touchKeys(["entries"]);
|
|
443
|
-
return this;
|
|
444
|
-
}
|
|
445
|
-
clone() {
|
|
446
|
-
const cloneData = deepClone(this.data);
|
|
447
|
-
const { "@type": _type, ...rest } = cloneData;
|
|
448
|
-
return new GroupObject(rest);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
5
|
+
import { applyPatch } from "./patch.js";
|
|
6
|
+
import { EventObject } from "./jscal/event.js";
|
|
7
|
+
import { TaskObject } from "./jscal/task.js";
|
|
8
|
+
import { GroupObject } from "./jscal/group.js";
|
|
9
|
+
import { Duration } from "./jscal/duration.js";
|
|
10
|
+
import { createId, createUid } from "./jscal/ids.js";
|
|
11
|
+
import { normalizeItems, normalizeToObjects } from "./jscal/normalize.js";
|
|
12
|
+
import { isEvent, isGroup, isTask } from "./jscal/guards.js";
|
|
13
|
+
import { buildAlert, buildLink, buildLocation, buildNDay, buildParticipants, buildRecurrenceRule, buildRelation, buildRelatedTo, buildTimeZone, buildTimeZoneMap, buildTimeZoneRule, buildVirtualLocation, buildVirtualLocations, buildLocations, buildLinks, buildParticipant, buildAlerts, } from "./jscal/builders.js";
|
|
14
|
+
export { createId, createUid, isEvent, isGroup, isTask };
|
|
451
15
|
export const JsCal = {
|
|
452
16
|
Event: EventObject,
|
|
453
17
|
Task: TaskObject,
|
|
@@ -458,47 +22,95 @@ export const JsCal = {
|
|
|
458
22
|
timeZone: resolveTimeZone,
|
|
459
23
|
timeZones: TimeZones,
|
|
460
24
|
applyPatch,
|
|
25
|
+
Participant: buildParticipant,
|
|
26
|
+
Location: buildLocation,
|
|
27
|
+
VirtualLocation: buildVirtualLocation,
|
|
28
|
+
Alert: buildAlert,
|
|
29
|
+
Relation: buildRelation,
|
|
30
|
+
Link: buildLink,
|
|
31
|
+
TimeZone: buildTimeZone,
|
|
32
|
+
TimeZoneRule: buildTimeZoneRule,
|
|
33
|
+
RecurrenceRule: buildRecurrenceRule,
|
|
34
|
+
NDay: buildNDay,
|
|
35
|
+
participants: buildParticipants,
|
|
36
|
+
locations: buildLocations,
|
|
37
|
+
virtualLocations: buildVirtualLocations,
|
|
38
|
+
alerts: buildAlerts,
|
|
39
|
+
links: buildLinks,
|
|
40
|
+
relatedTo: buildRelatedTo,
|
|
41
|
+
timeZonesMap: buildTimeZoneMap,
|
|
42
|
+
/**
|
|
43
|
+
* Find a JSCalendar object by UID.
|
|
44
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
45
|
+
* @param uid UID to match.
|
|
46
|
+
* @return Matching object, if found.
|
|
47
|
+
*/
|
|
461
48
|
findByUid(items, uid) {
|
|
462
49
|
return findByUid(normalizeItems(items), uid);
|
|
463
50
|
},
|
|
51
|
+
/**
|
|
52
|
+
* Filter JSCalendar objects by @type.
|
|
53
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
54
|
+
* @param type JSCalendar @type to match.
|
|
55
|
+
* @return Matching objects.
|
|
56
|
+
*/
|
|
464
57
|
filterByType(items, type) {
|
|
465
58
|
return filterByType(normalizeItems(items), type);
|
|
466
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* Group JSCalendar objects by @type.
|
|
62
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
63
|
+
* @return Record keyed by @type.
|
|
64
|
+
*/
|
|
467
65
|
groupByType(items) {
|
|
468
66
|
return groupByType(normalizeItems(items));
|
|
469
67
|
},
|
|
68
|
+
/**
|
|
69
|
+
* Filter JSCalendar objects whose text fields match a query.
|
|
70
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
71
|
+
* @param query Text query to match.
|
|
72
|
+
* @return Matching objects.
|
|
73
|
+
*/
|
|
470
74
|
filterByText(items, query) {
|
|
471
75
|
return filterByText(normalizeItems(items), query);
|
|
472
76
|
},
|
|
77
|
+
/**
|
|
78
|
+
* Filter JSCalendar objects that overlap a date range.
|
|
79
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
80
|
+
* @param range Date range bounds.
|
|
81
|
+
* @param options Range comparison options.
|
|
82
|
+
* @return Matching objects.
|
|
83
|
+
*/
|
|
473
84
|
filterByDateRange(items, range, options) {
|
|
474
85
|
return filterByDateRange(normalizeItems(items), range, options);
|
|
475
86
|
},
|
|
87
|
+
/**
|
|
88
|
+
* Expand recurrence rules into concrete occurrences.
|
|
89
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
90
|
+
* @param range Date range bounds.
|
|
91
|
+
* @return Generator of expanded JSCalendar objects.
|
|
92
|
+
*/
|
|
476
93
|
expandRecurrence(items, range) {
|
|
477
94
|
return expandRecurrence(normalizeItems(items), range);
|
|
478
95
|
},
|
|
96
|
+
/**
|
|
97
|
+
* Expand recurrence rules with pagination support.
|
|
98
|
+
* @param items JSCalendar objects or JsCal instances.
|
|
99
|
+
* @param range Date range bounds.
|
|
100
|
+
* @param options Pagination options.
|
|
101
|
+
* @return Page of expanded items plus an optional next cursor.
|
|
102
|
+
*/
|
|
479
103
|
expandRecurrencePaged(items, range, options) {
|
|
480
104
|
return expandRecurrencePaged(normalizeItems(items), range, options);
|
|
481
105
|
},
|
|
106
|
+
/**
|
|
107
|
+
* Convert JSCalendar objects to iCalendar text.
|
|
108
|
+
* @param value JSCalendar objects or JsCal instances.
|
|
109
|
+
* @param options iCalendar export options.
|
|
110
|
+
* @return iCalendar text.
|
|
111
|
+
*/
|
|
482
112
|
toICal(value, options) {
|
|
483
113
|
const objects = normalizeToObjects(value);
|
|
484
114
|
return toICal(objects, options);
|
|
485
115
|
},
|
|
486
116
|
};
|
|
487
|
-
function isJsCalInstance(value) {
|
|
488
|
-
return "data" in value;
|
|
489
|
-
}
|
|
490
|
-
function normalizeItems(items) {
|
|
491
|
-
const mapped = [];
|
|
492
|
-
for (const entry of items) {
|
|
493
|
-
if (typeof entry === "object" && entry !== null && isJsCalInstance(entry)) {
|
|
494
|
-
mapped.push(entry.data);
|
|
495
|
-
}
|
|
496
|
-
else {
|
|
497
|
-
mapped.push(entry);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
return mapped;
|
|
501
|
-
}
|
|
502
|
-
function normalizeToObjects(value) {
|
|
503
|
-
return normalizeItems(value);
|
|
504
|
-
}
|
package/dist/patch.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { PatchObject } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when a patch operation is invalid.
|
|
4
|
+
*/
|
|
2
5
|
export declare class PatchError extends Error {
|
|
6
|
+
/**
|
|
7
|
+
* Create a new PatchError.
|
|
8
|
+
* @param message Error message.
|
|
9
|
+
*/
|
|
3
10
|
constructor(message: string);
|
|
4
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Apply a JSCalendar PatchObject to an input object.
|
|
14
|
+
* @param input Input object.
|
|
15
|
+
* @param patch Patch object to apply.
|
|
16
|
+
* @return Patched clone of the input object.
|
|
17
|
+
*/
|
|
5
18
|
export declare function applyPatch<T extends object>(input: T, patch: PatchObject): T;
|