@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
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { fail } from "./error.js";
|
|
2
|
+
import { TYPE_ABSOLUTE_TRIGGER, TYPE_ALERT, TYPE_LINK, TYPE_LOCATION, TYPE_OFFSET_TRIGGER, TYPE_PARTICIPANT, TYPE_RELATION, TYPE_TIME_ZONE, TYPE_TIME_ZONE_RULE, TYPE_VIRTUAL_LOCATION } from "./constants.js";
|
|
3
|
+
import { assertBoolean, assertBooleanMap, assertContentId, assertId, assertIdBooleanMap, assertInteger, assertLocalDateTime, assertMediaType, assertNonEmptyString, assertPatchObject, assertSignedDuration, assertString, assertTextContentType, assertTimeZone, assertUnsignedInt, assertUtcDateTime, isRecord } from "./asserts.js";
|
|
4
|
+
import { validateRecurrenceRule } from "./validators-recurrence.js";
|
|
5
|
+
import { isObjectValue, isStringValue } from "../utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Validate alert structure.
|
|
8
|
+
* @param value Alert object to validate.
|
|
9
|
+
* @param path JSON pointer path for error messages.
|
|
10
|
+
* @return Nothing.
|
|
11
|
+
*/
|
|
12
|
+
export function validateAlert(value, path) {
|
|
13
|
+
if (value["@type"] !== TYPE_ALERT)
|
|
14
|
+
fail(path, "must have @type Alert");
|
|
15
|
+
if (!value.trigger)
|
|
16
|
+
fail(`${path}.trigger`, "is required");
|
|
17
|
+
if (value.trigger["@type"] === TYPE_OFFSET_TRIGGER) {
|
|
18
|
+
const offset = value.trigger.offset;
|
|
19
|
+
if (!isStringValue(offset)) {
|
|
20
|
+
fail(`${path}.trigger.offset`, "must be a duration string");
|
|
21
|
+
}
|
|
22
|
+
assertSignedDuration(offset, `${path}.trigger.offset`);
|
|
23
|
+
}
|
|
24
|
+
else if (value.trigger["@type"] === TYPE_ABSOLUTE_TRIGGER) {
|
|
25
|
+
const when = value.trigger.when;
|
|
26
|
+
if (!isStringValue(when)) {
|
|
27
|
+
fail(`${path}.trigger.when`, "must be a UTCDateTime string");
|
|
28
|
+
}
|
|
29
|
+
assertUtcDateTime(when, `${path}.trigger.when`);
|
|
30
|
+
}
|
|
31
|
+
assertUtcDateTime(value.acknowledged, `${path}.acknowledged`);
|
|
32
|
+
assertString(value.action, `${path}.action`);
|
|
33
|
+
if (value.relatedTo) {
|
|
34
|
+
if (!isObjectValue(value.relatedTo) || value.relatedTo === null || Array.isArray(value.relatedTo)) {
|
|
35
|
+
fail(`${path}.relatedTo`, "must be an object");
|
|
36
|
+
}
|
|
37
|
+
for (const [key, entry] of Object.entries(value.relatedTo)) {
|
|
38
|
+
if (!entry || !isObjectValue(entry)) {
|
|
39
|
+
fail(`${path}.relatedTo.${key}`, "must be a relation object");
|
|
40
|
+
}
|
|
41
|
+
validateRelation(entry, `${path}.relatedTo.${key}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validate relation structure.
|
|
47
|
+
* @param value Relation object to validate.
|
|
48
|
+
* @param path JSON pointer path for error messages.
|
|
49
|
+
* @return Nothing.
|
|
50
|
+
*/
|
|
51
|
+
export function validateRelation(value, path) {
|
|
52
|
+
if (value["@type"] !== TYPE_RELATION)
|
|
53
|
+
fail(path, "must have @type Relation");
|
|
54
|
+
if (value.relation)
|
|
55
|
+
assertBooleanMap(value.relation, `${path}.relation`);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validate link structure.
|
|
59
|
+
* @param value Link object to validate.
|
|
60
|
+
* @param path JSON pointer path for error messages.
|
|
61
|
+
* @return Nothing.
|
|
62
|
+
*/
|
|
63
|
+
export function validateLink(value, path) {
|
|
64
|
+
if (value["@type"] !== TYPE_LINK)
|
|
65
|
+
fail(path, "must have @type Link");
|
|
66
|
+
assertString(value.href, `${path}.href`);
|
|
67
|
+
if (!value.href)
|
|
68
|
+
fail(`${path}.href`, "is required");
|
|
69
|
+
assertContentId(value.cid, `${path}.cid`);
|
|
70
|
+
assertMediaType(value.contentType, `${path}.contentType`);
|
|
71
|
+
assertUnsignedInt(value.size, `${path}.size`);
|
|
72
|
+
assertString(value.rel, `${path}.rel`);
|
|
73
|
+
assertString(value.display, `${path}.display`);
|
|
74
|
+
assertString(value.title, `${path}.title`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate location structure.
|
|
78
|
+
* @param value Location object to validate.
|
|
79
|
+
* @param path JSON pointer path for error messages.
|
|
80
|
+
* @return Nothing.
|
|
81
|
+
*/
|
|
82
|
+
export function validateLocation(value, path) {
|
|
83
|
+
if (value["@type"] !== TYPE_LOCATION)
|
|
84
|
+
fail(path, "must have @type Location");
|
|
85
|
+
assertId(value.relativeTo, `${path}.relativeTo`);
|
|
86
|
+
assertString(value.name, `${path}.name`);
|
|
87
|
+
assertString(value.description, `${path}.description`);
|
|
88
|
+
if (value.locationTypes)
|
|
89
|
+
assertBooleanMap(value.locationTypes, `${path}.locationTypes`);
|
|
90
|
+
assertString(value.relativeTo, `${path}.relativeTo`);
|
|
91
|
+
assertTimeZone(value.timeZone, `${path}.timeZone`);
|
|
92
|
+
assertString(value.coordinates, `${path}.coordinates`);
|
|
93
|
+
if (value.links) {
|
|
94
|
+
if (!isObjectValue(value.links) || value.links === null || Array.isArray(value.links)) {
|
|
95
|
+
fail(`${path}.links`, "must be an object");
|
|
96
|
+
}
|
|
97
|
+
for (const [key, entry] of Object.entries(value.links)) {
|
|
98
|
+
assertId(key, `${path}.links.${key}`);
|
|
99
|
+
if (!entry || !isObjectValue(entry)) {
|
|
100
|
+
fail(`${path}.links.${key}`, "must be a link object");
|
|
101
|
+
}
|
|
102
|
+
validateLink(entry, `${path}.links.${key}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate virtual location structure.
|
|
108
|
+
* @param value VirtualLocation object to validate.
|
|
109
|
+
* @param path JSON pointer path for error messages.
|
|
110
|
+
* @return Nothing.
|
|
111
|
+
*/
|
|
112
|
+
export function validateVirtualLocation(value, path) {
|
|
113
|
+
if (value["@type"] !== TYPE_VIRTUAL_LOCATION)
|
|
114
|
+
fail(path, "must have @type VirtualLocation");
|
|
115
|
+
assertString(value.name, `${path}.name`);
|
|
116
|
+
assertString(value.description, `${path}.description`);
|
|
117
|
+
assertString(value.uri, `${path}.uri`);
|
|
118
|
+
if (!value.uri)
|
|
119
|
+
fail(`${path}.uri`, "is required");
|
|
120
|
+
if (value.features)
|
|
121
|
+
assertBooleanMap(value.features, `${path}.features`);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validate time zone rule structure.
|
|
125
|
+
* @param value TimeZoneRule object to validate.
|
|
126
|
+
* @param path JSON pointer path for error messages.
|
|
127
|
+
* @return Nothing.
|
|
128
|
+
*/
|
|
129
|
+
export function validateTimeZoneRule(value, path) {
|
|
130
|
+
if (value["@type"] !== TYPE_TIME_ZONE_RULE)
|
|
131
|
+
fail(path, "must have @type TimeZoneRule");
|
|
132
|
+
assertLocalDateTime(value.start, `${path}.start`);
|
|
133
|
+
if (!value.start)
|
|
134
|
+
fail(`${path}.start`, "is required");
|
|
135
|
+
assertString(value.offsetFrom, `${path}.offsetFrom`);
|
|
136
|
+
if (!value.offsetFrom)
|
|
137
|
+
fail(`${path}.offsetFrom`, "is required");
|
|
138
|
+
assertString(value.offsetTo, `${path}.offsetTo`);
|
|
139
|
+
if (!value.offsetTo)
|
|
140
|
+
fail(`${path}.offsetTo`, "is required");
|
|
141
|
+
if (value.recurrenceRules) {
|
|
142
|
+
value.recurrenceRules.forEach((rule, index) => validateRecurrenceRule(rule, `${path}.recurrenceRules[${index}]`));
|
|
143
|
+
}
|
|
144
|
+
if (value.comments) {
|
|
145
|
+
value.comments.forEach((entry, index) => assertString(entry, `${path}.comments[${index}]`));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validate time zone object structure.
|
|
150
|
+
* @param value TimeZone object to validate.
|
|
151
|
+
* @param path JSON pointer path for error messages.
|
|
152
|
+
* @return Nothing.
|
|
153
|
+
*/
|
|
154
|
+
export function validateTimeZoneObject(value, path) {
|
|
155
|
+
if (value["@type"] !== TYPE_TIME_ZONE)
|
|
156
|
+
fail(path, "must have @type TimeZone");
|
|
157
|
+
assertTimeZone(value.tzId, `${path}.tzId`);
|
|
158
|
+
if (!value.tzId)
|
|
159
|
+
fail(`${path}.tzId`, "is required");
|
|
160
|
+
assertUtcDateTime(value.updated, `${path}.updated`);
|
|
161
|
+
assertString(value.url, `${path}.url`);
|
|
162
|
+
assertUtcDateTime(value.validUntil, `${path}.validUntil`);
|
|
163
|
+
if (value.aliases)
|
|
164
|
+
assertBooleanMap(value.aliases, `${path}.aliases`);
|
|
165
|
+
if (value.standard) {
|
|
166
|
+
value.standard.forEach((rule, index) => validateTimeZoneRule(rule, `${path}.standard[${index}]`));
|
|
167
|
+
}
|
|
168
|
+
if (value.daylight) {
|
|
169
|
+
value.daylight.forEach((rule, index) => validateTimeZoneRule(rule, `${path}.daylight[${index}]`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Validate participant structure.
|
|
174
|
+
* @param value Participant object to validate.
|
|
175
|
+
* @param path JSON pointer path for error messages.
|
|
176
|
+
* @return Nothing.
|
|
177
|
+
*/
|
|
178
|
+
export function validateParticipant(value, path) {
|
|
179
|
+
if (value["@type"] !== TYPE_PARTICIPANT)
|
|
180
|
+
fail(path, "must have @type Participant");
|
|
181
|
+
assertString(value.name, `${path}.name`);
|
|
182
|
+
assertString(value.email, `${path}.email`);
|
|
183
|
+
assertString(value.description, `${path}.description`);
|
|
184
|
+
if (value.sendTo) {
|
|
185
|
+
if (!isObjectValue(value.sendTo) || value.sendTo === null || Array.isArray(value.sendTo)) {
|
|
186
|
+
fail(`${path}.sendTo`, "must be an object");
|
|
187
|
+
}
|
|
188
|
+
for (const [key, entry] of Object.entries(value.sendTo)) {
|
|
189
|
+
assertString(key, `${path}.sendTo.${key}`);
|
|
190
|
+
assertString(entry, `${path}.sendTo.${key}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
assertString(value.kind, `${path}.kind`);
|
|
194
|
+
if (value.roles)
|
|
195
|
+
assertBooleanMap(value.roles, `${path}.roles`);
|
|
196
|
+
assertId(value.locationId, `${path}.locationId`);
|
|
197
|
+
assertString(value.language, `${path}.language`);
|
|
198
|
+
assertString(value.participationStatus, `${path}.participationStatus`);
|
|
199
|
+
assertString(value.participationComment, `${path}.participationComment`);
|
|
200
|
+
assertBoolean(value.expectReply, `${path}.expectReply`);
|
|
201
|
+
assertString(value.scheduleAgent, `${path}.scheduleAgent`);
|
|
202
|
+
assertBoolean(value.scheduleForceSend, `${path}.scheduleForceSend`);
|
|
203
|
+
assertUnsignedInt(value.scheduleSequence, `${path}.scheduleSequence`);
|
|
204
|
+
if (value.scheduleStatus) {
|
|
205
|
+
value.scheduleStatus.forEach((entry, index) => assertString(entry, `${path}.scheduleStatus[${index}]`));
|
|
206
|
+
}
|
|
207
|
+
assertUtcDateTime(value.scheduleUpdated, `${path}.scheduleUpdated`);
|
|
208
|
+
assertString(value.sentBy, `${path}.sentBy`);
|
|
209
|
+
assertId(value.invitedBy, `${path}.invitedBy`);
|
|
210
|
+
if (value.delegatedTo)
|
|
211
|
+
assertIdBooleanMap(value.delegatedTo, `${path}.delegatedTo`);
|
|
212
|
+
if (value.delegatedFrom)
|
|
213
|
+
assertIdBooleanMap(value.delegatedFrom, `${path}.delegatedFrom`);
|
|
214
|
+
if (value.memberOf)
|
|
215
|
+
assertIdBooleanMap(value.memberOf, `${path}.memberOf`);
|
|
216
|
+
if (value.links) {
|
|
217
|
+
if (!isObjectValue(value.links) || value.links === null || Array.isArray(value.links)) {
|
|
218
|
+
fail(`${path}.links`, "must be an object");
|
|
219
|
+
}
|
|
220
|
+
for (const [key, entry] of Object.entries(value.links)) {
|
|
221
|
+
assertId(key, `${path}.links.${key}`);
|
|
222
|
+
if (!entry || !isObjectValue(entry)) {
|
|
223
|
+
fail(`${path}.links.${key}`, "must be a link object");
|
|
224
|
+
}
|
|
225
|
+
validateLink(entry, `${path}.links.${key}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
assertString(value.progress, `${path}.progress`);
|
|
229
|
+
assertUtcDateTime(value.progressUpdated, `${path}.progressUpdated`);
|
|
230
|
+
assertUnsignedInt(value.percentComplete, `${path}.percentComplete`);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Validate common structure.
|
|
234
|
+
* @param value JSCalendar object to validate.
|
|
235
|
+
* @param path JSON pointer path for error messages.
|
|
236
|
+
* @return Nothing.
|
|
237
|
+
*/
|
|
238
|
+
export function validateCommon(value, path) {
|
|
239
|
+
assertNonEmptyString(value.uid, `${path}.uid`);
|
|
240
|
+
if (!value.uid)
|
|
241
|
+
fail(`${path}.uid`, "is required");
|
|
242
|
+
assertUtcDateTime(value.updated, `${path}.updated`);
|
|
243
|
+
assertUtcDateTime(value.created, `${path}.created`);
|
|
244
|
+
assertUnsignedInt(value.sequence, `${path}.sequence`);
|
|
245
|
+
assertString(value.method, `${path}.method`);
|
|
246
|
+
if (value.method && value.method !== value.method.toLowerCase()) {
|
|
247
|
+
fail(`${path}.method`, "must be lowercase");
|
|
248
|
+
}
|
|
249
|
+
assertString(value.title, `${path}.title`);
|
|
250
|
+
assertString(value.description, `${path}.description`);
|
|
251
|
+
assertTextContentType(value.descriptionContentType, `${path}.descriptionContentType`);
|
|
252
|
+
assertBoolean(value.showWithoutTime, `${path}.showWithoutTime`);
|
|
253
|
+
if (value.relatedTo) {
|
|
254
|
+
if (!isObjectValue(value.relatedTo) || value.relatedTo === null || Array.isArray(value.relatedTo)) {
|
|
255
|
+
fail(`${path}.relatedTo`, "must be an object");
|
|
256
|
+
}
|
|
257
|
+
for (const [key, entry] of Object.entries(value.relatedTo)) {
|
|
258
|
+
assertNonEmptyString(key, `${path}.relatedTo.${key}`);
|
|
259
|
+
if (!entry || !isObjectValue(entry)) {
|
|
260
|
+
fail(`${path}.relatedTo.${key}`, "must be a relation object");
|
|
261
|
+
}
|
|
262
|
+
validateRelation(entry, `${path}.relatedTo.${key}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (value.keywords)
|
|
266
|
+
assertBooleanMap(value.keywords, `${path}.keywords`);
|
|
267
|
+
if (value.categories)
|
|
268
|
+
assertBooleanMap(value.categories, `${path}.categories`);
|
|
269
|
+
assertString(value.color, `${path}.color`);
|
|
270
|
+
assertLocalDateTime(value.recurrenceId, `${path}.recurrenceId`);
|
|
271
|
+
assertTimeZone(value.recurrenceIdTimeZone, `${path}.recurrenceIdTimeZone`);
|
|
272
|
+
if (value.recurrenceRules) {
|
|
273
|
+
value.recurrenceRules.forEach((rule, index) => validateRecurrenceRule(rule, `${path}.recurrenceRules[${index}]`));
|
|
274
|
+
}
|
|
275
|
+
if (value.excludedRecurrenceRules) {
|
|
276
|
+
value.excludedRecurrenceRules.forEach((rule, index) => validateRecurrenceRule(rule, `${path}.excludedRecurrenceRules[${index}]`));
|
|
277
|
+
}
|
|
278
|
+
if (value.recurrenceOverrides) {
|
|
279
|
+
if (!isObjectValue(value.recurrenceOverrides) || value.recurrenceOverrides === null || Array.isArray(value.recurrenceOverrides)) {
|
|
280
|
+
fail(`${path}.recurrenceOverrides`, "must be an object");
|
|
281
|
+
}
|
|
282
|
+
for (const [key, entry] of Object.entries(value.recurrenceOverrides)) {
|
|
283
|
+
assertLocalDateTime(key, `${path}.recurrenceOverrides.${key}`);
|
|
284
|
+
assertPatchObject(entry, `${path}.recurrenceOverrides.${key}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
assertBoolean(value.excluded, `${path}.excluded`);
|
|
288
|
+
assertInteger(value.priority, `${path}.priority`);
|
|
289
|
+
assertString(value.freeBusyStatus, `${path}.freeBusyStatus`);
|
|
290
|
+
assertString(value.privacy, `${path}.privacy`);
|
|
291
|
+
if (value.replyTo) {
|
|
292
|
+
if (!isObjectValue(value.replyTo) || value.replyTo === null || Array.isArray(value.replyTo)) {
|
|
293
|
+
fail(`${path}.replyTo`, "must be an object");
|
|
294
|
+
}
|
|
295
|
+
for (const [key, entry] of Object.entries(value.replyTo)) {
|
|
296
|
+
assertString(entry, `${path}.replyTo.${key}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
assertString(value.sentBy, `${path}.sentBy`);
|
|
300
|
+
if (value.locations) {
|
|
301
|
+
if (!isObjectValue(value.locations) || value.locations === null || Array.isArray(value.locations)) {
|
|
302
|
+
fail(`${path}.locations`, "must be an object");
|
|
303
|
+
}
|
|
304
|
+
for (const [key, entry] of Object.entries(value.locations)) {
|
|
305
|
+
assertId(key, `${path}.locations.${key}`);
|
|
306
|
+
if (!entry || !isObjectValue(entry)) {
|
|
307
|
+
fail(`${path}.locations.${key}`, "must be a location object");
|
|
308
|
+
}
|
|
309
|
+
validateLocation(entry, `${path}.locations.${key}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (value.virtualLocations) {
|
|
313
|
+
if (!isObjectValue(value.virtualLocations) || value.virtualLocations === null || Array.isArray(value.virtualLocations)) {
|
|
314
|
+
fail(`${path}.virtualLocations`, "must be an object");
|
|
315
|
+
}
|
|
316
|
+
for (const [key, entry] of Object.entries(value.virtualLocations)) {
|
|
317
|
+
assertId(key, `${path}.virtualLocations.${key}`);
|
|
318
|
+
if (!entry || !isObjectValue(entry)) {
|
|
319
|
+
fail(`${path}.virtualLocations.${key}`, "must be a virtual location object");
|
|
320
|
+
}
|
|
321
|
+
validateVirtualLocation(entry, `${path}.virtualLocations.${key}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (value.links) {
|
|
325
|
+
if (!isObjectValue(value.links) || value.links === null || Array.isArray(value.links)) {
|
|
326
|
+
fail(`${path}.links`, "must be an object");
|
|
327
|
+
}
|
|
328
|
+
for (const [key, entry] of Object.entries(value.links)) {
|
|
329
|
+
assertId(key, `${path}.links.${key}`);
|
|
330
|
+
if (!entry || !isObjectValue(entry)) {
|
|
331
|
+
fail(`${path}.links.${key}`, "must be a link object");
|
|
332
|
+
}
|
|
333
|
+
validateLink(entry, `${path}.links.${key}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (value.participants) {
|
|
337
|
+
if (!isObjectValue(value.participants) || value.participants === null || Array.isArray(value.participants)) {
|
|
338
|
+
fail(`${path}.participants`, "must be an object");
|
|
339
|
+
}
|
|
340
|
+
for (const [key, entry] of Object.entries(value.participants)) {
|
|
341
|
+
assertId(key, `${path}.participants.${key}`);
|
|
342
|
+
if (!entry || !isObjectValue(entry)) {
|
|
343
|
+
fail(`${path}.participants.${key}`, "must be a participant object");
|
|
344
|
+
}
|
|
345
|
+
validateParticipant(entry, `${path}.participants.${key}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
assertString(value.requestStatus, `${path}.requestStatus`);
|
|
349
|
+
assertBoolean(value.useDefaultAlerts, `${path}.useDefaultAlerts`);
|
|
350
|
+
if (value.alerts) {
|
|
351
|
+
if (!isObjectValue(value.alerts) || value.alerts === null || Array.isArray(value.alerts)) {
|
|
352
|
+
fail(`${path}.alerts`, "must be an object");
|
|
353
|
+
}
|
|
354
|
+
for (const [key, entry] of Object.entries(value.alerts)) {
|
|
355
|
+
assertId(key, `${path}.alerts.${key}`);
|
|
356
|
+
if (!entry || !isObjectValue(entry)) {
|
|
357
|
+
fail(`${path}.alerts.${key}`, "must be an alert object");
|
|
358
|
+
}
|
|
359
|
+
validateAlert(entry, `${path}.alerts.${key}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (value.localizations) {
|
|
363
|
+
if (!isRecord(value.localizations))
|
|
364
|
+
fail(`${path}.localizations`, "must be an object");
|
|
365
|
+
for (const [key, entry] of Object.entries(value.localizations)) {
|
|
366
|
+
assertPatchObject(entry, `${path}.localizations.${key}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
assertTimeZone(value.timeZone, `${path}.timeZone`);
|
|
370
|
+
if (value.timeZones) {
|
|
371
|
+
const timeZones = value.timeZones;
|
|
372
|
+
if (!isObjectValue(timeZones) || timeZones === null || Array.isArray(timeZones)) {
|
|
373
|
+
fail(`${path}.timeZones`, "must be an object");
|
|
374
|
+
}
|
|
375
|
+
for (const [key] of Object.entries(timeZones)) {
|
|
376
|
+
assertTimeZone(key, `${path}.timeZones.${key}`);
|
|
377
|
+
}
|
|
378
|
+
for (const [key, entry] of Object.entries(timeZones)) {
|
|
379
|
+
if (!entry || !isObjectValue(entry)) {
|
|
380
|
+
fail(`${path}.timeZones.${key}`, "must be a time zone object");
|
|
381
|
+
}
|
|
382
|
+
validateTimeZoneObject(entry, `${path}.timeZones.${key}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSCalendarObject } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate js calendar object structure.
|
|
4
|
+
* @param value JSCalendar object to validate.
|
|
5
|
+
* @param path JSON pointer path for error messages.
|
|
6
|
+
* @return Nothing.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateJsCalendarObject(value: JSCalendarObject, path?: string): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { fail } from "./error.js";
|
|
2
|
+
import { TYPE_EVENT, TYPE_GROUP, TYPE_TASK } from "./constants.js";
|
|
3
|
+
import { assertDuration, assertLocalDateTime, assertString, assertUnsignedInt, assertUtcDateTime } from "./asserts.js";
|
|
4
|
+
import { validateCommon } from "./validators-common.js";
|
|
5
|
+
import { isObjectValue } from "../utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Validate event structure.
|
|
8
|
+
* @param value Event object to validate.
|
|
9
|
+
* @param path JSON pointer path for error messages.
|
|
10
|
+
* @return Nothing.
|
|
11
|
+
*/
|
|
12
|
+
function validateEvent(value, path) {
|
|
13
|
+
if (value["@type"] !== TYPE_EVENT)
|
|
14
|
+
fail(path, "must have @type Event");
|
|
15
|
+
validateCommon(value, path);
|
|
16
|
+
assertLocalDateTime(value.start, `${path}.start`);
|
|
17
|
+
if (!value.start)
|
|
18
|
+
fail(`${path}.start`, "is required");
|
|
19
|
+
assertDuration(value.duration, `${path}.duration`);
|
|
20
|
+
assertString(value.status, `${path}.status`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate task structure.
|
|
24
|
+
* @param value Task object to validate.
|
|
25
|
+
* @param path JSON pointer path for error messages.
|
|
26
|
+
* @return Nothing.
|
|
27
|
+
*/
|
|
28
|
+
function validateTask(value, path) {
|
|
29
|
+
if (value["@type"] !== TYPE_TASK)
|
|
30
|
+
fail(path, "must have @type Task");
|
|
31
|
+
validateCommon(value, path);
|
|
32
|
+
assertLocalDateTime(value.start, `${path}.start`);
|
|
33
|
+
assertLocalDateTime(value.due, `${path}.due`);
|
|
34
|
+
assertDuration(value.estimatedDuration, `${path}.estimatedDuration`);
|
|
35
|
+
assertUnsignedInt(value.percentComplete, `${path}.percentComplete`);
|
|
36
|
+
assertString(value.progress, `${path}.progress`);
|
|
37
|
+
assertUtcDateTime(value.progressUpdated, `${path}.progressUpdated`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate group structure.
|
|
41
|
+
* @param value Group object to validate.
|
|
42
|
+
* @param path JSON pointer path for error messages.
|
|
43
|
+
* @return Nothing.
|
|
44
|
+
*/
|
|
45
|
+
function validateGroup(value, path) {
|
|
46
|
+
if (value["@type"] !== TYPE_GROUP)
|
|
47
|
+
fail(path, "must have @type Group");
|
|
48
|
+
validateCommon(value, path);
|
|
49
|
+
if (!Array.isArray(value.entries))
|
|
50
|
+
fail(`${path}.entries`, "must be an array");
|
|
51
|
+
value.entries.forEach((entry, index) => validateJsCalendarObject(entry, `${path}.entries[${index}]`));
|
|
52
|
+
assertString(value.source, `${path}.source`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate js calendar object structure.
|
|
56
|
+
* @param value JSCalendar object to validate.
|
|
57
|
+
* @param path JSON pointer path for error messages.
|
|
58
|
+
* @return Nothing.
|
|
59
|
+
*/
|
|
60
|
+
export function validateJsCalendarObject(value, path = "object") {
|
|
61
|
+
if (!value || !isObjectValue(value))
|
|
62
|
+
fail(path, "must be an object");
|
|
63
|
+
if (value["@type"] === TYPE_EVENT)
|
|
64
|
+
return validateEvent(value, path);
|
|
65
|
+
if (value["@type"] === TYPE_TASK)
|
|
66
|
+
return validateTask(value, path);
|
|
67
|
+
if (value["@type"] === TYPE_GROUP)
|
|
68
|
+
return validateGroup(value, path);
|
|
69
|
+
fail(`${path}["@type"]`, "must be Event, Task, or Group");
|
|
70
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NDay, RecurrenceRule } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate n day structure.
|
|
4
|
+
* @param value NDay object to validate.
|
|
5
|
+
* @param path JSON pointer path for error messages.
|
|
6
|
+
* @return Nothing.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateNDay(value: NDay, path: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Validate recurrence rule structure.
|
|
11
|
+
* @param value RecurrenceRule object to validate.
|
|
12
|
+
* @param path JSON pointer path for error messages.
|
|
13
|
+
* @return Nothing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateRecurrenceRule(value: RecurrenceRule, path: string): void;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { fail } from "./error.js";
|
|
2
|
+
import { DAY_OF_WEEK, RECURRENCE_FREQUENCY, RSCALE_GREGORIAN, SKIP, TYPE_NDAY, TYPE_RECURRENCE_RULE } from "./constants.js";
|
|
3
|
+
import { assertInteger, assertLocalDateTime, assertUnsignedInt } from "./asserts.js";
|
|
4
|
+
import { isNumberValue, isStringValue } from "../utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Validate n day structure.
|
|
7
|
+
* @param value NDay object to validate.
|
|
8
|
+
* @param path JSON pointer path for error messages.
|
|
9
|
+
* @return Nothing.
|
|
10
|
+
*/
|
|
11
|
+
export function validateNDay(value, path) {
|
|
12
|
+
if (value["@type"] !== TYPE_NDAY)
|
|
13
|
+
fail(path, "must have @type NDay");
|
|
14
|
+
if (!DAY_OF_WEEK.has(value.day))
|
|
15
|
+
fail(`${path}.day`, "must be a valid day of week");
|
|
16
|
+
assertInteger(value.nthOfPeriod, `${path}.nthOfPeriod`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate recurrence rule structure.
|
|
20
|
+
* @param value RecurrenceRule object to validate.
|
|
21
|
+
* @param path JSON pointer path for error messages.
|
|
22
|
+
* @return Nothing.
|
|
23
|
+
*/
|
|
24
|
+
export function validateRecurrenceRule(value, path) {
|
|
25
|
+
if (value["@type"] !== TYPE_RECURRENCE_RULE)
|
|
26
|
+
fail(path, "must have @type RecurrenceRule");
|
|
27
|
+
if (!RECURRENCE_FREQUENCY.has(value.frequency))
|
|
28
|
+
fail(`${path}.frequency`, "must be a valid frequency");
|
|
29
|
+
assertUnsignedInt(value.interval, `${path}.interval`);
|
|
30
|
+
assertUnsignedInt(value.count, `${path}.count`);
|
|
31
|
+
if (value.rscale !== undefined && value.rscale !== RSCALE_GREGORIAN) {
|
|
32
|
+
fail(`${path}.rscale`, "only gregorian is supported");
|
|
33
|
+
}
|
|
34
|
+
if (value.skip !== undefined && !SKIP.has(value.skip))
|
|
35
|
+
fail(`${path}.skip`, "must be omit, backward, or forward");
|
|
36
|
+
if (value.firstDayOfWeek !== undefined && !DAY_OF_WEEK.has(value.firstDayOfWeek)) {
|
|
37
|
+
fail(`${path}.firstDayOfWeek`, "must be a valid day of week");
|
|
38
|
+
}
|
|
39
|
+
if (value.byDay) {
|
|
40
|
+
for (let i = 0; i < value.byDay.length; i += 1) {
|
|
41
|
+
const entry = value.byDay[i];
|
|
42
|
+
if (!entry)
|
|
43
|
+
continue;
|
|
44
|
+
validateNDay(entry, `${path}.byDay[${i}]`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (value.byMonthDay) {
|
|
48
|
+
for (let i = 0; i < value.byMonthDay.length; i += 1) {
|
|
49
|
+
const entry = value.byMonthDay[i];
|
|
50
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry === 0 || entry < -31 || entry > 31) {
|
|
51
|
+
fail(`${path}.byMonthDay[${i}]`, "must be an integer between -31 and 31, excluding 0");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (value.byMonth) {
|
|
56
|
+
for (let i = 0; i < value.byMonth.length; i += 1) {
|
|
57
|
+
const entry = value.byMonth[i];
|
|
58
|
+
if (!isStringValue(entry))
|
|
59
|
+
fail(`${path}.byMonth[${i}]`, "must be a string month");
|
|
60
|
+
const numeric = Number.parseInt(entry, 10);
|
|
61
|
+
if (!Number.isInteger(numeric) || numeric < 1 || numeric > 12) {
|
|
62
|
+
fail(`${path}.byMonth[${i}]`, "must be a month number between 1 and 12");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (value.byYearDay) {
|
|
67
|
+
for (let i = 0; i < value.byYearDay.length; i += 1) {
|
|
68
|
+
const entry = value.byYearDay[i];
|
|
69
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry === 0 || entry < -366 || entry > 366) {
|
|
70
|
+
fail(`${path}.byYearDay[${i}]`, "must be an integer between -366 and 366, excluding 0");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (value.byWeekNo) {
|
|
75
|
+
for (let i = 0; i < value.byWeekNo.length; i += 1) {
|
|
76
|
+
const entry = value.byWeekNo[i];
|
|
77
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry === 0 || entry < -53 || entry > 53) {
|
|
78
|
+
fail(`${path}.byWeekNo[${i}]`, "must be an integer between -53 and 53, excluding 0");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (value.byHour) {
|
|
83
|
+
for (let i = 0; i < value.byHour.length; i += 1) {
|
|
84
|
+
const entry = value.byHour[i];
|
|
85
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry < 0 || entry > 23) {
|
|
86
|
+
fail(`${path}.byHour[${i}]`, "must be an integer between 0 and 23");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (value.byMinute) {
|
|
91
|
+
for (let i = 0; i < value.byMinute.length; i += 1) {
|
|
92
|
+
const entry = value.byMinute[i];
|
|
93
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry < 0 || entry > 59) {
|
|
94
|
+
fail(`${path}.byMinute[${i}]`, "must be an integer between 0 and 59");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (value.bySecond) {
|
|
99
|
+
for (let i = 0; i < value.bySecond.length; i += 1) {
|
|
100
|
+
const entry = value.bySecond[i];
|
|
101
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry < 0 || entry > 59) {
|
|
102
|
+
fail(`${path}.bySecond[${i}]`, "must be an integer between 0 and 59");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (value.bySetPosition) {
|
|
107
|
+
for (let i = 0; i < value.bySetPosition.length; i += 1) {
|
|
108
|
+
const entry = value.bySetPosition[i];
|
|
109
|
+
if (!isNumberValue(entry) || !Number.isInteger(entry) || entry === 0) {
|
|
110
|
+
fail(`${path}.bySetPosition[${i}]`, "must be a non-zero integer");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
assertLocalDateTime(value.until, `${path}.until`);
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateJsCalendarObject } from "./validators-objects.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateJsCalendarObject } from "./validators-objects.js";
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
path: string;
|
|
4
|
-
constructor(path: string, message: string);
|
|
5
|
-
}
|
|
6
|
-
export declare function validateJsCalendarObject(value: JSCalendarObject, path?: string): void;
|
|
1
|
+
export { ValidationError } from "./validate/error.js";
|
|
2
|
+
export { validateJsCalendarObject } from "./validate/validators.js";
|