dabke 0.82.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 (341) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +22 -24
  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 -21
  66. package/dist/cpsat/rules/index.d.ts.map +1 -1
  67. package/dist/cpsat/rules/index.js +30 -21
  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 +30 -0
  86. package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
  87. package/dist/cpsat/rules/max-days-week.js +83 -0
  88. package/dist/cpsat/rules/max-days-week.js.map +1 -0
  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 +21 -0
  110. package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
  111. package/dist/cpsat/rules/min-days-week.js +95 -0
  112. package/dist/cpsat/rules/min-days-week.js.map +1 -0
  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 +12 -0
  130. package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
  131. package/dist/cpsat/rules/must-assign.js +54 -0
  132. package/dist/cpsat/rules/must-assign.js.map +1 -0
  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 -27
  162. package/dist/cpsat/rules/registry.d.ts.map +1 -1
  163. package/dist/cpsat/rules/registry.js +98 -31
  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 +93 -17
  174. package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
  175. package/dist/cpsat/rules/scope.types.d.ts +25 -12
  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 +15 -9
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +11 -4
  228. package/dist/index.js.map +1 -1
  229. package/dist/schedule/cost.d.ts +192 -0
  230. package/dist/schedule/cost.d.ts.map +1 -0
  231. package/dist/schedule/cost.js +151 -0
  232. package/dist/schedule/cost.js.map +1 -0
  233. package/dist/schedule/coverage.d.ts +114 -0
  234. package/dist/schedule/coverage.d.ts.map +1 -0
  235. package/dist/schedule/coverage.js +56 -0
  236. package/dist/schedule/coverage.js.map +1 -0
  237. package/dist/schedule/definition.d.ts +346 -0
  238. package/dist/schedule/definition.d.ts.map +1 -0
  239. package/dist/schedule/definition.js +668 -0
  240. package/dist/schedule/definition.js.map +1 -0
  241. package/dist/schedule/index.d.ts +67 -0
  242. package/dist/schedule/index.d.ts.map +1 -0
  243. package/dist/schedule/index.js +69 -0
  244. package/dist/schedule/index.js.map +1 -0
  245. package/dist/schedule/rules.d.ts +678 -0
  246. package/dist/schedule/rules.d.ts.map +1 -0
  247. package/dist/schedule/rules.js +554 -0
  248. package/dist/schedule/rules.js.map +1 -0
  249. package/dist/schedule/shift-patterns.d.ts +17 -0
  250. package/dist/schedule/shift-patterns.d.ts.map +1 -0
  251. package/dist/schedule/shift-patterns.js +24 -0
  252. package/dist/schedule/shift-patterns.js.map +1 -0
  253. package/dist/schedule/time-periods.d.ts +79 -0
  254. package/dist/schedule/time-periods.d.ts.map +1 -0
  255. package/dist/schedule/time-periods.js +101 -0
  256. package/dist/schedule/time-periods.js.map +1 -0
  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 -14
  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 -21
  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 +117 -0
  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 +122 -0
  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 +71 -0
  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 -56
  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 +92 -17
  309. package/src/cpsat/rules/scope.types.ts +32 -27
  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 +72 -15
  323. package/src/schedule/cost.ts +221 -0
  324. package/src/schedule/coverage.ts +164 -0
  325. package/src/schedule/definition.ts +1447 -0
  326. package/src/schedule/index.ts +132 -0
  327. package/src/schedule/rules.ts +1028 -0
  328. package/src/schedule/shift-patterns.ts +29 -0
  329. package/src/schedule/time-periods.ts +120 -0
  330. package/src/types.ts +114 -2
  331. package/dist/llms.d.ts +0 -2
  332. package/dist/llms.d.ts.map +0 -1
  333. package/dist/llms.js +0 -3
  334. package/dist/llms.js.map +0 -1
  335. package/dist/schedule.d.ts +0 -917
  336. package/dist/schedule.d.ts.map +0 -1
  337. package/dist/schedule.js +0 -1323
  338. package/dist/schedule.js.map +0 -1
  339. package/llms.txt +0 -758
  340. package/src/llms.ts +0 -3
  341. package/src/schedule.ts +0 -1960
@@ -1,27 +1,27 @@
1
1
  import * as z from "zod";
2
2
  import { DayOfWeekSchema } from "../../types.js";
