@craftguild/jscalendar 0.2.0 → 0.3.1
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.
- package/README.md +63 -0
- package/dist/__tests__/builders.test.d.ts +1 -0
- package/dist/__tests__/builders.test.js +82 -0
- package/dist/__tests__/calendar-extra.test.js +36 -0
- package/dist/__tests__/recurrence.test.js +123 -0
- package/dist/__tests__/search.test.js +27 -0
- package/dist/__tests__/utils.test.js +3 -0
- package/dist/__tests__/validation.test.js +113 -0
- package/dist/ical.d.ts +6 -0
- package/dist/ical.js +71 -3
- package/dist/jscal/base.d.ts +90 -0
- package/dist/jscal/base.js +181 -0
- package/dist/jscal/builders.d.ts +135 -0
- package/dist/jscal/builders.js +220 -0
- package/dist/jscal/constants.d.ts +11 -0
- package/dist/jscal/constants.js +11 -0
- package/dist/jscal/datetime.d.ts +14 -0
- package/dist/jscal/datetime.js +42 -0
- package/dist/jscal/defaults.d.ts +31 -0
- package/dist/jscal/defaults.js +102 -0
- package/dist/jscal/duration.d.ts +43 -0
- package/dist/jscal/duration.js +72 -0
- package/dist/jscal/event.d.ts +17 -0
- package/dist/jscal/event.js +71 -0
- package/dist/jscal/group.d.ts +25 -0
- package/dist/jscal/group.js +62 -0
- package/dist/jscal/guards.d.ts +19 -0
- package/dist/jscal/guards.js +25 -0
- package/dist/jscal/ids.d.ts +11 -0
- package/dist/jscal/ids.js +77 -0
- package/dist/jscal/normalize.d.ts +32 -0
- package/dist/jscal/normalize.js +45 -0
- package/dist/jscal/task.d.ts +17 -0
- package/dist/jscal/task.js +60 -0
- package/dist/jscal/types.d.ts +38 -0
- package/dist/jscal/types.js +1 -0
- package/dist/jscal.d.ts +77 -70
- package/dist/jscal.js +77 -465
- package/dist/patch.d.ts +13 -0
- package/dist/patch.js +166 -41
- package/dist/recurrence/constants.d.ts +13 -0
- package/dist/recurrence/constants.js +13 -0
- package/dist/recurrence/date-utils.d.ts +125 -0
- package/dist/recurrence/date-utils.js +259 -0
- package/dist/recurrence/expand.d.ts +23 -0
- package/dist/recurrence/expand.js +294 -0
- package/dist/recurrence/rule-candidates.d.ts +21 -0
- package/dist/recurrence/rule-candidates.js +120 -0
- package/dist/recurrence/rule-generate.d.ts +11 -0
- package/dist/recurrence/rule-generate.js +36 -0
- package/dist/recurrence/rule-matchers.d.ts +34 -0
- package/dist/recurrence/rule-matchers.js +120 -0
- package/dist/recurrence/rule-normalize.d.ts +9 -0
- package/dist/recurrence/rule-normalize.js +57 -0
- package/dist/recurrence/rule-selectors.d.ts +7 -0
- package/dist/recurrence/rule-selectors.js +21 -0
- package/dist/recurrence/rules.d.ts +14 -0
- package/dist/recurrence/rules.js +57 -0
- package/dist/recurrence/types.d.ts +27 -0
- package/dist/recurrence/types.js +1 -0
- package/dist/recurrence.d.ts +2 -15
- package/dist/recurrence.js +1 -674
- package/dist/search.d.ts +30 -0
- package/dist/search.js +92 -8
- package/dist/timezones/chunk_1.d.ts +2 -0
- package/dist/timezones/chunk_1.js +72 -0
- package/dist/timezones/chunk_2.d.ts +2 -0
- package/dist/timezones/chunk_2.js +72 -0
- package/dist/timezones/chunk_3.d.ts +2 -0
- package/dist/timezones/chunk_3.js +72 -0
- package/dist/timezones/chunk_4.d.ts +2 -0
- package/dist/timezones/chunk_4.js +72 -0
- package/dist/timezones/chunk_5.d.ts +2 -0
- package/dist/timezones/chunk_5.js +72 -0
- package/dist/timezones/chunk_6.d.ts +2 -0
- package/dist/timezones/chunk_6.js +72 -0
- package/dist/timezones/chunk_7.d.ts +2 -0
- package/dist/timezones/chunk_7.js +6 -0
- package/dist/timezones.d.ts +5 -0
- package/dist/timezones.js +14 -3
- package/dist/utils.d.ts +72 -0
- package/dist/utils.js +85 -1
- package/dist/validate/asserts.d.ts +155 -0
- package/dist/validate/asserts.js +381 -0
- package/dist/validate/constants.d.ts +25 -0
- package/dist/validate/constants.js +33 -0
- package/dist/validate/error.d.ts +19 -0
- package/dist/validate/error.js +25 -0
- package/dist/validate/validators-common.d.ts +64 -0
- package/dist/validate/validators-common.js +385 -0
- package/dist/validate/validators-objects.d.ts +8 -0
- package/dist/validate/validators-objects.js +70 -0
- package/dist/validate/validators-recurrence.d.ts +15 -0
- package/dist/validate/validators-recurrence.js +115 -0
- package/dist/validate/validators.d.ts +1 -0
- package/dist/validate/validators.js +1 -0
- package/dist/validate.d.ts +2 -6
- package/dist/validate.js +2 -745
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
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[];
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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[];
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/recurrence.d.ts
CHANGED
|
@@ -1,15 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
from: Date;
|
|
4
|
-
to: Date;
|
|
5
|
-
};
|
|
6
|
-
export type RecurrencePage = {
|
|
7
|
-
items: JSCalendarObject[];
|
|
8
|
-
nextCursor?: string;
|
|
9
|
-
};
|
|
10
|
-
export type RecurrencePageOptions = {
|
|
11
|
-
limit: number;
|
|
12
|
-
cursor?: string;
|
|
13
|
-
};
|
|
14
|
-
export declare function expandRecurrence(items: JSCalendarObject[], range: RecurrenceRange): Generator<JSCalendarObject>;
|
|
15
|
-
export declare function expandRecurrencePaged(items: JSCalendarObject[], range: RecurrenceRange, options: RecurrencePageOptions): RecurrencePage;
|
|
1
|
+
export type { RecurrenceRange, RecurrencePage, RecurrencePageOptions } from "./recurrence/types.js";
|
|
2
|
+
export { expandRecurrence, expandRecurrencePaged } from "./recurrence/expand.js";
|