@hebcal/icalendar 6.3.3 → 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.
@@ -1,4 +1,4 @@
1
- /*! @hebcal/icalendar v6.3.3 */
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';
@@ -8,10 +8,8 @@ import { getHolidayDescription } from '@hebcal/rest-api/dist/esm/holiday';
8
8
  import { makeTorahMemoText } from '@hebcal/rest-api/dist/esm/memo';
9
9
  import { appendIsraelAndTracking } from '@hebcal/rest-api/dist/esm/url';
10
10
  import { makeAnchor } from '@hebcal/rest-api/dist/esm/makeAnchor';
11
- import { promises } from 'fs';
12
-
13
- // DO NOT EDIT THIS AUTO-GENERATED FILE!
14
- const version = '6.3.3';
11
+ import { promises } from 'node:fs';
12
+ import { version } from './pkgVersion.js';
15
13
 
16
14
  const vtimezoneCache = new Map();
17
15
  const CATEGORY = {
@@ -78,6 +76,7 @@ class IcalEvent {
78
76
  const opts = Object.assign({}, options);
79
77
  this.options = opts;
80
78
  this.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
80
  const ev0 = ev;
82
81
  if (typeof ev0.sequence === 'number') {
83
82
  this.sequence = ev0.sequence;
@@ -261,7 +260,7 @@ class IcalEvent {
261
260
  static fold(line) {
262
261
  let isASCII = true;
263
262
  for (let i = 0; i < line.length; i++) {
264
- if (line.charCodeAt(i) > 255) {
263
+ if (line.codePointAt(i) > 255) {
265
264
  isASCII = false;
266
265
  break;
267
266
  }
@@ -278,33 +277,33 @@ class IcalEvent {
278
277
  }
279
278
  // iterate unicode character by character, making sure
280
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);
281
283
  let result = '';
282
284
  let current = '';
283
285
  let len = 0;
284
- for (let i = 0; i < line.length; i++) {
285
- const char = line[i];
286
- 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;
287
288
  const newlen = len + octets;
288
289
  if (newlen < 75) {
289
- current += char;
290
+ current += ch;
290
291
  len = newlen;
291
292
  }
292
293
  else {
293
294
  result += current + '\r\n ';
294
- line = line.substring(i);
295
- current = '';
296
- len = 0;
297
- i = -1;
295
+ current = ch;
296
+ len = octets;
298
297
  }
299
298
  }
300
299
  return result + current;
301
300
  }
302
301
  static escape(str) {
303
- if (str.indexOf(',') !== -1) {
304
- str = str.replace(/,/g, '\\,');
302
+ if (str.includes(',')) {
303
+ str = str.replaceAll(',', String.raw `\,`);
305
304
  }
306
- if (str.indexOf(';') !== -1) {
307
- str = str.replace(/;/g, '\\;');
305
+ if (str.includes(';')) {
306
+ str = str.replaceAll(';', String.raw `\;`);
308
307
  }
309
308
  return str;
310
309
  }
@@ -342,10 +341,13 @@ const HOLIDAY_IGNORE_MASK = DAILY_LEARNING |
342
341
  flags.MOLAD |
343
342
  flags.USER_EVENT |
344
343
  flags.HEBREW_DATE;
344
+ const ESC_NEWLINE = String.raw `\n`;
345
+ const DBL_NEWLINE = ESC_NEWLINE + ESC_NEWLINE;
345
346
  /**
346
347
  * @private
347
348
  */
348
349
  function makeTorahMemo(ev, il) {
350
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
349
351
  if (ev.getFlags() & HOLIDAY_IGNORE_MASK || ev.eventTime) {
350
352
  return '';
351
353
  }
@@ -358,7 +360,7 @@ function makeTorahMemo(ev, il) {
358
360
  if (typeof memo === 'string') {
359
361
  return memo;
360
362
  }
361
- memo = makeTorahMemoText(ev, il).replace(/\n/g, '\\n');
363
+ memo = makeTorahMemoText(ev, il).replaceAll('\n', ESC_NEWLINE);
362
364
  torahMemoCache.set(key, memo);
363
365
  return memo;
364
366
  }
@@ -367,8 +369,8 @@ function makeTorahMemo(ev, il) {
367
369
  */
368
370
  function createMemo(ev, options) {
369
371
  let memo = ev.memo || '';
370
- if (memo.length && memo.indexOf('\n') !== -1) {
371
- memo = memo.replace(/\n/g, '\\n');
372
+ if (memo.length && memo.includes('\n')) {
373
+ memo = memo.replaceAll('\n', ESC_NEWLINE);
372
374
  }
373
375
  const desc = ev.getDesc();
374
376
  if (desc === 'Havdalah' || desc === 'Candle lighting') {
@@ -381,33 +383,34 @@ function createMemo(ev, options) {
381
383
  omerEv.sefira('en'),
382
384
  omerEv.sefira('he'),
383
385
  omerEv.sefira('translit'),
384
- ].join('\\n');
386
+ ].join(ESC_NEWLINE);
385
387
  return (omerEv.getTodayIs('en') +
386
- '\\n\\n' +
388
+ DBL_NEWLINE +
387
389
  omerEv.getTodayIs('he') +
388
- '\\n\\n' +
390
+ DBL_NEWLINE +
389
391
  sefira);
390
392
  }
391
393
  if (!memo) {
392
394
  memo = getHolidayDescription(ev);
393
395
  }
394
396
  if (!memo) {
397
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
395
398
  const linkEv = ev.linkedEvent;
396
- if (typeof linkEv !== 'undefined' && linkEv.getDesc() !== ev.getDesc()) {
399
+ if (linkEv && linkEv.getDesc() !== ev.getDesc()) {
397
400
  memo = linkEv.render(options.locale);
398
401
  }
399
402
  }
400
403
  const torahMemo = makeTorahMemo(ev, options.il);
401
404
  if (torahMemo) {
402
405
  if (memo.length) {
403
- memo += '\\n\\n';
406
+ memo += DBL_NEWLINE;
404
407
  }
405
408
  memo += torahMemo;
406
409
  }
407
410
  const url = appendTrackingToUrl(ev.url(), options);
408
411
  if (url) {
409
412
  if (memo.length) {
410
- memo += '\\n\\n';
413
+ memo += DBL_NEWLINE;
411
414
  }
412
415
  memo += url;
413
416
  }
@@ -438,20 +441,29 @@ const localeMap = {
438
441
  ashkenazi: 'en',
439
442
  ashkenazi_romanian: 'ro',
440
443
  };
441
- /**
442
- * Generates an RFC 2445 iCalendar string from an array of IcalEvents
443
- */
444
- async function icalEventsToString(icals, options) {
445
- if (!icals.length)
446
- throw new RangeError('Events can not be empty');
447
- if (!options)
448
- throw new TypeError('Invalid options object');
449
- const stream = [];
450
- 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';
451
465
  const lang = locale.length === 2 ? locale : localeMap[locale] || 'en';
452
466
  const uclang = lang.toUpperCase();
453
- const opts = Object.assign({}, options);
454
- opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
455
467
  const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
456
468
  const caldesc = opts.caldesc
457
469
  ? IcalEvent.escape(opts.caldesc)
@@ -470,46 +482,42 @@ async function icalEventsToString(icals, options) {
470
482
  ];
471
483
  if (opts.publishedTTL !== false) {
472
484
  const publishedTTL = opts.publishedTTL || 'P7D';
473
- preamble.push(`REFRESH-INTERVAL;VALUE=DURATION:${publishedTTL}`);
474
- preamble.push(`X-PUBLISHED-TTL:${publishedTTL}`);
485
+ preamble.push(`REFRESH-INTERVAL;VALUE=DURATION:${publishedTTL}`, `X-PUBLISHED-TTL:${publishedTTL}`);
475
486
  }
476
- preamble.push(`X-WR-CALNAME:${title}`);
477
- preamble.push(`X-WR-CALDESC:${caldesc}`);
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}`);
491
+ }
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);
478
508
  for (const line of preamble.map(IcalEvent.fold)) {
479
509
  stream.push(line);
480
510
  stream.push('\r\n');
481
511
  }
482
- if (opts.relcalid) {
483
- stream.push(IcalEvent.fold(`X-WR-RELCALID:${opts.relcalid}`));
484
- stream.push('\r\n');
485
- }
486
- if (opts.calendarColor) {
487
- stream.push(`X-APPLE-CALENDAR-COLOR:${opts.calendarColor}\r\n`);
488
- }
489
- const location = opts.location;
512
+ const location = options.location;
490
513
  const tzid = location === null || location === void 0 ? void 0 : location.getTzid();
491
514
  if (tzid) {
492
515
  stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
493
- const vtz = vtimezoneCache.get(tzid);
516
+ const vtz = await getVtimezone(tzid);
494
517
  if (typeof vtz === 'string') {
495
518
  stream.push(vtz);
496
519
  stream.push('\r\n');
497
520
  }
498
- else {
499
- const vtimezoneFilename = `./zoneinfo/${tzid}.ics`;
500
- try {
501
- const vtimezoneIcs = await promises.readFile(vtimezoneFilename, 'utf-8');
502
- const lines = vtimezoneIcs.split('\r\n');
503
- // ignore first 3 and last 1 lines
504
- const str = lines.slice(3, lines.length - 2).join('\r\n');
505
- stream.push(str);
506
- stream.push('\r\n');
507
- vtimezoneCache.set(tzid, str);
508
- }
509
- catch (error) {
510
- // ignore failure when no timezone definition to read
511
- }
512
- }
513
521
  }
514
522
  for (const ical of icals) {
515
523
  const lines = ical.getLines();
@@ -1 +1 @@
1
- export declare const version = "6.3.3";
1
+ export declare const version = "6.3.5";
@@ -0,0 +1,5 @@
1
+ /*! @hebcal/icalendar v6.3.5 */
2
+ // DO NOT EDIT THIS AUTO-GENERATED FILE!
3
+ const version = '6.3.5';
4
+
5
+ export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hebcal/icalendar",
3
- "version": "6.3.3",
3
+ "version": "6.3.5",
4
4
  "author": "Michael J. Radwin (https://github.com/mjradwin)",
5
5
  "keywords": [
6
6
  "ical",
@@ -10,13 +10,13 @@
10
10
  ],
11
11
  "description": "Jewish holidays and Hebrew calendar as iCalendar RFC 2445",
12
12
  "type": "module",
13
- "module": "./dist/index.js",
13
+ "module": "./dist/icalendar.js",
14
14
  "typings": "./dist/icalendar.d.ts",
15
15
  "exports": {
16
- ".": "./dist/index.js"
16
+ ".": "./dist/icalendar.js"
17
17
  },
18
18
  "engines": {
19
- "node": ">= 18.0.0"
19
+ "node": ">= 20"
20
20
  },
21
21
  "files": [
22
22
  "dist"
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "homepage": "https://hebcal.github.io/api/icalendar/",
32
32
  "dependencies": {
33
- "@hebcal/core": "^6.0.6",
33
+ "@hebcal/core": "^6.0.8",
34
34
  "@hebcal/hdate": "^0.21.1",
35
35
  "@hebcal/rest-api": "^6.4.0",
36
36
  "murmurhash3": "^0.5.0",
@@ -40,7 +40,6 @@
40
40
  "build:rollup": "rollup -c",
41
41
  "build:version": "node ./version.cjs package.json src/pkgVersion.ts",
42
42
  "build": "npm run build:version && npm run build:rollup",
43
- "prepare": "npm run build",
44
43
  "pretest": "npm run build",
45
44
  "docs": "typedoc",
46
45
  "test": "vitest",
@@ -51,13 +50,13 @@
51
50
  },
52
51
  "license": "BSD-2-Clause",
53
52
  "devDependencies": {
54
- "@hebcal/learning": "^6.6.0",
53
+ "@hebcal/learning": "^6.6.1",
55
54
  "@rollup/plugin-typescript": "^12.3.0",
56
- "@types/node": "24.9.2",
57
- "gts": "^5.3.1",
58
- "rollup": "^4.53.3",
59
- "typedoc": "^0.28.15",
55
+ "@types/node": "25.0.10",
56
+ "gts": "^7.0.0",
57
+ "rollup": "^4.56.0",
58
+ "typedoc": "^0.28.16",
60
59
  "typescript": "^5.9.3",
61
- "vitest": "^4.0.15"
60
+ "vitest": "^4.0.18"
62
61
  }
63
62
  }