@microsoft/feature-management 2.1.0-preview.1 → 2.2.0

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 (50) hide show
  1. package/dist/commonjs/featureManager.js +5 -5
  2. package/dist/commonjs/featureManager.js.map +1 -1
  3. package/dist/commonjs/featureProvider.js.map +1 -1
  4. package/dist/commonjs/filter/recurrence/evaluator.js +142 -0
  5. package/dist/commonjs/filter/recurrence/evaluator.js.map +1 -0
  6. package/dist/commonjs/filter/recurrence/model.js +52 -0
  7. package/dist/commonjs/filter/recurrence/model.js.map +1 -0
  8. package/dist/commonjs/filter/recurrence/utils.js +53 -0
  9. package/dist/commonjs/filter/recurrence/utils.js.map +1 -0
  10. package/dist/commonjs/filter/recurrence/validator.js +197 -0
  11. package/dist/commonjs/filter/recurrence/validator.js.map +1 -0
  12. package/dist/commonjs/filter/{TargetingFilter.js → targetingFilter.js} +1 -1
  13. package/dist/commonjs/filter/{TargetingFilter.js.map → targetingFilter.js.map} +1 -1
  14. package/dist/commonjs/filter/timeWindowFilter.js +49 -0
  15. package/dist/commonjs/filter/timeWindowFilter.js.map +1 -0
  16. package/dist/commonjs/filter/utils.js +16 -0
  17. package/dist/commonjs/filter/utils.js.map +1 -0
  18. package/dist/commonjs/variant/{Variant.js → variant.js} +1 -1
  19. package/dist/commonjs/variant/{Variant.js.map → variant.js.map} +1 -1
  20. package/dist/commonjs/version.js +1 -1
  21. package/dist/commonjs/version.js.map +1 -1
  22. package/dist/esm/featureManager.js +3 -3
  23. package/dist/esm/featureManager.js.map +1 -1
  24. package/dist/esm/featureProvider.js.map +1 -1
  25. package/dist/esm/filter/recurrence/evaluator.js +140 -0
  26. package/dist/esm/filter/recurrence/evaluator.js.map +1 -0
  27. package/dist/esm/filter/recurrence/model.js +49 -0
  28. package/dist/esm/filter/recurrence/model.js.map +1 -0
  29. package/dist/esm/filter/recurrence/utils.js +48 -0
  30. package/dist/esm/filter/recurrence/utils.js.map +1 -0
  31. package/dist/esm/filter/recurrence/validator.js +184 -0
  32. package/dist/esm/filter/recurrence/validator.js.map +1 -0
  33. package/dist/esm/filter/{TargetingFilter.js → targetingFilter.js} +1 -1
  34. package/dist/esm/filter/{TargetingFilter.js.map → targetingFilter.js.map} +1 -1
  35. package/dist/esm/filter/timeWindowFilter.js +47 -0
  36. package/dist/esm/filter/timeWindowFilter.js.map +1 -0
  37. package/dist/esm/filter/utils.js +11 -0
  38. package/dist/esm/filter/utils.js.map +1 -0
  39. package/dist/esm/variant/{Variant.js → variant.js} +1 -1
  40. package/dist/esm/variant/{Variant.js.map → variant.js.map} +1 -1
  41. package/dist/esm/version.js +1 -1
  42. package/dist/esm/version.js.map +1 -1
  43. package/dist/umd/index.js +439 -3
  44. package/dist/umd/index.js.map +1 -1
  45. package/package.json +4 -3
  46. package/types/index.d.ts +42 -42
  47. package/dist/commonjs/filter/TimeWindowFilter.js +0 -22
  48. package/dist/commonjs/filter/TimeWindowFilter.js.map +0 -1
  49. package/dist/esm/filter/TimeWindowFilter.js +0 -20
  50. package/dist/esm/filter/TimeWindowFilter.js.map +0 -1
