@craftguild/jscalendar 0.5.2 → 0.5.4

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 (124) hide show
  1. package/README.md +130 -94
  2. package/dist/index.cjs +3923 -0
  3. package/dist/index.d.cts +802 -0
  4. package/dist/index.d.mts +802 -0
  5. package/dist/index.mjs +3927 -0
  6. package/package.json +46 -31
  7. package/dist/__tests__/builders.test.d.ts +0 -1
  8. package/dist/__tests__/builders.test.js +0 -101
  9. package/dist/__tests__/calendar-extra.test.d.ts +0 -1
  10. package/dist/__tests__/calendar-extra.test.js +0 -221
  11. package/dist/__tests__/calendar.test.d.ts +0 -1
  12. package/dist/__tests__/calendar.test.js +0 -97
  13. package/dist/__tests__/ical-extra.test.d.ts +0 -1
  14. package/dist/__tests__/ical-extra.test.js +0 -87
  15. package/dist/__tests__/ical.test.d.ts +0 -1
  16. package/dist/__tests__/ical.test.js +0 -72
  17. package/dist/__tests__/index.test.d.ts +0 -1
  18. package/dist/__tests__/index.test.js +0 -9
  19. package/dist/__tests__/patch.test.d.ts +0 -1
  20. package/dist/__tests__/patch.test.js +0 -47
  21. package/dist/__tests__/recurrence.test.d.ts +0 -1
  22. package/dist/__tests__/recurrence.test.js +0 -640
  23. package/dist/__tests__/search.test.d.ts +0 -1
  24. package/dist/__tests__/search.test.js +0 -264
  25. package/dist/__tests__/timezones.test.d.ts +0 -1
  26. package/dist/__tests__/timezones.test.js +0 -12
  27. package/dist/__tests__/utils.test.d.ts +0 -1
  28. package/dist/__tests__/utils.test.js +0 -127
  29. package/dist/__tests__/validation.test.d.ts +0 -1
  30. package/dist/__tests__/validation.test.js +0 -224
  31. package/dist/ical.d.ts +0 -13
  32. package/dist/ical.js +0 -270
  33. package/dist/index.d.ts +0 -3
  34. package/dist/index.js +0 -2
  35. package/dist/jscal/base.d.ts +0 -89
  36. package/dist/jscal/base.js +0 -173
  37. package/dist/jscal/builders.d.ts +0 -183
  38. package/dist/jscal/builders.js +0 -287
  39. package/dist/jscal/constants.d.ts +0 -11
  40. package/dist/jscal/constants.js +0 -11
  41. package/dist/jscal/datetime.d.ts +0 -14
  42. package/dist/jscal/datetime.js +0 -42
  43. package/dist/jscal/defaults.d.ts +0 -31
  44. package/dist/jscal/defaults.js +0 -102
  45. package/dist/jscal/duration.d.ts +0 -43
  46. package/dist/jscal/duration.js +0 -75
  47. package/dist/jscal/event.d.ts +0 -23
  48. package/dist/jscal/event.js +0 -78
  49. package/dist/jscal/group.d.ts +0 -31
  50. package/dist/jscal/group.js +0 -69
  51. package/dist/jscal/guards.d.ts +0 -19
  52. package/dist/jscal/guards.js +0 -25
  53. package/dist/jscal/ids.d.ts +0 -11
  54. package/dist/jscal/ids.js +0 -77
  55. package/dist/jscal/normalize.d.ts +0 -32
  56. package/dist/jscal/normalize.js +0 -45
  57. package/dist/jscal/task.d.ts +0 -23
  58. package/dist/jscal/task.js +0 -67
  59. package/dist/jscal/types.d.ts +0 -38
  60. package/dist/jscal/types.js +0 -1
  61. package/dist/jscal.d.ts +0 -145
  62. package/dist/jscal.js +0 -126
  63. package/dist/patch.d.ts +0 -18
  64. package/dist/patch.js +0 -216
  65. package/dist/recurrence/constants.d.ts +0 -13
  66. package/dist/recurrence/constants.js +0 -13
  67. package/dist/recurrence/date-utils.d.ts +0 -125
  68. package/dist/recurrence/date-utils.js +0 -259
  69. package/dist/recurrence/expand.d.ts +0 -23
  70. package/dist/recurrence/expand.js +0 -315
  71. package/dist/recurrence/rule-candidates.d.ts +0 -21
  72. package/dist/recurrence/rule-candidates.js +0 -120
  73. package/dist/recurrence/rule-generate.d.ts +0 -11
  74. package/dist/recurrence/rule-generate.js +0 -36
  75. package/dist/recurrence/rule-matchers.d.ts +0 -34
  76. package/dist/recurrence/rule-matchers.js +0 -120
  77. package/dist/recurrence/rule-normalize.d.ts +0 -9
  78. package/dist/recurrence/rule-normalize.js +0 -57
  79. package/dist/recurrence/rule-selectors.d.ts +0 -7
  80. package/dist/recurrence/rule-selectors.js +0 -21
  81. package/dist/recurrence/rules.d.ts +0 -14
  82. package/dist/recurrence/rules.js +0 -57
  83. package/dist/recurrence/types.d.ts +0 -27
  84. package/dist/recurrence/types.js +0 -1
  85. package/dist/recurrence.d.ts +0 -2
  86. package/dist/recurrence.js +0 -1
  87. package/dist/search.d.ts +0 -44
  88. package/dist/search.js +0 -292
  89. package/dist/timezones/chunk_1.d.ts +0 -2
  90. package/dist/timezones/chunk_1.js +0 -72
  91. package/dist/timezones/chunk_2.d.ts +0 -2
  92. package/dist/timezones/chunk_2.js +0 -72
  93. package/dist/timezones/chunk_3.d.ts +0 -2
  94. package/dist/timezones/chunk_3.js +0 -72
  95. package/dist/timezones/chunk_4.d.ts +0 -2
  96. package/dist/timezones/chunk_4.js +0 -72
  97. package/dist/timezones/chunk_5.d.ts +0 -2
  98. package/dist/timezones/chunk_5.js +0 -72
  99. package/dist/timezones/chunk_6.d.ts +0 -2
  100. package/dist/timezones/chunk_6.js +0 -72
  101. package/dist/timezones/chunk_7.d.ts +0 -2
  102. package/dist/timezones/chunk_7.js +0 -6
  103. package/dist/timezones.d.ts +0 -9
  104. package/dist/timezones.js +0 -452
  105. package/dist/types.d.ts +0 -246
  106. package/dist/types.js +0 -1
  107. package/dist/utils.d.ts +0 -82
  108. package/dist/utils.js +0 -164
  109. package/dist/validate/asserts.d.ts +0 -155
  110. package/dist/validate/asserts.js +0 -381
  111. package/dist/validate/constants.d.ts +0 -25
  112. package/dist/validate/constants.js +0 -33
  113. package/dist/validate/error.d.ts +0 -19
  114. package/dist/validate/error.js +0 -25
  115. package/dist/validate/validators-common.d.ts +0 -64
  116. package/dist/validate/validators-common.js +0 -390
  117. package/dist/validate/validators-objects.d.ts +0 -8
  118. package/dist/validate/validators-objects.js +0 -70
  119. package/dist/validate/validators-recurrence.d.ts +0 -15
  120. package/dist/validate/validators-recurrence.js +0 -115
  121. package/dist/validate/validators.d.ts +0 -1
  122. package/dist/validate/validators.js +0 -1
  123. package/dist/validate.d.ts +0 -2
  124. package/dist/validate.js +0 -2
