dabke 0.82.0 → 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 +23 -0
- package/README.md +6 -3
- 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-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/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/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/registry.d.ts +4 -1
- 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/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 +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- 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.js → schedule/definition.js} +9 -673
- 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/package.json +4 -9
- package/src/cpsat/rules/index.ts +3 -0
- package/src/cpsat/rules/max-days-week.ts +143 -0
- package/src/cpsat/rules/min-days-week.ts +120 -0
- package/src/cpsat/rules/must-assign.ts +108 -0
- package/src/cpsat/rules/registry.ts +6 -0
- package/src/cpsat/rules/rules.types.ts +3 -0
- package/src/cpsat/rules/scope.types.ts +1 -1
- package/src/index.ts +8 -3
- 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/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.map +0 -1
- package/llms.txt +0 -758
- package/src/llms.ts +0 -3
- package/src/schedule.ts +0 -1960
package/llms.txt
DELETED
|
@@ -1,758 +0,0 @@
|
|
|
1
|
-
# dabke
|
|
2
|
-
|
|
3
|
-
> Scheduling library powered by constraint programming (CP-SAT)
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Schedule Definition
|
|
8
|
-
|
|
9
|
-
### `SolveResult`
|
|
10
|
-
|
|
11
|
-
Result of `Schedule.solve`.
|
|
12
|
-
|
|
13
|
-
**Properties:**
|
|
14
|
-
- `status: SolveStatus` — Outcome of the solve attempt.
|
|
15
|
-
- `assignments: ShiftAssignment[]` — Shift assignments (empty when infeasible or no solution).
|
|
16
|
-
- `validation: ScheduleValidation` — Validation diagnostics from compilation.
|
|
17
|
-
- `cost?: CostBreakdown` — Cost breakdown (present when cost rules are used and a solution is found).
|
|
18
|
-
|
|
19
|
-
### `SolveOptions`
|
|
20
|
-
|
|
21
|
-
Options for `Schedule.solve` and `Schedule.compile`.
|
|
22
|
-
|
|
23
|
-
**Properties:**
|
|
24
|
-
- `dateRange: { start: string; end: string }` — The date range to schedule.
|
|
25
|
-
- `pinned?: ShiftAssignment[]` — Fixed assignments from a prior solve (e.g., rolling schedule).
|
|
26
|
-
These are injected as fixed variables in the solver.
|
|
27
|
-
|
|
28
|
-
Not yet implemented. Providing pinned assignments throws an error.
|
|
29
|
-
|
|
30
|
-
### `ScheduleConfig`
|
|
31
|
-
|
|
32
|
-
Configuration for `schedule`.
|
|
33
|
-
|
|
34
|
-
Coverage entries for the same semantic time and target stack additively.
|
|
35
|
-
An unscoped entry applies every day; adding a weekend-only entry on top
|
|
36
|
-
doubles the count on those days. Use mutually exclusive `dayOfWeek` on
|
|
37
|
-
both entries to avoid stacking. See `cover` for details.
|
|
38
|
-
|
|
39
|
-
`roleIds`, `times`, `coverage`, and `shiftPatterns` are required.
|
|
40
|
-
These four fields form the minimum solvable schedule.
|
|
41
|
-
|
|
42
|
-
**Properties:**
|
|
43
|
-
- `roleIds: R` — Declared role IDs.
|
|
44
|
-
- `skillIds?: S` — Declared skill IDs.
|
|
45
|
-
- `times: T` — Named semantic time periods.
|
|
46
|
-
- `coverage: CoverageEntry<keyof T & string, R[number] | NonNullable<S>[number]>[]` — Staffing requirements per time period (entries stack additively).
|
|
47
|
-
- `shiftPatterns: ShiftPattern[]` — Available shift patterns.
|
|
48
|
-
- `rules?: RuleEntry[]` — Scheduling rules and constraints.
|
|
49
|
-
- `ruleFactories?: Record<string, CreateCpsatRuleFunction>` — Custom rule factories. Keys are rule names, values are functions
|
|
50
|
-
that take a config object and return a `CompilationRule`.
|
|
51
|
-
Built-in rule names cannot be overridden.
|
|
52
|
-
- `members?: SchedulingMember[]` — Team members (typically added via `.with()` at runtime).
|
|
53
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Days of the week the business operates (inclusion filter).
|
|
54
|
-
- `weekStartsOn?: DayOfWeek` — Which day starts the week for weekly rules. Defaults to `"monday"`.
|
|
55
|
-
|
|
56
|
-
### `schedule`
|
|
57
|
-
|
|
58
|
-
Create a schedule definition.
|
|
59
|
-
|
|
60
|
-
Returns an immutable `Schedule` that can be composed via `.with()`
|
|
61
|
-
and solved via `.solve()`.
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
const venue = schedule({
|
|
65
|
-
roleIds: ["waiter", "runner", "manager"],
|
|
66
|
-
skillIds: ["senior"],
|
|
67
|
-
times: {
|
|
68
|
-
lunch: time({ startTime: t(12), endTime: t(15) }),
|
|
69
|
-
dinner: time(
|
|
70
|
-
{ startTime: t(17), endTime: t(21) },
|
|
71
|
-
{ startTime: t(18), endTime: t(22), dayOfWeek: weekend },
|
|
72
|
-
),
|
|
73
|
-
},
|
|
74
|
-
coverage: [
|
|
75
|
-
cover("lunch", "waiter", 2),
|
|
76
|
-
cover("dinner", "waiter", 4, { dayOfWeek: weekdays }),
|
|
77
|
-
cover("dinner", "waiter", 5, { dayOfWeek: weekend }),
|
|
78
|
-
cover("dinner", "manager", 1),
|
|
79
|
-
],
|
|
80
|
-
shiftPatterns: [
|
|
81
|
-
shift("lunch_shift", t(11, 30), t(15)),
|
|
82
|
-
shift("evening", t(17), t(22)),
|
|
83
|
-
],
|
|
84
|
-
rules: [
|
|
85
|
-
maxHoursPerDay(10),
|
|
86
|
-
maxHoursPerWeek(48),
|
|
87
|
-
minRestBetweenShifts(11),
|
|
88
|
-
],
|
|
89
|
-
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### `partialSchedule`
|
|
93
|
-
|
|
94
|
-
Create a partial schedule for composition via `.with()`.
|
|
95
|
-
|
|
96
|
-
Unlike `schedule`, all fields are optional. Use this for
|
|
97
|
-
schedules that layer rules, coverage, or other config onto a
|
|
98
|
-
complete base schedule.
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
const companyPolicy = partialSchedule({
|
|
102
|
-
rules: [maxHoursPerWeek(40), minRestBetweenShifts(11)],
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const ready = venue.with(companyPolicy, teamMembers);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Time Periods
|
|
111
|
-
|
|
112
|
-
### `t`
|
|
113
|
-
|
|
114
|
-
Creates a `TimeOfDay` value.
|
|
115
|
-
|
|
116
|
-
Hours only
|
|
117
|
-
```ts
|
|
118
|
-
t(9) // { hours: 9, minutes: 0 }
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Parameters:**
|
|
122
|
-
- `hours: number` — Hour component (0-23)
|
|
123
|
-
- `minutes: number` — Minute component (0-59)
|
|
124
|
-
|
|
125
|
-
**Returns:** `TimeOfDay`
|
|
126
|
-
|
|
127
|
-
### `weekdays`
|
|
128
|
-
|
|
129
|
-
Monday through Friday.
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
readonly ["monday", "tuesday", "wednesday", "thursday", "friday"]
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### `weekend`
|
|
136
|
-
|
|
137
|
-
Saturday and Sunday.
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
readonly ["saturday", "sunday"]
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### `time`
|
|
144
|
-
|
|
145
|
-
Define a named semantic time period.
|
|
146
|
-
|
|
147
|
-
Each entry has `startTime`/`endTime` and optional `dayOfWeek` or `dates`
|
|
148
|
-
scoping. Entries without scoping are the default.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
times: {
|
|
152
|
-
// Simple: same times every day
|
|
153
|
-
lunch: time({ startTime: t(12), endTime: t(15) }),
|
|
154
|
-
|
|
155
|
-
// Variants: different times on weekends
|
|
156
|
-
dinner: time(
|
|
157
|
-
{ startTime: t(17), endTime: t(21) },
|
|
158
|
-
{ startTime: t(18), endTime: t(22), dayOfWeek: weekend },
|
|
159
|
-
),
|
|
160
|
-
|
|
161
|
-
// Point-in-time window (keyholder at opening)
|
|
162
|
-
opening: time({ startTime: t(8, 30), endTime: t(9) }),
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Coverage
|
|
169
|
-
|
|
170
|
-
### `CoverageVariant`
|
|
171
|
-
|
|
172
|
-
A day-specific count within a variant `cover` call.
|
|
173
|
-
|
|
174
|
-
Each variant specifies a count and optional day/date scope. During
|
|
175
|
-
resolution, the most specific matching variant wins for each day
|
|
176
|
-
(`dates` > `dayOfWeek` > default), mirroring `SemanticTimeVariant`.
|
|
177
|
-
At most one variant may be unscoped (the default).
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// Default: 4 agents. Christmas Eve: 2.
|
|
181
|
-
cover("peak_hours", "agent",
|
|
182
|
-
{ count: 4 },
|
|
183
|
-
{ count: 2, dates: ["2025-12-24"] },
|
|
184
|
-
)
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**Properties:**
|
|
188
|
-
- `count: number` — Number of people needed.
|
|
189
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restrict this variant to specific days of the week.
|
|
190
|
-
- `dates?: string[]` — Restrict this variant to specific dates (YYYY-MM-DD).
|
|
191
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
192
|
-
|
|
193
|
-
### `CoverageOptions`
|
|
194
|
-
|
|
195
|
-
Options for a `cover` call.
|
|
196
|
-
|
|
197
|
-
Day/date scoping controls which days this coverage entry applies to.
|
|
198
|
-
An entry without `dayOfWeek` or `dates` applies every day in the
|
|
199
|
-
scheduling period.
|
|
200
|
-
|
|
201
|
-
**Properties:**
|
|
202
|
-
- `skillIds?: [string, ...string[]]` — Additional skill ID filter (AND logic with the target role).
|
|
203
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restrict to specific days of the week.
|
|
204
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
205
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
206
|
-
|
|
207
|
-
### `cover`
|
|
208
|
-
|
|
209
|
-
Defines a staffing requirement for a semantic time period.
|
|
210
|
-
|
|
211
|
-
Entries for the same time and role **stack additively**.
|
|
212
|
-
For weekday vs weekend staffing, use mutually exclusive `dayOfWeek`
|
|
213
|
-
on both entries.
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
coverage: [
|
|
217
|
-
// 2 waiters during lunch
|
|
218
|
-
cover("lunch", "waiter", 2),
|
|
219
|
-
|
|
220
|
-
// 1 manager OR supervisor during dinner
|
|
221
|
-
cover("dinner", ["manager", "supervisor"], 1),
|
|
222
|
-
|
|
223
|
-
// 1 person with keyholder skill at opening
|
|
224
|
-
cover("opening", "keyholder", 1),
|
|
225
|
-
|
|
226
|
-
// 1 senior waiter (role + skill AND)
|
|
227
|
-
cover("lunch", "waiter", 1, { skillIds: ["senior"] }),
|
|
228
|
-
|
|
229
|
-
// Different counts by day (mutually exclusive dayOfWeek!)
|
|
230
|
-
cover("lunch", "waiter", 2, { dayOfWeek: weekdays }),
|
|
231
|
-
cover("lunch", "waiter", 3, { dayOfWeek: weekend }),
|
|
232
|
-
]
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**Parameters:**
|
|
236
|
-
- `timeName: T` — Name of a declared semantic time
|
|
237
|
-
- `target: R | [R, ...R[]]` — Role name (string), array of role names (OR logic), or skill name
|
|
238
|
-
- `count: number` — Number of people needed
|
|
239
|
-
- `opts?: CoverageOptions` — Options: `skillIds` (AND filter), `dayOfWeek`, `dates`, `priority`
|
|
240
|
-
|
|
241
|
-
**Returns:** `CoverageEntry<T, R>`
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Shift Patterns
|
|
246
|
-
|
|
247
|
-
### `shift`
|
|
248
|
-
|
|
249
|
-
Define a shift pattern: a time slot available for employee assignment.
|
|
250
|
-
|
|
251
|
-
Each pattern repeats daily unless filtered by `dayOfWeek`.
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
shiftPatterns: [
|
|
255
|
-
shift("morning", t(11, 30), t(15)),
|
|
256
|
-
shift("evening", t(17), t(22)),
|
|
257
|
-
|
|
258
|
-
// Role-restricted shift
|
|
259
|
-
shift("kitchen", t(6), t(14), { roleIds: ["chef", "prep_cook"] }),
|
|
260
|
-
|
|
261
|
-
// Day-restricted shift
|
|
262
|
-
shift("saturday_short", t(9), t(14), { dayOfWeek: ["saturday"] }),
|
|
263
|
-
|
|
264
|
-
// Location-specific shift
|
|
265
|
-
shift("terrace_lunch", t(12), t(16), { locationId: "terrace" }),
|
|
266
|
-
]
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
## Rules
|
|
272
|
-
|
|
273
|
-
### `RecurringPeriod`
|
|
274
|
-
|
|
275
|
-
Recurring calendar period for time scoping.
|
|
276
|
-
|
|
277
|
-
**Properties:**
|
|
278
|
-
- `name: string`
|
|
279
|
-
- `startMonth: number`
|
|
280
|
-
- `startDay: number`
|
|
281
|
-
- `endMonth: number`
|
|
282
|
-
- `endDay: number`
|
|
283
|
-
|
|
284
|
-
### `RuleOptions`
|
|
285
|
-
|
|
286
|
-
Scoping options shared by most rule functions.
|
|
287
|
-
|
|
288
|
-
Default priority is `MANDATORY`. Use `appliesTo` to scope to a
|
|
289
|
-
role, skill, or member ID. Use time scoping options (`dayOfWeek`,
|
|
290
|
-
`dateRange`, `dates`) to limit when the rule applies.
|
|
291
|
-
Not all rules support all scoping options. Entity-only rules
|
|
292
|
-
(e.g., `maxConsecutiveDays`) ignore time scoping.
|
|
293
|
-
|
|
294
|
-
**Properties:**
|
|
295
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
296
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restrict to specific days of the week.
|
|
297
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
298
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
299
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
300
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
301
|
-
|
|
302
|
-
### `EntityOnlyRuleOptions`
|
|
303
|
-
|
|
304
|
-
Options for rules that support entity scoping only (no time scoping).
|
|
305
|
-
|
|
306
|
-
Used by rules whose semantics are inherently per-day or per-week
|
|
307
|
-
(e.g., `minHoursPerDay`, `maxConsecutiveDays`) and cannot
|
|
308
|
-
be meaningfully restricted to a date range or day of week.
|
|
309
|
-
|
|
310
|
-
**Properties:**
|
|
311
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
312
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
313
|
-
|
|
314
|
-
### `TimeOffOptions`
|
|
315
|
-
|
|
316
|
-
Options for `timeOff`.
|
|
317
|
-
|
|
318
|
-
At least one time scoping field is required (`dayOfWeek`, `dateRange`,
|
|
319
|
-
`dates`, or `recurringPeriods`). Use `from`/`until` to block only part
|
|
320
|
-
of a day.
|
|
321
|
-
|
|
322
|
-
**Properties:**
|
|
323
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
324
|
-
- `from?: TimeOfDay` — Off from this time until end of day.
|
|
325
|
-
- `until?: TimeOfDay` — Off from start of day until this time.
|
|
326
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restrict to specific days of the week.
|
|
327
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
328
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
329
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
330
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
331
|
-
|
|
332
|
-
### `AssignTogetherOptions`
|
|
333
|
-
|
|
334
|
-
Options for `assignTogether`.
|
|
335
|
-
|
|
336
|
-
**Properties:**
|
|
337
|
-
- `priority?: Priority` — Defaults to `"MANDATORY"`.
|
|
338
|
-
|
|
339
|
-
### `RuleResolveContext`
|
|
340
|
-
|
|
341
|
-
Context passed to a rule's resolve function during compilation.
|
|
342
|
-
|
|
343
|
-
Contains the declared roles, skills, and member IDs so the resolver
|
|
344
|
-
can translate user-facing fields (like `appliesTo`) into internal
|
|
345
|
-
scoping fields.
|
|
346
|
-
|
|
347
|
-
**Properties:**
|
|
348
|
-
- `roles: ReadonlySet<string>`
|
|
349
|
-
- `skills: ReadonlySet<string>`
|
|
350
|
-
- `memberIds: ReadonlySet<string>`
|
|
351
|
-
|
|
352
|
-
### `defineRule`
|
|
353
|
-
|
|
354
|
-
Creates a rule entry for use in `ScheduleConfig.rules`.
|
|
355
|
-
|
|
356
|
-
Built-in rules use the helpers (`maxHoursPerDay`, `timeOff`, etc.).
|
|
357
|
-
Custom rules can use `defineRule` to create entries that plug into the
|
|
358
|
-
same resolution and compilation pipeline.
|
|
359
|
-
|
|
360
|
-
**Parameters:**
|
|
361
|
-
- `name: string` — Rule name. Must match a key in the rule factory registry.
|
|
362
|
-
- `fields: Record<string, unknown>` — Rule-specific configuration fields.
|
|
363
|
-
- `resolve?: (ctx: RuleResolveContext) => Record<string, unknown> & { name: string }` — Optional custom resolver. When omitted, the default
|
|
364
|
-
resolution applies: `appliesTo` is mapped to `roleIds`/`skillIds`/`memberIds`,
|
|
365
|
-
`dates` is renamed to `specificDates`, and all other fields pass through.
|
|
366
|
-
|
|
367
|
-
**Returns:** `RuleEntry`
|
|
368
|
-
|
|
369
|
-
### `maxHoursPerDay`
|
|
370
|
-
|
|
371
|
-
Limits hours per day.
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
maxHoursPerDay(10)
|
|
375
|
-
maxHoursPerDay(4, { appliesTo: "student", dayOfWeek: weekdays })
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
### `maxHoursPerWeek`
|
|
379
|
-
|
|
380
|
-
Limits hours per scheduling week.
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
maxHoursPerWeek(48)
|
|
384
|
-
maxHoursPerWeek(20, { appliesTo: "student" })
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### `minHoursPerDay`
|
|
388
|
-
|
|
389
|
-
Minimum hours when assigned on a day.
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
minHoursPerDay(4)
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
### `minHoursPerWeek`
|
|
396
|
-
|
|
397
|
-
Minimum hours per scheduling week.
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
minHoursPerWeek(20, { priority: "HIGH" })
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
### `maxShiftsPerDay`
|
|
404
|
-
|
|
405
|
-
Maximum distinct shifts per day.
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
maxShiftsPerDay(1)
|
|
409
|
-
maxShiftsPerDay(2, { appliesTo: "student", dayOfWeek: weekend })
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
### `maxConsecutiveDays`
|
|
413
|
-
|
|
414
|
-
Maximum consecutive working days.
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
maxConsecutiveDays(5)
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
### `minConsecutiveDays`
|
|
421
|
-
|
|
422
|
-
Once working, continue for at least this many consecutive days.
|
|
423
|
-
|
|
424
|
-
```typescript
|
|
425
|
-
minConsecutiveDays(2, { priority: "HIGH" })
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
### `minRestBetweenShifts`
|
|
429
|
-
|
|
430
|
-
Minimum rest hours between shifts.
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
minRestBetweenShifts(10)
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
### `preference`
|
|
437
|
-
|
|
438
|
-
Prefer (`"high"`) or avoid (`"low"`) assigning. Requires `appliesTo`.
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
preference("high", { appliesTo: "waiter" })
|
|
442
|
-
preference("low", { appliesTo: "student", dayOfWeek: weekdays })
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
### `preferLocation`
|
|
446
|
-
|
|
447
|
-
Prefer assigning to shifts at a specific location. Requires `appliesTo`.
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
preferLocation("terrace", { appliesTo: "alice" })
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### `timeOff`
|
|
454
|
-
|
|
455
|
-
Block assignments during specified periods.
|
|
456
|
-
Requires at least one time scope (`dayOfWeek`, `dateRange`, `dates`, or `from`/`until`).
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
// Full days off
|
|
460
|
-
timeOff({ appliesTo: "alice", dateRange: { start: "2024-02-01", end: "2024-02-05" } })
|
|
461
|
-
|
|
462
|
-
// Every weekend off
|
|
463
|
-
timeOff({ appliesTo: "mauro", dayOfWeek: weekend })
|
|
464
|
-
|
|
465
|
-
// Wednesday afternoons off
|
|
466
|
-
timeOff({ appliesTo: "student", dayOfWeek: ["wednesday"], from: t(14) })
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### `assignTogether`
|
|
470
|
-
|
|
471
|
-
Members work the same shifts on days they are both assigned.
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
assignTogether(["alice", "bob"])
|
|
475
|
-
assignTogether(["alice", "bob", "charlie"], { priority: "HIGH" })
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
---
|
|
479
|
-
|
|
480
|
-
## Cost Optimization
|
|
481
|
-
|
|
482
|
-
### `OvertimeTier`
|
|
483
|
-
|
|
484
|
-
A single tier in a tiered overtime configuration.
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
{ after: number; factor: number; }
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
### `CostRuleOptions`
|
|
491
|
-
|
|
492
|
-
Options for cost rules.
|
|
493
|
-
|
|
494
|
-
Cost rules are objective terms, not constraints. The `priority` field from
|
|
495
|
-
`RuleOptions` does not apply.
|
|
496
|
-
|
|
497
|
-
**Properties:**
|
|
498
|
-
- `appliesTo?: string | string[]` — Who this rule applies to (role name, skill name, or member ID).
|
|
499
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restrict to specific days of the week.
|
|
500
|
-
- `dateRange?: { start: string; end: string }` — Restrict to a date range.
|
|
501
|
-
- `dates?: string[]` — Restrict to specific dates (YYYY-MM-DD).
|
|
502
|
-
- `recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]]` — Restrict to recurring calendar periods.
|
|
503
|
-
|
|
504
|
-
### `minimizeCost`
|
|
505
|
-
|
|
506
|
-
Tells the solver to minimize total labor cost.
|
|
507
|
-
|
|
508
|
-
Without this rule, cost modifiers only affect post-solve calculation.
|
|
509
|
-
When present, the solver actively prefers cheaper assignments.
|
|
510
|
-
|
|
511
|
-
For hourly members, penalizes each assignment proportionally to cost.
|
|
512
|
-
For salaried members, adds a fixed weekly salary cost when they have
|
|
513
|
-
any assignment that week (zero marginal cost up to contracted hours).
|
|
514
|
-
|
|
515
|
-
Cost modifiers adjust the calculation:
|
|
516
|
-
- `dayMultiplier(factor, opts?)` - multiply base rate on specific days
|
|
517
|
-
- `daySurcharge(amount, opts?)` - flat extra per hour on specific days
|
|
518
|
-
- `timeSurcharge(amount, window, opts?)` - flat extra per hour during a time window
|
|
519
|
-
- `overtimeMultiplier({ after, factor }, opts?)` - weekly overtime multiplier
|
|
520
|
-
- `overtimeSurcharge({ after, amount }, opts?)` - weekly overtime surcharge
|
|
521
|
-
- `dailyOvertimeMultiplier({ after, factor }, opts?)` - daily overtime multiplier
|
|
522
|
-
- `dailyOvertimeSurcharge({ after, amount }, opts?)` - daily overtime surcharge
|
|
523
|
-
- `tieredOvertimeMultiplier(tiers, opts?)` - multiple overtime thresholds
|
|
524
|
-
|
|
525
|
-
```ts
|
|
526
|
-
minimizeCost()
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### `dayMultiplier`
|
|
530
|
-
|
|
531
|
-
Multiplies the base rate for assignments on specified days.
|
|
532
|
-
|
|
533
|
-
The base cost (1x) is already counted by `minimizeCost`;
|
|
534
|
-
this rule adds only the extra portion above 1x.
|
|
535
|
-
|
|
536
|
-
Weekend multiplier
|
|
537
|
-
```typescript
|
|
538
|
-
dayMultiplier(1.5, { dayOfWeek: weekend })
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
### `daySurcharge`
|
|
542
|
-
|
|
543
|
-
Adds a flat extra amount per hour for assignments on specified days.
|
|
544
|
-
|
|
545
|
-
The surcharge is independent of the member's base rate.
|
|
546
|
-
|
|
547
|
-
Weekend surcharge
|
|
548
|
-
```typescript
|
|
549
|
-
daySurcharge(500, { dayOfWeek: weekend })
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
### `timeSurcharge`
|
|
553
|
-
|
|
554
|
-
Adds a flat surcharge per hour for the portion of a shift that overlaps a time-of-day window.
|
|
555
|
-
|
|
556
|
-
The window supports overnight spans (e.g., 22:00-06:00). The surcharge
|
|
557
|
-
is independent of the member's base rate.
|
|
558
|
-
|
|
559
|
-
Night differential
|
|
560
|
-
```typescript
|
|
561
|
-
timeSurcharge(200, { from: t(22), until: t(6) })
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
**Parameters:**
|
|
565
|
-
- `amountPerHour: number` — Flat surcharge per hour in smallest currency unit
|
|
566
|
-
- `window: { from: TimeOfDay; until: TimeOfDay }` — Time-of-day window
|
|
567
|
-
- `opts?: CostRuleOptions` — Entity and time scoping
|
|
568
|
-
|
|
569
|
-
**Returns:** `RuleEntry`
|
|
570
|
-
|
|
571
|
-
### `overtimeMultiplier`
|
|
572
|
-
|
|
573
|
-
Applies a multiplier to hours beyond a weekly threshold.
|
|
574
|
-
|
|
575
|
-
Only the extra portion above 1x is added (the base cost is already
|
|
576
|
-
counted by `minimizeCost`).
|
|
577
|
-
|
|
578
|
-
```typescript
|
|
579
|
-
overtimeMultiplier({ after: 40, factor: 1.5 })
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
### `overtimeSurcharge`
|
|
583
|
-
|
|
584
|
-
Adds a flat surcharge per hour beyond a weekly threshold.
|
|
585
|
-
|
|
586
|
-
The surcharge is independent of the member's base rate.
|
|
587
|
-
|
|
588
|
-
```typescript
|
|
589
|
-
overtimeSurcharge({ after: 40, amount: 1000 })
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
### `dailyOvertimeMultiplier`
|
|
593
|
-
|
|
594
|
-
Applies a multiplier to hours beyond a daily threshold.
|
|
595
|
-
|
|
596
|
-
Only the extra portion above 1x is added (the base cost is already
|
|
597
|
-
counted by `minimizeCost`).
|
|
598
|
-
|
|
599
|
-
```typescript
|
|
600
|
-
dailyOvertimeMultiplier({ after: 8, factor: 1.5 })
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
### `dailyOvertimeSurcharge`
|
|
604
|
-
|
|
605
|
-
Adds a flat surcharge per hour beyond a daily threshold.
|
|
606
|
-
|
|
607
|
-
The surcharge is independent of the member's base rate.
|
|
608
|
-
|
|
609
|
-
```typescript
|
|
610
|
-
dailyOvertimeSurcharge({ after: 8, amount: 500 })
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### `tieredOvertimeMultiplier`
|
|
614
|
-
|
|
615
|
-
Applies multiple overtime thresholds with increasing multipliers.
|
|
616
|
-
|
|
617
|
-
Each tier applies only to the hours between its threshold and the next.
|
|
618
|
-
Tiers must be sorted by threshold ascending.
|
|
619
|
-
|
|
620
|
-
```typescript
|
|
621
|
-
// Hours 0-40: base rate
|
|
622
|
-
// Hours 40-48: 1.5x
|
|
623
|
-
// Hours 48+: 2.0x
|
|
624
|
-
tieredOvertimeMultiplier([
|
|
625
|
-
{ after: 40, factor: 1.5 },
|
|
626
|
-
{ after: 48, factor: 2.0 },
|
|
627
|
-
])
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
---
|
|
631
|
-
|
|
632
|
-
## Supporting Types
|
|
633
|
-
|
|
634
|
-
### `DayOfWeek`
|
|
635
|
-
|
|
636
|
-
Day of the week identifier.
|
|
637
|
-
|
|
638
|
-
```typescript
|
|
639
|
-
"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
### `TimeOfDay`
|
|
643
|
-
|
|
644
|
-
Time of day (24-hour format).
|
|
645
|
-
|
|
646
|
-
**Properties:**
|
|
647
|
-
- `hours: number`
|
|
648
|
-
- `minutes: number`
|
|
649
|
-
|
|
650
|
-
### `SchedulingPeriod`
|
|
651
|
-
|
|
652
|
-
Defines a scheduling period as a date range with optional filters.
|
|
653
|
-
|
|
654
|
-
The `dateRange` specifies the overall scheduling window. Use `dayOfWeek`
|
|
655
|
-
and/or `dates` to narrow which days within the range are included.
|
|
656
|
-
Filters compose: a day must pass all specified filters to be included.
|
|
657
|
-
|
|
658
|
-
All days in a week
|
|
659
|
-
```typescript
|
|
660
|
-
const period: SchedulingPeriod = {
|
|
661
|
-
dateRange: { start: '2025-02-03', end: '2025-02-09' },
|
|
662
|
-
};
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
**Properties:**
|
|
666
|
-
- `dateRange: { start: string; end: string }` — The overall scheduling window (start and end are inclusive).
|
|
667
|
-
Dates should be in YYYY-MM-DD format.
|
|
668
|
-
- `dayOfWeek?: readonly DayOfWeek[]` — Include only these days of the week.
|
|
669
|
-
If omitted, all days of the week are included.
|
|
670
|
-
- `dates?: string[]` — Include only these specific dates (YYYY-MM-DD) within the range.
|
|
671
|
-
If omitted, all dates in the range are included (subject to dayOfWeek filter).
|
|
672
|
-
|
|
673
|
-
### `HourlyPay`
|
|
674
|
-
|
|
675
|
-
Pay per hour in the caller's smallest currency unit (e.g., pence, cents).
|
|
676
|
-
|
|
677
|
-
**Properties:**
|
|
678
|
-
- `hourlyRate: number` — Pay per hour in smallest currency unit.
|
|
679
|
-
|
|
680
|
-
### `SalariedPay`
|
|
681
|
-
|
|
682
|
-
Annual salary with contracted weekly hours.
|
|
683
|
-
|
|
684
|
-
The solver treats salaried members as having a fixed weekly cost
|
|
685
|
-
(`annual / 52`) that is incurred once they work any shift in a week.
|
|
686
|
-
Additional shifts within the same week have zero marginal cost.
|
|
687
|
-
|
|
688
|
-
Note: overtime multiplier rules apply only to hourly members.
|
|
689
|
-
Overtime surcharge rules apply to all members regardless of pay type.
|
|
690
|
-
|
|
691
|
-
**Properties:**
|
|
692
|
-
- `annual: number` — Annual salary in smallest currency unit.
|
|
693
|
-
- `hoursPerWeek: number` — Contracted hours per week. Reserved for future overtime support.
|
|
694
|
-
|
|
695
|
-
### `Priority`
|
|
696
|
-
|
|
697
|
-
How strictly the solver enforces a rule.
|
|
698
|
-
|
|
699
|
-
- `"LOW"`, `"MEDIUM"`, `"HIGH"`: soft constraints with increasing penalty for violations
|
|
700
|
-
- `"MANDATORY"`: hard constraint; the solver will not produce a solution that violates it
|
|
701
|
-
|
|
702
|
-
```typescript
|
|
703
|
-
"LOW" | "MEDIUM" | "HIGH" | "MANDATORY"
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### `SchedulingMember`
|
|
707
|
-
|
|
708
|
-
A team member available for scheduling.
|
|
709
|
-
|
|
710
|
-
Members are assigned to shift patterns by the solver based on
|
|
711
|
-
coverage requirements, rules, and constraints.
|
|
712
|
-
|
|
713
|
-
**Properties:**
|
|
714
|
-
- `id: string` — Unique identifier for this member. Must not contain colons.
|
|
715
|
-
- `roleIds: string[]` — Role IDs this member can fill (e.g. "nurse", "doctor").
|
|
716
|
-
- `skillIds?: string[]` — Skill IDs this member has (e.g. "charge_nurse", "forklift").
|
|
717
|
-
- `pay?: HourlyPay | SalariedPay` — Base pay. Required when cost rules are used.
|
|
718
|
-
|
|
719
|
-
### `ShiftPattern`
|
|
720
|
-
|
|
721
|
-
A shift pattern defines WHEN people can work: the time slots available for assignment.
|
|
722
|
-
|
|
723
|
-
Shift patterns are templates that repeat across all scheduling days. The solver assigns
|
|
724
|
-
team members to these patterns based on coverage requirements and constraints.
|
|
725
|
-
|
|
726
|
-
// Simple setup: one shift type, anyone can work it
|
|
727
|
-
const patterns: ShiftPattern[] = [
|
|
728
|
-
{ id: "day", startTime: { hours: 9 }, endTime: { hours: 17 } }
|
|
729
|
-
];
|
|
730
|
-
|
|
731
|
-
**Properties:**
|
|
732
|
-
- `id: string` — Unique identifier for this shift pattern.
|
|
733
|
-
Used in assignments and rule configurations.
|
|
734
|
-
- `roleIds?: [string, ...string[]]` — Restricts who can be assigned to this shift based on their role IDs.
|
|
735
|
-
|
|
736
|
-
- If omitted: anyone can work this shift
|
|
737
|
-
- If provided: only team members whose roles overlap with this list can be assigned
|
|
738
|
-
|
|
739
|
-
Most venues have the same shifts for everyone and don't need this.
|
|
740
|
-
Use it when different roles have different schedules (e.g., kitchen staff starts
|
|
741
|
-
earlier than floor staff).
|
|
742
|
-
- `dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]]` — Restricts which days of the week this shift pattern can be used.
|
|
743
|
-
|
|
744
|
-
- If omitted: shift can be used on any day
|
|
745
|
-
- If provided: shift can only be assigned on the specified days
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
// Saturday-only short shift
|
|
749
|
-
{ id: "saturday_shift", startTime: t(9), endTime: t(14), dayOfWeek: ["saturday"] }
|
|
750
|
-
|
|
751
|
-
// Weekday-only full shift
|
|
752
|
-
{ id: "full_shift", startTime: t(9), endTime: t(18), dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"] }
|
|
753
|
-
```
|
|
754
|
-
- `locationId?: string` — Physical location where this shift takes place.
|
|
755
|
-
Used for multi-location scheduling and location-based constraints.
|
|
756
|
-
- `startTime: TimeOfDay` — When the shift starts (e.g., `{ hours: 9, minutes: 0 }` for 9:00 AM)
|
|
757
|
-
- `endTime: TimeOfDay` — When the shift ends (e.g., `{ hours: 17, minutes: 30 }` for 5:30 PM)
|
|
758
|
-
|