@hebcal/icalendar 6.3.6 → 6.3.8

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.
@@ -0,0 +1 @@
1
+ export declare function foldLine(line: string): string;
@@ -0,0 +1,51 @@
1
+ /*! @hebcal/icalendar v6.3.8 */
2
+ import { Buffer } from 'node:buffer';
3
+
4
+ let foldSegmenter;
5
+ const char74re = /.{1,74}/g;
6
+ function foldLine(line) {
7
+ const lineBytes = Buffer.byteLength(line);
8
+ if (lineBytes <= 74) {
9
+ return line;
10
+ }
11
+ if (lineBytes === line.length) {
12
+ // All-ASCII fast path: every UTF-16 unit encodes to one UTF-8 byte
13
+ // iff the string is pure ASCII, so we can split by JS chars.
14
+ return line.match(char74re).join('\r\n ');
15
+ }
16
+ // Walk grapheme clusters so we never split a multi-octet UTF-8
17
+ // sequence (or a combining/ZWJ/regional-indicator cluster) across
18
+ // a fold boundary. Reuse a single Segmenter to avoid per-call
19
+ // construction cost.
20
+ if (!foldSegmenter) {
21
+ foldSegmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
22
+ }
23
+ const parts = [];
24
+ let chunkStart = 0;
25
+ let len = 0;
26
+ for (const { segment, index } of foldSegmenter.segment(line)) {
27
+ const cp = segment.codePointAt(0);
28
+ // Most segments are a single code point; compute UTF-8 length
29
+ // arithmetically and skip the Buffer.byteLength call.
30
+ const cpUnits = cp >= 0x10000 ? 2 : 1;
31
+ let octets;
32
+ if (segment.length === cpUnits) {
33
+ octets = cp < 0x80 ? 1 : cp < 0x800 ? 2 : cp < 0x10000 ? 3 : 4;
34
+ }
35
+ else {
36
+ octets = Buffer.byteLength(segment);
37
+ }
38
+ if (len + octets >= 75) {
39
+ parts.push(line.slice(chunkStart, index));
40
+ chunkStart = index;
41
+ len = octets;
42
+ }
43
+ else {
44
+ len += octets;
45
+ }
46
+ }
47
+ parts.push(line.slice(chunkStart));
48
+ return parts.join('\r\n ');
49
+ }
50
+
51
+ export { foldLine };
package/dist/icalendar.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/icalendar v6.3.6 */
1
+ /*! @hebcal/icalendar v6.3.8 */
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';
@@ -10,6 +10,7 @@ import { appendIsraelAndTracking } from '@hebcal/rest-api/dist/esm/url';
10
10
  import { makeAnchor } from '@hebcal/rest-api/dist/esm/makeAnchor';
11
11
  import { promises } from 'node:fs';
12
12
  import { version } from './pkgVersion.js';
13
+ import { foldLine } from './foldLine.js';
13
14
 
14
15
  const vtimezoneCache = new Map();