@@ -0,0 +1,140 @@
1
+ import { RecurrencePatternType, RecurrenceRangeType, ONE_DAY_IN_MILLISECONDS, DAYS_PER_WEEK } from './model.js';
2
+ import { addDays, getDayOfWeek, sortDaysOfWeek, calculateWeeklyDayOffset } from './utils.js';
3
+
4
+ // Copyright (c) Microsoft Corporation.
5
+ // Licensed under the MIT license.
6
+ /**
7
+ * Checks if a provided datetime is within any recurring time window specified by the recurrence information
8
+ * @param time A datetime
9
+ * @param recurrenceSpec The recurrence spcification
10
+ * @returns True if the given time is within any recurring time window; otherwise, false
11
+ */
12
+ function matchRecurrence(time, recurrenceSpec) {
13
+ const recurrenceState = findPreviousRecurrence(time, recurrenceSpec);
14
+ if (recurrenceState) {
15
+ return time.getTime() < recurrenceState.previousOccurrence.getTime() + recurrenceSpec.duration;
16
+ }
17
+ return false;
18
+ }
19
+ /**
20
+ * Finds the closest previous recurrence occurrence before the given time according to the recurrence information
21
+ * @param time A datetime
22
+ * @param recurrenceSpec The recurrence specification
23
+ * @returns The recurrence state if any previous occurrence is found; otherwise, undefined
24
+ */
25
+ function findPreviousRecurrence(time, recurrenceSpec) {
26
+ if (time < recurrenceSpec.startTime) {
27
+ return undefined;
28
+ }
29
+ let result;
30
+ const pattern = recurrenceSpec.pattern;
31
+ if (pattern.type === RecurrencePatternType.Daily) {
32
+ result = findPreviousDailyRecurrence(time, recurrenceSpec);
33
+ }
34
+ else if (pattern.type === RecurrencePatternType.Weekly) {
35
+ result = findPreviousWeeklyRecurrence(time, recurrenceSpec);
36
+ }
37
+ else {
38
+ throw new Error("Unsupported recurrence pattern type.");
39
+ }
40
+ const { previousOccurrence, numberOfOccurrences } = result;
41
+ const range = recurrenceSpec.range;
42
+ if (range.type === RecurrenceRangeType.EndDate) {
43
+ if (previousOccurrence > range.endDate) {
44
+ return undefined;
45
+ }
46
+ }
47
+ else if (range.type === RecurrenceRangeType.Numbered) {
48
+ if (numberOfOccurrences > range.numberOfOccurrences) {
49
+ return undefined;
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+ function findPreviousDailyRecurrence(time, recurrenceSpec) {
55
+ const startTime = recurrenceSpec.startTime;
56
+ const timeGap = time.getTime() - startTime.getTime();
57
+ const pattern = recurrenceSpec.pattern;
58
+ const numberOfIntervals = Math.floor(timeGap / (pattern.interval * ONE_DAY_IN_MILLISECONDS));
59
+ return {
60
+ previousOccurrence: addDays(startTime, numberOfIntervals * pattern.interval),
61
+ numberOfOccurrences: numberOfIntervals + 1
62
+ };
63
+ }
64
+ function findPreviousWeeklyRecurrence(time, recurrenceSpec) {
65
+ /*
66
+ * Algorithm:
67
+ * 1. first find day 0 (d0), it's the day representing the start day on the week of `Start`.
68
+ * 2. find start day of the most recent occurring week d0 + floor((time - d0) / (interval * 7)) * (interval * 7)
69
+ * 3. if that's over 7 days ago, then previous occurence is the day with the max offset of the last occurring week
70
+ * 4. if gotten this far, then the current week is the most recent occurring week:
71
+ i. if time > day with min offset, then previous occurence is the day with max offset less than current
72
+ ii. if time < day with min offset, then previous occurence is the day with the max offset of previous occurring week
73
+ */
74
+ const startTime = recurrenceSpec.startTime;
75
+ const startDay = getDayOfWeek(startTime, recurrenceSpec.timezoneOffset);
76
+ const pattern = recurrenceSpec.pattern;
77
+ const sortedDaysOfWeek = sortDaysOfWeek(pattern.daysOfWeek, pattern.firstDayOfWeek);
78
+ /*
79
+ * Example:
80
+ * startTime = 2024-12-11 (Tue)
81
+ * pattern.interval = 2 pattern.firstDayOfWeek = Sun pattern.daysOfWeek = [Wed, Sun]
82
+ * sortedDaysOfWeek = [Sun, Wed]
83
+ * firstDayofStartWeek = 2024-12-08 (Sun)
84
+ *
85
+ * time = 2024-12-23 (Mon) timeGap = 15 days
86
+ * the most recent occurring week: 2024-12-22 ~ 2024-12-28
87
+ * number of intervals before the most recent occurring week = 15 / (2 * 7) = 1 (2024-12-08 ~ 2023-12-21)
88
+ * number of occurrences before the most recent occurring week = 1 * 2 - 1 = 1 (2024-12-11)
89
+ * firstDayOfLastOccurringWeek = 2024-12-22
90
+ */
91
+ const firstDayofStartWeek = addDays(startTime, -calculateWeeklyDayOffset(startDay, pattern.firstDayOfWeek));
92
+ const timeGap = time.getTime() - firstDayofStartWeek.getTime();
93
+ // number of intervals before the most recent occurring week
94
+ const numberOfIntervals = Math.floor(timeGap / (pattern.interval * DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS));
95
+ // number of occurrences before the most recent occurring week, it is possible to be negative
96
+ let numberOfOccurrences = numberOfIntervals * sortedDaysOfWeek.length - sortedDaysOfWeek.indexOf(startDay);
97
+ const firstDayOfLatestOccurringWeek = addDays(firstDayofStartWeek, numberOfIntervals * pattern.interval * DAYS_PER_WEEK);
98
+ // the current time is out of the last occurring week
99
+ if (time > addDays(firstDayOfLatestOccurringWeek, DAYS_PER_WEEK)) {
100
+ numberOfOccurrences += sortDaysOfWeek.length;
101
+ // day with max offset in the last occurring week
102
+ const previousOccurrence = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek.at(-1), pattern.firstDayOfWeek));
103
+ return {
104
+ previousOccurrence: previousOccurrence,
105
+ numberOfOccurrences: numberOfOccurrences
106
+ };
107
+ }
108
+ let dayWithMinOffset = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek[0], pattern.firstDayOfWeek));
109
+ if (dayWithMinOffset < startTime) {
110
+ numberOfOccurrences = 0;
111
+ dayWithMinOffset = startTime;
112
+ }
113
+ let previousOccurrence;
114
+ if (time >= dayWithMinOffset) {
115
+ // the previous occurence is the day with max offset less than current
116
+ previousOccurrence = dayWithMinOffset;
117
+ numberOfOccurrences += 1;
118
+ const dayWithMinOffsetIndex = sortedDaysOfWeek.indexOf(getDayOfWeek(dayWithMinOffset, recurrenceSpec.timezoneOffset));
119
+ for (let i = dayWithMinOffsetIndex + 1; i < sortedDaysOfWeek.length; i++) {
120
+ const day = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek[i], pattern.firstDayOfWeek));
121
+ if (time < day) {
122
+ break;
123
+ }
124
+ previousOccurrence = day;
125
+ numberOfOccurrences += 1;
126
+ }
127
+ }
128
+ else {
129
+ const firstDayOfPreviousOccurringWeek = addDays(firstDayOfLatestOccurringWeek, -pattern.interval * DAYS_PER_WEEK);
130
+ // the previous occurence is the day with the max offset of previous occurring week
131
+ previousOccurrence = addDays(firstDayOfPreviousOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek.at(-1), pattern.firstDayOfWeek));
132
+ }
133
+ return {
134
+ previousOccurrence: previousOccurrence,
135
+ numberOfOccurrences: numberOfOccurrences
136
+ };
137
+ }
138
+
139
+ export { matchRecurrence };
140
+ //# sourceMappingURL=evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.js","sources":["../../../../src/filter/recurrence/evaluator.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { RecurrenceSpec, RecurrencePatternType, RecurrenceRangeType, DAYS_PER_WEEK, ONE_DAY_IN_MILLISECONDS } from \"./model.js\";\nimport { calculateWeeklyDayOffset, sortDaysOfWeek, getDayOfWeek, addDays } from \"./utils.js\";\n\ntype RecurrenceState = {\n previousOccurrence: Date;\n numberOfOccurrences: number;\n}\n\n/**\n * Checks if a provided datetime is within any recurring time window specified by the recurrence information\n * @param time A datetime\n * @param recurrenceSpec The recurrence spcification\n * @returns True if the given time is within any recurring time window; otherwise, false\n */\nexport function matchRecurrence(time: Date, recurrenceSpec: RecurrenceSpec): boolean {\n const recurrenceState = findPreviousRecurrence(time, recurrenceSpec);\n if (recurrenceState) {\n return time.getTime() < recurrenceState.previousOccurrence.getTime() + recurrenceSpec.duration;\n }\n return false;\n}\n\n/**\n * Finds the closest previous recurrence occurrence before the given time according to the recurrence information\n * @param time A datetime\n * @param recurrenceSpec The recurrence specification\n * @returns The recurrence state if any previous occurrence is found; otherwise, undefined\n */\nfunction findPreviousRecurrence(time: Date, recurrenceSpec: RecurrenceSpec): RecurrenceState | undefined {\n if (time < recurrenceSpec.startTime) {\n return undefined;\n }\n let result: RecurrenceState;\n const pattern = recurrenceSpec.pattern;\n if (pattern.type === RecurrencePatternType.Daily) {\n result = findPreviousDailyRecurrence(time, recurrenceSpec);\n } else if (pattern.type === RecurrencePatternType.Weekly) {\n result = findPreviousWeeklyRecurrence(time, recurrenceSpec);\n } else {\n throw new Error(\"Unsupported recurrence pattern type.\");\n }\n const { previousOccurrence, numberOfOccurrences } = result;\n\n const range = recurrenceSpec.range;\n if (range.type === RecurrenceRangeType.EndDate) {\n if (previousOccurrence > range.endDate!) {\n return undefined;\n }\n } else if (range.type === RecurrenceRangeType.Numbered) {\n if (numberOfOccurrences > range.numberOfOccurrences!) {\n return undefined;\n }\n }\n return result;\n}\n\nfunction findPreviousDailyRecurrence(time: Date, recurrenceSpec: RecurrenceSpec): RecurrenceState {\n const startTime = recurrenceSpec.startTime;\n const timeGap = time.getTime() - startTime.getTime();\n const pattern = recurrenceSpec.pattern;\n const numberOfIntervals = Math.floor(timeGap / (pattern.interval * ONE_DAY_IN_MILLISECONDS));\n return {\n previousOccurrence: addDays(startTime, numberOfIntervals * pattern.interval),\n numberOfOccurrences: numberOfIntervals + 1\n };\n}\n\nfunction findPreviousWeeklyRecurrence(time: Date, recurrenceSpec: RecurrenceSpec): RecurrenceState {\n /*\n * Algorithm:\n * 1. first find day 0 (d0), it's the day representing the start day on the week of `Start`.\n * 2. find start day of the most recent occurring week d0 + floor((time - d0) / (interval * 7)) * (interval * 7)\n * 3. if that's over 7 days ago, then previous occurence is the day with the max offset of the last occurring week\n * 4. if gotten this far, then the current week is the most recent occurring week:\n i. if time > day with min offset, then previous occurence is the day with max offset less than current\n ii. if time < day with min offset, then previous occurence is the day with the max offset of previous occurring week\n */\n const startTime = recurrenceSpec.startTime;\n const startDay = getDayOfWeek(startTime, recurrenceSpec.timezoneOffset);\n const pattern = recurrenceSpec.pattern;\n const sortedDaysOfWeek = sortDaysOfWeek(pattern.daysOfWeek!, pattern.firstDayOfWeek!);\n\n /*\n * Example:\n * startTime = 2024-12-11 (Tue)\n * pattern.interval = 2 pattern.firstDayOfWeek = Sun pattern.daysOfWeek = [Wed, Sun]\n * sortedDaysOfWeek = [Sun, Wed]\n * firstDayofStartWeek = 2024-12-08 (Sun)\n *\n * time = 2024-12-23 (Mon) timeGap = 15 days\n * the most recent occurring week: 2024-12-22 ~ 2024-12-28\n * number of intervals before the most recent occurring week = 15 / (2 * 7) = 1 (2024-12-08 ~ 2023-12-21)\n * number of occurrences before the most recent occurring week = 1 * 2 - 1 = 1 (2024-12-11)\n * firstDayOfLastOccurringWeek = 2024-12-22\n */\n const firstDayofStartWeek = addDays(startTime, -calculateWeeklyDayOffset(startDay, pattern.firstDayOfWeek!));\n const timeGap = time.getTime() - firstDayofStartWeek.getTime();\n // number of intervals before the most recent occurring week\n const numberOfIntervals = Math.floor(timeGap / (pattern.interval * DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS));\n // number of occurrences before the most recent occurring week, it is possible to be negative\n let numberOfOccurrences = numberOfIntervals * sortedDaysOfWeek.length - sortedDaysOfWeek.indexOf(startDay);\n const firstDayOfLatestOccurringWeek = addDays(firstDayofStartWeek, numberOfIntervals * pattern.interval * DAYS_PER_WEEK);\n\n // the current time is out of the last occurring week\n if (time > addDays(firstDayOfLatestOccurringWeek, DAYS_PER_WEEK)) {\n numberOfOccurrences += sortDaysOfWeek.length;\n // day with max offset in the last occurring week\n const previousOccurrence = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek.at(-1)!, pattern.firstDayOfWeek!));\n return {\n previousOccurrence: previousOccurrence,\n numberOfOccurrences: numberOfOccurrences\n };\n }\n\n let dayWithMinOffset = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek[0], pattern.firstDayOfWeek!));\n if (dayWithMinOffset < startTime) {\n numberOfOccurrences = 0;\n dayWithMinOffset = startTime;\n }\n let previousOccurrence;\n if (time >= dayWithMinOffset) {\n // the previous occurence is the day with max offset less than current\n previousOccurrence = dayWithMinOffset;\n numberOfOccurrences += 1;\n const dayWithMinOffsetIndex = sortedDaysOfWeek.indexOf(getDayOfWeek(dayWithMinOffset, recurrenceSpec.timezoneOffset));\n for (let i = dayWithMinOffsetIndex + 1; i < sortedDaysOfWeek.length; i++) {\n const day = addDays(firstDayOfLatestOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek[i], pattern.firstDayOfWeek!));\n if (time < day) {\n break;\n }\n previousOccurrence = day;\n numberOfOccurrences += 1;\n }\n } else {\n const firstDayOfPreviousOccurringWeek = addDays(firstDayOfLatestOccurringWeek, -pattern.interval * DAYS_PER_WEEK);\n // the previous occurence is the day with the max offset of previous occurring week\n previousOccurrence = addDays(firstDayOfPreviousOccurringWeek, calculateWeeklyDayOffset(sortedDaysOfWeek.at(-1)!, pattern.firstDayOfWeek!));\n }\n return {\n previousOccurrence: previousOccurrence,\n numberOfOccurrences: numberOfOccurrences\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ const DAYS_PER_WEEK = 7;
4
+ const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
5
+ var DayOfWeek;
6
+ (function (DayOfWeek) {
7
+ DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
8
+ DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
9
+ DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
10
+ DayOfWeek[DayOfWeek["Wednesday"] = 3] = "Wednesday";
11
+ DayOfWeek[DayOfWeek["Thursday"] = 4] = "Thursday";
12
+ DayOfWeek[DayOfWeek["Friday"] = 5] = "Friday";
13
+ DayOfWeek[DayOfWeek["Saturday"] = 6] = "Saturday";
14
+ })(DayOfWeek || (DayOfWeek = {}));
15
+ /**
16
+ * The recurrence pattern describes the frequency by which the time window repeats
17
+ */
18
+ var RecurrencePatternType;
19
+ (function (RecurrencePatternType) {
20
+ /**
21
+ * The pattern where the time window will repeat based on the number of days specified by interval between occurrences
22
+ */
23
+ RecurrencePatternType[RecurrencePatternType["Daily"] = 0] = "Daily";
24
+ /**
25
+ * The pattern where the time window will repeat on the same day or days of the week, based on the number of weeks between each set of occurrences
26
+ */
27
+ RecurrencePatternType[RecurrencePatternType["Weekly"] = 1] = "Weekly";
28
+ })(RecurrencePatternType || (RecurrencePatternType = {}));
29
+ /**
30
+ * The recurrence range specifies the date range over which the time window repeats
31
+ */
32
+ var RecurrenceRangeType;
33
+ (function (RecurrenceRangeType) {
34
+ /**
35
+ * The recurrence has no end and repeats on all the days that fit the corresponding pattern
36
+ */
37
+ RecurrenceRangeType[RecurrenceRangeType["NoEnd"] = 0] = "NoEnd";
38
+ /**
39
+ * The recurrence repeats on all the days that fit the corresponding pattern until or on the specified end date
40
+ */
41
+ RecurrenceRangeType[RecurrenceRangeType["EndDate"] = 1] = "EndDate";
42
+ /**
43
+ * The recurrence repeats for the specified number of occurrences that match the pattern
44
+ */
45
+ RecurrenceRangeType[RecurrenceRangeType["Numbered"] = 2] = "Numbered";
46
+ })(RecurrenceRangeType || (RecurrenceRangeType = {}));
47
+
48
+ export { DAYS_PER_WEEK, DayOfWeek, ONE_DAY_IN_MILLISECONDS, RecurrencePatternType, RecurrenceRangeType };
49
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sources":["../../../../src/filter/recurrence/model.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nexport const DAYS_PER_WEEK = 7;\nexport const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;\n\nexport enum DayOfWeek {\n Sunday = 0,\n Monday = 1,\n Tuesday = 2,\n Wednesday = 3,\n Thursday = 4,\n Friday = 5,\n Saturday = 6\n}\n\n/**\n * The recurrence pattern describes the frequency by which the time window repeats\n */\nexport enum RecurrencePatternType {\n /**\n * The pattern where the time window will repeat based on the number of days specified by interval between occurrences\n */\n Daily,\n /**\n * The pattern where the time window will repeat on the same day or days of the week, based on the number of weeks between each set of occurrences\n */\n Weekly\n}\n\n/**\n * The recurrence range specifies the date range over which the time window repeats\n */\nexport enum RecurrenceRangeType {\n /**\n * The recurrence has no end and repeats on all the days that fit the corresponding pattern\n */\n NoEnd,\n /**\n * The recurrence repeats on all the days that fit the corresponding pattern until or on the specified end date\n */\n EndDate,\n /**\n * The recurrence repeats for the specified number of occurrences that match the pattern\n */\n Numbered\n}\n\n/**\n * The recurrence pattern describes the frequency by which the time window repeats\n */\nexport type RecurrencePattern = {\n /**\n * The type of the recurrence pattern\n */\n type: RecurrencePatternType;\n /**\n * The number of units between occurrences, where units can be in days or weeks, depending on the pattern type\n */\n interval: number;\n /**\n * The days of the week when the time window occurs, which is only applicable for 'Weekly' pattern\n */\n daysOfWeek?: DayOfWeek[];\n /**\n * The first day of the week, which is only applicable for 'Weekly' pattern\n */\n firstDayOfWeek?: DayOfWeek;\n};\n\n/**\n * The recurrence range describes a date range over which the time window repeats\n */\nexport type RecurrenceRange = {\n /**\n * The type of the recurrence range\n */\n type: RecurrenceRangeType;\n /**\n * The date to stop applying the recurrence pattern, which is only applicable for 'EndDate' range\n */\n endDate?: Date;\n /**\n * The number of times to repeat the time window, which is only applicable for 'Numbered' range\n */\n numberOfOccurrences?: number;\n};\n\n/**\n * Specification defines the recurring time window\n */\nexport type RecurrenceSpec = {\n /**\n * The start time of the first/base time window\n */\n startTime: Date;\n /**\n * The duration of each time window in milliseconds\n */\n duration: number;\n /**\n * The recurrence pattern\n */\n pattern: RecurrencePattern;\n /**\n * The recurrence range\n */\n range: RecurrenceRange;\n /**\n * The timezone offset in milliseconds, which helps to determine the day of week of a given date\n */\n timezoneOffset: number;\n};\n"],"names":[],"mappings":"AAAA;AACA;AAEO,MAAM,aAAa,GAAG,EAAE;AAClB,MAAA,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK;IAE/C,UAQX;AARD,CAAA,UAAY,SAAS,EAAA;AACjB,IAAA,SAAA,CAAA,SAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAU,CAAA;AACV,IAAA,SAAA,CAAA,SAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAU,CAAA;AACV,IAAA,SAAA,CAAA,SAAA,CAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,SAAW,CAAA;AACX,IAAA,SAAA,CAAA,SAAA,CAAA,WAAA,CAAA,GAAA,CAAA,CAAA,GAAA,WAAa,CAAA;AACb,IAAA,SAAA,CAAA,SAAA,CAAA,UAAA,CAAA,GAAA,CAAA,CAAA,GAAA,UAAY,CAAA;AACZ,IAAA,SAAA,CAAA,SAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAU,CAAA;AACV,IAAA,SAAA,CAAA,SAAA,CAAA,UAAA,CAAA,GAAA,CAAA,CAAA,GAAA,UAAY,CAAA;AAChB,CAAC,EARW,SAAS,KAAT,SAAS,GAQpB,EAAA,CAAA,CAAA,CAAA;AAED;;AAEG;IACS,sBASX;AATD,CAAA,UAAY,qBAAqB,EAAA;AAC7B;;AAEG;AACH,IAAA,qBAAA,CAAA,qBAAA,CAAA,OAAA,CAAA,GAAA,CAAA,CAAA,GAAA,OAAK,CAAA;AACL;;AAEG;AACH,IAAA,qBAAA,CAAA,qBAAA,CAAA,QAAA,CAAA,GAAA,CAAA,CAAA,GAAA,QAAM,CAAA;AACV,CAAC,EATW,qBAAqB,KAArB,qBAAqB,GAShC,EAAA,CAAA,CAAA,CAAA;AAED;;AAEG;IACS,oBAaX;AAbD,CAAA,UAAY,mBAAmB,EAAA;AAC3B;;AAEG;AACH,IAAA,mBAAA,CAAA,mBAAA,CAAA,OAAA,CAAA,GAAA,CAAA,CAAA,GAAA,OAAK,CAAA;AACL;;AAEG;AACH,IAAA,mBAAA,CAAA,mBAAA,CAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,SAAO,CAAA;AACP;;AAEG;AACH,IAAA,mBAAA,CAAA,mBAAA,CAAA,UAAA,CAAA,GAAA,CAAA,CAAA,GAAA,UAAQ,CAAA;AACZ,CAAC,EAbW,mBAAmB,KAAnB,mBAAmB,GAa9B,EAAA,CAAA,CAAA;;;;"}
@@ -0,0 +1,48 @@
1
+ import { DAYS_PER_WEEK } from './model.js';
2
+
3
+ // Copyright (c) Microsoft Corporation.
4
+ // Licensed under the MIT license.
5
+ /**
6
+ * Calculates the offset in days between two given days of the week.
7
+ * @param day1 A day of week
8
+ * @param day2 A day of week
9
+ * @returns The number of days to be added to day2 to reach day1
10
+ */
11
+ function calculateWeeklyDayOffset(day1, day2) {
12
+ return (day1 - day2 + DAYS_PER_WEEK) % DAYS_PER_WEEK;
13
+ }
14
+ /**
15
+ * Sorts a collection of days of week based on their offsets from a specified first day of week.
16
+ * @param daysOfWeek A collection of days of week
17
+ * @param firstDayOfWeek The first day of week which will be the first element in the sorted result
18
+ * @returns The sorted days of week
19
+ */
20
+ function sortDaysOfWeek(daysOfWeek, firstDayOfWeek) {
21
+ const sortedDaysOfWeek = daysOfWeek.slice();
22
+ sortedDaysOfWeek.sort((x, y) => calculateWeeklyDayOffset(x, firstDayOfWeek) - calculateWeeklyDayOffset(y, firstDayOfWeek));
23
+ return sortedDaysOfWeek;
24
+ }
25
+ /**
26
+ * Gets the day of week of a given date based on the timezone offset.
27
+ * @param date A UTC date
28
+ * @param timezoneOffsetInMs The timezone offset in milliseconds
29
+ * @returns The day of week (0 for Sunday, 1 for Monday, ..., 6 for Saturday)
30
+ */
31
+ function getDayOfWeek(date, timezoneOffsetInMs) {
32
+ const alignedDate = new Date(date.getTime() + timezoneOffsetInMs);
33
+ return alignedDate.getUTCDay();
34
+ }
35
+ /**
36
+ * Adds a specified number of days to a given date.
37
+ * @param date The date to add days to
38
+ * @param days The number of days to add
39
+ * @returns The new date
40
+ */
41
+ function addDays(date, days) {
42
+ const result = new Date(date);
43
+ result.setDate(result.getDate() + days);
44
+ return result;
45
+ }
46
+
47
+ export { addDays, calculateWeeklyDayOffset, getDayOfWeek, sortDaysOfWeek };
48
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../../src/filter/recurrence/utils.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { DayOfWeek, DAYS_PER_WEEK } from \"./model.js\";\n\n/**\n * Calculates the offset in days between two given days of the week.\n * @param day1 A day of week\n * @param day2 A day of week\n * @returns The number of days to be added to day2 to reach day1\n */\nexport function calculateWeeklyDayOffset(day1: DayOfWeek, day2: DayOfWeek): number {\n return (day1 - day2 + DAYS_PER_WEEK) % DAYS_PER_WEEK;\n}\n\n/**\n * Sorts a collection of days of week based on their offsets from a specified first day of week.\n * @param daysOfWeek A collection of days of week\n * @param firstDayOfWeek The first day of week which will be the first element in the sorted result\n * @returns The sorted days of week\n */\nexport function sortDaysOfWeek(daysOfWeek: DayOfWeek[], firstDayOfWeek: DayOfWeek): DayOfWeek[] {\n const sortedDaysOfWeek = daysOfWeek.slice();\n sortedDaysOfWeek.sort((x, y) => calculateWeeklyDayOffset(x, firstDayOfWeek) - calculateWeeklyDayOffset(y, firstDayOfWeek));\n return sortedDaysOfWeek;\n}\n\n/**\n * Gets the day of week of a given date based on the timezone offset.\n * @param date A UTC date\n * @param timezoneOffsetInMs The timezone offset in milliseconds\n * @returns The day of week (0 for Sunday, 1 for Monday, ..., 6 for Saturday)\n */\nexport function getDayOfWeek(date: Date, timezoneOffsetInMs: number): number {\n const alignedDate = new Date(date.getTime() + timezoneOffsetInMs);\n return alignedDate.getUTCDay();\n}\n\n/**\n * Adds a specified number of days to a given date.\n * @param date The date to add days to\n * @param days The number of days to add\n * @returns The new date\n */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n"],"names":[],"mappings":";;AAAA;AACA;AAIA;;;;;AAKG;AACa,SAAA,wBAAwB,CAAC,IAAe,EAAE,IAAe,EAAA;IACrE,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,aAAa,IAAI,aAAa,CAAC;AACzD,CAAC;AAED;;;;;AAKG;AACa,SAAA,cAAc,CAAC,UAAuB,EAAE,cAAyB,EAAA;AAC7E,IAAA,MAAM,gBAAgB,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC;IAC5C,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,wBAAwB,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,wBAAwB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;AAC3H,IAAA,OAAO,gBAAgB,CAAC;AAC5B,CAAC;AAED;;;;;AAKG;AACa,SAAA,YAAY,CAAC,IAAU,EAAE,kBAA0B,EAAA;AAC/D,IAAA,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,kBAAkB,CAAC,CAAC;AAClE,IAAA,OAAO,WAAW,CAAC,SAAS,EAAE,CAAC;AACnC,CAAC;AAED;;;;;AAKG;AACa,SAAA,OAAO,CAAC,IAAU,EAAE,IAAY,EAAA;AAC5C,IAAA,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;AACxC,IAAA,OAAO,MAAM,CAAC;AAClB;;;;"}
@@ -0,0 +1,184 @@
1
+ import { buildInvalidParameterErrorMessage, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE, VALUE_OUT_OF_RANGE_ERROR_MESSAGE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE } from '../utils.js';
2
+ import { ONE_DAY_IN_MILLISECONDS, RecurrencePatternType, DayOfWeek, DAYS_PER_WEEK, RecurrenceRangeType } from './model.js';
3
+ import { getDayOfWeek, sortDaysOfWeek, calculateWeeklyDayOffset } from './utils.js';
4
+
5
+ // Copyright (c) Microsoft Corporation.
6
+ // Licensed under the MIT license.
7
+ const START_NOT_MATCHED_ERROR_MESSAGE = "Start date is not a valid first occurrence.";
8
+ const TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE = "Time window duration cannot be longer than how frequently it occurs or be longer than 10 years.";
9
+ const PATTERN = "Recurrence.Pattern";
10
+ const PATTERN_TYPE = "Recurrence.Pattern.Type";
11
+ const INTERVAL = "Recurrence.Pattern.Interval";
12
+ const DAYS_OF_WEEK = "Recurrence.Pattern.DaysOfWeek";
13
+ const FIRST_DAY_OF_WEEK = "Recurrence.Pattern.FirstDayOfWeek";
14
+ const RANGE = "Recurrence.Range";
15
+ const RANGE_TYPE = "Recurrence.Range.Type";
16
+ const END_DATE = "Recurrence.Range.EndDate";
17
+ const NUMBER_OF_OCCURRENCES = "Recurrence.Range.NumberOfOccurrences";
18
+ /**
19
+ * Parses @see RecurrenceParameters into a @see RecurrenceSpec object. If the parameter is invalid, an error will be thrown.
20
+ * @param startTime The start time of the base time window
21
+ * @param day2 The end time of the base time window
22
+ * @param recurrenceParameters The @see RecurrenceParameters to parse
23
+ * @param TimeZoneOffset The time zone offset in milliseconds, by default 0
24
+ * @returns A @see RecurrenceSpec object
25
+ */
26
+ function parseRecurrenceParameter(startTime, endTime, recurrenceParameters, TimeZoneOffset = 0) {
27
+ if (startTime === undefined) {
28
+ throw new Error(buildInvalidParameterErrorMessage("Start", REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
29
+ }
30
+ if (endTime === undefined) {
31
+ throw new Error(buildInvalidParameterErrorMessage("End", REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
32
+ }
33
+ if (startTime >= endTime) {
34
+ throw new Error(buildInvalidParameterErrorMessage("End", VALUE_OUT_OF_RANGE_ERROR_MESSAGE));
35
+ }
36
+ const timeWindowDuration = endTime.getTime() - startTime.getTime();
37
+ if (timeWindowDuration > 10 * 365 * ONE_DAY_IN_MILLISECONDS) { // time window duration cannot be longer than 10 years
38
+ throw new Error(buildInvalidParameterErrorMessage("End", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));
39
+ }
40
+ return {
41
+ startTime: startTime,
42
+ duration: timeWindowDuration,
43
+ pattern: parseRecurrencePattern(startTime, endTime, recurrenceParameters, TimeZoneOffset),
44
+ range: parseRecurrenceRange(startTime, recurrenceParameters),
45
+ timezoneOffset: TimeZoneOffset
46
+ };
47
+ }
48
+ function parseRecurrencePattern(startTime, endTime, recurrenceParameters, timeZoneOffset) {
49
+ const rawPattern = recurrenceParameters.Pattern;
50
+ if (rawPattern === undefined) {
51
+ throw new Error(buildInvalidParameterErrorMessage(PATTERN, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
52
+ }
53
+ if (rawPattern.Type === undefined) {
54
+ throw new Error(buildInvalidParameterErrorMessage(PATTERN_TYPE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
55
+ }
56
+ const patternType = RecurrencePatternType[rawPattern.Type];
57
+ if (patternType === undefined) {
58
+ throw new Error(buildInvalidParameterErrorMessage(PATTERN_TYPE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
59
+ }
60
+ let interval = rawPattern.Interval;
61
+ if (interval !== undefined) {
62
+ if (typeof interval !== "number") {
63
+ throw new Error(buildInvalidParameterErrorMessage(INTERVAL, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
64
+ }
65
+ else if (interval <= 0 || !Number.isInteger(interval)) {
66
+ throw new Error(buildInvalidParameterErrorMessage(INTERVAL, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));
67
+ }
68
+ }
69
+ else {
70
+ interval = 1;
71
+ }
72
+ const parsedPattern = {
73
+ type: patternType,
74
+ interval: interval
75
+ };
76
+ const timeWindowDuration = endTime.getTime() - startTime.getTime();
77
+ if (patternType === RecurrencePatternType.Daily) {
78
+ if (timeWindowDuration > interval * ONE_DAY_IN_MILLISECONDS) {
79
+ throw new Error(buildInvalidParameterErrorMessage("End", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));
80
+ }
81
+ }
82
+ else if (patternType === RecurrencePatternType.Weekly) {
83
+ let firstDayOfWeek;
84
+ if (rawPattern.FirstDayOfWeek !== undefined) {
85
+ firstDayOfWeek = DayOfWeek[rawPattern.FirstDayOfWeek];
86
+ if (firstDayOfWeek === undefined) {
87
+ throw new Error(buildInvalidParameterErrorMessage(FIRST_DAY_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
88
+ }
89
+ }
90
+ else {
91
+ firstDayOfWeek = DayOfWeek.Sunday;
92
+ }
93
+ parsedPattern.firstDayOfWeek = firstDayOfWeek;
94
+ if (rawPattern.DaysOfWeek === undefined || rawPattern.DaysOfWeek.length === 0) {
95
+ throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
96
+ }
97
+ if (!Array.isArray(rawPattern.DaysOfWeek)) {
98
+ throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
99
+ }
100
+ const daysOfWeek = [...new Set(rawPattern.DaysOfWeek.map(day => DayOfWeek[day]))]; // dedup array
101
+ if (daysOfWeek.some(day => day === undefined)) {
102
+ throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
103
+ }
104
+ if (timeWindowDuration > interval * DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS ||
105
+ !isDurationCompliantWithDaysOfWeek(timeWindowDuration, interval, daysOfWeek, firstDayOfWeek)) {
106
+ throw new Error(buildInvalidParameterErrorMessage("End", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));
107
+ }
108
+ parsedPattern.daysOfWeek = daysOfWeek;
109
+ // check whether "Start" is a valid first occurrence
110
+ const alignedStartDay = getDayOfWeek(startTime, timeZoneOffset);
111
+ if (!daysOfWeek.find(day => day === alignedStartDay)) {
112
+ throw new Error(buildInvalidParameterErrorMessage("Start", START_NOT_MATCHED_ERROR_MESSAGE));
113
+ }
114
+ }
115
+ return parsedPattern;
116
+ }
117
+ function parseRecurrenceRange(startTime, recurrenceParameters) {
118
+ const rawRange = recurrenceParameters.Range;
119
+ if (rawRange === undefined) {
120
+ throw new Error(buildInvalidParameterErrorMessage(RANGE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
121
+ }
122
+ if (rawRange.Type === undefined) {
123
+ throw new Error(buildInvalidParameterErrorMessage(RANGE_TYPE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));
124
+ }
125
+ const rangeType = RecurrenceRangeType[rawRange.Type];
126
+ if (rangeType === undefined) {
127
+ throw new Error(buildInvalidParameterErrorMessage(RANGE_TYPE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
128
+ }
129
+ const parsedRange = { type: rangeType };
130
+ if (rangeType === RecurrenceRangeType.EndDate) {
131
+ let endDate;
132
+ if (rawRange.EndDate !== undefined) {
133
+ endDate = new Date(rawRange.EndDate);
134
+ if (isNaN(endDate.getTime())) {
135
+ throw new Error(buildInvalidParameterErrorMessage(END_DATE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
136
+ }
137
+ if (endDate < startTime) {
138
+ throw new Error(buildInvalidParameterErrorMessage(END_DATE, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));
139
+ }
140
+ }
141
+ else {
142
+ endDate = new Date(8.64e15); // the maximum date in ECMAScript: https://262.ecma-international.org/5.1/#sec-15.9.1.1
143
+ }
144
+ parsedRange.endDate = endDate;
145
+ }
146
+ else if (rangeType === RecurrenceRangeType.Numbered) {
147
+ let numberOfOccurrences = rawRange.NumberOfOccurrences;
148
+ if (numberOfOccurrences !== undefined) {
149
+ if (typeof numberOfOccurrences !== "number") {
150
+ throw new Error(buildInvalidParameterErrorMessage(NUMBER_OF_OCCURRENCES, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
151
+ }
152
+ else if (numberOfOccurrences <= 0 || !Number.isInteger(numberOfOccurrences)) {
153
+ throw new Error(buildInvalidParameterErrorMessage(NUMBER_OF_OCCURRENCES, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));
154
+ }
155
+ }
156
+ else {
157
+ numberOfOccurrences = Number.MAX_SAFE_INTEGER;
158
+ }
159
+ parsedRange.numberOfOccurrences = numberOfOccurrences;
160
+ }
161
+ return parsedRange;
162
+ }
163
+ function isDurationCompliantWithDaysOfWeek(duration, interval, daysOfWeek, firstDayOfWeek) {
164
+ if (daysOfWeek.length === 1) {
165
+ return true;
166
+ }
167
+ const sortedDaysOfWeek = sortDaysOfWeek(daysOfWeek, firstDayOfWeek);
168
+ let prev = sortedDaysOfWeek[0]; // the closest occurrence day to the first day of week
169
+ let minGap = DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS;
170
+ for (let i = 1; i < sortedDaysOfWeek.length; i++) { // skip the first day
171
+ const gap = calculateWeeklyDayOffset(sortedDaysOfWeek[i], prev) * ONE_DAY_IN_MILLISECONDS;
172
+ minGap = gap < minGap ? gap : minGap;
173
+ prev = sortedDaysOfWeek[i];
174
+ }
175
+ // It may across weeks. Check the next week if the interval is one week.
176
+ if (interval == 1) {
177
+ const gap = calculateWeeklyDayOffset(sortedDaysOfWeek[0], prev) * ONE_DAY_IN_MILLISECONDS;
178
+ minGap = gap < minGap ? gap : minGap;
179
+ }
180
+ return minGap >= duration;
181
+ }
182
+
183
+ export { DAYS_OF_WEEK, END_DATE, FIRST_DAY_OF_WEEK, INTERVAL, NUMBER_OF_OCCURRENCES, PATTERN, PATTERN_TYPE, RANGE, RANGE_TYPE, START_NOT_MATCHED_ERROR_MESSAGE, TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE, parseRecurrenceParameter };
184
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sources":["../../../../src/filter/recurrence/validator.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { RecurrenceParameters } from \"../timeWindowFilter.js\";\nimport { VALUE_OUT_OF_RANGE_ERROR_MESSAGE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE, buildInvalidParameterErrorMessage } from \"../utils.js\";\nimport { DayOfWeek, RecurrenceSpec, RecurrencePattern, RecurrenceRange, RecurrencePatternType, RecurrenceRangeType, DAYS_PER_WEEK, ONE_DAY_IN_MILLISECONDS } from \"./model.js\";\nimport { calculateWeeklyDayOffset, sortDaysOfWeek, getDayOfWeek } from \"./utils.js\";\n\nexport const START_NOT_MATCHED_ERROR_MESSAGE = \"Start date is not a valid first occurrence.\";\nexport const TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE = \"Time window duration cannot be longer than how frequently it occurs or be longer than 10 years.\";\nexport const PATTERN = \"Recurrence.Pattern\";\nexport const PATTERN_TYPE = \"Recurrence.Pattern.Type\";\nexport const INTERVAL = \"Recurrence.Pattern.Interval\";\nexport const DAYS_OF_WEEK = \"Recurrence.Pattern.DaysOfWeek\";\nexport const FIRST_DAY_OF_WEEK = \"Recurrence.Pattern.FirstDayOfWeek\";\nexport const RANGE = \"Recurrence.Range\";\nexport const RANGE_TYPE = \"Recurrence.Range.Type\";\nexport const END_DATE = \"Recurrence.Range.EndDate\";\nexport const NUMBER_OF_OCCURRENCES = \"Recurrence.Range.NumberOfOccurrences\";\n\n/**\n * Parses @see RecurrenceParameters into a @see RecurrenceSpec object. If the parameter is invalid, an error will be thrown.\n * @param startTime The start time of the base time window\n * @param day2 The end time of the base time window\n * @param recurrenceParameters The @see RecurrenceParameters to parse\n * @param TimeZoneOffset The time zone offset in milliseconds, by default 0\n * @returns A @see RecurrenceSpec object\n */\nexport function parseRecurrenceParameter(startTime: Date | undefined, endTime: Date | undefined, recurrenceParameters: RecurrenceParameters, TimeZoneOffset: number = 0): RecurrenceSpec {\n if (startTime === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(\"Start\", REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n if (endTime === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(\"End\", REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n if (startTime >= endTime) {\n throw new Error(buildInvalidParameterErrorMessage(\"End\", VALUE_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n const timeWindowDuration = endTime.getTime() - startTime.getTime();\n if (timeWindowDuration > 10 * 365 * ONE_DAY_IN_MILLISECONDS) { // time window duration cannot be longer than 10 years\n throw new Error(buildInvalidParameterErrorMessage(\"End\", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n\n return {\n startTime: startTime,\n duration: timeWindowDuration,\n pattern: parseRecurrencePattern(startTime, endTime, recurrenceParameters, TimeZoneOffset),\n range: parseRecurrenceRange(startTime, recurrenceParameters),\n timezoneOffset: TimeZoneOffset\n };\n}\n\nfunction parseRecurrencePattern(startTime: Date, endTime: Date, recurrenceParameters: RecurrenceParameters, timeZoneOffset: number): RecurrencePattern {\n const rawPattern = recurrenceParameters.Pattern;\n if (rawPattern === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(PATTERN, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n if (rawPattern.Type === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(PATTERN_TYPE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n const patternType = RecurrencePatternType[rawPattern.Type];\n if (patternType === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(PATTERN_TYPE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n let interval = rawPattern.Interval;\n if (interval !== undefined) {\n if (typeof interval !== \"number\") {\n throw new Error(buildInvalidParameterErrorMessage(INTERVAL, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n } else if (interval <= 0 || !Number.isInteger(interval)) {\n throw new Error(buildInvalidParameterErrorMessage(INTERVAL, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n } else {\n interval = 1;\n }\n const parsedPattern: RecurrencePattern = {\n type: patternType,\n interval: interval\n };\n const timeWindowDuration = endTime.getTime() - startTime.getTime();\n if (patternType === RecurrencePatternType.Daily) {\n if (timeWindowDuration > interval * ONE_DAY_IN_MILLISECONDS) {\n throw new Error(buildInvalidParameterErrorMessage(\"End\", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n } else if (patternType === RecurrencePatternType.Weekly) {\n let firstDayOfWeek: DayOfWeek;\n if (rawPattern.FirstDayOfWeek !== undefined) {\n firstDayOfWeek = DayOfWeek[rawPattern.FirstDayOfWeek];\n if (firstDayOfWeek === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(FIRST_DAY_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n }\n else {\n firstDayOfWeek = DayOfWeek.Sunday;\n }\n parsedPattern.firstDayOfWeek = firstDayOfWeek;\n\n if (rawPattern.DaysOfWeek === undefined || rawPattern.DaysOfWeek.length === 0) {\n throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n if (!Array.isArray(rawPattern.DaysOfWeek)) {\n throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n const daysOfWeek = [...new Set(rawPattern.DaysOfWeek.map(day => DayOfWeek[day]))]; // dedup array\n if (daysOfWeek.some(day => day === undefined)) {\n throw new Error(buildInvalidParameterErrorMessage(DAYS_OF_WEEK, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n if (timeWindowDuration > interval * DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS ||\n !isDurationCompliantWithDaysOfWeek(timeWindowDuration, interval, daysOfWeek, firstDayOfWeek)) {\n throw new Error(buildInvalidParameterErrorMessage(\"End\", TIME_WINDOW_DURATION_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n parsedPattern.daysOfWeek = daysOfWeek;\n\n // check whether \"Start\" is a valid first occurrence\n const alignedStartDay = getDayOfWeek(startTime, timeZoneOffset);\n if (!daysOfWeek.find(day => day === alignedStartDay)) {\n throw new Error(buildInvalidParameterErrorMessage(\"Start\", START_NOT_MATCHED_ERROR_MESSAGE));\n }\n }\n return parsedPattern;\n}\n\nfunction parseRecurrenceRange(startTime: Date, recurrenceParameters: RecurrenceParameters): RecurrenceRange {\n const rawRange = recurrenceParameters.Range;\n if (rawRange === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(RANGE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n if (rawRange.Type === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(RANGE_TYPE, REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE));\n }\n const rangeType = RecurrenceRangeType[rawRange.Type];\n if (rangeType === undefined) {\n throw new Error(buildInvalidParameterErrorMessage(RANGE_TYPE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n const parsedRange: RecurrenceRange = { type: rangeType };\n if (rangeType === RecurrenceRangeType.EndDate) {\n let endDate: Date;\n if (rawRange.EndDate !== undefined) {\n endDate = new Date(rawRange.EndDate);\n if (isNaN(endDate.getTime())) {\n throw new Error(buildInvalidParameterErrorMessage(END_DATE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n }\n if (endDate < startTime) {\n throw new Error(buildInvalidParameterErrorMessage(END_DATE, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n } else {\n endDate = new Date(8.64e15); // the maximum date in ECMAScript: https://262.ecma-international.org/5.1/#sec-15.9.1.1\n }\n parsedRange.endDate = endDate;\n } else if (rangeType === RecurrenceRangeType.Numbered) {\n let numberOfOccurrences = rawRange.NumberOfOccurrences;\n if (numberOfOccurrences !== undefined) {\n if (typeof numberOfOccurrences !== \"number\") {\n throw new Error(buildInvalidParameterErrorMessage(NUMBER_OF_OCCURRENCES, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n } else if (numberOfOccurrences <= 0 || !Number.isInteger(numberOfOccurrences)) {\n throw new Error(buildInvalidParameterErrorMessage(NUMBER_OF_OCCURRENCES, VALUE_OUT_OF_RANGE_ERROR_MESSAGE));\n }\n } else {\n numberOfOccurrences = Number.MAX_SAFE_INTEGER;\n }\n parsedRange.numberOfOccurrences = numberOfOccurrences;\n }\n return parsedRange;\n}\n\nfunction isDurationCompliantWithDaysOfWeek(duration: number, interval: number, daysOfWeek: DayOfWeek[], firstDayOfWeek: DayOfWeek): boolean {\n if (daysOfWeek.length === 1) {\n return true;\n }\n const sortedDaysOfWeek = sortDaysOfWeek(daysOfWeek, firstDayOfWeek);\n let prev = sortedDaysOfWeek[0]; // the closest occurrence day to the first day of week\n let minGap = DAYS_PER_WEEK * ONE_DAY_IN_MILLISECONDS;\n for (let i = 1; i < sortedDaysOfWeek.length; i++) { // skip the first day\n const gap = calculateWeeklyDayOffset(sortedDaysOfWeek[i], prev) * ONE_DAY_IN_MILLISECONDS;\n minGap = gap < minGap ? gap : minGap;\n prev = sortedDaysOfWeek[i];\n }\n // It may across weeks. Check the next week if the interval is one week.\n if (interval == 1) {\n const gap = calculateWeeklyDayOffset(sortedDaysOfWeek[0], prev) * ONE_DAY_IN_MILLISECONDS;\n minGap = gap < minGap ? gap : minGap;\n }\n return minGap >= duration;\n}\n"],"names":[],"mappings":"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"}
@@ -73,4 +73,4 @@ class TargetingFilter {
73
73
  }
74
74
 
75
75
  export { TargetingFilter };
76
- //# sourceMappingURL=TargetingFilter.js.map
76
+ //# sourceMappingURL=targetingFilter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TargetingFilter.js","sources":["../../../src/filter/TargetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./FeatureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext, ITargetingContextAccessor } from \"../common/targetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n readonly name: string = \"Microsoft.Targeting\";\n readonly #targetingContextAccessor?: ITargetingContextAccessor;\n\n constructor(targetingContextAccessor?: ITargetingContextAccessor) {\n this.#targetingContextAccessor = targetingContextAccessor;\n }\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(featureName, parameters);\n\n let targetingContext: ITargetingContext | undefined;\n if (appContext?.userId !== undefined || appContext?.groups !== undefined) {\n targetingContext = appContext;\n } else if (this.#targetingContextAccessor !== undefined) {\n targetingContext = this.#targetingContextAccessor.getTargetingContext();\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (targetingContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(targetingContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (targetingContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (targetingContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (targetingContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(targetingContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (targetingContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (targetingContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(targetingContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(targetingContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(featureName: string, parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);\n }\n // validate RolloutPercentage for each group\n if (parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"targetingFilter.js","sources":["../../../src/filter/targetingFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./featureFilter.js\";\nimport { isTargetedPercentile } from \"../common/targetingEvaluator.js\";\nimport { ITargetingContext, ITargetingContextAccessor } from \"../common/targetingContext.js\";\n\ntype TargetingFilterParameters = {\n Audience: {\n DefaultRolloutPercentage: number;\n Users?: string[];\n Groups?: {\n Name: string;\n RolloutPercentage: number;\n }[];\n Exclusion?: {\n Users?: string[];\n Groups?: string[];\n };\n }\n}\n\ntype TargetingFilterEvaluationContext = {\n featureName: string;\n parameters: TargetingFilterParameters;\n}\n\nexport class TargetingFilter implements IFeatureFilter {\n readonly name: string = \"Microsoft.Targeting\";\n readonly #targetingContextAccessor?: ITargetingContextAccessor;\n\n constructor(targetingContextAccessor?: ITargetingContextAccessor) {\n this.#targetingContextAccessor = targetingContextAccessor;\n }\n\n async evaluate(context: TargetingFilterEvaluationContext, appContext?: ITargetingContext): Promise<boolean> {\n const { featureName, parameters } = context;\n TargetingFilter.#validateParameters(featureName, parameters);\n\n let targetingContext: ITargetingContext | undefined;\n if (appContext?.userId !== undefined || appContext?.groups !== undefined) {\n targetingContext = appContext;\n } else if (this.#targetingContextAccessor !== undefined) {\n targetingContext = this.#targetingContextAccessor.getTargetingContext();\n }\n\n if (parameters.Audience.Exclusion !== undefined) {\n // check if the user is in the exclusion list\n if (targetingContext?.userId !== undefined &&\n parameters.Audience.Exclusion.Users !== undefined &&\n parameters.Audience.Exclusion.Users.includes(targetingContext.userId)) {\n return false;\n }\n // check if the user is in a group within exclusion list\n if (targetingContext?.groups !== undefined &&\n parameters.Audience.Exclusion.Groups !== undefined) {\n for (const excludedGroup of parameters.Audience.Exclusion.Groups) {\n if (targetingContext.groups.includes(excludedGroup)) {\n return false;\n }\n }\n }\n }\n\n // check if the user is being targeted directly\n if (targetingContext?.userId !== undefined &&\n parameters.Audience.Users !== undefined &&\n parameters.Audience.Users.includes(targetingContext.userId)) {\n return true;\n }\n\n // check if the user is in a group that is being targeted\n if (targetingContext?.groups !== undefined &&\n parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (targetingContext.groups.includes(group.Name)) {\n const hint = `${featureName}\\n${group.Name}`;\n if (await isTargetedPercentile(targetingContext.userId, hint, 0, group.RolloutPercentage)) {\n return true;\n }\n }\n }\n }\n\n // check if the user is being targeted by a default rollout percentage\n const hint = featureName;\n return isTargetedPercentile(targetingContext?.userId, hint, 0, parameters.Audience.DefaultRolloutPercentage);\n }\n\n static #validateParameters(featureName: string, parameters: TargetingFilterParameters): void {\n if (parameters.Audience.DefaultRolloutPercentage < 0 || parameters.Audience.DefaultRolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. Audience.DefaultRolloutPercentage must be a number between 0 and 100.`);\n }\n // validate RolloutPercentage for each group\n if (parameters.Audience.Groups !== undefined) {\n for (const group of parameters.Audience.Groups) {\n if (group.RolloutPercentage < 0 || group.RolloutPercentage > 100) {\n throw new Error(`Invalid feature flag: ${featureName}. RolloutPercentage of group ${group.Name} must be a number between 0 and 100.`);\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { parseRecurrenceParameter } from './recurrence/validator.js';
2
+ import { matchRecurrence } from './recurrence/evaluator.js';
3
+ import { buildInvalidParameterErrorMessage, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE } from './utils.js';
4
+
5
+ // Copyright (c) Microsoft Corporation.
6
+ // Licensed under the MIT license.
7
+ class TimeWindowFilter {
8
+ name = "Microsoft.TimeWindow";
9
+ evaluate(context) {
10
+ const { featureName, parameters } = context;
11
+ const startTime = parameters.Start !== undefined ? new Date(parameters.Start) : undefined;
12
+ const endTime = parameters.End !== undefined ? new Date(parameters.End) : undefined;
13
+ const baseErrorMessage = `The ${this.name} feature filter is not valid for feature ${featureName}. `;
14
+ if (startTime === undefined && endTime === undefined) {
15
+ // If neither start nor end time is specified, then the filter is not applicable.
16
+ console.warn(baseErrorMessage + "It must specify either 'Start', 'End', or both.");
17
+ return false;
18
+ }
19
+ if (startTime !== undefined && isNaN(startTime.getTime())) {
20
+ console.warn(baseErrorMessage + buildInvalidParameterErrorMessage("Start", UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
21
+ return false;
22
+ }
23
+ if (endTime !== undefined && isNaN(endTime.getTime())) {
24
+ console.warn(baseErrorMessage + buildInvalidParameterErrorMessage("End", UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));
25
+ return false;
26
+ }
27
+ const now = new Date();
28
+ if ((startTime === undefined || startTime <= now) && (endTime === undefined || now < endTime)) {
29
+ return true;
30
+ }
31
+ if (parameters.Recurrence !== undefined) {
32
+ let recurrence;
33
+ try {
34
+ recurrence = parseRecurrenceParameter(startTime, endTime, parameters.Recurrence);
35
+ }
36
+ catch (error) {
37
+ console.warn(baseErrorMessage + error.message);
38
+ return false;
39
+ }
40
+ return matchRecurrence(now, recurrence);
41
+ }
42
+ return false;
43
+ }
44
+ }
45
+
46
+ export { TimeWindowFilter };
47
+ //# sourceMappingURL=timeWindowFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeWindowFilter.js","sources":["../../../src/filter/timeWindowFilter.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { IFeatureFilter } from \"./featureFilter.js\";\nimport { RecurrenceSpec } from \"./recurrence/model.js\";\nimport { parseRecurrenceParameter } from \"./recurrence/validator.js\";\nimport { matchRecurrence } from \"./recurrence/evaluator.js\";\nimport { UNRECOGNIZABLE_VALUE_ERROR_MESSAGE, buildInvalidParameterErrorMessage } from \"./utils.js\";\n\ntype TimeWindowFilterEvaluationContext = {\n featureName: string;\n parameters: TimeWindowParameters;\n};\n\ntype TimeWindowParameters = {\n Start?: string;\n End?: string;\n Recurrence?: RecurrenceParameters;\n};\n\nexport type RecurrenceParameters = {\n Pattern: {\n Type: string;\n Interval?: number;\n DaysOfWeek?: string[];\n FirstDayOfWeek?: string;\n },\n Range: {\n Type: string;\n EndDate?: string;\n NumberOfOccurrences?: number;\n }\n};\n\nexport class TimeWindowFilter implements IFeatureFilter {\n readonly name: string = \"Microsoft.TimeWindow\";\n\n evaluate(context: TimeWindowFilterEvaluationContext): boolean {\n const {featureName, parameters} = context;\n const startTime = parameters.Start !== undefined ? new Date(parameters.Start) : undefined;\n const endTime = parameters.End !== undefined ? new Date(parameters.End) : undefined;\n\n const baseErrorMessage = `The ${this.name} feature filter is not valid for feature ${featureName}. `;\n\n if (startTime === undefined && endTime === undefined) {\n // If neither start nor end time is specified, then the filter is not applicable.\n console.warn(baseErrorMessage + \"It must specify either 'Start', 'End', or both.\");\n return false;\n }\n\n if (startTime !== undefined && isNaN(startTime.getTime())) {\n console.warn(baseErrorMessage + buildInvalidParameterErrorMessage(\"Start\", UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n return false;\n }\n\n if (endTime !== undefined && isNaN(endTime.getTime())) {\n console.warn(baseErrorMessage + buildInvalidParameterErrorMessage(\"End\", UNRECOGNIZABLE_VALUE_ERROR_MESSAGE));\n return false;\n }\n\n const now = new Date();\n\n if ((startTime === undefined || startTime <= now) && (endTime === undefined || now < endTime)) {\n return true;\n }\n\n if (parameters.Recurrence !== undefined) {\n let recurrence: RecurrenceSpec;\n try {\n recurrence = parseRecurrenceParameter(startTime, endTime, parameters.Recurrence);\n } catch (error) {\n console.warn(baseErrorMessage + error.message);\n return false;\n }\n return matchRecurrence(now, recurrence);\n }\n\n return false;\n }\n}\n"],"names":[],"mappings":";;;;AAAA;AACA;MAiCa,gBAAgB,CAAA;IAChB,IAAI,GAAW,sBAAsB,CAAC;AAE/C,IAAA,QAAQ,CAAC,OAA0C,EAAA;AAC/C,QAAA,MAAM,EAAC,WAAW,EAAE,UAAU,EAAC,GAAG,OAAO,CAAC;QAC1C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,KAAK,SAAS,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;QAC1F,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,KAAK,SAAS,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QAEpF,MAAM,gBAAgB,GAAG,CAAO,IAAA,EAAA,IAAI,CAAC,IAAI,CAAA,yCAAA,EAA4C,WAAW,CAAA,EAAA,CAAI,CAAC;QAErG,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE;;AAElD,YAAA,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,iDAAiD,CAAC,CAAC;AACnF,YAAA,OAAO,KAAK,CAAC;SAChB;AAED,QAAA,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAE;AACvD,YAAA,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,iCAAiC,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC,CAAC;AAChH,YAAA,OAAO,KAAK,CAAC;SAChB;AAED,QAAA,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE;AACnD,YAAA,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,iCAAiC,CAAC,KAAK,EAAE,kCAAkC,CAAC,CAAC,CAAC;AAC9G,YAAA,OAAO,KAAK,CAAC;SAChB;AAED,QAAA,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,SAAS,IAAI,GAAG,MAAM,OAAO,KAAK,SAAS,IAAI,GAAG,GAAG,OAAO,CAAC,EAAE;AAC3F,YAAA,OAAO,IAAI,CAAC;SACf;AAED,QAAA,IAAI,UAAU,CAAC,UAAU,KAAK,SAAS,EAAE;AACrC,YAAA,IAAI,UAA0B,CAAC;AAC/B,YAAA,IAAI;gBACA,UAAU,GAAG,wBAAwB,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;aACpF;YAAC,OAAO,KAAK,EAAE;gBACZ,OAAO,CAAC,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAC/C,gBAAA,OAAO,KAAK,CAAC;aAChB;AACD,YAAA,OAAO,eAAe,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;SAC3C;AAED,QAAA,OAAO,KAAK,CAAC;KAChB;AACJ;;;;"}
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ const VALUE_OUT_OF_RANGE_ERROR_MESSAGE = "The value is out of the accepted range.";
4
+ const UNRECOGNIZABLE_VALUE_ERROR_MESSAGE = "The value is unrecognizable.";
5
+ const REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE = "Value cannot be undefined or empty.";
6
+ function buildInvalidParameterErrorMessage(parameterName, additionalInfo) {
7
+ return `The ${parameterName} parameter is not valid. ` + (additionalInfo ?? "");
8
+ }
9
+
10
+ export { REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE, UNRECOGNIZABLE_VALUE_ERROR_MESSAGE, VALUE_OUT_OF_RANGE_ERROR_MESSAGE, buildInvalidParameterErrorMessage };
11
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../src/filter/utils.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nexport const VALUE_OUT_OF_RANGE_ERROR_MESSAGE = \"The value is out of the accepted range.\";\nexport const UNRECOGNIZABLE_VALUE_ERROR_MESSAGE = \"The value is unrecognizable.\";\nexport const REQUIRED_PARAMETER_MISSING_ERROR_MESSAGE = \"Value cannot be undefined or empty.\";\n\nexport function buildInvalidParameterErrorMessage(parameterName: string, additionalInfo?: string): string {\n return `The ${parameterName} parameter is not valid. ` + (additionalInfo ?? \"\");\n}\n"],"names":[],"mappings":"AAAA;AACA;AAEO,MAAM,gCAAgC,GAAG,0CAA0C;AACnF,MAAM,kCAAkC,GAAG,+BAA+B;AAC1E,MAAM,wCAAwC,GAAG,sCAAsC;AAE9E,SAAA,iCAAiC,CAAC,aAAqB,EAAE,cAAuB,EAAA;IAC5F,OAAO,CAAA,IAAA,EAAO,aAAa,CAA2B,yBAAA,CAAA,IAAI,cAAc,IAAI,EAAE,CAAC,CAAC;AACpF;;;;"}