dabke 0.80.0 → 0.81.1
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 +53 -0
- package/README.md +108 -299
- package/dist/cpsat/cost.d.ts +65 -0
- package/dist/cpsat/cost.d.ts.map +1 -0
- package/dist/cpsat/cost.js +77 -0
- package/dist/cpsat/cost.js.map +1 -0
- package/dist/cpsat/model-builder.d.ts +51 -17
- package/dist/cpsat/model-builder.d.ts.map +1 -1
- package/dist/cpsat/model-builder.js +76 -76
- package/dist/cpsat/model-builder.js.map +1 -1
- package/dist/cpsat/response.d.ts +11 -11
- package/dist/cpsat/response.d.ts.map +1 -1
- package/dist/cpsat/response.js +10 -10
- package/dist/cpsat/response.js.map +1 -1
- package/dist/cpsat/rules/assign-together.d.ts +4 -4
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
- package/dist/cpsat/rules/assign-together.js +17 -21
- package/dist/cpsat/rules/assign-together.js.map +1 -1
- package/dist/cpsat/rules/assignment-priority.d.ts +38 -0
- package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -0
- package/dist/cpsat/rules/{employee-assignment-priority.js → assignment-priority.js} +15 -15
- package/dist/cpsat/rules/assignment-priority.js.map +1 -0
- package/dist/cpsat/rules/day-cost-multiplier.d.ts +19 -0
- package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -0
- package/dist/cpsat/rules/day-cost-multiplier.js +104 -0
- package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -0
- package/dist/cpsat/rules/day-cost-surcharge.d.ts +19 -0
- package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -0
- package/dist/cpsat/rules/day-cost-surcharge.js +84 -0
- package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -0
- package/dist/cpsat/rules/index.d.ts +10 -1
- package/dist/cpsat/rules/index.d.ts.map +1 -1
- package/dist/cpsat/rules/index.js +10 -1
- package/dist/cpsat/rules/index.js.map +1 -1
- package/dist/cpsat/rules/location-preference.d.ts +4 -4
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
- package/dist/cpsat/rules/location-preference.js +6 -11
- package/dist/cpsat/rules/location-preference.js.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.d.ts +3 -3
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/max-consecutive-days.js +5 -10
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/max-hours-day.d.ts +3 -3
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-day.js +6 -11
- package/dist/cpsat/rules/max-hours-day.js.map +1 -1
- package/dist/cpsat/rules/max-hours-week.d.ts +3 -3
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/max-hours-week.js +6 -11
- package/dist/cpsat/rules/max-hours-week.js.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.d.ts +3 -3
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
- package/dist/cpsat/rules/max-shifts-day.js +6 -11
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.d.ts +3 -3
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
- package/dist/cpsat/rules/min-consecutive-days.js +5 -10
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
- package/dist/cpsat/rules/min-hours-day.d.ts +3 -3
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-day.js +5 -10
- package/dist/cpsat/rules/min-hours-day.js.map +1 -1
- package/dist/cpsat/rules/min-hours-week.d.ts +3 -3
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
- package/dist/cpsat/rules/min-hours-week.js +5 -10
- package/dist/cpsat/rules/min-hours-week.js.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +3 -3
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
- package/dist/cpsat/rules/min-rest-between-shifts.js +5 -10
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
- package/dist/cpsat/rules/minimize-cost.d.ts +23 -0
- package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -0
- package/dist/cpsat/rules/minimize-cost.js +206 -0
- package/dist/cpsat/rules/minimize-cost.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +16 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.js +142 -0
- package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -0
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +17 -0
- package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -0
- package/dist/cpsat/rules/overtime-daily-surcharge.js +120 -0
- package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -0
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +37 -0
- package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -0
- package/dist/cpsat/rules/overtime-tiered-multiplier.js +243 -0
- package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -0
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +23 -0
- package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -0
- package/dist/cpsat/rules/overtime-weekly-multiplier.js +178 -0
- package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -0
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +18 -0
- package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -0
- package/dist/cpsat/rules/overtime-weekly-surcharge.js +140 -0
- package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts.map +1 -1
- package/dist/cpsat/rules/registry.js +11 -2
- package/dist/cpsat/rules/registry.js.map +1 -1
- package/dist/cpsat/rules/resolver.d.ts +5 -10
- package/dist/cpsat/rules/resolver.d.ts.map +1 -1
- package/dist/cpsat/rules/resolver.js +33 -27
- package/dist/cpsat/rules/resolver.js.map +1 -1
- package/dist/cpsat/rules/rules.types.d.ts +31 -5
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.d.ts +30 -23
- package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
- package/dist/cpsat/rules/scope.types.js +33 -21
- package/dist/cpsat/rules/scope.types.js.map +1 -1
- package/dist/cpsat/rules/time-cost-surcharge.d.ts +29 -0
- package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -0
- package/dist/cpsat/rules/time-cost-surcharge.js +124 -0
- package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -0
- package/dist/cpsat/rules/time-off.d.ts +7 -7
- package/dist/cpsat/rules/time-off.d.ts.map +1 -1
- package/dist/cpsat/rules/time-off.js +14 -20
- package/dist/cpsat/rules/time-off.js.map +1 -1
- package/dist/cpsat/rules.d.ts +2 -2
- package/dist/cpsat/rules.d.ts.map +1 -1
- package/dist/cpsat/rules.js +2 -2
- package/dist/cpsat/rules.js.map +1 -1
- package/dist/cpsat/semantic-time.d.ts +139 -30
- package/dist/cpsat/semantic-time.d.ts.map +1 -1
- package/dist/cpsat/semantic-time.js +91 -33
- package/dist/cpsat/semantic-time.js.map +1 -1
- package/dist/cpsat/types.d.ts +55 -34
- package/dist/cpsat/types.d.ts.map +1 -1
- package/dist/cpsat/utils.d.ts +16 -2
- package/dist/cpsat/utils.d.ts.map +1 -1
- package/dist/cpsat/utils.js +27 -2
- package/dist/cpsat/utils.js.map +1 -1
- package/dist/cpsat/validation-reporter.js +13 -13
- package/dist/cpsat/validation-reporter.js.map +1 -1
- package/dist/cpsat/validation.types.d.ts +10 -10
- package/dist/cpsat/validation.types.d.ts.map +1 -1
- package/dist/datetime.utils.d.ts +4 -4
- package/dist/datetime.utils.js +6 -6
- package/dist/datetime.utils.js.map +1 -1
- package/dist/index.d.ts +80 -69
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -56
- package/dist/index.js.map +1 -1
- package/dist/llms.d.ts +1 -4
- package/dist/llms.d.ts.map +1 -1
- package/dist/llms.js +2 -7
- package/dist/llms.js.map +1 -1
- package/dist/schedule.d.ts +724 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +899 -0
- package/dist/schedule.js.map +1 -0
- package/dist/testing/solver-container.d.ts.map +1 -1
- package/dist/testing/solver-container.js +5 -0
- package/dist/testing/solver-container.js.map +1 -1
- package/dist/types.d.ts +11 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +21 -21
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +30 -30
- package/dist/validation.js.map +1 -1
- package/llms.txt +629 -2145
- package/package.json +1 -1
- package/src/cpsat/cost.ts +128 -0
- package/src/cpsat/model-builder.ts +124 -84
- package/src/cpsat/response.ts +16 -16
- package/src/cpsat/rules/assign-together.ts +18 -22
- package/src/cpsat/rules/{employee-assignment-priority.ts → assignment-priority.ts} +17 -19
- package/src/cpsat/rules/day-cost-multiplier.ts +124 -0
- package/src/cpsat/rules/day-cost-surcharge.ts +106 -0
- package/src/cpsat/rules/index.ts +10 -1
- package/src/cpsat/rules/location-preference.ts +12 -12
- package/src/cpsat/rules/max-consecutive-days.ts +11 -11
- package/src/cpsat/rules/max-hours-day.ts +8 -12
- package/src/cpsat/rules/max-hours-week.ts +8 -12
- package/src/cpsat/rules/max-shifts-day.ts +8 -12
- package/src/cpsat/rules/min-consecutive-days.ts +11 -11
- package/src/cpsat/rules/min-hours-day.ts +11 -11
- package/src/cpsat/rules/min-hours-week.ts +11 -11
- package/src/cpsat/rules/min-rest-between-shifts.ts +11 -11
- package/src/cpsat/rules/minimize-cost.ts +250 -0
- package/src/cpsat/rules/overtime-daily-multiplier.ts +181 -0
- package/src/cpsat/rules/overtime-daily-surcharge.ts +161 -0
- package/src/cpsat/rules/overtime-tiered-multiplier.ts +323 -0
- package/src/cpsat/rules/overtime-weekly-multiplier.ts +226 -0
- package/src/cpsat/rules/overtime-weekly-surcharge.ts +190 -0
- package/src/cpsat/rules/registry.ts +20 -2
- package/src/cpsat/rules/resolver.ts +39 -36
- package/src/cpsat/rules/rules.types.ts +29 -5
- package/src/cpsat/rules/scope.types.ts +46 -32
- package/src/cpsat/rules/time-cost-surcharge.ts +169 -0
- package/src/cpsat/rules/time-off.ts +18 -24
- package/src/cpsat/rules.ts +2 -2
- package/src/cpsat/semantic-time.ts +259 -53
- package/src/cpsat/types.ts +57 -35
- package/src/cpsat/utils.ts +27 -2
- package/src/cpsat/validation-reporter.ts +16 -16
- package/src/cpsat/validation.types.ts +10 -10
- package/src/datetime.utils.ts +6 -6
- package/src/index.ts +127 -145
- package/src/llms.ts +2 -8
- package/src/schedule.ts +1419 -0
- package/src/testing/solver-container.ts +7 -0
- package/src/types.ts +11 -21
- package/src/validation.ts +31 -31
- package/dist/cpsat/rules/employee-assignment-priority.d.ts +0 -38
- package/dist/cpsat/rules/employee-assignment-priority.d.ts.map +0 -1
- package/dist/cpsat/rules/employee-assignment-priority.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
This changelog was generated from the git history of the project when it was
|
|
6
6
|
named `scheduling-core`, prior to the rename to `dabke` in v0.78.0.
|
|
7
7
|
|
|
8
|
+
## 0.81.1 (2026-02-20)
|
|
9
|
+
|
|
10
|
+
### Documentation
|
|
11
|
+
|
|
12
|
+
- Rewrite README for the v2 `defineSchedule` API with a verified cafe scheduling example.
|
|
13
|
+
|
|
14
|
+
## 0.81.0 (2026-02-19)
|
|
15
|
+
|
|
16
|
+
### Breaking Changes
|
|
17
|
+
|
|
18
|
+
- **Rename employee to member**: `SchedulingEmployee` is now `SchedulingMember`,
|
|
19
|
+
`employeeIds` scope fields are now `memberIds`, and `Employee` type alias is removed.
|
|
20
|
+
The `employee-assignment-priority` rule is renamed to `assignment-priority`.
|
|
21
|
+
- **Rename roleIds/skillIds to roles/skills**: on `SchedulingMember`, `ShiftPattern`,
|
|
22
|
+
and all coverage requirement types. Scope fields on rules remain `roleIds`/`skillIds`.
|
|
23
|
+
- **Merge ShiftPatternDef into ShiftPattern**: the separate `ShiftPatternDef` type is
|
|
24
|
+
removed. `ShiftPattern` now directly includes `startTime`, `endTime`, and optional
|
|
25
|
+
`roles`/`skills` fields.
|
|
26
|
+
- **Flatten CpsatRuleConfigEntry**: rule config entries change from
|
|
27
|
+
`{ name, config: { ... } }` to `{ name, ...config }`. Config fields sit at the same
|
|
28
|
+
level as `name`. The type is now a distributive union preventing mismatched name/config
|
|
29
|
+
pairings at compile time.
|
|
30
|
+
- **Rename daysOfWeek to dayOfWeek**: on `SchedulingPeriod`, `SemanticTimeVariant`,
|
|
31
|
+
and coverage requirement types.
|
|
32
|
+
- **Remove v1-only exports**: `Employee`, `ShiftPatternDef`, `defineSemanticTimes`,
|
|
33
|
+
and related types removed from the public API. Use `defineSchedule` instead.
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
- **defineSchedule API**: new primary API with composable factory functions (`time`,
|
|
38
|
+
`cover`, `shift`, rule helpers like `maxHoursPerWeek`, `timeOff`, etc.) for building
|
|
39
|
+
a complete scheduling configuration declaratively.
|
|
40
|
+
- **Cost optimization**: base pay tracking (`HourlyPay`, `SalariedPay` on members),
|
|
41
|
+
cost minimization rule (`minimize-cost`), day/time cost surcharges and multipliers,
|
|
42
|
+
and post-solve cost calculation.
|
|
43
|
+
- **Overtime rules**: daily and weekly overtime multipliers and surcharges,
|
|
44
|
+
tiered overtime multiplier for graduated rates.
|
|
45
|
+
- **Variant coverage**: day-specific count overrides on coverage requirements
|
|
46
|
+
via `countByDay` on the `cover()` helper.
|
|
47
|
+
|
|
48
|
+
### Fixes
|
|
49
|
+
|
|
50
|
+
- Clean up stale solver containers on startup in the test harness.
|
|
51
|
+
- Strip all entity-scope fields consistently during scope resolution.
|
|
52
|
+
|
|
53
|
+
### Improvements
|
|
54
|
+
|
|
55
|
+
- Simplify rule translation layer; rule-specific knowledge (schemas, defaults,
|
|
56
|
+
validation, cost) is co-located in each rule file.
|
|
57
|
+
- Rewrite llms.txt generator as whitelist-based, aligned with the v2 API.
|
|
58
|
+
- Enrich TSDoc throughout: schedule.ts, semantic-time.ts, coverage types,
|
|
59
|
+
and package-level documentation updated for the defineSchedule API.
|
|
60
|
+
|
|
8
61
|
## 0.80.0 (2026-02-14)
|
|
9
62
|
|
|
10
63
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -4,378 +4,187 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/christianklotz/dabke/actions/workflows/ci.yml)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
TypeScript scheduling library powered by constraint programming.
|
|
8
8
|
|
|
9
|
-
Define
|
|
9
|
+
Define teams, shifts, coverage, and rules declaratively. dabke compiles them into a CP-SAT model and solves for an optimized schedule.
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
defineSchedule,
|
|
14
|
+
t,
|
|
15
|
+
time,
|
|
16
|
+
cover,
|
|
17
|
+
shift,
|
|
18
|
+
maxHoursPerWeek,
|
|
19
|
+
minRestBetweenShifts,
|
|
20
|
+
timeOff,
|
|
21
|
+
minimizeCost,
|
|
22
|
+
weekdays,
|
|
23
|
+
weekend,
|
|
24
|
+
} from "dabke";
|
|
25
|
+
|
|
26
|
+
const schedule = defineSchedule({
|
|
27
|
+
roles: ["barista", "server"],
|
|
28
|
+
|
|
29
|
+
// Times: WHEN you need people (named periods for coverage + rules)
|
|
30
|
+
times: {
|
|
31
|
+
breakfast: time({ startTime: t(7), endTime: t(10) }),
|
|
32
|
+
lunch_rush: time(
|
|
33
|
+
{ startTime: t(11, 30), endTime: t(14) },
|
|
34
|
+
{ startTime: t(11), endTime: t(15), dayOfWeek: weekend },
|
|
35
|
+
),
|
|
36
|
+
closing: time({ startTime: t(20), endTime: t(22) }),
|
|
37
|
+
},
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
39
|
+
// Coverage: HOW MANY people you need during each time
|
|
40
|
+
coverage: [
|
|
41
|
+
cover("breakfast", "barista", 1),
|
|
42
|
+
cover("lunch_rush", "barista", 2, { dayOfWeek: weekdays }),
|
|
43
|
+
cover("lunch_rush", "barista", 3, { dayOfWeek: weekend }),
|
|
44
|
+
cover("lunch_rush", "server", 1),
|
|
45
|
+
cover("closing", "server", 1),
|
|
18
46
|
],
|
|
47
|
+
|
|
48
|
+
// Shifts: WHEN people CAN work (the actual time slots)
|
|
49
|
+
// An opener covers breakfast + lunch; a closer covers lunch + closing
|
|
19
50
|
shiftPatterns: [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
coverage: [
|
|
24
|
-
{
|
|
25
|
-
day: "2026-02-09",
|
|
26
|
-
startTime: { hours: 8 },
|
|
27
|
-
endTime: { hours: 16 },
|
|
28
|
-
roleIds: ["server"],
|
|
29
|
-
targetCount: 1,
|
|
30
|
-
priority: "MANDATORY",
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
day: "2026-02-09",
|
|
34
|
-
startTime: { hours: 16 },
|
|
35
|
-
endTime: { hours: 23 },
|
|
36
|
-
roleIds: ["server"],
|
|
37
|
-
targetCount: 1,
|
|
38
|
-
priority: "MANDATORY",
|
|
39
|
-
},
|
|
51
|
+
shift("opener", t(6), t(14)),
|
|
52
|
+
shift("mid", t(10), t(18)),
|
|
53
|
+
shift("closer", t(14), t(22)),
|
|
40
54
|
],
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
|
|
55
|
+
|
|
56
|
+
rules: [
|
|
57
|
+
maxHoursPerWeek(40),
|
|
58
|
+
minRestBetweenShifts(11),
|
|
59
|
+
timeOff({ appliesTo: "alice", dayOfWeek: weekend }),
|
|
60
|
+
minimizeCost(),
|
|
47
61
|
],
|
|
48
62
|
});
|
|
49
|
-
|
|
50
|
-
const { request } = builder.compile();
|
|
51
|
-
const solver = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
52
|
-
const response = await solver.solve(request);
|
|
53
63
|
```
|
|
54
64
|
|
|
55
|
-
##
|
|
56
|
-
|
|
57
|
-
Staff scheduling with constraint programming is dominated by Python ([OR-Tools](https://developers.google.com/optimization)) and Java ([Timefold](https://timefold.ai/)). If you're building in TypeScript, your options have been: call a Python service and figure out the model yourself, or write scheduling heuristics by hand.
|
|
58
|
-
|
|
59
|
-
dabke gives you a **TypeScript-native API** for expressing scheduling problems declaratively — employees, shifts, coverage requirements, and rules — and compiles them into a CP-SAT model solved by OR-Tools. You describe _what_ you need, not _how_ to solve it.
|
|
60
|
-
|
|
61
|
-
**Key differences from rolling your own:**
|
|
62
|
-
|
|
63
|
-
- **Declarative rules** — express constraints like "max 8 hours/day" or "11 hours rest between shifts" as config, not code
|
|
64
|
-
- **Semantic time** — define named periods ("lunch_rush", "closing") that vary by day of week
|
|
65
|
-
- **Soft and hard constraints** — some rules are mandatory, others are preferences the solver optimizes for
|
|
66
|
-
- **Scoped rules** — apply constraints globally, per person, per role, per skill, or during specific time periods
|
|
67
|
-
- **Validation** — detailed reporting on coverage gaps and rule violations before and after solving
|
|
68
|
-
|
|
69
|
-
## Install
|
|
65
|
+
## Quick Start
|
|
70
66
|
|
|
71
67
|
```bash
|
|
72
68
|
npm install dabke
|
|
73
69
|
```
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### 1. Start the solver
|
|
78
|
-
|
|
79
|
-
dabke compiles scheduling problems into a constraint model. You need a CP-SAT solver to solve it. A ready-to-use solver is included:
|
|
71
|
+
dabke compiles scheduling problems into a constraint model. You need a CP-SAT solver to find the solution:
|
|
80
72
|
|
|
81
73
|
```bash
|
|
82
|
-
# Pull the solver image
|
|
83
74
|
docker pull christianklotz/dabke-solver
|
|
84
|
-
|
|
85
|
-
# Start it
|
|
86
75
|
docker run -p 8080:8080 christianklotz/dabke-solver
|
|
87
76
|
```
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
cd node_modules/dabke/solver
|
|
93
|
-
docker build -t dabke-solver .
|
|
94
|
-
docker run -p 8080:8080 dabke-solver
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 2. Build and solve a schedule
|
|
78
|
+
Once you have a schedule definition, create a config with runtime data and solve:
|
|
98
79
|
|
|
99
80
|
```typescript
|
|
100
81
|
import { ModelBuilder, HttpSolverClient, parseSolverResponse, resolveAssignments } from "dabke";
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
const employees = [
|
|
104
|
-
{ id: "alice", roleIds: ["server"] },
|
|
105
|
-
{ id: "bob", roleIds: ["server"] },
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
// Define shift patterns
|
|
109
|
-
const shiftPatterns = [
|
|
110
|
-
{ id: "morning", startTime: { hours: 8 }, endTime: { hours: 16 } },
|
|
111
|
-
{ id: "evening", startTime: { hours: 16 }, endTime: { hours: 23 } },
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
// Define coverage requirements
|
|
115
|
-
const coverage = [
|
|
116
|
-
{
|
|
117
|
-
day: "2026-02-09",
|
|
118
|
-
startTime: { hours: 8 },
|
|
119
|
-
endTime: { hours: 16 },
|
|
120
|
-
roleIds: ["server"] as [string],
|
|
121
|
-
targetCount: 1,
|
|
122
|
-
priority: "MANDATORY" as const,
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
day: "2026-02-09",
|
|
126
|
-
startTime: { hours: 16 },
|
|
127
|
-
endTime: { hours: 23 },
|
|
128
|
-
roleIds: ["server"] as [string],
|
|
129
|
-
targetCount: 1,
|
|
130
|
-
priority: "MANDATORY" as const,
|
|
131
|
-
},
|
|
132
|
-
];
|
|
133
|
-
|
|
134
|
-
// Build the model
|
|
135
|
-
const builder = new ModelBuilder({
|
|
136
|
-
employees,
|
|
137
|
-
shiftPatterns,
|
|
138
|
-
coverage,
|
|
83
|
+
const config = schedule.createSchedulerConfig({
|
|
139
84
|
schedulingPeriod: {
|
|
140
|
-
dateRange: { start: "2026-02-09", end: "2026-02-
|
|
85
|
+
dateRange: { start: "2026-02-09", end: "2026-02-15" },
|
|
141
86
|
},
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
{
|
|
87
|
+
members: [
|
|
88
|
+
{ id: "alice", roles: ["barista", "server"], pay: { hourlyRate: 1500 } },
|
|
89
|
+
{ id: "bob", roles: ["barista"], pay: { hourlyRate: 1200 } },
|
|
90
|
+
{ id: "carol", roles: ["barista", "server"], pay: { hourlyRate: 1400 } },
|
|
91
|
+
{ id: "dave", roles: ["barista", "server"], pay: { hourlyRate: 1200 } },
|
|
92
|
+
{ id: "eve", roles: ["barista", "server"], pay: { hourlyRate: 1300 } },
|
|
145
93
|
],
|
|
146
94
|
});
|
|
147
95
|
|
|
148
|
-
const
|
|
96
|
+
const builder = new ModelBuilder(config);
|
|
97
|
+
const { request, canSolve } = builder.compile();
|
|
149
98
|
|
|
150
|
-
if (!canSolve) {
|
|
151
|
-
console.error("Cannot solve:", validation.errors);
|
|
152
|
-
process.exit(1);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Solve
|
|
156
99
|
const solver = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
157
100
|
const response = await solver.solve(request);
|
|
158
101
|
const result = parseSolverResponse(response);
|
|
159
|
-
|
|
160
|
-
if (result.status === "OPTIMAL" || result.status === "FEASIBLE") {
|
|
161
|
-
const shifts = resolveAssignments(result.assignments, shiftPatterns);
|
|
162
|
-
for (const shift of shifts) {
|
|
163
|
-
console.log(
|
|
164
|
-
`${shift.employeeId}: ${shift.day} ${shift.startTime.hours}:00–${shift.endTime.hours}:00`,
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
102
|
+
const shifts = resolveAssignments(result.assignments, config.shiftPatterns);
|
|
168
103
|
```
|
|
169
104
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Define named time periods that can vary by day, then write coverage requirements in business terms:
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
import { defineSemanticTimes } from "dabke";
|
|
176
|
-
|
|
177
|
-
const times = defineSemanticTimes({
|
|
178
|
-
lunch: [
|
|
179
|
-
{
|
|
180
|
-
startTime: { hours: 11, minutes: 30 },
|
|
181
|
-
endTime: { hours: 14 },
|
|
182
|
-
days: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
startTime: { hours: 12 },
|
|
186
|
-
endTime: { hours: 15 },
|
|
187
|
-
days: ["saturday", "sunday"],
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
closing: { startTime: { hours: 21 }, endTime: { hours: 23 } },
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const coverage = times.coverage([
|
|
194
|
-
{ semanticTime: "lunch", roleIds: ["server"], targetCount: 3, priority: "MANDATORY" },
|
|
195
|
-
{ semanticTime: "closing", roleIds: ["server"], targetCount: 1, priority: "HIGH" },
|
|
196
|
-
]);
|
|
105
|
+
---
|
|
197
106
|
|
|
198
|
-
|
|
199
|
-
const days = ["2026-02-09", "2026-02-10", "2026-02-11"];
|
|
200
|
-
const resolved = times.resolve(coverage, days);
|
|
201
|
-
```
|
|
107
|
+
## Key Concepts
|
|
202
108
|
|
|
203
|
-
|
|
109
|
+
**Times vs shift patterns.** These are two distinct things. Times are named periods you need coverage for ("breakfast", "lunch_rush"). Shift patterns are the time slots people actually work ("opener 6-14", "closer 14-22"). They don't need to match. The solver assigns members to shift patterns whose hours overlap with times to satisfy coverage.
|
|
204
110
|
|
|
205
|
-
|
|
206
|
-
| ------------------------------ | ----------------------------------------- |
|
|
207
|
-
| `max-hours-day` | Max hours per person per day |
|
|
208
|
-
| `max-hours-week` | Max hours per person per week |
|
|
209
|
-
| `min-hours-day` | Min hours per person per day |
|
|
210
|
-
| `min-hours-week` | Min hours per person per week |
|
|
211
|
-
| `max-shifts-day` | Max shift assignments per day |
|
|
212
|
-
| `max-consecutive-days` | Max consecutive working days |
|
|
213
|
-
| `min-consecutive-days` | Min consecutive working days |
|
|
214
|
-
| `min-rest-between-shifts` | Min rest hours between shifts |
|
|
215
|
-
| `time-off` | Block assignments during periods |
|
|
216
|
-
| `employee-assignment-priority` | Prefer or avoid assigning team members |
|
|
217
|
-
| `assign-together` | Keep team members on the same shifts |
|
|
218
|
-
| `location-preference` | Prefer team members at specific locations |
|
|
111
|
+
**Coverage.** How many people with which roles you need during each time. Coverage can vary by day of week or specific dates using scoping options or the variant form of `cover()`.
|
|
219
112
|
|
|
220
|
-
|
|
113
|
+
**Rules.** Business constraints expressed as function calls. Rules with `priority: "MANDATORY"` (the default) are hard constraints the solver will never violate. Rules with `LOW`, `MEDIUM`, or `HIGH` priority are soft preferences the solver optimizes across.
|
|
221
114
|
|
|
222
|
-
|
|
115
|
+
**Scoping.** Every rule accepts `appliesTo` (a role, skill, or member ID), plus time scoping (`dayOfWeek`, `dateRange`, `dates`, `recurringPeriods`) to target when and who:
|
|
223
116
|
|
|
224
117
|
```typescript
|
|
225
|
-
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
config: {
|
|
229
|
-
roleIds: ["student"],
|
|
230
|
-
hours: 4,
|
|
231
|
-
dayOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"],
|
|
232
|
-
priority: "MANDATORY",
|
|
233
|
-
},
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Alice has time off next week
|
|
237
|
-
{
|
|
238
|
-
name: "time-off",
|
|
239
|
-
config: {
|
|
240
|
-
employeeIds: ["alice"],
|
|
241
|
-
dateRange: { start: "2026-02-09", end: "2026-02-13" },
|
|
242
|
-
priority: "MANDATORY",
|
|
243
|
-
},
|
|
244
|
-
}
|
|
118
|
+
maxHoursPerDay(8, { appliesTo: "barista", dayOfWeek: weekdays }),
|
|
119
|
+
timeOff({ appliesTo: "alice", dateRange: { start: "2026-02-09", end: "2026-02-13" } }),
|
|
120
|
+
maxHoursPerWeek(30, { appliesTo: "bob", priority: "MEDIUM" }),
|
|
245
121
|
```
|
|
246
122
|
|
|
247
|
-
|
|
123
|
+
**Cost optimization.** Members declare `pay` as hourly or salaried. Cost rules (`minimizeCost`, `dayMultiplier`, `overtimeMultiplier`, `tieredOvertimeMultiplier`, etc.) tell the solver to minimize total labor cost, factoring in overtime, day premiums, and time-based surcharges.
|
|
248
124
|
|
|
249
|
-
|
|
125
|
+
**Validation.** `ModelBuilder` reports coverage gaps and rule violations both pre-solve (via `compile()`) and post-solve (via `builder.reporter.analyzeSolution()`). Use `summarizeValidation()` for grouped reporting.
|
|
250
126
|
|
|
251
|
-
|
|
252
|
-
// Hard: legally required rest period
|
|
253
|
-
{ name: "min-rest-between-shifts", config: { hours: 11, priority: "MANDATORY" } }
|
|
127
|
+
---
|
|
254
128
|
|
|
255
|
-
|
|
256
|
-
{ name: "max-hours-week", config: { employeeIds: ["bob"], hours: 30, weekStartsOn: "monday", priority: "MEDIUM" } }
|
|
257
|
-
```
|
|
129
|
+
## Rules
|
|
258
130
|
|
|
259
|
-
|
|
131
|
+
### Scheduling
|
|
260
132
|
|
|
261
|
-
|
|
133
|
+
| Function | Description |
|
|
134
|
+
| -------------------------- | ------------------------------------ |
|
|
135
|
+
| `maxHoursPerDay(hours)` | Max hours per person per day |
|
|
136
|
+
| `maxHoursPerWeek(hours)` | Max hours per person per week |
|
|
137
|
+
| `minHoursPerDay(hours)` | Min hours per person per day |
|
|
138
|
+
| `minHoursPerWeek(hours)` | Min hours per person per week |
|
|
139
|
+
| `maxShiftsPerDay(n)` | Max shift assignments per day |
|
|
140
|
+
| `maxConsecutiveDays(n)` | Max consecutive working days |
|
|
141
|
+
| `minConsecutiveDays(n)` | Min consecutive working days |
|
|
142
|
+
| `minRestBetweenShifts(h)` | Min rest hours between shifts |
|
|
143
|
+
| `timeOff(opts)` | Block assignments during periods |
|
|
144
|
+
| `preference(level, opts)` | Prefer or avoid assigning members |
|
|
145
|
+
| `preferLocation(id, opts)` | Prefer members at specific locations |
|
|
146
|
+
| `assignTogether(opts)` | Keep members on the same shifts |
|
|
262
147
|
|
|
263
|
-
|
|
264
|
-
import { ValidationReporterImpl, summarizeValidation } from "dabke";
|
|
265
|
-
|
|
266
|
-
const reporter = new ValidationReporterImpl();
|
|
267
|
-
const builder = new ModelBuilder({ ...config, reporter });
|
|
268
|
-
const { request, validation, canSolve } = builder.compile();
|
|
269
|
-
|
|
270
|
-
// Pre-solve validation
|
|
271
|
-
if (!canSolve) {
|
|
272
|
-
for (const error of validation.errors) {
|
|
273
|
-
console.error(error.reason);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Post-solve validation (after solving)
|
|
278
|
-
reporter.analyzeSolution(response);
|
|
279
|
-
const results = reporter.getValidation();
|
|
280
|
-
const summaries = summarizeValidation(results);
|
|
281
|
-
|
|
282
|
-
for (const s of summaries) {
|
|
283
|
-
console.log(
|
|
284
|
-
`${s.description}: ${s.status} (${s.passedCount}/${s.passedCount + s.violatedCount})`,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
```
|
|
148
|
+
### Cost
|
|
288
149
|
|
|
289
|
-
|
|
150
|
+
| Function | Description |
|
|
151
|
+
| ---------------------------------------- | ---------------------------------------- |
|
|
152
|
+
| `minimizeCost()` | Minimize total labor cost |
|
|
153
|
+
| `dayMultiplier(factor, opts?)` | Pay multiplier for specific days |
|
|
154
|
+
| `daySurcharge(amount, opts?)` | Flat surcharge per hour on specific days |
|
|
155
|
+
| `timeSurcharge(amount, window, opts?)` | Surcharge during a time-of-day window |
|
|
156
|
+
| `overtimeMultiplier(opts)` | Weekly overtime pay multiplier |
|
|
157
|
+
| `overtimeSurcharge(opts)` | Weekly overtime flat surcharge |
|
|
158
|
+
| `dailyOvertimeMultiplier(opts)` | Daily overtime pay multiplier |
|
|
159
|
+
| `dailyOvertimeSurcharge(opts)` | Daily overtime flat surcharge |
|
|
160
|
+
| `tieredOvertimeMultiplier(tiers, opts?)` | Graduated overtime rates |
|
|
290
161
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
import { createCpsatRuleFactory, type CompilationRule } from "dabke";
|
|
295
|
-
|
|
296
|
-
function createNoSundayWorkRule(config: { priority: "MANDATORY" | "HIGH" }): CompilationRule {
|
|
297
|
-
return {
|
|
298
|
-
compile(b) {
|
|
299
|
-
for (const emp of b.employees) {
|
|
300
|
-
for (const day of b.days) {
|
|
301
|
-
const date = new Date(`${day}T00:00:00Z`);
|
|
302
|
-
if (date.getUTCDay() !== 0) continue; // Sunday = 0
|
|
303
|
-
for (const pattern of b.shiftPatterns) {
|
|
304
|
-
if (!b.canAssign(emp, pattern)) continue;
|
|
305
|
-
b.addLinear([{ var: b.assignment(emp.id, pattern.id, day), coeff: 1 }], "<=", 0);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const customRules = createCpsatRuleFactory({
|
|
314
|
-
"no-sunday-work": createNoSundayWorkRule,
|
|
315
|
-
});
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide on writing custom rules.
|
|
162
|
+
---
|
|
319
163
|
|
|
320
164
|
## LLM Integration
|
|
321
165
|
|
|
322
|
-
dabke ships an `llms.txt`
|
|
166
|
+
dabke ships an `llms.txt` with complete API documentation for AI code generation:
|
|
323
167
|
|
|
324
168
|
```typescript
|
|
325
169
|
import { apiDocs } from "dabke/llms";
|
|
326
|
-
|
|
327
|
-
// Pass to your LLM as context for generating scheduling code
|
|
328
170
|
```
|
|
329
171
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
```bash
|
|
333
|
-
cat node_modules/dabke/llms.txt
|
|
334
|
-
```
|
|
172
|
+
---
|
|
335
173
|
|
|
336
174
|
## Development
|
|
337
175
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
Unit tests have no external dependencies:
|
|
341
|
-
|
|
342
|
-
```bash
|
|
343
|
-
npm run test:unit
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
Integration tests require Docker to run the solver container:
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
# Ensure Docker is running, then:
|
|
350
|
-
npm run test:integration
|
|
351
|
-
```
|
|
176
|
+
Unit tests (no dependencies): `npm run test:unit`
|
|
352
177
|
|
|
353
|
-
|
|
178
|
+
Integration tests (requires Docker): `npm run test:integration`
|
|
354
179
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
```bash
|
|
358
|
-
npm test # runs typecheck + unit tests only
|
|
359
|
-
npm run test:integration # solver integration tests (requires Docker)
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
### Test utilities
|
|
363
|
-
|
|
364
|
-
dabke exports test helpers for writing your own integration tests:
|
|
180
|
+
The test harness builds and starts a solver container from `solver/Dockerfile` automatically.
|
|
365
181
|
|
|
366
182
|
```typescript
|
|
367
183
|
import { startSolverContainer } from "dabke/testing";
|
|
368
|
-
|
|
369
184
|
const solver = await startSolverContainer();
|
|
370
|
-
const response = await solver.client.solve(request);
|
|
371
|
-
solver.stop();
|
|
372
185
|
```
|
|
373
186
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
## Contributing
|
|
377
|
-
|
|
378
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, how to add rules, and contribution guidelines.
|
|
187
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add rules and contribute.
|
|
379
188
|
|
|
380
189
|
## License
|
|
381
190
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ShiftAssignment } from "./response.js";
|
|
2
|
+
import type { SchedulingMember, ShiftPattern } from "./types.js";
|
|
3
|
+
import type { CompilationRule } from "./model-builder.js";
|
|
4
|
+
/**
|
|
5
|
+
* Well-known cost categories used by built-in rules.
|
|
6
|
+
*
|
|
7
|
+
* Custom rules can use any string as a category. These constants
|
|
8
|
+
* are provided for convenience and consistency.
|
|
9
|
+
*/
|
|
10
|
+
export declare const COST_CATEGORY: {
|
|
11
|
+
/** Base pay cost (from {@link minimizeCost}). */
|
|
12
|
+
readonly BASE: "base";
|
|
13
|
+
/** Overtime cost (from overtime rules). */
|
|
14
|
+
readonly OVERTIME: "overtime";
|
|
15
|
+
/** Premium cost (from day/time multipliers and surcharges). */
|
|
16
|
+
readonly PREMIUM: "premium";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Per-member cost breakdown.
|
|
20
|
+
*
|
|
21
|
+
* Categories are open-ended strings. Built-in rules use categories
|
|
22
|
+
* from {@link COST_CATEGORY}. Custom rules can introduce their own.
|
|
23
|
+
*/
|
|
24
|
+
export interface MemberCostDetail {
|
|
25
|
+
/** Sum of all category costs. */
|
|
26
|
+
totalCost: number;
|
|
27
|
+
/** Total hours worked (computed from assignments, not from rules). */
|
|
28
|
+
totalHours: number;
|
|
29
|
+
/** Cost per category. Only categories with nonzero cost appear. */
|
|
30
|
+
categories: ReadonlyMap<string, number>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Full cost breakdown for a solved schedule.
|
|
34
|
+
*/
|
|
35
|
+
export interface CostBreakdown {
|
|
36
|
+
/** Total cost in the caller's currency unit. */
|
|
37
|
+
total: number;
|
|
38
|
+
/** Cost per member. */
|
|
39
|
+
byMember: ReadonlyMap<string, MemberCostDetail>;
|
|
40
|
+
/** Cost per day. */
|
|
41
|
+
byDay: ReadonlyMap<string, number>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Configuration accepted by {@link calculateScheduleCost}.
|
|
45
|
+
*
|
|
46
|
+
* In practice, callers pass the `ModelBuilderConfig` (which satisfies this)
|
|
47
|
+
* or the relevant subset.
|
|
48
|
+
*/
|
|
49
|
+
export interface CostCalculationConfig {
|
|
50
|
+
members: ReadonlyArray<SchedulingMember>;
|
|
51
|
+
shiftPatterns: ReadonlyArray<ShiftPattern>;
|
|
52
|
+
rules: ReadonlyArray<CompilationRule>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Computes the actual cost of a solved schedule using the same cost model
|
|
56
|
+
* as the solver rules.
|
|
57
|
+
*
|
|
58
|
+
* Collects `CostEntry` values from all rules' `cost()` methods and
|
|
59
|
+
* aggregates them into a {@link CostBreakdown}.
|
|
60
|
+
*
|
|
61
|
+
* @param assignments - Shift assignments from {@link parseSolverResponse}
|
|
62
|
+
* @param config - Members, shift patterns, and compiled rules
|
|
63
|
+
*/
|
|
64
|
+
export declare function calculateScheduleCost(assignments: ShiftAssignment[], config: CostCalculationConfig): CostBreakdown;
|
|
65
|
+
//# sourceMappingURL=cost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/cpsat/cost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,oBAAoB,CAAC;AAGrE;;;;;GAKG;AACH,eAAO,MAAM,aAAa;IACxB,iDAAiD;;IAEjD,2CAA2C;;IAE3C,+DAA+D;;CAEvD,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,oBAAoB;IACpB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACzC,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC3C,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,eAAe,EAAE,EAC9B,MAAM,EAAE,qBAAqB,GAC5B,aAAa,CAuDf"}
|