@craftguild/jscalendar 0.5.2 → 0.5.4
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 +130 -94
- package/dist/index.cjs +3923 -0
- package/dist/index.d.cts +802 -0
- package/dist/index.d.mts +802 -0
- package/dist/index.mjs +3927 -0
- package/package.json +46 -31
- package/dist/__tests__/builders.test.d.ts +0 -1
- package/dist/__tests__/builders.test.js +0 -101
- package/dist/__tests__/calendar-extra.test.d.ts +0 -1
- package/dist/__tests__/calendar-extra.test.js +0 -221
- package/dist/__tests__/calendar.test.d.ts +0 -1
- package/dist/__tests__/calendar.test.js +0 -97
- package/dist/__tests__/ical-extra.test.d.ts +0 -1
- package/dist/__tests__/ical-extra.test.js +0 -87
- package/dist/__tests__/ical.test.d.ts +0 -1
- package/dist/__tests__/ical.test.js +0 -72
- package/dist/__tests__/index.test.d.ts +0 -1
- package/dist/__tests__/index.test.js +0 -9
- package/dist/__tests__/patch.test.d.ts +0 -1
- package/dist/__tests__/patch.test.js +0 -47
- package/dist/__tests__/recurrence.test.d.ts +0 -1
- package/dist/__tests__/recurrence.test.js +0 -640
- package/dist/__tests__/search.test.d.ts +0 -1
- package/dist/__tests__/search.test.js +0 -264
- package/dist/__tests__/timezones.test.d.ts +0 -1
- package/dist/__tests__/timezones.test.js +0 -12
- package/dist/__tests__/utils.test.d.ts +0 -1
- package/dist/__tests__/utils.test.js +0 -127
- package/dist/__tests__/validation.test.d.ts +0 -1
- package/dist/__tests__/validation.test.js +0 -224
- package/dist/ical.d.ts +0 -13
- package/dist/ical.js +0 -270
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -2
- package/dist/jscal/base.d.ts +0 -89
- package/dist/jscal/base.js +0 -173
- package/dist/jscal/builders.d.ts +0 -183
- package/dist/jscal/builders.js +0 -287
- package/dist/jscal/constants.d.ts +0 -11
- package/dist/jscal/constants.js +0 -11
- package/dist/jscal/datetime.d.ts +0 -14
- package/dist/jscal/datetime.js +0 -42
- package/dist/jscal/defaults.d.ts +0 -31
- package/dist/jscal/defaults.js +0 -102
- package/dist/jscal/duration.d.ts +0 -43
- package/dist/jscal/duration.js +0 -75
- package/dist/jscal/event.d.ts +0 -23
- package/dist/jscal/event.js +0 -78
- package/dist/jscal/group.d.ts +0 -31
- package/dist/jscal/group.js +0 -69
- package/dist/jscal/guards.d.ts +0 -19
- package/dist/jscal/guards.js +0 -25
- package/dist/jscal/ids.d.ts +0 -11
- package/dist/jscal/ids.js +0 -77
- package/dist/jscal/normalize.d.ts +0 -32
- package/dist/jscal/normalize.js +0 -45
- package/dist/jscal/task.d.ts +0 -23
- package/dist/jscal/task.js +0 -67
- package/dist/jscal/types.d.ts +0 -38
- package/dist/jscal/types.js +0 -1
- package/dist/jscal.d.ts +0 -145
- package/dist/jscal.js +0 -126
- package/dist/patch.d.ts +0 -18
- package/dist/patch.js +0 -216
- package/dist/recurrence/constants.d.ts +0 -13
- package/dist/recurrence/constants.js +0 -13
- package/dist/recurrence/date-utils.d.ts +0 -125
- package/dist/recurrence/date-utils.js +0 -259
- package/dist/recurrence/expand.d.ts +0 -23
- package/dist/recurrence/expand.js +0 -315
- package/dist/recurrence/rule-candidates.d.ts +0 -21
- package/dist/recurrence/rule-candidates.js +0 -120
- package/dist/recurrence/rule-generate.d.ts +0 -11
- package/dist/recurrence/rule-generate.js +0 -36
- package/dist/recurrence/rule-matchers.d.ts +0 -34
- package/dist/recurrence/rule-matchers.js +0 -120
- package/dist/recurrence/rule-normalize.d.ts +0 -9
- package/dist/recurrence/rule-normalize.js +0 -57
- package/dist/recurrence/rule-selectors.d.ts +0 -7
- package/dist/recurrence/rule-selectors.js +0 -21
- package/dist/recurrence/rules.d.ts +0 -14
- package/dist/recurrence/rules.js +0 -57
- package/dist/recurrence/types.d.ts +0 -27
- package/dist/recurrence/types.js +0 -1
- package/dist/recurrence.d.ts +0 -2
- package/dist/recurrence.js +0 -1
- package/dist/search.d.ts +0 -44
- package/dist/search.js +0 -292
- package/dist/timezones/chunk_1.d.ts +0 -2
- package/dist/timezones/chunk_1.js +0 -72
- package/dist/timezones/chunk_2.d.ts +0 -2
- package/dist/timezones/chunk_2.js +0 -72
- package/dist/timezones/chunk_3.d.ts +0 -2
- package/dist/timezones/chunk_3.js +0 -72
- package/dist/timezones/chunk_4.d.ts +0 -2
- package/dist/timezones/chunk_4.js +0 -72
- package/dist/timezones/chunk_5.d.ts +0 -2
- package/dist/timezones/chunk_5.js +0 -72
- package/dist/timezones/chunk_6.d.ts +0 -2
- package/dist/timezones/chunk_6.js +0 -72
- package/dist/timezones/chunk_7.d.ts +0 -2
- package/dist/timezones/chunk_7.js +0 -6
- package/dist/timezones.d.ts +0 -9
- package/dist/timezones.js +0 -452
- package/dist/types.d.ts +0 -246
- package/dist/types.js +0 -1
- package/dist/utils.d.ts +0 -82
- package/dist/utils.js +0 -164
- package/dist/validate/asserts.d.ts +0 -155
- package/dist/validate/asserts.js +0 -381
- package/dist/validate/constants.d.ts +0 -25
- package/dist/validate/constants.js +0 -33
- package/dist/validate/error.d.ts +0 -19
- package/dist/validate/error.js +0 -25
- package/dist/validate/validators-common.d.ts +0 -64
- package/dist/validate/validators-common.js +0 -390
- package/dist/validate/validators-objects.d.ts +0 -8
- package/dist/validate/validators-objects.js +0 -70
- package/dist/validate/validators-recurrence.d.ts +0 -15
- package/dist/validate/validators-recurrence.js +0 -115
- package/dist/validate/validators.d.ts +0 -1
- package/dist/validate/validators.js +0 -1
- package/dist/validate.d.ts +0 -2
- package/dist/validate.js +0 -2
package/dist/ical.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { normalizeUtcDateTime } from "./utils.js";
|
|
2
|
-
const TYPE_EVENT = "Event";
|
|
3
|
-
const TYPE_GROUP = "Group";
|
|
4
|
-
const TYPE_TASK = "Task";
|
|
5
|
-
const DEFAULT_PRODID = "-//craftguild//EN";
|
|
6
|
-
/**
|
|
7
|
-
* Convert JSCalendar objects into an iCalendar string.
|
|
8
|
-
* @param objects JSCalendar objects to export.
|
|
9
|
-
* @param options Export options.
|
|
10
|
-
* @return iCalendar text.
|
|
11
|
-
*/
|
|
12
|
-
export function toICal(objects, options = {}) {
|
|
13
|
-
const lines = [];
|
|
14
|
-
const includeX = options.includeXJSCalendar !== false;
|
|
15
|
-
lines.push("BEGIN:VCALENDAR");
|
|
16
|
-
lines.push("VERSION:2.0");
|
|
17
|
-
lines.push(`PRODID:${options.prodId ?? DEFAULT_PRODID}`);
|
|
18
|
-
const method = options.method ?? findMethod(objects);
|
|
19
|
-
if (method)
|
|
20
|
-
lines.push(`METHOD:${method.toUpperCase()}`);
|
|
21
|
-
for (const object of objects) {
|
|
22
|
-
if (object["@type"] === TYPE_GROUP) {
|
|
23
|
-
const group = object;
|
|
24
|
-
if (includeX) {
|
|
25
|
-
lines.push(`X-JSCALENDAR-GROUP:${escapeText(JSON.stringify(stripEntries(group)))}`);
|
|
26
|
-
}
|
|
27
|
-
for (const entry of group.entries) {
|
|
28
|
-
lines.push(...buildComponent(entry, includeX));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
lines.push(...buildComponent(object, includeX));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
lines.push("END:VCALENDAR");
|
|
36
|
-
return foldLines(lines).join("\r\n");
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Find the first METHOD value from objects.
|
|
40
|
-
* @param objects JSCalendar objects.
|
|
41
|
-
* @return METHOD value or undefined.
|
|
42
|
-
*/
|
|
43
|
-
function findMethod(objects) {
|
|
44
|
-
for (const object of objects) {
|
|
45
|
-
if (object.method)
|
|
46
|
-
return object.method;
|
|
47
|
-
}
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Build an iCalendar component from a JSCalendar object.
|
|
52
|
-
* @param object JSCalendar object.
|
|
53
|
-
* @param includeX Whether to include X-JSCALENDAR.
|
|
54
|
-
* @return iCalendar lines for the component.
|
|
55
|
-
*/
|
|
56
|
-
function buildComponent(object, includeX) {
|
|
57
|
-
if (object["@type"] === TYPE_EVENT)
|
|
58
|
-
return buildEvent(object, includeX);
|
|
59
|
-
if (object["@type"] === TYPE_TASK)
|
|
60
|
-
return buildTask(object, includeX);
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Build a VEVENT component.
|
|
65
|
-
* @param event Event object.
|
|
66
|
-
* @param includeX Whether to include X-JSCALENDAR.
|
|
67
|
-
* @return VEVENT lines.
|
|
68
|
-
*/
|
|
69
|
-
function buildEvent(event, includeX) {
|
|
70
|
-
const lines = [];
|
|
71
|
-
lines.push("BEGIN:VEVENT");
|
|
72
|
-
lines.push(`UID:${escapeText(event.uid)}`);
|
|
73
|
-
lines.push(`DTSTAMP:${formatUtcDateTime(event.updated)}`);
|
|
74
|
-
if (event.sequence !== undefined)
|
|
75
|
-
lines.push(`SEQUENCE:${event.sequence}`);
|
|
76
|
-
if (event.title)
|
|
77
|
-
lines.push(`SUMMARY:${escapeText(event.title)}`);
|
|
78
|
-
if (event.description)
|
|
79
|
-
lines.push(`DESCRIPTION:${escapeText(event.description)}`);
|
|
80
|
-
const dtStart = formatLocalDateTime(event.start);
|
|
81
|
-
if (event.timeZone) {
|
|
82
|
-
lines.push(`DTSTART;TZID=${event.timeZone}:${dtStart}`);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
lines.push(`DTSTART:${dtStart}`);
|
|
86
|
-
}
|
|
87
|
-
if (event.duration)
|
|
88
|
-
lines.push(`DURATION:${event.duration}`);
|
|
89
|
-
if (event.status)
|
|
90
|
-
lines.push(`STATUS:${event.status.toUpperCase()}`);
|
|
91
|
-
appendRecurrence(lines, event.recurrenceRules);
|
|
92
|
-
if (includeX) {
|
|
93
|
-
lines.push(`X-JSCALENDAR:${escapeText(JSON.stringify(event))}`);
|
|
94
|
-
}
|
|
95
|
-
lines.push("END:VEVENT");
|
|
96
|
-
return lines;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Build a VTODO component.
|
|
100
|
-
* @param task Task object.
|
|
101
|
-
* @param includeX Whether to include X-JSCALENDAR.
|
|
102
|
-
* @return VTODO lines.
|
|
103
|
-
*/
|
|
104
|
-
function buildTask(task, includeX) {
|
|
105
|
-
const lines = [];
|
|
106
|
-
lines.push("BEGIN:VTODO");
|
|
107
|
-
lines.push(`UID:${escapeText(task.uid)}`);
|
|
108
|
-
lines.push(`DTSTAMP:${formatUtcDateTime(task.updated)}`);
|
|
109
|
-
if (task.sequence !== undefined)
|
|
110
|
-
lines.push(`SEQUENCE:${task.sequence}`);
|
|
111
|
-
if (task.title)
|
|
112
|
-
lines.push(`SUMMARY:${escapeText(task.title)}`);
|
|
113
|
-
if (task.description)
|
|
114
|
-
lines.push(`DESCRIPTION:${escapeText(task.description)}`);
|
|
115
|
-
if (task.start) {
|
|
116
|
-
const dtStart = formatLocalDateTime(task.start);
|
|
117
|
-
if (task.timeZone) {
|
|
118
|
-
lines.push(`DTSTART;TZID=${task.timeZone}:${dtStart}`);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
lines.push(`DTSTART:${dtStart}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (task.due) {
|
|
125
|
-
const due = formatLocalDateTime(task.due);
|
|
126
|
-
if (task.timeZone) {
|
|
127
|
-
lines.push(`DUE;TZID=${task.timeZone}:${due}`);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
lines.push(`DUE:${due}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (task.percentComplete !== undefined) {
|
|
134
|
-
lines.push(`PERCENT-COMPLETE:${task.percentComplete}`);
|
|
135
|
-
}
|
|
136
|
-
if (task.progress) {
|
|
137
|
-
lines.push(`STATUS:${task.progress.toUpperCase()}`);
|
|
138
|
-
}
|
|
139
|
-
appendRecurrence(lines, task.recurrenceRules);
|
|
140
|
-
if (includeX) {
|
|
141
|
-
lines.push(`X-JSCALENDAR:${escapeText(JSON.stringify(task))}`);
|
|
142
|
-
}
|
|
143
|
-
lines.push("END:VTODO");
|
|
144
|
-
return lines;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Append RRULE lines for recurrence rules.
|
|
148
|
-
* @param lines Lines to append to.
|
|
149
|
-
* @param rules Recurrence rules.
|
|
150
|
-
* @return Nothing.
|
|
151
|
-
*/
|
|
152
|
-
function appendRecurrence(lines, rules) {
|
|
153
|
-
if (!rules)
|
|
154
|
-
return;
|
|
155
|
-
for (const rule of rules) {
|
|
156
|
-
const rrule = recurrenceRuleToRRule(rule);
|
|
157
|
-
if (rrule)
|
|
158
|
-
lines.push(`RRULE:${rrule}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Convert a RecurrenceRule to an RRULE string.
|
|
163
|
-
* @param rule Recurrence rule.
|
|
164
|
-
* @return RRULE value or null.
|
|
165
|
-
*/
|
|
166
|
-
function recurrenceRuleToRRule(rule) {
|
|
167
|
-
const parts = [];
|
|
168
|
-
parts.push(`FREQ=${rule.frequency.toUpperCase()}`);
|
|
169
|
-
if (rule.interval)
|
|
170
|
-
parts.push(`INTERVAL=${rule.interval}`);
|
|
171
|
-
if (rule.count)
|
|
172
|
-
parts.push(`COUNT=${rule.count}`);
|
|
173
|
-
if (rule.until)
|
|
174
|
-
parts.push(`UNTIL=${formatLocalDateTime(rule.until)}`);
|
|
175
|
-
if (rule.byDay?.length) {
|
|
176
|
-
const days = rule.byDay
|
|
177
|
-
.map((day) => `${day.nthOfPeriod ?? ""}${day.day.toUpperCase()}`)
|
|
178
|
-
.join(",");
|
|
179
|
-
parts.push(`BYDAY=${days}`);
|
|
180
|
-
}
|
|
181
|
-
if (rule.byMonthDay?.length)
|
|
182
|
-
parts.push(`BYMONTHDAY=${rule.byMonthDay.join(",")}`);
|
|
183
|
-
if (rule.byMonth?.length)
|
|
184
|
-
parts.push(`BYMONTH=${rule.byMonth.join(",")}`);
|
|
185
|
-
if (rule.byYearDay?.length)
|
|
186
|
-
parts.push(`BYYEARDAY=${rule.byYearDay.join(",")}`);
|
|
187
|
-
if (rule.byWeekNo?.length)
|
|
188
|
-
parts.push(`BYWEEKNO=${rule.byWeekNo.join(",")}`);
|
|
189
|
-
if (rule.byHour?.length)
|
|
190
|
-
parts.push(`BYHOUR=${rule.byHour.join(",")}`);
|
|
191
|
-
if (rule.byMinute?.length)
|
|
192
|
-
parts.push(`BYMINUTE=${rule.byMinute.join(",")}`);
|
|
193
|
-
if (rule.bySecond?.length)
|
|
194
|
-
parts.push(`BYSECOND=${rule.bySecond.join(",")}`);
|
|
195
|
-
if (rule.bySetPosition?.length)
|
|
196
|
-
parts.push(`BYSETPOS=${rule.bySetPosition.join(",")}`);
|
|
197
|
-
if (rule.firstDayOfWeek)
|
|
198
|
-
parts.push(`WKST=${rule.firstDayOfWeek.toUpperCase()}`);
|
|
199
|
-
if (rule.rscale)
|
|
200
|
-
parts.push(`RSCALE=${rule.rscale.toUpperCase()}`);
|
|
201
|
-
if (rule.skip)
|
|
202
|
-
parts.push(`SKIP=${rule.skip.toUpperCase()}`);
|
|
203
|
-
return parts.join(";");
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Format a UTCDateTime for iCalendar.
|
|
207
|
-
* @param value UTCDateTime string.
|
|
208
|
-
* @return iCalendar-formatted UTCDateTime.
|
|
209
|
-
*/
|
|
210
|
-
function formatUtcDateTime(value) {
|
|
211
|
-
const normalized = normalizeUtcDateTime(value);
|
|
212
|
-
return normalized
|
|
213
|
-
.replace(/[-:]/g, "")
|
|
214
|
-
.replace(/\.\d+Z$/, "Z")
|
|
215
|
-
.replace(/Z$/, "Z");
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Format a LocalDateTime for iCalendar.
|
|
219
|
-
* @param value LocalDateTime string.
|
|
220
|
-
* @return iCalendar-formatted LocalDateTime.
|
|
221
|
-
*/
|
|
222
|
-
function formatLocalDateTime(value) {
|
|
223
|
-
return value
|
|
224
|
-
.replace(/[-:]/g, "")
|
|
225
|
-
.replace(/\.\d+$/, "")
|
|
226
|
-
.replace(/Z$/, "");
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Escape text for iCalendar content lines.
|
|
230
|
-
* @param value Raw text.
|
|
231
|
-
* @return Escaped text.
|
|
232
|
-
*/
|
|
233
|
-
function escapeText(value) {
|
|
234
|
-
return value
|
|
235
|
-
.replace(/\\/g, "\\\\")
|
|
236
|
-
.replace(/\n/g, "\\n")
|
|
237
|
-
.replace(/;/g, "\\;")
|
|
238
|
-
.replace(/,/g, "\\,");
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Fold iCalendar lines to 75 octets per RFC.
|
|
242
|
-
* @param lines Lines to fold.
|
|
243
|
-
* @return Folded lines.
|
|
244
|
-
*/
|
|
245
|
-
function foldLines(lines) {
|
|
246
|
-
const result = [];
|
|
247
|
-
for (const line of lines) {
|
|
248
|
-
if (line.length <= 75) {
|
|
249
|
-
result.push(line);
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
let remaining = line;
|
|
253
|
-
result.push(remaining.slice(0, 75));
|
|
254
|
-
remaining = remaining.slice(75);
|
|
255
|
-
while (remaining.length > 0) {
|
|
256
|
-
result.push(` ${remaining.slice(0, 74)}`);
|
|
257
|
-
remaining = remaining.slice(74);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return result;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Strip group entries for X-JSCALENDAR-GROUP payload.
|
|
264
|
-
* @param group Group object.
|
|
265
|
-
* @return Group without entries.
|
|
266
|
-
*/
|
|
267
|
-
function stripEntries(group) {
|
|
268
|
-
const { entries: _entries, ...rest } = group;
|
|
269
|
-
return rest;
|
|
270
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
package/dist/jscal/base.d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { Alert, Id, JSCalendarObject, Location, Participant, PatchLike, VirtualLocation } from "../types.js";
|
|
2
|
-
import type { UpdateOptions } from "./types.js";
|
|
3
|
-
export declare abstract class Base<T extends JSCalendarObject, TPatch extends PatchLike, TSelf extends Base<T, TPatch, TSelf>> {
|
|
4
|
-
data: T;
|
|
5
|
-
/**
|
|
6
|
-
* Create a new base instance that wraps a JSCalendar object.
|
|
7
|
-
* @param data Underlying JSCalendar data.
|
|
8
|
-
* @return Result.
|
|
9
|
-
*/
|
|
10
|
-
constructor(data: T);
|
|
11
|
-
/**
|
|
12
|
-
* Wrap updated data in a new instance.
|
|
13
|
-
* @param data Updated JSCalendar data.
|
|
14
|
-
* @return New instance containing the data.
|
|
15
|
-
*/
|
|
16
|
-
protected abstract wrap(data: T): TSelf;
|
|
17
|
-
/**
|
|
18
|
-
* Return a deep-cloned plain object for safe serialization.
|
|
19
|
-
* @return Cloned JSCalendar data.
|
|
20
|
-
*/
|
|
21
|
-
eject(): T;
|
|
22
|
-
/**
|
|
23
|
-
* Clone the current instance with a deep-cloned payload.
|
|
24
|
-
* @return New instance with cloned data.
|
|
25
|
-
*/
|
|
26
|
-
clone(): TSelf;
|
|
27
|
-
/**
|
|
28
|
-
* Read a field value from the underlying data.
|
|
29
|
-
* @param key Field key.
|
|
30
|
-
* @return Field value.
|
|
31
|
-
*/
|
|
32
|
-
get<K extends keyof T>(key: K): T[K];
|
|
33
|
-
/**
|
|
34
|
-
* Set a field value and update metadata as needed.
|
|
35
|
-
* @param key Field key.
|
|
36
|
-
* @param value Field value.
|
|
37
|
-
* @return New instance with the updated field.
|
|
38
|
-
*/
|
|
39
|
-
set<K extends keyof T>(key: K, value: T[K]): TSelf;
|
|
40
|
-
/**
|
|
41
|
-
* Apply a PatchObject and touch updated/sequence metadata.
|
|
42
|
-
* @param patch Patch to apply.
|
|
43
|
-
* @param options Update options.
|
|
44
|
-
* @return New instance with applied patch.
|
|
45
|
-
*/
|
|
46
|
-
patch(patch: TPatch, options?: UpdateOptions): TSelf;
|
|
47
|
-
/**
|
|
48
|
-
* Add a physical location and return a new instance.
|
|
49
|
-
* @param location Location data (without @type).
|
|
50
|
-
* @param id Optional location ID.
|
|
51
|
-
* @return New instance with the added location.
|
|
52
|
-
*/
|
|
53
|
-
addLocation(location: Omit<Location, "@type"> & Partial<Pick<Location, "@type">>, id?: Id): TSelf;
|
|
54
|
-
/**
|
|
55
|
-
* Add a virtual location and return a new instance.
|
|
56
|
-
* @param location Virtual location data (without @type).
|
|
57
|
-
* @param id Optional virtual location ID.
|
|
58
|
-
* @return New instance with the added virtual location.
|
|
59
|
-
*/
|
|
60
|
-
addVirtualLocation(location: Omit<VirtualLocation, "@type"> & Partial<Pick<VirtualLocation, "@type">>, id?: Id): TSelf;
|
|
61
|
-
/**
|
|
62
|
-
* Add a participant and return a new instance.
|
|
63
|
-
* @param participant Participant data (without @type).
|
|
64
|
-
* @param id Optional participant ID.
|
|
65
|
-
* @return New instance with the added participant.
|
|
66
|
-
*/
|
|
67
|
-
addParticipant(participant: Omit<Participant, "@type"> & Partial<Pick<Participant, "@type">>, id?: Id): TSelf;
|
|
68
|
-
/**
|
|
69
|
-
* Add an alert and return a new instance.
|
|
70
|
-
* @param alert Alert data (without @type).
|
|
71
|
-
* @param id Optional alert ID.
|
|
72
|
-
* @return New instance with the added alert.
|
|
73
|
-
*/
|
|
74
|
-
addAlert(alert: Omit<Alert, "@type"> & Partial<Pick<Alert, "@type">>, id?: Id): TSelf;
|
|
75
|
-
/**
|
|
76
|
-
* Update updated/sequence metadata for modified keys.
|
|
77
|
-
* @param keys Modified keys.
|
|
78
|
-
* @param options Update options.
|
|
79
|
-
* @return Updated instance.
|
|
80
|
-
*/
|
|
81
|
-
protected touchKeys(data: T, keys: string[], options?: UpdateOptions): T;
|
|
82
|
-
/**
|
|
83
|
-
* Update updated/sequence metadata for PatchObject changes.
|
|
84
|
-
* @param patch Patch applied to the object.
|
|
85
|
-
* @param options Update options.
|
|
86
|
-
* @return Updated instance.
|
|
87
|
-
*/
|
|
88
|
-
protected touchFromPatch(data: T, patch: PatchLike, options?: UpdateOptions): T;
|
|
89
|
-
}
|
package/dist/jscal/base.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { applyPatch } from "../patch.js";
|
|
2
|
-
import { deepClone, isNumberValue, nowUtc } from "../utils.js";
|
|
3
|
-
import { validateJsCalendarObject } from "../validate.js";
|
|
4
|
-
import { KEY_PARTICIPANTS } from "./constants.js";
|
|
5
|
-
import { applyAlertDefaults, applyParticipantDefaults } from "./defaults.js";
|
|
6
|
-
import { createId } from "./ids.js";
|
|
7
|
-
export class Base {
|
|
8
|
-
data;
|
|
9
|
-
/**
|
|
10
|
-
* Create a new base instance that wraps a JSCalendar object.
|
|
11
|
-
* @param data Underlying JSCalendar data.
|
|
12
|
-
* @return Result.
|
|
13
|
-
*/
|
|
14
|
-
constructor(data) {
|
|
15
|
-
this.data = data;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Return a deep-cloned plain object for safe serialization.
|
|
19
|
-
* @return Cloned JSCalendar data.
|
|
20
|
-
*/
|
|
21
|
-
eject() {
|
|
22
|
-
return deepClone(this.data);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Clone the current instance with a deep-cloned payload.
|
|
26
|
-
* @return New instance with cloned data.
|
|
27
|
-
*/
|
|
28
|
-
clone() {
|
|
29
|
-
return this.wrap(deepClone(this.data));
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Read a field value from the underlying data.
|
|
33
|
-
* @param key Field key.
|
|
34
|
-
* @return Field value.
|
|
35
|
-
*/
|
|
36
|
-
get(key) {
|
|
37
|
-
return this.data[key];
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Set a field value and update metadata as needed.
|
|
41
|
-
* @param key Field key.
|
|
42
|
-
* @param value Field value.
|
|
43
|
-
* @return New instance with the updated field.
|
|
44
|
-
*/
|
|
45
|
-
set(key, value) {
|
|
46
|
-
const next = deepClone(this.data);
|
|
47
|
-
next[key] = value;
|
|
48
|
-
const touched = this.touchKeys(next, [String(key)]);
|
|
49
|
-
return this.wrap(touched);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Apply a PatchObject and touch updated/sequence metadata.
|
|
53
|
-
* @param patch Patch to apply.
|
|
54
|
-
* @param options Update options.
|
|
55
|
-
* @return New instance with applied patch.
|
|
56
|
-
*/
|
|
57
|
-
patch(patch, options = {}) {
|
|
58
|
-
const next = applyPatch(this.data, patch);
|
|
59
|
-
if (options.validate !== false) {
|
|
60
|
-
validateJsCalendarObject(next);
|
|
61
|
-
}
|
|
62
|
-
const touched = this.touchFromPatch(next, patch, options);
|
|
63
|
-
return this.wrap(touched);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Add a physical location and return a new instance.
|
|
67
|
-
* @param location Location data (without @type).
|
|
68
|
-
* @param id Optional location ID.
|
|
69
|
-
* @return New instance with the added location.
|
|
70
|
-
*/
|
|
71
|
-
addLocation(location, id) {
|
|
72
|
-
const actualId = id ?? createId();
|
|
73
|
-
const next = deepClone(this.data);
|
|
74
|
-
if (!next.locations)
|
|
75
|
-
next.locations = {};
|
|
76
|
-
next.locations[actualId] = { ...location, "@type": "Location" };
|
|
77
|
-
const touched = this.touchKeys(next, ["locations"]);
|
|
78
|
-
return this.wrap(touched);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Add a virtual location and return a new instance.
|
|
82
|
-
* @param location Virtual location data (without @type).
|
|
83
|
-
* @param id Optional virtual location ID.
|
|
84
|
-
* @return New instance with the added virtual location.
|
|
85
|
-
*/
|
|
86
|
-
addVirtualLocation(location, id) {
|
|
87
|
-
const actualId = id ?? createId();
|
|
88
|
-
const next = deepClone(this.data);
|
|
89
|
-
if (!next.virtualLocations)
|
|
90
|
-
next.virtualLocations = {};
|
|
91
|
-
const name = location.name ?? "";
|
|
92
|
-
next.virtualLocations[actualId] = { ...location, name, "@type": "VirtualLocation" };
|
|
93
|
-
const touched = this.touchKeys(next, ["virtualLocations"]);
|
|
94
|
-
return this.wrap(touched);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Add a participant and return a new instance.
|
|
98
|
-
* @param participant Participant data (without @type).
|
|
99
|
-
* @param id Optional participant ID.
|
|
100
|
-
* @return New instance with the added participant.
|
|
101
|
-
*/
|
|
102
|
-
addParticipant(participant, id) {
|
|
103
|
-
const actualId = id ?? createId();
|
|
104
|
-
const next = deepClone(this.data);
|
|
105
|
-
if (!next.participants)
|
|
106
|
-
next.participants = {};
|
|
107
|
-
const filled = applyParticipantDefaults({ ...participant, "@type": "Participant" });
|
|
108
|
-
next.participants[actualId] = filled;
|
|
109
|
-
const touched = this.touchKeys(next, ["participants"], { sequence: false });
|
|
110
|
-
return this.wrap(touched);
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Add an alert and return a new instance.
|
|
114
|
-
* @param alert Alert data (without @type).
|
|
115
|
-
* @param id Optional alert ID.
|
|
116
|
-
* @return New instance with the added alert.
|
|
117
|
-
*/
|
|
118
|
-
addAlert(alert, id) {
|
|
119
|
-
const actualId = id ?? createId();
|
|
120
|
-
const next = deepClone(this.data);
|
|
121
|
-
if (!next.alerts)
|
|
122
|
-
next.alerts = {};
|
|
123
|
-
const filled = applyAlertDefaults({ ...alert, "@type": "Alert" });
|
|
124
|
-
next.alerts[actualId] = filled;
|
|
125
|
-
const touched = this.touchKeys(next, ["alerts"]);
|
|
126
|
-
return this.wrap(touched);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Update updated/sequence metadata for modified keys.
|
|
130
|
-
* @param keys Modified keys.
|
|
131
|
-
* @param options Update options.
|
|
132
|
-
* @return Updated instance.
|
|
133
|
-
*/
|
|
134
|
-
touchKeys(data, keys, options = {}) {
|
|
135
|
-
if (options.touch === false)
|
|
136
|
-
return data;
|
|
137
|
-
const now = options.now ?? nowUtc;
|
|
138
|
-
data.updated = now();
|
|
139
|
-
if (options.sequence === false)
|
|
140
|
-
return data;
|
|
141
|
-
const onlyParticipants = keys.length > 0 && keys.every((key) => key === KEY_PARTICIPANTS);
|
|
142
|
-
if (!onlyParticipants) {
|
|
143
|
-
const current = isNumberValue(data.sequence) ? data.sequence : 0;
|
|
144
|
-
data.sequence = current + 1;
|
|
145
|
-
}
|
|
146
|
-
return data;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Update updated/sequence metadata for PatchObject changes.
|
|
150
|
-
* @param patch Patch applied to the object.
|
|
151
|
-
* @param options Update options.
|
|
152
|
-
* @return Updated instance.
|
|
153
|
-
*/
|
|
154
|
-
touchFromPatch(data, patch, options = {}) {
|
|
155
|
-
if (options.touch === false)
|
|
156
|
-
return data;
|
|
157
|
-
const now = options.now ?? nowUtc;
|
|
158
|
-
data.updated = now();
|
|
159
|
-
if (options.sequence === false)
|
|
160
|
-
return data;
|
|
161
|
-
const pointers = Object.keys(patch);
|
|
162
|
-
const onlyParticipants = pointers.length > 0 &&
|
|
163
|
-
pointers.every((pointer) => {
|
|
164
|
-
const normalized = pointer.startsWith("/") ? pointer.slice(1) : pointer;
|
|
165
|
-
return normalized.startsWith("participants");
|
|
166
|
-
});
|
|
167
|
-
if (!onlyParticipants) {
|
|
168
|
-
const current = isNumberValue(data.sequence) ? data.sequence : 0;
|
|
169
|
-
data.sequence = current + 1;
|
|
170
|
-
}
|
|
171
|
-
return data;
|
|
172
|
-
}
|
|
173
|
-
}
|