iamcal 2.1.2 → 3.0.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.
Files changed (63) hide show
  1. package/lib/component.d.ts +25 -4
  2. package/lib/component.d.ts.map +1 -1
  3. package/lib/component.js +60 -51
  4. package/lib/components/Calendar.d.ts +0 -20
  5. package/lib/components/Calendar.d.ts.map +1 -1
  6. package/lib/components/Calendar.js +2 -24
  7. package/lib/components/CalendarEvent.d.ts +1 -25
  8. package/lib/components/CalendarEvent.d.ts.map +1 -1
  9. package/lib/components/CalendarEvent.js +4 -29
  10. package/lib/components/TimeZone.d.ts +0 -38
  11. package/lib/components/TimeZone.d.ts.map +1 -1
  12. package/lib/components/TimeZone.js +1 -40
  13. package/lib/components/TimeZoneOffset.d.ts +0 -28
  14. package/lib/components/TimeZoneOffset.d.ts.map +1 -1
  15. package/lib/components/TimeZoneOffset.js +1 -30
  16. package/lib/date.d.ts +2 -10
  17. package/lib/date.d.ts.map +1 -1
  18. package/lib/date.js +15 -20
  19. package/lib/parse.d.ts +9 -16
  20. package/lib/parse.d.ts.map +1 -1
  21. package/lib/parse.js +188 -35
  22. package/lib/patterns.d.ts +28 -0
  23. package/lib/patterns.d.ts.map +1 -1
  24. package/lib/patterns.js +56 -2
  25. package/lib/property/Property.d.ts +344 -0
  26. package/lib/property/Property.d.ts.map +1 -0
  27. package/lib/property/Property.js +508 -0
  28. package/lib/property/escape.d.ts +46 -0
  29. package/lib/property/escape.d.ts.map +1 -0
  30. package/lib/property/escape.js +101 -0
  31. package/lib/property/index.d.ts +7 -0
  32. package/lib/property/index.d.ts.map +1 -0
  33. package/lib/property/index.js +23 -0
  34. package/lib/property/names.d.ts +11 -0
  35. package/lib/property/names.d.ts.map +1 -0
  36. package/lib/property/names.js +62 -0
  37. package/lib/property/parameter.d.ts +10 -0
  38. package/lib/property/parameter.d.ts.map +1 -0
  39. package/lib/property/parameter.js +3 -0
  40. package/lib/{property.d.ts → property/validate.d.ts} +9 -35
  41. package/lib/property/validate.d.ts.map +1 -0
  42. package/lib/property/validate.js +317 -0
  43. package/lib/property/valueType.d.ts +18 -0
  44. package/lib/property/valueType.d.ts.map +1 -0
  45. package/lib/property/valueType.js +82 -0
  46. package/package.json +3 -1
  47. package/src/component.ts +58 -52
  48. package/src/components/Calendar.ts +6 -30
  49. package/src/components/CalendarEvent.ts +7 -33
  50. package/src/components/TimeZone.ts +3 -51
  51. package/src/components/TimeZoneOffset.ts +5 -40
  52. package/src/date.ts +14 -30
  53. package/src/parse.ts +212 -40
  54. package/src/patterns.ts +64 -0
  55. package/src/property/Property.ts +609 -0
  56. package/src/property/escape.ts +102 -0
  57. package/src/property/index.ts +6 -0
  58. package/src/property/names.ts +65 -0
  59. package/src/property/parameter.ts +33 -0
  60. package/src/{property.ts → property/validate.ts} +23 -204
  61. package/src/property/valueType.ts +87 -0
  62. package/lib/property.d.ts.map +0 -1
  63. package/lib/property.js +0 -450
