dabke 0.78.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 +120 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +42 -0
- package/dist/client.js.map +1 -0
- package/dist/client.schemas.d.ts +250 -0
- package/dist/client.schemas.d.ts.map +1 -0
- package/dist/client.schemas.js +137 -0
- package/dist/client.schemas.js.map +1 -0
- package/dist/client.types.d.ts +34 -0
- package/dist/client.types.d.ts.map +1 -0
- package/dist/client.types.js +18 -0
- package/dist/client.types.js.map +1 -0
- package/dist/cpsat/model-builder.d.ts +128 -0
- package/dist/cpsat/model-builder.d.ts.map +1 -0
- package/dist/cpsat/model-builder.js +640 -0
- package/dist/cpsat/model-builder.js.map +1 -0
- package/dist/cpsat/response.d.ts +74 -0
- package/dist/cpsat/response.d.ts.map +1 -0
- package/dist/cpsat/response.js +92 -0
- package/dist/cpsat/response.js.map +1 -0
- package/dist/cpsat/rules/assign-together.d.ts +23 -0
- package/dist/cpsat/rules/assign-together.d.ts.map +1 -0
- package/dist/cpsat/rules/assign-together.js +78 -0
- package/dist/cpsat/rules/assign-together.js.map +1 -0
- package/dist/cpsat/rules/employee-assignment-priority.d.ts +64 -0
- package/dist/cpsat/rules/employee-assignment-priority.d.ts.map +1 -0
- package/dist/cpsat/rules/employee-assignment-priority.js +151 -0
- package/dist/cpsat/rules/employee-assignment-priority.js.map +1 -0
- package/dist/cpsat/rules/index.d.ts +13 -0
- package/dist/cpsat/rules/index.d.ts.map +1 -0
- package/dist/cpsat/rules/index.js +13 -0
- package/dist/cpsat/rules/index.js.map +1 -0
- package/dist/cpsat/rules/location-preference.d.ts +29 -0
- package/dist/cpsat/rules/location-preference.d.ts.map +1 -0
- package/dist/cpsat/rules/location-preference.js +59 -0
- package/dist/cpsat/rules/location-preference.js.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts +28 -0
- package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -0
- package/dist/cpsat/rules/max-consecutive-days.js +70 -0
- package/dist/cpsat/rules/max-consecutive-days.js.map +1 -0
- package/dist/cpsat/rules/max-hours-day.d.ts +57 -0
- package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -0
- package/dist/cpsat/rules/max-hours-day.js +159 -0
- package/dist/cpsat/rules/max-hours-day.js.map +1 -0
- package/dist/cpsat/rules/max-hours-week.d.ts +62 -0
- package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -0
- package/dist/cpsat/rules/max-hours-week.js +169 -0
- package/dist/cpsat/rules/max-hours-week.js.map +1 -0
- package/dist/cpsat/rules/max-shifts-day.d.ts +69 -0
- package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -0
- package/dist/cpsat/rules/max-shifts-day.js +170 -0
- package/dist/cpsat/rules/max-shifts-day.js.map +1 -0
- package/dist/cpsat/rules/min-consecutive-days.d.ts +29 -0
- package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -0
- package/dist/cpsat/rules/min-consecutive-days.js +104 -0
- package/dist/cpsat/rules/min-consecutive-days.js.map +1 -0
- package/dist/cpsat/rules/min-hours-day.d.ts +28 -0
- package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -0
- package/dist/cpsat/rules/min-hours-day.js +61 -0
- package/dist/cpsat/rules/min-hours-day.js.map +1 -0
- package/dist/cpsat/rules/min-hours-week.d.ts +29 -0
- package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -0
- package/dist/cpsat/rules/min-hours-week.js +68 -0
- package/dist/cpsat/rules/min-hours-week.js.map +1 -0
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts +28 -0
- package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -0
- package/dist/cpsat/rules/min-rest-between-shifts.js +95 -0
- package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -0
- package/dist/cpsat/rules/registry.d.ts +7 -0
- package/dist/cpsat/rules/registry.d.ts.map +1 -0
- package/dist/cpsat/rules/registry.js +28 -0
- package/dist/cpsat/rules/registry.js.map +1 -0
- package/dist/cpsat/rules/resolver.d.ts +31 -0
- package/dist/cpsat/rules/resolver.d.ts.map +1 -0
- package/dist/cpsat/rules/resolver.js +124 -0
- package/dist/cpsat/rules/resolver.js.map +1 -0
- package/dist/cpsat/rules/rules.types.d.ts +32 -0
- package/dist/cpsat/rules/rules.types.d.ts.map +1 -0
- package/dist/cpsat/rules/rules.types.js +2 -0
- package/dist/cpsat/rules/rules.types.js.map +1 -0
- package/dist/cpsat/rules/scoping.d.ts +129 -0
- package/dist/cpsat/rules/scoping.d.ts.map +1 -0
- package/dist/cpsat/rules/scoping.js +190 -0
- package/dist/cpsat/rules/scoping.js.map +1 -0
- package/dist/cpsat/rules/time-off.d.ts +78 -0
- package/dist/cpsat/rules/time-off.d.ts.map +1 -0
- package/dist/cpsat/rules/time-off.js +261 -0
- package/dist/cpsat/rules/time-off.js.map +1 -0
- package/dist/cpsat/rules.d.ts +5 -0
- package/dist/cpsat/rules.d.ts.map +1 -0
- package/dist/cpsat/rules.js +4 -0
- package/dist/cpsat/rules.js.map +1 -0
- package/dist/cpsat/semantic-time.d.ts +198 -0
- package/dist/cpsat/semantic-time.d.ts.map +1 -0
- package/dist/cpsat/semantic-time.js +222 -0
- package/dist/cpsat/semantic-time.js.map +1 -0
- package/dist/cpsat/types.d.ts +180 -0
- package/dist/cpsat/types.d.ts.map +1 -0
- package/dist/cpsat/types.js +2 -0
- package/dist/cpsat/types.js.map +1 -0
- package/dist/cpsat/utils.d.ts +47 -0
- package/dist/cpsat/utils.d.ts.map +1 -0
- package/dist/cpsat/utils.js +92 -0
- package/dist/cpsat/utils.js.map +1 -0
- package/dist/cpsat/validation-reporter.d.ts +54 -0
- package/dist/cpsat/validation-reporter.d.ts.map +1 -0
- package/dist/cpsat/validation-reporter.js +261 -0
- package/dist/cpsat/validation-reporter.js.map +1 -0
- package/dist/cpsat/validation.types.d.ts +141 -0
- package/dist/cpsat/validation.types.d.ts.map +1 -0
- package/dist/cpsat/validation.types.js +14 -0
- package/dist/cpsat/validation.types.js.map +1 -0
- package/dist/datetime.utils.d.ts +245 -0
- package/dist/datetime.utils.d.ts.map +1 -0
- package/dist/datetime.utils.js +372 -0
- package/dist/datetime.utils.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/llms.d.ts +5 -0
- package/dist/llms.d.ts.map +1 -0
- package/dist/llms.js +8 -0
- package/dist/llms.js.map +1 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/solver-container.d.ts +49 -0
- package/dist/testing/solver-container.d.ts.map +1 -0
- package/dist/testing/solver-container.js +127 -0
- package/dist/testing/solver-container.js.map +1 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +105 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +130 -0
- package/dist/validation.js.map +1 -0
- package/llms.txt +2188 -0
- package/package.json +76 -0
- package/solver/Dockerfile +31 -0
- package/solver/README.md +23 -0
- package/solver/pyproject.toml +28 -0
- package/solver/src/solver/__init__.py +1 -0
- package/solver/src/solver/app.py +24 -0
- package/solver/src/solver/models.py +120 -0
- package/solver/src/solver/solver.py +359 -0
- package/solver/tests/test_solver.py +156 -0
- package/solver/uv.lock +661 -0
- package/src/client.schemas.ts +163 -0
- package/src/client.ts +67 -0
- package/src/client.types.ts +66 -0
- package/src/cpsat/model-builder.ts +858 -0
- package/src/cpsat/response.ts +130 -0
- package/src/cpsat/rules/assign-together.ts +96 -0
- package/src/cpsat/rules/employee-assignment-priority.ts +182 -0
- package/src/cpsat/rules/index.ts +12 -0
- package/src/cpsat/rules/location-preference.ts +68 -0
- package/src/cpsat/rules/max-consecutive-days.ts +98 -0
- package/src/cpsat/rules/max-hours-day.ts +187 -0
- package/src/cpsat/rules/max-hours-week.ts +197 -0
- package/src/cpsat/rules/max-shifts-day.ts +198 -0
- package/src/cpsat/rules/min-consecutive-days.ts +140 -0
- package/src/cpsat/rules/min-hours-day.ts +69 -0
- package/src/cpsat/rules/min-hours-week.ts +77 -0
- package/src/cpsat/rules/min-rest-between-shifts.ts +121 -0
- package/src/cpsat/rules/registry.ts +49 -0
- package/src/cpsat/rules/resolver.ts +181 -0
- package/src/cpsat/rules/rules.types.ts +41 -0
- package/src/cpsat/rules/scoping.ts +340 -0
- package/src/cpsat/rules/time-off.ts +336 -0
- package/src/cpsat/rules.ts +27 -0
- package/src/cpsat/semantic-time.ts +463 -0
- package/src/cpsat/types.ts +194 -0
- package/src/cpsat/utils.ts +105 -0
- package/src/cpsat/validation-reporter.ts +366 -0
- package/src/cpsat/validation.types.ts +185 -0
- package/src/datetime.utils.ts +426 -0
- package/src/errors.ts +17 -0
- package/src/index.ts +289 -0
- package/src/llms.ts +9 -0
- package/src/testing/index.ts +12 -0
- package/src/testing/solver-container.ts +172 -0
- package/src/types.ts +191 -0
- package/src/validation.ts +188 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduling library powered by constraint programming (CP-SAT).
|
|
3
|
+
*
|
|
4
|
+
* Define teams, shifts, coverage, and rules — dabke turns them
|
|
5
|
+
* into an optimized schedule.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* ## Core Concepts
|
|
9
|
+
*
|
|
10
|
+
* **ModelBuilder**: Creates the constraint programming model from your team,
|
|
11
|
+
* shift patterns, coverage requirements, and rules.
|
|
12
|
+
*
|
|
13
|
+
* **Semantic Time**: Flexible time period definitions that can vary by day or date.
|
|
14
|
+
* - `{ name: "morning", startTime: { hours: 8 }, endTime: { hours: 12 } }`
|
|
15
|
+
* - The same semantic name can map to different times based on context
|
|
16
|
+
* - Enables business-friendly scheduling: "Need 3 waiters during lunch_rush"
|
|
17
|
+
*
|
|
18
|
+
* **Rules System**: Translate business requirements into scheduling constraints.
|
|
19
|
+
* - 12 built-in rules: hours limits, time-off, rest periods, prioritization, etc.
|
|
20
|
+
* - Scoping: Apply rules globally, per person, per role, or per time period
|
|
21
|
+
* - Priority levels: MANDATORY (hard constraint) vs LOW/MEDIUM/HIGH (soft preferences)
|
|
22
|
+
*
|
|
23
|
+
* @example Complete workflow
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import {
|
|
26
|
+
* ModelBuilder,
|
|
27
|
+
* HttpSolverClient,
|
|
28
|
+
* parseSolverResponse,
|
|
29
|
+
* defineSemanticTimes,
|
|
30
|
+
* } from "dabke";
|
|
31
|
+
*
|
|
32
|
+
* // Define semantic times
|
|
33
|
+
* const times = defineSemanticTimes({
|
|
34
|
+
* business_hours: { startTime: { hours: 9 }, endTime: { hours: 17 } },
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* // Create coverage requirements
|
|
38
|
+
* const coverage = times.coverage([
|
|
39
|
+
* { semanticTime: "business_hours", roleIds: ["worker"], targetCount: 2 },
|
|
40
|
+
* ]);
|
|
41
|
+
*
|
|
42
|
+
* // Define shift patterns
|
|
43
|
+
* const shiftPatterns = [
|
|
44
|
+
* { id: "day_shift", startTime: { hours: 9 }, endTime: { hours: 17 } },
|
|
45
|
+
* ];
|
|
46
|
+
*
|
|
47
|
+
* // Build the model
|
|
48
|
+
* const builder = new ModelBuilder({
|
|
49
|
+
* employees: [{ id: "alice", roleIds: ["worker"] }],
|
|
50
|
+
* shiftPatterns,
|
|
51
|
+
* schedulingPeriod: {
|
|
52
|
+
* dateRange: { start: "2026-02-09", end: "2026-02-15" },
|
|
53
|
+
* },
|
|
54
|
+
* coverage: times.resolve(coverage, ["2026-02-09", "2026-02-10"]),
|
|
55
|
+
* ruleConfigs: [
|
|
56
|
+
* { name: "max-hours-week", config: { hours: 40, priority: "MANDATORY" } },
|
|
57
|
+
* ],
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Compile and solve
|
|
61
|
+
* const client = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
62
|
+
* const { request, canSolve, validation } = builder.compile();
|
|
63
|
+
* if (!canSolve) {
|
|
64
|
+
* console.error("Cannot solve:", validation.errors);
|
|
65
|
+
* } else {
|
|
66
|
+
* const response = await client.solve(request);
|
|
67
|
+
* const result = parseSolverResponse(response);
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @packageDocumentation
|
|
72
|
+
*/
|
|
73
|
+
export type { TimeOfDay, CalendarDate, DayOfWeek, TimeHorizon, DateTime, DateTimeComponents, DateTimeRange, SchedulingPeriod, } from "./types.js";
|
|
74
|
+
export { DayOfWeekSchema } from "./types.js";
|
|
75
|
+
export { dateToCalendarDate, dateTimeToDate, compareDateTimes, toDayOfWeek, toDayOfWeekUTC, formatDateString, generateDays, splitPeriodIntoDays, splitPeriodIntoWeeks, dateTimeRangesOverlap, daysBetween, resolveDaysFromPeriod, } from "./datetime.utils.js";
|
|
76
|
+
export { ORSchedulingError } from "./errors.js";
|
|
77
|
+
export { HttpSolverClient } from "./client.js";
|
|
78
|
+
export type { SolverClient, SolverRequest, SolverResponse, SolverVariable, SolverConstraint, SolverTerm, SolverObjective, SolverStatus, SoftConstraintViolation, FetcherLike, } from "./client.types.js";
|
|
79
|
+
export { SOLVER_STATUS } from "./client.types.js";
|
|
80
|
+
export { SolverRequestSchema, SolverResponseSchema } from "./client.schemas.js";
|
|
81
|
+
export { ModelBuilder } from "./cpsat/model-builder.js";
|
|
82
|
+
export type { ModelBuilderConfig, CompilationResult, CompilationRule, RuleValidationContext, } from "./cpsat/model-builder.js";
|
|
83
|
+
export { parseSolverResponse, resolveAssignments } from "./cpsat/response.js";
|
|
84
|
+
export type { ShiftAssignment, ResolvedShiftAssignment, SolverResult, } from "./cpsat/response.js";
|
|
85
|
+
export { createAssignTogetherRule, createEmployeeAssignmentPriorityRule, createLocationPreferenceRule, createMaxConsecutiveDaysRule, createMaxHoursDayRule, createMaxHoursWeekRule, createMaxShiftsDayRule, createMinConsecutiveDaysRule, createMinHoursDayRule, createMinHoursWeekRule, createMinRestBetweenShiftsRule, createTimeOffRule, } from "./cpsat/rules/index.js";
|
|
86
|
+
export type { AssignTogetherConfig } from "./cpsat/rules/assign-together.js";
|
|
87
|
+
export type { EmployeeAssignmentPriorityConfig } from "./cpsat/rules/employee-assignment-priority.js";
|
|
88
|
+
export type { LocationPreferenceConfig } from "./cpsat/rules/location-preference.js";
|
|
89
|
+
export type { MaxConsecutiveDaysConfig } from "./cpsat/rules/max-consecutive-days.js";
|
|
90
|
+
export type { MaxHoursDayConfig } from "./cpsat/rules/max-hours-day.js";
|
|
91
|
+
export type { MaxHoursWeekConfig } from "./cpsat/rules/max-hours-week.js";
|
|
92
|
+
export type { MaxShiftsDayConfig } from "./cpsat/rules/max-shifts-day.js";
|
|
93
|
+
export type { MinConsecutiveDaysConfig } from "./cpsat/rules/min-consecutive-days.js";
|
|
94
|
+
export type { MinHoursDayConfig } from "./cpsat/rules/min-hours-day.js";
|
|
95
|
+
export type { MinHoursWeekConfig } from "./cpsat/rules/min-hours-week.js";
|
|
96
|
+
export type { MinRestBetweenShiftsConfig } from "./cpsat/rules/min-rest-between-shifts.js";
|
|
97
|
+
export type { TimeOffConfig } from "./cpsat/rules/time-off.js";
|
|
98
|
+
export { builtInCpsatRuleFactories, createCpsatRuleFactory } from "./cpsat/rules/registry.js";
|
|
99
|
+
export type { CpsatRuleRegistry, CpsatRuleName, CpsatRuleConfigEntry, CpsatRuleFactories, BuiltInCpsatRuleFactories, CreateCpsatRuleFunction, CpsatRuleRegistryFromFactories, } from "./cpsat/rules/rules.types.js";
|
|
100
|
+
export { defineSemanticTimes, isConcreteCoverage, isSemanticCoverage, } from "./cpsat/semantic-time.js";
|
|
101
|
+
export type { SemanticTimeDef, SemanticTimeVariant, SemanticTimeEntry, SemanticTimeContext, SemanticCoverageRequirement, ConcreteCoverageRequirement, MixedCoverageRequirement, } from "./cpsat/semantic-time.js";
|
|
102
|
+
export type { SchedulingEmployee, Employee, ShiftPattern, CoverageRequirement, TimeInterval, Priority, ModelBuilderOptions, } from "./cpsat/types.js";
|
|
103
|
+
export { OBJECTIVE_WEIGHTS } from "./cpsat/utils.js";
|
|
104
|
+
export { ValidationReporterImpl } from "./cpsat/validation-reporter.js";
|
|
105
|
+
export type { ValidationReporter } from "./cpsat/validation-reporter.js";
|
|
106
|
+
export type { TrackedConstraint, CoverageExclusion } from "./cpsat/validation.types.js";
|
|
107
|
+
export type { ScheduleValidation, ScheduleError, ScheduleViolation, SchedulePassed, CoverageError, CoverageViolation, CoveragePassed, RuleError, RuleViolation, RulePassed, SolverError, ValidationContext, ValidationSummary, GroupKey, } from "./cpsat/validation.types.js";
|
|
108
|
+
export { groupKey } from "./cpsat/validation.types.js";
|
|
109
|
+
export { summarizeValidation } from "./cpsat/validation-reporter.js";
|
|
110
|
+
export { validateCoverageRoles, validateCoverageSkills, validateCoverageConfig, } from "./validation.js";
|
|
111
|
+
export type { CoverageValidationResult, SkillValidationResult, CoverageConfigValidationResult, } from "./validation.js";
|
|
112
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAMH,YAAY,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM7C,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,YAAY,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,YAAY,EACZ,uBAAuB,EACvB,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAMhF,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAMlC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACL,wBAAwB,EACxB,oCAAoC,EACpC,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,YAAY,EAAE,gCAAgC,EAAE,MAAM,+CAA+C,CAAC;AACtG,YAAY,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AACrF,YAAY,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,YAAY,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,YAAY,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAC3F,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAE9F,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC;AAMtC,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,2BAA2B,EAC3B,2BAA2B,EAC3B,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAMlC,YAAY,EACV,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,QAAQ,EACR,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAMrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAExE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAExF,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,GACT,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduling library powered by constraint programming (CP-SAT).
|
|
3
|
+
*
|
|
4
|
+
* Define teams, shifts, coverage, and rules — dabke turns them
|
|
5
|
+
* into an optimized schedule.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* ## Core Concepts
|
|
9
|
+
*
|
|
10
|
+
* **ModelBuilder**: Creates the constraint programming model from your team,
|
|
11
|
+
* shift patterns, coverage requirements, and rules.
|
|
12
|
+
*
|
|
13
|
+
* **Semantic Time**: Flexible time period definitions that can vary by day or date.
|
|
14
|
+
* - `{ name: "morning", startTime: { hours: 8 }, endTime: { hours: 12 } }`
|
|
15
|
+
* - The same semantic name can map to different times based on context
|
|
16
|
+
* - Enables business-friendly scheduling: "Need 3 waiters during lunch_rush"
|
|
17
|
+
*
|
|
18
|
+
* **Rules System**: Translate business requirements into scheduling constraints.
|
|
19
|
+
* - 12 built-in rules: hours limits, time-off, rest periods, prioritization, etc.
|
|
20
|
+
* - Scoping: Apply rules globally, per person, per role, or per time period
|
|
21
|
+
* - Priority levels: MANDATORY (hard constraint) vs LOW/MEDIUM/HIGH (soft preferences)
|
|
22
|
+
*
|
|
23
|
+
* @example Complete workflow
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import {
|
|
26
|
+
* ModelBuilder,
|
|
27
|
+
* HttpSolverClient,
|
|
28
|
+
* parseSolverResponse,
|
|
29
|
+
* defineSemanticTimes,
|
|
30
|
+
* } from "dabke";
|
|
31
|
+
*
|
|
32
|
+
* // Define semantic times
|
|
33
|
+
* const times = defineSemanticTimes({
|
|
34
|
+
* business_hours: { startTime: { hours: 9 }, endTime: { hours: 17 } },
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* // Create coverage requirements
|
|
38
|
+
* const coverage = times.coverage([
|
|
39
|
+
* { semanticTime: "business_hours", roleIds: ["worker"], targetCount: 2 },
|
|
40
|
+
* ]);
|
|
41
|
+
*
|
|
42
|
+
* // Define shift patterns
|
|
43
|
+
* const shiftPatterns = [
|
|
44
|
+
* { id: "day_shift", startTime: { hours: 9 }, endTime: { hours: 17 } },
|
|
45
|
+
* ];
|
|
46
|
+
*
|
|
47
|
+
* // Build the model
|
|
48
|
+
* const builder = new ModelBuilder({
|
|
49
|
+
* employees: [{ id: "alice", roleIds: ["worker"] }],
|
|
50
|
+
* shiftPatterns,
|
|
51
|
+
* schedulingPeriod: {
|
|
52
|
+
* dateRange: { start: "2026-02-09", end: "2026-02-15" },
|
|
53
|
+
* },
|
|
54
|
+
* coverage: times.resolve(coverage, ["2026-02-09", "2026-02-10"]),
|
|
55
|
+
* ruleConfigs: [
|
|
56
|
+
* { name: "max-hours-week", config: { hours: 40, priority: "MANDATORY" } },
|
|
57
|
+
* ],
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Compile and solve
|
|
61
|
+
* const client = new HttpSolverClient(fetch, "http://localhost:8080");
|
|
62
|
+
* const { request, canSolve, validation } = builder.compile();
|
|
63
|
+
* if (!canSolve) {
|
|
64
|
+
* console.error("Cannot solve:", validation.errors);
|
|
65
|
+
* } else {
|
|
66
|
+
* const response = await client.solve(request);
|
|
67
|
+
* const result = parseSolverResponse(response);
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @packageDocumentation
|
|
72
|
+
*/
|
|
73
|
+
export { DayOfWeekSchema } from "./types.js";
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Date/time utilities
|
|
76
|
+
// ============================================================================
|
|
77
|
+
export { dateToCalendarDate, dateTimeToDate, compareDateTimes, toDayOfWeek, toDayOfWeekUTC, formatDateString, generateDays, splitPeriodIntoDays, splitPeriodIntoWeeks, dateTimeRangesOverlap, daysBetween, resolveDaysFromPeriod, } from "./datetime.utils.js";
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Errors
|
|
80
|
+
// ============================================================================
|
|
81
|
+
export { ORSchedulingError } from "./errors.js";
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// Solver client
|
|
84
|
+
// ============================================================================
|
|
85
|
+
export { HttpSolverClient } from "./client.js";
|
|
86
|
+
export { SOLVER_STATUS } from "./client.types.js";
|
|
87
|
+
export { SolverRequestSchema, SolverResponseSchema } from "./client.schemas.js";
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Model builder
|
|
90
|
+
// ============================================================================
|
|
91
|
+
export { ModelBuilder } from "./cpsat/model-builder.js";
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Solver response parsing
|
|
94
|
+
// ============================================================================
|
|
95
|
+
export { parseSolverResponse, resolveAssignments } from "./cpsat/response.js";
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Rules
|
|
98
|
+
// ============================================================================
|
|
99
|
+
export { createAssignTogetherRule, createEmployeeAssignmentPriorityRule, createLocationPreferenceRule, createMaxConsecutiveDaysRule, createMaxHoursDayRule, createMaxHoursWeekRule, createMaxShiftsDayRule, createMinConsecutiveDaysRule, createMinHoursDayRule, createMinHoursWeekRule, createMinRestBetweenShiftsRule, createTimeOffRule, } from "./cpsat/rules/index.js";
|
|
100
|
+
export { builtInCpsatRuleFactories, createCpsatRuleFactory } from "./cpsat/rules/registry.js";
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Semantic time
|
|
103
|
+
// ============================================================================
|
|
104
|
+
export { defineSemanticTimes, isConcreteCoverage, isSemanticCoverage, } from "./cpsat/semantic-time.js";
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Constants
|
|
107
|
+
// ============================================================================
|
|
108
|
+
export { OBJECTIVE_WEIGHTS } from "./cpsat/utils.js";
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Validation
|
|
111
|
+
// ============================================================================
|
|
112
|
+
export { ValidationReporterImpl } from "./cpsat/validation-reporter.js";
|
|
113
|
+
export { groupKey } from "./cpsat/validation.types.js";
|
|
114
|
+
export { summarizeValidation } from "./cpsat/validation-reporter.js";
|
|
115
|
+
export { validateCoverageRoles, validateCoverageSkills, validateCoverageConfig, } from "./validation.js";
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAiBH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,+EAA+E;AAC/E,SAAS;AACT,+EAA+E;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAe/C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AASxD,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAQ9E,+EAA+E;AAC/E,QAAQ;AACR,+EAA+E;AAE/E,OAAO,EACL,wBAAwB,EACxB,oCAAoC,EACpC,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAehC,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAY9F,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AA0BlC,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAuBxE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC"}
|
package/dist/llms.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-friendly API documentation for dabke
|
|
3
|
+
*/
|
|
4
|
+
export declare const apiDocs = "# dabke\n\n> Scheduling library powered by constraint programming (CP-SAT)\n\nScheduling library powered by constraint programming (CP-SAT).\n\nDefine teams, shifts, coverage, and rules \u2014 dabke turns them\ninto an optimized schedule.\n\n## Core Concepts\n\n**ModelBuilder**: Creates the constraint programming model from your team,\nshift patterns, coverage requirements, and rules.\n\n**Semantic Time**: Flexible time period definitions that can vary by day or date.\n- `{ name: \"morning\", startTime: { hours: 8 }, endTime: { hours: 12 } }`\n- The same semantic name can map to different times based on context\n- Enables business-friendly scheduling: \"Need 3 waiters during lunch_rush\"\n\n**Rules System**: Translate business requirements into scheduling constraints.\n- 12 built-in rules: hours limits, time-off, rest periods, prioritization, etc.\n- Scoping: Apply rules globally, per person, per role, or per time period\n- Priority levels: MANDATORY (hard constraint) vs LOW/MEDIUM/HIGH (soft preferences)\n\n---\n\n# CP-SAT Scheduling API\n\nCore scheduling types for the CP-SAT solver.\n\n## Interfaces\n\n### TimeOfDay\nTime of day representation (hours and minutes, with optional seconds/nanos).\n\nUsed for defining shift start/end times and semantic time boundaries.\nHours are in 24-hour format (0-23).\n\n**Example:**\n```typescript\nconst morningStart: TimeOfDay = {\n hours: 9,\n minutes: 0\n};\n\nconst afternoonEnd: TimeOfDay = {\n hours: 17,\n minutes: 30\n};\n```\n\n**Properties:**\n- `hours: number`\n- `minutes: number`\n- `seconds?: number | undefined`\n- `nanos?: number | undefined`\n\n### CalendarDate\nCalendar date representation (year, month, day).\n\n**Example:**\n```typescript\nconst christmas: CalendarDate = {\n year: 2025,\n month: 12,\n day: 25\n};\n```\n\n**Properties:**\n- `year: number`\n- `month: number`\n- `day: number`\n\n### TimeHorizon\nTime horizon defining the start and end dates for scheduling.\n\nSpecifies the date range over which the schedule should be generated.\nThe range is inclusive of start date and exclusive of end date.\n\n**Example:**\n```typescript\n// One week schedule starting Monday, March 3, 2025\nconst horizon: TimeHorizon = {\n start: new Date('2025-03-03'), // Monday\n end: new Date('2025-03-10') // Following Monday (exclusive)\n};\n```\n\n**Properties:**\n- `start: Date`\n- `end: Date`\n\n### DateTimeComponents\n**Properties:**\n- `year?: number | undefined`\n- `month?: number | undefined`\n- `day?: number | undefined`\n- `hours?: number | undefined`\n- `minutes?: number | undefined`\n- `seconds?: number | undefined`\n- `nanos?: number | undefined`\n\n### DateTimeRange\nRepresents a time range with start and end DateTimes.\nUsed for checking overlaps and scheduling constraints.\n\n**Properties:**\n- `start: DateTimeWithUtcOffset | DateTimeWithTimeZone`\n- `end: DateTimeWithUtcOffset | DateTimeWithTimeZone`\n\n### SolverClient\n**Properties:**\n- `solve: (request: SolverRequest, options?: { signal?: AbortSignal; }) => Promise<SolverResponse>`\n- `health?: (() => Promise<void>) | undefined`\n\n### ModelBuilderConfig\nConfiguration for ModelBuilder.\n\n**Example:**\nDate range with day-of-week filtering (restaurant closed Mon/Tue)\n```typescript\nconst config: ModelBuilderConfig = {\nemployees: [...],\nshiftPatterns: [...],\ncoverage: [...],\nschedulingPeriod: {\ndateRange: { start: '2025-02-03', end: '2025-02-09' },\ndaysOfWeek: ['wednesday', 'thursday', 'friday', 'saturday', 'sunday'],\n},\n};\n```\n\n**Properties:**\n- `employees: SchedulingEmployee[]`\n- `shiftPatterns: ShiftPattern[]`\n- `schedulingPeriod: { dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }` - Defines when scheduling should occur. Can specify either a date range\n(with optional day-of-week filtering) or a list of specific dates.\n- `coverage: CoverageRequirement[]`\n- `rules?: CompilationRule[] | undefined` - Pre-compiled rules; use this for custom rules that are not part of the registry.\n- `ruleConfigs?: CpsatRuleConfigEntry[] | undefined` - Named rule configurations that will be compiled using the provided rule factories.\n- `ruleFactories?: CpsatRuleFactories | undefined` - Rule factories to use when compiling ruleConfigs. Defaults to built-in CP-SAT rules.\n- `reporter?: ValidationReporter | undefined` - Optional validation reporter for diagnostics.\n\n### CompilationResult\n**Properties:**\n- `request: { variables: ({ type: \"bool\"; name: string; } | { type: \"int\"; name: string; min: number; max: number; } | { type: \"interval\"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; })[]; constraints: ({ type: \"linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\" | \"==\"; rhs: number; } | { type: \"soft_linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\"; rhs: number; penalty: number; id?: string | undefined; } | { type: \"exactly_one\"; vars: string[]; } | { type: \"at_most_one\"; vars: string[]; } | { type: \"implication\"; if: string; then: string; } | { type: \"bool_or\"; vars: string[]; } | { type: \"bool_and\"; vars: string[]; } | { type: \"no_overlap\"; intervals: string[]; })[]; objective?: { sense: \"minimize\" | \"maximize\"; terms: { var: string; coeff: number; }[]; } | undefined; options?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined; }`\n- `validation: ScheduleValidation`\n- `canSolve: boolean`\n\n### CompilationRule\n**Properties:**\n- `compile: (builder: ModelBuilder) => void`\n- `validate?: ((assignments: ResolvedShiftAssignment[], reporter: ValidationReporter, context: RuleValidationContext) => void) | undefined`\n\n### RuleValidationContext\nContext provided to rules during post-solve validation.\n\n**Properties:**\n- `employees: SchedulingEmployee[]`\n- `days: string[]`\n- `shiftPatterns: ShiftPattern[]`\n\n### ShiftAssignment\nA shift assignment extracted from the solver response.\n\n**Properties:**\n- `employeeId: string`\n- `shiftPatternId: string`\n- `day: string`\n\n### ResolvedShiftAssignment\nA shift assignment with resolved times.\n\n**Properties:**\n- `employeeId: string`\n- `day: string`\n- `startTime: TimeOfDay`\n- `endTime: TimeOfDay`\n\n### SolverResult\nParsed solver result with assignments and metadata.\n\n**Properties:**\n- `status: \"OPTIMAL\" | \"FEASIBLE\" | \"INFEASIBLE\" | \"TIMEOUT\" | \"ERROR\"`\n- `assignments: ShiftAssignment[]`\n- `statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined`\n- `error?: string | undefined`\n\n### CpsatRuleRegistry\n**Properties:**\n- `\"assign-together\": { groupEmployeeIds: [string, string, ...string[]]; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; }`\n- `\"employee-assignment-priority\": { preference: \"high\" | \"low\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n- `\"location-preference\": { locationId: string; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"max-consecutive-days\": { days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"max-hours-day\": { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n- `\"max-hours-week\": { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n- `\"max-shifts-day\": { shifts: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n- `\"min-consecutive-days\": { days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"min-hours-day\": { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"min-hours-week\": { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"min-rest-between-shifts\": { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n- `\"time-off\": { priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n### SemanticTimeDef\nBase definition for a semantic time period.\n\n**Properties:**\n- `startTime: TimeOfDay`\n- `endTime: TimeOfDay`\n\n### SemanticTimeVariant\nVariant of a semantic time that applies to specific days or dates.\n\n**Properties:**\n- `days?: DayOfWeek[] | undefined` - Apply this variant only on these days of the week\n- `dates?: string[] | undefined` - Apply this variant only on these specific dates (YYYY-MM-DD)\n\n### SemanticTimeContext\nResult of defineSemanticTimes - provides type-safe coverage function.\n\n**Properties:**\n- `defs: { [P in S]: SemanticTimeEntry; }` - The semantic time definitions\n- `coverage: (reqs: MixedCoverageRequirement<S>[]) => MixedCoverageRequirement<S>[]` - Create coverage requirements with type-safe semantic time names.\nAccepts both semantic references and concrete one-off requirements.\n- `resolve: (reqs: MixedCoverageRequirement<S>[], days: string[]) => CoverageRequirement[]` - Resolve all coverage requirements to concrete CoverageRequirement[]\nfor the given days in the scheduling horizon.\n\n### SchedulingEmployee\n**Properties:**\n- `id: string`\n- `roleIds: string[]`\n- `skillIds?: string[] | undefined`\n\n### ShiftPattern\nA shift pattern defines WHEN people can work \u2014 the time slots available for assignment.\n\nShift patterns are templates that repeat across all scheduling days. The solver assigns\nteam members to these patterns based on coverage requirements and constraints.\n\n**Example:**\n// Simple venue: one shift type, anyone can work it\nconst patterns: ShiftPattern[] = [\n { id: \"day\", startTime: { hours: 9 }, endTime: { hours: 17 } }\n];\n\n**Example:**\n// Restaurant: different shifts for different roles\nconst patterns: ShiftPattern[] = [\n { id: \"kitchen_morning\", startTime: { hours: 6 }, endTime: { hours: 14 }, roleIds: [\"chef\", \"prep_cook\"] },\n { id: \"floor_lunch\", startTime: { hours: 11 }, endTime: { hours: 15 }, roleIds: [\"waiter\", \"host\"] },\n];\n\n**Properties:**\n- `id: string` - Unique identifier for this shift pattern.\nUsed in assignments and rule configurations.\n- `roleIds?: [string, ...string[]] | undefined` - Restricts who can be assigned to this shift based on their roles.\n\n- If omitted: anyone can work this shift\n- If provided: only team members whose roleIds overlap with this list can be assigned\n\nMost venues have the same shifts for everyone and don't need this.\nUse it when different roles have different schedules (e.g., kitchen staff starts\nearlier than floor staff).\n- `daysOfWeek?: DayOfWeek[] | undefined` - Restricts which days of the week this shift pattern can be used.\n\n- If omitted: shift can be used on any day\n- If provided: shift can only be assigned on the specified days\n\n**Example:**\n```typescript\n// Saturday-only short shift\n{ id: \"saturday_shift\", startTime: t(9), endTime: t(14), daysOfWeek: [\"saturday\"] }\n\n// Weekday-only full shift\n{ id: \"full_shift\", startTime: t(9), endTime: t(18), daysOfWeek: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"] }\n```\n- `locationId?: string | undefined` - Physical location where this shift takes place.\nUsed for multi-location scheduling and location-based constraints.\n- `startTime: TimeOfDay` - When the shift starts (e.g., { hours: 9, minutes: 0 } for 9:00 AM)\n- `endTime: TimeOfDay` - When the shift ends (e.g., { hours: 17, minutes: 30 } for 5:30 PM)\n\n### TimeInterval\n**Properties:**\n- `day: string`\n- `startTime: TimeOfDay`\n- `endTime: TimeOfDay`\n\n### ModelBuilderOptions\n**Properties:**\n- `weekStartsOn?: DayOfWeek | undefined`\n- `solverOptions?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined`\n- `coverageBucketMinutes?: number | undefined` - Bucket size used when translating coverage requirements into time-indexed constraints.\nSmaller buckets are more accurate but increase the number of constraints.\n- `fairDistribution?: boolean | undefined` - Whether to enable fair distribution of shifts across team members.\n\nWhen enabled (default), the solver minimizes the maximum number of shifts\nany single person works, ensuring work is distributed evenly. Each person\nworks between floor(total/n) and ceil(total/n) shifts.\n\nDisable this if you want other rules (like employee-assignment-priority)\nto have full control over shift distribution.\n\n### ValidationReporter\n**Properties:**\n- `excludeFromCoverage: (exclusion: CoverageExclusion) => void`\n- `reportCoverageError: (error: Omit<CoverageError, \"type\" | \"id\">) => void`\n- `reportRuleError: (error: Omit<RuleError, \"type\" | \"id\">) => void`\n- `reportSolverError: (reason: string) => void`\n- `reportCoverageViolation: (violation: Omit<CoverageViolation, \"type\" | \"id\">) => void`\n- `reportRuleViolation: (violation: Omit<RuleViolation, \"type\" | \"id\">) => void`\n- `reportCoveragePassed: (passed: Omit<CoveragePassed, \"type\" | \"id\">) => void`\n- `reportRulePassed: (passed: Omit<RulePassed, \"type\" | \"id\">) => void`\n- `trackConstraint: (constraint: TrackedConstraint) => void`\n- `hasErrors: () => boolean`\n- `getValidation: () => ScheduleValidation`\n- `getExclusions: () => CoverageExclusion[]`\n- `analyzeSolution: (response: SolverResponse) => void`\n\n### TrackedConstraint\n**Properties:**\n- `id: string`\n- `type: \"coverage\" | \"rule\"`\n- `rule?: string | undefined`\n- `description: string`\n- `targetValue: number`\n- `comparator: \"<=\" | \">=\"`\n- `day?: string | undefined`\n- `timeSlot?: string | undefined`\n- `roleIds?: string[] | undefined`\n- `skillIds?: readonly string[] | undefined`\n- `context: ValidationContext`\n- `groupKey?: GroupKey | undefined`\n\n### CoverageExclusion\nCoverage exclusion - indicates a team member is unavailable for coverage during a time period.\nUsed during compile-time to determine coverage feasibility.\n\n**Properties:**\n- `employeeId: string`\n- `day: string`\n- `startTime?: TimeOfDay | undefined`\n- `endTime?: TimeOfDay | undefined`\n\n### ScheduleValidation\n**Properties:**\n- `errors: readonly ScheduleError[]`\n- `violations: readonly ScheduleViolation[]`\n- `passed: readonly SchedulePassed[]`\n\n### CoverageError\n**Properties:**\n- `id: string`\n- `type: \"coverage\"`\n- `day: string`\n- `timeSlots: readonly string[]`\n- `roleIds?: string[] | undefined`\n- `skillIds?: readonly string[] | undefined`\n- `reason: string`\n- `suggestions?: readonly string[] | undefined`\n- `groupKey?: GroupKey | undefined`\n\n### CoverageViolation\n**Properties:**\n- `id: string`\n- `type: \"coverage\"`\n- `day: string`\n- `timeSlots: readonly string[]`\n- `roleIds?: string[] | undefined`\n- `skillIds?: readonly string[] | undefined`\n- `targetCount: number`\n- `actualCount: number`\n- `shortfall: number`\n- `groupKey?: GroupKey | undefined`\n\n### CoveragePassed\n**Properties:**\n- `id: string`\n- `type: \"coverage\"`\n- `day: string`\n- `timeSlots: readonly string[]`\n- `roleIds?: string[] | undefined`\n- `skillIds?: readonly string[] | undefined`\n- `description: string`\n- `groupKey?: GroupKey | undefined`\n\n### RuleError\n**Properties:**\n- `id: string`\n- `type: \"rule\"`\n- `rule: string`\n- `reason: string`\n- `context: ValidationContext`\n- `suggestions?: readonly string[] | undefined`\n- `groupKey?: GroupKey | undefined`\n\n### RuleViolation\n**Properties:**\n- `id: string`\n- `type: \"rule\"`\n- `rule: string`\n- `reason: string`\n- `context: ValidationContext`\n- `shortfall?: number | undefined`\n- `overflow?: number | undefined`\n- `groupKey?: GroupKey | undefined`\n\n### RulePassed\n**Properties:**\n- `id: string`\n- `type: \"rule\"`\n- `rule: string`\n- `description: string`\n- `context: ValidationContext`\n- `groupKey?: GroupKey | undefined`\n\n### SolverError\n**Properties:**\n- `id: string`\n- `type: \"solver\"`\n- `reason: string`\n\n### ValidationContext\nContext shared across validation results for grouping/display.\n\n**Properties:**\n- `days?: string[] | undefined`\n- `timeSlots?: string[] | undefined`\n- `employeeIds?: string[] | undefined`\n\n### ValidationSummary\nSummary of validation items grouped by their source instruction.\nUse `summarizeValidation()` to create these from a ScheduleValidation.\n\n**Properties:**\n- `groupKey: string & { readonly [GroupKeyBrand]: never; }`\n- `type: \"coverage\" | \"rule\"`\n- `description: string`\n- `days: readonly string[]`\n- `status: \"passed\" | \"partial\" | \"failed\"`\n- `passedCount: number`\n- `violatedCount: number`\n- `errorCount: number`\n\n### CoverageValidationResult\nResult of coverage role validation.\n\n**Properties:**\n- `valid: boolean`\n- `unknownRoles: string[]` - Role IDs used in coverage that don't match any team member\n- `knownRoles: string[]` - Role IDs used in coverage that match team members\n\n### SkillValidationResult\nResult of coverage skill validation.\n\n**Properties:**\n- `valid: boolean`\n- `unknownSkills: string[]` - Skill IDs used in coverage that don't match any team member\n- `knownSkills: string[]` - Skill IDs used in coverage that match team members\n\n### CoverageConfigValidationResult\nCombined validation result for coverage requirements.\n\n**Properties:**\n- `valid: boolean`\n- `roles: CoverageValidationResult`\n- `skills: SkillValidationResult`\n- `errors: string[]` - Human-readable error messages\n\n## Type Aliases\n\n### DayOfWeek\n```typescript\n\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\"\n```\n\n\n### DateTime\nDate and time representation supporting both UTC offset and timezone-aware formats.\n\nCan be specified either with a UTC offset (e.g., \"-08:00\") or with a timezone ID\n(e.g., \"America/Los_Angeles\").\n\n```typescript\nDateTimeWithUtcOffset | DateTimeWithTimeZone\n```\n\n\n### SchedulingPeriod\nDefines a scheduling period either as a date range or specific dates.\n\nUse this to specify when scheduling should occur. This is more expressive\nthan a simple list of days because it can filter by day-of-week.\n\n**Example:**\nDate range with day-of-week filtering (restaurant closed Mon/Tue)\n```typescript\nconst period: SchedulingPeriod = {\ndateRange: { start: '2025-02-03', end: '2025-02-09' },\ndaysOfWeek: ['wednesday', 'thursday', 'friday', 'saturday', 'sunday'],\n};\n```\n\n**Example:**\nDate range for all days\n```typescript\nconst period: SchedulingPeriod = {\ndateRange: { start: '2025-02-03', end: '2025-02-09' },\n};\n```\n\n**Example:**\nSpecific dates (non-contiguous or custom selection)\n```typescript\nconst period: SchedulingPeriod = {\nspecificDates: ['2025-02-05', '2025-02-07', '2025-02-10'],\n};\n```\n\n```typescript\n{ dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }\n```\n\n\n### SolverRequest\n```typescript\n{ variables: ({ type: \"bool\"; name: string; } | { type: \"int\"; name: string; min: number; max: number; } | { type: \"interval\"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; })[]; constraints: ({ type: \"linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\" | \"==\"; rhs: number; } | { type: \"soft_linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\"; rhs: number; penalty: number; id?: string | undefined; } | { type: \"exactly_one\"; vars: string[]; } | { type: \"at_most_one\"; vars: string[]; } | { type: \"implication\"; if: string; then: string; } | { type: \"bool_or\"; vars: string[]; } | { type: \"bool_and\"; vars: string[]; } | { type: \"no_overlap\"; intervals: string[]; })[]; objective?: { sense: \"minimize\" | \"maximize\"; terms: { var: string; coeff: number; }[]; } | undefined; options?: { timeLimitSeconds?: number | undefined; solutionLimit?: number | undefined; } | undefined; }\n```\n\n\n### SolverResponse\n```typescript\n{ status: \"OPTIMAL\" | \"FEASIBLE\" | \"INFEASIBLE\" | \"TIMEOUT\" | \"ERROR\"; values?: Record<string, number> | undefined; statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined; error?: string | undefined; solutionInfo?: string | undefined; softViolations?: { constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }[] | undefined; }\n```\n\n\n### SolverVariable\n```typescript\n{ type: \"bool\"; name: string; } | { type: \"int\"; name: string; min: number; max: number; } | { type: \"interval\"; name: string; start: number; end: number; size: number; presenceVar?: string | undefined; }\n```\n\n\n### SolverConstraint\n```typescript\n{ type: \"linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\" | \"==\"; rhs: number; } | { type: \"soft_linear\"; terms: { var: string; coeff: number; }[]; op: \"<=\" | \">=\"; rhs: number; penalty: number; id?: string | undefined; } | { type: \"exactly_one\"; vars: string[]; } | { type: \"at_most_one\"; vars: string[]; } | { type: \"implication\"; if: string; then: string; } | { type: \"bool_or\"; vars: string[]; } | { type: \"bool_and\"; vars: string[]; } | { type: \"no_overlap\"; intervals: string[]; }\n```\n\n\n### SolverTerm\n```typescript\n{ var: string; coeff: number; }\n```\n\n\n### SolverObjective\n```typescript\n{ sense: \"minimize\" | \"maximize\"; terms: { var: string; coeff: number; }[]; }\n```\n\n\n### SolverStatus\n```typescript\n\"OPTIMAL\" | \"FEASIBLE\" | \"INFEASIBLE\" | \"TIMEOUT\" | \"ERROR\"\n```\n\n\n### SoftConstraintViolation\n```typescript\n{ constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }\n```\n\n\n### FetcherLike\n```typescript\n((input: string | URL | Request, init?: RequestInit) => Promise<Response>) | { fetch: typeof fetch; }\n```\n\n\n### AssignTogetherConfig\n```typescript\n{ groupEmployeeIds: [string, string, ...string[]]; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; }\n```\n\n\n### EmployeeAssignmentPriorityConfig\n```typescript\n{ preference: \"high\" | \"low\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }\n```\n\n\n### LocationPreferenceConfig\n```typescript\n{ locationId: string; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### MaxConsecutiveDaysConfig\n```typescript\n{ days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### MaxHoursDayConfig\n```typescript\n{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }\n```\n\n\n### MaxHoursWeekConfig\n```typescript\n{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }\n```\n\n\n### MaxShiftsDayConfig\n```typescript\n{ shifts: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }\n```\n\n\n### MinConsecutiveDaysConfig\n```typescript\n{ days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### MinHoursDayConfig\n```typescript\n{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### MinHoursWeekConfig\n```typescript\n{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### MinRestBetweenShiftsConfig\n```typescript\n{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }\n```\n\n\n### TimeOffConfig\n```typescript\n{ priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }\n```\n\n\n### CpsatRuleName\n```typescript\nkeyof CpsatRuleRegistry\n```\n\n\n### CpsatRuleConfigEntry\n```typescript\n{ name: K; config: CpsatRuleRegistry[K]; }\n```\n\n\n### CpsatRuleFactories\n```typescript\n{ [ruleName: string]: CreateCpsatRuleFunction<any>; }\n```\n\n\n### BuiltInCpsatRuleFactories\n```typescript\n[Complex type: __type]\n```\n\n\n### CreateCpsatRuleFunction\n```typescript\n(config: TConfig) => CompilationRule\n```\n\n\n### CpsatRuleRegistryFromFactories\n```typescript\n{ [K in keyof F]: InferCpsatRuleConfig<F[K]>; }\n```\n\n\n### SemanticTimeEntry\nA semantic time can be a simple definition (applies every day)\nor an array of variants with different times for different days/dates.\n\n```typescript\nSemanticTimeDef | SemanticTimeVariant[]\n```\n\n\n### SemanticCoverageRequirement\nCoverage requirement that references a semantic time by name.\nType-safe: S is constrained to known semantic time names.\n\nThis is a discriminated union enforcing at compile time that at least\none of `roleIds` or `skillIds` must be provided.\n\n```typescript\nRoleBasedSemanticCoverageRequirement<S> | SkillBasedSemanticCoverageRequirement<S>\n```\n\n\n### ConcreteCoverageRequirement\nConcrete coverage requirement with explicit day and times.\nUsed for one-off requirements that don't fit a semantic time.\n\nThis is a discriminated union enforcing at compile time that at least\none of `roleIds` or `skillIds` must be provided.\n\n```typescript\nRoleBasedConcreteCoverageRequirement | SkillBasedConcreteCoverageRequirement\n```\n\n\n### MixedCoverageRequirement\nUnion type for coverage - either semantic (type-safe) or concrete.\n\n```typescript\nConcreteCoverageRequirement | SemanticCoverageRequirement<S>\n```\n\n\n### Employee\n```typescript\nSchedulingEmployee\n```\n\n\n### CoverageRequirement\nDefines staffing needs for a specific time period.\n\nThis is a discriminated union that enforces at compile time that at least\none of `roleIds` or `skillIds` must be provided:\n\n- Role-based: `{ roleIds: [\"waiter\"], ... }` - anyone with ANY of these roles (OR logic)\n- Role + skill: `{ roleIds: [\"waiter\"], skillIds: [\"senior\"], ... }` - role AND skills\n- Skill-only: `{ skillIds: [\"keyholder\"], ... }` - any role with ALL skills (AND logic)\n\n**Example:**\n// Need 2 waiters during lunch (role-based)\n{ day: \"2024-01-01\", startTime: { hours: 11 }, endTime: { hours: 14 }, roleIds: [\"waiter\"], targetCount: 2, priority: \"MANDATORY\" }\n\n**Example:**\n// Need 1 manager OR supervisor during service (OR logic on roles)\n{ day: \"2024-01-01\", startTime: { hours: 11 }, endTime: { hours: 22 }, roleIds: [\"manager\", \"supervisor\"], targetCount: 1, priority: \"MANDATORY\" }\n\n**Example:**\n// Need 1 keyholder for opening (skill-only, any role)\n{ day: \"2024-01-01\", startTime: { hours: 6 }, endTime: { hours: 8 }, skillIds: [\"keyholder\"], targetCount: 1, priority: \"MANDATORY\" }\n\n**Example:**\n// Need 1 senior waiter for training shift (role + skill filter)\n{ day: \"2024-01-01\", startTime: { hours: 9 }, endTime: { hours: 17 }, roleIds: [\"waiter\"], skillIds: [\"senior\"], targetCount: 1, priority: \"HIGH\" }\n\n```typescript\nRoleBasedCoverageRequirement | SkillBasedCoverageRequirement\n```\n\n\n### Priority\n```typescript\n\"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"\n```\n\n\n### ScheduleError\n```typescript\nCoverageError | RuleError | SolverError\n```\n\n\n### ScheduleViolation\n```typescript\nCoverageViolation | RuleViolation\n```\n\n\n### SchedulePassed\n```typescript\nCoveragePassed | RulePassed\n```\n\n\n### GroupKey\nBranded type for validation group keys.\nGroups related validation items that originated from the same instruction.\n\n```typescript\nstring & { readonly [GroupKeyBrand]: never; }\n```\n\n\n### InferCpsatRuleConfig\n```typescript\nT extends CreateCpsatRuleFunction<infer Config> ? Config : never\n```\n\n\n### Term\n```typescript\n{ var: string; coeff: number; }\n```\n\n\n## Functions\n\n### dateToCalendarDate\nConverts a JavaScript Date to a CalendarDate\n\n**Parameters:**\n- `date: Date`\n\n**Returns:** `CalendarDate`\n\n\n### dateTimeToDate\nConverts a DateTime to a JavaScript Date\nInternal helper function\n\n**Parameters:**\n- `dateTime: DateTimeWithUtcOffset | DateTimeWithTimeZone`\n\n**Returns:** `Date`\n\n\n### compareDateTimes\nCompares two DateTimes\nReturns:\n -1 if dateTime1 < dateTime2\n 0 if dateTime1 = dateTime2\n 1 if dateTime1 > dateTime2\n\n**Parameters:**\n- `dateTime1: DateTimeWithUtcOffset | DateTimeWithTimeZone`\n- `dateTime2: DateTimeWithUtcOffset | DateTimeWithTimeZone`\n\n**Returns:** `number`\n\n\n### toDayOfWeek\nHelper to get the day of week name from a Date (local time)\n\n**Parameters:**\n- `date: Date`\n\n**Returns:** `\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\"`\n\n\n### toDayOfWeekUTC\nHelper to get the day of week name from a Date (UTC)\nUse this when working with date strings like \"2026-01-10\" that are timezone-agnostic.\n\n**Parameters:**\n- `date: Date`\n\n**Returns:** `\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\"`\n\n\n### formatDateString\nFormats a date as YYYY-MM-DD string\n\n**Parameters:**\n- `date: Date`\n\n**Returns:** `string`\n\n\n### generateDays\nGenerates an array of day strings (YYYY-MM-DD) from a time horizon.\n\n**Example:**\n```typescript\nconst days = generateDays({\n start: new Date('2025-01-01'),\n end: new Date('2025-01-04')\n});\n// Returns: [\"2025-01-01\", \"2025-01-02\", \"2025-01-03\", \"2025-01-04\"]\n```\n\n**Parameters:**\n- `horizon: { start: Date; end: Date; }`\n\n**Returns:** `string[]`\n\n\n### splitPeriodIntoDays\nSplits a time period into consecutive day ranges.\n\nEach range represents a single calendar day within the period from start to end.\nThis is useful for rules that need to apply constraints on a per-day basis,\nsuch as maximum or minimum hours per day.\n\n**Example:**\n```typescript\nconst ranges = splitPeriodIntoDays({\n start: new Date('2025-01-01'),\n end: new Date('2025-01-03')\n});\n// Returns:\n// [\n// [Date('2025-01-01'), Date('2025-01-02')],\n// [Date('2025-01-02'), Date('2025-01-03')]\n// ]\n```\n\n**Parameters:**\n- `{ start, end }: { start: Date; end: Date; }`\n\n**Returns:** `[Date, Date][]`\n\n\n### splitPeriodIntoWeeks\nSplits a time period into consecutive week ranges.\n\nEach range represents a week period starting on the specified day of the week.\nThis is useful for rules that need to apply constraints on a per-week basis,\nsuch as maximum or minimum hours per week.\n\nThe first range starts at the provided start date (not necessarily on weekStartsOn).\nSubsequent ranges align to the weekStartsOn day. The last range's end date will be\nthe provided end date.\n\n**Example:**\n```typescript\nconst ranges = splitPeriodIntoWeeks({\n start: new Date('2025-01-01'), // Wednesday\n end: new Date('2025-01-15'),\n weekStartsOn: 'monday'\n});\n// Returns ranges starting from Jan 1 (Wed), then aligning to Mondays:\n// [\n// [Date('2025-01-01 Wed'), Date('2025-01-06 Mon')],\n// [Date('2025-01-06 Mon'), Date('2025-01-13 Mon')],\n// [Date('2025-01-13 Mon'), Date('2025-01-15 Wed')]\n// ]\n```\n\n**Parameters:**\n- `{\n start,\n end,\n weekStartsOn,\n}: { start: Date; end: Date; weekStartsOn: DayOfWeek; }`\n\n**Returns:** `[Date, Date][]`\n\n\n### dateTimeRangesOverlap\nChecks if two DateTime ranges overlap in both date and time.\nRanges overlap if they share any moment in time.\n\nTwo ranges overlap if: range1.start < range2.end AND range2.start < range1.end\n\n**Example:**\n```typescript\n// Same day, overlapping times (9-17 overlaps with 12-20)\ndateTimeRangesOverlap(\n {\n start: { year: 2025, month: 6, day: 1, hours: 9, minutes: 0 },\n end: { year: 2025, month: 6, day: 1, hours: 17, minutes: 0 }\n },\n {\n start: { year: 2025, month: 6, day: 1, hours: 12, minutes: 0 },\n end: { year: 2025, month: 6, day: 1, hours: 20, minutes: 0 }\n }\n); // true\n\n// Different days - no overlap\ndateTimeRangesOverlap(\n {\n start: { year: 2025, month: 6, day: 1, hours: 9, minutes: 0 },\n end: { year: 2025, month: 6, day: 1, hours: 17, minutes: 0 }\n },\n {\n start: { year: 2025, month: 6, day: 2, hours: 9, minutes: 0 },\n end: { year: 2025, month: 6, day: 2, hours: 17, minutes: 0 }\n }\n); // false\n\n// Works naturally with Shift objects\ndateTimeRangesOverlap(\n { start: shift1.startDateTime, end: shift1.endDateTime },\n { start: shift2.startDateTime, end: shift2.endDateTime }\n);\n```\n\n**Parameters:**\n- `range1: DateTimeRange`\n- `range2: DateTimeRange`\n\n**Returns:** `boolean`\n\n\n### daysBetween\nCalculates the number of complete days between two dates\n\n**Example:**\n```typescript\ndaysBetween(new Date('2025-01-01'), new Date('2025-01-05')); // 4\n```\n\n**Parameters:**\n- `start: Date`\n- `end: Date`\n\n**Returns:** `number`\n\n\n### resolveDaysFromPeriod\nComputes the list of day strings (YYYY-MM-DD) from a SchedulingPeriod.\n\nFor date ranges, generates all days between start and end (inclusive),\noptionally filtering to specific days of the week.\n\nFor specific dates, returns the dates as-is (sorted).\n\n**Example:**\nDate range with day-of-week filter\n```typescript\nconst days = resolveDaysFromPeriod({\ndateRange: { start: '2025-02-03', end: '2025-02-09' },\ndaysOfWeek: ['wednesday', 'friday'],\n});\n// Returns: ['2025-02-05', '2025-02-07'] (Wed and Fri only)\n```\n\n**Example:**\nDate range without filter\n```typescript\nconst days = resolveDaysFromPeriod({\ndateRange: { start: '2025-02-03', end: '2025-02-05' },\n});\n// Returns: ['2025-02-03', '2025-02-04', '2025-02-05']\n```\n\n**Example:**\nSpecific dates\n```typescript\nconst days = resolveDaysFromPeriod({\nspecificDates: ['2025-02-07', '2025-02-03', '2025-02-10'],\n});\n// Returns: ['2025-02-03', '2025-02-07', '2025-02-10'] (sorted)\n```\n\n**Parameters:**\n- `period: { dateRange: { start: string; end: string; }; daysOfWeek?: DayOfWeek[]; specificDates?: never; } | { specificDates: string[]; dateRange?: never; daysOfWeek?: never; }`\n\n**Returns:** `string[]`\n\n\n### parseSolverResponse\nExtracts shift assignments from solver response.\n\nParses variable names matching the pattern `assign:${employeeId}:${patternId}:${day}`\nand returns assignments where the variable value is 1 (true).\n\nIDs are validated by ModelBuilder to not contain colons,\nensuring unambiguous parsing.\n\n**Example:**\n```typescript\nconst response = await client.solve(request);\nconst result = parseSolverResponse(response);\n\nif (result.status === \"OPTIMAL\" || result.status === \"FEASIBLE\") {\n for (const assignment of result.assignments) {\n console.log(`${assignment.employeeId} works ${assignment.shiftPatternId} on ${assignment.day}`);\n }\n}\n```\n\n**Parameters:**\n- `response: { status: \"OPTIMAL\" | \"FEASIBLE\" | \"INFEASIBLE\" | \"TIMEOUT\" | \"ERROR\"; values?: Record<string, number> | undefined; statistics?: { solveTimeMs?: number | undefined; conflicts?: number | undefined; branches?: number | undefined; } | undefined; error?: string | undefined; solutionInfo?: string | undefined; softViolations?: { constraintId: string; violationAmount: number; targetValue: number; actualValue: number; }[] | undefined; }`\n\n**Returns:** `SolverResult`\n\n\n### resolveAssignments\nResolves shift assignments to concrete times using shift patterns.\n\n**Example:**\n```typescript\nconst result = parseScheduleResult(response);\nconst resolved = resolveAssignments(result.assignments, shiftPatterns);\n\nfor (const shift of resolved) {\n console.log(`${shift.employeeId} works ${shift.day} from ${shift.startTime.hours}:${shift.startTime.minutes}`);\n}\n```\n\n**Parameters:**\n- `assignments: ShiftAssignment[]`\n- `shiftPatterns: ShiftPattern[]`\n\n**Returns:** `ResolvedShiftAssignment[]`\n\n\n### createAssignTogetherRule\nEncourages or enforces that team members in the group work the same shift patterns on a day.\nFor each pair of team members in the group, ensures they are assigned to the same shifts.\n\n**Example:**\n```ts\nconst rule = createAssignTogetherRule({\n groupEmployeeIds: [\"alice\", \"bob\", \"charlie\"],\n priority: \"HIGH\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { groupEmployeeIds: [string, string, ...string[]]; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; }`\n\n**Returns:** `CompilationRule`\n\n\n### createEmployeeAssignmentPriorityRule\nAdds objective weight to prefer or avoid assigning team members.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nPrefer specific team members\n```ts\ncreateEmployeeAssignmentPriorityRule({\nemployeeIds: [\"alice\", \"bob\"],\npreference: \"high\",\n});\n```\n\n**Example:**\nAvoid assigning students on weekdays\n```ts\ncreateEmployeeAssignmentPriorityRule({\nroleIds: [\"student\"],\ndayOfWeek: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"],\npreference: \"low\",\n});\n```\n\n**Example:**\nPrefer experienced staff on weekends\n```ts\ncreateEmployeeAssignmentPriorityRule({\nskillIds: [\"senior\"],\ndayOfWeek: [\"saturday\", \"sunday\"],\npreference: \"high\",\n});\n```\n\n**Parameters:**\n- `config: { preference: \"high\" | \"low\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createLocationPreferenceRule\nPrefers assigning a person to shift patterns matching a specific location.\n\n**Example:**\n```ts\nconst rule = createLocationPreferenceRule({\n locationId: \"terrace\",\n priority: \"HIGH\",\n employeeIds: [\"alice\"],\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { locationId: string; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMaxConsecutiveDaysRule\nLimits how many consecutive days a person can be assigned.\n\n**Example:**\n```ts\nconst rule = createMaxConsecutiveDaysRule({\n days: 5,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMaxHoursDayRule\nLimits how many hours a person can work in a single day.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nLimit everyone to 8 hours per day\n```ts\ncreateMaxHoursDayRule({\nhours: 8,\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nStudents limited to 4 hours on weekdays during term\n```ts\ncreateMaxHoursDayRule({\nroleIds: [\"student\"],\nhours: 4,\ndayOfWeek: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"],\npriority: \"MANDATORY\",\n});\n```\n\n**Parameters:**\n- `config: { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMaxHoursWeekRule\nCaps total hours a person can work within each scheduling week.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\nTime scoping filters which days within each week count toward the limit.\n\n**Example:**\nLimit everyone to 40 hours per week\n```ts\ncreateMaxHoursWeekRule({\nhours: 40,\npriority: \"HIGH\",\n});\n```\n\n**Example:**\nStudents limited to 20 hours during term time\n```ts\ncreateMaxHoursWeekRule({\nroleIds: [\"student\"],\nhours: 20,\nrecurringPeriods: [\n{ name: \"fall-term\", startMonth: 9, startDay: 1, endMonth: 12, endDay: 15 },\n{ name: \"spring-term\", startMonth: 1, startDay: 15, endMonth: 5, endDay: 31 },\n],\npriority: \"MANDATORY\",\n});\n```\n\n**Parameters:**\n- `config: { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMaxShiftsDayRule\nLimits how many shifts a person can work in a single day.\n\nThis rule controls the maximum number of distinct shift assignments per day,\nregardless of shift duration. For limiting total hours worked, use `max-hours-day`.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nLimit to one shift per day (common for most schedules)\n```ts\ncreateMaxShiftsDayRule({\nshifts: 1,\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nAllow up to two shifts per day for part-time workers\n```ts\ncreateMaxShiftsDayRule({\nroleIds: [\"part-time\"],\nshifts: 2,\npriority: \"HIGH\",\n});\n```\n\n**Example:**\nStudents can work 2 shifts on weekends only\n```ts\ncreateMaxShiftsDayRule({\nroleIds: [\"student\"],\nshifts: 2,\ndayOfWeek: [\"saturday\", \"sunday\"],\npriority: \"MANDATORY\",\n});\n```\n\n**Parameters:**\n- `config: { shifts: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMinConsecutiveDaysRule\nRequires that once a person starts working, they continue for a minimum\nnumber of consecutive days.\n\n**Example:**\n```ts\nconst rule = createMinConsecutiveDaysRule({\n days: 3,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMinHoursDayRule\nEnsures a person works at least a minimum number of hours per day.\n\n**Example:**\n```ts\nconst rule = createMinHoursDayRule({\n hours: 6,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMinHoursWeekRule\nEnforces a minimum total number of hours per scheduling week.\n\n**Example:**\n```ts\nconst rule = createMinHoursWeekRule({\n hours: 30,\n priority: \"HIGH\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createMinRestBetweenShiftsRule\nEnforces a minimum rest period between any two shifts a person works.\n\n**Example:**\n```ts\nconst rule = createMinRestBetweenShiftsRule({\n hours: 10,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Parameters:**\n- `config: { hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createTimeOffRule\nBlocks or penalizes assignments during specified time periods.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\nOptionally supports partial-day time-off with startTime/endTime.\n\n**Example:**\nFull day vacation\n```ts\ncreateTimeOffRule({\nemployeeIds: [\"alice\"],\ndateRange: { start: \"2024-02-01\", end: \"2024-02-05\" },\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nEvery Wednesday afternoon off for students\n```ts\ncreateTimeOffRule({\nroleIds: [\"student\"],\ndayOfWeek: [\"wednesday\"],\nstartTime: { hours: 14, minutes: 0 },\nendTime: { hours: 23, minutes: 59 },\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nSpecific date, partial day\n```ts\ncreateTimeOffRule({\nemployeeIds: [\"bob\"],\nspecificDates: [\"2024-03-15\"],\nstartTime: { hours: 16, minutes: 0 },\nendTime: { hours: 23, minutes: 59 },\npriority: \"MANDATORY\",\n});\n```\n\n**Parameters:**\n- `config: { priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n**Returns:** `CompilationRule`\n\n\n### createCpsatRuleFactory\nCreates a rule factory map, preventing overriding built-in rules.\n\n**Parameters:**\n- `factories: F`\n\n**Returns:** `F`\n\n\n### defineSemanticTimes\nDefine semantic times with type-safe names.\n\nReturns a context object that provides:\n- Type-safe coverage() function that only accepts defined semantic time names\n- resolve() function to expand semantic times to concrete requirements\n\n**Example:**\nBasic usage\n```typescript\nconst times = defineSemanticTimes({\nopening: { startTime: { hours: 6 }, endTime: { hours: 8 } },\nlunch: { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 } },\nclosing: { startTime: { hours: 21 }, endTime: { hours: 23 } },\n});\n\nconst coverage = times.coverage([\n{ semanticTime: \"lunch\", roleId: \"server\", targetCount: 3 },\n{ semanticTime: \"opening\", roleId: \"keyholder\", targetCount: 1, priority: \"MANDATORY\" },\n// Type error: \"dinner\" is not a defined semantic time\n// { semanticTime: \"dinner\", roleId: \"server\", targetCount: 2 },\n]);\n```\n\n**Example:**\nVariants for different days\n```typescript\nconst times = defineSemanticTimes({\nlunch: [\n{ startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 }, days: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"] },\n{ startTime: { hours: 12 }, endTime: { hours: 15 }, days: [\"saturday\", \"sunday\"] },\n],\n});\n```\n\n**Example:**\nMixed semantic and concrete coverage\n```typescript\nconst coverage = times.coverage([\n{ semanticTime: \"lunch\", roleId: \"server\", targetCount: 3 },\n// One-off party - concrete time\n{ day: \"2026-01-14\", startTime: { hours: 15 }, endTime: { hours: 20 }, roleId: \"server\", targetCount: 5 },\n]);\n```\n\n**Parameters:**\n- `defs: T`\n\n**Returns:** `SemanticTimeContext<keyof T & string>`\n\n\n### isConcreteCoverage\nType guard to check if a requirement is concrete (has explicit day/times).\n\n**Parameters:**\n- `req: ConcreteCoverageRequirement | SemanticCoverageRequirement<S>`\n\n**Returns:** `boolean`\n\n\n### isSemanticCoverage\nType guard to check if a requirement is semantic (references a named time).\n\n**Parameters:**\n- `req: ConcreteCoverageRequirement | SemanticCoverageRequirement<S>`\n\n**Returns:** `boolean`\n\n\n### groupKey\nCreates a GroupKey from a description string.\nUse this to create keys that group related validation items together.\n\n**Example:**\n```typescript\nconst key = groupKey(\"2x waiter during lunch\");\ncoverage.groupKey = key;\n```\n\n**Parameters:**\n- `description: string`\n\n**Returns:** `string & { readonly [GroupKeyBrand]: never; }`\n\n\n### summarizeValidation\nAggregates validation items by their groupKey into summaries.\nThis is a pure function that doesn't modify the input.\n\nItems without a groupKey are grouped by their ID (ungrouped).\n\n**Example:**\n```typescript\nconst validation = reporter.getValidation();\nconst summaries = summarizeValidation(validation);\n// summaries[0] = {\n// groupKey: \"2x waiter during lunch\",\n// status: \"passed\",\n// passedCount: 180,\n// days: [\"2026-02-02\", \"2026-02-03\", ...]\n// }\n```\n\n**Parameters:**\n- `validation: ScheduleValidation`\n\n**Returns:** `readonly ValidationSummary[]`\n\n\n### validateCoverageRoles\nValidates that all roleIds used in coverage requirements match the team.\n\nThis catches a common LLM error where the model generates coverage requirements\nusing role names that don't match any team member's roleIds. Without this validation,\nsuch mismatches would result in valid but semantically wrong schedules (e.g.,\ncoverage requirements that no one can satisfy).\n\n**Example:**\n```typescript\nconst employees = [\n { id: \"alice\", roleIds: [\"cashier\"] },\n { id: \"bob\", roleIds: [\"stocker\"] },\n];\n\nconst coverage = [\n { roleId: \"cashier\", targetCount: 1, ... }, // OK\n { roleId: \"worker\", targetCount: 1, ... }, // Unknown role!\n];\n\nconst result = validateCoverageRoles(coverage, employees);\n// result.valid = false\n// result.unknownRoles = [\"worker\"]\n// result.knownRoles = [\"cashier\"]\n```\n\n**Parameters:**\n- `coverage: CoverageRequirement[]`\n- `employees: SchedulingEmployee[]`\n\n**Returns:** `CoverageValidationResult`\n\n\n### validateCoverageSkills\nValidates that all skillIds used in coverage requirements match the team.\n\nSimilar to role validation, this catches LLM hallucinations where skill names\nin coverage don't match any team member's skillIds.\n\n**Example:**\n```typescript\nconst employees = [\n { id: \"alice\", roleIds: [\"server\"], skillIds: [\"keyholder\"] },\n { id: \"bob\", roleIds: [\"server\"] },\n];\n\nconst coverage = [\n { skillIds: [\"keyholder\"], targetCount: 1, ... }, // OK\n { skillIds: [\"manager\"], targetCount: 1, ... }, // Unknown skill!\n];\n\nconst result = validateCoverageSkills(coverage, employees);\n// result.valid = false\n// result.unknownSkills = [\"manager\"]\n```\n\n**Parameters:**\n- `coverage: CoverageRequirement[]`\n- `employees: SchedulingEmployee[]`\n\n**Returns:** `SkillValidationResult`\n\n\n### validateCoverageConfig\nValidates coverage requirements against team roles and skills.\n\nThis is the primary validation function to call before building a scheduling model.\nIt checks both roles and skills, returning a combined result with error messages.\n\n**Example:**\n```typescript\nconst result = validateCoverageConfig(coverage, employees);\nif (!result.valid) {\n throw new Error(result.errors.join(\"; \"));\n}\n```\n\n**Parameters:**\n- `coverage: CoverageRequirement[]`\n- `employees: SchedulingEmployee[]`\n\n**Returns:** `CoverageConfigValidationResult`\n\n\n### addMinutesToDate\nAdds a number of minutes to a base date and returns a DateTime.\nTreats the base date as a reference point (typically midnight of horizon start),\nand the minutes parameter as absolute minutes from that point.\n\n**Example:**\n```typescript\n// Add 90 minutes from midnight\naddMinutesToDate(new Date('2025-01-01'), 90);\n// Returns: { year: 2025, month: 1, day: 1, hours: 1, minutes: 30 }\n\n// Add 1500 minutes (spans to next day)\naddMinutesToDate(new Date('2025-01-01'), 1500);\n// Returns: { year: 2025, month: 1, day: 2, hours: 1, minutes: 0 }\n```\n\n**Parameters:**\n- `baseDate: Date`\n- `minutes: number`\n\n**Returns:** `DateTimeWithUtcOffset | DateTimeWithTimeZone`\n\n\n### splitPoints\nReturns the points where a range should be split, filtered to within [start, end).\nAlways includes range start. Sorted ascending.\n\n**Parameters:**\n- `[start, end]: [number, number]`\n- `splitAt: number[]`\n\n**Returns:** `number[]`\n\n\n### parseDayString\nParse a day string (YYYY-MM-DD) to a UTC Date.\nUsed internally for day-of-week calculations and date comparisons.\n\n**Parameters:**\n- `day: string`\n\n**Returns:** `Date`\n\n\n### timeOfDayToMinutes\n**Parameters:**\n- `time: TimeOfDay`\n\n**Returns:** `number`\n\n\n### normalizeEndMinutes\n**Parameters:**\n- `startMinutes: number`\n- `endMinutes: number`\n\n**Returns:** `number`\n\n\n### priorityToPenalty\n**Parameters:**\n- `priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"`\n\n**Returns:** `number`\n\n\n### splitIntoWeeks\n**Parameters:**\n- `days: string[]`\n- `weekStartsOn: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\"`\n\n**Returns:** `string[][]`\n\n\n## Classes\n\n### ORSchedulingError\nError thrown when Google OR Tools scheduling API requests fail.\n\nContains the HTTP status code and raw response data from the API for debugging.\nCommon causes include infeasible constraints, invalid requests, or API unavailability.\n\n\n### HttpSolverClient\nGeneric HTTP client for the solver service.\n\n\n### ModelBuilder\nCompilation context that creates variables, constraints, and objectives\nand emits a `SolverRequest` for the Python CP-SAT solver service.\n\n\n### ValidationReporterImpl\n\n## Constants\n\n### DayOfWeekSchema\nZod schema for {@link DayOfWeek}.\nUseful for rule configs that need to accept a day-of-week string.\n\n**Type:** `z.ZodUnion<readonly [z.ZodLiteral<\"monday\">, z.ZodLiteral<\"tuesday\">, z.ZodLiteral<\"wednesday\">, z.ZodLiteral<\"thursday\">, z.ZodLiteral<\"friday\">, z.ZodLiteral<\"saturday\">, z.ZodLiteral<\"sunday\">]>`\n\n\n### SOLVER_STATUS\n**Type:** `{ readonly OPTIMAL: \"OPTIMAL\"; readonly FEASIBLE: \"FEASIBLE\"; readonly INFEASIBLE: \"INFEASIBLE\"; readonly TIMEOUT: \"TIMEOUT\"; readonly ERROR: \"ERROR\"; }`\n\n\n### SolverRequestSchema\n**Type:** `[Complex type: ZodObject]`\n\n\n### SolverResponseSchema\n**Type:** `z.ZodObject<{ status: z.ZodEnum<{ OPTIMAL: \"OPTIMAL\"; FEASIBLE: \"FEASIBLE\"; INFEASIBLE: \"INFEASIBLE\"; TIMEOUT: \"TIMEOUT\"; ERROR: \"ERROR\"; }>; values: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>; statistics: z.ZodOptional<z.ZodObject<{ solveTimeMs: z.ZodOptional<z.ZodNumber>; conflicts: z.ZodOptional<z.ZodNumber>; branches: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>>; error: z.ZodOptional<z.ZodString>; solutionInfo: z.ZodOptional<z.ZodString>; softViolations: z.ZodOptional<z.ZodArray<z.ZodObject<{ constraintId: z.ZodString; violationAmount: z.ZodNumber; targetValue: z.ZodNumber; actualValue: z.ZodNumber; }, z.core.$strip>>>; }, z.core.$strip>`\n\n\n### builtInCpsatRuleFactories\n**Type:** `[Complex type: __type]`\n\n\n### OBJECTIVE_WEIGHTS\nStandard objective weights for the scheduling solver.\n\nThese weights define the relative importance of different objectives.\nHigher weights mean stronger preference. Rules can use these as reference\npoints when adding their own penalties.\n\nWeight hierarchy (highest to lowest priority):\n- SHIFT_ACTIVE (1000): Minimize number of active shift patterns\n- ASSIGNMENT_PREFERENCE (10): Per-assignment preference (e.g., prefer permanent staff)\n- FAIRNESS (5): Fair distribution of shifts across team members\n- ASSIGNMENT_BASE (1): Tiebreaker - minimize total assignments\n\n**Example:**\nUsing weights in a custom rule\n```ts\nimport { OBJECTIVE_WEIGHTS } from \"feasible\";\n\n// Prefer senior staff with same weight as employee-assignment-priority\nb.addPenalty(assignment, -OBJECTIVE_WEIGHTS.ASSIGNMENT_PREFERENCE);\n\n// Strong preference (2x normal)\nb.addPenalty(assignment, -2 * OBJECTIVE_WEIGHTS.ASSIGNMENT_PREFERENCE);\n```\n\n**Type:** `{ readonly SHIFT_ACTIVE: 1000; readonly ASSIGNMENT_PREFERENCE: 10; readonly FAIRNESS: 5; readonly ASSIGNMENT_BASE: 1; }`\n\n\n### DAY_OF_WEEK_MAP\n**Type:** `{ sunday: number; monday: number; tuesday: number; wednesday: number; thursday: number; friday: number; saturday: number; }`\n\n\n### SolverTermSchema\n**Type:** `z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>`\n\n\n### BoolVariableSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"bool\">; name: z.ZodString; }, z.core.$strip>`\n\n\n### IntVariableSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"int\">; name: z.ZodString; min: z.ZodNumber; max: z.ZodNumber; }, z.core.$strip>`\n\n\n### IntervalVariableSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"interval\">; name: z.ZodString; start: z.ZodNumber; end: z.ZodNumber; size: z.ZodNumber; presenceVar: z.ZodOptional<z.ZodString>; }, z.core.$strip>`\n\n\n### SolverVariableSchema\n**Type:** `z.ZodUnion<readonly [z.ZodObject<{ type: z.ZodLiteral<\"bool\">; name: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"int\">; name: z.ZodString; min: z.ZodNumber; max: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"interval\">; name: z.ZodString; start: z.ZodNumber; end: z.ZodNumber; size: z.ZodNumber; presenceVar: z.ZodOptional<z.ZodString>; }, z.core.$strip>]>`\n\n\n### LinearConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"linear\">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<\"<=\">, z.ZodLiteral<\">=\">, z.ZodLiteral<\"==\">]>; rhs: z.ZodNumber; }, z.core.$strip>`\n\n\n### SoftLinearConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"soft_linear\">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<\"<=\">, z.ZodLiteral<\">=\">]>; rhs: z.ZodNumber; penalty: z.ZodNumber; id: z.ZodOptional<z.ZodString>; }, z.core.$strip>`\n\n\n### ExactlyOneConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"exactly_one\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`\n\n\n### AtMostOneConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"at_most_one\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`\n\n\n### ImplicationConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"implication\">; if: z.ZodString; then: z.ZodString; }, z.core.$strip>`\n\n\n### BoolOrConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"bool_or\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`\n\n\n### BoolAndConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"bool_and\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>`\n\n\n### NoOverlapConstraintSchema\n**Type:** `z.ZodObject<{ type: z.ZodLiteral<\"no_overlap\">; intervals: z.ZodArray<z.ZodString>; }, z.core.$strip>`\n\n\n### SolverConstraintSchema\n**Type:** `z.ZodUnion<readonly [z.ZodObject<{ type: z.ZodLiteral<\"linear\">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<\"<=\">, z.ZodLiteral<\">=\">, z.ZodLiteral<\"==\">]>; rhs: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"soft_linear\">; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; op: z.ZodUnion<readonly [z.ZodLiteral<\"<=\">, z.ZodLiteral<\">=\">]>; rhs: z.ZodNumber; penalty: z.ZodNumber; id: z.ZodOptional<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"exactly_one\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"at_most_one\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"implication\">; if: z.ZodString; then: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"bool_or\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"bool_and\">; vars: z.ZodArray<z.ZodString>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<\"no_overlap\">; intervals: z.ZodArray<z.ZodString>; }, z.core.$strip>]>`\n\n\n### SolverObjectiveSchema\n**Type:** `z.ZodObject<{ sense: z.ZodUnion<readonly [z.ZodLiteral<\"minimize\">, z.ZodLiteral<\"maximize\">]>; terms: z.ZodArray<z.ZodObject<{ var: z.ZodString; coeff: z.ZodNumber; }, z.core.$strip>>; }, z.core.$strip>`\n\n\n### SolverOptionsSchema\n**Type:** `z.ZodObject<{ timeLimitSeconds: z.ZodOptional<z.ZodNumber>; solutionLimit: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>`\n\n\n### SolverStatusSchema\n**Type:** `z.ZodEnum<{ OPTIMAL: \"OPTIMAL\"; FEASIBLE: \"FEASIBLE\"; INFEASIBLE: \"INFEASIBLE\"; TIMEOUT: \"TIMEOUT\"; ERROR: \"ERROR\"; }>`\n\n\n### SolverStatisticsSchema\n**Type:** `z.ZodObject<{ solveTimeMs: z.ZodOptional<z.ZodNumber>; conflicts: z.ZodOptional<z.ZodNumber>; branches: z.ZodOptional<z.ZodNumber>; }, z.core.$strip>`\n\n\n### SoftConstraintViolationSchema\n**Type:** `z.ZodObject<{ constraintId: z.ZodString; violationAmount: z.ZodNumber; targetValue: z.ZodNumber; actualValue: z.ZodNumber; }, z.core.$strip>`\n\n\n### MINUTES_PER_DAY\n**Type:** `number`\n\n\n\n## Built-In Rules Reference\n\nRules are constraints and preferences you can apply to schedules using `ctx.addRule(ruleName, config)`.\n\n### assign-together\n\n\n\nEncourages or enforces that team members in the group work the same shift patterns on a day.\nFor each pair of team members in the group, ensures they are assigned to the same shifts.\n\n**Example:**\n```ts\nconst rule = createAssignTogetherRule({\n groupEmployeeIds: [\"alice\", \"bob\", \"charlie\"],\n priority: \"HIGH\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ groupEmployeeIds: [string, string, ...string[]]; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; }`\n\n### employee-assignment-priority\n\n\n\nAdds objective weight to prefer or avoid assigning team members.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nPrefer specific team members\n```ts\ncreateEmployeeAssignmentPriorityRule({\nemployeeIds: [\"alice\", \"bob\"],\npreference: \"high\",\n});\n```\n\n**Example:**\nAvoid assigning students on weekdays\n```ts\ncreateEmployeeAssignmentPriorityRule({\nroleIds: [\"student\"],\ndayOfWeek: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"],\npreference: \"low\",\n});\n```\n\n**Example:**\nPrefer experienced staff on weekends\n```ts\ncreateEmployeeAssignmentPriorityRule({\nskillIds: [\"senior\"],\ndayOfWeek: [\"saturday\", \"sunday\"],\npreference: \"high\",\n});\n```\n\n**Config:** `{ preference: \"high\" | \"low\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n### location-preference\n\n\n\nPrefers assigning a person to shift patterns matching a specific location.\n\n**Example:**\n```ts\nconst rule = createLocationPreferenceRule({\n locationId: \"terrace\",\n priority: \"HIGH\",\n employeeIds: [\"alice\"],\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ locationId: string; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### max-consecutive-days\n\n\n\nLimits how many consecutive days a person can be assigned.\n\n**Example:**\n```ts\nconst rule = createMaxConsecutiveDaysRule({\n days: 5,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### max-hours-day\n\n\n\nLimits how many hours a person can work in a single day.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nLimit everyone to 8 hours per day\n```ts\ncreateMaxHoursDayRule({\nhours: 8,\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nStudents limited to 4 hours on weekdays during term\n```ts\ncreateMaxHoursDayRule({\nroleIds: [\"student\"],\nhours: 4,\ndayOfWeek: [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"],\npriority: \"MANDATORY\",\n});\n```\n\n**Config:** `{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n### max-hours-week\n\n\n\nCaps total hours a person can work within each scheduling week.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\nTime scoping filters which days within each week count toward the limit.\n\n**Example:**\nLimit everyone to 40 hours per week\n```ts\ncreateMaxHoursWeekRule({\nhours: 40,\npriority: \"HIGH\",\n});\n```\n\n**Example:**\nStudents limited to 20 hours during term time\n```ts\ncreateMaxHoursWeekRule({\nroleIds: [\"student\"],\nhours: 20,\nrecurringPeriods: [\n{ name: \"fall-term\", startMonth: 9, startDay: 1, endMonth: 12, endDay: 15 },\n{ name: \"spring-term\", startMonth: 1, startDay: 15, endMonth: 5, endDay: 31 },\n],\npriority: \"MANDATORY\",\n});\n```\n\n**Config:** `{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n### max-shifts-day\n\n\n\nLimits how many shifts a person can work in a single day.\n\nThis rule controls the maximum number of distinct shift assignments per day,\nregardless of shift duration. For limiting total hours worked, use `max-hours-day`.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\n\n**Example:**\nLimit to one shift per day (common for most schedules)\n```ts\ncreateMaxShiftsDayRule({\nshifts: 1,\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nAllow up to two shifts per day for part-time workers\n```ts\ncreateMaxShiftsDayRule({\nroleIds: [\"part-time\"],\nshifts: 2,\npriority: \"HIGH\",\n});\n```\n\n**Example:**\nStudents can work 2 shifts on weekends only\n```ts\ncreateMaxShiftsDayRule({\nroleIds: [\"student\"],\nshifts: 2,\ndayOfWeek: [\"saturday\", \"sunday\"],\npriority: \"MANDATORY\",\n});\n```\n\n**Config:** `{ shifts: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n### min-consecutive-days\n\n\n\nRequires that once a person starts working, they continue for a minimum\nnumber of consecutive days.\n\n**Example:**\n```ts\nconst rule = createMinConsecutiveDaysRule({\n days: 3,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ days: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### min-hours-day\n\n\n\nEnsures a person works at least a minimum number of hours per day.\n\n**Example:**\n```ts\nconst rule = createMinHoursDayRule({\n hours: 6,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### min-hours-week\n\n\n\nEnforces a minimum total number of hours per scheduling week.\n\n**Example:**\n```ts\nconst rule = createMinHoursWeekRule({\n hours: 30,\n priority: \"HIGH\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; weekStartsOn?: \"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\" | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### min-rest-between-shifts\n\n\n\nEnforces a minimum rest period between any two shifts a person works.\n\n**Example:**\n```ts\nconst rule = createMinRestBetweenShiftsRule({\n hours: 10,\n priority: \"MANDATORY\",\n});\nbuilder = new ModelBuilder({ ...config, rules: [rule] });\n```\n\n**Config:** `{ hours: number; priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; }`\n\n### time-off\n\n\n\nBlocks or penalizes assignments during specified time periods.\n\nSupports entity scoping (people, roles, skills) and time scoping\n(date ranges, specific dates, days of week, recurring periods).\nOptionally supports partial-day time-off with startTime/endTime.\n\n**Example:**\nFull day vacation\n```ts\ncreateTimeOffRule({\nemployeeIds: [\"alice\"],\ndateRange: { start: \"2024-02-01\", end: \"2024-02-05\" },\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nEvery Wednesday afternoon off for students\n```ts\ncreateTimeOffRule({\nroleIds: [\"student\"],\ndayOfWeek: [\"wednesday\"],\nstartTime: { hours: 14, minutes: 0 },\nendTime: { hours: 23, minutes: 59 },\npriority: \"MANDATORY\",\n});\n```\n\n**Example:**\nSpecific date, partial day\n```ts\ncreateTimeOffRule({\nemployeeIds: [\"bob\"],\nspecificDates: [\"2024-03-15\"],\nstartTime: { hours: 16, minutes: 0 },\nendTime: { hours: 23, minutes: 59 },\npriority: \"MANDATORY\",\n});\n```\n\n**Config:** `{ priority: \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"MANDATORY\"; startTime?: { hours: number; minutes: number; } | undefined; endTime?: { hours: number; minutes: number; } | undefined; employeeIds?: string[] | undefined; roleIds?: string[] | undefined; skillIds?: string[] | undefined; dateRange?: { start: string; end: string; } | undefined; specificDates?: string[] | undefined; dayOfWeek?: (\"monday\" | \"tuesday\" | \"wednesday\" | \"thursday\" | \"friday\" | \"saturday\" | \"sunday\")[] | undefined; recurringPeriods?: { name: string; startMonth: number; startDay: number; endMonth: number; endDay: number; }[] | undefined; }`\n\n";
|
|
5
|
+
//# sourceMappingURL=llms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../src/llms.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,eAAO,MAAM,OAAO,qw6EACs03E,CAAC"}
|