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.
Files changed (194) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE +21 -0
  3. package/README.md +187 -0
  4. package/dist/client.d.ts +14 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +42 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/client.schemas.d.ts +250 -0
  9. package/dist/client.schemas.d.ts.map +1 -0
  10. package/dist/client.schemas.js +137 -0
  11. package/dist/client.schemas.js.map +1 -0
  12. package/dist/client.types.d.ts +34 -0
  13. package/dist/client.types.d.ts.map +1 -0
  14. package/dist/client.types.js +18 -0
  15. package/dist/client.types.js.map +1 -0
  16. package/dist/cpsat/model-builder.d.ts +128 -0
  17. package/dist/cpsat/model-builder.d.ts.map +1 -0
  18. package/dist/cpsat/model-builder.js +640 -0
  19. package/dist/cpsat/model-builder.js.map +1 -0
  20. package/dist/cpsat/response.d.ts +74 -0
  21. package/dist/cpsat/response.d.ts.map +1 -0
  22. package/dist/cpsat/response.js +92 -0
  23. package/dist/cpsat/response.js.map +1 -0
  24. package/dist/cpsat/rules/assign-together.d.ts +23 -0
  25. package/dist/cpsat/rules/assign-together.d.ts.map +1 -0
  26. package/dist/cpsat/rules/assign-together.js +78 -0
  27. package/dist/cpsat/rules/assign-together.js.map +1 -0
  28. package/dist/cpsat/rules/employee-assignment-priority.d.ts +64 -0
  29. package/dist/cpsat/rules/employee-assignment-priority.d.ts.map +1 -0
  30. package/dist/cpsat/rules/employee-assignment-priority.js +151 -0
  31. package/dist/cpsat/rules/employee-assignment-priority.js.map +1 -0
  32. package/dist/cpsat/rules/index.d.ts +13 -0
  33. package/dist/cpsat/rules/index.d.ts.map +1 -0
  34. package/dist/cpsat/rules/index.js +13 -0
  35. package/dist/cpsat/rules/index.js.map +1 -0
  36. package/dist/cpsat/rules/location-preference.d.ts +29 -0
  37. package/dist/cpsat/rules/location-preference.d.ts.map +1 -0
  38. package/dist/cpsat/rules/location-preference.js +59 -0
  39. package/dist/cpsat/rules/location-preference.js.map +1 -0
  40. package/dist/cpsat/rules/max-consecutive-days.d.ts +28 -0
  41. package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -0
  42. package/dist/cpsat/rules/max-consecutive-days.js +70 -0
  43. package/dist/cpsat/rules/max-consecutive-days.js.map +1 -0
  44. package/dist/cpsat/rules/max-hours-day.d.ts +57 -0
  45. package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -0
  46. package/dist/cpsat/rules/max-hours-day.js +159 -0
  47. package/dist/cpsat/rules/max-hours-day.js.map +1 -0
  48. package/dist/cpsat/rules/max-hours-week.d.ts +62 -0
  49. package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -0
  50. package/dist/cpsat/rules/max-hours-week.js +169 -0
  51. package/dist/cpsat/rules/max-hours-week.js.map +1 -0
  52. package/dist/cpsat/rules/max-shifts-day.d.ts +69 -0
  53. package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -0
  54. package/dist/cpsat/rules/max-shifts-day.js +170 -0
  55. package/dist/cpsat/rules/max-shifts-day.js.map +1 -0
  56. package/dist/cpsat/rules/min-consecutive-days.d.ts +29 -0
  57. package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -0
  58. package/dist/cpsat/rules/min-consecutive-days.js +104 -0
  59. package/dist/cpsat/rules/min-consecutive-days.js.map +1 -0
  60. package/dist/cpsat/rules/min-hours-day.d.ts +28 -0
  61. package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -0
  62. package/dist/cpsat/rules/min-hours-day.js +61 -0
  63. package/dist/cpsat/rules/min-hours-day.js.map +1 -0
  64. package/dist/cpsat/rules/min-hours-week.d.ts +29 -0
  65. package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -0
  66. package/dist/cpsat/rules/min-hours-week.js +68 -0
  67. package/dist/cpsat/rules/min-hours-week.js.map +1 -0
  68. package/dist/cpsat/rules/min-rest-between-shifts.d.ts +28 -0
  69. package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -0
  70. package/dist/cpsat/rules/min-rest-between-shifts.js +95 -0
  71. package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -0
  72. package/dist/cpsat/rules/registry.d.ts +7 -0
  73. package/dist/cpsat/rules/registry.d.ts.map +1 -0
  74. package/dist/cpsat/rules/registry.js +28 -0
  75. package/dist/cpsat/rules/registry.js.map +1 -0
  76. package/dist/cpsat/rules/resolver.d.ts +31 -0
  77. package/dist/cpsat/rules/resolver.d.ts.map +1 -0
  78. package/dist/cpsat/rules/resolver.js +124 -0
  79. package/dist/cpsat/rules/resolver.js.map +1 -0
  80. package/dist/cpsat/rules/rules.types.d.ts +32 -0
  81. package/dist/cpsat/rules/rules.types.d.ts.map +1 -0
  82. package/dist/cpsat/rules/rules.types.js +2 -0
  83. package/dist/cpsat/rules/rules.types.js.map +1 -0
  84. package/dist/cpsat/rules/scoping.d.ts +129 -0
  85. package/dist/cpsat/rules/scoping.d.ts.map +1 -0
  86. package/dist/cpsat/rules/scoping.js +190 -0
  87. package/dist/cpsat/rules/scoping.js.map +1 -0
  88. package/dist/cpsat/rules/time-off.d.ts +78 -0
  89. package/dist/cpsat/rules/time-off.d.ts.map +1 -0
  90. package/dist/cpsat/rules/time-off.js +261 -0
  91. package/dist/cpsat/rules/time-off.js.map +1 -0
  92. package/dist/cpsat/rules.d.ts +5 -0
  93. package/dist/cpsat/rules.d.ts.map +1 -0
  94. package/dist/cpsat/rules.js +4 -0
  95. package/dist/cpsat/rules.js.map +1 -0
  96. package/dist/cpsat/semantic-time.d.ts +198 -0
  97. package/dist/cpsat/semantic-time.d.ts.map +1 -0
  98. package/dist/cpsat/semantic-time.js +222 -0
  99. package/dist/cpsat/semantic-time.js.map +1 -0
  100. package/dist/cpsat/types.d.ts +180 -0
  101. package/dist/cpsat/types.d.ts.map +1 -0
  102. package/dist/cpsat/types.js +2 -0
  103. package/dist/cpsat/types.js.map +1 -0
  104. package/dist/cpsat/utils.d.ts +47 -0
  105. package/dist/cpsat/utils.d.ts.map +1 -0
  106. package/dist/cpsat/utils.js +92 -0
  107. package/dist/cpsat/utils.js.map +1 -0
  108. package/dist/cpsat/validation-reporter.d.ts +54 -0
  109. package/dist/cpsat/validation-reporter.d.ts.map +1 -0
  110. package/dist/cpsat/validation-reporter.js +261 -0
  111. package/dist/cpsat/validation-reporter.js.map +1 -0
  112. package/dist/cpsat/validation.types.d.ts +141 -0
  113. package/dist/cpsat/validation.types.d.ts.map +1 -0
  114. package/dist/cpsat/validation.types.js +14 -0
  115. package/dist/cpsat/validation.types.js.map +1 -0
  116. package/dist/datetime.utils.d.ts +245 -0
  117. package/dist/datetime.utils.d.ts.map +1 -0
  118. package/dist/datetime.utils.js +372 -0
  119. package/dist/datetime.utils.js.map +1 -0
  120. package/dist/errors.d.ts +12 -0
  121. package/dist/errors.d.ts.map +1 -0
  122. package/dist/errors.js +17 -0
  123. package/dist/errors.js.map +1 -0
  124. package/dist/index.d.ts +112 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +116 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/llms.d.ts +5 -0
  129. package/dist/llms.d.ts.map +1 -0
  130. package/dist/llms.js +8 -0
  131. package/dist/llms.js.map +1 -0
  132. package/dist/testing/index.d.ts +12 -0
  133. package/dist/testing/index.d.ts.map +1 -0
  134. package/dist/testing/index.js +11 -0
  135. package/dist/testing/index.js.map +1 -0
  136. package/dist/testing/solver-container.d.ts +49 -0
  137. package/dist/testing/solver-container.d.ts.map +1 -0
  138. package/dist/testing/solver-container.js +127 -0
  139. package/dist/testing/solver-container.js.map +1 -0
  140. package/dist/types.d.ts +155 -0
  141. package/dist/types.d.ts.map +1 -0
  142. package/dist/types.js +20 -0
  143. package/dist/types.js.map +1 -0
  144. package/dist/validation.d.ts +105 -0
  145. package/dist/validation.d.ts.map +1 -0
  146. package/dist/validation.js +130 -0
  147. package/dist/validation.js.map +1 -0
  148. package/llms.txt +2188 -0
  149. package/package.json +76 -0
  150. package/solver/Dockerfile +31 -0
  151. package/solver/README.md +23 -0
  152. package/solver/pyproject.toml +28 -0
  153. package/solver/src/solver/__init__.py +1 -0
  154. package/solver/src/solver/app.py +24 -0
  155. package/solver/src/solver/models.py +120 -0
  156. package/solver/src/solver/solver.py +359 -0
  157. package/solver/tests/test_solver.py +156 -0
  158. package/solver/uv.lock +661 -0
  159. package/src/client.schemas.ts +163 -0
  160. package/src/client.ts +67 -0
  161. package/src/client.types.ts +66 -0
  162. package/src/cpsat/model-builder.ts +858 -0
  163. package/src/cpsat/response.ts +130 -0
  164. package/src/cpsat/rules/assign-together.ts +96 -0
  165. package/src/cpsat/rules/employee-assignment-priority.ts +182 -0
  166. package/src/cpsat/rules/index.ts +12 -0
  167. package/src/cpsat/rules/location-preference.ts +68 -0
  168. package/src/cpsat/rules/max-consecutive-days.ts +98 -0
  169. package/src/cpsat/rules/max-hours-day.ts +187 -0
  170. package/src/cpsat/rules/max-hours-week.ts +197 -0
  171. package/src/cpsat/rules/max-shifts-day.ts +198 -0
  172. package/src/cpsat/rules/min-consecutive-days.ts +140 -0
  173. package/src/cpsat/rules/min-hours-day.ts +69 -0
  174. package/src/cpsat/rules/min-hours-week.ts +77 -0
  175. package/src/cpsat/rules/min-rest-between-shifts.ts +121 -0
  176. package/src/cpsat/rules/registry.ts +49 -0
  177. package/src/cpsat/rules/resolver.ts +181 -0
  178. package/src/cpsat/rules/rules.types.ts +41 -0
  179. package/src/cpsat/rules/scoping.ts +340 -0
  180. package/src/cpsat/rules/time-off.ts +336 -0
  181. package/src/cpsat/rules.ts +27 -0
  182. package/src/cpsat/semantic-time.ts +463 -0
  183. package/src/cpsat/types.ts +194 -0
  184. package/src/cpsat/utils.ts +105 -0
  185. package/src/cpsat/validation-reporter.ts +366 -0
  186. package/src/cpsat/validation.types.ts +185 -0
  187. package/src/datetime.utils.ts +426 -0
  188. package/src/errors.ts +17 -0
  189. package/src/index.ts +289 -0
  190. package/src/llms.ts +9 -0
  191. package/src/testing/index.ts +12 -0
  192. package/src/testing/solver-container.ts +172 -0
  193. package/src/types.ts +191 -0
  194. package/src/validation.ts +188 -0
