@forcecalendar/core 2.1.22 → 2.1.25

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.
@@ -256,8 +256,11 @@ export class DateUtils {
256
256
  * @returns {number}
257
257
  */
258
258
  static differenceInDays(date1, date2) {
259
- const diff = date1.getTime() - date2.getTime();
260
- return Math.floor(diff / (1000 * 60 * 60 * 24));
259
+ // Normalize to midnight to avoid DST-related hour differences
260
+ const d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
261
+ const d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
262
+ const diff = d1.getTime() - d2.getTime();
263
+ return Math.round(diff / (1000 * 60 * 60 * 24));
261
264
  }
262
265
 
263
266
  /**
@@ -47,8 +47,12 @@ export class Event {
47
47
  }
48
48
 
49
49
  // Normalize string fields with size limits
50
- normalized.id = String(normalized.id || '').trim().slice(0, Event.FIELD_LIMITS.id);
51
- normalized.title = String(normalized.title || '').trim().slice(0, Event.FIELD_LIMITS.title);
50
+ normalized.id = String(normalized.id || '')
51
+ .trim()
52
+ .slice(0, Event.FIELD_LIMITS.id);
53
+ normalized.title = String(normalized.title || '')
54
+ .trim()
55
+ .slice(0, Event.FIELD_LIMITS.title);
52
56
  normalized.description = String(normalized.description || '')
53
57
  .trim()
54
58
  .slice(0, Event.FIELD_LIMITS.description);
@@ -241,8 +241,14 @@ export class EventStore {
241
241
  for (let offset = -1; offset <= 1; offset++) {
242
242
  let m = filters.month + offset;
243
243
  let y = filters.year;
244
- if (m < 1) { m = 12; y--; }
245
- if (m > 12) { m = 1; y++; }
244
+ if (m < 1) {
245
+ m = 12;
246
+ y--;
247
+ }
248
+ if (m > 12) {
249
+ m = 1;
250
+ y++;
251
+ }
246
252
  const key = `${y}-${String(m).padStart(2, '0')}`;
247
253
  const ids = this.indices.byMonth.get(key);
248
254
  if (ids) {
@@ -639,6 +645,8 @@ export class EventStore {
639
645
  this.indices.byDate.clear();
640
646
  this.indices.byMonth.clear();
641
647
  this.indices.recurring.clear();
648
+ this.indices.byCategory.clear();
649
+ this.indices.byStatus.clear();
642
650
 
643
651
  this._notifyChange({
644
652
  type: 'clear',
@@ -64,9 +64,7 @@ export class RRuleParser {
64
64
 
65
65
  case 'BYWEEKNO':
66
66
  // RFC 5545: valid range is 1-53 or -53 to -1 (no zero)
67
- rule.byWeekNo = this.parseIntList(value).filter(
68
- v => v !== 0 && v >= -53 && v <= 53
69
- );
67
+ rule.byWeekNo = this.parseIntList(value).filter(v => v !== 0 && v >= -53 && v <= 53);
70
68
  break;
71
69
 
72
70
  case 'BYMONTH':
@@ -228,7 +228,9 @@ export class ICSHandler {
228
228
 
229
229
  // Only allow http and https schemes
230
230
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
231
- throw new Error(`URL scheme "${parsed.protocol}" is not allowed. Only http and https are permitted`);
231
+ throw new Error(
232
+ `URL scheme "${parsed.protocol}" is not allowed. Only http and https are permitted`
233
+ );
232
234
  }
233
235
 
234
236
  const hostname = parsed.hostname.toLowerCase();
@@ -10,7 +10,10 @@ export class ICSParser {
10
10
  static MAX_LINES = 100000; // 100k lines
11
11
  static MAX_EVENTS = 10000; // 10k events
12
12
 
13
- constructor() {
13
+ constructor(options = {}) {
14
+ // Configurable max file size (defaults to static limit)
15
+ this.maxFileSize = options.maxFileSize || ICSParser.MAX_INPUT_SIZE;
16
+
14
17
  // ICS line folding max width
15
18
  this.maxLineLength = 75;
16
19
 
@@ -38,11 +41,12 @@ export class ICSParser {
38
41
  * @returns {Array} Array of event objects
39
42
  */
40
43
  parse(icsString) {
41
- // Enforce input size limit
42
- if (typeof icsString === 'string' && icsString.length > ICSParser.MAX_INPUT_SIZE) {
43
- throw new Error(
44
- `ICS input exceeds maximum size of ${ICSParser.MAX_INPUT_SIZE / (1024 * 1024)}MB`
45
- );
44
+ if (typeof icsString !== 'string') {
45
+ throw new Error('ICS input must be a string');
46
+ }
47
+ // Enforce input size limit (uses instance config, falling back to static default)
48
+ if (icsString.length > this.maxFileSize) {
49
+ throw new Error(`ICS input exceeds maximum size of ${this.maxFileSize / (1024 * 1024)}MB`);
46
50
  }
47
51
 
48
52
  const events = [];
@@ -306,6 +310,13 @@ export class ICSParser {
306
310
  event.recurrenceRule = value;
307
311
  event.recurring = true;
308
312
  break;
313
+
314
+ case 'EXDATE': {
315
+ if (!event.excludeDates) event.excludeDates = [];
316
+ const dates = value.split(',').map(d => this.parseDate(d.trim(), property));
317
+ event.excludeDates.push(...dates.filter(d => d !== null));
318
+ break;
319
+ }
309
320
  }
310
321
  }
311
322
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forcecalendar/core",
3
- "version": "2.1.22",
3
+ "version": "2.1.25",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "A modern, lightweight, framework-agnostic calendar engine optimized for Salesforce",