@@ -0,0 +1,609 @@
1
+ import { CalendarDateOrTime } from 'src/date'
2
+ import {
3
+ escapePropertyParameterValue,
4
+ escapeTextPropertyValue,
5
+ foldLine,
6
+ } from './escape'
7
+ import {
8
+ AlarmTriggerRelationship,
9
+ CalendarUserType,
10
+ Encoding,
11
+ FreeBusyTimeType,
12
+ ParticipationRole,
13
+ ParticipationStatus,
14
+ RecurrenceIdentifierRange,
15
+ RelationshipType,
16
+ RsvpExpectation,
17
+ } from './parameter'
18
+ import { validateCalendarUserAddress, validateContentType } from './validate'
19
+ import { AllowedValueType, getDefaultValueType } from './valueType'
20
+
21
+ /**
22
+ * Represents a property of a calendar component as described by RFC 5545 in
23
+ * Section 3.5.
24
+ */
25
+ export class Property {
26
+ private _name: string
27
+ public get name(): string {
28
+ return this._name
29
+ }
30
+ public set name(value: string) {
31
+ this._name = value.toUpperCase()
32
+ }
33
+
34
+ value: string
35
+
36
+ private _parameters: Map<string, string[]>
37
+ public get parameters(): Map<string, string[]> {
38
+ return this._parameters
39
+ }
40
+
41
+ constructor(
42
+ name: string,
43
+ value: string,
44
+ params: { [k: string]: string | string[] } = {}
45
+ ) {
46
+ this._name = name.toUpperCase()
47
+ this.value = value
48
+ this._parameters = new Map()
49
+ for (const [key, value] of Object.entries(params)) {
50
+ this._parameters.set(
51
+ key.toUpperCase(),
52
+ typeof value === 'string' ? [value] : value
53
+ )
54
+ }
55
+ }
56
+
57
+ static fromDate(name: string, value: CalendarDateOrTime): Property {
58
+ return new Property(
59
+ name,
60
+ value.getValue(),
61
+ value.isFullDay() ? { VALUE: 'DATE' } : undefined
62
+ )
63
+ }
64
+
65
+ setParameter(name: string, value: string | string[]) {
66
+ this._parameters.set(
67
+ name.toUpperCase(),
68
+ typeof value === 'string' ? [value] : value
69
+ )
70
+ }
71
+
72
+ /**
73
+ * Get the values of a parameter on this property.
74
+ * @param name The name of the parameter to get the values of.
75
+ * @returns The values of the parameter, or undefined if the parameter is unset.
76
+ */
77
+ getParameter(name: string): string[] | undefined {
78
+ return this._parameters.get(name.toUpperCase())
79
+ }
80
+
81
+ /**
82
+ * Get the first value of a parameter.
83
+ * @param name The name of the parameter to get the value of.
84
+ * @returns The first value of the parameter if the parameter is set, else undefined.
85
+ */
86
+ getFirstParameter(name: string): string | undefined {
87
+ return this.getParameter(name.toUpperCase())?.[0]
88
+ }
89
+
90
+ /**
91
+ * Remove all values of a parameter.
92
+ * @param name The name of the parameter to remove.
93
+ */
94
+ removeParameter(name: string) {
95
+ this._parameters.delete(name.toUpperCase())
96
+ }
97
+
98
+ /**
99
+ * Check whether this property has a parameter.
100
+ * @param name The parameter name to check.
101
+ * @returns Whether the parameter is present.
102
+ */
103
+ hasParameter(name: string): boolean {
104
+ return this.getParameter(name.toUpperCase()) !== undefined
105
+ }
106
+
107
+ /**
108
+ * Serialize this property into a contennt line.
109
+ * @returns The serialized content line.
110
+ */
111
+ serialize(): string {
112
+ const escapedParams = []
113
+ for (const [paramName, paramValues] of this._parameters.entries()) {
114
+ const escapedParamValue = paramValues
115
+ .map(paramValue => escapePropertyParameterValue(paramValue))
116
+ .join(',')
117
+ escapedParams.push(`;${paramName}=${escapedParamValue}`)
118
+ }
119
+ const serializedValue =
120
+ this.getValueType() === 'TEXT'
121
+ ? escapeTextPropertyValue(this.value)
122
+ : this.value
123
+ const line = this.name + escapedParams.join('') + ':' + serializedValue
124
+ return foldLine(line)
125
+ }
126
+
127
+ /**
128
+ * Set the value type of this property.
129
+ * @param valueType The new value of the VALUE parameter.
130
+ */
131
+ setValueType(valueType: AllowedValueType) {
132
+ this.setParameter('VALUE', valueType.toUpperCase())
133
+ }
134
+
135
+ /**
136
+ * Remove the explicit value type of this property.
137
+ *
138
+ * Note that this will not remove the inferred value type of this property.
139
+ */
140
+ removeValueType() {
141
+ this.removeParameter('VALUE')
142
+ }
143
+
144
+ /**
145
+ * Get the value type of this property. If the `VALUE` parameter is unset
146
+ * the type will be inferred based on {@link name} or default to 'TEXT' if
147
+ * the property is unknown.
148
+ * @returns The value type of this property.
149
+ */
150
+ getValueType(): AllowedValueType {
151
+ const assignedValueType = this.getParameter('VALUE')?.[0].toUpperCase()
152
+ return assignedValueType ?? getDefaultValueType(this.name)
153
+ }
154
+
155
+ /**
156
+ * Check whether this property has an explicit value type set.
157
+ * @returns Whether this property has the VALUE parameter.
158
+ */
159
+ hasValueType(): boolean {
160
+ return this.hasParameter('VALUE')
161
+ }
162
+
163
+ /**
164
+ * Set the `ALTREP` parameter for this property.
165
+ * @param uri A URI that points to an alternate representation for a textual property value.
166
+ */
167
+ setAlternateTextRepresentation(uri: string) {
168
+ validateCalendarUserAddress(uri)
169
+ this.setParameter('ALTREP', uri)
170
+ }
171
+
172
+ /**
173
+ * Get the `ALTREP` parameter for this property.
174
+ * @returns The value of the `ALTREP` parameter, or undefined if unset.
175
+ */
176
+ getAlternateTextRepresentation(): string | undefined {
177
+ return this.getParameter('ALTREP')?.[0]
178
+ }
179
+
180
+ /**
181
+ * Remove the `ALTREP` parameter for this property.
182
+ */
183
+ removeAlternateTextRepresentation() {
184
+ this.removeParameter('ALTREP')
185
+ }
186
+
187
+ /**
188
+ * Set the `CN` parameter for this property.
189
+ * @param commonName The common name to be associated with the calendar user specified by this property.
190
+ */
191
+ setCommonName(commonName: string) {
192
+ this.setParameter('CN', commonName)
193
+ }
194
+
195
+ /**
196
+ * Get the `CN` parameter for this property.
197
+ * @returns The value of the `CN` parameter, or undefined if unset.
198
+ */
199
+ getCommonName(): string | undefined {
200
+ return this.getParameter('CN')?.[0]
201
+ }
202
+
203
+ /**
204
+ * Remove the `CN` parameter for this property.
205
+ */
206
+ removeCommonName() {
207
+ this.removeParameter('CN')
208
+ }
209
+
210
+ /**
211
+ * Set the `CUTYPE` parameter for this property.
212
+ * @param userType The type of calendar user specified by the property.
213
+ */
214
+ setCalendarUserType(userType: CalendarUserType) {
215
+ this.setParameter('CUTYPE', userType)
216
+ }
217
+
218
+ /**
219
+ * Get the `CUTYPE` parameter for this property.
220
+ * @returns The value of the `CUTYPE` parameter, or undefined if unset.
221
+ */
222
+ getCalendarUserType(): CalendarUserType | undefined {
223
+ return this.getParameter('CUTYPE')?.[0]
224
+ }
225
+
226
+ /**
227
+ * Remove the `CUTYPE` parameter for this property.
228
+ */
229
+ removeCalendarUserType() {
230
+ this.removeParameter('CUTYPE')
231
+ }
232
+
233
+ /**
234
+ * Set the `DELEGATED-FROM` parameter for this property.
235
+ * @param delegators Calendar users whom have been delegated participation in a group-scheduled event or to-do by the calendar user specified by the property.
236
+ */
237
+ setDelegators(...delegators: string[]) {
238
+ delegators.forEach(validateCalendarUserAddress)
239
+ this.setParameter('DELEGATED-FROM', delegators)
240
+ }
241
+
242
+ /**
243
+ * Get the `DELEGATED-FROM` parameter for this property.
244
+ * @returns The value of the `DELEGATED-FROM` parameter, or undefined if unset.
245
+ */
246
+ getDelegators(): string[] | undefined {
247
+ return this.getParameter('DELEGATED-FROM')
248
+ }
249
+
250
+ /**
251
+ * Remove the `DELEGATED-FROM` parameter for this property.
252
+ */
253
+ removeDelegators() {
254
+ this.removeParameter('DELEGATED-FROM')
255
+ }
256
+
257
+ /**
258
+ * Set the `DIR` parameter for this property.
259
+ * @param uri A directory entry associated with the calendar user specified by this property.
260
+ */
261
+ setDirectoryEntryReference(uri: string) {
262
+ validateCalendarUserAddress(uri)
263
+ this.setParameter('DIR', uri)
264
+ }
265
+
266
+ /**
267
+ * Get the `DIR` parameter for this property.
268
+ * @returns The value of the `DIR` parameter, or undefined if unset.
269
+ */
270
+ getDirectoryEntryReference(): string | undefined {
271
+ return this.getParameter('DIR')?.[0]
272
+ }
273
+
274
+ /**
275
+ * Remove the `DIR` parameter for this property.
276
+ */
277
+ removeDirectoryEntryReference() {
278
+ this.removeParameter('DIR')
279
+ }
280
+
281
+ /**
282
+ * Set the `ENCODING` parameter for this property.
283
+ * @param encoding An alternate inline encoding for this property value.
284
+ */
285
+ setEncoding(encoding: Encoding) {
286
+ this.setParameter('ENCODING', encoding)
287
+ }
288
+
289
+ /**
290
+ * Get the `ENCODING` parameter for this property.
291
+ * @returns The value of the `ENCODING` parameter, or undefined if unset.
292
+ */
293
+ getEncoding(): Encoding | undefined {
294
+ return this.getParameter('ENCODING')?.[0] as Encoding | undefined
295
+ }
296
+
297
+ /**
298
+ * Remove the `ENCODING` parameter for this property.
299
+ */
300
+ removeEncoding() {
301
+ this.removeParameter('ENCODING')
302
+ }
303
+
304
+ /**
305
+ * Set the `FMTTYPE` parameter for this property.
306
+ * @param contentType The content type of a referenced object as "type/subtype".
307
+ * @example
308
+ * property.setFormatType('application/msword')
309
+ */
310
+ setFormatType(contentType: string) {
311
+ validateContentType(contentType)
312
+ this.setParameter('FMTTYPE', contentType)
313
+ }
314
+
315
+ /**
316
+ * Get the `FMTTYPE` parameter for this property.
317
+ * @returns The value of the `FMTTYPE` parameter, or undefined if unset.
318
+ */
319
+ getFormatType(): string | undefined {
320
+ return this.getParameter('FMTTYPE')?.[0]
321
+ }
322
+
323
+ /**
324
+ * Remove the `FMTTYPE` parameter for this property.
325
+ */
326
+ removeFormatType() {
327
+ this.removeParameter('FMTTYPE')
328
+ }
329
+
330
+ /**
331
+ * Set the `FBTYPE` parameter for this property.
332
+ * @param freeBusy The free or busy time type.
333
+ */
334
+ setFreeBusyTimeType(freeBusy: FreeBusyTimeType) {
335
+ this.setParameter('FBTYPE', freeBusy)
336
+ }
337
+
338
+ /**
339
+ * Get the `FBTYPE` parameter for this property.
340
+ * @returns The value of the `FBTYPE` parameter, or undefined if unset.
341
+ */
342
+ getFreeBusyTimeType(): FreeBusyTimeType | undefined {
343
+ return this.getParameter('FBTYPE')?.[0]
344
+ }
345
+
346
+ /**
347
+ * Remove the `FBTYPE` parameter for this property.
348
+ */
349
+ removeFreeBusyTimeType() {
350
+ this.removeParameter('FBTYPE')
351
+ }
352
+
353
+ /**
354
+ * Set the `LANGUAGE` parameter for this property.
355
+ * @param language The language tag identifying the language of text values on this property.
356
+ */
357
+ setLanguage(language: string) {
358
+ this.setParameter('LANGUAGE', language)
359
+ }
360
+
361
+ /**
362
+ * Get the `LANGUAGE` parameter for this property.
363
+ * @returns The value of the `LANGUAGE` parameter, or undefined if unset.
364
+ */
365
+ getLanguage(): string | undefined {
366
+ return this.getParameter('LANGUAGE')?.[0]
367
+ }
368
+
369
+ /**
370
+ * Remove the `LANGUAGE` parameter for this property.
371
+ */
372
+ removeLanguage() {
373
+ this.removeParameter('LANGUAGE')
374
+ }
375
+
376
+ /**
377
+ * Set the `MEMBER` parameter for this property.
378
+ * @param groups The group or list memberships of the calendar user specified by this property.
379
+ * @example
380
+ * const property = new ComponentProperty('ATTENDEE', 'mailto:user@example.org')
381
+ * property.setMembership('mailto:group@example.org')
382
+ */
383
+ setMembership(...groups: string[]) {
384
+ groups.forEach(validateCalendarUserAddress)
385
+ this.setParameter('MEMBER', groups)
386
+ }
387
+
388
+ /**
389
+ * Get the `MEMBER` parameter for this property.
390
+ * @returns The value of the `MEMBER` parameter, or undefined if unset.
391
+ */
392
+ getMembership(): string[] | undefined {
393
+ return this.getParameter('MEMBER')
394
+ }
395
+
396
+ /**
397
+ * Remove the `MEMBER` parameter for this property.
398
+ */
399
+ removeMembership() {
400
+ this.removeParameter('MEMBER')
401
+ }
402
+
403
+ /**
404
+ * Set the `PARTSTAT` parameter for this property.
405
+ * @param status The participation status for the calendar user specified by this property.
406
+ */
407
+ setParticipationStatus(status: ParticipationStatus) {
408
+ this.setParameter('PARTSTAT', status)
409
+ }
410
+
411
+ /**
412
+ * Get the `PARTSTAT` parameter for this property.
413
+ * @returns The value of the `PARTSTAT` parameter, or undefined if unset.
414
+ */
415
+ getParticipationStatus(): ParticipationStatus | undefined {
416
+ return this.getParameter('PARTSTAT')?.[0] as
417
+ | ParticipationStatus
418
+ | undefined
419
+ }
420
+
421
+ /**
422
+ * Remove the `PARTSTAT` parameter for this property.
423
+ */
424
+ removeParticipationStatus() {
425
+ this.removeParameter('PARTSTAT')
426
+ }
427
+
428
+ /**
429
+ * Set the `RANGE` parameter for this property.
430
+ * @param range The effective range of recurrence instances from the instance specified by the recurrence identifier specified by this property.
431
+ */
432
+ setRecurrenceIdentifierRange(range: RecurrenceIdentifierRange) {
433
+ this.setParameter('RANGE', range)
434
+ }
435
+
436
+ /**
437
+ * Get the `RANGE` parameter for this property.
438
+ * @returns The value of the `RANGE` parameter, or undefined if unset.
439
+ */
440
+ getRecurrenceIdentifierRange(): RecurrenceIdentifierRange | undefined {
441
+ return this.getParameter('RANGE')?.[0] as
442
+ | RecurrenceIdentifierRange
443
+ | undefined
444
+ }
445
+
446
+ /**
447
+ * Remove the `RANGE` parameter for this property.
448
+ */
449
+ removeRecurrenceIdentifierRange() {
450
+ this.removeParameter('RANGE')
451
+ }
452
+
453
+ /**
454
+ * Set the `RELATED` parameter for this property.
455
+ * @param relationship The relationship of the alarm trigger with respect to the start or end of the calendar component.
456
+ */
457
+ setAlarmTriggerRelationship(relationship: AlarmTriggerRelationship) {
458
+ this.setParameter('RELATED', relationship)
459
+ }
460
+
461
+ /**
462
+ * Get the `RELATED` parameter for this property.
463
+ * @returns The value of the `RELATED` parameter, or undefined if unset.
464
+ */
465
+ getAlarmTriggerRelationship(): AlarmTriggerRelationship | undefined {
466
+ return this.getParameter('RELATED')?.[0] as
467
+ | AlarmTriggerRelationship
468
+ | undefined
469
+ }
470
+
471
+ /**
472
+ * Remove the `RELATED` parameter for this property.
473
+ */
474
+ removeAlarmTriggerRelationship() {
475
+ this.removeParameter('RELATED')
476
+ }
477
+
478
+ /**
479
+ * Set the `RELTYPE` parameter for this property.
480
+ * @param relationship The type of hierarchical relationship associated with the calendar component specified by the property.
481
+ * @example
482
+ * const property = new ComponentProperty('RELATED-TO', 'vevent-uid')
483
+ * property.setRelationshipType('SIBLING')
484
+ */
485
+ setRelationshipType(relationship: RelationshipType) {
486
+ this.setParameter('RELTYPE', relationship)
487
+ }
488
+
489
+ /**
490
+ * Get the `RELTYPE` parameter for this property.
491
+ * @returns The value of the `RELTYPE` parameter, or undefined if unset.
492
+ */
493
+ getRelationshipType(): RelationshipType | undefined {
494
+ return this.getParameter('RELTYPE')?.[0] as RelationshipType | undefined
495
+ }
496
+
497
+ /**
498
+ * Remove the `RELTYPE` parameter for this property.
499
+ */
500
+ removeRelationshipType() {
501
+ this.removeParameter('RELTYPE')
502
+ }
503
+
504
+ /**
505
+ * Set the `ROLE` parameter for this property.
506
+ * @param role The participation role for the calendar user specified by this property.
507
+ * @example
508
+ * const property = new ComponentProperty('ATTENDEE', 'mailto:chair@example.org')
509
+ * property.setParticipationRole('CHAIR')
510
+ */
511
+ setParticipationRole(role: ParticipationRole) {
512
+ this.setParameter('ROLE', role)
513
+ }
514
+
515
+ /**
516
+ * Get the `ROLE` parameter for this property.
517
+ * @returns The value of the `ROLE` parameter, or undefined if unset.
518
+ */
519
+ getParticipationRole(): ParticipationRole | undefined {
520
+ return this.getParameter('ROLE')?.[0] as ParticipationRole | undefined
521
+ }
522
+
523
+ /**
524
+ * Remove the `ROLE` parameter for this property.
525
+ */
526
+ removeParticipationRole() {
527
+ this.removeParameter('ROLE')
528
+ }
529
+
530
+ /**
531
+ * Set the `RSVP` parameter for this property.
532
+ * @param rsvp Whether there is an expectation of a favor of a reply from the calendar user specified by this property value.
533
+ * @example
534
+ * const property = new ComponentProperty('ATTENDEE', 'mailto:user@example.org')
535
+ * property.setRsvpExpectation('TRUE')
536
+ */
537
+ setRsvpExpectation(rsvp: RsvpExpectation) {
538
+ this.setParameter('RSVP', rsvp)
539
+ }
540
+
541
+ /**
542
+ * Get the `RSVP` parameter for this property.
543
+ * @returns The value of the `RSVP` parameter, or undefined if unset.
544
+ */
545
+ getRsvpExpectation(): RsvpExpectation | undefined {
546
+ return this.getParameter('RSVP')?.[0] as RsvpExpectation | undefined
547
+ }
548
+
549
+ /**
550
+ * Remove the `RSVP` parameter for this property.
551
+ */
552
+ removeRsvpExpectation() {
553
+ this.removeParameter('RSVP')
554
+ }
555
+
556
+ /**
557
+ * Set the `SENT-BY` parameter for this property.
558
+ * @param sentBy The calendar user that is acting on behalf of the calendar user specified by the property.
559
+ * @example
560
+ * const property = new ComponentProperty('ORGANIZER', 'malto:jsmith@example.com')
561
+ * property.setSentBy('mailto:sray@example.com')
562
+ */
563
+ setSentBy(sentBy: string) {
564
+ validateCalendarUserAddress(sentBy)
565
+ this.setParameter('SENT-BY', sentBy)
566
+ }
567
+
568
+ /**
569
+ * Get the `SENT-BY` parameter for this property.
570
+ * @returns The value of the `SENT-BY` parameter, or undefined if unset.
571
+ */
572
+ getSentBy(): string | undefined {
573
+ return this.getParameter('SENT-BY')?.[0]
574
+ }
575
+
576
+ /**
577
+ * Remove the `SENT-BY` parameter for this property.
578
+ */
579
+ removeSentBy() {
580
+ this.removeParameter('SENT-BY')
581
+ }
582
+
583
+ /**
584
+ * Set the `TZID` parameter for this property.
585
+ * @param sentBy The identifier for the time zone definition for a time component in this property value.
586
+ * @example
587
+ * const property = new ComponentProperty('DTSTART', '20250101T120000')
588
+ * property.setTimeZone('Europe/Stockholm')
589
+ */
590
+ setTimeZone(sentBy: string) {
591
+ validateCalendarUserAddress(sentBy)
592
+ this.setParameter('TZID', sentBy)
593
+ }
594
+
595
+ /**
596
+ * Get the `TZID` parameter for this property.
597
+ * @returns The value of the `TZID` parameter, or undefined if unset.
598
+ */
599
+ getTimeZone(): string | undefined {
600
+ return this.getParameter('TZID')?.[0]
601
+ }
602
+
603
+ /**
604
+ * Remove the `TZID` parameter for this property.
605
+ */
606
+ removeTimeZone() {
607
+ this.removeParameter('TZID')
608
+ }
609
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Escape special characters in a TEXT property value.
3
+ *
4
+ * Note: This method converts both CRLF and LF to '\n'.
5
+ * @param value The property value to escape.
6
+ * @returns The escaped property value.
7
+ * @see {@link unescapeTextPropertyValue}
8
+ */
9
+ export function escapeTextPropertyValue(value: string): string {
10
+ return value.replace(/(?=[,;\\])/g, '\\').replace(/\r?\n/g, '\\n')
11
+ }
12
+
13
+ /**
14
+ * Unescape special characters in a TEXT property value.
15
+ * @param value The property value to unescape.
16
+ * @returns The unescaped property value.
17
+ * @throws If the value contains a bad escaped character, i.e., a character that should not be escaped after a backslash.
18
+ * @see {@link unescapeTextPropertyValue}
19
+ */
20
+ export function unescapeTextPropertyValue(value: string): string {
21
+ const broadBadEscapePattern = /(?<!\\)\\(\\\\)*[^,;\\nN]/
22
+ const broadBadEscape = broadBadEscapePattern.exec(value)
23
+ if (broadBadEscape) {
24
+ const badEscape = value
25
+ .substring(broadBadEscape.index)
26
+ .match(/\\[^,;\\nN]/)!
27
+ const position = broadBadEscape.index + badEscape.index!
28
+ throw new SyntaxError(
29
+ `Bad escaped character '${badEscape[0]}' at position ${position}`
30
+ )
31
+ }
32
+
33
+ const jsonValue = value.replace(/\\N/, '\\n').replace(/\\(?=[,;:"])/g, '')
34
+ return JSON.parse(`"${jsonValue}"`) as string
35
+ }
36
+
37
+ /**
38
+ * Escape a property parameter value.
39
+ * @param param The parameter value to escape.
40
+ * @returns The escaped parameter value.
41
+ * @throws If the parameter value contains a DQUOTE (") character.
42
+ * @see {@link unescapePropertyParameterValue}
43
+ */
44
+ export function escapePropertyParameterValue(param: string): string {
45
+ // Property parameter values MUST NOT contain the DQUOTE character. The
46
+ // DQUOTE character is used as a delimiter for parameter values that
47
+ // contain restricted characters or URI text.
48
+ if (param.includes('"')) {
49
+ throw new Error('Parameter value must not contain DQUOTE (").')
50
+ }
51
+
52
+ // Property parameter values that contain the COLON, SEMICOLON, or COMMA
53
+ // character separators MUST be specified as quoted-string text values.
54
+ if (/[:;,]/.test(param)) {
55
+ return `"${param}"`
56
+ }
57
+ return param
58
+ }
59
+
60
+ /**
61
+ * Unescape a property parameter value.
62
+ * @param param The parameter value to unescape.
63
+ * @returns The unescaped parameter value.
64
+ * @see {@link escapePropertyParameterValue}
65
+ */
66
+ export function unescapePropertyParameterValue(param: string): string {
67
+ if (param.startsWith('"') && param.endsWith('"')) {
68
+ return param.slice(1, -1)
69
+ }
70
+ return param
71
+ }
72
+
73
+ // Max line length as defined by RFC 5545 3.1.
74
+ export const MAX_LINE_LENGTH = 75
75
+
76
+ /**
77
+ * Fold a single line as specified in RFC 5545 3.1.
78
+ * @param line The line to fold.
79
+ * @returns The folded line.
80
+ */
81
+ export function foldLine(line: string): string {
82
+ if (line.length < MAX_LINE_LENGTH) return line
83
+
84
+ const lines = [line.substring(0, MAX_LINE_LENGTH)]
85
+ const matches = line
86
+ .substring(MAX_LINE_LENGTH)
87
+ .matchAll(new RegExp(`.{${MAX_LINE_LENGTH - 2}}`, 'g'))
88
+ for (const match of matches) {
89
+ lines.push(match[0])
90
+ }
91
+
92
+ return lines.join('\r\n ')
93
+ }
94
+
95
+ /**
96
+ * Unfold a single line as specified in RFC 5545 3.1.
97
+ * @param lines The lines to unfold.
98
+ * @returns The unfolded line.
99
+ */
100
+ export function unfoldLine(lines: string): string {
101
+ return lines.replace(/\r\n /g, '')
102
+ }