scschedule 3.0.0 → 3.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.
- package/README.md +25 -39
- package/dist/constants.d.ts +0 -7
- package/dist/constants.js +0 -7
- package/dist/getAvailableRangesFromSchedule.js +10 -13
- package/dist/getNextAvailableFromSchedule.js +7 -9
- package/dist/getNextUnavailableFromSchedule.js +19 -23
- package/dist/internal/doTimeRangesOverlap.d.ts +1 -1
- package/dist/internal/getEffectiveTimesForWeekday.d.ts +2 -1
- package/dist/internal/getEffectiveTimesForWeekday.js +9 -14
- package/dist/internal/index.d.ts +1 -2
- package/dist/internal/index.js +0 -2
- package/dist/internal/isTimeInTimeRange.d.ts +2 -1
- package/dist/internal/splitCrossMidnightTimeRange.d.ts +1 -1
- package/dist/internal/splitCrossMidnightTimeRange.js +3 -2
- package/dist/internal/types.d.ts +13 -0
- package/dist/internal/types.js +1 -0
- package/dist/internal/validateNoSpilloverConflictsAtOverrideBoundaries.js +91 -101
- package/dist/internal/validateScDateFormats.js +2 -4
- package/dist/isScheduleAvailable.js +4 -4
- package/dist/types.d.ts +6 -46
- package/dist/validateSchedule.d.ts +2 -2
- package/dist/validateSchedule.js +2 -6
- package/package.json +2 -2
- package/dist/internal/validateNoOverlappingTimesInRule.d.ts +0 -5
- package/dist/internal/validateNoOverlappingTimesInRule.js +0 -55
- package/dist/internal/validateNonEmptyTimes.d.ts +0 -5
- package/dist/internal/validateNonEmptyTimes.js +0 -36
package/README.md
CHANGED
|
@@ -51,7 +51,8 @@ const restaurant: Schedule = {
|
|
|
51
51
|
weekly: [
|
|
52
52
|
{
|
|
53
53
|
weekdays: sWeekdays('-MTWTFS'), // Mon-Sat
|
|
54
|
-
|
|
54
|
+
from: sTime('11:00'),
|
|
55
|
+
to: sTime('22:00'),
|
|
55
56
|
},
|
|
56
57
|
],
|
|
57
58
|
}
|
|
@@ -97,10 +98,6 @@ Define recurring availability patterns for specific days of the week:
|
|
|
97
98
|
```typescript
|
|
98
99
|
interface WeeklyScheduleRule {
|
|
99
100
|
weekdays: SWeekdays // e.g., 'SMTWTFS' or '-MTWTF-'
|
|
100
|
-
times: TimeRange[] // Array of time ranges
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
interface TimeRange {
|
|
104
101
|
from: STime // e.g., '09:00'
|
|
105
102
|
to: STime // e.g., '17:00'
|
|
106
103
|
}
|
|
@@ -278,7 +275,8 @@ const restaurant: Schedule = {
|
|
|
278
275
|
weekly: [
|
|
279
276
|
{
|
|
280
277
|
weekdays: sWeekdays('-MTWTFS'), // Mon-Sat
|
|
281
|
-
|
|
278
|
+
from: sTime('11:00'),
|
|
279
|
+
to: sTime('22:00'),
|
|
282
280
|
},
|
|
283
281
|
],
|
|
284
282
|
}
|
|
@@ -291,10 +289,13 @@ const restaurant: Schedule = {
|
|
|
291
289
|
weekly: [
|
|
292
290
|
{
|
|
293
291
|
weekdays: sWeekdays('-MTWTFS'), // Mon-Sat
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
292
|
+
from: sTime('11:00'),
|
|
293
|
+
to: sTime('14:00'), // Lunch
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
weekdays: sWeekdays('-MTWTFS'), // Mon-Sat
|
|
297
|
+
from: sTime('17:00'),
|
|
298
|
+
to: sTime('22:00'), // Dinner
|
|
298
299
|
},
|
|
299
300
|
],
|
|
300
301
|
}
|
|
@@ -308,12 +309,14 @@ const restaurant: Schedule = {
|
|
|
308
309
|
{
|
|
309
310
|
// Weekdays: longer hours
|
|
310
311
|
weekdays: sWeekdays('-MTWTF-'), // Mon-Fri
|
|
311
|
-
|
|
312
|
+
from: sTime('10:00'),
|
|
313
|
+
to: sTime('23:00'),
|
|
312
314
|
},
|
|
313
315
|
{
|
|
314
316
|
// Weekends: shorter hours
|
|
315
317
|
weekdays: sWeekdays('S-----S'), // Sat-Sun
|
|
316
|
-
|
|
318
|
+
from: sTime('12:00'),
|
|
319
|
+
to: sTime('20:00'),
|
|
317
320
|
},
|
|
318
321
|
],
|
|
319
322
|
}
|
|
@@ -348,7 +351,8 @@ const withExtendedHours: Schedule = {
|
|
|
348
351
|
{
|
|
349
352
|
// Extended hours for December, weekends only
|
|
350
353
|
weekdays: sWeekdays('S-----S'),
|
|
351
|
-
|
|
354
|
+
from: sTime('08:00'),
|
|
355
|
+
to: sTime('23:00'),
|
|
352
356
|
},
|
|
353
357
|
],
|
|
354
358
|
},
|
|
@@ -369,7 +373,8 @@ const newSchedule: Schedule = {
|
|
|
369
373
|
rules: [
|
|
370
374
|
{
|
|
371
375
|
weekdays: sWeekdays('SMTWTFS'), // All days
|
|
372
|
-
|
|
376
|
+
from: sTime('09:00'),
|
|
377
|
+
to: sTime('21:00'),
|
|
373
378
|
},
|
|
374
379
|
],
|
|
375
380
|
},
|
|
@@ -384,7 +389,8 @@ const lateNightBar: Schedule = {
|
|
|
384
389
|
weekly: [
|
|
385
390
|
{
|
|
386
391
|
weekdays: sWeekdays('----TFS'), // Thu-Sat
|
|
387
|
-
|
|
392
|
+
from: sTime('20:00'),
|
|
393
|
+
to: sTime('03:00'), // 8PM-3AM
|
|
388
394
|
},
|
|
389
395
|
],
|
|
390
396
|
}
|
|
@@ -429,7 +435,8 @@ const popUpShop: Schedule = {
|
|
|
429
435
|
rules: [
|
|
430
436
|
{
|
|
431
437
|
weekdays: sWeekdays('SMTWTFS'),
|
|
432
|
-
|
|
438
|
+
from: sTime('10:00'),
|
|
439
|
+
to: sTime('18:00'),
|
|
433
440
|
},
|
|
434
441
|
],
|
|
435
442
|
},
|
|
@@ -446,7 +453,8 @@ const businessHours: Schedule = {
|
|
|
446
453
|
weekly: [
|
|
447
454
|
{
|
|
448
455
|
weekdays: sWeekdays('-MTWTFS'),
|
|
449
|
-
|
|
456
|
+
from: sTime('11:00'),
|
|
457
|
+
to: sTime('22:00'),
|
|
450
458
|
},
|
|
451
459
|
],
|
|
452
460
|
}
|
|
@@ -488,17 +496,6 @@ type ValidationError =
|
|
|
488
496
|
issue: ValidationIssue.OverlappingSpecificOverrides
|
|
489
497
|
overrideIndexes: [number, number]
|
|
490
498
|
}
|
|
491
|
-
| {
|
|
492
|
-
issue: ValidationIssue.OverlappingTimesInRule
|
|
493
|
-
location:
|
|
494
|
-
| { type: RuleLocationType.Weekly; ruleIndex: number }
|
|
495
|
-
| {
|
|
496
|
-
type: RuleLocationType.Override
|
|
497
|
-
overrideIndex: number
|
|
498
|
-
ruleIndex: number
|
|
499
|
-
}
|
|
500
|
-
timeRangeIndexes: [number, number]
|
|
501
|
-
}
|
|
502
499
|
| {
|
|
503
500
|
issue: ValidationIssue.OverlappingRulesInWeekly
|
|
504
501
|
ruleIndexes: [number, number]
|
|
@@ -510,16 +507,6 @@ type ValidationError =
|
|
|
510
507
|
ruleIndexes: [number, number]
|
|
511
508
|
weekday: Weekday
|
|
512
509
|
}
|
|
513
|
-
| {
|
|
514
|
-
issue: ValidationIssue.EmptyTimes
|
|
515
|
-
location:
|
|
516
|
-
| { type: RuleLocationType.Weekly; ruleIndex: number }
|
|
517
|
-
| {
|
|
518
|
-
type: RuleLocationType.Override
|
|
519
|
-
overrideIndex: number
|
|
520
|
-
ruleIndex: number
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
510
|
| {
|
|
524
511
|
issue: ValidationIssue.EmptyWeekdays
|
|
525
512
|
location:
|
|
@@ -590,7 +577,6 @@ import type {
|
|
|
590
577
|
Schedule,
|
|
591
578
|
WeeklyScheduleRule,
|
|
592
579
|
OverrideScheduleRule,
|
|
593
|
-
TimeRange,
|
|
594
580
|
AvailabilityRange,
|
|
595
581
|
ValidationError,
|
|
596
582
|
ValidationResult,
|
package/dist/constants.d.ts
CHANGED
|
@@ -8,13 +8,6 @@ export declare enum ValidationIssue {
|
|
|
8
8
|
DuplicateOverrides = "duplicate-overrides",
|
|
9
9
|
/** Two or more specific overrides have overlapping date ranges */
|
|
10
10
|
OverlappingSpecificOverrides = "overlapping-specific-overrides",
|
|
11
|
-
/** Time ranges within a single rule overlap with each other */
|
|
12
|
-
OverlappingTimesInRule = "overlapping-times-in-rule",
|
|
13
|
-
/**
|
|
14
|
-
* A rule has an empty times array (should have at least one time range or be
|
|
15
|
-
* removed)
|
|
16
|
-
*/
|
|
17
|
-
EmptyTimes = "empty-times",
|
|
18
11
|
/**
|
|
19
12
|
* A field contains an invalid scdate format (SDate, STime, SWeekdays, or
|
|
20
13
|
* STimestamp)
|
package/dist/constants.js
CHANGED
|
@@ -9,13 +9,6 @@ export var ValidationIssue;
|
|
|
9
9
|
ValidationIssue["DuplicateOverrides"] = "duplicate-overrides";
|
|
10
10
|
/** Two or more specific overrides have overlapping date ranges */
|
|
11
11
|
ValidationIssue["OverlappingSpecificOverrides"] = "overlapping-specific-overrides";
|
|
12
|
-
/** Time ranges within a single rule overlap with each other */
|
|
13
|
-
ValidationIssue["OverlappingTimesInRule"] = "overlapping-times-in-rule";
|
|
14
|
-
/**
|
|
15
|
-
* A rule has an empty times array (should have at least one time range or be
|
|
16
|
-
* removed)
|
|
17
|
-
*/
|
|
18
|
-
ValidationIssue["EmptyTimes"] = "empty-times";
|
|
19
12
|
/**
|
|
20
13
|
* A field contains an invalid scdate format (SDate, STime, SWeekdays, or
|
|
21
14
|
* STimestamp)
|
|
@@ -36,19 +36,16 @@ export const getAvailableRangesFromSchedule = (schedule, startDate, endDate) =>
|
|
|
36
36
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, weekday)) {
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
to: rangeEnd,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
39
|
+
// Handle cross-midnight ranges (from > to means it crosses midnight)
|
|
40
|
+
const isCrossMidnight = isAfterTime(rule.from, rule.to);
|
|
41
|
+
const rangeStart = getTimestampFromDateAndTime(currentDate, rule.from);
|
|
42
|
+
const rangeEnd = isCrossMidnight
|
|
43
|
+
? getTimestampFromDateAndTime(addDaysToDate(currentDate, 1), rule.to)
|
|
44
|
+
: getTimestampFromDateAndTime(currentDate, rule.to);
|
|
45
|
+
ranges.push({
|
|
46
|
+
from: rangeStart,
|
|
47
|
+
to: rangeEnd,
|
|
48
|
+
});
|
|
52
49
|
}
|
|
53
50
|
// Move to the next day
|
|
54
51
|
currentDate = addDaysToDate(currentDate, 1);
|
|
@@ -77,15 +77,13 @@ export const getNextAvailableFromSchedule = (schedule, fromTimestamp, maxDaysToS
|
|
|
77
77
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, weekday)) {
|
|
78
78
|
continue;
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
earliestTime = timeRange.from;
|
|
88
|
-
}
|
|
80
|
+
// Day 0: only consider ranges starting after fromTimestamp's time
|
|
81
|
+
// Day 1+: consider all ranges (any start time qualifies)
|
|
82
|
+
if (day === 0 && !isAfterTime(rule.from, fromTime)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (!earliestTime || isBeforeTime(rule.from, earliestTime)) {
|
|
86
|
+
earliestTime = rule.from;
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
89
|
if (earliestTime) {
|
|
@@ -14,20 +14,18 @@ const collectRangeEndCandidates = (rules, date, timeZone, afterTime) => {
|
|
|
14
14
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, weekday)) {
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
const rangeEnd = getTimestampFromDateAndTime(date, timeRange.to);
|
|
29
|
-
candidates.push(addMinutesToTimestamp(rangeEnd, 1, timeZone));
|
|
17
|
+
if (isAfterTime(rule.from, rule.to)) {
|
|
18
|
+
// Cross-midnight range ends tomorrow
|
|
19
|
+
const tomorrow = addDaysToDate(date, 1);
|
|
20
|
+
const rangeEnd = getTimestampFromDateAndTime(tomorrow, rule.to);
|
|
21
|
+
candidates.push(addMinutesToTimestamp(rangeEnd, 1, timeZone));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (afterTime && !isSameTimeOrAfter(rule.to, afterTime)) {
|
|
25
|
+
continue;
|
|
30
26
|
}
|
|
27
|
+
const rangeEnd = getTimestampFromDateAndTime(date, rule.to);
|
|
28
|
+
candidates.push(addMinutesToTimestamp(rangeEnd, 1, timeZone));
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
return candidates;
|
|
@@ -49,17 +47,15 @@ const collectSpilloverCandidates = (schedule, currentDate, timeZone, afterTime)
|
|
|
49
47
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, previousWeekday)) {
|
|
50
48
|
continue;
|
|
51
49
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
const rangeEnd = getTimestampFromDateAndTime(currentDate, timeRange.to);
|
|
61
|
-
candidates.push(addMinutesToTimestamp(rangeEnd, 1, timeZone));
|
|
50
|
+
// Only cross-midnight ranges (from > to) spill into today
|
|
51
|
+
if (!isAfterTime(rule.from, rule.to)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!isSameTimeOrAfter(rule.to, afterTime)) {
|
|
55
|
+
continue;
|
|
62
56
|
}
|
|
57
|
+
const rangeEnd = getTimestampFromDateAndTime(currentDate, rule.to);
|
|
58
|
+
candidates.push(addMinutesToTimestamp(rangeEnd, 1, timeZone));
|
|
63
59
|
}
|
|
64
60
|
return candidates;
|
|
65
61
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Weekday } from 'scdate';
|
|
2
|
-
import type {
|
|
2
|
+
import type { WeeklyScheduleRule } from '../types.js';
|
|
3
|
+
import type { TimeRange } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Gets the effective time ranges that apply to a specific weekday for a given
|
|
5
6
|
* rule, accounting for both direct ranges and cross-midnight spillover from
|
|
@@ -14,13 +14,10 @@ export const getEffectiveTimesForWeekday = (rule, weekday) => {
|
|
|
14
14
|
const previousWeekday = getPreviousWeekday(weekday);
|
|
15
15
|
// Check if this weekday is directly included in the rule's weekdays
|
|
16
16
|
if (doesWeekdaysIncludeWeekday(rule.weekdays, weekday)) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
effectiveTimes.push(splitRanges[0]);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
17
|
+
const splitRanges = splitCrossMidnightTimeRange(rule);
|
|
18
|
+
if (splitRanges.length > 0 && splitRanges[0]) {
|
|
19
|
+
effectiveTimes.push(splitRanges[0]);
|
|
20
|
+
}
|
|
24
21
|
}
|
|
25
22
|
// Check if previous weekday has cross-midnight ranges that spill into this
|
|
26
23
|
// weekday.
|
|
@@ -30,13 +27,11 @@ export const getEffectiveTimesForWeekday = (rule, weekday) => {
|
|
|
30
27
|
// only those that occur in the date range. This ensures spillover is only
|
|
31
28
|
// included from days that actually exist in the override period.
|
|
32
29
|
if (doesWeekdaysIncludeWeekday(rule.weekdays, previousWeekday)) {
|
|
33
|
-
rule
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
});
|
|
30
|
+
const splitRanges = splitCrossMidnightTimeRange(rule);
|
|
31
|
+
// If there are 2 ranges, the second one is the spillover to next day
|
|
32
|
+
if (splitRanges.length === 2 && splitRanges[1]) {
|
|
33
|
+
effectiveTimes.push(splitRanges[1]);
|
|
34
|
+
}
|
|
40
35
|
}
|
|
41
36
|
return effectiveTimes;
|
|
42
37
|
};
|
package/dist/internal/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type * from './types.js';
|
|
1
2
|
export * from './doOverridesOverlap.js';
|
|
2
3
|
export * from './doRulesOverlap.js';
|
|
3
4
|
export * from './doTimeRangesOverlap.js';
|
|
@@ -7,10 +8,8 @@ export * from './isTimeInTimeRange.js';
|
|
|
7
8
|
export * from './normalizeScheduleForValidation.js';
|
|
8
9
|
export * from './splitCrossMidnightTimeRange.js';
|
|
9
10
|
export * from './validateNoEmptyWeekdays.js';
|
|
10
|
-
export * from './validateNonEmptyTimes.js';
|
|
11
11
|
export * from './validateNoOverlappingOverrides.js';
|
|
12
12
|
export * from './validateNoOverlappingRules.js';
|
|
13
|
-
export * from './validateNoOverlappingTimesInRule.js';
|
|
14
13
|
export * from './validateNoSpilloverConflictsAtOverrideBoundaries.js';
|
|
15
14
|
export * from './validateOverrideDateOrder.js';
|
|
16
15
|
export * from './validateOverrideWeekdaysMatchDates.js';
|
package/dist/internal/index.js
CHANGED
|
@@ -9,10 +9,8 @@ export * from './normalizeScheduleForValidation.js';
|
|
|
9
9
|
export * from './splitCrossMidnightTimeRange.js';
|
|
10
10
|
// Internal validation helpers
|
|
11
11
|
export * from './validateNoEmptyWeekdays.js';
|
|
12
|
-
export * from './validateNonEmptyTimes.js';
|
|
13
12
|
export * from './validateNoOverlappingOverrides.js';
|
|
14
13
|
export * from './validateNoOverlappingRules.js';
|
|
15
|
-
export * from './validateNoOverlappingTimesInRule.js';
|
|
16
14
|
export * from './validateNoSpilloverConflictsAtOverrideBoundaries.js';
|
|
17
15
|
export * from './validateOverrideDateOrder.js';
|
|
18
16
|
export * from './validateOverrideWeekdaysMatchDates.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type STime } from 'scdate';
|
|
2
|
-
import type { STimeString
|
|
2
|
+
import type { STimeString } from '../types.js';
|
|
3
|
+
import type { TimeRange } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Checks if a time falls within a time range, with support for
|
|
5
6
|
* cross-midnight ranges (next-day portion).
|
|
@@ -5,8 +5,9 @@ import { isAfterTime, sTime } from 'scdate';
|
|
|
5
5
|
export const splitCrossMidnightTimeRange = (timeRange) => {
|
|
6
6
|
// Check if range crosses midnight
|
|
7
7
|
if (isAfterTime(timeRange.to, timeRange.from)) {
|
|
8
|
-
// Same day range
|
|
9
|
-
|
|
8
|
+
// Same day range — construct a new object to strip extra fields (e.g.,
|
|
9
|
+
// weekdays) when callers pass a WeeklyScheduleRule as a TimeRange.
|
|
10
|
+
return [{ from: timeRange.from, to: timeRange.to }];
|
|
10
11
|
}
|
|
11
12
|
// Cross-midnight range: split into two periods
|
|
12
13
|
return [
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { STime } from 'scdate';
|
|
2
|
+
import type { STimeString } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a time range within a day. Time ranges can cross midnight. For
|
|
5
|
+
* example, a range from 20:00 to 02:00 represents 8:00 PM to 2:00 AM the next
|
|
6
|
+
* day.
|
|
7
|
+
*/
|
|
8
|
+
export interface TimeRange {
|
|
9
|
+
/** Start time of the range (inclusive) */
|
|
10
|
+
from: STime | STimeString;
|
|
11
|
+
/** End time of the range (inclusive) */
|
|
12
|
+
to: STime | STimeString;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -38,52 +38,47 @@ export const validateNoSpilloverConflictsAtOverrideBoundaries = (schedule) => {
|
|
|
38
38
|
if (!doesWeekdaysIncludeWeekday(previousDayRule.weekdays, previousWeekday)) {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
// Check
|
|
42
|
-
previousDayRule
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
override
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
// Check for cross-midnight
|
|
42
|
+
const splitRanges = splitCrossMidnightTimeRange(previousDayRule);
|
|
43
|
+
// If cross-midnight, splitRanges[1] is the spillover portion
|
|
44
|
+
if (splitRanges.length === 2 && splitRanges[1]) {
|
|
45
|
+
const spilloverRange = splitRanges[1];
|
|
46
|
+
// Check if spillover conflicts with override first day's times
|
|
47
|
+
override.rules.forEach((overrideRule, overrideRuleIndex) => {
|
|
48
|
+
// Does first day match override rule's weekdays?
|
|
49
|
+
if (!doesWeekdaysIncludeWeekday(overrideRule.weekdays, firstDateWeekday)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const overrideSplitRanges = splitCrossMidnightTimeRange(overrideRule);
|
|
53
|
+
// Check same-day portion of override time range
|
|
54
|
+
const overrideSameDayRange = overrideSplitRanges[0];
|
|
55
|
+
if (overrideSameDayRange &&
|
|
56
|
+
doTimeRangesOverlap(spilloverRange, overrideSameDayRange)) {
|
|
57
|
+
// Use source information from getApplicableRuleForDate
|
|
58
|
+
if (previousDayResult.source === 'override') {
|
|
59
|
+
// Previous day is in an override
|
|
60
|
+
errors.push({
|
|
61
|
+
issue: ValidationIssue.SpilloverConflictIntoOverrideFirstDay,
|
|
62
|
+
overrideIndex,
|
|
63
|
+
date: firstDate.toJSON(),
|
|
64
|
+
overrideRuleIndex,
|
|
65
|
+
sourceOverrideIndex: previousDayResult.overrideIndex,
|
|
66
|
+
sourceOverrideRuleIndex: previousDayRuleIndex,
|
|
67
|
+
});
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
date: firstDate.toJSON(),
|
|
67
|
-
overrideRuleIndex,
|
|
68
|
-
sourceOverrideIndex: previousDayResult.overrideIndex,
|
|
69
|
-
sourceOverrideRuleIndex: previousDayRuleIndex,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// Previous day is weekly
|
|
74
|
-
errors.push({
|
|
75
|
-
issue: ValidationIssue.SpilloverConflictIntoOverrideFirstDay,
|
|
76
|
-
overrideIndex,
|
|
77
|
-
date: firstDate.toJSON(),
|
|
78
|
-
overrideRuleIndex,
|
|
79
|
-
sourceWeeklyRuleIndex: previousDayRuleIndex,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
69
|
+
else {
|
|
70
|
+
// Previous day is weekly
|
|
71
|
+
errors.push({
|
|
72
|
+
issue: ValidationIssue.SpilloverConflictIntoOverrideFirstDay,
|
|
73
|
+
overrideIndex,
|
|
74
|
+
date: firstDate.toJSON(),
|
|
75
|
+
overrideRuleIndex,
|
|
76
|
+
sourceWeeklyRuleIndex: previousDayRuleIndex,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
87
82
|
});
|
|
88
83
|
}
|
|
89
84
|
// PART 2: Check spillover FROM override last day into next day
|
|
@@ -101,65 +96,60 @@ export const validateNoSpilloverConflictsAtOverrideBoundaries = (schedule) => {
|
|
|
101
96
|
if (!doesWeekdaysIncludeWeekday(overrideRule.weekdays, lastDateWeekday)) {
|
|
102
97
|
return;
|
|
103
98
|
}
|
|
104
|
-
// Check
|
|
105
|
-
overrideRule
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
99
|
+
// Check for cross-midnight
|
|
100
|
+
const splitRanges = splitCrossMidnightTimeRange(overrideRule);
|
|
101
|
+
// If cross-midnight, splitRanges[1] is the spillover portion
|
|
102
|
+
if (splitRanges.length === 2 && splitRanges[1]) {
|
|
103
|
+
const spilloverRange = splitRanges[1];
|
|
104
|
+
// Get what rule applies to next day
|
|
105
|
+
const nextDayResult = getApplicableRuleForDate(schedule, nextDate);
|
|
106
|
+
// If next day is always available (weekly: true), spillover
|
|
107
|
+
// creates overlapping ranges with the full-day availability.
|
|
108
|
+
if (nextDayResult.rules === true) {
|
|
109
|
+
errors.push({
|
|
110
|
+
issue: ValidationIssue.SpilloverConflictOverrideIntoNext,
|
|
111
|
+
overrideIndex,
|
|
112
|
+
date: lastDate.toJSON(),
|
|
113
|
+
overrideRuleIndex,
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Check if spillover conflicts with next day's times
|
|
118
|
+
nextDayResult.rules.forEach((nextDayRule, nextDayRuleIndex) => {
|
|
119
|
+
// Does next day match the rule's weekdays?
|
|
120
|
+
if (!doesWeekdaysIncludeWeekday(nextDayRule.weekdays, nextDateWeekday)) {
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
const nextDaySplitRanges = splitCrossMidnightTimeRange(nextDayRule);
|
|
124
|
+
// Check same-day portion of next day's time range
|
|
125
|
+
const nextDaySameDayRange = nextDaySplitRanges[0];
|
|
126
|
+
if (nextDaySameDayRange &&
|
|
127
|
+
doTimeRangesOverlap(spilloverRange, nextDaySameDayRange)) {
|
|
128
|
+
// Use source information from getApplicableRuleForDate
|
|
129
|
+
if (nextDayResult.source === 'override') {
|
|
130
|
+
// Next day is in an override
|
|
131
|
+
errors.push({
|
|
132
|
+
issue: ValidationIssue.SpilloverConflictOverrideIntoNext,
|
|
133
|
+
overrideIndex,
|
|
134
|
+
date: lastDate.toJSON(),
|
|
135
|
+
overrideRuleIndex,
|
|
136
|
+
nextDayOverrideIndex: nextDayResult.overrideIndex,
|
|
137
|
+
nextDayOverrideRuleIndex: nextDayRuleIndex,
|
|
138
|
+
});
|
|
128
139
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
date: lastDate.toJSON(),
|
|
143
|
-
overrideRuleIndex,
|
|
144
|
-
nextDayOverrideIndex: nextDayResult.overrideIndex,
|
|
145
|
-
nextDayOverrideRuleIndex: nextDayRuleIndex,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
// Next day is weekly
|
|
150
|
-
errors.push({
|
|
151
|
-
issue: ValidationIssue.SpilloverConflictOverrideIntoNext,
|
|
152
|
-
overrideIndex,
|
|
153
|
-
date: lastDate.toJSON(),
|
|
154
|
-
overrideRuleIndex,
|
|
155
|
-
nextDayWeeklyRuleIndex: nextDayRuleIndex,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
});
|
|
140
|
+
else {
|
|
141
|
+
// Next day is weekly
|
|
142
|
+
errors.push({
|
|
143
|
+
issue: ValidationIssue.SpilloverConflictOverrideIntoNext,
|
|
144
|
+
overrideIndex,
|
|
145
|
+
date: lastDate.toJSON(),
|
|
146
|
+
overrideRuleIndex,
|
|
147
|
+
nextDayWeeklyRuleIndex: nextDayRuleIndex,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
163
153
|
});
|
|
164
154
|
});
|
|
165
155
|
return errors;
|
|
@@ -67,7 +67,7 @@ const validateTimeRange = (timeRange, fieldPrefix) => {
|
|
|
67
67
|
return errors;
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
|
-
* Validates a rule (weekdays and
|
|
70
|
+
* Validates a rule (weekdays and time range).
|
|
71
71
|
*/
|
|
72
72
|
const validateRule = (rule, fieldPrefix) => {
|
|
73
73
|
const errors = [];
|
|
@@ -75,9 +75,7 @@ const validateRule = (rule, fieldPrefix) => {
|
|
|
75
75
|
if (weekdaysError) {
|
|
76
76
|
errors.push(weekdaysError);
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
errors.push(...validateTimeRange(timeRange, `${fieldPrefix}.times[${String(timeIndex)}]`));
|
|
80
|
-
});
|
|
78
|
+
errors.push(...validateTimeRange(rule, fieldPrefix));
|
|
81
79
|
return errors;
|
|
82
80
|
};
|
|
83
81
|
/**
|
|
@@ -29,8 +29,8 @@ export const isScheduleAvailable = (schedule, timestamp) => {
|
|
|
29
29
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, weekday)) {
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
|
-
// Check if
|
|
33
|
-
return
|
|
32
|
+
// Check if time range matches
|
|
33
|
+
return isTimeInTimeRange(time, rule, true);
|
|
34
34
|
});
|
|
35
35
|
if (matchesSameDay) {
|
|
36
36
|
return true;
|
|
@@ -48,7 +48,7 @@ export const isScheduleAvailable = (schedule, timestamp) => {
|
|
|
48
48
|
if (!doesWeekdaysIncludeWeekday(rule.weekdays, previousWeekday)) {
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
|
-
// Check if
|
|
52
|
-
return
|
|
51
|
+
// Check if time range matches (next-day portion)
|
|
52
|
+
return isTimeInTimeRange(time, rule, false);
|
|
53
53
|
});
|
|
54
54
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -16,29 +16,18 @@ export type STimestampString = string;
|
|
|
16
16
|
* String in SMTWTFS format representing weekdays.
|
|
17
17
|
*/
|
|
18
18
|
export type SWeekdaysString = string;
|
|
19
|
-
/**
|
|
20
|
-
* Represents a time range within a day. Time ranges can cross midnight. For
|
|
21
|
-
* example, a range from 20:00 to 02:00 represents 8:00 PM to 2:00 AM the next
|
|
22
|
-
* day.
|
|
23
|
-
*/
|
|
24
|
-
export interface TimeRange {
|
|
25
|
-
/** Start time of the range (inclusive) */
|
|
26
|
-
from: STime | STimeString;
|
|
27
|
-
/** End time of the range (inclusive) */
|
|
28
|
-
to: STime | STimeString;
|
|
29
|
-
}
|
|
30
19
|
/**
|
|
31
20
|
* Defines a recurring weekly availability pattern. Specifies which days of the
|
|
32
|
-
* week are available and what time
|
|
21
|
+
* week are available and what time range applies on those days. For split
|
|
22
|
+
* shifts, use multiple rules with the same weekdays.
|
|
33
23
|
*/
|
|
34
24
|
export interface WeeklyScheduleRule {
|
|
35
25
|
/** Days of the week this rule applies to */
|
|
36
26
|
weekdays: SWeekdays | SWeekdaysString;
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
times: TimeRange[];
|
|
27
|
+
/** Start time of the range (inclusive). Ranges can cross midnight. */
|
|
28
|
+
from: STime | STimeString;
|
|
29
|
+
/** End time of the range (inclusive). Ranges can cross midnight. */
|
|
30
|
+
to: STime | STimeString;
|
|
42
31
|
}
|
|
43
32
|
/**
|
|
44
33
|
* Defines a date-specific override to the weekly schedule. Overrides apply to
|
|
@@ -103,35 +92,6 @@ export type ValidationError = {
|
|
|
103
92
|
issue: ValidationIssue.OverlappingSpecificOverrides;
|
|
104
93
|
/** Indexes of the two overlapping overrides */
|
|
105
94
|
overrideIndexes: [number, number];
|
|
106
|
-
} | {
|
|
107
|
-
/** Time ranges within a single rule overlap with each other */
|
|
108
|
-
issue: ValidationIssue.OverlappingTimesInRule;
|
|
109
|
-
/** Location of the rule containing overlapping times */
|
|
110
|
-
location: {
|
|
111
|
-
type: RuleLocationType.Weekly;
|
|
112
|
-
ruleIndex: number;
|
|
113
|
-
} | {
|
|
114
|
-
type: RuleLocationType.Override;
|
|
115
|
-
overrideIndex: number;
|
|
116
|
-
ruleIndex: number;
|
|
117
|
-
};
|
|
118
|
-
/** Indexes of the two overlapping time ranges within the rule */
|
|
119
|
-
timeRangeIndexes: [number, number];
|
|
120
|
-
} | {
|
|
121
|
-
/**
|
|
122
|
-
* A rule has an empty times array
|
|
123
|
-
* (should have at least one time range or be removed)
|
|
124
|
-
*/
|
|
125
|
-
issue: ValidationIssue.EmptyTimes;
|
|
126
|
-
/** Location of the rule with empty times */
|
|
127
|
-
location: {
|
|
128
|
-
type: RuleLocationType.Weekly;
|
|
129
|
-
ruleIndex: number;
|
|
130
|
-
} | {
|
|
131
|
-
type: RuleLocationType.Override;
|
|
132
|
-
overrideIndex: number;
|
|
133
|
-
ruleIndex: number;
|
|
134
|
-
};
|
|
135
95
|
} | {
|
|
136
96
|
/**
|
|
137
97
|
* A field contains an invalid scdate format
|
|
@@ -3,8 +3,8 @@ import type { Schedule, ValidationResult } from './types.js';
|
|
|
3
3
|
* Validates a schedule configuration and returns all validation errors found.
|
|
4
4
|
*
|
|
5
5
|
* Validation is performed in two phases:
|
|
6
|
-
* 1. Structural validation (formats, date order, empty weekdays,
|
|
7
|
-
*
|
|
6
|
+
* 1. Structural validation (formats, date order, empty weekdays,
|
|
7
|
+
* weekday-date mismatch) - runs on original schedule
|
|
8
8
|
* 2. Semantic validation (overlaps, conflicts) - runs on normalized schedule
|
|
9
9
|
* after filtering weekdays to actual dates
|
|
10
10
|
*
|
package/dist/validateSchedule.js
CHANGED
|
@@ -2,9 +2,7 @@ import { normalizeScheduleForValidation } from './internal/normalizeScheduleForV
|
|
|
2
2
|
import { validateNoEmptyWeekdays } from './internal/validateNoEmptyWeekdays.js';
|
|
3
3
|
import { validateNoOverlappingOverrides } from './internal/validateNoOverlappingOverrides.js';
|
|
4
4
|
import { validateNoOverlappingRules } from './internal/validateNoOverlappingRules.js';
|
|
5
|
-
import { validateNoOverlappingTimesInRule } from './internal/validateNoOverlappingTimesInRule.js';
|
|
6
5
|
import { validateNoSpilloverConflictsAtOverrideBoundaries } from './internal/validateNoSpilloverConflictsAtOverrideBoundaries.js';
|
|
7
|
-
import { validateNonEmptyTimes } from './internal/validateNonEmptyTimes.js';
|
|
8
6
|
import { validateOverrideDateOrder } from './internal/validateOverrideDateOrder.js';
|
|
9
7
|
import { validateOverrideWeekdaysMatchDates } from './internal/validateOverrideWeekdaysMatchDates.js';
|
|
10
8
|
import { validateScDateFormats } from './internal/validateScDateFormats.js';
|
|
@@ -12,8 +10,8 @@ import { validateScDateFormats } from './internal/validateScDateFormats.js';
|
|
|
12
10
|
* Validates a schedule configuration and returns all validation errors found.
|
|
13
11
|
*
|
|
14
12
|
* Validation is performed in two phases:
|
|
15
|
-
* 1. Structural validation (formats, date order, empty weekdays,
|
|
16
|
-
*
|
|
13
|
+
* 1. Structural validation (formats, date order, empty weekdays,
|
|
14
|
+
* weekday-date mismatch) - runs on original schedule
|
|
17
15
|
* 2. Semantic validation (overlaps, conflicts) - runs on normalized schedule
|
|
18
16
|
* after filtering weekdays to actual dates
|
|
19
17
|
*
|
|
@@ -31,7 +29,6 @@ export const validateSchedule = (schedule) => {
|
|
|
31
29
|
...validateScDateFormats(schedule),
|
|
32
30
|
...validateOverrideDateOrder(schedule),
|
|
33
31
|
...validateNoEmptyWeekdays(schedule),
|
|
34
|
-
...validateNonEmptyTimes(schedule),
|
|
35
32
|
...validateOverrideWeekdaysMatchDates(schedule),
|
|
36
33
|
];
|
|
37
34
|
// Return immediately if structural errors exist
|
|
@@ -46,7 +43,6 @@ export const validateSchedule = (schedule) => {
|
|
|
46
43
|
// Phase 3: Semantic validation on normalized schedule
|
|
47
44
|
const semanticErrors = [
|
|
48
45
|
...validateNoOverlappingOverrides(normalizedSchedule),
|
|
49
|
-
...validateNoOverlappingTimesInRule(normalizedSchedule),
|
|
50
46
|
...validateNoOverlappingRules(normalizedSchedule),
|
|
51
47
|
...validateNoSpilloverConflictsAtOverrideBoundaries(normalizedSchedule),
|
|
52
48
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scschedule",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "vitest run"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"scdate": "3.
|
|
23
|
+
"scdate": "3.2.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@eslint/js": "^9.39.2",
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { RuleLocationType, ValidationIssue } from '../constants.js';
|
|
2
|
-
import { doTimeRangesOverlap } from './doTimeRangesOverlap.js';
|
|
3
|
-
/**
|
|
4
|
-
* Validates that time ranges within a single rule do not overlap.
|
|
5
|
-
*/
|
|
6
|
-
const validateRuleTimes = (rule, location) => {
|
|
7
|
-
const errors = [];
|
|
8
|
-
if (rule.times.length < 2) {
|
|
9
|
-
return errors;
|
|
10
|
-
}
|
|
11
|
-
// Check all pairs of time ranges for overlap on the same weekday
|
|
12
|
-
for (let i = 0; i < rule.times.length; i++) {
|
|
13
|
-
for (let j = i + 1; j < rule.times.length; j++) {
|
|
14
|
-
const timeRange1 = rule.times[i];
|
|
15
|
-
const timeRange2 = rule.times[j];
|
|
16
|
-
if (timeRange1 &&
|
|
17
|
-
timeRange2 &&
|
|
18
|
-
doTimeRangesOverlap(timeRange1, timeRange2)) {
|
|
19
|
-
errors.push({
|
|
20
|
-
issue: ValidationIssue.OverlappingTimesInRule,
|
|
21
|
-
location,
|
|
22
|
-
timeRangeIndexes: [i, j],
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return errors;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Validates that time ranges within rules do not overlap with each other.
|
|
31
|
-
*/
|
|
32
|
-
export const validateNoOverlappingTimesInRule = (schedule) => {
|
|
33
|
-
const errors = [];
|
|
34
|
-
// Validate weekly rules
|
|
35
|
-
const weeklyRules = schedule.weekly === true ? [] : schedule.weekly;
|
|
36
|
-
weeklyRules.forEach((rule, ruleIndex) => {
|
|
37
|
-
const ruleErrors = validateRuleTimes(rule, {
|
|
38
|
-
type: RuleLocationType.Weekly,
|
|
39
|
-
ruleIndex,
|
|
40
|
-
});
|
|
41
|
-
errors.push(...ruleErrors);
|
|
42
|
-
});
|
|
43
|
-
// Validate override rules
|
|
44
|
-
schedule.overrides?.forEach((override, overrideIndex) => {
|
|
45
|
-
override.rules.forEach((rule, ruleIndex) => {
|
|
46
|
-
const ruleErrors = validateRuleTimes(rule, {
|
|
47
|
-
type: RuleLocationType.Override,
|
|
48
|
-
overrideIndex,
|
|
49
|
-
ruleIndex,
|
|
50
|
-
});
|
|
51
|
-
errors.push(...ruleErrors);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
return errors;
|
|
55
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { RuleLocationType, ValidationIssue } from '../constants.js';
|
|
2
|
-
/**
|
|
3
|
-
* Validates that all rules have at least one time range defined.
|
|
4
|
-
*/
|
|
5
|
-
export const validateNonEmptyTimes = (schedule) => {
|
|
6
|
-
const errors = [];
|
|
7
|
-
// Validate weekly rules
|
|
8
|
-
const weeklyRules = schedule.weekly === true ? [] : schedule.weekly;
|
|
9
|
-
weeklyRules.forEach((rule, ruleIndex) => {
|
|
10
|
-
if (rule.times.length === 0) {
|
|
11
|
-
errors.push({
|
|
12
|
-
issue: ValidationIssue.EmptyTimes,
|
|
13
|
-
location: {
|
|
14
|
-
type: RuleLocationType.Weekly,
|
|
15
|
-
ruleIndex,
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
// Validate override rules
|
|
21
|
-
schedule.overrides?.forEach((override, overrideIndex) => {
|
|
22
|
-
override.rules.forEach((rule, ruleIndex) => {
|
|
23
|
-
if (rule.times.length === 0) {
|
|
24
|
-
errors.push({
|
|
25
|
-
issue: ValidationIssue.EmptyTimes,
|
|
26
|
-
location: {
|
|
27
|
-
type: RuleLocationType.Override,
|
|
28
|
-
overrideIndex,
|
|
29
|
-
ruleIndex,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
return errors;
|
|
36
|
-
};
|