@craftguild/jscalendar 0.5.3 → 0.5.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.
Files changed (124) hide show
  1. package/README.md +128 -92
  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,120 +0,0 @@
1
- import { FREQ_DAILY, FREQ_HOURLY, FREQ_MINUTELY, FREQ_MONTHLY, FREQ_SECONDLY, FREQ_WEEKLY, FREQ_YEARLY, SKIP_BACKWARD, SKIP_FORWARD, SKIP_OMIT } from "./constants.js";
2
- import { addDays, addMonths, daysInMonth, pad } from "./date-utils.js";
3
- import { matchesByDay, matchesByMonthDay, matchesByWeekNo, matchesByYearDay } from "./rule-matchers.js";
4
- /**
5
- * Build candidate dates for the current recurrence period.
6
- * @param periodStart Start of the current recurrence period.
7
- * @param rule Normalized recurrence rule.
8
- * @param firstDay First day of the week for weekly calculations.
9
- * @param skip Skip policy for invalid month days.
10
- * @return Candidate dates for further filtering.
11
- */
12
- export function generateDateCandidates(periodStart, rule, firstDay, skip) {
13
- const result = [];
14
- const wantsInvalid = skip !== SKIP_OMIT && rule.byMonthDay && rule.byMonthDay.length > 0;
15
- if (rule.frequency === FREQ_YEARLY) {
16
- for (let month = 1; month <= 12; month += 1) {
17
- const maxDays = wantsInvalid ? 31 : daysInMonth(periodStart.year, month);
18
- for (let day = 1; day <= maxDays; day += 1) {
19
- const valid = day <= daysInMonth(periodStart.year, month);
20
- result.push({ year: periodStart.year, month, day, valid });
21
- }
22
- }
23
- return result;
24
- }
25
- if (rule.frequency === FREQ_MONTHLY) {
26
- const maxDays = wantsInvalid ? 31 : daysInMonth(periodStart.year, periodStart.month);
27
- for (let day = 1; day <= maxDays; day += 1) {
28
- const valid = day <= daysInMonth(periodStart.year, periodStart.month);
29
- result.push({ year: periodStart.year, month: periodStart.month, day, valid });
30
- }
31
- return result;
32
- }
33
- if (rule.frequency === FREQ_WEEKLY) {
34
- let cursor = periodStart;
35
- for (let i = 0; i < 7; i += 1) {
36
- result.push({ year: cursor.year, month: cursor.month, day: cursor.day, valid: true });
37
- cursor = addDays(cursor, 1);
38
- }
39
- return result;
40
- }
41
- if (rule.frequency === FREQ_DAILY) {
42
- result.push({ year: periodStart.year, month: periodStart.month, day: periodStart.day, valid: true });
43
- return result;
44
- }
45
- if (rule.frequency === FREQ_HOURLY || rule.frequency === FREQ_MINUTELY || rule.frequency === FREQ_SECONDLY) {
46
- result.push({ year: periodStart.year, month: periodStart.month, day: periodStart.day, valid: true });
47
- return result;
48
- }
49
- return result;
50
- }
51
- /**
52
- * Apply BY* filters and skip behavior to candidate dates.
53
- * @param candidates Candidate dates for the period.
54
- * @param rule Normalized recurrence rule.
55
- * @param periodStart Start of the current recurrence period.
56
- * @param firstDay First day of the week for weekly calculations.
57
- * @param skip Skip policy for invalid month days.
58
- * @return Filtered candidate dates.
59
- */
60
- export function filterDateCandidates(candidates, rule, periodStart, firstDay, skip) {
61
- let result = candidates;
62
- if (rule.byMonth && rule.byMonth.length > 0) {
63
- const months = rule.byMonth.map((m) => parseInt(m, 10)).filter((m) => !Number.isNaN(m));
64
- result = result.filter((d) => months.includes(d.month));
65
- }
66
- if (rule.byWeekNo && rule.byWeekNo.length > 0) {
67
- const byWeekNo = rule.byWeekNo.filter((d) => d !== 0);
68
- result = result.filter((d) => d.valid && matchesByWeekNo(d, byWeekNo, firstDay));
69
- }
70
- if (rule.byYearDay && rule.byYearDay.length > 0) {
71
- const byYearDay = rule.byYearDay.filter((d) => d !== 0);
72
- result = result.filter((d) => d.valid && matchesByYearDay(d, byYearDay));
73
- }
74
- if (rule.byMonthDay && rule.byMonthDay.length > 0) {
75
- const byMonthDay = rule.byMonthDay.filter((d) => d !== 0);
76
- result = result.filter((d) => matchesByMonthDay(d, byMonthDay));
77
- }
78
- const byDay = rule.byDay;
79
- if (byDay && byDay.length > 0) {
80
- result = result.filter((d) => matchesByDay(d, byDay, rule.frequency, periodStart, firstDay));
81
- }
82
- if (skip !== SKIP_OMIT && rule.byMonthDay && rule.byMonthDay.length > 0) {
83
- result = adjustInvalidMonthDays(result, skip);
84
- }
85
- else {
86
- result = result.filter((d) => d.valid);
87
- }
88
- return result;
89
- }
90
- /**
91
- * Shift invalid month-day candidates using the skip policy.
92
- * @param candidates Candidate dates that may include invalid month days.
93
- * @param skip Skip policy for invalid month days.
94
- * @return Candidates with invalid days adjusted or removed.
95
- */
96
- function adjustInvalidMonthDays(candidates, skip) {
97
- const adjusted = [];
98
- for (const candidate of candidates) {
99
- if (candidate.valid) {
100
- adjusted.push(candidate);
101
- continue;
102
- }
103
- if (skip === SKIP_FORWARD) {
104
- const next = addMonths({ year: candidate.year, month: candidate.month, day: 1, hour: 0, minute: 0, second: 0 }, 1);
105
- adjusted.push({ year: next.year, month: next.month, day: 1, valid: true });
106
- }
107
- else if (skip === SKIP_BACKWARD) {
108
- const day = daysInMonth(candidate.year, candidate.month);
109
- adjusted.push({ year: candidate.year, month: candidate.month, day, valid: true });
110
- }
111
- }
112
- const deduped = new Map();
113
- for (const candidate of adjusted) {
114
- const key = `${pad(candidate.year, 4)}-${pad(candidate.month, 2)}-${pad(candidate.day, 2)}`;
115
- if (!deduped.has(key)) {
116
- deduped.set(key, candidate);
117
- }
118
- }
119
- return Array.from(deduped.values());
120
- }
@@ -1,11 +0,0 @@
1
- import type { RecurrenceRule } from "../types.js";
2
- import type { DateTime, DayOfWeek } from "./types.js";
3
- /**
4
- * Generate date-time strings for the current period using rule filters.
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 Date-time strings for this period.
10
- */
11
- export declare function generateDateTimes(periodStart: DateTime, rule: RecurrenceRule, firstDay: DayOfWeek, skip: string): string[];
@@ -1,36 +0,0 @@
1
- import { formatLocalDateTime } from "./date-utils.js";
2
- import { filterDateCandidates, generateDateCandidates } from "./rule-candidates.js";
3
- /**
4
- * Generate date-time strings for the current period using rule filters.
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 Date-time strings for this period.
10
- */
11
- export function generateDateTimes(periodStart, rule, firstDay, skip) {
12
- const dateCandidates = generateDateCandidates(periodStart, rule, firstDay, skip);
13
- const filteredDates = filterDateCandidates(dateCandidates, rule, periodStart, firstDay, skip);
14
- const hours = rule.byHour && rule.byHour.length > 0 ? rule.byHour : [periodStart.hour];
15
- const minutes = rule.byMinute && rule.byMinute.length > 0 ? rule.byMinute : [periodStart.minute];
16
- const seconds = rule.bySecond && rule.bySecond.length > 0 ? rule.bySecond : [periodStart.second];
17
- const result = [];
18
- for (const date of filteredDates) {
19
- for (const hour of hours) {
20
- for (const minute of minutes) {
21
- for (const second of seconds) {
22
- const dt = formatLocalDateTime({
23
- year: date.year,
24
- month: date.month,
25
- day: date.day,
26
- hour,
27
- minute,
28
- second,
29
- });
30
- result.push(dt);
31
- }
32
- }
33
- }
34
- }
35
- return result;
36
- }
@@ -1,34 +0,0 @@
1
- import type { NDay, RecurrenceRule } from "../types.js";
2
- import type { DateCandidate, DateTime, DayOfWeek } from "./types.js";
3
- /**
4
- * Check whether a candidate date matches BYMONTHDAY values.
5
- * @param date Candidate date.
6
- * @param byMonthDay BYMONTHDAY values (positive or negative).
7
- * @return True when the candidate date matches any BYMONTHDAY value.
8
- */
9
- export declare function matchesByMonthDay(date: DateCandidate, byMonthDay: number[]): boolean;
10
- /**
11
- * Check whether a candidate date matches BYYEARDAY values.
12
- * @param date Candidate date.
13
- * @param byYearDay BYYEARDAY values (positive or negative).
14
- * @return True when the candidate date matches any BYYEARDAY value.
15
- */
16
- export declare function matchesByYearDay(date: DateCandidate, byYearDay: number[]): boolean;
17
- /**
18
- * Check whether a candidate date matches BYWEEKNO values.
19
- * @param date Candidate date.
20
- * @param byWeekNo BYWEEKNO values (positive or negative).
21
- * @param firstDay First day of the week for week number calculations.
22
- * @return True when the candidate date matches any BYWEEKNO value.
23
- */
24
- export declare function matchesByWeekNo(date: DateCandidate, byWeekNo: number[], firstDay: DayOfWeek): boolean;
25
- /**
26
- * Check whether a candidate date matches BYDAY rules.
27
- * @param date Candidate date.
28
- * @param byDay BYDAY rules (with optional nth-of-period entries).
29
- * @param frequency Rule frequency.
30
- * @param periodStart Start of the current recurrence period.
31
- * @param firstDay First day of the week for weekly calculations.
32
- * @return True when the candidate date matches any BYDAY rule.
33
- */
34
- export declare function matchesByDay(date: DateCandidate, byDay: NDay[], frequency: RecurrenceRule["frequency"], periodStart: DateTime, firstDay: DayOfWeek): boolean;
@@ -1,120 +0,0 @@
1
- import { FREQ_MONTHLY, FREQ_YEARLY } from "./constants.js";
2
- import { addDays, dayOfWeek, dayOfYear, daysInMonth, daysInYear, totalWeeksInYear, weekNumber } from "./date-utils.js";
3
- /**
4
- * Check whether a candidate date matches BYMONTHDAY values.
5
- * @param date Candidate date.
6
- * @param byMonthDay BYMONTHDAY values (positive or negative).
7
- * @return True when the candidate date matches any BYMONTHDAY value.
8
- */
9
- export function matchesByMonthDay(date, byMonthDay) {
10
- const dim = daysInMonth(date.year, date.month);
11
- for (const v of byMonthDay) {
12
- if (v > 0 && date.day === v)
13
- return true;
14
- if (v < 0 && date.day === dim + v + 1)
15
- return true;
16
- }
17
- return false;
18
- }
19
- /**
20
- * Check whether a candidate date matches BYYEARDAY values.
21
- * @param date Candidate date.
22
- * @param byYearDay BYYEARDAY values (positive or negative).
23
- * @return True when the candidate date matches any BYYEARDAY value.
24
- */
25
- export function matchesByYearDay(date, byYearDay) {
26
- const diy = daysInYear(date.year);
27
- const doy = dayOfYear({ year: date.year, month: date.month, day: date.day, hour: 0, minute: 0, second: 0 });
28
- for (const v of byYearDay) {
29
- if (v > 0 && doy === v)
30
- return true;
31
- if (v < 0 && doy === diy + v + 1)
32
- return true;
33
- }
34
- return false;
35
- }
36
- /**
37
- * Check whether a candidate date matches BYWEEKNO values.
38
- * @param date Candidate date.
39
- * @param byWeekNo BYWEEKNO values (positive or negative).
40
- * @param firstDay First day of the week for week number calculations.
41
- * @return True when the candidate date matches any BYWEEKNO value.
42
- */
43
- export function matchesByWeekNo(date, byWeekNo, firstDay) {
44
- const week = weekNumber({ year: date.year, month: date.month, day: date.day, hour: 0, minute: 0, second: 0 }, firstDay);
45
- const total = totalWeeksInYear(date.year, firstDay);
46
- for (const v of byWeekNo) {
47
- if (v > 0 && week === v)
48
- return true;
49
- if (v < 0 && week === total + v + 1)
50
- return true;
51
- }
52
- return false;
53
- }
54
- /**
55
- * Check whether a candidate date matches BYDAY rules.
56
- * @param date Candidate date.
57
- * @param byDay BYDAY rules (with optional nth-of-period entries).
58
- * @param frequency Rule frequency.
59
- * @param periodStart Start of the current recurrence period.
60
- * @param firstDay First day of the week for weekly calculations.
61
- * @return True when the candidate date matches any BYDAY rule.
62
- */
63
- export function matchesByDay(date, byDay, frequency, periodStart, firstDay) {
64
- const weekday = dayOfWeek({ year: date.year, month: date.month, day: date.day, hour: 0, minute: 0, second: 0 });
65
- for (const entry of byDay) {
66
- if (entry.nthOfPeriod === undefined) {
67
- if (entry.day === weekday)
68
- return true;
69
- continue;
70
- }
71
- if (frequency !== FREQ_MONTHLY && frequency !== FREQ_YEARLY) {
72
- continue;
73
- }
74
- const matches = listNthPeriodDates(date, frequency, periodStart, firstDay)
75
- .filter((d) => dayOfWeek(d) === entry.day);
76
- const index = entry.nthOfPeriod > 0 ? entry.nthOfPeriod - 1 : matches.length + entry.nthOfPeriod;
77
- if (index >= 0 && index < matches.length) {
78
- const target = matches[index];
79
- if (target && target.year === date.year && target.month === date.month && target.day === date.day) {
80
- return true;
81
- }
82
- }
83
- }
84
- return false;
85
- }
86
- /**
87
- * List dates in the current period for nth-of-period matching.
88
- * @param date Candidate date (year/month used for period selection).
89
- * @param frequency Rule frequency.
90
- * @param periodStart Start of the current recurrence period.
91
- * @param firstDay First day of the week for weekly calculations.
92
- * @return Date list used to resolve nth-of-period BYDAY entries.
93
- */
94
- function listNthPeriodDates(date, frequency, periodStart, firstDay) {
95
- if (frequency === FREQ_YEARLY) {
96
- const result = [];
97
- for (let month = 1; month <= 12; month += 1) {
98
- const days = daysInMonth(date.year, month);
99
- for (let day = 1; day <= days; day += 1) {
100
- result.push({ year: date.year, month, day, hour: 0, minute: 0, second: 0 });
101
- }
102
- }
103
- return result;
104
- }
105
- if (frequency === FREQ_MONTHLY) {
106
- const result = [];
107
- const days = daysInMonth(date.year, date.month);
108
- for (let day = 1; day <= days; day += 1) {
109
- result.push({ year: date.year, month: date.month, day, hour: 0, minute: 0, second: 0 });
110
- }
111
- return result;
112
- }
113
- const result = [];
114
- let cursor = periodStart;
115
- for (let i = 0; i < 7; i += 1) {
116
- result.push({ year: cursor.year, month: cursor.month, day: cursor.day, hour: 0, minute: 0, second: 0 });
117
- cursor = addDays(cursor, 1);
118
- }
119
- return result;
120
- }
@@ -1,9 +0,0 @@
1
- import type { RecurrenceRule } from "../types.js";
2
- import type { DateTime } from "./types.js";
3
- /**
4
- * Normalize rule fields by copying arrays and filling defaults from the start date-time.
5
- * @param rule Recurrence rule to normalize.
6
- * @param start Anchor date-time that supplies default by* values.
7
- * @return Normalized recurrence rule with default by* fields filled.
8
- */
9
- export declare function normalizeRule(rule: RecurrenceRule, start: DateTime): RecurrenceRule;
@@ -1,57 +0,0 @@
1
- import { FREQ_HOURLY, FREQ_MINUTELY, FREQ_MONTHLY, FREQ_SECONDLY, FREQ_WEEKLY, FREQ_YEARLY } from "./constants.js";
2
- import { dayOfWeek } from "./date-utils.js";
3
- /**
4
- * Normalize rule fields by copying arrays and filling defaults from the start date-time.
5
- * @param rule Recurrence rule to normalize.
6
- * @param start Anchor date-time that supplies default by* values.
7
- * @return Normalized recurrence rule with default by* fields filled.
8
- */
9
- export function normalizeRule(rule, start) {
10
- const normalized = {
11
- ...rule,
12
- bySecond: rule.bySecond ? [...rule.bySecond] : undefined,
13
- byMinute: rule.byMinute ? [...rule.byMinute] : undefined,
14
- byHour: rule.byHour ? [...rule.byHour] : undefined,
15
- byDay: rule.byDay ? [...rule.byDay] : undefined,
16
- byMonthDay: rule.byMonthDay ? [...rule.byMonthDay] : undefined,
17
- byMonth: rule.byMonth ? [...rule.byMonth] : undefined,
18
- byYearDay: rule.byYearDay ? [...rule.byYearDay] : undefined,
19
- byWeekNo: rule.byWeekNo ? [...rule.byWeekNo] : undefined,
20
- bySetPosition: rule.bySetPosition ? [...rule.bySetPosition] : undefined,
21
- };
22
- if (normalized.frequency !== FREQ_SECONDLY && (!normalized.bySecond || normalized.bySecond.length === 0)) {
23
- normalized.bySecond = [start.second];
24
- }
25
- if (normalized.frequency !== FREQ_SECONDLY && normalized.frequency !== FREQ_MINUTELY &&
26
- (!normalized.byMinute || normalized.byMinute.length === 0)) {
27
- normalized.byMinute = [start.minute];
28
- }
29
- if (normalized.frequency !== FREQ_SECONDLY && normalized.frequency !== FREQ_MINUTELY &&
30
- normalized.frequency !== FREQ_HOURLY &&
31
- (!normalized.byHour || normalized.byHour.length === 0)) {
32
- normalized.byHour = [start.hour];
33
- }
34
- if (normalized.frequency === FREQ_WEEKLY && (!normalized.byDay || normalized.byDay.length === 0)) {
35
- normalized.byDay = [{ "@type": "NDay", day: dayOfWeek(start) }];
36
- }
37
- if (normalized.frequency === FREQ_MONTHLY && (!normalized.byDay || normalized.byDay.length === 0) &&
38
- (!normalized.byMonthDay || normalized.byMonthDay.length === 0)) {
39
- normalized.byMonthDay = [start.day];
40
- }
41
- if (normalized.frequency === FREQ_YEARLY && (!normalized.byYearDay || normalized.byYearDay.length === 0)) {
42
- const hasByMonth = normalized.byMonth && normalized.byMonth.length > 0;
43
- const hasByWeekNo = normalized.byWeekNo && normalized.byWeekNo.length > 0;
44
- const hasByMonthDay = normalized.byMonthDay && normalized.byMonthDay.length > 0;
45
- const hasByDay = normalized.byDay && normalized.byDay.length > 0;
46
- if (!hasByMonth && !hasByWeekNo && (hasByMonthDay || !hasByDay)) {
47
- normalized.byMonth = [start.month.toString()];
48
- }
49
- if (!hasByMonthDay && !hasByWeekNo && !hasByDay) {
50
- normalized.byMonthDay = [start.day];
51
- }
52
- if (hasByWeekNo && !hasByMonthDay && !hasByDay) {
53
- normalized.byDay = [{ "@type": "NDay", day: dayOfWeek(start) }];
54
- }
55
- }
56
- return normalized;
57
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Select candidate positions based on BYSETPOS rules.
3
- * @param candidates Candidate date-time strings.
4
- * @param setPos BYSETPOS indices (1-based, negative from end).
5
- * @return Selected date-time strings in the order of set positions.
6
- */
7
- export declare function applyBySetPos(candidates: string[], setPos: number[]): string[];
@@ -1,21 +0,0 @@
1
- /**
2
- * Select candidate positions based on BYSETPOS rules.
3
- * @param candidates Candidate date-time strings.
4
- * @param setPos BYSETPOS indices (1-based, negative from end).
5
- * @return Selected date-time strings in the order of set positions.
6
- */
7
- export function applyBySetPos(candidates, setPos) {
8
- const sorted = [...candidates].sort();
9
- const result = [];
10
- const total = sorted.length;
11
- for (const pos of setPos) {
12
- const index = pos > 0 ? pos - 1 : total + pos;
13
- if (index >= 0 && index < total) {
14
- const value = sorted[index];
15
- if (value !== undefined) {
16
- result.push(value);
17
- }
18
- }
19
- }
20
- return result;
21
- }
@@ -1,14 +0,0 @@
1
- import type { RecurrenceRule, TimeZoneId } from "../types.js";
2
- /**
3
- * Expand a recurrence rule into local date-time strings within a range.
4
- * @param anchor Anchor LocalDateTime for the rule.
5
- * @param rule Recurrence rule to expand.
6
- * @param fromLocal Local date-time lower bound (inclusive).
7
- * @param toLocal Local date-time upper bound (inclusive).
8
- * @param includeAnchor Whether to include the anchor occurrence.
9
- * @param timeZone Optional time zone for range comparisons.
10
- * @param fromDate Optional Date lower bound (time zone-aware).
11
- * @param toDate Optional Date upper bound (time zone-aware).
12
- * @return Local date-time strings that match the rule in range order.
13
- */
14
- export declare function expandRule(anchor: string, rule: RecurrenceRule, fromLocal: string, toLocal: string, includeAnchor: boolean, timeZone?: TimeZoneId, fromDate?: Date, toDate?: Date): string[];
@@ -1,57 +0,0 @@
1
- import { RSCALE_GREGORIAN, SKIP_OMIT } from "./constants.js";
2
- import { addInterval, compareDate, formatLocalDateTime, parseLocalDateTime } from "./date-utils.js";
3
- import { generateDateTimes } from "./rule-generate.js";
4
- import { normalizeRule } from "./rule-normalize.js";
5
- import { applyBySetPos } from "./rule-selectors.js";
6
- /**
7
- * Expand a recurrence rule into local date-time strings within a range.
8
- * @param anchor Anchor LocalDateTime for the rule.
9
- * @param rule Recurrence rule to expand.
10
- * @param fromLocal Local date-time lower bound (inclusive).
11
- * @param toLocal Local date-time upper bound (inclusive).
12
- * @param includeAnchor Whether to include the anchor occurrence.
13
- * @param timeZone Optional time zone for range comparisons.
14
- * @param fromDate Optional Date lower bound (time zone-aware).
15
- * @param toDate Optional Date upper bound (time zone-aware).
16
- * @return Local date-time strings that match the rule in range order.
17
- */
18
- export function expandRule(anchor, rule, fromLocal, toLocal, includeAnchor, timeZone, fromDate, toDate) {
19
- if (rule.rscale && rule.rscale !== RSCALE_GREGORIAN) {
20
- throw new Error(`Unsupported rscale: ${rule.rscale}`);
21
- }
22
- const start = parseLocalDateTime(anchor);
23
- const normalized = normalizeRule(rule, start);
24
- const interval = normalized.interval ?? 1;
25
- const until = normalized.until;
26
- const count = normalized.count ?? Infinity;
27
- const skip = normalized.skip ?? SKIP_OMIT;
28
- const firstDay = normalized.firstDayOfWeek ?? "mo";
29
- const results = [];
30
- let generated = 0;
31
- let cursor = start;
32
- while (generated < count) {
33
- const candidates = generateDateTimes(cursor, normalized, firstDay, skip).sort();
34
- let filtered = candidates;
35
- if (normalized.bySetPosition && normalized.bySetPosition.length > 0) {
36
- filtered = applyBySetPos(filtered, normalized.bySetPosition);
37
- }
38
- for (const dt of filtered) {
39
- generated += 1;
40
- if (generated > count)
41
- break;
42
- if (until && dt > until)
43
- return results;
44
- if (dt >= fromLocal && dt <= toLocal) {
45
- results.push(dt);
46
- }
47
- }
48
- if (until && cursor && formatLocalDateTime(cursor) > until)
49
- break;
50
- if (generated >= count)
51
- break;
52
- if (cursor && compareDate(cursor, parseLocalDateTime(toLocal)) > 0)
53
- break;
54
- cursor = addInterval(cursor, normalized.frequency, interval, firstDay);
55
- }
56
- return results;
57
- }
@@ -1,27 +0,0 @@
1
- export type RecurrenceRange = {
2
- from: Date;
3
- to: Date;
4
- };
5
- export type RecurrencePage = {
6
- items: import("../types.js").JSCalendarObject[];
7
- nextCursor?: string;
8
- };
9
- export type RecurrencePageOptions = {
10
- limit: number;
11
- cursor?: string;
12
- };
13
- export type DayOfWeek = "mo" | "tu" | "we" | "th" | "fr" | "sa" | "su";
14
- export type DateTime = {
15
- year: number;
16
- month: number;
17
- day: number;
18
- hour: number;
19
- minute: number;
20
- second: number;
21
- };
22
- export type DateCandidate = {
23
- year: number;
24
- month: number;
25
- day: number;
26
- valid: boolean;
27
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- export type { RecurrenceRange, RecurrencePage, RecurrencePageOptions } from "./recurrence/types.js";
2
- export { expandRecurrence, expandRecurrencePaged } from "./recurrence/expand.js";
@@ -1 +0,0 @@
1
- export { expandRecurrence, expandRecurrencePaged } from "./recurrence/expand.js";
package/dist/search.d.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { JSCalendarObject } from "./types.js";
2
- export type DateRangeValue = string | Date;
3
- export type DateRange = {
4
- start?: DateRangeValue;
5
- end?: DateRangeValue;
6
- };
7
- export type DateRangeOptions = {
8
- includeIncomparable?: boolean;
9
- };
10
- /**
11
- * Find a JSCalendar object by UID.
12
- * @param items Items to search.
13
- * @param uid UID to match.
14
- * @return The matching item or undefined.
15
- */
16
- export declare function findByUid<T extends JSCalendarObject>(items: T[], uid: string): T | undefined;
17
- /**
18
- * Filter items by JSCalendar @type.
19
- * @param items Items to filter.
20
- * @param type Type to match.
21
- * @return Filtered items.
22
- */
23
- export declare function filterByType<T extends JSCalendarObject>(items: T[], type: T["@type"]): T[];
24
- /**
25
- * Group JSCalendar objects by @type.
26
- * @param items Items to group.
27
- * @return Record keyed by type.
28
- */
29
- export declare function groupByType(items: JSCalendarObject[]): Record<string, JSCalendarObject[]>;
30
- /**
31
- * Filter items by free-text match.
32
- * @param items Items to search.
33
- * @param query Text query.
34
- * @return Filtered items.
35
- */
36
- export declare function filterByText(items: JSCalendarObject[], query: string): JSCalendarObject[];
37
- /**
38
- * Filter items that intersect a date range.
39
- * @param items Items to filter.
40
- * @param range Range with optional start and end.
41
- * @param options Filtering options.
42
- * @return Filtered items.
43
- */
44
- export declare function filterByDateRange(items: JSCalendarObject[], range: DateRange, options?: DateRangeOptions): JSCalendarObject[];