15
16
  const CATEGORY = {
@@ -56,8 +57,6 @@ function appendTrackingToUrl(url, options) {
56
57
  const utmCampaign = options.utmCampaign;
57
58
  return appendIsraelAndTracking(url, options.il, utmSource, utmMedium, utmCampaign);
58
59
  }
59
- const encoder = new TextEncoder();
60
- const char74re = /(.{1,74})/g;
61
60
  const DAILY_LEARNING = flags.DAILY_LEARNING |
62
61
  flags.DAF_YOMI |
63
62
  flags.MISHNA_YOMI |
@@ -67,13 +66,27 @@ const DAILY_LEARNING = flags.DAILY_LEARNING |
67
66
  * Represents an RFC 2445 iCalendar VEVENT
68
67
  */
69
68
  class IcalEvent {
69
+ ev;
70
+ options;
71
+ dtstamp;
72
+ sequence;
73
+ timed;
74
+ locationName;
75
+ startDate;
76
+ isoDateOnly;
77
+ dtargs;
78
+ transp;
79
+ busyStatus;
80
+ endDate;
81
+ subj;
82
+ category;
83
+ lines;
70
84
  /**
71
85
  * Builds an IcalEvent object from a Hebcal Event
72
86
  */
73
87
  constructor(ev, options = {}) {
74
- var _a;
75
88
  this.ev = ev;
76
- const opts = Object.assign({}, options);
89
+ const opts = { ...options };
77
90
  this.options = opts;
78
91
  this.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
79
92
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -112,7 +125,7 @@ class IcalEvent {
112
125
  minute = +minute;
113
126
  this.startDate += 'T' + pad2(hour) + pad2(minute) + '00';
114
127
  this.endDate = this.startDate;
115
- if (location === null || location === void 0 ? void 0 : location.getTzid()) {
128
+ if (location?.getTzid()) {
116
129
  this.dtargs = `;TZID=${location.getTzid()}`;
117
130
  }
118
131
  }
@@ -147,7 +160,7 @@ class IcalEvent {
147
160
  }
148
161
  }
149
162
  this.subj = subj;
150
- this.category = ev0.category || CATEGORY[(_a = getEventCategories(ev)) === null || _a === void 0 ? void 0 : _a[0]];
163
+ this.category = ev0.category || CATEGORY[getEventCategories(ev)?.[0]];
151
164
  }
152
165
  getAlarm() {
153
166
  const ev = this.ev;
@@ -252,51 +265,13 @@ class IcalEvent {
252
265
  * fold lines to 75 characters
253
266
  */
254
267
  getLines() {
255
- return this.getLongLines().map(IcalEvent.fold);
268
+ return this.getLongLines().map(foldLine);
256
269
  }
257
270
  /**
258
271
  * fold line to 75 characters
259
272
  */
260
273
  static fold(line) {
261
- let isASCII = true;
262
- for (let i = 0; i < line.length; i++) {
263
- if (line.codePointAt(i) > 255) {
264
- isASCII = false;
265
- break;
266
- }
267
- }
268
- if (isASCII) {
269
- if (line.length <= 74) {
270
- return line;
271
- }
272
- const matches = line.match(char74re);
273
- return matches.join('\r\n ');
274
- }
275
- if (encoder.encode(line).length <= 74) {
276
- return line;
277
- }
278
- // iterate unicode character by character, making sure
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);
283
- let result = '';
284
- let current = '';
285
- let len = 0;
286
- for (const ch of chars) {
287
- const octets = ch.codePointAt(0) < 256 ? 1 : encoder.encode(ch).length;
288
- const newlen = len + octets;
289
- if (newlen < 75) {
290
- current += ch;
291
- len = newlen;
292
- }
293
- else {
294
- result += current + '\r\n ';
295
- current = ch;
296
- len = octets;
297
- }
298
- }
299
- return result + current;
274
+ return foldLine(line);
300
275
  }
301
276
  static escape(str) {
302
277
  if (str.includes(',')) {
@@ -424,7 +399,7 @@ async function eventsToIcalendar(events, options) {
424
399
  throw new RangeError('Events can not be empty');
425
400
  if (!options)
426
401
  throw new TypeError('Invalid options object');
427
- const opts = Object.assign({}, options);
402
+ const opts = { ...options };
428
403
  opts.dtstamp = opts.dtstamp || IcalEvent.makeDtstamp(new Date());
429
404
  if (!opts.title) {
430
405
  opts.title = getCalendarTitle(events, opts);
@@ -455,7 +430,7 @@ async function getVtimezone(tzid) {
455
430
  vtimezoneCache.set(tzid, str);
456
431
  return str;
457
432
  }
458
- catch (_a) {
433
+ catch {
459
434
  // ignore failure when no timezone definition to read
460
435
  return undefined;
461
436
  }
@@ -465,9 +440,7 @@ function makeIcalPreamble(opts) {
465
440
  const lang = locale.length === 2 ? locale : localeMap[locale] || 'en';
466
441
  const uclang = lang.toUpperCase();
467
442
  const title = opts.title ? IcalEvent.escape(opts.title) : 'Untitled';
468
- const caldesc = opts.caldesc
469
- ? IcalEvent.escape(opts.caldesc)
470
- : undefined;
443
+ const caldesc = opts.caldesc ? IcalEvent.escape(opts.caldesc) : undefined;
471
444
  const prodid = opts.prodid ||
472
445
  `-//hebcal.com/NONSGML Hebcal Calendar v1${version}//${uclang}`;
473
446
  const preamble = [
@@ -506,12 +479,12 @@ async function icalEventsToString(icals, options) {
506
479
  throw new TypeError('Invalid options object');
507
480
  const stream = [];
508
481
  const preamble = makeIcalPreamble(options);
509
- for (const line of preamble.map(IcalEvent.fold)) {
482
+ for (const line of preamble.map(foldLine)) {
510
483
  stream.push(line);
511
484
  stream.push('\r\n');
512
485
  }
513
486
  const location = options.location;
514
- const tzid = location === null || location === void 0 ? void 0 : location.getTzid();
487
+ const tzid = location?.getTzid();
515
488
  if (tzid) {
516
489
  stream.push(`X-WR-TIMEZONE;VALUE=TEXT:${tzid}\r\n`);
517
490
  const vtz = await getVtimezone(tzid);
@@ -1 +1 @@
1
- export declare const version = "6.3.6";
1
+ export declare const version = "6.3.8";
@@ -1,5 +1,5 @@
1
- /*! @hebcal/icalendar v6.3.6 */
1
+ /*! @hebcal/icalendar v6.3.8 */
2
2
  // DO NOT EDIT THIS AUTO-GENERATED FILE!
3
- const version = '6.3.6';
3
+ const version = '6.3.8';
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.6",
3
+ "version": "6.3.8",
4
4
  "author": "Michael J. Radwin (https://github.com/mjradwin)",
5
5
  "keywords": [
6
6
  "ical",
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "homepage": "https://hebcal.github.io/api/icalendar/",
32
32
  "dependencies": {
33
- "@hebcal/core": "^6.0.8",
34
- "@hebcal/hdate": "^0.21.1",
35
- "@hebcal/rest-api": "^6.4.0",
33
+ "@hebcal/core": "^6.3.3",
34
+ "@hebcal/hdate": "^0.22.2",
35
+ "@hebcal/rest-api": "^6.4.7",
36
36
  "murmurhash3": "^0.5.0",
37
37
  "tslib": "^2.8.1"
38
38
  },
@@ -50,13 +50,13 @@
50
50
  },
51
51
  "license": "BSD-2-Clause",
52
52
  "devDependencies": {
53
- "@hebcal/learning": "^6.6.1",
53
+ "@hebcal/learning": "^6.8.0",
54
54
  "@rollup/plugin-typescript": "^12.3.0",
55
- "@types/node": "25.3.0",
55
+ "@types/node": "25.6.0",
56
56
  "gts": "^7.0.0",
57
- "rollup": "^4.59.0",
58
- "typedoc": "^0.28.17",
59
- "typescript": "^5.9.3",
60
- "vitest": "^4.0.18"
57
+ "rollup": "^4.60.2",
58
+ "typedoc": "^0.28.19",
59
+ "typescript": "^6.0.3",
60
+ "vitest": "^4.1.5"
61
61
  }
62
62
  }