dabke 0.83.0 → 0.84.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 (330) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +22 -27
  3. package/dist/client.d.ts +39 -3
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/client.schemas.d.ts +118 -18
  7. package/dist/client.schemas.d.ts.map +1 -1
  8. package/dist/client.schemas.js +110 -6
  9. package/dist/client.schemas.js.map +1 -1
  10. package/dist/client.types.d.ts +63 -7
  11. package/dist/client.types.d.ts.map +1 -1
  12. package/dist/client.types.js.map +1 -1
  13. package/dist/cpsat/cost.d.ts +5 -2
  14. package/dist/cpsat/cost.d.ts.map +1 -1
  15. package/dist/cpsat/cost.js +13 -6
  16. package/dist/cpsat/cost.js.map +1 -1
  17. package/dist/cpsat/model-builder.d.ts +42 -61
  18. package/dist/cpsat/model-builder.d.ts.map +1 -1
  19. package/dist/cpsat/model-builder.js +451 -56
  20. package/dist/cpsat/model-builder.js.map +1 -1
  21. package/dist/cpsat/response.d.ts +19 -7
  22. package/dist/cpsat/response.d.ts.map +1 -1
  23. package/dist/cpsat/response.js +48 -9
  24. package/dist/cpsat/response.js.map +1 -1
  25. package/dist/cpsat/rule-descriptor.d.ts +250 -0
  26. package/dist/cpsat/rule-descriptor.d.ts.map +1 -0
  27. package/dist/cpsat/rule-descriptor.js +51 -0
  28. package/dist/cpsat/rule-descriptor.js.map +1 -0
  29. package/dist/cpsat/rules/artifacts.d.ts +9 -0
  30. package/dist/cpsat/rules/artifacts.d.ts.map +1 -0
  31. package/dist/cpsat/rules/artifacts.js +31 -0
  32. package/dist/cpsat/rules/artifacts.js.map +1 -0
  33. package/dist/cpsat/rules/assign-together.d.ts +18 -20
  34. package/dist/cpsat/rules/assign-together.d.ts.map +1 -1
  35. package/dist/cpsat/rules/assign-together.js +90 -54
  36. package/dist/cpsat/rules/assign-together.js.map +1 -1
  37. package/dist/cpsat/rules/assigned-day.d.ts +12 -0
  38. package/dist/cpsat/rules/assigned-day.d.ts.map +1 -0
  39. package/dist/cpsat/rules/assigned-day.js +43 -0
  40. package/dist/cpsat/rules/assigned-day.js.map +1 -0
  41. package/dist/cpsat/rules/assignment-priority.d.ts +15 -29
  42. package/dist/cpsat/rules/assignment-priority.d.ts.map +1 -1
  43. package/dist/cpsat/rules/assignment-priority.js +47 -50
  44. package/dist/cpsat/rules/assignment-priority.js.map +1 -1
  45. package/dist/cpsat/rules/assignment-terms.d.ts +6 -0
  46. package/dist/cpsat/rules/assignment-terms.d.ts.map +1 -0
  47. package/dist/cpsat/rules/assignment-terms.js +19 -0
  48. package/dist/cpsat/rules/assignment-terms.js.map +1 -0
  49. package/dist/cpsat/rules/concurrent-intervals.d.ts +19 -0
  50. package/dist/cpsat/rules/concurrent-intervals.d.ts.map +1 -0
  51. package/dist/cpsat/rules/concurrent-intervals.js +57 -0
  52. package/dist/cpsat/rules/concurrent-intervals.js.map +1 -0
  53. package/dist/cpsat/rules/cost-utils.d.ts +1 -3
  54. package/dist/cpsat/rules/cost-utils.d.ts.map +1 -1
  55. package/dist/cpsat/rules/cost-utils.js +0 -7
  56. package/dist/cpsat/rules/cost-utils.js.map +1 -1
  57. package/dist/cpsat/rules/day-cost-multiplier.d.ts +10 -10
  58. package/dist/cpsat/rules/day-cost-multiplier.d.ts.map +1 -1
  59. package/dist/cpsat/rules/day-cost-multiplier.js +88 -77
  60. package/dist/cpsat/rules/day-cost-multiplier.js.map +1 -1
  61. package/dist/cpsat/rules/day-cost-surcharge.d.ts +10 -10
  62. package/dist/cpsat/rules/day-cost-surcharge.d.ts.map +1 -1
  63. package/dist/cpsat/rules/day-cost-surcharge.js +76 -65
  64. package/dist/cpsat/rules/day-cost-surcharge.js.map +1 -1
  65. package/dist/cpsat/rules/index.d.ts +30 -24
  66. package/dist/cpsat/rules/index.d.ts.map +1 -1
  67. package/dist/cpsat/rules/index.js +30 -24
  68. package/dist/cpsat/rules/index.js.map +1 -1
  69. package/dist/cpsat/rules/location-preference.d.ts +14 -21
  70. package/dist/cpsat/rules/location-preference.d.ts.map +1 -1
  71. package/dist/cpsat/rules/location-preference.js +45 -45
  72. package/dist/cpsat/rules/location-preference.js.map +1 -1
  73. package/dist/cpsat/rules/max-concurrent-assignments.d.ts +46 -0
  74. package/dist/cpsat/rules/max-concurrent-assignments.d.ts.map +1 -0
  75. package/dist/cpsat/rules/max-concurrent-assignments.js +121 -0
  76. package/dist/cpsat/rules/max-concurrent-assignments.js.map +1 -0
  77. package/dist/cpsat/rules/max-consecutive-days.d.ts +18 -17
  78. package/dist/cpsat/rules/max-consecutive-days.d.ts.map +1 -1
  79. package/dist/cpsat/rules/max-consecutive-days.js +67 -66
  80. package/dist/cpsat/rules/max-consecutive-days.js.map +1 -1
  81. package/dist/cpsat/rules/max-days-of-week-per-period.d.ts +45 -0
  82. package/dist/cpsat/rules/max-days-of-week-per-period.d.ts.map +1 -0
  83. package/dist/cpsat/rules/max-days-of-week-per-period.js +95 -0
  84. package/dist/cpsat/rules/max-days-of-week-per-period.js.map +1 -0
  85. package/dist/cpsat/rules/max-days-week.d.ts +17 -31
  86. package/dist/cpsat/rules/max-days-week.d.ts.map +1 -1
  87. package/dist/cpsat/rules/max-days-week.js +69 -81
  88. package/dist/cpsat/rules/max-days-week.js.map +1 -1
  89. package/dist/cpsat/rules/max-hours-day.d.ts +14 -31
  90. package/dist/cpsat/rules/max-hours-day.d.ts.map +1 -1
  91. package/dist/cpsat/rules/max-hours-day.js +56 -72
  92. package/dist/cpsat/rules/max-hours-day.js.map +1 -1
  93. package/dist/cpsat/rules/max-hours-week.d.ts +15 -31
  94. package/dist/cpsat/rules/max-hours-week.d.ts.map +1 -1
  95. package/dist/cpsat/rules/max-hours-week.js +60 -75
  96. package/dist/cpsat/rules/max-hours-week.js.map +1 -1
  97. package/dist/cpsat/rules/max-shifts-day.d.ts +17 -33
  98. package/dist/cpsat/rules/max-shifts-day.d.ts.map +1 -1
  99. package/dist/cpsat/rules/max-shifts-day.js +57 -73
  100. package/dist/cpsat/rules/max-shifts-day.js.map +1 -1
  101. package/dist/cpsat/rules/min-consecutive-days.d.ts +18 -18
  102. package/dist/cpsat/rules/min-consecutive-days.d.ts.map +1 -1
  103. package/dist/cpsat/rules/min-consecutive-days.js +123 -94
  104. package/dist/cpsat/rules/min-consecutive-days.js.map +1 -1
  105. package/dist/cpsat/rules/min-days-of-week-per-period.d.ts +35 -0
  106. package/dist/cpsat/rules/min-days-of-week-per-period.d.ts.map +1 -0
  107. package/dist/cpsat/rules/min-days-of-week-per-period.js +108 -0
  108. package/dist/cpsat/rules/min-days-of-week-per-period.js.map +1 -0
  109. package/dist/cpsat/rules/min-days-week.d.ts +13 -26
  110. package/dist/cpsat/rules/min-days-week.d.ts.map +1 -1
  111. package/dist/cpsat/rules/min-days-week.js +85 -74
  112. package/dist/cpsat/rules/min-days-week.js.map +1 -1
  113. package/dist/cpsat/rules/min-hours-day.d.ts +14 -17
  114. package/dist/cpsat/rules/min-hours-day.d.ts.map +1 -1
  115. package/dist/cpsat/rules/min-hours-day.js +72 -54
  116. package/dist/cpsat/rules/min-hours-day.js.map +1 -1
  117. package/dist/cpsat/rules/min-hours-week.d.ts +15 -18
  118. package/dist/cpsat/rules/min-hours-week.d.ts.map +1 -1
  119. package/dist/cpsat/rules/min-hours-week.js +75 -57
  120. package/dist/cpsat/rules/min-hours-week.js.map +1 -1
  121. package/dist/cpsat/rules/min-rest-between-shifts.d.ts +14 -17
  122. package/dist/cpsat/rules/min-rest-between-shifts.d.ts.map +1 -1
  123. package/dist/cpsat/rules/min-rest-between-shifts.js +148 -138
  124. package/dist/cpsat/rules/min-rest-between-shifts.js.map +1 -1
  125. package/dist/cpsat/rules/minimize-cost.d.ts +2 -18
  126. package/dist/cpsat/rules/minimize-cost.d.ts.map +1 -1
  127. package/dist/cpsat/rules/minimize-cost.js +151 -163
  128. package/dist/cpsat/rules/minimize-cost.js.map +1 -1
  129. package/dist/cpsat/rules/must-assign.d.ts +5 -42
  130. package/dist/cpsat/rules/must-assign.d.ts.map +1 -1
  131. package/dist/cpsat/rules/must-assign.js +43 -75
  132. package/dist/cpsat/rules/must-assign.js.map +1 -1
  133. package/dist/cpsat/rules/overtime-daily-multiplier.d.ts +6 -10
  134. package/dist/cpsat/rules/overtime-daily-multiplier.d.ts.map +1 -1
  135. package/dist/cpsat/rules/overtime-daily-multiplier.js +118 -112
  136. package/dist/cpsat/rules/overtime-daily-multiplier.js.map +1 -1
  137. package/dist/cpsat/rules/overtime-daily-surcharge.d.ts +6 -11
  138. package/dist/cpsat/rules/overtime-daily-surcharge.d.ts.map +1 -1
  139. package/dist/cpsat/rules/overtime-daily-surcharge.js +104 -99
  140. package/dist/cpsat/rules/overtime-daily-surcharge.js.map +1 -1
  141. package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts +9 -21
  142. package/dist/cpsat/rules/overtime-tiered-multiplier.d.ts.map +1 -1
  143. package/dist/cpsat/rules/overtime-tiered-multiplier.js +181 -199
  144. package/dist/cpsat/rules/overtime-tiered-multiplier.js.map +1 -1
  145. package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts +13 -12
  146. package/dist/cpsat/rules/overtime-weekly-multiplier.d.ts.map +1 -1
  147. package/dist/cpsat/rules/overtime-weekly-multiplier.js +143 -143
  148. package/dist/cpsat/rules/overtime-weekly-multiplier.js.map +1 -1
  149. package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts +7 -11
  150. package/dist/cpsat/rules/overtime-weekly-surcharge.d.ts.map +1 -1
  151. package/dist/cpsat/rules/overtime-weekly-surcharge.js +124 -119
  152. package/dist/cpsat/rules/overtime-weekly-surcharge.js.map +1 -1
  153. package/dist/cpsat/rules/pattern-eligibility.d.ts +6 -0
  154. package/dist/cpsat/rules/pattern-eligibility.d.ts.map +1 -0
  155. package/dist/cpsat/rules/pattern-eligibility.js +16 -0
  156. package/dist/cpsat/rules/pattern-eligibility.js.map +1 -0
  157. package/dist/cpsat/rules/pattern-time.d.ts +5 -0
  158. package/dist/cpsat/rules/pattern-time.d.ts.map +1 -0
  159. package/dist/cpsat/rules/pattern-time.js +20 -0
  160. package/dist/cpsat/rules/pattern-time.js.map +1 -0
  161. package/dist/cpsat/rules/registry.d.ts +217 -30
  162. package/dist/cpsat/rules/registry.d.ts.map +1 -1
  163. package/dist/cpsat/rules/registry.js +98 -34
  164. package/dist/cpsat/rules/registry.js.map +1 -1
  165. package/dist/cpsat/rules/resolver.d.ts +9 -7
  166. package/dist/cpsat/rules/resolver.d.ts.map +1 -1
  167. package/dist/cpsat/rules/resolver.js +19 -18
  168. package/dist/cpsat/rules/resolver.js.map +1 -1
  169. package/dist/cpsat/rules/role-preference.d.ts +24 -0
  170. package/dist/cpsat/rules/role-preference.d.ts.map +1 -0
  171. package/dist/cpsat/rules/role-preference.js +72 -0
  172. package/dist/cpsat/rules/role-preference.js.map +1 -0
  173. package/dist/cpsat/rules/rules.types.d.ts +90 -17
  174. package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
  175. package/dist/cpsat/rules/scope.types.d.ts +24 -11
  176. package/dist/cpsat/rules/scope.types.d.ts.map +1 -1
  177. package/dist/cpsat/rules/scope.types.js +14 -19
  178. package/dist/cpsat/rules/scope.types.js.map +1 -1
  179. package/dist/cpsat/rules/target-days-week.d.ts +31 -0
  180. package/dist/cpsat/rules/target-days-week.d.ts.map +1 -0
  181. package/dist/cpsat/rules/target-days-week.js +80 -0
  182. package/dist/cpsat/rules/target-days-week.js.map +1 -0
  183. package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts +33 -0
  184. package/dist/cpsat/rules/target-peak-concurrent-assignments.d.ts.map +1 -0
  185. package/dist/cpsat/rules/target-peak-concurrent-assignments.js +146 -0
  186. package/dist/cpsat/rules/target-peak-concurrent-assignments.js.map +1 -0
  187. package/dist/cpsat/rules/time-cost-surcharge.d.ts +15 -14
  188. package/dist/cpsat/rules/time-cost-surcharge.d.ts.map +1 -1
  189. package/dist/cpsat/rules/time-cost-surcharge.js +82 -96
  190. package/dist/cpsat/rules/time-cost-surcharge.js.map +1 -1
  191. package/dist/cpsat/rules/time-off.d.ts +21 -57
  192. package/dist/cpsat/rules/time-off.d.ts.map +1 -1
  193. package/dist/cpsat/rules/time-off.js +114 -127
  194. package/dist/cpsat/rules/time-off.js.map +1 -1
  195. package/dist/cpsat/rules/variables.d.ts +4 -0
  196. package/dist/cpsat/rules/variables.d.ts.map +1 -0
  197. package/dist/cpsat/rules/variables.js +10 -0
  198. package/dist/cpsat/rules/variables.js.map +1 -0
  199. package/dist/cpsat/rules.d.ts +20 -3
  200. package/dist/cpsat/rules.d.ts.map +1 -1
  201. package/dist/cpsat/rules.js +19 -2
  202. package/dist/cpsat/rules.js.map +1 -1
  203. package/dist/cpsat/semantic-time.d.ts +7 -6
  204. package/dist/cpsat/semantic-time.d.ts.map +1 -1
  205. package/dist/cpsat/semantic-time.js +15 -28
  206. package/dist/cpsat/semantic-time.js.map +1 -1
  207. package/dist/cpsat/types.d.ts +47 -4
  208. package/dist/cpsat/types.d.ts.map +1 -1
  209. package/dist/cpsat/types.js +24 -1
  210. package/dist/cpsat/types.js.map +1 -1
  211. package/dist/cpsat/utils.d.ts +23 -6
  212. package/dist/cpsat/utils.d.ts.map +1 -1
  213. package/dist/cpsat/utils.js +76 -15
  214. package/dist/cpsat/utils.js.map +1 -1
  215. package/dist/cpsat/validation-reporter.d.ts +6 -2
  216. package/dist/cpsat/validation-reporter.d.ts.map +1 -1
  217. package/dist/cpsat/validation-reporter.js +122 -10
  218. package/dist/cpsat/validation-reporter.js.map +1 -1
  219. package/dist/cpsat/validation.types.d.ts +21 -10
  220. package/dist/cpsat/validation.types.d.ts.map +1 -1
  221. package/dist/datetime.utils.d.ts +7 -13
  222. package/dist/datetime.utils.d.ts.map +1 -1
  223. package/dist/datetime.utils.js +29 -36
  224. package/dist/datetime.utils.js.map +1 -1
  225. package/dist/index.d.ts +12 -8
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +8 -3
  228. package/dist/index.js.map +1 -1
  229. package/dist/schedule/cost.d.ts +48 -60
  230. package/dist/schedule/cost.d.ts.map +1 -1
  231. package/dist/schedule/cost.js +15 -51
  232. package/dist/schedule/cost.js.map +1 -1
  233. package/dist/schedule/coverage.d.ts +57 -28
  234. package/dist/schedule/coverage.d.ts.map +1 -1
  235. package/dist/schedule/coverage.js +24 -1
  236. package/dist/schedule/coverage.js.map +1 -1
  237. package/dist/schedule/definition.d.ts +138 -19
  238. package/dist/schedule/definition.d.ts.map +1 -1
  239. package/dist/schedule/definition.js +99 -90
  240. package/dist/schedule/definition.js.map +1 -1
  241. package/dist/schedule/index.d.ts +4 -4
  242. package/dist/schedule/index.d.ts.map +1 -1
  243. package/dist/schedule/index.js +2 -2
  244. package/dist/schedule/index.js.map +1 -1
  245. package/dist/schedule/rules.d.ts +438 -113
  246. package/dist/schedule/rules.d.ts.map +1 -1
  247. package/dist/schedule/rules.js +310 -108
  248. package/dist/schedule/rules.js.map +1 -1
  249. package/dist/schedule/shift-patterns.d.ts +0 -17
  250. package/dist/schedule/shift-patterns.d.ts.map +1 -1
  251. package/dist/schedule/shift-patterns.js +0 -17
  252. package/dist/schedule/shift-patterns.js.map +1 -1
  253. package/dist/schedule/time-periods.d.ts +37 -27
  254. package/dist/schedule/time-periods.d.ts.map +1 -1
  255. package/dist/schedule/time-periods.js +37 -27
  256. package/dist/schedule/time-periods.js.map +1 -1
  257. package/dist/types.d.ts +67 -3
  258. package/dist/types.d.ts.map +1 -1
  259. package/dist/types.js +64 -0
  260. package/dist/types.js.map +1 -1
  261. package/package.json +8 -9
  262. package/solver/src/solver/models.py +41 -4
  263. package/solver/src/solver/solver.py +546 -157
  264. package/solver/tests/test_solver.py +363 -7
  265. package/src/client.schemas.ts +127 -10
  266. package/src/client.ts +3 -0
  267. package/src/client.types.ts +73 -7
  268. package/src/cpsat/cost.ts +17 -7
  269. package/src/cpsat/model-builder.ts +614 -129
  270. package/src/cpsat/response.ts +57 -13
  271. package/src/cpsat/rule-descriptor.ts +311 -0
  272. package/src/cpsat/rules/artifacts.ts +56 -0
  273. package/src/cpsat/rules/assign-together.ts +104 -69
  274. package/src/cpsat/rules/assigned-day.ts +70 -0
  275. package/src/cpsat/rules/assignment-priority.ts +54 -58
  276. package/src/cpsat/rules/assignment-terms.ts +43 -0
  277. package/src/cpsat/rules/concurrent-intervals.ts +93 -0
  278. package/src/cpsat/rules/cost-utils.ts +1 -9
  279. package/src/cpsat/rules/day-cost-multiplier.ts +101 -81
  280. package/src/cpsat/rules/day-cost-surcharge.ts +91 -70
  281. package/src/cpsat/rules/index.ts +30 -24
  282. package/src/cpsat/rules/location-preference.ts +47 -52
  283. package/src/cpsat/rules/max-concurrent-assignments.ts +170 -0
  284. package/src/cpsat/rules/max-consecutive-days.ts +74 -91
  285. package/src/cpsat/rules/max-days-of-week-per-period.ts +127 -0
  286. package/src/cpsat/rules/max-days-week.ts +89 -115
  287. package/src/cpsat/rules/max-hours-day.ts +64 -84
  288. package/src/cpsat/rules/max-hours-week.ts +74 -90
  289. package/src/cpsat/rules/max-shifts-day.ts +65 -85
  290. package/src/cpsat/rules/min-consecutive-days.ts +161 -127
  291. package/src/cpsat/rules/min-days-of-week-per-period.ts +144 -0
  292. package/src/cpsat/rules/min-days-week.ts +93 -91
  293. package/src/cpsat/rules/min-hours-day.ts +79 -58
  294. package/src/cpsat/rules/min-hours-week.ts +87 -62
  295. package/src/cpsat/rules/min-rest-between-shifts.ts +191 -178
  296. package/src/cpsat/rules/minimize-cost.ts +175 -188
  297. package/src/cpsat/rules/must-assign.ts +44 -81
  298. package/src/cpsat/rules/overtime-daily-multiplier.ts +158 -140
  299. package/src/cpsat/rules/overtime-daily-surcharge.ts +141 -126
  300. package/src/cpsat/rules/overtime-tiered-multiplier.ts +234 -253
  301. package/src/cpsat/rules/overtime-weekly-multiplier.ts +183 -175
  302. package/src/cpsat/rules/overtime-weekly-surcharge.ts +161 -150
  303. package/src/cpsat/rules/pattern-eligibility.ts +26 -0
  304. package/src/cpsat/rules/pattern-time.ts +30 -0
  305. package/src/cpsat/rules/registry.ts +140 -62
  306. package/src/cpsat/rules/resolver.ts +58 -30
  307. package/src/cpsat/rules/role-preference.ts +92 -0
  308. package/src/cpsat/rules/rules.types.ts +89 -17
  309. package/src/cpsat/rules/scope.types.ts +31 -26
  310. package/src/cpsat/rules/target-days-week.ts +105 -0
  311. package/src/cpsat/rules/target-peak-concurrent-assignments.ts +212 -0
  312. package/src/cpsat/rules/time-cost-surcharge.ts +116 -122
  313. package/src/cpsat/rules/time-off.ts +154 -163
  314. package/src/cpsat/rules/variables.ts +11 -0
  315. package/src/cpsat/rules.ts +43 -17
  316. package/src/cpsat/semantic-time.ts +29 -43
  317. package/src/cpsat/types.ts +53 -4
  318. package/src/cpsat/utils.ts +92 -20
  319. package/src/cpsat/validation-reporter.ts +170 -19
  320. package/src/cpsat/validation.types.ts +22 -11
  321. package/src/datetime.utils.ts +45 -44
  322. package/src/index.ts +64 -12
  323. package/src/schedule/cost.ts +40 -61
  324. package/src/schedule/coverage.ts +57 -28
  325. package/src/schedule/definition.ts +565 -76
  326. package/src/schedule/index.ts +23 -3
  327. package/src/schedule/rules.ts +631 -132
  328. package/src/schedule/shift-patterns.ts +0 -17
  329. package/src/schedule/time-periods.ts +37 -27
  330. package/src/types.ts +114 -2