@@ -1,259 +0,0 @@
1
- import { FREQ_DAILY, FREQ_HOURLY, FREQ_MINUTELY, FREQ_MONTHLY, FREQ_WEEKLY, FREQ_YEARLY } from "./constants.js";
2
- /**
3
- * Advance a date-time by a recurrence interval.
4
- * @param start Anchor date-time.
5
- * @param frequency Recurrence frequency.
6
- * @param amount Number of intervals to add.
7
- * @param firstDay First day of week for weekly intervals.
8
- * @return Date-time moved to the next interval boundary.
9
- */
10
- export function addInterval(start, frequency, amount, firstDay) {
11
- if (frequency === FREQ_YEARLY) {
12
- return { year: start.year + amount, month: 1, day: 1, hour: 0, minute: 0, second: 0 };
13
- }
14
- if (frequency === FREQ_MONTHLY) {
15
- const next = addMonths(start, amount);
16
- return { year: next.year, month: next.month, day: 1, hour: 0, minute: 0, second: 0 };
17
- }
18
- if (frequency === FREQ_WEEKLY) {
19
- const weekStart = startOfWeek(start, firstDay);
20
- return addDays(weekStart, amount * 7);
21
- }
22
- if (frequency === FREQ_DAILY) {
23
- return addDays(start, amount);
24
- }
25
- if (frequency === FREQ_HOURLY) {
26
- return addHours(start, amount);
27
- }
28
- if (frequency === FREQ_MINUTELY) {
29
- return addMinutes(start, amount);
30
- }
31
- return addSeconds(start, amount);
32
- }
33
- /**
34
- * Parse local date time into structured data.
35
- * @param value LocalDateTime string.
36
- * @return Parsed date-time parts.
37
- */
38
- export function parseLocalDateTime(value) {
39
- const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/.exec(value);
40
- if (!match) {
41
- throw new Error(`Invalid LocalDateTime: ${value}`);
42
- }
43
- return {
44
- year: Number(match[1]),
45
- month: Number(match[2]),
46
- day: Number(match[3]),
47
- hour: Number(match[4]),
48
- minute: Number(match[5]),
49
- second: Number(match[6]),
50
- };
51
- }
52
- /**
53
- * Format local date time as string.
54
- * @param dt Date-time parts.
55
- * @return LocalDateTime string.
56
- */
57
- export function formatLocalDateTime(dt) {
58
- return `${pad(dt.year, 4)}-${pad(dt.month, 2)}-${pad(dt.day, 2)}T${pad(dt.hour, 2)}:${pad(dt.minute, 2)}:${pad(dt.second, 2)}`;
59
- }
60
- /**
61
- * Pad a numeric value with leading zeros.
62
- * @param value Number to pad.
63
- * @param length Total length.
64
- * @return Zero-padded string.
65
- */
66
- export function pad(value, length) {
67
- return value.toString().padStart(length, "0");
68
- }
69
- /**
70
- * Add days to the target.
71
- * @param dt Date-time parts.
72
- * @param days Days to add.
73
- * @return New date-time with days added.
74
- */
75
- export function addDays(dt, days) {
76
- const ms = Date.UTC(dt.year, dt.month - 1, dt.day, dt.hour, dt.minute, dt.second) + days * 86400 * 1000;
77
- const d = new Date(ms);
78
- return {
79
- year: d.getUTCFullYear(),
80
- month: d.getUTCMonth() + 1,
81
- day: d.getUTCDate(),
82
- hour: dt.hour,
83
- minute: dt.minute,
84
- second: dt.second,
85
- };
86
- }
87
- /**
88
- * Add hours to the target.
89
- * @param dt Date-time parts.
90
- * @param hours Hours to add.
91
- * @return New date-time with hours added.
92
- */
93
- export function addHours(dt, hours) {
94
- const ms = Date.UTC(dt.year, dt.month - 1, dt.day, dt.hour, dt.minute, dt.second) + hours * 3600 * 1000;
95
- const d = new Date(ms);
96
- return {
97
- year: d.getUTCFullYear(),
98
- month: d.getUTCMonth() + 1,
99
- day: d.getUTCDate(),
100
- hour: d.getUTCHours(),
101
- minute: d.getUTCMinutes(),
102
- second: d.getUTCSeconds(),
103
- };
104
- }
105
- /**
106
- * Add minutes to the target.
107
- * @param dt Date-time parts.
108
- * @param minutes Minutes to add.
109
- * @return New date-time with minutes added.
110
- */
111
- export function addMinutes(dt, minutes) {
112
- return addSeconds(dt, minutes * 60);
113
- }
114
- /**
115
- * Add seconds to the target.
116
- * @param dt Date-time parts.
117
- * @param seconds Seconds to add.
118
- * @return New date-time with seconds added.
119
- */
120
- export function addSeconds(dt, seconds) {
121
- const ms = Date.UTC(dt.year, dt.month - 1, dt.day, dt.hour, dt.minute, dt.second) + seconds * 1000;
122
- const d = new Date(ms);
123
- return {
124
- year: d.getUTCFullYear(),
125
- month: d.getUTCMonth() + 1,
126
- day: d.getUTCDate(),
127
- hour: d.getUTCHours(),
128
- minute: d.getUTCMinutes(),
129
- second: d.getUTCSeconds(),
130
- };
131
- }
132
- /**
133
- * Add months to the target.
134
- * @param dt Date-time parts.
135
- * @param months Months to add.
136
- * @return New date-time with months added.
137
- */
138
- export function addMonths(dt, months) {
139
- const total = (dt.year * 12 + (dt.month - 1)) + months;
140
- const year = Math.floor(total / 12);
141
- const month = (total % 12) + 1;
142
- const day = Math.min(dt.day, daysInMonth(year, month));
143
- return { year, month, day, hour: dt.hour, minute: dt.minute, second: dt.second };
144
- }
145
- /**
146
- * Get day of week for a date-time.
147
- * @param dt Date-time parts.
148
- * @return Two-letter weekday code.
149
- */
150
- export function dayOfWeek(dt) {
151
- const d = new Date(Date.UTC(dt.year, dt.month - 1, dt.day));
152
- const idx = d.getUTCDay();
153
- if (idx === 0)
154
- return "su";
155
- if (idx === 1)
156
- return "mo";
157
- if (idx === 2)
158
- return "tu";
159
- if (idx === 3)
160
- return "we";
161
- if (idx === 4)
162
- return "th";
163
- if (idx === 5)
164
- return "fr";
165
- return "sa";
166
- }
167
- /**
168
- * Get day of year for a date-time.
169
- * @param dt Date-time parts.
170
- * @return Day of year (1-366).
171
- */
172
- export function dayOfYear(dt) {
173
- const start = Date.UTC(dt.year, 0, 1);
174
- const current = Date.UTC(dt.year, dt.month - 1, dt.day);
175
- return Math.floor((current - start) / (24 * 3600 * 1000)) + 1;
176
- }
177
- /**
178
- * Get number of days in a month.
179
- * @param year Year number.
180
- * @param month Month number (1-12).
181
- * @return Days in the month.
182
- */
183
- export function daysInMonth(year, month) {
184
- return new Date(Date.UTC(year, month, 0)).getUTCDate();
185
- }
186
- /**
187
- * Get number of days in a year.
188
- * @param year Year number.
189
- * @return Days in the year.
190
- */
191
- export function daysInYear(year) {
192
- return new Date(Date.UTC(year + 1, 0, 0)).getUTCDate();
193
- }
194
- /**
195
- * Get the start of week for a date-time.
196
- * @param dt Date-time parts.
197
- * @param firstDay Weekday that starts the week.
198
- * @return Date-time at the start of the week.
199
- */
200
- export function startOfWeek(dt, firstDay) {
201
- const order = ["mo", "tu", "we", "th", "fr", "sa", "su"];
202
- const dow = dayOfWeek(dt);
203
- const offset = (order.indexOf(dow) - order.indexOf(firstDay) + 7) % 7;
204
- return addDays(dt, -offset);
205
- }
206
- /**
207
- * Get ISO-like week number for a date-time.
208
- * @param dt Date-time parts.
209
- * @param firstDay Weekday that starts the week.
210
- * @return Week number for the given date.
211
- */
212
- export function weekNumber(dt, firstDay) {
213
- const yearStart = { year: dt.year, month: 1, day: 1, hour: 0, minute: 0, second: 0 };
214
- const weekStart = startOfWeek(yearStart, firstDay);
215
- const daysBeforeYear = daysBetween(weekStart, yearStart);
216
- const daysInFirstWeek = 7 - daysBeforeYear;
217
- const week1Start = daysInFirstWeek >= 4 ? weekStart : addDays(weekStart, 7);
218
- if (compareDate(dt, week1Start) < 0) {
219
- return totalWeeksInYear(dt.year - 1, firstDay);
220
- }
221
- const diff = daysBetween(week1Start, dt);
222
- return Math.floor(diff / 7) + 1;
223
- }
224
- /**
225
- * Get total weeks in a year.
226
- * @param year Year number.
227
- * @param firstDay Weekday that starts the week.
228
- * @return Total number of weeks in the year.
229
- */
230
- export function totalWeeksInYear(year, firstDay) {
231
- const lastDay = { year, month: 12, day: 31, hour: 0, minute: 0, second: 0 };
232
- return weekNumber(lastDay, firstDay);
233
- }
234
- /**
235
- * Get whole-day difference between two dates.
236
- * @param a Start date.
237
- * @param b End date.
238
- * @return Whole-day difference (b - a).
239
- */
240
- export function daysBetween(a, b) {
241
- const msA = Date.UTC(a.year, a.month - 1, a.day);
242
- const msB = Date.UTC(b.year, b.month - 1, b.day);
243
- return Math.floor((msB - msA) / (24 * 3600 * 1000));
244
- }
245
- /**
246
- * Compare two dates without time.
247
- * @param a First date.
248
- * @param b Second date.
249
- * @return Negative/zero/positive comparison result.
250
- */
251
- export function compareDate(a, b) {
252
- if (a.year !== b.year)
253
- return a.year - b.year;
254
- if (a.month !== b.month)
255
- return a.month - b.month;
256
- if (a.day !== b.day)
257
- return a.day - b.day;
258
- return 0;
259
- }
@@ -1,23 +0,0 @@
1
- import type { JSCalendarObject } from "../types.js";
2
- import type { RecurrenceRange } from "./types.js";
3
- /**
4
- * Expand recurrence into occurrences sorted by recurrenceId/start.
5
- * @param items JSCalendar objects to expand.
6
- * @param range Date range bounds.
7
- * @return Generator of expanded occurrences.
8
- */
9
- export declare function expandRecurrence(items: JSCalendarObject[], range: RecurrenceRange): Generator<JSCalendarObject>;
10
- /**
11
- * Expand recurrence paged into occurrences.
12
- * @param items JSCalendar objects to expand.
13
- * @param range Date range bounds.
14
- * @param options Pagination options.
15
- * @return Page of expanded items plus an optional next cursor.
16
- */
17
- export declare function expandRecurrencePaged(items: JSCalendarObject[], range: RecurrenceRange, options: {
18
- limit: number;
19
- cursor?: string;
20
- }): {
21
- items: JSCalendarObject[];
22
- nextCursor?: string;
23
- };
@@ -1,315 +0,0 @@
1
- import { applyPatch } from "../patch.js";
2
- import { dateTimeInTimeZone, localDateTimeFromDate, localDateTimeToUtcDate } from "../utils.js";
3
- import { TYPE_EVENT, TYPE_TASK } from "./constants.js";
4
- import { expandRule } from "./rules.js";
5
- /**
6
- * Expand recurrence into occurrences sorted by recurrenceId/start.
7
- * @param items JSCalendar objects to expand.
8
- * @param range Date range bounds.
9
- * @return Generator of expanded occurrences.
10
- */
11
- export function* expandRecurrence(items, range) {
12
- const occurrences = [];
13
- let index = 0;
14
- for (const item of items) {
15
- if (item["@type"] === TYPE_EVENT) {
16
- for (const occurrence of expandEvent(item, range)) {
17
- occurrences.push({ value: occurrence, key: occurrenceKey(occurrence), index });
18
- index += 1;
19
- }
20
- continue;
21
- }
22
- if (item["@type"] === TYPE_TASK) {
23
- for (const occurrence of expandTask(item, range)) {
24
- occurrences.push({ value: occurrence, key: occurrenceKey(occurrence), index });
25
- index += 1;
26
- }
27
- continue;
28
- }
29
- occurrences.push({ value: item, key: occurrenceKey(item), index });
30
- index += 1;
31
- }
32
- occurrences.sort((a, b) => {
33
- if (a.key && b.key)
34
- return a.key.localeCompare(b.key);
35
- if (a.key)
36
- return -1;
37
- if (b.key)
38
- return 1;
39
- return a.index - b.index;
40
- });
41
- for (const occurrence of occurrences) {
42
- yield occurrence.value;
43
- }
44
- }
45
- /**
46
- * Expand recurrence paged into occurrences.
47
- * @param items JSCalendar objects to expand.
48
- * @param range Date range bounds.
49
- * @param options Pagination options.
50
- * @return Page of expanded items plus an optional next cursor.
51
- */
52
- export function expandRecurrencePaged(items, range, options) {
53
- const result = [];
54
- let nextCursor;
55
- for (const occurrence of expandRecurrence(items, range)) {
56
- const key = occurrenceKey(occurrence);
57
- if (options.cursor && key) {
58
- if (key <= options.cursor) {
59
- continue;
60
- }
61
- }
62
- else if (options.cursor && !key) {
63
- continue;
64
- }
65
- result.push(occurrence);
66
- if (key) {
67
- nextCursor = key;
68
- }
69
- if (result.length >= options.limit) {
70
- break;
71
- }
72
- }
73
- return { items: result, nextCursor };
74
- }
75
- /**
76
- * Expand event into occurrences.
77
- * @param event Event to expand.
78
- * @param range Date range bounds.
79
- * @return Generator of expanded occurrences.
80
- */
81
- function expandEvent(event, range) {
82
- return expandObject(event, range, event.start, event.recurrenceRules, event.excludedRecurrenceRules, event.recurrenceOverrides, event.timeZone ?? null);
83
- }
84
- /**
85
- * Expand task into occurrences.
86
- * @param task Task to expand.
87
- * @param range Date range bounds.
88
- * @return Generator of expanded occurrences.
89
- */
90
- function expandTask(task, range) {
91
- const anchor = task.start ?? task.due;
92
- if (!anchor) {
93
- return (function* empty() { })();
94
- }
95
- return expandObject(task, range, anchor, task.recurrenceRules, task.excludedRecurrenceRules, task.recurrenceOverrides, task.timeZone ?? null);
96
- }
97
- /**
98
- * Build a stable ordering key for an occurrence.
99
- * @param value Occurrence object.
100
- * @return Sort key or undefined when not available.
101
- */
102
- function occurrenceKey(value) {
103
- if (value.recurrenceId)
104
- return value.recurrenceId;
105
- if (value["@type"] === TYPE_EVENT)
106
- return value.start;
107
- if (value["@type"] === TYPE_TASK)
108
- return value.start ?? value.due;
109
- return undefined;
110
- }
111
- /**
112
- * Expand object into occurrences.
113
- * @param base Base JSCalendar object.
114
- * @param range Date range bounds.
115
- * @param anchor Anchor LocalDateTime for the series.
116
- * @param rules Inclusion recurrence rules.
117
- * @param excludedRules Exclusion recurrence rules.
118
- * @param overrides Recurrence overrides keyed by LocalDateTime.
119
- * @param recurrenceIdTimeZone Optional time zone for recurrence IDs.
120
- * @return Generator of expanded occurrences.
121
- */
122
- function* expandObject(base, range, anchor, rules, excludedRules, overrides, recurrenceIdTimeZone) {
123
- const hasZone = Boolean(recurrenceIdTimeZone);
124
- const fromLocal = hasZone && recurrenceIdTimeZone
125
- ? dateTimeInTimeZone(range.from, recurrenceIdTimeZone)
126
- : localDateTimeFromDate(range.from);
127
- const toLocal = hasZone && recurrenceIdTimeZone
128
- ? dateTimeInTimeZone(range.to, recurrenceIdTimeZone)
129
- : localDateTimeFromDate(range.to);
130
- const fromDate = range.from;
131
- const toDate = range.to;
132
- const overrideKeys = overrides ? Object.keys(overrides) : [];
133
- if (!rules || rules.length === 0) {
134
- if (hasZone && recurrenceIdTimeZone) {
135
- if (isInRangeWithZone(anchor, fromDate, toDate, recurrenceIdTimeZone)) {
136
- yield base;
137
- }
138
- }
139
- else if (isInRange(anchor, fromLocal, toLocal)) {
140
- yield base;
141
- }
142
- for (const key of overrideKeys) {
143
- const patch = overrides ? overrides[key] : undefined;
144
- const instance = buildInstance(base, key, recurrenceIdTimeZone, patch);
145
- if (!instance)
146
- continue;
147
- if (hasZone && recurrenceIdTimeZone) {
148
- if (isInRangeWithZone(key, fromDate, toDate, recurrenceIdTimeZone)) {
149
- yield instance;
150
- }
151
- }
152
- else if (isInRange(key, fromLocal, toLocal)) {
153
- yield instance;
154
- }
155
- }
156
- return;
157
- }
158
- const occurrences = [];
159
- for (const rule of rules) {
160
- const expanded = expandRule(anchor, rule, fromLocal, toLocal, true, recurrenceIdTimeZone ?? undefined, fromDate, toDate);
161
- occurrences.push(...expanded);
162
- }
163
- const excluded = new Set();
164
- if (excludedRules && excludedRules.length > 0) {
165
- for (const rule of excludedRules) {
166
- const expanded = expandRule(anchor, rule, fromLocal, toLocal, true, recurrenceIdTimeZone ?? undefined, fromDate, toDate);
167
- for (const value of expanded) {
168
- excluded.add(value);
169
- }
170
- }
171
- }
172
- if (!occurrences.includes(anchor)) {
173
- occurrences.push(anchor);
174
- }
175
- for (const key of overrideKeys) {
176
- if (!occurrences.includes(key)) {
177
- occurrences.push(key);
178
- }
179
- }
180
- let sorted = Array.from(new Set(occurrences)).sort((a, b) => compareLocal(a, b, recurrenceIdTimeZone ?? undefined));
181
- if (rules[0]?.count && sorted.length > rules[0].count) {
182
- sorted = sorted.slice(0, rules[0].count);
183
- }
184
- for (const dt of sorted) {
185
- if (excluded.has(dt))
186
- continue;
187
- const patch = overrides ? overrides[dt] : undefined;
188
- const instance = buildInstance(base, dt, recurrenceIdTimeZone, patch);
189
- if (!instance)
190
- continue;
191
- if (hasZone && recurrenceIdTimeZone) {
192
- if (isInRangeWithZone(dt, fromDate, toDate, recurrenceIdTimeZone)) {
193
- yield instance;
194
- }
195
- }
196
- else if (isInRange(dt, fromLocal, toLocal)) {
197
- yield instance;
198
- }
199
- }
200
- }
201
- /**
202
- * Build an occurrence instance from a base object plus override patch.
203
- * @param base Base JSCalendar object.
204
- * @param recurrenceId LocalDateTime recurrence id.
205
- * @param recurrenceIdTimeZone Optional time zone for recurrence id.
206
- * @param patch Override patch for the occurrence.
207
- * @return Occurrence instance or null if excluded.
208
- */
209
- function buildInstance(base, recurrenceId, recurrenceIdTimeZone, patch) {
210
- const patched = patch ? applyPatch(base, patch) : base;
211
- if (isExcludedInstance(patched)) {
212
- return null;
213
- }
214
- const overridesStart = patchHasKey(patch, "start");
215
- const overridesDue = patchHasKey(patch, "due");
216
- let shifted;
217
- if (patched["@type"] === TYPE_EVENT) {
218
- shifted = overridesStart ? patched : { ...patched, start: recurrenceId };
219
- }
220
- else if (patched["@type"] === TYPE_TASK) {
221
- if (patched.start) {
222
- shifted = overridesStart ? patched : { ...patched, start: recurrenceId };
223
- }
224
- else if (patched.due) {
225
- shifted = overridesDue ? patched : { ...patched, due: recurrenceId };
226
- }
227
- else {
228
- shifted = patched;
229
- }
230
- }
231
- else {
232
- shifted = patched;
233
- }
234
- const instance = {
235
- ...stripRecurrenceProperties(shifted),
236
- recurrenceId,
237
- };
238
- if (recurrenceIdTimeZone) {
239
- instance.recurrenceIdTimeZone = recurrenceIdTimeZone;
240
- }
241
- return instance;
242
- }
243
- /**
244
- * Check if a patch contains a key or pointer.
245
- * @param patch Patch object to inspect.
246
- * @param key Field name to look up.
247
- * @return True when the patch modifies the field.
248
- */
249
- function patchHasKey(patch, key) {
250
- if (!patch)
251
- return false;
252
- if (Object.prototype.hasOwnProperty.call(patch, key))
253
- return true;
254
- if (Object.prototype.hasOwnProperty.call(patch, `/${key}`))
255
- return true;
256
- return false;
257
- }
258
- /**
259
- * Strip recurrence properties from value.
260
- * @param object JSCalendar object to clean.
261
- * @return Object without recurrence rule fields.
262
- */
263
- function stripRecurrenceProperties(object) {
264
- const { recurrenceRules: _recurrenceRules, excludedRecurrenceRules: _excludedRecurrenceRules, recurrenceOverrides: _recurrenceOverrides, ...rest } = object;
265
- return rest;
266
- }
267
- /**
268
- * Check whether value is excluded instance.
269
- * @param object JSCalendar object.
270
- * @return True when the occurrence is excluded.
271
- */
272
- function isExcludedInstance(object) {
273
- return object.excluded === true;
274
- }
275
- /**
276
- * Check whether value is in range.
277
- * @param value LocalDateTime string.
278
- * @param from LocalDateTime lower bound.
279
- * @param to LocalDateTime upper bound.
280
- * @return True when value is within the range.
281
- */
282
- function isInRange(value, from, to) {
283
- return value >= from && value <= to;
284
- }
285
- /**
286
- * Check whether value is in range with zone.
287
- * @param value LocalDateTime string.
288
- * @param from Date lower bound.
289
- * @param to Date upper bound.
290
- * @param timeZone Time zone for LocalDateTime conversion.
291
- * @return True when value is within the range.
292
- */
293
- function isInRangeWithZone(value, from, to, timeZone) {
294
- const utc = localDateTimeToUtcDate(value, timeZone);
295
- return utc >= from && utc <= to;
296
- }
297
- /**
298
- * Compare local date-time strings, optionally using a time zone.
299
- * @param a LocalDateTime string A.
300
- * @param b LocalDateTime string B.
301
- * @param timeZone Optional time zone for comparison.
302
- * @return Negative/zero/positive comparison result.
303
- */
304
- function compareLocal(a, b, timeZone) {
305
- if (!timeZone) {
306
- if (a === b)
307
- return 0;
308
- return a < b ? -1 : 1;
309
- }
310
- const aUtc = localDateTimeToUtcDate(a, timeZone).getTime();
311
- const bUtc = localDateTimeToUtcDate(b, timeZone).getTime();
312
- if (aUtc === bUtc)
313
- return 0;
314
- return aUtc < bUtc ? -1 : 1;
315
- }
@@ -1,21 +0,0 @@
1
- import type { RecurrenceRule } from "../types.js";
2
- import type { DateCandidate, DateTime, DayOfWeek } from "./types.js";
3
- /**
4
- * Build candidate dates for the current recurrence period.
5
- * @param periodStart Start of the current recurrence period.
6
- * @param rule Normalized recurrence rule.
7
- * @param firstDay First day of the week for weekly calculations.
8
- * @param skip Skip policy for invalid month days.
9
- * @return Candidate dates for further filtering.
10
- */
11
- export declare function generateDateCandidates(periodStart: DateTime, rule: RecurrenceRule, firstDay: DayOfWeek, skip: string): DateCandidate[];
12
- /**
13
- * Apply BY* filters and skip behavior to candidate dates.
14
- * @param candidates Candidate dates for the period.
15
- * @param rule Normalized recurrence rule.
16
- * @param periodStart Start of the current recurrence period.
17
- * @param firstDay First day of the week for weekly calculations.
18
- * @param skip Skip policy for invalid month days.
19
- * @return Filtered candidate dates.
20
- */
21
- export declare function filterDateCandidates(candidates: DateCandidate[], rule: RecurrenceRule, periodStart: DateTime, firstDay: DayOfWeek, skip: string): DateCandidate[];