3
- import type { CompilationRule, CostContribution } from "../model-builder.js";
4
- import type { ShiftPattern, SchedulingMember, Term } from "../types.js";
5
- import type { ShiftAssignment } from "../response.js";
3
+ import { defineRuleDescriptor } from "../rule-descriptor.js";
4
+ import type { CostArtifact } from "../rule-descriptor.js";
6
5
  import { COST_CATEGORY } from "../cost.js";
7
6
  import {
8
- timeOfDayToMinutes,
7
+ OBJECTIVE_WEIGHTS,
9
8
  normalizeEndMinutes,
10
9
  splitIntoWeeks,
10
+ timeOfDayToMinutes,
11
11
  unionMinutes,
12
- OBJECTIVE_WEIGHTS,
13
12
  } from "../utils.js";
14
13
  import {
15
14
  entityScope,
16
- timeScope,
17
15
  parseEntityScope,
18
16
  parseTimeScope,
19
- resolveMembersFromScope,
20
17
  resolveActiveDaysFromScope,
18
+ resolveMembersFromScope,
19
+ timeScope,
21
20
  } from "./scope.types.js";
22
- import { getHourlyRate, patternDurationMinutes } from "./cost-utils.js";
21
+ import { getHourlyRate } from "./cost-utils.js";
22
+ import { patternDurationMinutes } from "./pattern-time.js";
23
23
 