@@ -1,5 +1,5 @@
1
1
  import * as z from "zod";
2
- import type { CompilationRule } from "../model-builder.js";
2
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
3
3
  import { priorityToPenalty } from "../utils.js";
4
4
  import {
5
5
  PrioritySchema,
@@ -8,8 +8,11 @@ import {
8
8
  resolveMembersFromScope,
9
9
  ruleGroup,
10
10
  } from "./scope.types.js";
11
+ import { assignmentVarsForDay } from "./assignment-terms.js";
12
+ import { hardConstraint, reportValidation, softConstraint } from "./artifacts.js";
13
+ import { buildAssignedDayIndicator } from "./assigned-day.js";
11
14
 
12
- const MaxConsecutiveDaysSchema = z
15
+ export const MaxConsecutiveDaysSchema = z
13
16
  .object({
14
17
  days: z.number().min(0),
15
18
  priority: PrioritySchema,
@@ -17,103 +20,83 @@ const MaxConsecutiveDaysSchema = z
17
20
  .and(entityScope(["members", "roles", "skills"]));
18
21
 
19
22
  /**
20
- * Configuration for {@link createMaxConsecutiveDaysRule}.
21
- *
22
- * - `days` (required): maximum consecutive days allowed
23
- * - `priority` (required): how strictly the solver enforces this rule
24
- *
25
- * Entity scoping (at most one): `memberIds`, `roleIds`, `skillIds`
23
+ * Configuration for {@link maxConsecutiveDaysRuleDescriptor}.
26
24
  */
27
25
  export type MaxConsecutiveDaysConfig = z.infer<typeof MaxConsecutiveDaysSchema>;
28
26
 
29
27
  /**
30
- * Limits how many consecutive days a person can be assigned.
28
+ * Low-level descriptor for the `max-consecutive-days` rule.
31
29
  *
32
- * @param config - See {@link MaxConsecutiveDaysConfig}
33
- * @example
34
- * ```ts
35
- * createMaxConsecutiveDaysRule({ days: 5, priority: "MANDATORY" });
36
- * ```
30
+ * @remarks
31
+ * This rule limits the length of each assignment streak by counting assigned
32
+ * days in every sliding window of `days + 1` days.
33
+ *
34
+ * @category Rules
37
35
  */
38
- export function createMaxConsecutiveDaysRule(config: MaxConsecutiveDaysConfig): CompilationRule {
39
- const parsed = MaxConsecutiveDaysSchema.parse(config);
40
- const scope = parseEntityScope(parsed);
41
- const { days, priority } = parsed;
42
- const windowSize = days + 1;
43
- const group = ruleGroup(`max-consecutive-days:${days}`, `Max ${days} consecutive days`, scope);
44
-
45
- return {
46
- compile(b) {
47
- const members = resolveMembersFromScope(scope, b.members);
36
+ export const maxConsecutiveDaysRuleDescriptor = defineRuleDescriptor({
37
+ name: "max-consecutive-days",
38
+ schema: MaxConsecutiveDaysSchema,
39
+ compile(config, ctx) {
40
+ const scope = parseEntityScope(config);
41
+ const { days, priority } = config;
42
+ const windowSize = days + 1;
43
+ const group = ruleGroup(`max-consecutive-days:${days}`, `Max ${days} consecutive days`, scope);
44
+ const members = resolveMembersFromScope(scope, [...ctx.members]);
48
45
 
49
- for (const emp of members) {
50
- for (let i = 0; i <= b.days.length - windowSize; i++) {
51
- const windowDays = b.days.slice(i, i + windowSize);
46
+ const artifacts = members.flatMap((member) => {
47
+ const supportArtifacts = ctx.days.flatMap(
48
+ (day) =>
49
+ buildAssignedDayIndicator({
50
+ memberId: member.id,
51
+ day,
52
+ assignmentVars: assignmentVarsForDay(member, day, ctx.shiftPatterns),
53
+ variableName: `assigned_${member.id}_${day.iso}`,
54
+ }).artifacts,
55
+ );
52
56
 
53
- const worksDayVars: string[] = [];
54
- for (const day of windowDays) {
55
- const worksVar = b.boolVar(`works_${emp.id}_${day}`);
56
- worksDayVars.push(worksVar);
57
-
58
- const dayAssignments = b.shiftPatterns
59
- .filter((p) => b.canAssign(emp, p) && b.patternAvailableOnDay(p, day))
60
- .map((p) => b.assignment(emp.id, p.id, day));
57
+ const windowArtifacts = Array.from(
58
+ { length: Math.max(0, ctx.days.length - windowSize + 1) },
59
+ (_, index) => {
60
+ const windowDays = ctx.days.slice(index, index + windowSize);
61
+ const windowStart = windowDays[0]!;
62
+ const terms = windowDays.map((day) => ({
63
+ var: `assigned_${member.id}_${day.iso}`,
64
+ coeff: 1,
65
+ }));
66
+ const description = `${member.id} max ${days} consecutive days from ${windowStart.iso}`;
67
+ const context = { memberIds: [member.id], days: windowDays.map((day) => day.iso) };
68
+ const constraintId = `max-consecutive-days:${member.id}:${windowStart.iso}`;
61
69
 
62
- if (dayAssignments.length === 0) {
63
- b.addLinear([{ var: worksVar, coeff: 1 }], "==", 0);
64
- } else {
65
- for (const assignVar of dayAssignments) {
66
- b.addLinear(
67
- [
68
- { var: worksVar, coeff: 1 },
69
- { var: assignVar, coeff: -1 },
70
- ],
71
- ">=",
72
- 0,
73
- );
74
- }
75
- b.addLinear(
76
- [
77
- { var: worksVar, coeff: 1 },
78
- ...dayAssignments.map((v) => ({ var: v, coeff: -1 })),
79
- ],
80
- "<=",
81
- 0,
82
- );
83
- }
84
- }
70
+ return priority === "MANDATORY"
71
+ ? hardConstraint({
72
+ group,
73
+ description,
74
+ context,
75
+ validation: reportValidation(constraintId),
76
+ terms,
77
+ comparator: "<=",
78
+ targetValue: days,
79
+ })
80
+ : softConstraint({
81
+ group,
82
+ description,
83
+ context,
84
+ validation: reportValidation(),
85
+ terms,
86
+ comparator: "<=",
87
+ targetValue: days,
88
+ penalty: priorityToPenalty(priority),
89
+ constraintId,
90
+ });
91
+ },
92
+ );
85
93
 
86
- const windowStart = windowDays[0]!;
87
- const constraintId = `max-consecutive-days:${emp.id}:${windowStart}`;
94
+ return supportArtifacts.concat(windowArtifacts);
95
+ });
88
96
 
89
- if (priority === "MANDATORY") {
90
- b.addLinear(
91
- worksDayVars.map((v) => ({ var: v, coeff: 1 })),
92
- "<=",
93
- days,
94
- );
95
- } else {
96
- b.addSoftLinear(
97
- worksDayVars.map((v) => ({ var: v, coeff: 1 })),
98
- "<=",
99
- days,
100
- priorityToPenalty(priority),
101
- constraintId,
102
- );
103
- b.reporter.trackConstraint({
104
- id: constraintId,
105
- type: "rule",
106
- rule: "max-consecutive-days",
107
- description: `${emp.id} max ${days} consecutive days from ${windowStart}`,
108
- targetValue: days,
109
- comparator: "<=",
110
- day: windowStart,
111
- context: { memberIds: [emp.id], days: windowDays },
112
- group,
113
- });
114
- }
115
- }
116
- }
117
- },
118
- };
119
- }
97
+ return {
98
+ rule: "max-consecutive-days",
99
+ artifacts,
100
+ };
101
+ },
102
+ });
@@ -0,0 +1,127 @@
1
+ import * as z from "zod";
2
+ import { DayOfWeekSchema } from "../../types.js";
3
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
4
+ import { groupWeekChunks, priorityToPenalty, splitIntoMonths } from "../utils.js";
5
+ import {
6
+ PrioritySchema,
7
+ entityScope,
8
+ parseEntityScope,
9
+ resolveMembersFromScope,
10
+ ruleGroup,
11
+ } from "./scope.types.js";
12
+ import { assignmentVarsForDay } from "./assignment-terms.js";
13
+ import { assignedDayVariableName } from "./variables.js";
14
+ import { hardConstraint, reportValidation, softConstraint } from "./artifacts.js";
15
+ import { buildAssignedDayIndicator } from "./assigned-day.js";
16
+
17
+ const PeriodSchema = z.discriminatedUnion("type", [
18
+ z.object({ type: z.literal("weeks"), value: z.number().int().min(1) }),
19
+ z.object({ type: z.literal("months"), value: z.number().int().min(1) }),
20
+ ]);
21
+
22
+ const MaxDaysOfWeekPerPeriodBase = z.object({
23
+ days: z.number().int().min(0),
24
+ dayOfWeek: z.array(DayOfWeekSchema).nonempty(),
25
+ period: PeriodSchema,
26
+ priority: PrioritySchema,
27
+ });
28
+
29
+ export const MaxDaysOfWeekPerPeriodSchema = MaxDaysOfWeekPerPeriodBase.and(
30
+ entityScope(["members", "roles", "skills"]),
31
+ );
32
+
33
+ /**
34
+ * Configuration for {@link maxDaysOfWeekPerPeriodRuleDescriptor}.
35
+ */
36
+ export type MaxDaysOfWeekPerPeriodConfig = z.infer<typeof MaxDaysOfWeekPerPeriodSchema>;
37
+
38
+ /**
39
+ * Low-level descriptor for the `max-days-of-week-per-period` rule.
40
+ *
41
+ * @remarks
42
+ * This rule counts assigned days matching the configured days of the week.
43
+ * Week-based periods align to `weekStartsOn`; month-based periods align to
44
+ * calendar month boundaries.
45
+ *
46
+ * @category Rules
47
+ */
48
+ export const maxDaysOfWeekPerPeriodRuleDescriptor = defineRuleDescriptor({
49
+ name: "max-days-of-week-per-period",
50
+ schema: MaxDaysOfWeekPerPeriodSchema,
51
+ compile(config, ctx) {
52
+ const entityScopeValue = parseEntityScope(config);
53
+ const { days, dayOfWeek, period, priority } = config;
54
+ const dowLabel = dayOfWeek.toSorted().join(",");
55
+ const periodLabel = `${period.value}${period.type === "weeks" ? "w" : "m"}`;
56
+ const group = ruleGroup(
57
+ `max-days-of-week-per-period:${days}:${dowLabel}:${periodLabel}`,
58
+ `Max ${days} ${dowLabel} per ${period.value} ${period.type}`,
59
+ entityScopeValue,
60
+ );
61
+ const targetMembers = resolveMembersFromScope(entityScopeValue, [...ctx.members]);
62
+ const targetDows = new Set(dayOfWeek);
63
+ const chunks =
64
+ period.type === "weeks"
65
+ ? groupWeekChunks([...ctx.days], period.value, ctx.weekStartsOn)
66
+ : splitIntoMonths([...ctx.days], period.value);
67
+
68
+ const artifacts = targetMembers.flatMap((member) =>
69
+ chunks.flatMap((chunkDays) => {
70
+ const chunkStart = chunkDays[0];
71
+ if (!chunkStart) return [];
72
+
73
+ const matchingDays = chunkDays.filter((day) => targetDows.has(day.dayOfWeek));
74
+ if (matchingDays.length === 0) return [];
75
+
76
+ const supportArtifacts = matchingDays.flatMap(
77
+ (day) =>
78
+ buildAssignedDayIndicator({
79
+ memberId: member.id,
80
+ day,
81
+ assignmentVars: assignmentVarsForDay(member, day, ctx.shiftPatterns),
82
+ }).artifacts,
83
+ );
84
+
85
+ const assignedDayVars = matchingDays
86
+ .filter((day) => assignmentVarsForDay(member, day, ctx.shiftPatterns).length > 0)
87
+ .map((day) => assignedDayVariableName(member.id, day.iso));
88
+ if (assignedDayVars.length === 0) return supportArtifacts;
89
+
90
+ const description = `${member.id} max ${days} ${dowLabel} in period starting ${chunkStart.iso}`;
91
+ const context = { memberIds: [member.id], days: matchingDays.map((day) => day.iso) };
92
+ const constraintId = `max-days-of-week-per-period:${member.id}:${chunkStart.iso}`;
93
+ const terms = assignedDayVars.map((varName) => ({ var: varName, coeff: 1 }));
94
+
95
+ const finalArtifact =
96
+ priority === "MANDATORY"
97
+ ? hardConstraint({
98
+ group,
99
+ description,
100
+ context,
101
+ validation: reportValidation(constraintId),
102
+ terms,
103
+ comparator: "<=",
104
+ targetValue: days,
105
+ })
106
+ : softConstraint({
107
+ group,
108
+ description,
109
+ context,
110
+ validation: reportValidation(),
111
+ terms,
112
+ comparator: "<=",
113
+ targetValue: days,
114
+ penalty: priorityToPenalty(priority),
115
+ constraintId,
116
+ });
117
+
118
+ return supportArtifacts.concat(finalArtifact);
119
+ }),
120
+ );
121
+
122
+ return {
123
+ rule: "max-days-of-week-per-period",
124
+ artifacts,
125
+ };
126
+ },
127
+ });
@@ -1,18 +1,21 @@
1
1
  import * as z from "zod";
2
2
  import { DayOfWeekSchema } from "../../types.js";
3
- import type { CompilationRule } from "../model-builder.js";
4
- import type { Term } from "../types.js";
3
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
5
4
  import { priorityToPenalty, splitIntoWeeks } from "../utils.js";
6
5
  import {
7
6
  PrioritySchema,
8
7
  entityScope,
9
- timeScope,
10
8
  parseEntityScope,
11
9
  parseTimeScope,
12
- resolveMembersFromScope,
13
10
  resolveActiveDaysFromScope,
11
+ resolveMembersFromScope,
14
12
  ruleGroup,
13
+ timeScope,
15
14
  } from "./scope.types.js";
15
+ import { assignmentVarsForDay } from "./assignment-terms.js";
16
+ import { assignedDayVariableName } from "./variables.js";
17
+ import { hardConstraint, reportValidation, softConstraint } from "./artifacts.js";
18
+ import { buildAssignedDayIndicator } from "./assigned-day.js";
16
19
 
17
20
  const MaxDaysWeekBase = z.object({
18
21
  days: z.number().int().min(0),
@@ -20,124 +23,95 @@ const MaxDaysWeekBase = z.object({
20
23
  weekStartsOn: DayOfWeekSchema.optional(),
21
24
  });
22
25
 
23
- const MaxDaysWeekSchema = MaxDaysWeekBase.and(entityScope(["members", "roles", "skills"])).and(
24
- timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]),
25
- );
26
+ export const MaxDaysWeekSchema = MaxDaysWeekBase.and(
27
+ entityScope(["members", "roles", "skills"]),
28
+ ).and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
26
29
 
27
30
  /**
28
- * Configuration for {@link createMaxDaysWeekRule}.
29
- *
30
- * - `days` (required): maximum number of days allowed per scheduling week
31
- * - `priority` (required): how strictly the solver enforces this rule
32
- * - `weekStartsOn` (optional): which day starts the week; defaults to {@link ModelBuilder.weekStartsOn}
33
- *
34
- * Entity scoping (at most one): `memberIds`, `roleIds`, `skillIds`
35
- * Time scoping (at most one, optional): `dateRange`, `specificDates`, `dayOfWeek`, `recurringPeriods`
31
+ * Configuration for {@link maxDaysWeekRuleDescriptor}.
36
32
  */
37
33
  export type MaxDaysWeekConfig = z.infer<typeof MaxDaysWeekSchema>;
38
34
 
39
35
  /**
40
- * Caps total number of days a person can work within each scheduling week.
36
+ * Low-level descriptor for the `max-days-week` rule.
41
37
  *
42
38
  * @remarks
43
- * Creates a binary "works on day" variable for each member and day, then
44
- * constrains the weekly sum. This counts distinct days, regardless of how
45
- * many shifts are assigned on a single day.
46
- *
47
- * @param config - See {@link MaxDaysWeekConfig}
48
- * @example Limit everyone to 5 days per week
49
- * ```ts
50
- * createMaxDaysWeekRule({ days: 5, priority: "MANDATORY" });
51
- * ```
39
+ * This rule counts assigned days, not assigned shifts. Multiple assignments on
40
+ * the same day still count as one assigned day.
52
41
  *
53
- * @example Part-time staff limited to 3 days
54
- * ```ts
55
- * createMaxDaysWeekRule({
56
- * roleIds: ["part-time"],
57
- * days: 3,
58
- * priority: "HIGH",
59
- * });
60
- * ```
42
+ * @category Rules
61
43
  */
62
- export function createMaxDaysWeekRule(config: MaxDaysWeekConfig): CompilationRule {
63
- const parsed = MaxDaysWeekSchema.parse(config);
64
- const entityScopeValue = parseEntityScope(parsed);
65
- const timeScopeValue = parseTimeScope(parsed);
66
- const { days, priority, weekStartsOn } = parsed;
67
- const group = ruleGroup(
68
- `max-days-week:${days}`,
69
- `Max ${days}d per week`,
70
- entityScopeValue,
71
- timeScopeValue,
72
- );
73
-
74
- return {
75
- compile(b) {
76
- const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
77
- const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
78
-
79
- if (targetMembers.length === 0 || activeDays.length === 0) return;
80
-
81
- const weeks = splitIntoWeeks(activeDays, weekStartsOn ?? b.weekStartsOn);
82
-
83
- for (const emp of targetMembers) {
84
- for (const weekDays of weeks) {
85
- const weekWorkVars: string[] = [];
86
-
87
- for (const day of weekDays) {
88
- const dayAssignments = b.shiftPatterns
89
- .filter((p) => b.canAssign(emp, p) && b.patternAvailableOnDay(p, day))
90
- .map((p) => b.assignment(emp.id, p.id, day));
91
-
92
- if (dayAssignments.length === 0) continue;
93
-
94
- const worksVar = b.boolVar(`works_day_${emp.id}_${day}`);
95
- weekWorkVars.push(worksVar);
96
-
97
- // worksVar >= each assignment (if any assignment is 1, worksVar must be 1)
98
- for (const assignVar of dayAssignments) {
99
- b.addLinear(
100
- [
101
- { var: worksVar, coeff: 1 },
102
- { var: assignVar, coeff: -1 },
103
- ],
104
- ">=",
105
- 0,
106
- );
107
- }
108
-
109
- // worksVar <= sum(assignments) (if no assignment is 1, worksVar must be 0)
110
- b.addLinear(
111
- [{ var: worksVar, coeff: 1 }, ...dayAssignments.map((v) => ({ var: v, coeff: -1 }))],
112
- "<=",
113
- 0,
114
- );
115
- }
116
-
117
- if (weekWorkVars.length === 0) continue;
118
-
119
- const weekLabel = weekDays[0]!;
120
- const constraintId = `max-days-week:${emp.id}:${weekLabel}`;
121
- const terms: Term[] = weekWorkVars.map((v) => ({ var: v, coeff: 1 }));
122
-
123
- if (priority === "MANDATORY") {
124
- b.addLinear(terms, "<=", days);
125
- } else {
126
- b.addSoftLinear(terms, "<=", days, priorityToPenalty(priority), constraintId);
127
- b.reporter.trackConstraint({
128
- id: constraintId,
129
- type: "rule",
130
- rule: "max-days-week",
131
- description: `${emp.id} max ${days}d in week starting ${weekLabel}`,
132
- targetValue: days,
133
- comparator: "<=",
134
- day: weekLabel,
135
- context: { memberIds: [emp.id], days: weekDays },
136
- group,
137
- });
138
- }
139
- }
140
- }
141
- },
142
- };
143
- }
44
+ export const maxDaysWeekRuleDescriptor = defineRuleDescriptor({
45
+ name: "max-days-week",
46
+ schema: MaxDaysWeekSchema,
47
+ compile(config, ctx) {
48
+ const entityScopeValue = parseEntityScope(config);
49
+ const timeScopeValue = parseTimeScope(config);
50
+ const { days, priority, weekStartsOn } = config;
51
+ const group = ruleGroup(
52
+ `max-days-week:${days}`,
53
+ `Max ${days}d per week`,
54
+ entityScopeValue,
55
+ timeScopeValue,
56
+ );
57
+ const targetMembers = resolveMembersFromScope(entityScopeValue, [...ctx.members]);
58
+ const activeDays = resolveActiveDaysFromScope(timeScopeValue, [...ctx.days]);
59
+ const weeks = splitIntoWeeks(activeDays, weekStartsOn ?? ctx.weekStartsOn);
60
+
61
+ const artifacts = targetMembers.flatMap((member) =>
62
+ weeks.flatMap((weekDays) => {
63
+ const weekStart = weekDays[0];
64
+ if (!weekStart) return [];
65
+
66
+ const supportArtifacts = weekDays.flatMap(
67
+ (day) =>
68
+ buildAssignedDayIndicator({
69
+ memberId: member.id,
70
+ day,
71
+ assignmentVars: assignmentVarsForDay(member, day, ctx.shiftPatterns),
72
+ }).artifacts,
73
+ );
74
+
75
+ const assignedDayVars = weekDays
76
+ .filter((day) => assignmentVarsForDay(member, day, ctx.shiftPatterns).length > 0)
77
+ .map((day) => assignedDayVariableName(member.id, day.iso));
78
+ if (assignedDayVars.length === 0) return supportArtifacts;
79
+
80
+ const description = `${member.id} max ${days}d in week starting ${weekStart.iso}`;
81
+ const context = { memberIds: [member.id], days: weekDays.map((day) => day.iso) };
82
+ const constraintId = `max-days-week:${member.id}:${weekStart.iso}`;
83
+ const terms = assignedDayVars.map((varName) => ({ var: varName, coeff: 1 }));
84
+
85
+ const finalArtifact =
86
+ priority === "MANDATORY"
87
+ ? hardConstraint({
88
+ group,
89
+ description,
90
+ context,
91
+ validation: reportValidation(constraintId),
92
+ terms,
93
+ comparator: "<=",
94
+ targetValue: days,
95
+ })
96
+ : softConstraint({
97
+ group,
98
+ description,
99
+ context,
100
+ validation: reportValidation(),
101
+ terms,
102
+ comparator: "<=",
103
+ targetValue: days,
104
+ penalty: priorityToPenalty(priority),
105
+ constraintId,
106
+ });
107
+
108
+ return supportArtifacts.concat(finalArtifact);
109
+ }),
110
+ );
111
+
112
+ return {
113
+ rule: "max-days-week",
114
+ artifacts,
115
+ };
116
+ },
117
+ });