iamcal 2.0.0 → 2.1.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 +47 -11
- package/lib/component.d.ts +46 -9
- package/lib/component.d.ts.map +1 -1
- package/lib/component.js +105 -17
- package/lib/components/Calendar.d.ts +35 -14
- package/lib/components/Calendar.d.ts.map +1 -1
- package/lib/components/Calendar.js +43 -15
- package/lib/components/CalendarEvent.d.ts +84 -22
- package/lib/components/CalendarEvent.d.ts.map +1 -1
- package/lib/components/CalendarEvent.js +142 -67
- package/lib/components/TimeZone.d.ts +62 -39
- package/lib/components/TimeZone.d.ts.map +1 -1
- package/lib/components/TimeZone.js +81 -86
- package/lib/components/TimeZoneOffset.d.ts +103 -0
- package/lib/components/TimeZoneOffset.d.ts.map +1 -0
- package/lib/components/TimeZoneOffset.js +148 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +2 -1
- package/lib/date.d.ts +165 -0
- package/lib/date.d.ts.map +1 -0
- package/lib/date.js +373 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -2
- package/lib/io.d.ts +9 -7
- package/lib/io.d.ts.map +1 -1
- package/lib/io.js +16 -11
- package/lib/parse.d.ts +32 -15
- package/lib/parse.d.ts.map +1 -1
- package/lib/parse.js +55 -53
- package/lib/patterns.d.ts +36 -0
- package/lib/patterns.d.ts.map +1 -0
- package/lib/patterns.js +50 -0
- package/lib/property.d.ts +149 -0
- package/lib/property.d.ts.map +1 -0
- package/lib/property.js +450 -0
- package/package.json +50 -38
- package/src/component.ts +132 -23
- package/src/components/Calendar.ts +58 -20
- package/src/components/CalendarEvent.ts +170 -66
- package/src/components/TimeZone.ts +86 -96
- package/src/components/TimeZoneOffset.ts +187 -0
- package/src/components/index.ts +2 -1
- package/src/date.ts +395 -0
- package/src/index.ts +3 -1
- package/src/io.ts +16 -11
- package/src/parse.ts +71 -51
- package/src/patterns.ts +69 -0
- package/src/property.ts +492 -0
package/src/property.ts
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { parseDateString, parseDateTimeString } from './date'
|
|
2
|
+
import * as patterns from './patterns'
|
|
3
|
+
import { matchesWholeString } from './patterns'
|
|
4
|
+
|
|
5
|
+
export interface Property {
|
|
6
|
+
name: string
|
|
7
|
+
params: string[]
|
|
8
|
+
value: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const knownPropertyNames = [
|
|
12
|
+
'CALSCALE',
|
|
13
|
+
'METHOD',
|
|
14
|
+
'PRODID',
|
|
15
|
+
'VERSION',
|
|
16
|
+
'ATTACH',
|
|
17
|
+
'CATEGORIES',
|
|
18
|
+
'CLASS',
|
|
19
|
+
'COMMENT',
|
|
20
|
+
'DESCRIPTION',
|
|
21
|
+
'GEO',
|
|
22
|
+
'LOCATION',
|
|
23
|
+
'PERCENT-COMPLETE',
|
|
24
|
+
'PRIORITY',
|
|
25
|
+
'RESOURCES',
|
|
26
|
+
'STATUS',
|
|
27
|
+
'SUMMARY',
|
|
28
|
+
'COMPLETED',
|
|
29
|
+
'DTEND',
|
|
30
|
+
'DUE',
|
|
31
|
+
'DTSTART',
|
|
32
|
+
'DURATION',
|
|
33
|
+
'FREEBUSY',
|
|
34
|
+
'TRANSP',
|
|
35
|
+
'TZID',
|
|
36
|
+
'TZNAME',
|
|
37
|
+
'TZOFFSETFROM',
|
|
38
|
+
'TZOFFSETTO',
|
|
39
|
+
'TZURL',
|
|
40
|
+
'ATTENDEE',
|
|
41
|
+
'CONTACT',
|
|
42
|
+
'ORGANIZER',
|
|
43
|
+
'RECURRENCE-ID',
|
|
44
|
+
'RELATED-TO',
|
|
45
|
+
'URL',
|
|
46
|
+
'UID',
|
|
47
|
+
'EXDATE',
|
|
48
|
+
'RDATE',
|
|
49
|
+
'RRULE',
|
|
50
|
+
'ACTION',
|
|
51
|
+
'REPEAT',
|
|
52
|
+
'TRIGGER',
|
|
53
|
+
'CREATED',
|
|
54
|
+
'DTSTAMP',
|
|
55
|
+
'LAST-MODIFIED',
|
|
56
|
+
'SEQUENCE',
|
|
57
|
+
'REQUEST-STATUS',
|
|
58
|
+
] as const
|
|
59
|
+
export type KnownPropertyName = (typeof knownPropertyNames)[number]
|
|
60
|
+
export type AllowedPropertyName =
|
|
61
|
+
| KnownPropertyName
|
|
62
|
+
| (`X-${string}` & {})
|
|
63
|
+
| (string & {})
|
|
64
|
+
|
|
65
|
+
export const knownValueTypes = [
|
|
66
|
+
'BINARY',
|
|
67
|
+
'BOOLEAN',
|
|
68
|
+
'CAL-ADDRESS',
|
|
69
|
+
'DATE',
|
|
70
|
+
'DATE-TIME',
|
|
71
|
+
'DURATION',
|
|
72
|
+
'FLOAT',
|
|
73
|
+
'INTEGER',
|
|
74
|
+
'PERIOD',
|
|
75
|
+
'RECUR',
|
|
76
|
+
'TEXT',
|
|
77
|
+
'TIME',
|
|
78
|
+
'URI',
|
|
79
|
+
'UTC-OFFSET',
|
|
80
|
+
] as const
|
|
81
|
+
export type KnownValueType = (typeof knownValueTypes)[number]
|
|
82
|
+
export type AllowedValueType = KnownValueType | (string & {})
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The value types that each property supports as defined by the iCalendar
|
|
86
|
+
* specification. The first in the list is the default type.
|
|
87
|
+
*/
|
|
88
|
+
export const supportedValueTypes: {
|
|
89
|
+
[name in KnownPropertyName]: KnownValueType[]
|
|
90
|
+
} = {
|
|
91
|
+
CALSCALE: ['TEXT'],
|
|
92
|
+
METHOD: ['TEXT'],
|
|
93
|
+
PRODID: ['TEXT'],
|
|
94
|
+
VERSION: ['TEXT'],
|
|
95
|
+
ATTACH: ['URI', 'BINARY'],
|
|
96
|
+
CATEGORIES: ['TEXT'],
|
|
97
|
+
CLASS: ['TEXT'],
|
|
98
|
+
COMMENT: ['TEXT'],
|
|
99
|
+
DESCRIPTION: ['TEXT'],
|
|
100
|
+
GEO: ['FLOAT'],
|
|
101
|
+
LOCATION: ['TEXT'],
|
|
102
|
+
'PERCENT-COMPLETE': ['INTEGER'],
|
|
103
|
+
PRIORITY: ['INTEGER'],
|
|
104
|
+
RESOURCES: ['TEXT'],
|
|
105
|
+
STATUS: ['TEXT'],
|
|
106
|
+
SUMMARY: ['TEXT'],
|
|
107
|
+
COMPLETED: ['DATE-TIME'],
|
|
108
|
+
DTEND: ['DATE-TIME', 'DATE'],
|
|
109
|
+
DUE: ['DATE-TIME', 'DATE'],
|
|
110
|
+
DTSTART: ['DATE-TIME', 'DATE'],
|
|
111
|
+
DURATION: ['DURATION'],
|
|
112
|
+
FREEBUSY: ['PERIOD'],
|
|
113
|
+
TRANSP: ['TEXT'],
|
|
114
|
+
TZID: ['TEXT'],
|
|
115
|
+
TZNAME: ['TEXT'],
|
|
116
|
+
TZOFFSETFROM: ['UTC-OFFSET'],
|
|
117
|
+
TZOFFSETTO: ['UTC-OFFSET'],
|
|
118
|
+
TZURL: ['URI'],
|
|
119
|
+
ATTENDEE: ['CAL-ADDRESS'],
|
|
120
|
+
CONTACT: ['TEXT'],
|
|
121
|
+
ORGANIZER: ['CAL-ADDRESS'],
|
|
122
|
+
'RECURRENCE-ID': ['DATE-TIME', 'DATE'],
|
|
123
|
+
'RELATED-TO': ['TEXT'],
|
|
124
|
+
URL: ['URI'],
|
|
125
|
+
UID: ['TEXT'],
|
|
126
|
+
EXDATE: ['DATE-TIME', 'DATE'],
|
|
127
|
+
RDATE: ['DATE-TIME', 'DATE', 'PERIOD'],
|
|
128
|
+
RRULE: ['RECUR'],
|
|
129
|
+
ACTION: ['TEXT'],
|
|
130
|
+
REPEAT: ['INTEGER'],
|
|
131
|
+
TRIGGER: ['DURATION', 'DATE-TIME'],
|
|
132
|
+
CREATED: ['DATE-TIME'],
|
|
133
|
+
DTSTAMP: ['DATE-TIME'],
|
|
134
|
+
'LAST-MODIFIED': ['DATE-TIME'],
|
|
135
|
+
SEQUENCE: ['INTEGER'],
|
|
136
|
+
'REQUEST-STATUS': ['TEXT'],
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the value type of a property, as defined by the VALUE parameter.
|
|
141
|
+
* @param property The property to get the value type of.
|
|
142
|
+
* @returns The value type if present, else `undefined`.
|
|
143
|
+
* @throws If the parameter value is misformed.
|
|
144
|
+
*/
|
|
145
|
+
export function getPropertyValueType(
|
|
146
|
+
property: Property
|
|
147
|
+
): AllowedValueType | undefined
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get the value type of a property, as defined by the VALUE parameter.
|
|
151
|
+
* @param property The property to get the value type of.
|
|
152
|
+
* @param defaultValue The default value to return if the property VALUE parameter is not present.
|
|
153
|
+
* @returns The value type if present, else `defaultValue` or `undefined`.
|
|
154
|
+
* @throws If the parameter value is misformed.
|
|
155
|
+
*/
|
|
156
|
+
export function getPropertyValueType(
|
|
157
|
+
property: Property,
|
|
158
|
+
defaultValue: AllowedValueType
|
|
159
|
+
): AllowedValueType
|
|
160
|
+
export function getPropertyValueType(
|
|
161
|
+
property: Property,
|
|
162
|
+
defaultValue: AllowedValueType | undefined
|
|
163
|
+
): AllowedValueType | undefined
|
|
164
|
+
export function getPropertyValueType(
|
|
165
|
+
property: Property,
|
|
166
|
+
defaultValue?: AllowedValueType | undefined
|
|
167
|
+
): AllowedValueType | undefined {
|
|
168
|
+
const found = property.params.find(param => /^VALUE=.+$/i.test(param))
|
|
169
|
+
if (!found) return defaultValue
|
|
170
|
+
|
|
171
|
+
if (!patterns.matchesWholeString(patterns.paramValue, found)) {
|
|
172
|
+
throw new Error('Parameter value is misformed')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Return as uppercase if known value
|
|
176
|
+
const value = found?.split('=')[1]
|
|
177
|
+
if ((knownValueTypes as readonly string[]).includes(value.toUpperCase())) {
|
|
178
|
+
return value.toUpperCase()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return value
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Represents an error which occurs while validating a calendar property. */
|
|
185
|
+
export class PropertyValidationError extends Error {
|
|
186
|
+
constructor(message: string) {
|
|
187
|
+
super(message)
|
|
188
|
+
this.name = 'PropertyValidationError'
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Represents an error which occurs if a required property is missing. */
|
|
193
|
+
export class MissingPropertyError extends Error {
|
|
194
|
+
constructor(message: string) {
|
|
195
|
+
super(message)
|
|
196
|
+
this.name = 'MissingPropertyError'
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Validate if a property value is a valid binary string.
|
|
202
|
+
* @param value The property value to validate.
|
|
203
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
204
|
+
*/
|
|
205
|
+
export function validateBinary(value: string) {
|
|
206
|
+
if (!matchesWholeString(patterns.valueTypeBinary, value))
|
|
207
|
+
throw new PropertyValidationError(
|
|
208
|
+
`${value} does not match pattern for BINARY`
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Validate if a property value is a valid boolean.
|
|
214
|
+
* @param value The property value to validate.
|
|
215
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
216
|
+
*/
|
|
217
|
+
export function validateBoolean(value: string) {
|
|
218
|
+
if (!matchesWholeString(patterns.valueTypeBoolean, value))
|
|
219
|
+
throw new PropertyValidationError(
|
|
220
|
+
`${value} does not match pattern for BOOLEAN`
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Validate if a property value is a valid calendar user address.
|
|
226
|
+
* @param value The property value to validate.
|
|
227
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
228
|
+
*/
|
|
229
|
+
export function validateCalendarUserAddress(value: string) {
|
|
230
|
+
try {
|
|
231
|
+
new URL(value)
|
|
232
|
+
} catch {
|
|
233
|
+
throw new PropertyValidationError(
|
|
234
|
+
`${value} does not match pattern for CAL-ADDRESS`
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Validate if a property value is a valid date.
|
|
241
|
+
* @param value The property value to validate.
|
|
242
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
243
|
+
*/
|
|
244
|
+
export function validateDate(value: string) {
|
|
245
|
+
try {
|
|
246
|
+
parseDateString(value)
|
|
247
|
+
} catch {
|
|
248
|
+
throw new PropertyValidationError(
|
|
249
|
+
`${value} does not match pattern for DATE`
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Validate if a property value is a valid date-time.
|
|
256
|
+
* @param value The property value to validate.
|
|
257
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
258
|
+
*/
|
|
259
|
+
export function validateDateTime(value: string) {
|
|
260
|
+
try {
|
|
261
|
+
parseDateTimeString(value)
|
|
262
|
+
} catch {
|
|
263
|
+
throw new PropertyValidationError(
|
|
264
|
+
`${value} does not match pattern for DATETIME`
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Validate if a property value is a valid duration.
|
|
271
|
+
* @param value The property value to validate.
|
|
272
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
273
|
+
*/
|
|
274
|
+
export function validateDuration(value: string) {
|
|
275
|
+
if (!matchesWholeString(patterns.valueTypeDuration, value))
|
|
276
|
+
throw new PropertyValidationError(
|
|
277
|
+
`${value} does not match pattern for DURATION`
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Validate if a property value is a valid float.
|
|
283
|
+
* @param value The property value to validate.
|
|
284
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
285
|
+
*/
|
|
286
|
+
export function validateFloat(value: string) {
|
|
287
|
+
if (!matchesWholeString(patterns.valueTypeFloat, value))
|
|
288
|
+
throw new PropertyValidationError(
|
|
289
|
+
`${value} does not match pattern for FLOAT`
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Validate if a property value is a valid integer.
|
|
295
|
+
* @param value The property value to validate.
|
|
296
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
297
|
+
*/
|
|
298
|
+
export function validateInteger(value: string) {
|
|
299
|
+
if (!matchesWholeString(patterns.valueTypeInteger, value))
|
|
300
|
+
throw new PropertyValidationError(
|
|
301
|
+
`${value} does not match pattern for INTEGER`
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Validate if a property value is a valid period.
|
|
307
|
+
* @param value The property value to validate.
|
|
308
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
309
|
+
*/
|
|
310
|
+
export function validatePeriod(value: string) {
|
|
311
|
+
if (!matchesWholeString(patterns.valueTypePeriod, value))
|
|
312
|
+
throw new PropertyValidationError(
|
|
313
|
+
`${value} does not match pattern for PERIOD`
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Validate if a property value is a valid recurrence rule.
|
|
319
|
+
* @param value The property value to validate.
|
|
320
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
321
|
+
*/
|
|
322
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
323
|
+
export function validateRecurrenceRule(value: string) {
|
|
324
|
+
// TODO: Not implemented
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Validate if a property value is valid text.
|
|
329
|
+
* @param value The property value to validate.
|
|
330
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
331
|
+
*/
|
|
332
|
+
export function validateText(value: string) {
|
|
333
|
+
if (!matchesWholeString(patterns.valueTypeText, value))
|
|
334
|
+
throw new PropertyValidationError(
|
|
335
|
+
`${value} does not match pattern for TEXT`
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Validate if a property value is a valid time.
|
|
341
|
+
* @param value The property value to validate.
|
|
342
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
343
|
+
*/
|
|
344
|
+
export function validateTime(value: string) {
|
|
345
|
+
if (!matchesWholeString(patterns.valueTypeTime, value))
|
|
346
|
+
throw new PropertyValidationError(
|
|
347
|
+
`${value} does not match pattern for TIME`
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Validate if a property value is a valid URI.
|
|
353
|
+
* @param value The property value to validate.
|
|
354
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
355
|
+
*/
|
|
356
|
+
export function validateUri(value: string) {
|
|
357
|
+
try {
|
|
358
|
+
new URL(value)
|
|
359
|
+
} catch {
|
|
360
|
+
throw new PropertyValidationError(
|
|
361
|
+
`${value} does not match pattern for URI`
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Validate if a property value is a valid UTC offset.
|
|
368
|
+
* @param value The property value to validate.
|
|
369
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
370
|
+
*/
|
|
371
|
+
export function validateUtcOffset(value: string) {
|
|
372
|
+
if (!matchesWholeString(patterns.valueTypeUtcOffset, value))
|
|
373
|
+
throw new PropertyValidationError(
|
|
374
|
+
`${value} does not match pattern for UTC-OFFSET`
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Validate a property value for a set value type.
|
|
380
|
+
* @param value The property value to validate.
|
|
381
|
+
* @param type The property value type which `value` will be validated against.
|
|
382
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
383
|
+
*/
|
|
384
|
+
export function validateValue(value: string, type: AllowedValueType) {
|
|
385
|
+
switch (type) {
|
|
386
|
+
case 'BINARY':
|
|
387
|
+
validateBinary(value)
|
|
388
|
+
break
|
|
389
|
+
case 'BOOLEAN':
|
|
390
|
+
validateBoolean(value)
|
|
391
|
+
break
|
|
392
|
+
case 'CAL-ADDRESS':
|
|
393
|
+
validateCalendarUserAddress(value)
|
|
394
|
+
break
|
|
395
|
+
case 'DATE':
|
|
396
|
+
validateDate(value)
|
|
397
|
+
break
|
|
398
|
+
case 'DATE-TIME':
|
|
399
|
+
validateDateTime(value)
|
|
400
|
+
break
|
|
401
|
+
case 'DURATION':
|
|
402
|
+
validateDuration(value)
|
|
403
|
+
break
|
|
404
|
+
case 'FLOAT':
|
|
405
|
+
validateFloat(value)
|
|
406
|
+
break
|
|
407
|
+
case 'INTEGER':
|
|
408
|
+
validateInteger(value)
|
|
409
|
+
break
|
|
410
|
+
case 'PERIOD':
|
|
411
|
+
validatePeriod(value)
|
|
412
|
+
break
|
|
413
|
+
case 'RECUR':
|
|
414
|
+
validateRecurrenceRule(value)
|
|
415
|
+
break
|
|
416
|
+
case 'TEXT':
|
|
417
|
+
validateText(value)
|
|
418
|
+
break
|
|
419
|
+
case 'TIME':
|
|
420
|
+
validateTime(value)
|
|
421
|
+
break
|
|
422
|
+
case 'URI':
|
|
423
|
+
validateUri(value)
|
|
424
|
+
break
|
|
425
|
+
case 'UTC-OFFSET':
|
|
426
|
+
validateUtcOffset(value)
|
|
427
|
+
break
|
|
428
|
+
default:
|
|
429
|
+
console.warn(`Cannot validate value, unknown type ${type}`)
|
|
430
|
+
break
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* eslint-disable jsdoc/require-description-complete-sentence --
|
|
435
|
+
* Does not allow line to end with ':'.
|
|
436
|
+
**/
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Validate the value of a property based on it's value type.
|
|
440
|
+
*
|
|
441
|
+
* The validation will fail if the property:
|
|
442
|
+
*
|
|
443
|
+
* - has a value which is not valid for its value type.
|
|
444
|
+
* - has a value type which is not valid for that property.
|
|
445
|
+
* - has no known value type and is invalid TEXT. (see below)
|
|
446
|
+
*
|
|
447
|
+
* Unknown properties are validated as TEXT by if no value type is set, as
|
|
448
|
+
* defined by {@link https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.8|RFC5545#3.8.8.}
|
|
449
|
+
* @param property The property to validate.
|
|
450
|
+
* @throws {PropertyValidationError} If the validation fails.
|
|
451
|
+
*/
|
|
452
|
+
export function validateProperty(property: Property) {
|
|
453
|
+
// Get supported and default types
|
|
454
|
+
let supportedTypes: AllowedValueType[] | undefined = undefined
|
|
455
|
+
let defaultType: AllowedValueType | undefined = undefined
|
|
456
|
+
|
|
457
|
+
if (knownPropertyNames.includes(property.name as KnownPropertyName)) {
|
|
458
|
+
const name = property.name as KnownPropertyName
|
|
459
|
+
supportedTypes = supportedValueTypes[name]
|
|
460
|
+
defaultType = supportedTypes[0]
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Find value type
|
|
464
|
+
const valueType = getPropertyValueType(property, defaultType)
|
|
465
|
+
|
|
466
|
+
// If value type is unknown, validate as TEXT
|
|
467
|
+
if (valueType === undefined) {
|
|
468
|
+
try {
|
|
469
|
+
validateText(property.value)
|
|
470
|
+
} catch (e) {
|
|
471
|
+
throw e instanceof PropertyValidationError
|
|
472
|
+
? new PropertyValidationError(
|
|
473
|
+
`Unknown property ${property.name} is not valid text`
|
|
474
|
+
)
|
|
475
|
+
: e
|
|
476
|
+
}
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Check if type is unsupported
|
|
481
|
+
if (supportedTypes !== undefined && !supportedTypes.includes(valueType)) {
|
|
482
|
+
throw new PropertyValidationError(
|
|
483
|
+
supportedTypes.length === 1
|
|
484
|
+
? `Property ${property.name} has unsupported value type ${valueType}, must be ${supportedTypes[0]}`
|
|
485
|
+
: `Property ${property.name} has unsupported value type ${valueType}, must be one of ${supportedTypes.join(', ')}`
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Validate according to value type
|
|
490
|
+
validateValue(property.value, valueType)
|
|
491
|
+
}
|
|
492
|
+
/* eslint-enable jsdoc/require-description-complete-sentence */
|