24
- const OvertimeWeeklyMultiplierSchema = z
24
+ export const OvertimeWeeklyMultiplierSchema = z
25
25
  .object({
26
26
  after: z.number().min(0),
27
27
  factor: z.number().min(1),
@@ -30,186 +30,194 @@ const OvertimeWeeklyMultiplierSchema = z
30
30
  .and(entityScope(["members", "roles", "skills"]))
31
31
  .and(timeScope(["dateRange", "specificDates", "dayOfWeek", "recurring"]));
32
32
 
33
- /** Configuration for {@link createOvertimeWeeklyMultiplierRule}. */
33
+ /** Configuration for {@link overtimeWeeklyMultiplierRuleDescriptor}. */
34
34
  export type OvertimeWeeklyMultiplierConfig = z.infer<typeof OvertimeWeeklyMultiplierSchema>;
35
35
 
36
36
  /**
37
- * Creates a weekly overtime rate multiplier rule.
37
+ * Low-level descriptor for the `overtime-weekly-multiplier` rule.
38
38
  *
39
- * Hours beyond the threshold per week are paid at `factor` times the base rate.
40
- * The base cost for all hours (including overtime at 1x) is already counted by
41
- * `minimizeCost()`. This rule adds only the extra portion (`factor - 1`).
39
+ * @remarks
40
+ * Weekly overtime windows align to `weekStartsOn`. This artifact always
41
+ * contributes to post-solve cost calculation and only affects optimization when
42
+ * `minimize-cost` is active.
42
43
  *
43
- * Auxiliary variables and constraints are always emitted (they define the overtime
44
- * structure). The penalty terms both contribute to the objective and tighten the
45
- * overtime variables via minimization.
44
+ * @category Rules
46
45
  */
47
- export function createOvertimeWeeklyMultiplierRule(
48
- config: OvertimeWeeklyMultiplierConfig,
49
- ): CompilationRule {
50
- const parsed = OvertimeWeeklyMultiplierSchema.parse(config);
51
- const { after, factor, weekStartsOn } = parsed;
52
- const entityScopeValue = parseEntityScope(parsed);
53
- const timeScopeValue = parseTimeScope(parsed);
54
- const thresholdMinutes = after * 60;
55
- const extraFactor = factor - 1;
56
- let resolvedWeekStartsOn = weekStartsOn ?? "monday";
57
-
58
- return {
59
- compile(b) {
60
- resolvedWeekStartsOn = weekStartsOn ?? b.weekStartsOn;
61
- if (extraFactor <= 0) return;
62
-
63
- const targetMembers = resolveMembersFromScope(entityScopeValue, b.members);
64
- const activeDays = resolveActiveDaysFromScope(timeScopeValue, b.days);
65
-
66
- if (targetMembers.length === 0 || activeDays.length === 0) return;
67
-
68
- const weeks = splitIntoWeeks(activeDays, weekStartsOn ?? b.weekStartsOn);
69
-
70
- const hasCostContext = b.costContext?.active === true;
71
-
72
- for (const emp of targetMembers) {
73
- const rate = getHourlyRate(emp);
74
- if (rate === undefined) continue;
75
-
76
- for (const [weekIdx, weekDays] of weeks.entries()) {
77
- // Build total minutes expression and compute per-member week max
78
- // using time-range union per day (accounts for no-overlap constraints)
79
- let empWeekMaxMinutes = 0;
80
- const terms: Term[] = [];
81
- for (const day of weekDays) {
82
- const dayRanges: Array<{ start: number; end: number }> = [];
83
- for (const pattern of b.shiftPatterns) {
84
- if (!b.canAssign(emp, pattern)) continue;
85
- if (!b.patternAvailableOnDay(pattern, day)) continue;
86
- const start = timeOfDayToMinutes(pattern.startTime);
87
- dayRanges.push({
88
- start,
89
- end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
90
- });
91
- terms.push({
92
- var: b.assignment(emp.id, pattern.id, day),
93
- coeff: patternDurationMinutes(pattern),
94
- });
46
+ export const overtimeWeeklyMultiplierRuleDescriptor = defineRuleDescriptor({
47
+ name: "overtime-weekly-multiplier",
48
+ schema: OvertimeWeeklyMultiplierSchema,
49
+ compile(config) {
50
+ const { after, factor, weekStartsOn } = config;
51
+ const entityScopeValue = parseEntityScope(config);
52
+ const timeScopeValue = parseTimeScope(config);
53
+ const thresholdMinutes = after * 60;
54
+ const extraFactor = factor - 1;
55
+ let resolvedWeekStartsOn = weekStartsOn ?? "monday";
56
+
57
+ const costArtifact: CostArtifact = {
58
+ kind: "cost",
59
+ validation: {
60
+ strategy: "skip",
61
+ category: "no-meaningful-feedback",
62
+ rationale:
63
+ "Overtime cost artifacts influence optimization and post-solve accounting, not validation feedback.",
64
+ },
65
+ compileObjective(builder) {
66
+ resolvedWeekStartsOn = weekStartsOn ?? builder.weekStartsOn;
67
+ if (extraFactor <= 0) return;
68
+
69
+ const targetMembers = resolveMembersFromScope(entityScopeValue, builder.members);
70
+ const activeDays = resolveActiveDaysFromScope(timeScopeValue, builder.days);
71
+ if (targetMembers.length === 0 || activeDays.length === 0) return;
72
+
73
+ const weeks = splitIntoWeeks(activeDays, weekStartsOn ?? builder.weekStartsOn);
74
+ const hasCostContext = builder.costContext?.active === true;
75
+
76
+ for (const member of targetMembers) {
77
+ const rate = getHourlyRate(member);
78
+ if (rate === undefined) continue;
79
+
80
+ for (const [weekIndex, weekDays] of weeks.entries()) {
81
+ let memberWeekMaxMinutes = 0;
82
+ const terms = [] as Array<{ var: string; coeff: number }>;
83
+
84
+ for (const day of weekDays) {
85
+ const dayRanges: Array<{ start: number; end: number }> = [];
86
+ for (const pattern of builder.shiftPatterns) {
87
+ if (
88
+ !builder.canAssign(member, pattern) ||
89
+ !builder.patternAvailableOnDay(pattern, day)
90
+ ) {
91
+ continue;
92
+ }
93
+ const start = timeOfDayToMinutes(pattern.startTime);
94
+ dayRanges.push({
95
+ start,
96
+ end: normalizeEndMinutes(start, timeOfDayToMinutes(pattern.endTime)),
97
+ });
98
+ terms.push({
99
+ var: builder.assignment(member.id, pattern.id, day),
100
+ coeff: patternDurationMinutes(pattern),
101
+ });
102
+ }
103
+ memberWeekMaxMinutes += unionMinutes(dayRanges);
95
104
  }
96
- empWeekMaxMinutes += unionMinutes(dayRanges);
97
- }
98
105
 
99
- if (terms.length === 0) continue;
100
-
101
- const maxOvertime = Math.max(0, empWeekMaxMinutes - thresholdMinutes);
102
- if (maxOvertime === 0) continue;
103
-
104
- // Create overtime variable: max(0, totalMinutes - threshold)
105
- const overtimeVar = b.intVar(`overtime:mult:${emp.id}:w${weekIdx}`, 0, maxOvertime);
106
-
107
- // overtimeVar >= totalMinutes - threshold
108
- // => overtimeVar - totalMinutes >= -threshold
109
- b.addLinear(
110
- [
111
- { var: overtimeVar, coeff: 1 },
112
- ...terms.map((t) => ({ var: t.var, coeff: -t.coeff })),
113
- ],
114
- ">=",
115
- -thresholdMinutes,
116
- );
117
-
118
- // Penalty tightens the variable via minimization.
119
- // For int vars, coeff is per unit (minute). Scale so max overtime
120
- // contribution is proportional to OBJECTIVE_WEIGHTS.COST.
121
- if (rate > 0) {
122
- const totalOvertimeCost = (rate * extraFactor * maxOvertime) / 60;
123
- if (hasCostContext) {
124
- const normalizedMax = totalOvertimeCost / b.costContext!.normalizationFactor;
125
- b.addPenalty(overtimeVar, Math.max(1, Math.round(normalizedMax / maxOvertime)));
106
+ if (terms.length === 0) continue;
107
+ const maxOvertime = Math.max(0, memberWeekMaxMinutes - thresholdMinutes);
108
+ if (maxOvertime === 0) continue;
109
+
110
+ const overtimeVar = builder.intVar(
111
+ `overtime:mult:${member.id}:w${weekIndex}`,
112
+ 0,
113
+ maxOvertime,
114
+ );
115
+ builder.addLinear(
116
+ [
117
+ { var: overtimeVar, coeff: 1 },
118
+ ...terms.map((term) => ({ var: term.var, coeff: -term.coeff })),
119
+ ],
120
+ ">=",
121
+ -thresholdMinutes,
122
+ );
123
+
124
+ if (rate > 0) {
125
+ const totalOvertimeCost = (rate * extraFactor * maxOvertime) / 60;
126
+ if (hasCostContext) {
127
+ const normalizedMax = totalOvertimeCost / builder.costContext!.normalizationFactor;
128
+ builder.addPenalty(
129
+ overtimeVar,
130
+ Math.max(1, Math.round(normalizedMax / maxOvertime)),
131
+ );
132
+ } else {
133
+ builder.addPenalty(
134
+ overtimeVar,
135
+ Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
136
+ );
137
+ }
126
138
  } else {
127
- b.addPenalty(
128
- overtimeVar,
129
- Math.max(1, Math.round(OBJECTIVE_WEIGHTS.COST / maxOvertime)),
130
- );
139
+ builder.addPenalty(overtimeVar, 1);
131
140
  }
132
- } else {
133
- b.addPenalty(overtimeVar, 1);
134
141
  }
135
142
  }
136
- }
137
- },
138
-
139
- cost(
140
- assignments: ShiftAssignment[],
141
- members: ReadonlyArray<SchedulingMember>,
142
- shiftPatterns: ReadonlyArray<ShiftPattern>,
143
- ): CostContribution {
144
- if (extraFactor <= 0) return { entries: [] };
145
-
146
- const memberMap = new Map(members.map((e) => [e.id, e]));
147
- const patternMap = new Map(shiftPatterns.map((p) => [p.id, p]));
148
-
149
- const allDays = [...new Set(assignments.map((a) => a.day))].toSorted();
150
- const activeDays = new Set(resolveActiveDaysFromScope(timeScopeValue, allDays));
151
- const targetEmpIds = new Set(
152
- resolveMembersFromScope(entityScopeValue, [...memberMap.values()]).map((e) => e.id),
153
- );
154
-
155
- // Group assignments by member and week
156
- const activeDaysList = allDays.filter((d) => activeDays.has(d));
157
- const weeks = splitIntoWeeks(activeDaysList, resolvedWeekStartsOn);
158
-
159
- const entries: CostContribution["entries"] = [];
160
-
161
- for (const weekDays of weeks) {
162
- const weekDaySet = new Set(weekDays);
163
-
164
- // Get per-member total minutes and per-day minutes this week
165
- const empWeekData = new Map<
166
- string,
167
- { totalMinutes: number; dayMinutes: Map<string, number> }
168
- >();
169
-
170
- for (const a of assignments) {
171
- if (!weekDaySet.has(a.day)) continue;
172
- if (!activeDays.has(a.day)) continue;
173
- if (!targetEmpIds.has(a.memberId)) continue;
174
-
175
- const pattern = patternMap.get(a.shiftPatternId);
176
- if (!pattern) continue;
177
- const duration = patternDurationMinutes(pattern);
178
-
179
- let data = empWeekData.get(a.memberId);
180
- if (!data) {
181
- data = { totalMinutes: 0, dayMinutes: new Map() };
182
- empWeekData.set(a.memberId, data);
143
+ },
144
+ calculateCost(assignments, costContext) {
145
+ if (extraFactor <= 0) return { entries: [] };
146
+
147
+ const memberMap = new Map(costContext.members.map((member) => [member.id, member]));
148
+ const patternMap = new Map(
149
+ costContext.shiftPatterns.map((pattern) => [pattern.id, pattern]),
150
+ );
151
+ const activeDays = new Set(
152
+ resolveActiveDaysFromScope(timeScopeValue, [...costContext.days]).map((day) => day.iso),
153
+ );
154
+ const targetMemberIds = new Set(
155
+ resolveMembersFromScope(entityScopeValue, [...costContext.members]).map(
156
+ (member) => member.id,
157
+ ),
158
+ );
159
+ const activeDaysList = costContext.days.filter((day) => activeDays.has(day.iso));
160
+ const weeks = splitIntoWeeks(activeDaysList, resolvedWeekStartsOn);
161
+
162
+ const entries = [] as Array<{
163
+ memberId: string;
164
+ day: string;
165
+ category: string;
166
+ amount: number;
167
+ }>;
168
+ for (const weekDays of weeks) {
169
+ const weekDaySet = new Set(weekDays.map((day) => day.iso));
170
+ const memberWeekData = new Map<
171
+ string,
172
+ { totalMinutes: number; dayMinutes: Map<string, number> }
173
+ >();
174
+
175
+ for (const assignment of assignments) {
176
+ if (!weekDaySet.has(assignment.day) || !activeDays.has(assignment.day)) continue;
177
+ if (!targetMemberIds.has(assignment.memberId)) continue;
178
+ const pattern = patternMap.get(assignment.shiftPatternId);
179
+ if (!pattern) continue;
180
+ const duration = patternDurationMinutes(pattern);
181
+ let data = memberWeekData.get(assignment.memberId);
182
+ if (!data) {
183
+ data = { totalMinutes: 0, dayMinutes: new Map() };
184
+ memberWeekData.set(assignment.memberId, data);
185
+ }
186
+ data.totalMinutes += duration;
187
+ data.dayMinutes.set(
188
+ assignment.day,
189
+ (data.dayMinutes.get(assignment.day) ?? 0) + duration,
190
+ );
183
191
  }
184
- data.totalMinutes += duration;
185
- data.dayMinutes.set(a.day, (data.dayMinutes.get(a.day) ?? 0) + duration);
186
- }
187
-
188
- for (const [empId, data] of empWeekData) {
189
- const overtimeMinutes = Math.max(0, data.totalMinutes - thresholdMinutes);
190
- if (overtimeMinutes <= 0) continue;
191
192
 
192
- const emp = memberMap.get(empId);
193
- if (!emp) continue;
194
- const rate = getHourlyRate(emp);
195
- if (rate === undefined) continue;
196
-
197
- const totalOvertimeCost = (rate * extraFactor * overtimeMinutes) / 60;
198
-
199
- // Distribute overtime cost across the week's days proportionally
200
- for (const [day, dayMinutes] of data.dayMinutes) {
201
- const proportion = dayMinutes / data.totalMinutes;
202
- entries.push({
203
- memberId: empId,
204
- day,
205
- category: COST_CATEGORY.OVERTIME,
206
- amount: totalOvertimeCost * proportion,
207
- });
193
+ for (const [memberId, data] of memberWeekData) {
194
+ const overtimeMinutes = Math.max(0, data.totalMinutes - thresholdMinutes);
195
+ if (overtimeMinutes <= 0) continue;
196
+ const member = memberMap.get(memberId);
197
+ if (!member) continue;
198
+ const rate = getHourlyRate(member);
199
+ if (rate === undefined) continue;
200
+
201
+ const totalOvertimeCost = (rate * extraFactor * overtimeMinutes) / 60;
202
+ for (const [day, dayMinutes] of data.dayMinutes) {
203
+ const proportion = dayMinutes / data.totalMinutes;
204
+ entries.push({
205
+ memberId,
206
+ day,
207
+ category: COST_CATEGORY.OVERTIME,
208
+ amount: totalOvertimeCost * proportion,
209
+ });
210
+ }
208
211
  }
209
212
  }
210
- }
211
213
 
212
- return { entries };
213
- },
214
- };
215
- }
214
+ return { entries };
215
+ },
216
+ };
217
+
218
+ return {
219
+ rule: "overtime-weekly-multiplier",
220
+ artifacts: [costArtifact],
221
+ };
222
+ },
223
+ });