@@ -0,0 +1,198 @@
1
+ import type { DayOfWeek, TimeOfDay } from "../types.js";
2
+ import type { CoverageRequirement, Priority } from "./types.js";
3
+ import { type GroupKey } from "./validation.types.js";
4
+ /**
5
+ * Base definition for a semantic time period.
6
+ */
7
+ export interface SemanticTimeDef {
8
+ startTime: TimeOfDay;
9
+ endTime: TimeOfDay;
10
+ }
11
+ /**
12
+ * Variant of a semantic time that applies to specific days or dates.
13
+ */
14
+ export interface SemanticTimeVariant extends SemanticTimeDef {
15
+ /** Apply this variant only on these days of the week */
16
+ days?: DayOfWeek[];
17
+ /** Apply this variant only on these specific dates (YYYY-MM-DD) */
18
+ dates?: string[];
19
+ }
20
+ /**
21
+ * A semantic time can be a simple definition (applies every day)
22
+ * or an array of variants with different times for different days/dates.
23
+ */
24
+ export type SemanticTimeEntry = SemanticTimeDef | SemanticTimeVariant[];
25
+ /**
26
+ * Base fields shared by semantic coverage requirement variants.
27
+ */
28
+ interface SemanticCoverageRequirementBase<S extends string> {
29
+ semanticTime: S;
30
+ targetCount: number;
31
+ priority?: Priority;
32
+ /** Scope this requirement to specific days of the week */
33
+ days?: DayOfWeek[];
34
+ /** Scope this requirement to specific dates (YYYY-MM-DD) */
35
+ dates?: string[];
36
+ /**
37
+ * Override the auto-generated group key for validation reporting.
38
+ * If not provided, a key is auto-generated from the semantic time name,
39
+ * role/skills, and target count.
40
+ */
41
+ groupKey?: GroupKey;
42
+ }
43
+ /**
44
+ * Semantic coverage requiring specific roles, optionally filtered by skills.
45
+ */
46
+ interface RoleBasedSemanticCoverageRequirement<S extends string> extends SemanticCoverageRequirementBase<S> {
47
+ /**
48
+ * Roles that satisfy this coverage (OR logic).
49
+ * Must have at least one role.
50
+ */
51
+ roleIds: [string, ...string[]];
52
+ /**
53
+ * Additional skill filter (AND logic with roles).
54
+ */
55
+ skillIds?: [string, ...string[]];
56
+ }
57
+ /**
58
+ * Semantic coverage requiring specific skills only (any role).
59
+ */
60
+ interface SkillBasedSemanticCoverageRequirement<S extends string> extends SemanticCoverageRequirementBase<S> {
61
+ roleIds?: never;
62
+ /**
63
+ * Skills required (ALL required, AND logic).
64
+ * Must have at least one skill.
65
+ */
66
+ skillIds: [string, ...string[]];
67
+ }
68
+ /**
69
+ * Coverage requirement that references a semantic time by name.
70
+ * Type-safe: S is constrained to known semantic time names.
71
+ *
72
+ * This is a discriminated union enforcing at compile time that at least
73
+ * one of `roleIds` or `skillIds` must be provided.
74
+ */
75
+ export type SemanticCoverageRequirement<S extends string> = RoleBasedSemanticCoverageRequirement<S> | SkillBasedSemanticCoverageRequirement<S>;
76
+ /**
77
+ * Base fields shared by concrete coverage requirement variants.
78
+ */
79
+ interface ConcreteCoverageRequirementBase {
80
+ day: string;
81
+ startTime: TimeOfDay;
82
+ endTime: TimeOfDay;
83
+ targetCount: number;
84
+ priority?: Priority;
85
+ /**
86
+ * Override the auto-generated group key for validation reporting.
87
+ * If not provided, a key is auto-generated from the day, time range,
88
+ * role/skills, and target count.
89
+ */
90
+ groupKey?: GroupKey;
91
+ }
92
+ /**
93
+ * Concrete coverage requiring specific roles, optionally filtered by skills.
94
+ */
95
+ interface RoleBasedConcreteCoverageRequirement extends ConcreteCoverageRequirementBase {
96
+ /**
97
+ * Roles that satisfy this coverage (OR logic).
98
+ * Must have at least one role.
99
+ */
100
+ roleIds: [string, ...string[]];
101
+ /**
102
+ * Additional skill filter (AND logic with roles).
103
+ */
104
+ skillIds?: [string, ...string[]];
105
+ }
106
+ /**
107
+ * Concrete coverage requiring specific skills only (any role).
108
+ */
109
+ interface SkillBasedConcreteCoverageRequirement extends ConcreteCoverageRequirementBase {
110
+ roleIds?: never;
111
+ /**
112
+ * Skills required (ALL required, AND logic).
113
+ * Must have at least one skill.
114
+ */
115
+ skillIds: [string, ...string[]];
116
+ }
117
+ /**
118
+ * Concrete coverage requirement with explicit day and times.
119
+ * Used for one-off requirements that don't fit a semantic time.
120
+ *
121
+ * This is a discriminated union enforcing at compile time that at least
122
+ * one of `roleIds` or `skillIds` must be provided.
123
+ */
124
+ export type ConcreteCoverageRequirement = RoleBasedConcreteCoverageRequirement | SkillBasedConcreteCoverageRequirement;
125
+ /**
126
+ * Union type for coverage - either semantic (type-safe) or concrete.
127
+ */
128
+ export type MixedCoverageRequirement<S extends string> = SemanticCoverageRequirement<S> | ConcreteCoverageRequirement;
129
+ /**
130
+ * Type guard to check if a requirement is concrete (has explicit day/times).
131
+ */
132
+ export declare function isConcreteCoverage<S extends string>(req: MixedCoverageRequirement<S>): req is ConcreteCoverageRequirement;
133
+ /**
134
+ * Type guard to check if a requirement is semantic (references a named time).
135
+ */
136
+ export declare function isSemanticCoverage<S extends string>(req: MixedCoverageRequirement<S>): req is SemanticCoverageRequirement<S>;
137
+ /**
138
+ * Result of defineSemanticTimes - provides type-safe coverage function.
139
+ */
140
+ export interface SemanticTimeContext<S extends string> {
141
+ /** The semantic time definitions */
142
+ readonly defs: Record<S, SemanticTimeEntry>;
143
+ /**
144
+ * Create coverage requirements with type-safe semantic time names.
145
+ * Accepts both semantic references and concrete one-off requirements.
146
+ */
147
+ coverage(reqs: MixedCoverageRequirement<S>[]): MixedCoverageRequirement<S>[];
148
+ /**
149
+ * Resolve all coverage requirements to concrete CoverageRequirement[]
150
+ * for the given days in the scheduling horizon.
151
+ */
152
+ resolve(reqs: MixedCoverageRequirement<S>[], days: string[]): CoverageRequirement[];
153
+ }
154
+ /**
155
+ * Define semantic times with type-safe names.
156
+ *
157
+ * Returns a context object that provides:
158
+ * - Type-safe coverage() function that only accepts defined semantic time names
159
+ * - resolve() function to expand semantic times to concrete requirements
160
+ *
161
+ * @example Basic usage
162
+ * ```typescript
163
+ * const times = defineSemanticTimes({
164
+ * opening: { startTime: { hours: 6 }, endTime: { hours: 8 } },
165
+ * lunch: { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 } },
166
+ * closing: { startTime: { hours: 21 }, endTime: { hours: 23 } },
167
+ * });
168
+ *
169
+ * const coverage = times.coverage([
170
+ * { semanticTime: "lunch", roleId: "server", targetCount: 3 },
171
+ * { semanticTime: "opening", roleId: "keyholder", targetCount: 1, priority: "MANDATORY" },
172
+ * // Type error: "dinner" is not a defined semantic time
173
+ * // { semanticTime: "dinner", roleId: "server", targetCount: 2 },
174
+ * ]);
175
+ * ```
176
+ *
177
+ * @example Variants for different days
178
+ * ```typescript
179
+ * const times = defineSemanticTimes({
180
+ * lunch: [
181
+ * { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 }, days: ["monday", "tuesday", "wednesday", "thursday", "friday"] },
182
+ * { startTime: { hours: 12 }, endTime: { hours: 15 }, days: ["saturday", "sunday"] },
183
+ * ],
184
+ * });
185
+ * ```
186
+ *
187
+ * @example Mixed semantic and concrete coverage
188
+ * ```typescript
189
+ * const coverage = times.coverage([
190
+ * { semanticTime: "lunch", roleId: "server", targetCount: 3 },
191
+ * // One-off party - concrete time
192
+ * { day: "2026-01-14", startTime: { hours: 15 }, endTime: { hours: 20 }, roleId: "server", targetCount: 5 },
193
+ * ]);
194
+ * ```
195
+ */
196
+ export declare function defineSemanticTimes<const T extends Record<string, SemanticTimeEntry>>(defs: T): SemanticTimeContext<keyof T & string>;
197
+ export {};
198
+ //# sourceMappingURL=semantic-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-time.d.ts","sourceRoot":"","sources":["../../src/cpsat/semantic-time.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ import { toDayOfWeekUTC } from "../datetime.utils.js";
2
+ import { parseDayString } from "./utils.js";
3
+ import { groupKey } from "./validation.types.js";
4
+ /**
5
+ * Type guard to check if a requirement is concrete (has explicit day/times).
6
+ */
7
+ export function isConcreteCoverage(req) {
8
+ return "day" in req && "startTime" in req && "endTime" in req;
9
+ }
10
+ /**
11
+ * Type guard to check if a requirement is semantic (references a named time).
12
+ */
13
+ export function isSemanticCoverage(req) {
14
+ return "semanticTime" in req;
15
+ }
16
+ /**
17
+ * Define semantic times with type-safe names.
18
+ *
19
+ * Returns a context object that provides:
20
+ * - Type-safe coverage() function that only accepts defined semantic time names
21
+ * - resolve() function to expand semantic times to concrete requirements
22
+ *
23
+ * @example Basic usage
24
+ * ```typescript
25
+ * const times = defineSemanticTimes({
26
+ * opening: { startTime: { hours: 6 }, endTime: { hours: 8 } },
27
+ * lunch: { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 } },
28
+ * closing: { startTime: { hours: 21 }, endTime: { hours: 23 } },
29
+ * });
30
+ *
31
+ * const coverage = times.coverage([
32
+ * { semanticTime: "lunch", roleId: "server", targetCount: 3 },
33
+ * { semanticTime: "opening", roleId: "keyholder", targetCount: 1, priority: "MANDATORY" },
34
+ * // Type error: "dinner" is not a defined semantic time
35
+ * // { semanticTime: "dinner", roleId: "server", targetCount: 2 },
36
+ * ]);
37
+ * ```
38
+ *
39
+ * @example Variants for different days
40
+ * ```typescript
41
+ * const times = defineSemanticTimes({
42
+ * lunch: [
43
+ * { startTime: { hours: 11, minutes: 30 }, endTime: { hours: 14 }, days: ["monday", "tuesday", "wednesday", "thursday", "friday"] },
44
+ * { startTime: { hours: 12 }, endTime: { hours: 15 }, days: ["saturday", "sunday"] },
45
+ * ],
46
+ * });
47
+ * ```
48
+ *
49
+ * @example Mixed semantic and concrete coverage
50
+ * ```typescript
51
+ * const coverage = times.coverage([
52
+ * { semanticTime: "lunch", roleId: "server", targetCount: 3 },
53
+ * // One-off party - concrete time
54
+ * { day: "2026-01-14", startTime: { hours: 15 }, endTime: { hours: 20 }, roleId: "server", targetCount: 5 },
55
+ * ]);
56
+ * ```
57
+ */
58
+ export function defineSemanticTimes(defs) {
59
+ return {
60
+ defs: defs,
61
+ coverage(reqs) {
62
+ return reqs;
63
+ },
64
+ resolve(reqs, days) {
65
+ return resolveSemanticCoverage(defs, reqs, days);
66
+ },
67
+ };
68
+ }
69
+ /**
70
+ * Build a CoverageRequirement from resolved fields.
71
+ * Handles the discriminated union by checking which variant to construct.
72
+ */
73
+ function buildCoverageRequirement(day, startTime, endTime, roleIds, skillIds, targetCount, priority, gKey) {
74
+ const base = { day, startTime, endTime, targetCount, priority, groupKey: gKey };
75
+ if (roleIds && roleIds.length > 0) {
76
+ // Role-based (with optional skills)
77
+ return skillIds && skillIds.length > 0
78
+ ? { ...base, roleIds, skillIds }
79
+ : { ...base, roleIds };
80
+ }
81
+ else if (skillIds && skillIds.length > 0) {
82
+ // Skill-only
83
+ return { ...base, skillIds };
84
+ }
85
+ // This shouldn't happen if input types are correct, but handle gracefully
86
+ throw new Error(`Coverage requirement for day "${day}" must have at least one of roleIds or skillIds`);
87
+ }
88
+ /**
89
+ * Resolve semantic coverage requirements to concrete requirements.
90
+ * Internal implementation.
91
+ */
92
+ function resolveSemanticCoverage(defs, reqs, days) {
93
+ const result = [];
94
+ const daySet = new Set(days);
95
+ for (const req of reqs) {
96
+ if (isConcreteCoverage(req)) {
97
+ // Concrete requirement - pass through if day is in horizon
98
+ if (daySet.has(req.day)) {
99
+ const autoGroupKey = generateConcreteGroupKey(req);
100
+ result.push(buildCoverageRequirement(req.day, req.startTime, req.endTime, req.roleIds, req.skillIds, req.targetCount, req.priority ?? "MANDATORY", req.groupKey ?? autoGroupKey));
101
+ }
102
+ }
103
+ else {
104
+ // Semantic requirement - resolve for each applicable day
105
+ const entry = defs[req.semanticTime];
106
+ if (!entry) {
107
+ throw new Error(`Unknown semantic time: ${req.semanticTime}`);
108
+ }
109
+ const autoGroupKey = generateSemanticGroupKey(req);
110
+ const applicableDays = filterDays(days, req.days, req.dates);
111
+ for (const day of applicableDays) {
112
+ const resolved = resolveTimeForDay(entry, day);
113
+ if (resolved) {
114
+ result.push(buildCoverageRequirement(day, resolved.startTime, resolved.endTime, req.roleIds, req.skillIds, req.targetCount, req.priority ?? "MANDATORY", req.groupKey ?? autoGroupKey));
115
+ }
116
+ }
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Generates a human-readable group key for a semantic coverage requirement.
123
+ * Format: "{count}x {role/skills} during {semanticTime}" with optional scope
124
+ */
125
+ function generateSemanticGroupKey(req) {
126
+ const roleOrSkills = req.roleIds?.join("/") ?? req.skillIds?.join("+") ?? "staff";
127
+ const base = `${req.targetCount}x ${roleOrSkills} during ${req.semanticTime}`;
128
+ // Add scope qualifier if scoped to specific days
129
+ if (req.days && req.days.length > 0 && req.days.length < 7) {
130
+ return groupKey(`${base} (${formatDaysScope(req.days)})`);
131
+ }
132
+ if (req.dates && req.dates.length > 0) {
133
+ return groupKey(`${base} (specific dates)`);
134
+ }
135
+ return groupKey(base);
136
+ }
137
+ /**
138
+ * Generates a human-readable group key for a concrete coverage requirement.
139
+ * Format: "{count}x {role/skills} on {day} {time}"
140
+ */
141
+ function generateConcreteGroupKey(req) {
142
+ const roleOrSkills = req.roleIds?.join("/") ?? req.skillIds?.join("+") ?? "staff";
143
+ const timeRange = `${formatTime(req.startTime)}-${formatTime(req.endTime)}`;
144
+ return groupKey(`${req.targetCount}x ${roleOrSkills} on ${req.day} ${timeRange}`);
145
+ }
146
+ /**
147
+ * Formats days of week for display in group keys.
148
+ */
149
+ function formatDaysScope(days) {
150
+ if (days.length === 0)
151
+ return "";
152
+ if (days.length === 1)
153
+ return days[0];
154
+ // Check for common patterns
155
+ const weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
156
+ const weekend = ["saturday", "sunday"];
157
+ const sorted = [...days].sort((a, b) => weekdays.indexOf(a) - weekdays.indexOf(b));
158
+ if (sorted.length === 5 && weekdays.every((d) => sorted.includes(d))) {
159
+ return "weekdays";
160
+ }
161
+ if (sorted.length === 2 && weekend.every((d) => sorted.includes(d))) {
162
+ return "weekends";
163
+ }
164
+ // Abbreviate day names
165
+ return sorted.map((d) => d.slice(0, 3)).join(", ");
166
+ }
167
+ /**
168
+ * Formats a TimeOfDay for display.
169
+ */
170
+ function formatTime(time) {
171
+ const hours = String(time.hours).padStart(2, "0");
172
+ const minutes = String(time.minutes).padStart(2, "0");
173
+ return `${hours}:${minutes}`;
174
+ }
175
+ /**
176
+ * Filter days based on optional day-of-week and specific date constraints.
177
+ */
178
+ function filterDays(allDays, daysOfWeek, specificDates) {
179
+ let result = allDays;
180
+ if (specificDates && specificDates.length > 0) {
181
+ // If specific dates are provided, use only those (intersection with allDays)
182
+ const dateSet = new Set(specificDates);
183
+ result = result.filter((d) => dateSet.has(d));
184
+ }
185
+ else if (daysOfWeek && daysOfWeek.length > 0) {
186
+ // Filter by day of week
187
+ const daySet = new Set(daysOfWeek);
188
+ result = result.filter((d) => daySet.has(toDayOfWeekUTC(parseDayString(d))));
189
+ }
190
+ return result;
191
+ }
192
+ /**
193
+ * Resolve the time definition for a specific day.
194
+ * Returns the most specific match: date-specific > day-of-week > default.
195
+ */
196
+ function resolveTimeForDay(entry, day) {
197
+ // Simple definition - applies to all days
198
+ if (!Array.isArray(entry)) {
199
+ return entry;
200
+ }
201
+ const date = parseDayString(day);
202
+ const dayOfWeek = toDayOfWeekUTC(date);
203
+ // Find best match: date-specific first, then day-of-week, then default
204
+ let dateMatch = null;
205
+ let dowMatch = null;
206
+ let defaultMatch = null;
207
+ for (const variant of entry) {
208
+ if (variant.dates && variant.dates.includes(day)) {
209
+ dateMatch = variant;
210
+ break; // Date-specific is most specific
211
+ }
212
+ if (variant.days && variant.days.includes(dayOfWeek)) {
213
+ dowMatch = variant;
214
+ }
215
+ if (!variant.dates && !variant.days) {
216
+ defaultMatch = variant;
217
+ }
218
+ }
219
+ const match = dateMatch ?? dowMatch ?? defaultMatch;
220
+ return match ?? null;
221
+ }
222
+ //# sourceMappingURL=semantic-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-time.js","sourceRoot":"","sources":["../../src/cpsat/semantic-time.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ import type { TimeOfDay, DayOfWeek } from "../types.js";
2
+ import type { SolverRequest, SolverTerm } from "../client.types.js";
3
+ import type { GroupKey } from "./validation.types.js";
4
+ export type Priority = "LOW" | "MEDIUM" | "HIGH" | "MANDATORY";
5
+ export interface SchedulingEmployee {
6
+ id: string;
7
+ roleIds: string[];
8
+ skillIds?: string[];
9
+ }
10
+ /**
11
+ * @deprecated Use SchedulingEmployee instead. This alias exists for backwards compatibility.
12
+ */
13
+ export type Employee = SchedulingEmployee;
14
+ /**
15
+ * A shift pattern defines WHEN people can work — the time slots available for assignment.
16
+ *
17
+ * Shift patterns are templates that repeat across all scheduling days. The solver assigns
18
+ * team members to these patterns based on coverage requirements and constraints.
19
+ *
20
+ * @example
21
+ * // Simple venue: one shift type, anyone can work it
22
+ * const patterns: ShiftPattern[] = [
23
+ * { id: "day", startTime: { hours: 9 }, endTime: { hours: 17 } }
24
+ * ];
25
+ *
26
+ * @example
27
+ * // Restaurant: different shifts for different roles
28
+ * const patterns: ShiftPattern[] = [
29
+ * { id: "kitchen_morning", startTime: { hours: 6 }, endTime: { hours: 14 }, roleIds: ["chef", "prep_cook"] },
30
+ * { id: "floor_lunch", startTime: { hours: 11 }, endTime: { hours: 15 }, roleIds: ["waiter", "host"] },
31
+ * ];
32
+ */
33
+ export interface ShiftPattern {
34
+ /**
35
+ * Unique identifier for this shift pattern.
36
+ * Used in assignments and rule configurations.
37
+ */
38
+ id: string;
39
+ /**
40
+ * Restricts who can be assigned to this shift based on their roles.
41
+ *
42
+ * - If omitted: anyone can work this shift
43
+ * - If provided: only team members whose roleIds overlap with this list can be assigned
44
+ *
45
+ * Most venues have the same shifts for everyone and don't need this.
46
+ * Use it when different roles have different schedules (e.g., kitchen staff starts
47
+ * earlier than floor staff).
48
+ */
49
+ roleIds?: [string, ...string[]];
50
+ /**
51
+ * Restricts which days of the week this shift pattern can be used.
52
+ *
53
+ * - If omitted: shift can be used on any day
54
+ * - If provided: shift can only be assigned on the specified days
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Saturday-only short shift
59
+ * { id: "saturday_shift", startTime: t(9), endTime: t(14), daysOfWeek: ["saturday"] }
60
+ *
61
+ * // Weekday-only full shift
62
+ * { id: "full_shift", startTime: t(9), endTime: t(18), daysOfWeek: ["monday", "tuesday", "wednesday", "thursday", "friday"] }
63
+ * ```
64
+ */
65
+ daysOfWeek?: DayOfWeek[];
66
+ /**
67
+ * Physical location where this shift takes place.
68
+ * Used for multi-location scheduling and location-based constraints.
69
+ */
70
+ locationId?: string;
71
+ /** When the shift starts (e.g., { hours: 9, minutes: 0 } for 9:00 AM) */
72
+ startTime: TimeOfDay;
73
+ /** When the shift ends (e.g., { hours: 17, minutes: 30 } for 5:30 PM) */
74
+ endTime: TimeOfDay;
75
+ }
76
+ export interface TimeInterval {
77
+ day: string;
78
+ startTime: TimeOfDay;
79
+ endTime: TimeOfDay;
80
+ }
81
+ /**
82
+ * Base fields shared by all coverage requirement variants.
83
+ */
84
+ interface CoverageRequirementBase extends TimeInterval {
85
+ targetCount: number;
86
+ priority: Priority;
87
+ /**
88
+ * Groups this requirement with others sharing the same key for validation reporting.
89
+ * When provided, all coverage constraints generated from this requirement will
90
+ * share the same groupKey, enabling meaningful aggregation in reports.
91
+ *
92
+ * If not provided, an auto-generated key will be used based on the coverage parameters.
93
+ */
94
+ groupKey?: GroupKey;
95
+ }
96
+ /**
97
+ * Coverage requiring specific roles, optionally filtered by skills.
98
+ * Team members must have ANY of the specified roles (OR logic).
99
+ * If skillIds provided, they must ALSO have ALL specified skills (AND logic).
100
+ */
101
+ interface RoleBasedCoverageRequirement extends CoverageRequirementBase {
102
+ /**
103
+ * Roles that satisfy this coverage (OR logic).
104
+ * A person matches if they have ANY of these roles.
105
+ * Must have at least one role.
106
+ */
107
+ roleIds: [string, ...string[]];
108
+ /**
109
+ * Additional skill filter (AND logic with roles).
110
+ * If provided, team members must have ALL specified skills in addition to matching a role.
111
+ */
112
+ skillIds?: [string, ...string[]];
113
+ }
114
+ /**
115
+ * Coverage requiring specific skills only (any role).
116
+ * Team members must have ALL specified skills (AND logic).
117
+ */
118
+ interface SkillBasedCoverageRequirement extends CoverageRequirementBase {
119
+ /**
120
+ * Must not be present for skill-based coverage.
121
+ */
122
+ roleIds?: never;
123
+ /**
124
+ * Skills required to satisfy this coverage (ALL required, AND logic).
125
+ * Must have at least one skill.
126
+ */
127
+ skillIds: [string, ...string[]];
128
+ }
129
+ /**
130
+ * Defines staffing needs for a specific time period.
131
+ *
132
+ * This is a discriminated union that enforces at compile time that at least
133
+ * one of `roleIds` or `skillIds` must be provided:
134
+ *
135
+ * - Role-based: `{ roleIds: ["waiter"], ... }` - anyone with ANY of these roles (OR logic)
136
+ * - Role + skill: `{ roleIds: ["waiter"], skillIds: ["senior"], ... }` - role AND skills
137
+ * - Skill-only: `{ skillIds: ["keyholder"], ... }` - any role with ALL skills (AND logic)
138
+ *
139
+ * @example
140
+ * // Need 2 waiters during lunch (role-based)
141
+ * { day: "2024-01-01", startTime: { hours: 11 }, endTime: { hours: 14 }, roleIds: ["waiter"], targetCount: 2, priority: "MANDATORY" }
142
+ *
143
+ * @example
144
+ * // Need 1 manager OR supervisor during service (OR logic on roles)
145
+ * { day: "2024-01-01", startTime: { hours: 11 }, endTime: { hours: 22 }, roleIds: ["manager", "supervisor"], targetCount: 1, priority: "MANDATORY" }
146
+ *
147
+ * @example
148
+ * // Need 1 keyholder for opening (skill-only, any role)
149
+ * { day: "2024-01-01", startTime: { hours: 6 }, endTime: { hours: 8 }, skillIds: ["keyholder"], targetCount: 1, priority: "MANDATORY" }
150
+ *
151
+ * @example
152
+ * // Need 1 senior waiter for training shift (role + skill filter)
153
+ * { day: "2024-01-01", startTime: { hours: 9 }, endTime: { hours: 17 }, roleIds: ["waiter"], skillIds: ["senior"], targetCount: 1, priority: "HIGH" }
154
+ */
155
+ export type CoverageRequirement = RoleBasedCoverageRequirement | SkillBasedCoverageRequirement;
156
+ export interface ModelBuilderOptions {
157
+ weekStartsOn?: DayOfWeek;
158
+ solverOptions?: SolverRequest["options"];
159
+ /**
160
+ * Bucket size used when translating coverage requirements into time-indexed constraints.
161
+ * Smaller buckets are more accurate but increase the number of constraints.
162
+ */
163
+ coverageBucketMinutes?: number;
164
+ /**
165
+ * Whether to enable fair distribution of shifts across team members.
166
+ *
167
+ * When enabled (default), the solver minimizes the maximum number of shifts
168
+ * any single person works, ensuring work is distributed evenly. Each person
169
+ * works between floor(total/n) and ceil(total/n) shifts.
170
+ *
171
+ * Disable this if you want other rules (like employee-assignment-priority)
172
+ * to have full control over shift distribution.
173
+ *
174
+ * @default true
175
+ */
176
+ fairDistribution?: boolean;
177
+ }
178
+ export type Term = SolverTerm;
179
+ export {};
180
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cpsat/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,kBAAkB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAEhC;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IAEzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,yEAAyE;IACzE,SAAS,EAAE,SAAS,CAAC;IAErB,yEAAyE;IACzE,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,SAAS,CAAC;CACpB;AAED;;GAEG;AACH,UAAU,uBAAwB,SAAQ,YAAY;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;GAIG;AACH,UAAU,4BAA6B,SAAQ,uBAAuB;IACpE;;;;OAIG;IACH,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,UAAU,6BAA8B,SAAQ,uBAAuB;IACrE;;OAEG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,mBAAmB,GAAG,4BAA4B,GAAG,6BAA6B,CAAC;AAE/F,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACzC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,IAAI,GAAG,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cpsat/types.ts"],"names":[],"mappings":""}