@hebcal/icalendar 6.3.4 → 6.3.5

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/dist/icalendar.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/icalendar v6.3.4 */
1
+ /*! @hebcal/icalendar v6.3.5 */
2
2
  import { flags } from '@hebcal/core/dist/esm/event';
3
3
  import { Locale } from '@hebcal/core/dist/esm/locale';
4
4
  import { murmur32HexSync } from 'murmurhash3';
@@ -260,7 +260,7 @@ class IcalEvent {
260
260
  static fold(line) {
261
261
  let isASCII = true;
262
262
  for (let i = 0; i < line.length; i++) {
263
- if (line.charCodeAt(i) > 255) {
263
+ if (line.codePointAt(i) > 255) {
264
264
  isASCII = false;
265
265
  break;
266
266
  }
@@ -277,33 +277,33 @@ class IcalEvent {
277
277
  }
278
278
  // iterate unicode character by character, making sure
279
279
  // that adding a new character would keep the line <= 75 octets
280
+ const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
281
+ const segments = segmenter.segment(line);
282
+ const chars = Array.from(segments, s => s.segment);
280
283
  let result = '';
281
284
  let current = '';
282
285
  let len = 0;
283
- for (let i = 0; i < line.length; i++) {
284
- const char = line[i];
285
- const octets = char.charCodeAt(0) < 256 ? 1 : encoder.encode(char).length;
286
+ for (const ch of chars) {
287
+ const octets = ch.codePointAt(0) < 256 ? 1 : encoder.encode(ch).length;
286
288
  const newlen = len + octets;
287
289
  if (newlen < 75) {
288
- current += char;
290
+ current += ch;
289
291
  len = newlen;
290
292
  }
291
293
  else {
292
294
  result += current + '\r\n ';
293
- line = line.substring(i);
294
- current = '';
295
- len = 0;
296
- i = -1;
295
+ current = ch;
296
+ len = octets;
297
297
  }
298
298
  }
299
299
  return result + current;
300
300
  }
301
301
  static escape(str) {
302
302
  if (str.includes(',')) {
303
- str = str.replaceAll(',', '\\,');
303
+ str = str.replaceAll(',', String.raw `\,`);
304
304
  }
305
305
  if (str.includes(';')) {
306
- str = str.replaceAll(';', '\\;');
306
+ str = str.replaceAll(';', String.raw `\;`);
307
307
  }
308
308
  return str;
309
309
  }
@@ -441,20 +441,29 @@ const localeMap = {
441
441
  ashkenazi: 'en',
442
442
  ashkenazi_romanian: 'ro',
443
443
  };
444
- /**
445
- * Generates an RFC 2445 iCalendar string from an array of IcalEvents
446
- */
447
- async function icalEventsToString(icals, options) {
448
- if (!icals.length)
449
- throw new RangeError('Events can not be empty');
450
- if (!options)
451
- throw new TypeError('Invalid options object');
452
- const stream = [];
453
- const locale = options.locale || 'en';
444
+ async function getVtimezone(tzid) {
445
+ const vtz = vtimezoneCache.get(tzid);
446
+ if (typeof vtz === 'string') {
447
+ return vtz;
448
+ }
449
+ const filename = `./zoneinfo/${tzid}.ics`;
450
+ try {
451
+ const contents = await promises.readFile(filename, 'utf-8');
452
+ const lines = contents.split('\r\n');
453
+ // ignore first 3 and last 1 lines
454
+ const str = lines.slice(3, lines.length - 2).join('\r\n');
455
+ vtimezoneCache.set(tzid, str);
456
+ return str;
457
+ }
458
+ catch (_a) {
459
+ // ignore failure when no timezone definition to read
460
+ return undefined;
461
+ }
462
+ }
463
+ function makeIcalPreamble(opts) {
464
+ const locale = opts.locale || 'en';
454
465
  const lang = locale.length === 2 ? locale : localeMap[locale] || 'en';
455
466
  const uclang = lang.toUpperCase();
456
- const opts = Object.assign({}, options);
457
- opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
458
467
  const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
459
468
  const caldesc = opts.caldesc
460
469
  ? IcalEvent.escape(opts.caldesc)
@@ -473,46 +482,42 @@ async function icalEventsToString(icals, options) {
473
482
  ];
474
483
  if (opts.publishedTTL !== false) {
475
484
  const publishedTTL = opts.publishedTTL || 'P7D';
476
- preamble.push(`REFRESH-INTERVAL;VALUE=DURATION:${publishedTTL}`);
477
- preamble.push(`X-PUBLISHED-TTL:${publishedTTL}`);
485
+ preamble.push(`REFRESH-INTERVAL;VALUE=DURATION:${publishedTTL}`, `X-PUBLISHED-TTL:${publishedTTL}`);
486
+ }
487
+ preamble.push(`X-WR-CALNAME:${title}`, `X-WR-CALDESC:${caldesc}`);
488
+ const relcalid = opts.relcalid;
489
+ if (relcalid) {
490
+ preamble.push(`X-WR-RELCALID:${relcalid}`);
478
491
  }
479
- preamble.push(`X-WR-CALNAME:${title}`);
480
- preamble.push(`X-WR-CALDESC:${caldesc}`);
492
+ const calendarColor = opts.calendarColor;
493
+ if (calendarColor) {
494
+ preamble.push(`X-APPLE-CALENDAR-COLOR:${calendarColor}`);
495
+ }
496
+ return preamble;
497
+ }
498
+ /**
499
+ * Generates an RFC 2445 iCalendar string from an array of IcalEvents
500
+ */
501
+ async function icalEventsToString(icals, options) {
502
+ if (!icals.length)
503
+ throw new RangeError('Events can not be empty');
504
+ if (!options)
505
+ throw new TypeError('Invalid options object');
506
+ const stream = [];
507
+ const preamble = makeIcalPreamble(options);
481
508
  for (const line of preamble.map(IcalEvent.fold)) {
482
509
  stream.push(line);
483
510
  stream.push('\r\n');
484
511
  }
485
- if (opts.relcalid) {
486
- stream.push(IcalEvent.fold(`X-WR-RELCALID:${opts.relcalid}`));
487
- stream.push('\r\n');
488
- }
489
- if (opts.calendarColor) {
490
- stream.push(`X-APPLE-CALENDAR-COLOR:${opts.calendarColor}\r\n`);
491
- }
492
- const location = opts.location;
512
+ const location = options.location;
493
513
  const tzid = location === null || location === void 0 ? void 0 : location.getTzid();
494
514
  if (tzid) {
495
515
  stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
496
- const vtz = vtimezoneCache.get(tzid);
516
+ const vtz = await getVtimezone(tzid);
497
517
  if (typeof vtz === 'string') {
498
518
  stream.push(vtz);
499
519
  stream.push('\r\n');
500
520
  }
501
- else {
502
- const vtimezoneFilename = `./zoneinfo/${tzid}.ics`;
503
- try {
504
- const vtimezoneIcs = await promises.readFile(vtimezoneFilename, 'utf-8');
505
- const lines = vtimezoneIcs.split('\r\n');
506
- // ignore first 3 and last 1 lines
507
- const str = lines.slice(3, lines.length - 2).join('\r\n');
508
- stream.push(str);
509
- stream.push('\r\n');
510
- vtimezoneCache.set(tzid, str);
511
- }
512
- catch (_a) {
513
- // ignore failure when no timezone definition to read
514
- }
515
- }
516
521
  }
517
522
  for (const ical of icals) {
518
523
  const lines = ical.getLines();
@@ -1 +1 @@
1
- export declare const version = "6.3.4";
1
+ export declare const version = "6.3.5";
@@ -1,5 +1,5 @@
1
- /*! @hebcal/icalendar v6.3.4 */
1
+ /*! @hebcal/icalendar v6.3.5 */
2
2
  // DO NOT EDIT THIS AUTO-GENERATED FILE!
3
- const version = '6.3.4';
3
+ const version = '6.3.5';
4
4
 
5
5
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hebcal/icalendar",
3
- "version": "6.3.4",
3
+ "version": "6.3.5",
4
4
  "author": "Michael J. Radwin (https://github.com/mjradwin)",
5
5
  "keywords": [
6
6
  "ical",
@@ -50,13 +50,13 @@
50
50
  },
51
51
  "license": "BSD-2-Clause",
52
52
  "devDependencies": {
53
- "@hebcal/learning": "^6.6.0",
53
+ "@hebcal/learning": "^6.6.1",
54
54
  "@rollup/plugin-typescript": "^12.3.0",
55
- "@types/node": "25.0.6",
55
+ "@types/node": "25.0.10",
56
56
  "gts": "^7.0.0",
57
- "rollup": "^4.55.1",
58
- "typedoc": "^0.28.15",
57
+ "rollup": "^4.56.0",
58
+ "typedoc": "^0.28.16",
59
59
  "typescript": "^5.9.3",
60
- "vitest": "^4.0.16"
60
+ "vitest": "^4.0.18"
61
61
  }
62
62
  }