dabke 0.81.1 → 0.83.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 +58 -0
- package/README.md +45 -27
- package/dist/client.d.ts +20 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -1
- package/dist/client.js.map +1 -1
- package/dist/client.types.d.ts +9 -0
- package/dist/client.types.d.ts.map +1 -1
- package/dist/client.types.js +1 -0
- package/dist/client.types.js.map +1 -1
- package/dist/cpsat/model-builder.d.ts +9 -0
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +36 -34
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +13 -1
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +4 -0
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rules/cost-utils.d.ts +11 -0
- package/dist/cpsat/rules/cost-utils.d.ts.map +1 -0
- package/dist/cpsat/rules/cost-utils.js +24 -0
- package/dist/cpsat/rules/cost-utils.js.map +1 -0
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-multiplier.js +3 -14
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/day-cost-surcharge.js +3 -7
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/index.d.ts +3 -0
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +3 -0
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +16 -2
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-days-week.d.ts +44 -0
- package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-days-week.js +95 -0
- package/dist/cpsat/rules/max-days-week.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +15 -2
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +16 -2
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +15 -2
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +15 -2
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-days-week.d.ts +34 -0
- package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-days-week.js +84 -0
- package/dist/cpsat/rules/min-days-week.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +15 -2
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +16 -2
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +72 -2
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
- package/dist/cpsat/rules/minimize-cost.js +2 -23
- package/dist/cpsat/rules/minimize-cost.js.map +1 -1
- package/dist/cpsat/rules/must-assign.d.ts +49 -0
- package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
- package/dist/cpsat/rules/must-assign.js +86 -0
- package/dist/cpsat/rules/must-assign.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-daily-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +5 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +1 -12
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +1 -5
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
- package/dist/cpsat/rules/registry.d.ts +28 -2
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +4 -1
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.js +2 -2
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/rules.types.d.ts +3 -0
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +18 -1
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +59 -16
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.js +2 -1
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +6 -3
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +44 -42
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +64 -46
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +37 -27
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +7 -12
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.d.ts +10 -7
- package/dist/cpsat/validation-reporter.d.ts.map +1 -1
- package/dist/cpsat/validation-reporter.js +44 -72
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +54 -44
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/cpsat/validation.types.js +15 -10
- package/dist/cpsat/validation.types.js.map +1 -1
- package/dist/datetime.utils.d.ts +3 -203
- package/dist/datetime.utils.d.ts.map +1 -1
- package/dist/datetime.utils.js +1 -288
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +14 -83
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -83
- package/dist/index.js.map +1 -1
- package/dist/schedule/cost.d.ts +204 -0
- package/dist/schedule/cost.d.ts.map +1 -0
- package/dist/schedule/cost.js +187 -0
- package/dist/schedule/cost.js.map +1 -0
- package/dist/schedule/coverage.d.ts +85 -0
- package/dist/schedule/coverage.d.ts.map +1 -0
- package/dist/schedule/coverage.js +33 -0
- package/dist/schedule/coverage.js.map +1 -0
- package/dist/schedule/definition.d.ts +227 -0
- package/dist/schedule/definition.d.ts.map +1 -0
- package/dist/schedule/definition.js +659 -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 +353 -0
- package/dist/schedule/rules.d.ts.map +1 -0
- package/dist/schedule/rules.js +352 -0
- package/dist/schedule/rules.js.map +1 -0
- package/dist/schedule/shift-patterns.d.ts +34 -0
- package/dist/schedule/shift-patterns.d.ts.map +1 -0
- package/dist/schedule/shift-patterns.js +41 -0
- package/dist/schedule/shift-patterns.js.map +1 -0
- package/dist/schedule/time-periods.d.ts +69 -0
- package/dist/schedule/time-periods.d.ts.map +1 -0
- package/dist/schedule/time-periods.js +91 -0
- package/dist/schedule/time-periods.js.map +1 -0
- package/dist/types.d.ts +14 -78
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -9
- package/solver/src/solver/app.py +1 -1
- package/solver/src/solver/solver.py +7 -4
- package/src/client.ts +6 -8
- package/src/client.types.ts +9 -0
- package/src/cpsat/model-builder.ts +44 -35
- package/src/cpsat/response.ts +13 -1
- package/src/cpsat/rules/cost-utils.ts +25 -0
- package/src/cpsat/rules/day-cost-multiplier.ts +3 -14
- package/src/cpsat/rules/day-cost-surcharge.ts +3 -8
- package/src/cpsat/rules/index.ts +3 -0
- package/src/cpsat/rules/max-consecutive-days.ts +17 -0
- package/src/cpsat/rules/max-days-week.ts +143 -0
- package/src/cpsat/rules/max-hours-day.ts +21 -1
- package/src/cpsat/rules/max-hours-week.ts +22 -1
- package/src/cpsat/rules/max-shifts-day.ts +21 -1
- package/src/cpsat/rules/min-consecutive-days.ts +16 -1
- package/src/cpsat/rules/min-days-week.ts +120 -0
- package/src/cpsat/rules/min-hours-day.ts +16 -1
- package/src/cpsat/rules/min-hours-week.ts +17 -1
- package/src/cpsat/rules/min-rest-between-shifts.ts +92 -2
- package/src/cpsat/rules/minimize-cost.ts +2 -29
- package/src/cpsat/rules/must-assign.ts +108 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-daily-surcharge.ts +1 -6
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +6 -13
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +1 -12
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +1 -6
- package/src/cpsat/rules/registry.ts +8 -2
- package/src/cpsat/rules/resolver.ts +2 -2
- package/src/cpsat/rules/rules.types.ts +3 -0
- package/src/cpsat/rules/scope.types.ts +73 -20
- package/src/cpsat/rules/time-cost-surcharge.ts +2 -1
- package/src/cpsat/rules/time-off.ts +6 -2
- package/src/cpsat/semantic-time.ts +115 -91
- package/src/cpsat/types.ts +37 -27
- package/src/cpsat/utils.ts +8 -12
- package/src/cpsat/validation-reporter.ts +51 -82
- package/src/cpsat/validation.types.ts +72 -47
- package/src/datetime.utils.ts +3 -334
- package/src/index.ts +35 -107
- package/src/schedule/cost.ts +242 -0
- package/src/schedule/coverage.ts +135 -0
- package/src/schedule/definition.ts +958 -0
- package/src/schedule/index.ts +112 -0
- package/src/schedule/rules.ts +529 -0
- package/src/schedule/shift-patterns.ts +46 -0
- package/src/schedule/time-periods.ts +110 -0
- package/src/types.ts +14 -88
- package/dist/errors.d.ts +0 -12
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -17
- package/dist/errors.js.map +0 -1
- 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 -724
- package/dist/schedule.d.ts.map +0 -1
- package/dist/schedule.js +0 -899
- package/dist/schedule.js.map +0 -1
- package/dist/validation.d.ts +0 -105
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -130
- package/dist/validation.js.map +0 -1
- package/llms.txt +0 -925
- package/src/errors.ts +0 -17
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1419
- package/src/validation.ts +0 -188
package/llms.txt
DELETED
|
@@ -1,925 +0,0 @@
|
|
|
1
|
-
# dabke
|
|
2
|
-
|
|
3
|
-
> Scheduling library powered by constraint programming (CP-SAT)
|
|
4
|
-
|
|
5
|
-
Define teams, shifts, coverage, and rules declaratively. dabke compiles
|
|
6
|
-
them into a constraint model and solves for an optimized schedule.
|
|
7
|
-
|
|
8
|
-
## Core Concepts
|
|
9
|
-
|
|
10
|
-
**Schedule Definition**: The primary API. Small, composable functions
|
|
11
|
-
(`time`, `cover`, `shift`, rule functions) produce a
|
|
12
|
-
complete scheduling configuration via `defineSchedule`. Each concept
|
|
13
|
-
is a single function call with full type safety.
|
|
14
|
-
|
|
15
|
-
**Times vs Shift Patterns**: These are two distinct concepts.
|
|
16
|
-
`times` are named time windows used to define and reference recurring
|
|
17
|
-
periods: service hours, delivery windows, peak periods, weekly events
|
|
18
|
-
like a fire drill. Times may overlap (e.g., "dinner" 18:00-22:00 and
|
|
19
|
-
"happy_hour" 17:30-18:30). Coverage and rules reference these names.
|
|
20
|
-
`shiftPatterns` define WHEN people CAN work (available time slots).
|
|
21
|
-
The solver assigns people to shift patterns whose hours overlap with
|
|
22
|
-
times to satisfy coverage. Not every shift pattern needs a
|
|
23
|
-
corresponding time; create times only for periods you need to
|
|
24
|
-
reference.
|
|
25
|
-
|
|
26
|
-
**Rules**: Business requirements expressed as scheduling constraints.
|
|
27
|
-
- Built-in rules: hours limits, time-off, rest periods, preferences, cost optimization
|
|
28
|
-
- Scoping: apply rules globally, per person, per role, per skill, or per time period
|
|
29
|
-
- Priority: `MANDATORY` (hard constraint) vs `LOW`/`MEDIUM`/`HIGH` (soft preferences)
|
|
30
|
-
|
|
31
|
-
**Solving**: `ScheduleDefinition.createSchedulerConfig` merges the
|
|
32
|
-
static definition with runtime data (members, scheduling period).
|
|
33
|
-
`ModelBuilder` compiles the config into a solver request;
|
|
34
|
-
`HttpSolverClient` sends it to the CP-SAT solver.
|
|
35
|
-
|
|
36
|
-
**Example:**
|
|
37
|
-
Define a schedule
|
|
38
|
-
```typescript
|
|
39
|
-
import {
|
|
40
|
-
defineSchedule, t, time, cover, shift,
|
|
41
|
-
maxHoursPerWeek, minRestBetweenShifts, timeOff,
|
|
42
|
-
weekdays, weekend,
|
|
43
|
-
} from "dabke";
|
|
44
|
-
|
|
45
|
-
const schedule = defineSchedule({
|
|
46
|
-
roles: ["nurse", "doctor"],
|
|
47
|
-
skills: ["charge_nurse"],
|
|
48
|
-
|
|
49
|
-
times: {
|
|
50
|
-
morning_round: time({ startTime: t(7), endTime: t(9) }),
|
|
51
|
-
day_ward: time({ startTime: t(7), endTime: t(15) }),
|
|
52
|
-
night_ward: time({ startTime: t(23), endTime: t(7) }),
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
coverage: [
|
|
56
|
-
cover("morning_round", "doctor", 1),
|
|
57
|
-
cover("day_ward", "nurse", 3, { dayOfWeek: weekdays }),
|
|
58
|
-
cover("day_ward", "nurse", 2, { dayOfWeek: weekend }),
|
|
59
|
-
cover("night_ward", "nurse", 2),
|
|
60
|
-
cover("night_ward", "charge_nurse", 1),
|
|
61
|
-
],
|
|
62
|
-
|
|
63
|
-
shiftPatterns: [
|
|
64
|
-
shift("day", t(7), t(15)),
|
|
65
|
-
shift("night", t(23), t(7)),
|
|
66
|
-
],
|
|
67
|
-
|
|
68
|
-
rules: [
|
|
69
|
-
maxHoursPerWeek(40),
|
|
70
|
-
minRestBetweenShifts(11),
|
|
71
|
-
timeOff({ appliesTo: "alice", dayOfWeek: weekend }),
|
|
72
|
-
],
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Example:**
|
|
77
|
-
Solve a schedule
|
|
78
|
-
```typescript
|
|
79
|
-
import { ModelBuilder, HttpSolverClient, parseSolverResponse } from "dabke";
|
|
80
|
-
|
|
81
|
-
const config = schedule.createSchedulerConfig({
|
|
82
|
-
schedulingPeriod: {
|
|
83
|
-
dateRange: { start: "2026-02-09", end: "2026-02-15" },
|
|
84
|
-
},
|
|
85
|
-
members: [
|
|
86
|
-
{ id: "alice", roles: ["nurse"], skills: ["charge_nurse"] },
|
|
87
|
-
{ id: "bob", roles: ["nurse"] },
|
|
88
|
-
{ id: "carol", roles: ["doctor"] },
|
|
89
|
-
],
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const builder = new ModelBuilder(config);
|
|
93
|
-
const { request, canSolve, validation } = builder.compile();
|
|
94
|
-
if (canSolve) {
|
|
95
|
-
const client = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
96
|
-
const response = await client.solve(request);
|
|
97
|
-
const result = parseSolverResponse(response);
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## Schedule Definition
|
|
104
|
-
|
|
105
|
-
### `defineSchedule`
|
|
106
|
-
|
|
107
|
-
Defines a complete schedule configuration.
|
|
108
|
-
|
|
109
|
-
Validates the static config at call time (role/skill disjointness, coverage
|
|
110
|
-
targets, shift pattern roles). Returns a `ScheduleDefinition` whose
|
|
111
|
-
`createSchedulerConfig` method validates runtime data (member IDs,
|
|
112
|
-
`appliesTo` resolution) and produces a `ModelBuilderConfig`.
|
|
113
|
-
|
|
114
|
-
**Example:**
|
|
115
|
-
```typescript
|
|
116
|
-
import { defineSchedule, t, time, cover, shift, maxHoursPerDay } from "dabke";
|
|
117
|
-
|
|
118
|
-
export default defineSchedule({
|
|
119
|
-
roles: ["agent", "supervisor"],
|
|
120
|
-
times: { peak: time({ startTime: t(9), endTime: t(17) }) },
|
|
121
|
-
coverage: [cover("peak", "agent", 4)],
|
|
122
|
-
shiftPatterns: [shift("day", t(9), t(17))],
|
|
123
|
-
rules: [maxHoursPerDay(8)],
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
defineSchedule(config: ScheduleConfig<R, S, T>): ScheduleDefinition
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### `ScheduleDefinition`
|
|
132
|
-
|
|
133
|
-
Result of `defineSchedule`.
|
|
134
|
-
|
|
135
|
-
**Properties:**
|
|
136
|
-
- `createSchedulerConfig: ModelBuilderConfig` — Produce a `ModelBuilderConfig` for the solver.
|
|
137
|
-
- `roles: readonly string[]` — Declared role names.
|
|
138
|
-
- `skills: readonly string[]` — Declared skill names.
|
|
139
|
-
- `timeNames: readonly string[]` — Names of declared semantic times.
|
|
140
|
-
- `shiftPatternIds: readonly string[]` — Shift pattern IDs.
|
|
141
|
-
- `ruleNames: readonly string[]` — Internal rule identifiers in kebab-case (e.g., "max-hours-day", "time-off").
|
|
142
|
-
|
|
143
|
-
### `RuntimeArgs`
|
|
144
|
-
|
|
145
|
-
Runtime arguments passed to `ScheduleDefinition.createSchedulerConfig`.
|
|
146
|
-
|
|
147
|
-
Separates data known at runtime (team roster, date range, ad-hoc rules)
|
|
148
|
-
from the static schedule definition. Runtime rules are merged after the
|
|
149
|
-
definition's own rules and undergo the same `appliesTo` resolution.
|
|
150
|
-
|
|
151
|
-
**Properties:**
|
|
152
|
-
- `schedulingPeriod: SchedulingPeriod` — The scheduling period (date range + optional filters).
|
|
153
|
-
- `members: SchedulingMember[]` — Team members available for this scheduling run.
|
|
154
|
-
- `runtimeRules?: RuleEntry[]` — Ad-hoc rules injected at runtime (e.g., vacation, holiday closures).
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Time Periods
|
|
159
|
-
|
|
160
|
-
### `t`
|
|
161
|
-
|
|
162
|
-
Creates a `TimeOfDay` value.
|
|
163
|
-
|
|
164
|
-
**Example:**
|
|
165
|
-
Hours only
|
|
166
|
-
```ts
|
|
167
|
-
t(9) // { hours: 9, minutes: 0 }
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
**Example:**
|
|
171
|
-
Hours and minutes
|
|
172
|
-
```ts
|
|
173
|
-
t(17, 30) // { hours: 17, minutes: 30 }
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Parameters:**
|
|
177
|
-
- `hours: number` — Hour component (0-23)
|
|
178
|
-
- `minutes: number` — Minute component (0-59)
|
|
179
|
-
|
|
180
|
-
**Returns:** `TimeOfDay`
|
|
181
|
-
|
|
182
|
-
### `time`
|
|
183
|
-
|
|
184
|
-
Defines a named time window.
|
|
185
|
-
|
|
186
|
-
A semantic time is any recurring period you need to reference:
|
|
187
|
-
service hours, delivery windows, peak periods, weekly events. Times
|
|
188
|
-
may overlap (e.g., "dinner" 18:00-22:00 and "happy_hour"
|
|
189
|
-
17:30-18:30, or "lunch" 12:00-14:00 with "peak_lunch"
|
|
190
|
-
13:00-13:30). Coverage and rules reference these names; each
|
|
191
|
-
generates independent constraints.
|
|
192
|
-
|
|
193
|
-
Every argument is a `SemanticTimeVariant` with `startTime`/`endTime`
|
|
194
|
-
and optional `dayOfWeek`/`dates` scoping. An entry without scoping is the
|
|
195
|
-
default (applies when no scoped entry matches). At most one default is
|
|
196
|
-
allowed. If no default, the time only exists on the scoped days.
|
|
197
|
-
|
|
198
|
-
Resolution precedence: `dates` > `dayOfWeek` > default.
|
|
199
|
-
|
|
200
|
-
**Example:**
|
|
201
|
-
Every day
|
|
202
|
-
```typescript
|
|
203
|
-
day_shift: time({ startTime: t(7), endTime: t(15) }),
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Example:**
|
|
207
|
-
Default with weekend variant
|
|
208
|
-
```typescript
|
|
209
|
-
peak_hours: time(
|
|
210
|
-
{ startTime: t(9), endTime: t(17) },
|
|
211
|
-
{ startTime: t(10), endTime: t(15), dayOfWeek: weekend },
|
|
212
|
-
),
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**Example:**
|
|
216
|
-
No default (specific days only)
|
|
217
|
-
```typescript
|
|
218
|
-
happy_hour: time(
|
|
219
|
-
{ startTime: t(16), endTime: t(18), dayOfWeek: ["monday", "tuesday"] },
|
|
220
|
-
{ startTime: t(17), endTime: t(19), dayOfWeek: ["friday"] },
|
|
221
|
-
),
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
```ts
|
|
225
|
-
time(entries: [SemanticTimeVariant, ...SemanticTimeVariant[]]): SemanticTimeEntry
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### `weekdays`
|
|
229
|
-
|
|
230
|
-
Monday through Friday.
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
readonly DayOfWeek[]
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### `weekend`
|
|
237
|
-
|
|
238
|
-
Saturday and Sunday.
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
readonly DayOfWeek[]
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## Coverage
|
|
247
|
-
|
|
248
|
-
### `cover`
|
|
249
|
-
|
|
250
|
-
Defines a staffing requirement for a semantic time period.
|
|
251
|
-
|
|
252
|
-
Two call forms are supported:
|
|
253
|
-
|
|
254
|
-
**Simple form** `cover(time, target, count, opts?)` creates a single
|
|
255
|
-
constraint. Use `dayOfWeek`/`dates` in `opts` to restrict which days
|
|
256
|
-
it applies to.
|
|
257
|
-
|
|
258
|
-
**Variant form** `cover(time, target, ...variants)` accepts one or more
|
|
259
|
-
`CoverageVariant` entries with day-specific counts. For each
|
|
260
|
-
scheduling day, exactly one variant is selected using the same
|
|
261
|
-
precedence as `time`: `dates` > `dayOfWeek` > default (unscoped).
|
|
262
|
-
At most one variant may be unscoped (the default). Days with no matching
|
|
263
|
-
variant produce no coverage. See `CoverageVariant` for the entry
|
|
264
|
-
shape.
|
|
265
|
-
|
|
266
|
-
**Target resolution.** The `target` parameter is resolved against declared
|
|
267
|
-
`roles` and `skills`:
|
|
268
|
-
|
|
269
|
-
- Single string: matched against roles first, then skills.
|
|
270
|
-
- Array of strings: OR logic (any of the listed roles).
|
|
271
|
-
- With `skills` option (simple form only): role AND skill(s) filter.
|
|
272
|
-
|
|
273
|
-
**Example:**
|
|
274
|
-
Basic role coverage
|
|
275
|
-
```ts
|
|
276
|
-
cover("day_shift", "nurse", 3)
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
**Example:**
|
|
280
|
-
OR logic (any of the listed roles)
|
|
281
|
-
```ts
|
|
282
|
-
cover("day_shift", ["manager", "team_lead"], 1)
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Example:**
|
|
286
|
-
Skill-based coverage
|
|
287
|
-
```ts
|
|
288
|
-
cover("night_shift", "keyholder", 1)
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**Example:**
|
|
292
|
-
Role with skill filter (role AND skill)
|
|
293
|
-
```ts
|
|
294
|
-
cover("day_shift", "nurse", 1, { skills: ["charge_nurse"] })
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
**Example:**
|
|
298
|
-
Day-of-week scoping (simple form)
|
|
299
|
-
```ts
|
|
300
|
-
cover("peak_hours", "cashier", 3, { dayOfWeek: weekdays }),
|
|
301
|
-
cover("peak_hours", "cashier", 5, { dayOfWeek: weekend }),
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
**Example:**
|
|
305
|
-
Default with date override (variant form)
|
|
306
|
-
```ts
|
|
307
|
-
cover("peak_hours", "agent",
|
|
308
|
-
{ count: 4 },
|
|
309
|
-
{ count: 2, dates: ["2025-12-24"] },
|
|
310
|
-
)
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
**Example:**
|
|
314
|
-
Weekday vs weekend with holiday override (variant form)
|
|
315
|
-
```ts
|
|
316
|
-
cover("peak_hours", "agent",
|
|
317
|
-
{ count: 3, dayOfWeek: weekdays },
|
|
318
|
-
{ count: 5, dayOfWeek: weekend },
|
|
319
|
-
{ count: 8, dates: ["2025-12-31"] },
|
|
320
|
-
)
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
**Parameters:**
|
|
324
|
-
- `timeName: T` — Name of a declared semantic time
|
|
325
|
-
- `target: R | [R, ...R[]]` — Role name, skill name, or array of role names (OR)
|
|
326
|
-
- `count: number` — Number of people needed (simple form)
|
|
327
|
-
- `opts?: CoverageOptions` — See `CoverageOptions` (simple form)
|
|
328
|
-
|
|
329
|
-
**Returns:** `CoverageEntry<T, R>`
|
|
330
|
-
|
|
331
|
-
### `CoverageOptions`
|
|
332
|
-
|
|
333
|
-
Options for a `cover` call.
|
|
334
|
-
|
|
335
|
-
Day/date scoping controls which days this coverage entry applies to.
|
|
336
|
-
An entry without `dayOfWeek` or `dates` applies every day in the
|
|
337
|
-
scheduling period.
|
|
338
|
-
|
|
339
|
-
**Properties:**
|
|
340
|
-
- `skills?: [string, ...string[]]` — Additional skill filter (AND logic with the target role).
|
|
341
|
-
- `dayOfWeek?: readonly DayOfWeek[]` — Restrict to specific days of the week.
|
|
342
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
343
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
344
|
-
|
|
345
|
-
### `CoverageVariant`
|
|
346
|
-
|
|
347
|
-
A day-specific count within a variant `cover` call.
|
|
348
|
-
|
|
349
|
-
Each variant specifies a count and optional day/date scope. During
|
|
350
|
-
resolution, the most specific matching variant wins for each day
|
|
351
|
-
(`dates` > `dayOfWeek` > default), mirroring `SemanticTimeVariant`.
|
|
352
|
-
At most one variant may be unscoped (the default).
|
|
353
|
-
|
|
354
|
-
**Example:**
|
|
355
|
-
```typescript
|
|
356
|
-
// Default: 4 agents. Christmas Eve: 2.
|
|
357
|
-
cover("peak_hours", "agent",
|
|
358
|
-
{ count: 4 },
|
|
359
|
-
{ count: 2, dates: ["2025-12-24"] },
|
|
360
|
-
)
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
**Properties:**
|
|
364
|
-
- `count: number` — Number of people needed.
|
|
365
|
-
- `dayOfWeek?: readonly DayOfWeek[]` — Restrict this variant to specific days of the week.
|
|
366
|
-
- `dates?: string[]` — Restrict this variant to specific dates (YYYY-MM-DD).
|
|
367
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
368
|
-
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
## Shift Patterns
|
|
372
|
-
|
|
373
|
-
### `shift`
|
|
374
|
-
|
|
375
|
-
Creates a `ShiftPattern` (time slot template).
|
|
376
|
-
|
|
377
|
-
Shift patterns define when people can work: the concrete time slots
|
|
378
|
-
the solver may assign members to. Each pattern repeats across all
|
|
379
|
-
scheduling days unless filtered by `dayOfWeek` or `roles`.
|
|
380
|
-
|
|
381
|
-
**Example:**
|
|
382
|
-
```typescript
|
|
383
|
-
shift("early", t(6), t(14)),
|
|
384
|
-
shift("day", t(9), t(17)),
|
|
385
|
-
shift("night", t(22), t(6), { roles: ["nurse", "doctor"] }),
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
```ts
|
|
389
|
-
shift(id: string, startTime: TimeOfDay, endTime: TimeOfDay, opts?: Pick<ShiftPattern, "roles" | "dayOfWeek" | "locationId">): ShiftPattern
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Rules
|
|
395
|
-
|
|
396
|
-
### `RuleOptions`
|
|
397
|
-
|
|
398
|
-
Scoping options shared by most rule functions.
|
|
399
|
-
|
|
400
|
-
Each rule function returns an opaque `RuleEntry` for the `rules`
|
|
401
|
-
array. Most accept a `RuleOptions` parameter for scoping and priority.
|
|
402
|
-
|
|
403
|
-
**Entity scoping.** `appliesTo` targets a role name, skill name, or
|
|
404
|
-
member ID. It is resolved against declared roles first, then skills,
|
|
405
|
-
then runtime member IDs. The namespaces are guaranteed disjoint by
|
|
406
|
-
validation. Unscoped rules apply to all members.
|
|
407
|
-
|
|
408
|
-
**Time scoping.** `dayOfWeek`, `dateRange`, `dates`, and
|
|
409
|
-
`recurringPeriods` narrow when the rule is active. Unscoped rules
|
|
410
|
-
apply to every day in the scheduling period.
|
|
411
|
-
|
|
412
|
-
**Priority.** Defaults to `MANDATORY` (hard constraint the solver
|
|
413
|
-
must satisfy). Use `LOW`, `MEDIUM`, or `HIGH` for soft preferences
|
|
414
|
-
the solver may violate when necessary.
|
|
415
|
-
|
|
416
|
-
**Properties:**
|
|
417
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
418
|
-
- `dayOfWeek?: readonly DayOfWeek[]` — Restrict to specific days of the week.
|
|
419
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
420
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
421
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
422
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
423
|
-
|
|
424
|
-
### `EntityOnlyRuleOptions`
|
|
425
|
-
|
|
426
|
-
Options for rules that support entity scoping only (no time scoping).
|
|
427
|
-
|
|
428
|
-
Used by rules whose semantics are inherently per-day or per-week
|
|
429
|
-
(e.g., `minHoursPerDay`, `maxConsecutiveDays`) and cannot
|
|
430
|
-
be meaningfully restricted to a date range or day of week.
|
|
431
|
-
|
|
432
|
-
**Properties:**
|
|
433
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
434
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
435
|
-
|
|
436
|
-
### `TimeOffOptions`
|
|
437
|
-
|
|
438
|
-
Options for `timeOff`.
|
|
439
|
-
|
|
440
|
-
At least one time scoping field is required (`dayOfWeek`, `dateRange`,
|
|
441
|
-
`dates`, or `recurringPeriods`). Use `from`/`until` to block only part
|
|
442
|
-
of a day.
|
|
443
|
-
|
|
444
|
-
**Properties:**
|
|
445
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
446
|
-
- `from?: TimeOfDay` — Off from this time until end of day.
|
|
447
|
-
- `until?: TimeOfDay` — Off from start of day until this time.
|
|
448
|
-
- `dayOfWeek?: readonly DayOfWeek[]` — Restrict to specific days of the week.
|
|
449
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
450
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
451
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
452
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
453
|
-
|
|
454
|
-
### `CostRuleOptions`
|
|
455
|
-
|
|
456
|
-
Options for cost rules.
|
|
457
|
-
|
|
458
|
-
Cost rules are objective terms, not constraints. The `priority` field from
|
|
459
|
-
`RuleOptions` does not apply.
|
|
460
|
-
|
|
461
|
-
**Properties:**
|
|
462
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
463
|
-
- `dayOfWeek?: DayOfWeek[]` — Restrict to specific days of the week.
|
|
464
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
465
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
466
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
467
|
-
|
|
468
|
-
### `maxHoursPerDay`
|
|
469
|
-
|
|
470
|
-
Limits how many hours a person can work in a single day.
|
|
471
|
-
|
|
472
|
-
**Example:**
|
|
473
|
-
Global limit
|
|
474
|
-
```ts
|
|
475
|
-
maxHoursPerDay(10)
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
**Example:**
|
|
479
|
-
Scoped to a role
|
|
480
|
-
```ts
|
|
481
|
-
maxHoursPerDay(6, { appliesTo: "student" })
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
```ts
|
|
485
|
-
maxHoursPerDay(hours: number, opts?: RuleOptions): RuleEntry
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### `maxHoursPerWeek`
|
|
489
|
-
|
|
490
|
-
Caps total hours a person can work within each scheduling week.
|
|
491
|
-
|
|
492
|
-
**Example:**
|
|
493
|
-
Global cap
|
|
494
|
-
```ts
|
|
495
|
-
maxHoursPerWeek(48)
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
**Example:**
|
|
499
|
-
Part-time cap for a skill group
|
|
500
|
-
```ts
|
|
501
|
-
maxHoursPerWeek(20, { appliesTo: "part_time" })
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
```ts
|
|
505
|
-
maxHoursPerWeek(hours: number, opts?: RuleOptions): RuleEntry
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### `minHoursPerDay`
|
|
509
|
-
|
|
510
|
-
Ensures a person works at least a minimum number of hours per day when assigned.
|
|
511
|
-
|
|
512
|
-
**Example:**
|
|
513
|
-
```ts
|
|
514
|
-
minHoursPerDay(4)
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
```ts
|
|
518
|
-
minHoursPerDay(hours: number, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### `minHoursPerWeek`
|
|
522
|
-
|
|
523
|
-
Enforces a minimum total number of hours per scheduling week.
|
|
524
|
-
|
|
525
|
-
**Example:**
|
|
526
|
-
Guaranteed minimum for full-time members
|
|
527
|
-
```ts
|
|
528
|
-
minHoursPerWeek(30, { appliesTo: "full_time" })
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
```ts
|
|
532
|
-
minHoursPerWeek(hours: number, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### `maxShiftsPerDay`
|
|
536
|
-
|
|
537
|
-
Limits how many shifts a person can work in a single day.
|
|
538
|
-
|
|
539
|
-
**Example:**
|
|
540
|
-
One shift per day
|
|
541
|
-
```ts
|
|
542
|
-
maxShiftsPerDay(1)
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
```ts
|
|
546
|
-
maxShiftsPerDay(shifts: number, opts?: RuleOptions): RuleEntry
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
### `maxConsecutiveDays`
|
|
550
|
-
|
|
551
|
-
Limits how many consecutive days a person can be assigned.
|
|
552
|
-
|
|
553
|
-
**Example:**
|
|
554
|
-
Five-day work week limit
|
|
555
|
-
```ts
|
|
556
|
-
maxConsecutiveDays(5)
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
```ts
|
|
560
|
-
maxConsecutiveDays(days: number, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### `minConsecutiveDays`
|
|
564
|
-
|
|
565
|
-
Requires a minimum stretch of consecutive working days once assigned.
|
|
566
|
-
|
|
567
|
-
**Example:**
|
|
568
|
-
```ts
|
|
569
|
-
minConsecutiveDays(2)
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
```ts
|
|
573
|
-
minConsecutiveDays(days: number, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
### `minRestBetweenShifts`
|
|
577
|
-
|
|
578
|
-
Enforces a minimum rest period between any two shifts a person works.
|
|
579
|
-
|
|
580
|
-
**Example:**
|
|
581
|
-
EU Working Time Directive (11 hours)
|
|
582
|
-
```ts
|
|
583
|
-
minRestBetweenShifts(11)
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
```ts
|
|
587
|
-
minRestBetweenShifts(hours: number, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### `preference`
|
|
591
|
-
|
|
592
|
-
Adds objective weight to prefer or avoid assigning team members.
|
|
593
|
-
|
|
594
|
-
**Example:**
|
|
595
|
-
Prefer assigning full-time staff
|
|
596
|
-
```ts
|
|
597
|
-
preference("high", { appliesTo: "full_time" })
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
**Example:**
|
|
601
|
-
Avoid assigning a specific member on weekends
|
|
602
|
-
```ts
|
|
603
|
-
preference("low", { appliesTo: "alice", dayOfWeek: weekend })
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
**Parameters:**
|
|
607
|
-
- `level: "high" | "low"` — `"high"` to prefer assigning, `"low"` to avoid
|
|
608
|
-
- `opts?: Omit<RuleOptions, "priority">` — Entity and time scoping (no priority; preference is the priority mechanism)
|
|
609
|
-
|
|
610
|
-
**Returns:** `RuleEntry`
|
|
611
|
-
|
|
612
|
-
### `preferLocation`
|
|
613
|
-
|
|
614
|
-
Prefers assigning a person to shift patterns at a specific location.
|
|
615
|
-
|
|
616
|
-
**Example:**
|
|
617
|
-
```ts
|
|
618
|
-
preferLocation("north_wing", { appliesTo: "alice" })
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
```ts
|
|
622
|
-
preferLocation(locationId: string, opts?: EntityOnlyRuleOptions): RuleEntry
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
### `timeOff`
|
|
626
|
-
|
|
627
|
-
Blocks or penalizes assignments during specified time periods.
|
|
628
|
-
|
|
629
|
-
At least one time scoping field is required (`dayOfWeek`, `dateRange`,
|
|
630
|
-
`dates`, or `recurringPeriods`).
|
|
631
|
-
|
|
632
|
-
Use `from` for "off from this time until end of day" and `until` for
|
|
633
|
-
"off from start of day until this time."
|
|
634
|
-
|
|
635
|
-
**Example:**
|
|
636
|
-
```typescript
|
|
637
|
-
timeOff({ appliesTo: "mauro", dayOfWeek: weekend }),
|
|
638
|
-
timeOff({ appliesTo: "student", dayOfWeek: ["wednesday"], from: t(14) }),
|
|
639
|
-
timeOff({ appliesTo: "alice", dateRange: { start: "2024-02-01", end: "2024-02-05" } }),
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
```ts
|
|
643
|
-
timeOff(opts: TimeOffOptions): RuleEntry
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
### `assignTogether`
|
|
647
|
-
|
|
648
|
-
Encourages or enforces that team members work the same shifts on a day.
|
|
649
|
-
|
|
650
|
-
**Example:**
|
|
651
|
-
```typescript
|
|
652
|
-
assignTogether(["alice", "bob"], { priority: "HIGH" }),
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
```ts
|
|
656
|
-
assignTogether(members: [string, string, ...string[]], opts?: AssignTogetherOptions): RuleEntry
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
### `minimizeCost`
|
|
660
|
-
|
|
661
|
-
Tells the solver to minimize total labor cost.
|
|
662
|
-
|
|
663
|
-
Without this rule, cost modifiers only affect post-solve calculation.
|
|
664
|
-
When present, the solver actively prefers cheaper assignments.
|
|
665
|
-
|
|
666
|
-
For hourly members, penalizes each assignment proportionally to cost.
|
|
667
|
-
For salaried members, adds a fixed weekly salary cost when they have
|
|
668
|
-
any assignment that week (zero marginal cost up to contracted hours).
|
|
669
|
-
|
|
670
|
-
**Example:**
|
|
671
|
-
```ts
|
|
672
|
-
minimizeCost()
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
```ts
|
|
676
|
-
minimizeCost(opts?: CostRuleOptions): RuleEntry
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
### `dayMultiplier`
|
|
680
|
-
|
|
681
|
-
Multiplies the base rate for assignments on specified days.
|
|
682
|
-
|
|
683
|
-
The base cost (1x) is already counted by `minimizeCost`;
|
|
684
|
-
this rule adds only the extra portion above 1x.
|
|
685
|
-
|
|
686
|
-
**Example:**
|
|
687
|
-
Weekend multiplier
|
|
688
|
-
```typescript
|
|
689
|
-
dayMultiplier(1.5, { dayOfWeek: weekend })
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
```ts
|
|
693
|
-
dayMultiplier(factor: number, opts?: CostRuleOptions): RuleEntry
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
### `daySurcharge`
|
|
697
|
-
|
|
698
|
-
Adds a flat extra amount per hour for assignments on specified days.
|
|
699
|
-
|
|
700
|
-
The surcharge is independent of the member's base rate.
|
|
701
|
-
|
|
702
|
-
**Example:**
|
|
703
|
-
Weekend surcharge
|
|
704
|
-
```typescript
|
|
705
|
-
daySurcharge(500, { dayOfWeek: weekend })
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
```ts
|
|
709
|
-
daySurcharge(amountPerHour: number, opts?: CostRuleOptions): RuleEntry
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
### `timeSurcharge`
|
|
713
|
-
|
|
714
|
-
Adds a flat surcharge per hour for the portion of a shift that overlaps a time-of-day window.
|
|
715
|
-
|
|
716
|
-
The window supports overnight spans (e.g., 22:00-06:00). The surcharge
|
|
717
|
-
is independent of the member's base rate.
|
|
718
|
-
|
|
719
|
-
**Example:**
|
|
720
|
-
Night differential
|
|
721
|
-
```typescript
|
|
722
|
-
timeSurcharge(200, { from: t(22), until: t(6) })
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
**Parameters:**
|
|
726
|
-
- `amountPerHour: number` — Flat surcharge per hour in smallest currency unit
|
|
727
|
-
- `window: { from: TimeOfDay; until: TimeOfDay }` — Time-of-day window
|
|
728
|
-
- `opts?: CostRuleOptions` — Entity and time scoping
|
|
729
|
-
|
|
730
|
-
**Returns:** `RuleEntry`
|
|
731
|
-
|
|
732
|
-
### `overtimeMultiplier`
|
|
733
|
-
|
|
734
|
-
Applies a multiplier to hours beyond a weekly threshold.
|
|
735
|
-
|
|
736
|
-
Only the extra portion above 1x is added (the base cost is already
|
|
737
|
-
counted by `minimizeCost`).
|
|
738
|
-
|
|
739
|
-
**Example:**
|
|
740
|
-
```typescript
|
|
741
|
-
overtimeMultiplier({ after: 40, factor: 1.5 })
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
```ts
|
|
745
|
-
overtimeMultiplier(opts: { after: number; factor: number } & CostRuleOptions): RuleEntry
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
### `overtimeSurcharge`
|
|
749
|
-
|
|
750
|
-
Adds a flat surcharge per hour beyond a weekly threshold.
|
|
751
|
-
|
|
752
|
-
The surcharge is independent of the member's base rate.
|
|
753
|
-
|
|
754
|
-
**Example:**
|
|
755
|
-
```typescript
|
|
756
|
-
overtimeSurcharge({ after: 40, amount: 1000 })
|
|
757
|
-
```
|
|
758
|
-
|
|
759
|
-
```ts
|
|
760
|
-
overtimeSurcharge(opts: { after: number; amount: number } & CostRuleOptions): RuleEntry
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
### `dailyOvertimeMultiplier`
|
|
764
|
-
|
|
765
|
-
Applies a multiplier to hours beyond a daily threshold.
|
|
766
|
-
|
|
767
|
-
Only the extra portion above 1x is added (the base cost is already
|
|
768
|
-
counted by `minimizeCost`).
|
|
769
|
-
|
|
770
|
-
**Example:**
|
|
771
|
-
```typescript
|
|
772
|
-
dailyOvertimeMultiplier({ after: 8, factor: 1.5 })
|
|
773
|
-
```
|
|
774
|
-
|
|
775
|
-
```ts
|
|
776
|
-
dailyOvertimeMultiplier(opts: { after: number; factor: number } & CostRuleOptions): RuleEntry
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
### `dailyOvertimeSurcharge`
|
|
780
|
-
|
|
781
|
-
Adds a flat surcharge per hour beyond a daily threshold.
|
|
782
|
-
|
|
783
|
-
The surcharge is independent of the member's base rate.
|
|
784
|
-
|
|
785
|
-
**Example:**
|
|
786
|
-
```typescript
|
|
787
|
-
dailyOvertimeSurcharge({ after: 8, amount: 500 })
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
```ts
|
|
791
|
-
dailyOvertimeSurcharge(opts: { after: number; amount: number } & CostRuleOptions): RuleEntry
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
### `tieredOvertimeMultiplier`
|
|
795
|
-
|
|
796
|
-
Applies multiple overtime thresholds with increasing multipliers.
|
|
797
|
-
|
|
798
|
-
Each tier applies only to the hours between its threshold and the next.
|
|
799
|
-
Tiers must be sorted by threshold ascending.
|
|
800
|
-
|
|
801
|
-
**Example:**
|
|
802
|
-
```typescript
|
|
803
|
-
// Hours 0-40: base rate
|
|
804
|
-
// Hours 40-48: 1.5x
|
|
805
|
-
// Hours 48+: 2.0x
|
|
806
|
-
tieredOvertimeMultiplier([
|
|
807
|
-
{ after: 40, factor: 1.5 },
|
|
808
|
-
{ after: 48, factor: 2.0 },
|
|
809
|
-
])
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
```ts
|
|
813
|
-
tieredOvertimeMultiplier(tiers: [OvertimeTier, ...OvertimeTier[]], opts?: CostRuleOptions): RuleEntry
|
|
814
|
-
```
|
|
815
|
-
|
|
816
|
-
---
|
|
817
|
-
|
|
818
|
-
## Supporting Types
|
|
819
|
-
|
|
820
|
-
### `TimeOfDay`
|
|
821
|
-
|
|
822
|
-
Time of day representation (hours and minutes, with optional seconds/nanos).
|
|
823
|
-
|
|
824
|
-
Used for defining shift start/end times and semantic time boundaries.
|
|
825
|
-
Hours are in 24-hour format (0-23).
|
|
826
|
-
|
|
827
|
-
**Example:**
|
|
828
|
-
```typescript
|
|
829
|
-
{ hours: 9, minutes: 0 }
|
|
830
|
-
{ hours: 17, minutes: 30 }
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
**Properties:**
|
|
834
|
-
- `hours: number`
|
|
835
|
-
- `minutes: number`
|
|
836
|
-
- `seconds?: number`
|
|
837
|
-
- `nanos?: number`
|
|
838
|
-
|
|
839
|
-
### `DayOfWeek`
|
|
840
|
-
|
|
841
|
-
Day of the week identifier.
|
|
842
|
-
|
|
843
|
-
```typescript
|
|
844
|
-
"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"
|
|
845
|
-
```
|
|
846
|
-
|
|
847
|
-
### `SchedulingPeriod`
|
|
848
|
-
|
|
849
|
-
Defines a scheduling period as a date range with optional filters.
|
|
850
|
-
|
|
851
|
-
The `dateRange` specifies the overall scheduling window. Use `dayOfWeek`
|
|
852
|
-
and/or `dates` to narrow which days within the range are included.
|
|
853
|
-
Filters compose: a day must pass all specified filters to be included.
|
|
854
|
-
|
|
855
|
-
**Example:**
|
|
856
|
-
All days in a week
|
|
857
|
-
```typescript
|
|
858
|
-
const period: SchedulingPeriod = {
|
|
859
|
-
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
860
|
-
};
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
**Example:**
|
|
864
|
-
Only specific days of the week (closed Mon/Tue)
|
|
865
|
-
```typescript
|
|
866
|
-
const period: SchedulingPeriod = {
|
|
867
|
-
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
868
|
-
dayOfWeek: ['wednesday', 'thursday', 'friday', 'saturday', 'sunday'],
|
|
869
|
-
};
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
**Properties:**
|
|
873
|
-
- `dateRange: { start: string; end: string }` — The overall scheduling window (start and end are inclusive).
|
|
874
|
-
Dates should be in YYYY-MM-DD format.
|
|
875
|
-
- `dayOfWeek?: DayOfWeek[]` — Include only these days of the week.
|
|
876
|
-
If omitted, all days of the week are included.
|
|
877
|
-
- `dates?: string[]` — Include only these specific dates (YYYY-MM-DD) within the range.
|
|
878
|
-
If omitted, all dates in the range are included (subject to dayOfWeek filter).
|
|
879
|
-
|
|
880
|
-
### `SchedulingMember`
|
|
881
|
-
|
|
882
|
-
A team member available for scheduling.
|
|
883
|
-
|
|
884
|
-
Members are assigned to shift patterns by the solver based on
|
|
885
|
-
coverage requirements, rules, and constraints.
|
|
886
|
-
|
|
887
|
-
**Properties:**
|
|
888
|
-
- `id: string` — Unique identifier for this member. Must not contain colons.
|
|
889
|
-
- `roles: string[]` — Roles this member can fill (e.g. "nurse", "doctor").
|
|
890
|
-
- `skills?: string[]` — Skills this member has (e.g. "charge_nurse", "forklift").
|
|
891
|
-
- `pay?: HourlyPay | SalariedPay` — Base pay. Required when cost rules are used.
|
|
892
|
-
|
|
893
|
-
### `HourlyPay`
|
|
894
|
-
|
|
895
|
-
Pay per hour in the caller's smallest currency unit (e.g., pence, cents).
|
|
896
|
-
|
|
897
|
-
**Properties:**
|
|
898
|
-
- `hourlyRate: number` — Pay per hour in smallest currency unit.
|
|
899
|
-
|
|
900
|
-
### `SalariedPay`
|
|
901
|
-
|
|
902
|
-
Annual salary with contracted weekly hours.
|
|
903
|
-
|
|
904
|
-
The solver treats salaried members as having a fixed weekly cost
|
|
905
|
-
(`annual / 52`) that is incurred once they work any shift in a week.
|
|
906
|
-
Additional shifts within the same week have zero marginal cost.
|
|
907
|
-
|
|
908
|
-
Note: overtime multiplier rules apply only to hourly members.
|
|
909
|
-
Overtime surcharge rules apply to all members regardless of pay type.
|
|
910
|
-
|
|
911
|
-
**Properties:**
|
|
912
|
-
- `annual: number` — Annual salary in smallest currency unit.
|
|
913
|
-
- `hoursPerWeek: number` — Contracted hours per week. Reserved for future overtime support.
|
|
914
|
-
|
|
915
|
-
### `Priority`
|
|
916
|
-
|
|
917
|
-
How strictly the solver enforces a rule.
|
|
918
|
-
|
|
919
|
-
- `"LOW"`, `"MEDIUM"`, `"HIGH"`: soft constraints with increasing penalty for violations
|
|
920
|
-
- `"MANDATORY"`: hard constraint; the solver will not produce a solution that violates it
|
|
921
|
-
|
|
922
|
-
```typescript
|
|
923
|
-
"LOW" | "MEDIUM" | "HIGH" | "MANDATORY"
|
|
924
|
-
```
|
|
925
|
-
|