dabke 0.82.0 → 0.84.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/CHANGELOG.md +44 -0
- package/README.md +22 -24
- package/dist/client.d.ts +39 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/client.schemas.d.ts +118 -18
- package/dist/client.schemas.d.ts.map +1 -1
- package/dist/client.schemas.js +110 -6
- package/dist/client.schemas.js.map +1 -1
- package/dist/client.types.d.ts +63 -7
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/cost.d.ts +5 -2
- package/dist/cpsat/cost.d.ts.map +1 -1
- package/dist/cpsat/cost.js +13 -6
- package/dist/cpsat/cost.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +42 -61
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +451 -56
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +19 -7
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +48 -9
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rule-descriptor.d.ts +250 -0
- package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
- package/dist/cpsat/rule-descriptor.js +51 -0
- package/dist/cpsat/rule-descriptor.js.map +1 -0
- package/dist/cpsat/rules/artifacts.d.ts +9 -0
- package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
- package/dist/cpsat/rules/artifacts.js +31 -0
- package/dist/cpsat/rules/artifacts.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +18 -20
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
- package/dist/cpsat/rules/assign-together.js +90 -54
- package/dist/cpsat/rules/assign-together.js.map +1 -1
- package/dist/cpsat/rules/assigned-day.d.ts +12 -0
- package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
- package/dist/cpsat/rules/assigned-day.js +43 -0
- package/dist/cpsat/rules/assigned-day.js.map +1 -0
- package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
- package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
- package/dist/cpsat/rules/assignment-priority.js +47 -50
- package/dist/cpsat/rules/assignment-priority.js.map +1 -1
- package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
- package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
- package/dist/cpsat/rules/assignment-terms.js +19 -0
- package/dist/cpsat/rules/assignment-terms.js.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
- package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
- package/dist/cpsat/rules/concurrent-intervals.js +57 -0
- package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
- package/dist/cpsat/rules/cost-utils.d.ts +1 -3
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
- package/dist/cpsat/rules/cost-utils.js +0 -7
- package/dist/cpsat/rules/cost-utils.js.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +30 -21
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +30 -21
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/location-preference.d.ts +14 -21
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
- package/dist/cpsat/rules/location-preference.js +45 -45
- package/dist/cpsat/rules/location-preference.js.map +1 -1
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
- package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
- package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +67 -66
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
- package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/max-days-week.d.ts +30 -0
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-week.js +83 -0
- package/dist/cpsat/rules/max-days-week.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +56 -72
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +60 -75
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +57 -73
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +123 -94
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
- package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
- package/dist/cpsat/rules/min-days-week.d.ts +21 -0
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-week.js +95 -0
- package/dist/cpsat/rules/min-days-week.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +72 -54
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +75 -57
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +151 -163
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +12 -0
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
- package/dist/cpsat/rules/must-assign.js +54 -0
- package/dist/cpsat/rules/must-assign.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
- package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-eligibility.js +16 -0
- package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
- package/dist/cpsat/rules/pattern-time.d.ts +5 -0
- package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
- package/dist/cpsat/rules/pattern-time.js +20 -0
- package/dist/cpsat/rules/pattern-time.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +217 -27
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +98 -31
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.d.ts +9 -7
- package/dist/cpsat/rules/resolver.d.ts.map +1 -1
- package/dist/cpsat/rules/resolver.js +19 -18
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/role-preference.d.ts +24 -0
- package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/role-preference.js +72 -0
- package/dist/cpsat/rules/role-preference.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +93 -17
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +25 -12
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +14 -19
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/target-days-week.d.ts +31 -0
- package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/target-days-week.js +80 -0
- package/dist/cpsat/rules/target-days-week.js.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
- package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
- package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts +21 -57
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +114 -127
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/rules/variables.d.ts +4 -0
- package/dist/cpsat/rules/variables.d.ts.map +1 -0
- package/dist/cpsat/rules/variables.js +10 -0
- package/dist/cpsat/rules/variables.js.map +1 -0
- package/dist/cpsat/rules.d.ts +20 -3
- package/dist/cpsat/rules.d.ts.map +1 -1
- package/dist/cpsat/rules.js +19 -2
- package/dist/cpsat/rules.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +7 -6
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +15 -28
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +47 -4
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/types.js +24 -1
- package/dist/cpsat/types.js.map +1 -1
- package/dist/cpsat/utils.d.ts +23 -6
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +76 -15
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +6 -2
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +122 -10
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +21 -10
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/datetime.utils.d.ts +7 -13
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +29 -36
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +192 -0
- package/dist/schedule/cost.d.ts.map +1 -0
- package/dist/schedule/cost.js +151 -0
- package/dist/schedule/cost.js.map +1 -0
- package/dist/schedule/coverage.d.ts +114 -0
- package/dist/schedule/coverage.d.ts.map +1 -0
- package/dist/schedule/coverage.js +56 -0
- package/dist/schedule/coverage.js.map +1 -0
- package/dist/schedule/definition.d.ts +346 -0
- package/dist/schedule/definition.d.ts.map +1 -0
- package/dist/schedule/definition.js +668 -0
- package/dist/schedule/definition.js.map +1 -0
- package/dist/schedule/index.d.ts +67 -0
- package/dist/schedule/index.d.ts.map +1 -0
- package/dist/schedule/index.js +69 -0
- package/dist/schedule/index.js.map +1 -0
- package/dist/schedule/rules.d.ts +678 -0
- package/dist/schedule/rules.d.ts.map +1 -0
- package/dist/schedule/rules.js +554 -0
- package/dist/schedule/rules.js.map +1 -0
- package/dist/schedule/shift-patterns.d.ts +17 -0
- package/dist/schedule/shift-patterns.d.ts.map +1 -0
- package/dist/schedule/shift-patterns.js +24 -0
- package/dist/schedule/shift-patterns.js.map +1 -0
- package/dist/schedule/time-periods.d.ts +79 -0
- package/dist/schedule/time-periods.d.ts.map +1 -0
- package/dist/schedule/time-periods.js +101 -0
- package/dist/schedule/time-periods.js.map +1 -0
- package/dist/types.d.ts +67 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +64 -0
- package/dist/types.js.map +1 -1
- package/package.json +8 -14
- package/solver/src/solver/models.py +41 -4
- package/solver/src/solver/solver.py +546 -157
- package/solver/tests/test_solver.py +363 -7
- package/src/client.schemas.ts +127 -10
- package/src/client.ts +3 -0
- package/src/client.types.ts +73 -7
- package/src/cpsat/cost.ts +17 -7
- package/src/cpsat/model-builder.ts +614 -129
- package/src/cpsat/response.ts +57 -13
- package/src/cpsat/rule-descriptor.ts +311 -0
- package/src/cpsat/rules/artifacts.ts +56 -0
- package/src/cpsat/rules/assign-together.ts +104 -69
- package/src/cpsat/rules/assigned-day.ts +70 -0
- package/src/cpsat/rules/assignment-priority.ts +54 -58
- package/src/cpsat/rules/assignment-terms.ts +43 -0
- package/src/cpsat/rules/concurrent-intervals.ts +93 -0
- package/src/cpsat/rules/cost-utils.ts +1 -9
- package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
- package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
- package/src/cpsat/rules/index.ts +30 -21
- package/src/cpsat/rules/location-preference.ts +47 -52
- package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
- package/src/cpsat/rules/max-consecutive-days.ts +74 -91
- package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
- package/src/cpsat/rules/max-days-week.ts +117 -0
- package/src/cpsat/rules/max-hours-day.ts +64 -84
- package/src/cpsat/rules/max-hours-week.ts +74 -90
- package/src/cpsat/rules/max-shifts-day.ts +65 -85
- package/src/cpsat/rules/min-consecutive-days.ts +161 -127
- package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
- package/src/cpsat/rules/min-days-week.ts +122 -0
- package/src/cpsat/rules/min-hours-day.ts +79 -58
- package/src/cpsat/rules/min-hours-week.ts +87 -62
- package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
- package/src/cpsat/rules/minimize-cost.ts +175 -188
- package/src/cpsat/rules/must-assign.ts +71 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
- package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
- package/src/cpsat/rules/pattern-eligibility.ts +26 -0
- package/src/cpsat/rules/pattern-time.ts +30 -0
- package/src/cpsat/rules/registry.ts +140 -56
- package/src/cpsat/rules/resolver.ts +58 -30
- package/src/cpsat/rules/role-preference.ts +92 -0
- package/src/cpsat/rules/rules.types.ts +92 -17
- package/src/cpsat/rules/scope.types.ts +32 -27
- package/src/cpsat/rules/target-days-week.ts +105 -0
- package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
- package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
- package/src/cpsat/rules/time-off.ts +154 -163
- package/src/cpsat/rules/variables.ts +11 -0
- package/src/cpsat/rules.ts +43 -17
- package/src/cpsat/semantic-time.ts +29 -43
- package/src/cpsat/types.ts +53 -4
- package/src/cpsat/utils.ts +92 -20
- package/src/cpsat/validation-reporter.ts +170 -19
- package/src/cpsat/validation.types.ts +22 -11
- package/src/datetime.utils.ts +45 -44
- package/src/index.ts +72 -15
- package/src/schedule/cost.ts +221 -0
- package/src/schedule/coverage.ts +164 -0
- package/src/schedule/definition.ts +1447 -0
- package/src/schedule/index.ts +132 -0
- package/src/schedule/rules.ts +1028 -0
- package/src/schedule/shift-patterns.ts +29 -0
- package/src/schedule/time-periods.ts +120 -0
- package/src/types.ts +114 -2
- package/dist/llms.d.ts +0 -2
- package/dist/llms.d.ts.map +0 -1
- package/dist/llms.js +0 -3
- package/dist/llms.js.map +0 -1
- package/dist/schedule.d.ts +0 -917
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -1323
- package/dist/schedule.js.map +0 -1
- package/llms.txt +0 -758
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1960
package/src/datetime.utils.ts
CHANGED
|
@@ -1,50 +1,46 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
thursday: 4,
|
|
9
|
-
friday: 5,
|
|
10
|
-
saturday: 6,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const DAY_NAMES = [
|
|
14
|
-
"sunday",
|
|
15
|
-
"monday",
|
|
16
|
-
"tuesday",
|
|
17
|
-
"wednesday",
|
|
18
|
-
"thursday",
|
|
19
|
-
"friday",
|
|
20
|
-
"saturday",
|
|
21
|
-
] as const;
|
|
1
|
+
import {
|
|
2
|
+
DAY_NAMES,
|
|
3
|
+
type DateString,
|
|
4
|
+
type DayOfWeek,
|
|
5
|
+
type SchedulingDay,
|
|
6
|
+
type SchedulingPeriod,
|
|
7
|
+
} from "./types.js";
|
|
22
8
|
|
|
23
9
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
10
|
+
* Builds a {@link SchedulingDay} from an existing UTC `Date`, avoiding a
|
|
11
|
+
* redundant `new Date()` call. Used internally by {@link resolveDaysFromPeriod}
|
|
12
|
+
* where the loop already maintains a `Date` cursor.
|
|
26
13
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
function schedulingDayFromDate(date: Date, iso: DateString): SchedulingDay {
|
|
15
|
+
const dayOfWeek = DAY_NAMES[date.getUTCDay()]!;
|
|
16
|
+
const month = date.getUTCMonth() + 1;
|
|
17
|
+
const dayOfMonth = date.getUTCDate();
|
|
18
|
+
return {
|
|
19
|
+
iso,
|
|
20
|
+
dayOfWeek,
|
|
21
|
+
month,
|
|
22
|
+
dayOfMonth,
|
|
23
|
+
yearMonth: iso.slice(0, 7),
|
|
24
|
+
epochDay: Math.floor(date.getTime() / 86_400_000),
|
|
25
|
+
};
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
/**
|
|
33
|
-
* Computes the list of
|
|
29
|
+
* Computes the list of {@link SchedulingDay} values from a {@link SchedulingPeriod}.
|
|
34
30
|
*
|
|
35
31
|
* Generates all days between start and end (inclusive), applying optional
|
|
36
32
|
* dayOfWeek and dates filters. Filters compose: a day must pass all
|
|
37
33
|
* specified filters to be included.
|
|
38
34
|
*
|
|
39
35
|
* @param period - The scheduling period specification
|
|
40
|
-
* @returns Array of
|
|
36
|
+
* @returns Array of scheduling days, sorted chronologically
|
|
41
37
|
*
|
|
42
38
|
* @example All days in range
|
|
43
39
|
* ```typescript
|
|
44
40
|
* const days = resolveDaysFromPeriod({
|
|
45
41
|
* dateRange: { start: '2025-02-03', end: '2025-02-05' },
|
|
46
42
|
* });
|
|
47
|
-
* // Returns
|
|
43
|
+
* // Returns days for 2025-02-03, 2025-02-04, 2025-02-05
|
|
48
44
|
* ```
|
|
49
45
|
*
|
|
50
46
|
* @example Day-of-week filter
|
|
@@ -53,7 +49,7 @@ export function toDayOfWeekUTC(date: Date): DayOfWeek {
|
|
|
53
49
|
* dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
54
50
|
* dayOfWeek: ['wednesday', 'friday'],
|
|
55
51
|
* });
|
|
56
|
-
* // Returns
|
|
52
|
+
* // Returns days for 2025-02-05, 2025-02-07
|
|
57
53
|
* ```
|
|
58
54
|
*
|
|
59
55
|
* @example Specific dates filter
|
|
@@ -62,31 +58,36 @@ export function toDayOfWeekUTC(date: Date): DayOfWeek {
|
|
|
62
58
|
* dateRange: { start: '2025-02-03', end: '2025-02-10' },
|
|
63
59
|
* dates: ['2025-02-05', '2025-02-07'],
|
|
64
60
|
* });
|
|
65
|
-
* // Returns
|
|
61
|
+
* // Returns days for 2025-02-05, 2025-02-07
|
|
66
62
|
* ```
|
|
67
63
|
*/
|
|
68
|
-
export function resolveDaysFromPeriod(period: SchedulingPeriod):
|
|
64
|
+
export function resolveDaysFromPeriod(period: SchedulingPeriod): SchedulingDay[] {
|
|
69
65
|
const { dateRange, dayOfWeek, dates } = period;
|
|
70
|
-
const allowedDays = dayOfWeek ? new Set(dayOfWeek) : null;
|
|
71
|
-
const allowedDates = dates ? new Set(dates) : null;
|
|
66
|
+
const allowedDays: Set<DayOfWeek> | null = dayOfWeek ? new Set(dayOfWeek) : null;
|
|
67
|
+
const allowedDates: Set<string> | null = dates ? new Set(dates) : null;
|
|
72
68
|
|
|
73
69
|
const startDate = new Date(`${dateRange.start}T00:00:00Z`);
|
|
74
70
|
const endDate = new Date(`${dateRange.end}T00:00:00Z`);
|
|
75
|
-
const result:
|
|
71
|
+
const result: SchedulingDay[] = [];
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
for (
|
|
74
|
+
let currentTime = startDate.getTime(), endTime = endDate.getTime();
|
|
75
|
+
currentTime <= endTime;
|
|
76
|
+
currentTime += 24 * 60 * 60 * 1000
|
|
77
|
+
) {
|
|
78
|
+
const current = new Date(currentTime);
|
|
79
79
|
const year = current.getUTCFullYear();
|
|
80
80
|
const month = (current.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
81
|
-
const
|
|
82
|
-
const iso = `${year}-${month}-${
|
|
83
|
-
const
|
|
81
|
+
const d = current.getUTCDate().toString().padStart(2, "0");
|
|
82
|
+
const iso = `${year}-${month}-${d}` as DateString;
|
|
83
|
+
const day = schedulingDayFromDate(current, iso);
|
|
84
84
|
|
|
85
|
-
if (
|
|
86
|
-
|
|
85
|
+
if (
|
|
86
|
+
(!allowedDays || allowedDays.has(day.dayOfWeek)) &&
|
|
87
|
+
(!allowedDates || allowedDates.has(iso))
|
|
88
|
+
) {
|
|
89
|
+
result.push(day);
|
|
87
90
|
}
|
|
88
|
-
|
|
89
|
-
current.setUTCDate(current.getUTCDate() + 1);
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
return result;
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Scheduling library powered by constraint programming (CP-SAT).
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
3
5
|
* ## Core Concepts
|
|
4
6
|
*
|
|
5
7
|
* **Schedule**: The primary API. Small, composable functions
|
|
@@ -34,9 +36,9 @@
|
|
|
34
36
|
// Time primitives
|
|
35
37
|
// ============================================================================
|
|
36
38
|
|
|
37
|
-
export type { TimeOfDay, DayOfWeek, SchedulingPeriod } from "./types.js";
|
|
39
|
+
export type { DateString, TimeOfDay, DayOfWeek, SchedulingDay, SchedulingPeriod } from "./types.js";
|
|
38
40
|
|
|
39
|
-
export { DayOfWeekSchema } from "./types.js";
|
|
41
|
+
export { schedulingDay, DAY_OF_WEEK_INDEX, DayOfWeekSchema } from "./types.js";
|
|
40
42
|
|
|
41
43
|
// ============================================================================
|
|
42
44
|
// Solver client
|
|
@@ -49,13 +51,27 @@ export type {
|
|
|
49
51
|
SolverRequest,
|
|
50
52
|
SolverResponse,
|
|
51
53
|
SolverStatus,
|
|
52
|
-
|
|
54
|
+
SolverMode,
|
|
55
|
+
SolverDiagnosticMode,
|
|
56
|
+
SolverObjectiveStage,
|
|
57
|
+
SolverStageResult,
|
|
58
|
+
SolverSoftConstraintViolation,
|
|
59
|
+
SolverHardConstraintConflict,
|
|
53
60
|
FetcherLike,
|
|
54
61
|
} from "./client.types.js";
|
|
55
62
|
|
|
56
63
|
export { SOLVER_STATUS } from "./client.types.js";
|
|
57
64
|
|
|
58
|
-
export {
|
|
65
|
+
export {
|
|
66
|
+
SolverModeSchema,
|
|
67
|
+
SolverDiagnosticModeSchema,
|
|
68
|
+
SolverObjectiveStageSchema,
|
|
69
|
+
SolverRequestSchema,
|
|
70
|
+
SolverResponseSchema,
|
|
71
|
+
SolverStageResultSchema,
|
|
72
|
+
SolverStatusSchema,
|
|
73
|
+
SolverHardConstraintConflictSchema,
|
|
74
|
+
} from "./client.schemas.js";
|
|
59
75
|
|
|
60
76
|
// ============================================================================
|
|
61
77
|
// Model builder
|
|
@@ -63,14 +79,27 @@ export { SolverRequestSchema, SolverResponseSchema, SolverStatusSchema } from ".
|
|
|
63
79
|
|
|
64
80
|
export { ModelBuilder } from "./cpsat/model-builder.js";
|
|
65
81
|
|
|
82
|
+
export type { ModelBuilderConfig, CompilationResult } from "./cpsat/model-builder.js";
|
|
83
|
+
export type { ModelSolveStrategy } from "./cpsat/types.js";
|
|
84
|
+
|
|
85
|
+
export { compileRuleDescriptor, defineRuleDescriptor } from "./cpsat/rule-descriptor.js";
|
|
86
|
+
|
|
66
87
|
export type {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
88
|
+
CostValidationStrategy,
|
|
89
|
+
HardConstraintValidationStrategy,
|
|
90
|
+
ObjectiveValidationStrategy,
|
|
91
|
+
ReportHardConstraintValidationStrategy,
|
|
92
|
+
ReportSoftConstraintValidationStrategy,
|
|
93
|
+
RuleDescriptor,
|
|
94
|
+
RuleCompileContext,
|
|
95
|
+
RuleArtifact,
|
|
96
|
+
SkipValidationStrategy,
|
|
97
|
+
SoftConstraintValidationStrategy,
|
|
98
|
+
CompiledRule,
|
|
99
|
+
ValidationSkipCategory,
|
|
71
100
|
CostEntry,
|
|
72
101
|
CostContribution,
|
|
73
|
-
} from "./cpsat/
|
|
102
|
+
} from "./cpsat/rule-descriptor.js";
|
|
74
103
|
|
|
75
104
|
// ============================================================================
|
|
76
105
|
// Solver response parsing
|
|
@@ -92,10 +121,19 @@ export type { CostBreakdown, MemberCostDetail, CostCalculationConfig } from "./c
|
|
|
92
121
|
// Rules (registry types)
|
|
93
122
|
// ============================================================================
|
|
94
123
|
|
|
124
|
+
export {
|
|
125
|
+
assertValidCpsatRuleRegistry,
|
|
126
|
+
builtInCpsatRuleRegistry,
|
|
127
|
+
createCpsatRuleRegistry,
|
|
128
|
+
} from "./cpsat/rules/registry.js";
|
|
129
|
+
|
|
95
130
|
export type {
|
|
131
|
+
BuiltInCpsatRuleConfigRegistry,
|
|
132
|
+
BuiltInCpsatRuleRegistry,
|
|
133
|
+
CpsatRuleConfigByName,
|
|
96
134
|
CpsatRuleConfigEntry,
|
|
97
|
-
|
|
98
|
-
|
|
135
|
+
CpsatRuleConfigEntryFor,
|
|
136
|
+
CpsatRuleRegistry,
|
|
99
137
|
} from "./cpsat/rules/rules.types.js";
|
|
100
138
|
|
|
101
139
|
export type { RecurringPeriod } from "./cpsat/rules/scope.types.js";
|
|
@@ -148,6 +186,7 @@ export { summarizeValidation } from "./cpsat/validation-reporter.js";
|
|
|
148
186
|
|
|
149
187
|
export {
|
|
150
188
|
schedule,
|
|
189
|
+
scheduleWithRuleRegistry,
|
|
151
190
|
partialSchedule,
|
|
152
191
|
Schedule,
|
|
153
192
|
t,
|
|
@@ -158,15 +197,26 @@ export {
|
|
|
158
197
|
maxHoursPerWeek,
|
|
159
198
|
minHoursPerDay,
|
|
160
199
|
minHoursPerWeek,
|
|
200
|
+
maxDaysPerWeek,
|
|
201
|
+
minDaysPerWeek,
|
|
202
|
+
targetDaysPerWeek,
|
|
161
203
|
maxShiftsPerDay,
|
|
204
|
+
maxConcurrentAssignments,
|
|
205
|
+
targetPeakConcurrentAssignments,
|
|
162
206
|
maxConsecutiveDays,
|
|
163
207
|
minConsecutiveDays,
|
|
164
208
|
minRestBetweenShifts,
|
|
165
|
-
|
|
209
|
+
mustAssign,
|
|
210
|
+
preferAssignment,
|
|
211
|
+
avoidAssignment,
|
|
212
|
+
preferRole,
|
|
166
213
|
preferLocation,
|
|
167
214
|
timeOff,
|
|
168
215
|
assignTogether,
|
|
216
|
+
maxDaysOfWeekPerPeriod,
|
|
217
|
+
minDaysOfWeekPerPeriod,
|
|
169
218
|
defineRule,
|
|
219
|
+
defineRuleFor,
|
|
170
220
|
minimizeCost,
|
|
171
221
|
dayMultiplier,
|
|
172
222
|
daySurcharge,
|
|
@@ -178,7 +228,7 @@ export {
|
|
|
178
228
|
tieredOvertimeMultiplier,
|
|
179
229
|
weekdays,
|
|
180
230
|
weekend,
|
|
181
|
-
} from "./schedule.js";
|
|
231
|
+
} from "./schedule/index.js";
|
|
182
232
|
|
|
183
233
|
export type {
|
|
184
234
|
CoverageEntry,
|
|
@@ -187,12 +237,19 @@ export type {
|
|
|
187
237
|
RuleEntry,
|
|
188
238
|
RuleResolveContext,
|
|
189
239
|
RuleOptions,
|
|
240
|
+
ScheduleRuleEntry,
|
|
190
241
|
EntityOnlyRuleOptions,
|
|
242
|
+
TargetDaysPerWeekOptions,
|
|
191
243
|
TimeOffOptions,
|
|
192
244
|
AssignTogetherOptions,
|
|
245
|
+
MaxConcurrentAssignmentsOptions,
|
|
246
|
+
TargetPeakConcurrentAssignmentsOptions,
|
|
247
|
+
DaysOfWeekPerPeriodOptions,
|
|
193
248
|
CostRuleOptions,
|
|
194
249
|
ScheduleConfig,
|
|
250
|
+
ScheduleWithRuleRegistryConfig,
|
|
195
251
|
SolveResult,
|
|
196
252
|
SolveStatus,
|
|
253
|
+
SolveStrategy,
|
|
197
254
|
SolveOptions,
|
|
198
|
-
} from "./schedule.js";
|
|
255
|
+
} from "./schedule/index.js";
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost optimization rules: minimize labor cost with modifiers.
|
|
3
|
+
*
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* rules: [
|
|
8
|
+
* minimizeCost(),
|
|
9
|
+
* dayMultiplier(1.5, { dayOfWeek: weekend }),
|
|
10
|
+
* overtimeMultiplier({ after: 40, factor: 1.5 }),
|
|
11
|
+
* dailyOvertimeSurcharge({ after: 8, amount: 500 }),
|
|
12
|
+
* tieredOvertimeMultiplier([
|
|
13
|
+
* { after: 40, factor: 1.5 },
|
|
14
|
+
* { after: 48, factor: 2.0 },
|
|
15
|
+
* ]),
|
|
16
|
+
* ]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { DayOfWeek, TimeOfDay } from "../types.js";
|
|
23
|
+
import type { OvertimeTier } from "../cpsat/rules/overtime-tiered-multiplier.js";
|
|
24
|
+
import type { RecurringPeriod } from "../cpsat/rules/scope.types.js";
|
|
25
|
+
import type { RuleEntry } from "./rules.js";
|
|
26
|
+
import { defineRule } from "./rules.js";
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Cost Rule Options
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Options for cost rules.
|
|
34
|
+
*
|
|
35
|
+
* Cost rules are objective terms, not constraints. The `priority` field from
|
|
36
|
+
* {@link RuleOptions} does not apply.
|
|
37
|
+
*
|
|
38
|
+
* @category Cost Optimization
|
|
39
|
+
*/
|
|
40
|
+
export interface CostRuleOptions {
|
|
41
|
+
/** Who this rule applies to (role name, skill name, or member ID). */
|
|
42
|
+
appliesTo?: string | string[];
|
|
43
|
+
/** Restrict to specific days of the week. */
|
|
44
|
+
dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
|
|
45
|
+
/** Restrict to a date range. */
|
|
46
|
+
dateRange?: { start: string; end: string };
|
|
47
|
+
/** Restrict to specific dates (YYYY-MM-DD). */
|
|
48
|
+
dates?: string[];
|
|
49
|
+
/** Restrict to recurring calendar periods. */
|
|
50
|
+
recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Cost Rules
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
function makeCostRule<const Name extends string, const Fields extends object>(
|
|
58
|
+
rule: Name,
|
|
59
|
+
fields: Fields,
|
|
60
|
+
): RuleEntry<Name, Omit<Fields, "_type" | "_rule">> {
|
|
61
|
+
return defineRule(rule, fields);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Tells the solver to minimize total labor cost.
|
|
66
|
+
*
|
|
67
|
+
* @remarks
|
|
68
|
+
* Without this rule, cost modifiers only affect post-solve calculation.
|
|
69
|
+
* When present, the solver actively prefers cheaper assignments.
|
|
70
|
+
*
|
|
71
|
+
* For hourly members, penalizes each assignment proportionally to cost.
|
|
72
|
+
* For salaried members, adds a fixed weekly salary cost when they have
|
|
73
|
+
* any assignment that week (zero marginal cost up to contracted hours).
|
|
74
|
+
*
|
|
75
|
+
* Cost modifiers adjust the calculation:
|
|
76
|
+
* - `dayMultiplier(factor, opts?)` - multiply base rate on specific days
|
|
77
|
+
* - `daySurcharge(amount, opts?)` - flat extra per hour on specific days
|
|
78
|
+
* - `timeSurcharge(amount, window, opts?)` - flat extra per hour during a time window
|
|
79
|
+
* - `overtimeMultiplier({ after, factor }, opts?)` - weekly overtime multiplier
|
|
80
|
+
* - `overtimeSurcharge({ after, amount }, opts?)` - weekly overtime surcharge
|
|
81
|
+
* - `dailyOvertimeMultiplier({ after, factor }, opts?)` - daily overtime multiplier
|
|
82
|
+
* - `dailyOvertimeSurcharge({ after, amount }, opts?)` - daily overtime surcharge
|
|
83
|
+
* - `tieredOvertimeMultiplier(tiers, opts?)` - multiple overtime thresholds
|
|
84
|
+
*
|
|
85
|
+
* @category Cost Optimization
|
|
86
|
+
*/
|
|
87
|
+
export function minimizeCost(opts?: CostRuleOptions): RuleEntry<"minimize-cost", CostRuleOptions> {
|
|
88
|
+
return makeCostRule("minimize-cost", { ...opts });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Multiplies the base rate for assignments on specified days.
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* The base cost (1x) is already counted by {@link minimizeCost};
|
|
96
|
+
* this rule adds only the extra portion above 1x.
|
|
97
|
+
*
|
|
98
|
+
* @category Cost Optimization
|
|
99
|
+
*/
|
|
100
|
+
export function dayMultiplier(
|
|
101
|
+
factor: number,
|
|
102
|
+
opts?: CostRuleOptions,
|
|
103
|
+
): RuleEntry<"day-cost-multiplier", { factor: number } & CostRuleOptions> {
|
|
104
|
+
return makeCostRule("day-cost-multiplier", { factor, ...opts });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Adds a flat extra amount per hour for assignments on specified days.
|
|
109
|
+
*
|
|
110
|
+
* @remarks
|
|
111
|
+
* The surcharge is independent of the member's base rate.
|
|
112
|
+
*
|
|
113
|
+
* @category Cost Optimization
|
|
114
|
+
*/
|
|
115
|
+
export function daySurcharge(
|
|
116
|
+
amountPerHour: number,
|
|
117
|
+
opts?: CostRuleOptions,
|
|
118
|
+
): RuleEntry<"day-cost-surcharge", { amountPerHour: number } & CostRuleOptions> {
|
|
119
|
+
return makeCostRule("day-cost-surcharge", { amountPerHour, ...opts });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Adds a flat surcharge per hour for the portion of a shift that overlaps a time-of-day window.
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* The window supports overnight spans (e.g., 22:00-06:00). The surcharge
|
|
127
|
+
* is independent of the member's base rate.
|
|
128
|
+
*
|
|
129
|
+
* @param amountPerHour - Flat surcharge per hour in smallest currency unit
|
|
130
|
+
* @param window - Time-of-day window
|
|
131
|
+
* @param opts - Entity and time scoping
|
|
132
|
+
*
|
|
133
|
+
* @category Cost Optimization
|
|
134
|
+
*/
|
|
135
|
+
export function timeSurcharge(
|
|
136
|
+
amountPerHour: number,
|
|
137
|
+
window: { from: TimeOfDay; until: TimeOfDay },
|
|
138
|
+
opts?: CostRuleOptions,
|
|
139
|
+
): RuleEntry<
|
|
140
|
+
"time-cost-surcharge",
|
|
141
|
+
{ amountPerHour: number; window: { from: TimeOfDay; until: TimeOfDay } } & CostRuleOptions
|
|
142
|
+
> {
|
|
143
|
+
return makeCostRule("time-cost-surcharge", { amountPerHour, window, ...opts });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Applies a multiplier to hours beyond a weekly threshold.
|
|
148
|
+
*
|
|
149
|
+
* @remarks
|
|
150
|
+
* Only the extra portion above 1x is added (the base cost is already
|
|
151
|
+
* counted by {@link minimizeCost}).
|
|
152
|
+
*
|
|
153
|
+
* @category Cost Optimization
|
|
154
|
+
*/
|
|
155
|
+
export function overtimeMultiplier(
|
|
156
|
+
opts: { after: number; factor: number } & CostRuleOptions,
|
|
157
|
+
): RuleEntry<"overtime-weekly-multiplier", { after: number; factor: number } & CostRuleOptions> {
|
|
158
|
+
return makeCostRule("overtime-weekly-multiplier", { ...opts });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Adds a flat surcharge per hour beyond a weekly threshold.
|
|
163
|
+
*
|
|
164
|
+
* @remarks
|
|
165
|
+
* The surcharge is independent of the member's base rate.
|
|
166
|
+
*
|
|
167
|
+
* @category Cost Optimization
|
|
168
|
+
*/
|
|
169
|
+
export function overtimeSurcharge(
|
|
170
|
+
opts: { after: number; amount: number } & CostRuleOptions,
|
|
171
|
+
): RuleEntry<"overtime-weekly-surcharge", { after: number; amount: number } & CostRuleOptions> {
|
|
172
|
+
return makeCostRule("overtime-weekly-surcharge", { ...opts });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Applies a multiplier to hours beyond a daily threshold.
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* Only the extra portion above 1x is added (the base cost is already
|
|
180
|
+
* counted by {@link minimizeCost}).
|
|
181
|
+
*
|
|
182
|
+
* @category Cost Optimization
|
|
183
|
+
*/
|
|
184
|
+
export function dailyOvertimeMultiplier(
|
|
185
|
+
opts: { after: number; factor: number } & CostRuleOptions,
|
|
186
|
+
): RuleEntry<"overtime-daily-multiplier", { after: number; factor: number } & CostRuleOptions> {
|
|
187
|
+
return makeCostRule("overtime-daily-multiplier", { ...opts });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Adds a flat surcharge per hour beyond a daily threshold.
|
|
192
|
+
*
|
|
193
|
+
* @remarks
|
|
194
|
+
* The surcharge is independent of the member's base rate.
|
|
195
|
+
*
|
|
196
|
+
* @category Cost Optimization
|
|
197
|
+
*/
|
|
198
|
+
export function dailyOvertimeSurcharge(
|
|
199
|
+
opts: { after: number; amount: number } & CostRuleOptions,
|
|
200
|
+
): RuleEntry<"overtime-daily-surcharge", { after: number; amount: number } & CostRuleOptions> {
|
|
201
|
+
return makeCostRule("overtime-daily-surcharge", { ...opts });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Applies multiple overtime thresholds with increasing multipliers.
|
|
206
|
+
*
|
|
207
|
+
* @remarks
|
|
208
|
+
* Each tier applies only to the hours between its threshold and the next.
|
|
209
|
+
* Tiers must be sorted by threshold ascending.
|
|
210
|
+
*
|
|
211
|
+
* @category Cost Optimization
|
|
212
|
+
*/
|
|
213
|
+
export function tieredOvertimeMultiplier(
|
|
214
|
+
tiers: [OvertimeTier, ...OvertimeTier[]],
|
|
215
|
+
opts?: CostRuleOptions,
|
|
216
|
+
): RuleEntry<
|
|
217
|
+
"overtime-tiered-multiplier",
|
|
218
|
+
{ tiers: [OvertimeTier, ...OvertimeTier[]] } & CostRuleOptions
|
|
219
|
+
> {
|
|
220
|
+
return makeCostRule("overtime-tiered-multiplier", { tiers, ...opts });
|
|
221
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage definitions: lower-bound staffing requirements per semantic time period.
|
|
3
|
+
*
|
|
4
|
+
* Coverage answers "what floor should apply throughout this scoped time, with
|
|
5
|
+
* the chosen priority?". It attaches sustained lower bounds to semantic times,
|
|
6
|
+
* whether those periods are
|
|
7
|
+
* explicit in the requirements or inferred to separate distinct business
|
|
8
|
+
* requirements. Coverage does not express caps, targets, fairness, or other
|
|
9
|
+
* assignment-shaping logic. Use rules for those concerns.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* coverage: [
|
|
14
|
+
* cover("lunch", "waiter", 2, { dayOfWeek: weekdays }),
|
|
15
|
+
* cover("lunch", "waiter", 3, { dayOfWeek: weekend }),
|
|
16
|
+
* cover("dinner", ["manager", "supervisor"], 1),
|
|
17
|
+
* cover("opening", "keyholder", 1),
|
|
18
|
+
*
|
|
19
|
+
* // Variant form: different counts by day
|
|
20
|
+
* cover("peak_hours", "agent",
|
|
21
|
+
* { count: 4 },
|
|
22
|
+
* { count: 2, dates: ["2025-12-24"] },
|
|
23
|
+
* ),
|
|
24
|
+
* ]
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type { DateString, DayOfWeek } from "../types.js";
|
|
31
|
+
import type { CoverageVariant } from "../cpsat/semantic-time.js";
|
|
32
|
+
import type { Priority } from "../cpsat/types.js";
|
|
33
|
+
|
|
34
|
+
export type { CoverageVariant } from "../cpsat/semantic-time.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for a {@link cover} call.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Day/date scoping controls which days this coverage entry applies to.
|
|
41
|
+
* An entry without `dayOfWeek` or `dates` applies every day in the
|
|
42
|
+
* scheduling period. Scope answers where the floor applies; it does not change
|
|
43
|
+
* the meaning of coverage itself. `priority` controls how hard the coverage floor is.
|
|
44
|
+
* Use it to distinguish language like "must keep 5 staffed on Saturdays"
|
|
45
|
+
* from "ideally keep 5 staffed on Mondays too" without changing the
|
|
46
|
+
* underlying coverage shape. `skillIds` is a hard filter, not a preference.
|
|
47
|
+
* Use it only when the required minimum truly needs those skills throughout
|
|
48
|
+
* the window. If a role or skill mix is preferred rather than required, model
|
|
49
|
+
* that with rules such as {@link preferAssignment} instead of skill-filtered
|
|
50
|
+
* coverage.
|
|
51
|
+
*
|
|
52
|
+
* @category Coverage
|
|
53
|
+
*/
|
|
54
|
+
export interface CoverageOptions {
|
|
55
|
+
/** Additional skill ID filter (AND logic with the target role). */
|
|
56
|
+
skillIds?: [string, ...string[]];
|
|
57
|
+
/** Restrict to specific days of the week. */
|
|
58
|
+
dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
|
|
59
|
+
/** Restrict to specific dates (YYYY-MM-DD). */
|
|
60
|
+
dates?: DateString[];
|
|
61
|
+
/** Defaults to `"MANDATORY"`. */
|
|
62
|
+
priority?: Priority;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A coverage entry returned by {@link cover}.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* Carries the semantic time name and target type information for
|
|
70
|
+
* compile-time validation by {@link schedule}. This is an opaque
|
|
71
|
+
* token; pass it directly into the `coverage` array.
|
|
72
|
+
*/
|
|
73
|
+
export interface CoverageEntry<T extends string = string, R extends string = string> {
|
|
74
|
+
/** @internal */ readonly _type: "coverage";
|
|
75
|
+
/** @internal */ readonly timeName: T;
|
|
76
|
+
/** @internal */ readonly target: R | R[];
|
|
77
|
+
/** @internal */ readonly count: number;
|
|
78
|
+
/** @internal */ readonly options: CoverageOptions;
|
|
79
|
+
/** @internal When present, this entry uses variant-based resolution. */
|
|
80
|
+
readonly variants?: readonly CoverageVariant[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Defines a staffing requirement for a semantic time period.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* Coverage always defines a lower bound that applies throughout the scoped
|
|
88
|
+
* semantic time, with strength controlled by `priority`. If the requirements
|
|
89
|
+
* also state an upper bound or productive
|
|
90
|
+
* cap, model that separately in `rules`, for example with
|
|
91
|
+
* {@link maxConcurrentAssignments}.
|
|
92
|
+
*
|
|
93
|
+
* Scope answers where the floor applies. Priority answers how strictly the
|
|
94
|
+
* solver should preserve that floor when trade-offs are necessary.
|
|
95
|
+
*
|
|
96
|
+
* Attach coverage to the narrowest semantic time that actually carries that
|
|
97
|
+
* minimum. Avoid layering a broad parent coverage window on top of narrower
|
|
98
|
+
* windows unless the same lower bound truly applies throughout the full span.
|
|
99
|
+
* If the requirements talk about hitting full occupancy at the busy point,
|
|
100
|
+
* prefer {@link targetPeakConcurrentAssignments} over turning that into a
|
|
101
|
+
* whole-window minimum. Do not infer a synthetic "peak" semantic time just to attach
|
|
102
|
+
* `cover(..., 5)` unless the requirements really define a sustained window
|
|
103
|
+
* that needs that minimum throughout.
|
|
104
|
+
*
|
|
105
|
+
* Overlapping entries for the same time and role produce independent
|
|
106
|
+
* constraints; the solver enforces the **max** count, not the sum.
|
|
107
|
+
* An unscoped entry acts as a floor that scoped entries cannot reduce.
|
|
108
|
+
* Use mutually exclusive scopes when different days need different coverage.
|
|
109
|
+
*
|
|
110
|
+
* @param timeName - Name of a declared semantic time
|
|
111
|
+
* @param target - Role name (string), array of role names (OR logic), or skill name
|
|
112
|
+
* @param count - Number of people needed
|
|
113
|
+
* @param opts - Options: `skillIds` (AND filter), `dayOfWeek`, `dates`, `priority`
|
|
114
|
+
*
|
|
115
|
+
* @category Coverage
|
|
116
|
+
*/
|
|
117
|
+
export function cover<T extends string, const R extends string>(
|
|
118
|
+
timeName: T,
|
|
119
|
+
target: R | [R, ...R[]],
|
|
120
|
+
count: number,
|
|
121
|
+
opts?: CoverageOptions,
|
|
122
|
+
): CoverageEntry<T, R>;
|
|
123
|
+
export function cover<T extends string, const R extends string>(
|
|
124
|
+
timeName: T,
|
|
125
|
+
target: R | [R, ...R[]],
|
|
126
|
+
...variants: [CoverageVariant, ...CoverageVariant[]]
|
|
127
|
+
): CoverageEntry<T, R>;
|
|
128
|
+
export function cover<T extends string, R extends string>(
|
|
129
|
+
timeName: T,
|
|
130
|
+
target: R | [R, ...R[]],
|
|
131
|
+
countOrFirstVariant: number | CoverageVariant,
|
|
132
|
+
...rest: unknown[]
|
|
133
|
+
): CoverageEntry<T, R> {
|
|
134
|
+
if (typeof countOrFirstVariant === "number") {
|
|
135
|
+
// Simple form: cover(time, target, count, opts?)
|
|
136
|
+
return {
|
|
137
|
+
_type: "coverage",
|
|
138
|
+
timeName,
|
|
139
|
+
target,
|
|
140
|
+
count: countOrFirstVariant,
|
|
141
|
+
options: (rest[0] as CoverageOptions | undefined) ?? {},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Variant form: cover(time, target, ...variants)
|
|
146
|
+
const variants = [countOrFirstVariant, ...(rest as CoverageVariant[])];
|
|
147
|
+
|
|
148
|
+
const defaults = variants.filter((v) => !v.dayOfWeek && !v.dates);
|
|
149
|
+
if (defaults.length > 1) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"cover() accepts at most one default variant (without dayOfWeek or dates). " +
|
|
152
|
+
`Found ${defaults.length} default variants.`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
_type: "coverage",
|
|
158
|
+
timeName,
|
|
159
|
+
target,
|
|
160
|
+
count: 0,
|
|
161
|
+
options: {},
|
|
162
|
+
variants,
|
|
163
|
+
};
|
|
164
|
+
}
|